1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
|
// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>
use errors;
use fmt;
use rt;
use time;
use unix;
use unix::signal;
// Stores information about a child process.
export type process = unix::pid;
// Returns the currently running [[process]].
export fn self() process = {
return unix::getpid();
};
// Stores information about an exited process.
export type status = struct {
status: int,
// Not all of these members are supported on all operating systems.
// Only utime and stime are guaranteed to be available.
rusage: struct {
utime: time::instant,
stime: time::instant,
maxrss: u64,
minflt: u64,
majflt: u64,
inblock: u64,
oublock: u64,
nvcsw: u64,
nivcsw: u64,
},
};
fn rusage(st: *status, ru: *rt::rusage) void = {
st.rusage.utime = time::instant {
sec = ru.ru_utime.tv_sec,
nsec = ru.ru_utime.tv_usec * time::MICROSECOND: i64,
};
st.rusage.stime = time::instant {
sec = ru.ru_stime.tv_sec,
nsec = ru.ru_stime.tv_usec * time::MICROSECOND: i64,
};
st.rusage.maxrss = ru.ru_maxrss;
st.rusage.minflt = ru.ru_minflt;
st.rusage.majflt = ru.ru_majflt;
st.rusage.inblock = ru.ru_inblock;
st.rusage.oublock = ru.ru_oublock;
st.rusage.nvcsw = ru.ru_nvcsw;
st.rusage.nivcsw = ru.ru_nivcsw;
};
// Waits for a process to complete, then returns its status information.
export fn wait(proc: *process) (status | error) = {
let ru: rt::rusage = rt::rusage { ... };
let st: status = status { ... };
match (rt::wait4(*proc, &st.status, 0, &ru)) {
case let err: rt::errno =>
return errors::errno(err);
case let pid: rt::pid_t =>
assert(pid == *proc: rt::pid_t);
};
rusage(&st, &ru);
return st;
};
// Waits for the first child process to complete, then returns its process info
// and status
export fn waitany() ((process, status) | error) = {
let ru: rt::rusage = rt::rusage { ... };
let st: status = status { ... };
match (rt::wait4(-1, &st.status, 0, &ru)) {
case let err: rt::errno =>
return errors::errno(err);
case let pid: rt::pid_t =>
rusage(&st, &ru);
return (pid, st);
};
};
// Waits for all children to terminate succesfully. If a child process exits
// with a nonzero status, returns its process info and exit status immediately,
// not waiting for the remaining children.
export fn waitall() (uint | error | !(process, exit_status)) = {
let st: status = status { ... };
let ru: rt::rusage = rt::rusage { ... };
for (let i = 0u; true; i += 1) match (rt::wait4(-1, &st.status, 0, &ru)) {
case let err: rt::errno =>
if (err == rt::ECHILD) {
return i;
};
return errors::errno(err);
case let pid: rt::pid_t =>
match (check(&st)) {
case void => void;
case let es: !exit_status =>
return (pid, es);
};
};
};
// Checks for process completion, returning its status information on
// completion, or void if it is still running.
export fn peek(proc: *process) (status | void | error) = {
let ru: rt::rusage = rt::rusage { ... };
let st: status = status { ... };
match (rt::wait4(*proc, &st.status, rt::WNOHANG, &ru)) {
case let err: rt::errno =>
return errors::errno(err);
case let pid: rt::pid_t =>
switch (pid) {
case 0 =>
return;
case =>
assert(pid == *proc: rt::pid_t);
};
};
rusage(&st, &ru);
return st;
};
// Checks if any child process has completed, returning its process info and
// status if so.
export fn peekany() ((process, status) | void | error) = {
let ru: rt::rusage = rt::rusage { ... };
let st: status = status { ... };
match (rt::wait4(-1, &st.status, rt::WNOHANG, &ru)) {
case let err: rt::errno =>
return errors::errno(err);
case let pid: rt::pid_t =>
switch (pid) {
case 0 =>
return;
case =>
return (pid, st);
};
};
};
// The exit status code of a process.
export type exited = int;
// The signal number which caused a process to terminate.
export type signaled = signal::sig;
// The exit status of a process.
export type exit_status = (exited | signaled);
// Returns a human friendly string describing the exit status. The string is
// statically allocated; use [[strings::dup]] to extend its lifetime.
export fn exitstr(status: exit_status) const str = {
static let buf: [1024]u8 = [0...];
match (status) {
case let i: exited =>
switch (i) {
case 0 =>
return "exited normally";
case =>
return fmt::bsprintf(buf, "exited with status {}",
i: int)!;
};
case let s: signaled =>
return fmt::bsprintf(buf, "exited with signal {}",
signal::signame(s))!;
};
};
// Returns the exit status of a completed process.
export fn exit(stat: *status) exit_status = {
if (rt::wifexited(stat.status)) {
return rt::wexitstatus(stat.status): exited;
};
if (rt::wifsignaled(stat.status)) {
return rt::wtermsig(stat.status): signaled;
};
abort("Unexpected exit status");
};
// Checks the exit status of a completed process, returning void if successful,
// or its status code as an error type if not.
export fn check(stat: *status) (void | !exit_status) = {
if (rt::wifexited(stat.status)) {
switch (rt::wexitstatus(stat.status)) {
case 0 =>
return void;
case =>
return exit(stat);
};
};
return exit(stat);
};
// Terminates a process. On Linux, this sends [[unix::signal::sig::TERM]] to the
// process.
export fn kill(proc: process) (void | errors::error) = {
return sig(proc, signal::sig::TERM);
};
// Sends a signal to a child process. This function is only supported on
// Unix-like systems.
export fn sig(proc: process, sig: signal::sig) (void | errors::error) = {
match (rt::kill(proc, sig)) {
case let errno: rt::errno =>
return errors::errno(errno);
case void =>
return;
};
};
|