process_linux.odin 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866
  1. //+build linux
  2. //+private file
  3. package os2
  4. import "base:runtime"
  5. import "base:intrinsics"
  6. import "core:time"
  7. import "core:slice"
  8. import "core:strings"
  9. import "core:strconv"
  10. import "core:sys/linux"
  11. import "core:path/filepath"
  12. PIDFD_UNASSIGNED :: ~uintptr(0)
  13. @(private="package")
  14. _exit :: proc "contextless" (code: int) -> ! {
  15. linux.exit_group(i32(code))
  16. }
  17. @(private="package")
  18. _get_uid :: proc() -> int {
  19. return int(linux.getuid())
  20. }
  21. @(private="package")
  22. _get_euid :: proc() -> int {
  23. return int(linux.geteuid())
  24. }
  25. @(private="package")
  26. _get_gid :: proc() -> int {
  27. return int(linux.getgid())
  28. }
  29. @(private="package")
  30. _get_egid :: proc() -> int {
  31. return int(linux.getegid())
  32. }
  33. @(private="package")
  34. _get_pid :: proc() -> int {
  35. return int(linux.getpid())
  36. }
  37. @(private="package")
  38. _get_ppid :: proc() -> int {
  39. return int(linux.getppid())
  40. }
  41. @(private="package")
  42. _process_list :: proc(allocator: runtime.Allocator) -> (list: []int, err: Error) {
  43. TEMP_ALLOCATOR_GUARD()
  44. dir_fd, errno := linux.open("/proc/", _OPENDIR_FLAGS)
  45. #partial switch errno {
  46. case .NONE:
  47. // okay
  48. case .ENOTDIR:
  49. err = .Invalid_Dir
  50. return
  51. case .ENOENT:
  52. err = .Not_Exist
  53. return
  54. case:
  55. err = _get_platform_error(errno)
  56. return
  57. }
  58. defer linux.close(dir_fd)
  59. dynamic_list := make([dynamic]int, temp_allocator()) or_return
  60. buf := make([dynamic]u8, 128, 128, temp_allocator()) or_return
  61. loop: for {
  62. buflen: int
  63. buflen, errno = linux.getdents(dir_fd, buf[:])
  64. #partial switch errno {
  65. case .EINVAL:
  66. resize(&buf, len(buf) * 2)
  67. continue loop
  68. case .NONE:
  69. if buflen == 0 { break loop }
  70. case:
  71. return {}, _get_platform_error(errno)
  72. }
  73. offset: int
  74. for d in linux.dirent_iterate_buf(buf[:buflen], &offset) {
  75. d_name_str := linux.dirent_name(d)
  76. if pid, ok := strconv.parse_int(d_name_str); ok {
  77. append(&dynamic_list, pid)
  78. }
  79. }
  80. }
  81. list, err = slice.clone(dynamic_list[:], allocator)
  82. return
  83. }
  84. @(private="package")
  85. _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
  86. TEMP_ALLOCATOR_GUARD()
  87. info.pid = pid
  88. // Use this to make cstrings without copying.
  89. path_backing: [48]u8
  90. path_builder := strings.builder_from_bytes(path_backing[:])
  91. strings.write_string(&path_builder, "/proc/")
  92. strings.write_int(&path_builder, pid)
  93. proc_fd, errno := linux.open(strings.to_cstring(&path_builder), _OPENDIR_FLAGS)
  94. if errno != .NONE {
  95. err = _get_platform_error(errno)
  96. return
  97. }
  98. defer linux.close(proc_fd)
  99. username_if: if .Username in selection {
  100. s: linux.Stat
  101. if errno = linux.fstat(proc_fd, &s); errno != .NONE {
  102. err = _get_platform_error(errno)
  103. break username_if
  104. }
  105. passwd_bytes: []u8
  106. passwd_err: Error
  107. passwd_bytes, passwd_err = _read_entire_pseudo_file_cstring("/etc/passwd", temp_allocator())
  108. if passwd_err != nil {
  109. err = passwd_err
  110. break username_if
  111. }
  112. passwd := string(passwd_bytes)
  113. for len(passwd) > 0 {
  114. n := strings.index_byte(passwd, ':')
  115. if n < 0 {
  116. break
  117. }
  118. username := passwd[:n]
  119. passwd = passwd[n+1:]
  120. // skip password field
  121. passwd = passwd[strings.index_byte(passwd, ':') + 1:]
  122. n = strings.index_byte(passwd, ':')
  123. if uid, ok := strconv.parse_int(passwd[:n]); ok && uid == int(s.uid) {
  124. info.username = strings.clone(username, allocator) or_return
  125. info.fields += {.Username}
  126. break
  127. } else if !ok {
  128. err = .Invalid_File
  129. break username_if
  130. }
  131. eol := strings.index_byte(passwd, '\n')
  132. if eol < 0 {
  133. break
  134. }
  135. passwd = passwd[eol + 1:]
  136. }
  137. }
  138. cmdline_if: if selection & {.Working_Dir, .Command_Line, .Command_Args, .Executable_Path} != {} {
  139. strings.builder_reset(&path_builder)
  140. strings.write_string(&path_builder, "/proc/")
  141. strings.write_int(&path_builder, pid)
  142. strings.write_string(&path_builder, "/cmdline")
  143. cmdline_bytes, cmdline_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder), temp_allocator())
  144. if cmdline_err != nil || len(cmdline_bytes) == 0 {
  145. err = cmdline_err
  146. break cmdline_if
  147. }
  148. cmdline := string(cmdline_bytes)
  149. terminator := strings.index_byte(cmdline, 0)
  150. assert(terminator > 0)
  151. command_line_exec := cmdline[:terminator]
  152. // Still need cwd if the execution on the command line is relative.
  153. cwd: string
  154. cwd_err: Error
  155. if .Working_Dir in selection || (.Executable_Path in selection && command_line_exec[0] != '/') {
  156. strings.builder_reset(&path_builder)
  157. strings.write_string(&path_builder, "/proc/")
  158. strings.write_int(&path_builder, pid)
  159. strings.write_string(&path_builder, "/cwd")
  160. cwd, cwd_err = _read_link_cstr(strings.to_cstring(&path_builder), temp_allocator()) // allowed to fail
  161. if cwd_err == nil && .Working_Dir in selection {
  162. info.working_dir = strings.clone(cwd, allocator) or_return
  163. info.fields += {.Working_Dir}
  164. } else if cwd_err != nil {
  165. err = cwd_err
  166. break cmdline_if
  167. }
  168. }
  169. if .Executable_Path in selection {
  170. if cmdline[0] == '/' {
  171. info.executable_path = strings.clone(cmdline[:terminator], allocator) or_return
  172. info.fields += {.Executable_Path}
  173. } else if cwd_err == nil {
  174. info.executable_path = filepath.join({ cwd, cmdline[:terminator] }, allocator) or_return
  175. info.fields += {.Executable_Path}
  176. } else {
  177. break cmdline_if
  178. }
  179. }
  180. if selection & {.Command_Line, .Command_Args} != {} {
  181. // skip to first arg
  182. //cmdline = cmdline[terminator + 1:]
  183. command_line_builder: strings.Builder
  184. command_args_list: [dynamic]string
  185. if .Command_Line in selection {
  186. command_line_builder = strings.builder_make(allocator) or_return
  187. info.fields += {.Command_Line}
  188. }
  189. for i := 0; len(cmdline) > 0; i += 1 {
  190. if terminator = strings.index_byte(cmdline, 0); terminator < 0 {
  191. break
  192. }
  193. if .Command_Line in selection {
  194. if i > 0 {
  195. strings.write_byte(&command_line_builder, ' ')
  196. }
  197. strings.write_string(&command_line_builder, cmdline[:terminator])
  198. }
  199. if .Command_Args in selection {
  200. if i == 1 {
  201. command_args_list = make([dynamic]string, allocator) or_return
  202. info.fields += {.Command_Args}
  203. }
  204. if i > 0 {
  205. arg := strings.clone(cmdline[:terminator], allocator) or_return
  206. append(&command_args_list, arg) or_return
  207. }
  208. }
  209. cmdline = cmdline[terminator + 1:]
  210. }
  211. info.command_line = strings.to_string(command_line_builder)
  212. info.command_args = command_args_list[:]
  213. }
  214. }
  215. stat_if: if selection & {.PPid, .Priority} != {} {
  216. strings.builder_reset(&path_builder)
  217. strings.write_string(&path_builder, "/proc/")
  218. strings.write_int(&path_builder, pid)
  219. strings.write_string(&path_builder, "/stat")
  220. proc_stat_bytes, stat_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder), temp_allocator())
  221. if stat_err != nil {
  222. err = stat_err
  223. break stat_if
  224. }
  225. if len(proc_stat_bytes) <= 0 {
  226. break stat_if
  227. }
  228. // Skip to the first field after the executable name
  229. stats: string
  230. if start := strings.last_index_byte(string(proc_stat_bytes), ')'); start != -1 {
  231. stats = string(proc_stat_bytes[start + 2:])
  232. } else {
  233. break stat_if
  234. }
  235. // NOTE: index 0 corresponds to field 3 (state) from `man 5 proc_pid_stat`
  236. // because we skipped passed the executable name above.
  237. Fields :: enum {
  238. State,
  239. PPid,
  240. PGrp,
  241. Session,
  242. Tty_Nr,
  243. TpGid,
  244. Flags,
  245. MinFlt,
  246. CMinFlt,
  247. MajFlt,
  248. CMajFlt,
  249. UTime,
  250. STime,
  251. CUTime,
  252. CSTime,
  253. Priority,
  254. Nice,
  255. //... etc,
  256. }
  257. stat_fields := strings.split(stats, " ", temp_allocator()) or_return
  258. if len(stat_fields) <= int(Fields.Nice) {
  259. break stat_if
  260. }
  261. if .PPid in selection {
  262. if ppid, ok := strconv.parse_int(stat_fields[Fields.PPid]); ok {
  263. info.ppid = ppid
  264. info.fields += {.PPid}
  265. } else {
  266. err = .Invalid_File
  267. break stat_if
  268. }
  269. }
  270. if .Priority in selection {
  271. if nice, ok := strconv.parse_int(stat_fields[Fields.Nice]); ok {
  272. info.priority = nice
  273. info.fields += {.Priority}
  274. } else {
  275. err = .Invalid_File
  276. break stat_if
  277. }
  278. }
  279. }
  280. if .Environment in selection {
  281. strings.builder_reset(&path_builder)
  282. strings.write_string(&path_builder, "/proc/")
  283. strings.write_int(&path_builder, pid)
  284. strings.write_string(&path_builder, "/environ")
  285. if env_bytes, env_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder), temp_allocator()); env_err == nil {
  286. env := string(env_bytes)
  287. env_list := make([dynamic]string, allocator) or_return
  288. for len(env) > 0 {
  289. terminator := strings.index_byte(env, 0)
  290. if terminator <= 0 {
  291. break
  292. }
  293. e := strings.clone(env[:terminator], allocator) or_return
  294. append(&env_list, e) or_return
  295. env = env[terminator + 1:]
  296. }
  297. info.environment = env_list[:]
  298. info.fields += {.Environment}
  299. } else if err == nil {
  300. err = env_err
  301. }
  302. }
  303. return
  304. }
  305. @(private="package")
  306. _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
  307. return _process_info_by_pid(process.pid, selection, allocator)
  308. }
  309. @(private="package")
  310. _current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
  311. return _process_info_by_pid(get_pid(), selection, allocator)
  312. }
  313. @(private="package")
  314. _process_open :: proc(pid: int, _: Process_Open_Flags) -> (process: Process, err: Error) {
  315. process.pid = pid
  316. process.handle = PIDFD_UNASSIGNED
  317. pidfd, errno := linux.pidfd_open(linux.Pid(pid), {})
  318. if errno == .ENOSYS {
  319. return process, .Unsupported
  320. }
  321. if errno != .NONE {
  322. return process, _get_platform_error(errno)
  323. }
  324. process.handle = uintptr(pidfd)
  325. return
  326. }
  327. @(private="package")
  328. _Sys_Process_Attributes :: struct {}
  329. @(private="package")
  330. _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
  331. has_executable_permissions :: proc(fd: linux.Fd) -> bool {
  332. backing: [48]u8
  333. b := strings.builder_from_bytes(backing[:])
  334. strings.write_string(&b, "/proc/self/fd/")
  335. strings.write_int(&b, int(fd))
  336. return linux.access(strings.to_cstring(&b), linux.X_OK) == .NONE
  337. }
  338. TEMP_ALLOCATOR_GUARD()
  339. if len(desc.command) == 0 {
  340. return process, .Invalid_Command
  341. }
  342. dir_fd := linux.AT_FDCWD
  343. errno: linux.Errno
  344. if desc.working_dir != "" {
  345. dir_cstr := temp_cstring(desc.working_dir) or_return
  346. if dir_fd, errno = linux.open(dir_cstr, _OPENDIR_FLAGS); errno != .NONE {
  347. return process, _get_platform_error(errno)
  348. }
  349. }
  350. defer if desc.working_dir != "" {
  351. linux.close(dir_fd)
  352. }
  353. // search PATH if just a plain name is provided
  354. exe_fd: linux.Fd
  355. executable_name := desc.command[0]
  356. if strings.index_byte(executable_name, '/') < 0 {
  357. path_env := get_env("PATH", temp_allocator())
  358. path_dirs := filepath.split_list(path_env, temp_allocator()) or_return
  359. exe_builder := strings.builder_make(temp_allocator()) or_return
  360. found: bool
  361. for dir in path_dirs {
  362. strings.builder_reset(&exe_builder)
  363. strings.write_string(&exe_builder, dir)
  364. strings.write_byte(&exe_builder, '/')
  365. strings.write_string(&exe_builder, executable_name)
  366. exe_path := strings.to_cstring(&exe_builder)
  367. if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
  368. continue
  369. }
  370. if !has_executable_permissions(exe_fd) {
  371. linux.close(exe_fd)
  372. continue
  373. }
  374. found = true
  375. break
  376. }
  377. if !found {
  378. // check in cwd to match windows behavior
  379. strings.builder_reset(&exe_builder)
  380. strings.write_string(&exe_builder, "./")
  381. strings.write_string(&exe_builder, executable_name)
  382. exe_path := strings.to_cstring(&exe_builder)
  383. if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
  384. return process, .Not_Exist
  385. }
  386. if !has_executable_permissions(exe_fd) {
  387. linux.close(exe_fd)
  388. return process, .Permission_Denied
  389. }
  390. }
  391. } else {
  392. exe_path := temp_cstring(executable_name) or_return
  393. if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
  394. return process, _get_platform_error(errno)
  395. }
  396. if !has_executable_permissions(exe_fd) {
  397. linux.close(exe_fd)
  398. return process, .Permission_Denied
  399. }
  400. }
  401. // At this point, we have an executable.
  402. defer linux.close(exe_fd)
  403. // args and environment need to be a list of cstrings
  404. // that are terminated by a nil pointer.
  405. cargs := make([]cstring, len(desc.command) + 1, temp_allocator()) or_return
  406. for command, i in desc.command {
  407. cargs[i] = temp_cstring(command) or_return
  408. }
  409. // Use current process' environment if description didn't provide it.
  410. env: [^]cstring
  411. if desc.env == nil {
  412. // take this process's current environment
  413. env = raw_data(export_cstring_environment(temp_allocator()))
  414. } else {
  415. cenv := make([]cstring, len(desc.env) + 1, temp_allocator()) or_return
  416. for env, i in desc.env {
  417. cenv[i] = temp_cstring(env) or_return
  418. }
  419. env = &cenv[0]
  420. }
  421. child_pipe_fds: [2]linux.Fd
  422. if errno = linux.pipe2(&child_pipe_fds, {.CLOEXEC}); errno != .NONE {
  423. return process, _get_platform_error(errno)
  424. }
  425. defer linux.close(child_pipe_fds[READ])
  426. // TODO: This is the traditional textbook implementation with fork.
  427. // A more efficient implementation with vfork:
  428. //
  429. // 1. retrieve signal handlers
  430. // 2. block all signals
  431. // 3. allocate some stack space
  432. // 4. vfork (waits for child exit or execve); In child:
  433. // a. set child signal handlers
  434. // b. set up any necessary pipes
  435. // c. execve
  436. // 5. restore signal handlers
  437. //
  438. pid: linux.Pid
  439. if pid, errno = linux.fork(); errno != .NONE {
  440. linux.close(child_pipe_fds[WRITE])
  441. return process, _get_platform_error(errno)
  442. }
  443. STDIN :: linux.Fd(0)
  444. STDOUT :: linux.Fd(1)
  445. STDERR :: linux.Fd(2)
  446. READ :: 0
  447. WRITE :: 1
  448. if pid == 0 {
  449. // in child process now
  450. write_errno_to_parent_and_abort :: proc(parent_fd: linux.Fd, errno: linux.Errno) -> ! {
  451. error_byte: [1]u8 = { u8(errno) }
  452. linux.write(parent_fd, error_byte[:])
  453. intrinsics.trap()
  454. }
  455. stdin_fd: linux.Fd
  456. stdout_fd: linux.Fd
  457. stderr_fd: linux.Fd
  458. if desc.stdin != nil {
  459. stdin_fd = linux.Fd(fd(desc.stdin))
  460. } else {
  461. stdin_fd, errno = linux.open("/dev/null", {})
  462. if errno != .NONE {
  463. write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
  464. }
  465. }
  466. write_devnull: linux.Fd = -1
  467. if desc.stdout != nil {
  468. stdout_fd = linux.Fd(fd(desc.stdout))
  469. } else {
  470. write_devnull, errno = linux.open("/dev/null", {.WRONLY})
  471. if errno != .NONE {
  472. write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
  473. }
  474. stdout_fd = write_devnull
  475. }
  476. if desc.stderr != nil {
  477. stderr_fd = linux.Fd(fd(desc.stderr))
  478. } else {
  479. if write_devnull < 0 {
  480. write_devnull, errno = linux.open("/dev/null", {.WRONLY})
  481. if errno != .NONE {
  482. write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
  483. }
  484. }
  485. stderr_fd = write_devnull
  486. }
  487. if _, errno = linux.dup2(stdin_fd, STDIN); errno != .NONE {
  488. write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
  489. }
  490. if _, errno = linux.dup2(stdout_fd, STDOUT); errno != .NONE {
  491. write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
  492. }
  493. if _, errno = linux.dup2(stderr_fd, STDERR); errno != .NONE {
  494. write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
  495. }
  496. errno = linux.execveat(exe_fd, "", &cargs[0], env, {.AT_EMPTY_PATH})
  497. assert(errno != nil)
  498. write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
  499. }
  500. linux.close(child_pipe_fds[WRITE])
  501. process.pid = int(pid)
  502. child_byte: [1]u8
  503. errno = .EINTR
  504. for errno == .EINTR {
  505. _, errno = linux.read(child_pipe_fds[READ], child_byte[:])
  506. }
  507. // If the read failed, something weird happened. Do not return the read
  508. // error so the user knows to wait on it.
  509. if errno == .NONE {
  510. child_errno := linux.Errno(child_byte[0])
  511. if child_errno != .NONE {
  512. // We can assume it trapped here.
  513. _reap_terminated(process)
  514. process.pid = 0
  515. return process, _get_platform_error(child_errno)
  516. }
  517. }
  518. process, _ = process_open(int(pid))
  519. return
  520. }
  521. _process_state_update_times :: proc(state: ^Process_State) -> (err: Error) {
  522. TEMP_ALLOCATOR_GUARD()
  523. stat_path_buf: [48]u8
  524. path_builder := strings.builder_from_bytes(stat_path_buf[:])
  525. strings.write_string(&path_builder, "/proc/")
  526. strings.write_int(&path_builder, int(state.pid))
  527. strings.write_string(&path_builder, "/stat")
  528. stat_buf: []u8
  529. stat_buf, err = _read_entire_pseudo_file(strings.to_cstring(&path_builder), temp_allocator())
  530. if err != nil {
  531. return
  532. }
  533. // ')' will be the end of the executable name (item 2)
  534. idx := strings.last_index_byte(string(stat_buf), ')')
  535. stats := string(stat_buf[idx + 2:])
  536. // utime and stime are the 14 and 15th items, respectively, and we are
  537. // currently on item 3. Skip 11 items here.
  538. for _ in 0..<11 {
  539. stats = stats[strings.index_byte(stats, ' ') + 1:]
  540. }
  541. idx = strings.index_byte(stats, ' ')
  542. utime_str := stats[:idx]
  543. stats = stats[idx + 1:]
  544. stime_str := stats[:strings.index_byte(stats, ' ')]
  545. utime, stime: int
  546. ok: bool
  547. if utime, ok = strconv.parse_int(utime_str, 10); !ok {
  548. return .Invalid_File
  549. }
  550. if stime, ok = strconv.parse_int(stime_str, 10); !ok {
  551. return .Invalid_File
  552. }
  553. // NOTE: Assuming HZ of 100, 1 jiffy == 10 ms
  554. state.user_time = time.Duration(utime) * 10 * time.Millisecond
  555. state.system_time = time.Duration(stime) * 10 * time.Millisecond
  556. return
  557. }
  558. _reap_terminated :: proc(process: Process) -> (state: Process_State, err: Error) {
  559. state.pid = process.pid
  560. _process_state_update_times(&state)
  561. info: linux.Sig_Info
  562. errno := linux.Errno.EINTR
  563. for errno == .EINTR {
  564. errno = linux.waitid(.PID, linux.Id(process.pid), &info, {.WEXITED}, nil)
  565. }
  566. err = _get_platform_error(errno)
  567. switch linux.Sig_Child_Code(info.code) {
  568. case .NONE, .CONTINUED, .STOPPED:
  569. unreachable()
  570. case .EXITED:
  571. state.exited = true
  572. state.exit_code = int(info.status)
  573. state.success = state.exit_code == 0
  574. case .KILLED, .DUMPED, .TRAPPED:
  575. state.exited = true
  576. state.exit_code = int(info.status)
  577. state.success = false
  578. }
  579. return
  580. }
  581. _timed_wait_on_handle :: proc(process: Process, timeout: time.Duration) -> (process_state: Process_State, err: Error) {
  582. timeout := timeout
  583. process_state.pid = process.pid
  584. pidfd := linux.Fd(process.handle)
  585. pollfd: [1]linux.Poll_Fd = {
  586. {
  587. fd = pidfd,
  588. events = {.IN},
  589. },
  590. }
  591. start_tick := time.tick_now()
  592. mask: bit_set[0..<64; u64]
  593. mask += { int(linux.Signal.SIGCHLD) - 1 }
  594. sigchld_set := transmute(linux.Sig_Set)(mask)
  595. info: linux.Sig_Info
  596. for {
  597. if timeout <= 0 {
  598. _process_state_update_times(&process_state)
  599. err = .Timeout
  600. return
  601. }
  602. ts: linux.Time_Spec = {
  603. time_sec = uint(timeout / time.Second),
  604. time_nsec = uint(timeout % time.Second),
  605. }
  606. n, errno := linux.ppoll(pollfd[:], &ts, &sigchld_set)
  607. if errno != .NONE {
  608. if errno == .EINTR {
  609. timeout -= time.tick_since(start_tick)
  610. start_tick = time.tick_now()
  611. continue
  612. }
  613. return process_state, _get_platform_error(errno)
  614. }
  615. if n == 0 { // timeout with no events
  616. _process_state_update_times(&process_state)
  617. err = .Timeout
  618. return
  619. }
  620. if errno = linux.waitid(.PIDFD, linux.Id(process.handle), &info, {.WEXITED, .WNOHANG, .WNOWAIT}, nil); errno != .NONE {
  621. return process_state, _get_platform_error(errno)
  622. }
  623. if info.signo == .SIGCHLD {
  624. break
  625. }
  626. timeout -= time.tick_since(start_tick)
  627. start_tick = time.tick_now()
  628. }
  629. // _reap_terminated for pidfd
  630. {
  631. _process_state_update_times(&process_state)
  632. errno := linux.Errno.EINTR
  633. for errno == .EINTR {
  634. errno = linux.waitid(.PIDFD, linux.Id(process.handle), &info, {.WEXITED}, nil)
  635. }
  636. err = _get_platform_error(errno)
  637. switch linux.Sig_Child_Code(info.code) {
  638. case .NONE, .CONTINUED, .STOPPED:
  639. unreachable()
  640. case .EXITED:
  641. process_state.exited = true
  642. process_state.exit_code = int(info.status)
  643. process_state.success = process_state.exit_code == 0
  644. case .KILLED, .DUMPED, .TRAPPED:
  645. process_state.exited = true
  646. process_state.exit_code = int(info.status)
  647. process_state.success = false
  648. }
  649. }
  650. return
  651. }
  652. _timed_wait_on_pid :: proc(process: Process, timeout: time.Duration) -> (process_state: Process_State, err: Error) {
  653. timeout := timeout
  654. process_state.pid = process.pid
  655. mask: bit_set[0..<64; u64]
  656. mask += { int(linux.Signal.SIGCHLD) - 1 }
  657. sigchld_set := transmute(linux.Sig_Set)(mask)
  658. start_tick := time.tick_now()
  659. org_sigset: linux.Sig_Set
  660. errno := linux.rt_sigprocmask(.SIG_BLOCK, &sigchld_set, &org_sigset)
  661. if errno != .NONE {
  662. return process_state, _get_platform_error(errno)
  663. }
  664. defer linux.rt_sigprocmask(.SIG_SETMASK, &org_sigset, nil)
  665. // In case there was a signal handler on SIGCHLD, avoid race
  666. // condition by checking wait first.
  667. info: linux.Sig_Info
  668. errno = linux.waitid(.PID, linux.Id(process.pid), &info, {.WNOWAIT, .WEXITED, .WNOHANG}, nil)
  669. for errno != .NONE || info.code == 0 || info.pid != linux.Pid(process.pid) {
  670. if timeout <= 0 {
  671. _process_state_update_times(&process_state)
  672. err = .Timeout
  673. return
  674. }
  675. ts: linux.Time_Spec = {
  676. time_sec = uint(timeout / time.Second),
  677. time_nsec = uint(timeout % time.Second),
  678. }
  679. _, errno = linux.rt_sigtimedwait(&sigchld_set, &info, &ts)
  680. #partial switch errno {
  681. case .EAGAIN: // timeout
  682. _process_state_update_times(&process_state)
  683. err = .Timeout
  684. return
  685. case .EINTR:
  686. timeout -= time.tick_since(start_tick)
  687. start_tick = time.tick_now()
  688. case .EINVAL:
  689. return process_state, _get_platform_error(errno)
  690. }
  691. }
  692. return _reap_terminated(process)
  693. }
  694. @(private="package")
  695. _process_wait :: proc(process: Process, timeout: time.Duration) -> (Process_State, Error) {
  696. if timeout > 0 {
  697. if process.handle == PIDFD_UNASSIGNED {
  698. return _timed_wait_on_pid(process, timeout)
  699. } else {
  700. return _timed_wait_on_handle(process, timeout)
  701. }
  702. }
  703. process_state: Process_State = {
  704. pid = process.pid,
  705. }
  706. errno: linux.Errno
  707. options: linux.Wait_Options = {.WEXITED}
  708. if timeout == 0 {
  709. options += {.WNOHANG}
  710. }
  711. info: linux.Sig_Info
  712. errno = .EINTR
  713. for errno == .EINTR {
  714. errno = linux.waitid(.PID, linux.Id(process.pid), &info, options + {.WNOWAIT}, nil)
  715. }
  716. if errno == .EAGAIN || (errno == .NONE && info.signo != .SIGCHLD) {
  717. _process_state_update_times(&process_state)
  718. return process_state, .Timeout
  719. }
  720. if errno != .NONE {
  721. return process_state, _get_platform_error(errno)
  722. }
  723. return _reap_terminated(process)
  724. }
  725. @(private="package")
  726. _process_close :: proc(process: Process) -> Error {
  727. if process.handle == 0 || process.handle == PIDFD_UNASSIGNED {
  728. return nil
  729. }
  730. pidfd := linux.Fd(process.handle)
  731. return _get_platform_error(linux.close(pidfd))
  732. }
  733. @(private="package")
  734. _process_kill :: proc(process: Process) -> Error {
  735. return _get_platform_error(linux.kill(linux.Pid(process.pid), .SIGKILL))
  736. }