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