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