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