process.odin 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. package os2
  2. import "base:runtime"
  3. import "core:time"
  4. /*
  5. In procedures that explicitly state this as one of the allowed values,
  6. specifies an infinite timeout.
  7. */
  8. TIMEOUT_INFINITE :: time.MIN_DURATION // Note(flysand): Any negative duration will be treated as infinity
  9. /*
  10. Arguments to the current process.
  11. */
  12. args := get_args()
  13. @(private="file")
  14. get_args :: proc() -> []string {
  15. result := make([]string, len(runtime.args__), heap_allocator())
  16. for rt_arg, i in runtime.args__ {
  17. result[i] = string(rt_arg)
  18. }
  19. return result
  20. }
  21. /*
  22. Exit the current process.
  23. */
  24. exit :: proc "contextless" (code: int) -> ! {
  25. _exit(code)
  26. }
  27. /*
  28. Obtain the UID of the current process.
  29. **Note(windows)**: Windows doesn't follow the posix permissions model, so
  30. the function simply returns -1.
  31. */
  32. @(require_results)
  33. get_uid :: proc() -> int {
  34. return _get_uid()
  35. }
  36. /*
  37. Obtain the effective UID of the current process.
  38. The effective UID is typically the same as the UID of the process. In case
  39. the process was run by a user with elevated permissions, the process may
  40. lower the privilege to perform some tasks without privilege. In these cases
  41. the real UID of the process and the effective UID are different.
  42. **Note(windows)**: Windows doesn't follow the posix permissions model, so
  43. the function simply returns -1.
  44. */
  45. @(require_results)
  46. get_euid :: proc() -> int {
  47. return _get_euid()
  48. }
  49. /*
  50. Obtain the GID of the current process.
  51. **Note(windows)**: Windows doesn't follow the posix permissions model, so
  52. the function simply returns -1.
  53. */
  54. @(require_results)
  55. get_gid :: proc() -> int {
  56. return _get_gid()
  57. }
  58. /*
  59. Obtain the effective GID of the current process.
  60. The effective GID is typically the same as the GID of the process. In case
  61. the process was run by a user with elevated permissions, the process may
  62. lower the privilege to perform some tasks without privilege. In these cases
  63. the real GID of the process and the effective GID are different.
  64. **Note(windows)**: Windows doesn't follow the posix permissions model, so
  65. the function simply returns -1.
  66. */
  67. @(require_results)
  68. get_egid :: proc() -> int {
  69. return _get_egid()
  70. }
  71. /*
  72. Obtain the ID of the current process.
  73. */
  74. @(require_results)
  75. get_pid :: proc() -> int {
  76. return _get_pid()
  77. }
  78. /*
  79. Obtain the ID of the parent process.
  80. **Note(windows)**: Windows does not mantain strong relationships between
  81. parent and child processes. This function returns the ID of the process
  82. that has created the current process. In case the parent has died, the ID
  83. returned by this function can identify a non-existent or a different
  84. process.
  85. */
  86. @(require_results)
  87. get_ppid :: proc() -> int {
  88. return _get_ppid()
  89. }
  90. /*
  91. Obtain ID's of all processes running in the system.
  92. */
  93. @(require_results)
  94. process_list :: proc(allocator: runtime.Allocator) -> ([]int, Error) {
  95. return _process_list(allocator)
  96. }
  97. /*
  98. Bit set specifying which fields of the `Process_Info` struct need to be
  99. obtained by the `process_info()` procedure. Each bit corresponds to a
  100. field in the `Process_Info` struct.
  101. */
  102. Process_Info_Fields :: bit_set[Process_Info_Field]
  103. Process_Info_Field :: enum {
  104. Executable_Path,
  105. PPid,
  106. Priority,
  107. Command_Line,
  108. Command_Args,
  109. Environment,
  110. Username,
  111. Working_Dir,
  112. }
  113. ALL_INFO :: Process_Info_Fields{.Executable_Path, .PPid, .Priority, .Command_Line, .Command_Args, .Environment, .Username, .Working_Dir}
  114. /*
  115. Contains information about the process as obtained by the `process_info()`
  116. procedure.
  117. */
  118. Process_Info :: struct {
  119. // The information about a process the struct contains. `pid` is always
  120. // stored, no matter what.
  121. fields: Process_Info_Fields,
  122. // The ID of the process.
  123. pid: int,
  124. // The ID of the parent process.
  125. ppid: int,
  126. // The process priority.
  127. priority: int,
  128. // The path to the executable, which the process runs.
  129. executable_path: string,
  130. // The command line supplied to the process.
  131. command_line: string,
  132. // The arguments supplied to the process.
  133. command_args: []string,
  134. // The environment of the process.
  135. environment: []string,
  136. // The username of the user who started the process.
  137. username: string,
  138. // The current working directory of the process.
  139. working_dir: string,
  140. }
  141. /*
  142. Obtain information about a process.
  143. This procedure obtains an information, specified by `selection` parameter of
  144. a process given by `pid`.
  145. Use `free_process_info` to free the memory allocated by this procedure. The
  146. `free_process_info` procedure needs to be called, even if this procedure
  147. returned an error, as some of the fields may have been allocated.
  148. **Note**: The resulting information may or may contain the fields specified
  149. by the `selection` parameter. Always check whether the returned
  150. `Process_Info` struct has the required fields before checking the error code
  151. returned by this procedure.
  152. */
  153. @(require_results)
  154. process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
  155. return _process_info_by_pid(pid, selection, allocator)
  156. }
  157. /*
  158. Obtain information about a process.
  159. This procedure obtains information, specified by `selection` parameter
  160. about a process that has been opened by the application, specified in
  161. the `process` parameter.
  162. Use `free_process_info` to free the memory allocated by this procedure. The
  163. `free_process_info` procedure needs to be called, even if this procedure
  164. returned an error, as some of the fields may have been allocated.
  165. **Note**: The resulting information may or may contain the fields specified
  166. by the `selection` parameter. Always check whether the returned
  167. `Process_Info` struct has the required fields before checking the error code
  168. returned by this procedure.
  169. */
  170. @(require_results)
  171. process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
  172. return _process_info_by_handle(process, selection, allocator)
  173. }
  174. /*
  175. Obtain information about the current process.
  176. This procedure obtains the information, specified by `selection` parameter
  177. about the currently running process.
  178. Use `free_process_info` to free the memory allocated by this procedure. The
  179. `free_process_info` procedure needs to be called, even if this procedure
  180. returned an error, as some of the fields may have been allocated.
  181. **Note**: The resulting information may or may contain the fields specified
  182. by the `selection` parameter. Always check whether the returned
  183. `Process_Info` struct has the required fields before checking the error code
  184. returned by this procedure.
  185. */
  186. @(require_results)
  187. current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
  188. return _current_process_info(selection, allocator)
  189. }
  190. /*
  191. Obtain information about the specified process.
  192. */
  193. process_info :: proc {
  194. process_info_by_pid,
  195. process_info_by_handle,
  196. current_process_info,
  197. }
  198. /*
  199. Free the information about the process.
  200. This procedure frees the memory occupied by process info using the provided
  201. allocator. The allocator needs to be the same allocator that was supplied
  202. to the `process_info` function.
  203. */
  204. free_process_info :: proc(pi: Process_Info, allocator: runtime.Allocator) {
  205. delete(pi.executable_path, allocator)
  206. delete(pi.command_line, allocator)
  207. for a in pi.command_args {
  208. delete(a, allocator)
  209. }
  210. delete(pi.command_args, allocator)
  211. for s in pi.environment {
  212. delete(s, allocator)
  213. }
  214. delete(pi.environment, allocator)
  215. delete(pi.working_dir, allocator)
  216. delete(pi.username, allocator)
  217. }
  218. /*
  219. Represents a process handle.
  220. When a process dies, the OS is free to re-use the pid of that process. The
  221. `Process` struct represents a handle to the process that will refer to a
  222. specific process, even after it has died.
  223. **Note(linux)**: The `handle` will be referring to pidfd.
  224. */
  225. Process :: struct {
  226. pid: int,
  227. handle: uintptr,
  228. }
  229. Process_Open_Flags :: bit_set[Process_Open_Flag]
  230. Process_Open_Flag :: enum {
  231. // Request for reading from the virtual memory of another process.
  232. Mem_Read,
  233. // Request for writing to the virtual memory of another process.
  234. Mem_Write,
  235. }
  236. /*
  237. Open a process handle using it's pid.
  238. This procedure obtains a process handle of a process specified by `pid`.
  239. This procedure can be subject to race conditions. See the description of
  240. `Process`.
  241. Use `process_close()` function to close the process handle.
  242. */
  243. @(require_results)
  244. process_open :: proc(pid: int, flags := Process_Open_Flags {}) -> (Process, Error) {
  245. return _process_open(pid, flags)
  246. }
  247. /*
  248. The description of how a process should be created.
  249. */
  250. Process_Desc :: struct {
  251. // OS-specific attributes.
  252. sys_attr: _Sys_Process_Attributes,
  253. // The working directory of the process. If the string has length 0, the
  254. // working directory is assumed to be the current working directory of the
  255. // current process.
  256. working_dir: string,
  257. // The command to run. Each element of the slice is a separate argument to
  258. // the process. The first element of the slice would be the executable.
  259. command: []string,
  260. // A slice of strings, each having the format `KEY=VALUE` representing the
  261. // full environment that the child process will receive.
  262. // In case this slice is `nil`, the current process' environment is used.
  263. // NOTE(laytan): maybe should be `Maybe([]string)` so you can do `nil` == current env, empty == empty/no env.
  264. env: []string,
  265. // The `stderr` handle to give to the child process. It can be either a file
  266. // or a writeable end of a pipe. Passing `nil` will shut down the process'
  267. // stderr output.
  268. stderr: ^File,
  269. // The `stdout` handle to give to the child process. It can be either a file
  270. // or a writeabe end of a pipe. Passing a `nil` will shut down the process'
  271. // stdout output.
  272. stdout: ^File,
  273. // The `stdin` handle to give to the child process. It can either be a file
  274. // or a readable end of a pipe. Passing a `nil` will shut down the process'
  275. // input.
  276. stdin: ^File,
  277. }
  278. /*
  279. Create a new process and obtain its handle.
  280. This procedure creates a new process, with a given command and environment
  281. strings as parameters. Use `environ()` to inherit the environment of the
  282. current process.
  283. The `desc` parameter specifies the description of how the process should
  284. be created. It contains information such as the command line, the
  285. environment of the process, the starting directory and many other options.
  286. Most of the fields in the struct can be set to `nil` or an empty value.
  287. Use `process_close` to close the handle to the process. Note, that this
  288. is not the same as terminating the process. One can terminate the process
  289. and not close the handle, in which case the handle would be leaked. In case
  290. the function returns an error, an invalid handle is returned.
  291. This procedure is not thread-safe. It may alter the inheritance properties
  292. of file handles in an unpredictable manner. In case multiple threads change
  293. handle inheritance properties, make sure to serialize all those calls.
  294. */
  295. @(require_results)
  296. process_start :: proc(desc: Process_Desc) -> (Process, Error) {
  297. return _process_start(desc)
  298. }
  299. /*
  300. Execute the process and capture stdout and stderr streams.
  301. This procedure creates a new process, with a given command and environment
  302. strings as parameters, and waits until the process finishes execution. While
  303. the process is running, this procedure accumulates the output of its stdout
  304. and stderr streams and returns byte slices containing the captured data from
  305. the streams.
  306. This procedure expects that `stdout` and `stderr` fields of the `desc` parameter
  307. are left at default, i.e. a `nil` value. You can not capture stdout/stderr and
  308. redirect it to a file at the same time.
  309. This procedure does not free `stdout` and `stderr` slices before an error is
  310. returned. Make sure to call `delete` on these slices.
  311. */
  312. @(require_results)
  313. process_exec :: proc(
  314. desc: Process_Desc,
  315. allocator: runtime.Allocator,
  316. loc := #caller_location,
  317. ) -> (
  318. state: Process_State,
  319. stdout: []byte,
  320. stderr: []byte,
  321. err: Error,
  322. ) {
  323. assert(desc.stdout == nil, "Cannot redirect stdout when it's being captured", loc)
  324. assert(desc.stderr == nil, "Cannot redirect stderr when it's being captured", loc)
  325. stdout_r, stdout_w := pipe() or_return
  326. defer close(stdout_r)
  327. stderr_r, stderr_w := pipe() or_return
  328. defer close(stderr_r)
  329. process: Process
  330. {
  331. // NOTE(flysand): Make sure the write-ends are closed, regardless
  332. // of the outcome. This makes read-ends readable on our side.
  333. defer close(stdout_w)
  334. defer close(stderr_w)
  335. desc := desc
  336. desc.stdout = stdout_w
  337. desc.stderr = stderr_w
  338. process = process_start(desc) or_return
  339. }
  340. {
  341. stdout_b: [dynamic]byte
  342. stdout_b.allocator = allocator
  343. defer stdout = stdout_b[:]
  344. stderr_b: [dynamic]byte
  345. stderr_b.allocator = allocator
  346. defer stderr = stderr_b[:]
  347. buf: [1024]u8 = ---
  348. stdout_done, stderr_done, has_data: bool
  349. for err == nil && (!stdout_done || !stderr_done) {
  350. n := 0
  351. if !stdout_done {
  352. has_data, err = pipe_has_data(stdout_r)
  353. if has_data {
  354. n, err = read(stdout_r, buf[:])
  355. }
  356. switch err {
  357. case nil:
  358. _, err = append(&stdout_b, ..buf[:n])
  359. case .EOF, .Broken_Pipe:
  360. stdout_done = true
  361. err = nil
  362. }
  363. }
  364. if err == nil && !stderr_done {
  365. n = 0
  366. has_data, err = pipe_has_data(stderr_r)
  367. if has_data {
  368. n, err = read(stderr_r, buf[:])
  369. }
  370. switch err {
  371. case nil:
  372. _, err = append(&stderr_b, ..buf[:n])
  373. case .EOF, .Broken_Pipe:
  374. stderr_done = true
  375. err = nil
  376. }
  377. }
  378. }
  379. }
  380. if err != nil {
  381. state, _ = process_wait(process, timeout=0)
  382. if !state.exited {
  383. _ = process_kill(process)
  384. state, _ = process_wait(process)
  385. }
  386. return
  387. }
  388. state, err = process_wait(process)
  389. return
  390. }
  391. /*
  392. The state of the process after it has finished execution.
  393. */
  394. Process_State :: struct {
  395. // The ID of the process.
  396. pid: int,
  397. // Specifies whether the process has terminated or is still running.
  398. exited: bool,
  399. // The exit code of the process, if it has exited.
  400. // Will also store the number of the exception or signal that has crashed the
  401. // process.
  402. exit_code: int,
  403. // Specifies whether the termination of the process was successfull or not,
  404. // i.e. whether it has crashed or not.
  405. // **Note(windows)**: On windows `true` is always returned, as there is no
  406. // reliable way to obtain information about whether the process has crashed.
  407. success: bool,
  408. // The time the process has spend executing in kernel time.
  409. system_time: time.Duration,
  410. // The time the process has spend executing in userspace.
  411. user_time: time.Duration,
  412. }
  413. /*
  414. Wait for a process event.
  415. This procedure blocks the execution until the process has exited or the
  416. timeout (if specified) has reached zero. If the timeout is `TIMEOUT_INFINITE`,
  417. no timeout restriction is imposed and the procedure can block indefinately.
  418. If the timeout has expired, the `General_Error.Timeout` is returned as
  419. the error.
  420. If an error is returned for any other reason, other than timeout, the
  421. process state is considered undetermined.
  422. */
  423. @(require_results)
  424. process_wait :: proc(process: Process, timeout := TIMEOUT_INFINITE) -> (Process_State, Error) {
  425. return _process_wait(process, timeout)
  426. }
  427. /*
  428. Close the handle to a process.
  429. This procedure closes the handle associated with a process. It **does not**
  430. terminate a process, in case it was running. In case a termination is
  431. desired, kill the process first, wait for the process to finish,
  432. then close the handle.
  433. */
  434. @(require_results)
  435. process_close :: proc(process: Process) -> (Error) {
  436. return _process_close(process)
  437. }
  438. /*
  439. Terminate a process.
  440. This procedure terminates a process, specified by it's handle, `process`.
  441. */
  442. @(require_results)
  443. process_kill :: proc(process: Process) -> (Error) {
  444. return _process_kill(process)
  445. }