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