process_linux.odin 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. //+private
  2. package os2
  3. import "base:runtime"
  4. import "core:fmt"
  5. import "core:mem"
  6. import "core:time"
  7. import "core:strings"
  8. import "core:strconv"
  9. import "core:sys/linux"
  10. import "core:path/filepath"
  11. _alloc_command_line_arguments :: proc() -> []string {
  12. res := make([]string, len(runtime.args__), heap_allocator())
  13. for arg, i in runtime.args__ {
  14. res[i] = string(arg)
  15. }
  16. return res
  17. }
  18. _exit :: proc "contextless" (code: int) -> ! {
  19. linux.exit_group(i32(code))
  20. }
  21. _get_uid :: proc() -> int {
  22. return int(linux.getuid())
  23. }
  24. _get_euid :: proc() -> int {
  25. return int(linux.geteuid())
  26. }
  27. _get_gid :: proc() -> int {
  28. return int(linux.getgid())
  29. }
  30. _get_egid :: proc() -> int {
  31. return int(linux.getegid())
  32. }
  33. _get_pid :: proc() -> int {
  34. return int(linux.getpid())
  35. }
  36. _get_ppid :: proc() -> int {
  37. return int(linux.getppid())
  38. }
  39. Process_Attributes_OS_Specific :: struct {}
  40. _process_find :: proc(pid: int) -> (Process, Error) {
  41. TEMP_ALLOCATOR_GUARD()
  42. pid_path := fmt.ctprintf("/proc/%d", pid)
  43. p: Process
  44. dir_fd: linux.Fd
  45. errno: linux.Errno
  46. #partial switch dir_fd, errno = linux.open(pid_path, _OPENDIR_FLAGS); errno {
  47. case .NONE:
  48. linux.close(dir_fd)
  49. p.pid = pid
  50. return p, nil
  51. case .ENOTDIR:
  52. return p, .Invalid_Dir
  53. case .ENOENT:
  54. return p, .Not_Exist
  55. }
  56. return p, _get_platform_error(errno)
  57. }
  58. _process_get_state :: proc(p: Process) -> (state: Process_State, err: Error) {
  59. TEMP_ALLOCATOR_GUARD()
  60. stat_name := fmt.ctprintf("/proc/%d/stat", p.pid)
  61. stat_buf: []u8
  62. stat_buf, err = _read_entire_pseudo_file(stat_name, temp_allocator())
  63. if err != nil {
  64. return
  65. }
  66. idx := strings.last_index_byte(string(stat_buf), ')')
  67. stats := string(stat_buf[idx + 2:])
  68. // utime and stime are the 12 and 13th items, respectively
  69. // skip the first 11 items here.
  70. for i := 0; i < 11; i += 1 {
  71. stats = stats[strings.index_byte(stats, ' ') + 1:]
  72. }
  73. idx = strings.index_byte(stats, ' ')
  74. utime_str := stats[:idx]
  75. stats = stats[idx + 1:]
  76. stime_str := stats[:strings.index_byte(stats, ' ')]
  77. utime, _ := strconv.parse_int(utime_str, 10)
  78. stime, _ := strconv.parse_int(stime_str, 10)
  79. // NOTE: Assuming HZ of 100, 1 jiffy == 10 ms
  80. state.user_time = time.Duration(utime) * 10 * time.Millisecond
  81. state.system_time = time.Duration(stime) * 10 * time.Millisecond
  82. return
  83. }
  84. _process_start :: proc(name: string, argv: []string, attr: ^Process_Attributes) -> (child: Process, err: Error) {
  85. TEMP_ALLOCATOR_GUARD()
  86. dir_fd := linux.AT_FDCWD
  87. errno: linux.Errno
  88. if attr != nil && attr.dir != "" {
  89. dir_cstr := temp_cstring(attr.dir) or_return
  90. if dir_fd, errno = linux.open(dir_cstr, _OPENDIR_FLAGS); errno != .NONE {
  91. return child, _get_platform_error(errno)
  92. }
  93. }
  94. // search PATH if just a plain name is provided
  95. executable: cstring
  96. if !strings.contains_rune(name, '/') {
  97. path_env := get_env("PATH", temp_allocator())
  98. path_dirs := filepath.split_list(path_env, temp_allocator())
  99. found: bool
  100. for dir in path_dirs {
  101. executable = fmt.ctprintf("%s/%s", dir, name)
  102. fail: bool
  103. if fail, errno = linux.faccessat(dir_fd, executable, linux.F_OK); errno == .NONE && !fail {
  104. found = true
  105. break
  106. }
  107. }
  108. if !found {
  109. // check in cwd to match windows behavior
  110. executable = fmt.ctprintf("./%s", name)
  111. fail: bool
  112. if fail, errno = linux.faccessat(dir_fd, executable, linux.F_OK); errno != .NONE || fail {
  113. return child, .Not_Exist
  114. }
  115. }
  116. } else {
  117. executable = temp_cstring(name) or_return
  118. }
  119. not_exec: bool
  120. if not_exec, errno = linux.faccessat(dir_fd, executable, linux.F_OK | linux.X_OK); errno != .NONE || not_exec {
  121. return child, errno == .NONE ? .Permission_Denied : _get_platform_error(errno)
  122. }
  123. // args and environment need to be a list of cstrings
  124. // that are terminated by a nil pointer.
  125. // The first argument is a copy of the executable name.
  126. cargs := make([]cstring, len(argv) + 2, temp_allocator())
  127. cargs[0] = executable
  128. for i := 0; i < len(argv); i += 1 {
  129. cargs[i + 1] = temp_cstring(argv[i]) or_return
  130. }
  131. // Use current process's environment if attributes not provided
  132. env: [^]cstring
  133. if attr == nil {
  134. // take this process's current environment
  135. env = raw_data(export_cstring_environment(temp_allocator()))
  136. } else {
  137. cenv := make([]cstring, len(attr.env) + 1, temp_allocator())
  138. for i := 0; i < len(attr.env); i += 1 {
  139. cenv[i] = temp_cstring(attr.env[i]) or_return
  140. }
  141. env = &cenv[0]
  142. }
  143. // TODO: This is the traditional textbook implementation with fork.
  144. // A more efficient implementation with vfork:
  145. //
  146. // 1. retrieve signal handlers
  147. // 2. block all signals
  148. // 3. allocate some stack space
  149. // 4. vfork (waits for child exit or execve); In child:
  150. // a. set child signal handlers
  151. // b. set up any necessary pipes
  152. // c. execve
  153. // 5. restore signal handlers
  154. //
  155. stdin_fds: [2]linux.Fd
  156. stdout_fds: [2]linux.Fd
  157. stderr_fds: [2]linux.Fd
  158. if attr != nil && attr.stdin != nil {
  159. if errno = linux.pipe2(&stdin_fds, nil); errno != .NONE {
  160. return child, _get_platform_error(errno)
  161. }
  162. }
  163. if attr != nil && attr.stdout != nil {
  164. if errno = linux.pipe2(&stdout_fds, nil); errno != .NONE {
  165. return child, _get_platform_error(errno)
  166. }
  167. }
  168. if attr != nil && attr.stderr != nil {
  169. if errno = linux.pipe2(&stderr_fds, nil); errno != .NONE {
  170. return child, _get_platform_error(errno)
  171. }
  172. }
  173. pid: linux.Pid
  174. if pid, errno = linux.fork(); errno != .NONE {
  175. return child, _get_platform_error(errno)
  176. }
  177. IN :: 1
  178. OUT :: 0
  179. STDIN :: linux.Fd(0)
  180. STDOUT :: linux.Fd(1)
  181. STDERR :: linux.Fd(2)
  182. if pid == 0 {
  183. // in child process now
  184. if attr != nil && attr.stdin != nil {
  185. if linux.close(stdin_fds[IN]) != .NONE { linux.exit(1) }
  186. if _, errno = linux.dup2(stdin_fds[OUT], STDIN); errno != .NONE { linux.exit(1) }
  187. if linux.close(stdin_fds[OUT]) != .NONE { linux.exit(1) }
  188. }
  189. if attr != nil && attr.stdout != nil {
  190. if linux.close(stdout_fds[OUT]) != .NONE { linux.exit(1) }
  191. if _, errno = linux.dup2(stdout_fds[IN], STDOUT); errno != .NONE { linux.exit(1) }
  192. if linux.close(stdout_fds[IN]) != .NONE { linux.exit(1) }
  193. }
  194. if attr != nil && attr.stderr != nil {
  195. if linux.close(stderr_fds[OUT]) != .NONE { linux.exit(1) }
  196. if _, errno = linux.dup2(stderr_fds[IN], STDERR); errno != .NONE { linux.exit(1) }
  197. if linux.close(stderr_fds[IN]) != .NONE { linux.exit(1) }
  198. }
  199. if errno = linux.execveat(dir_fd, executable, &cargs[OUT], env); errno != .NONE {
  200. print_error(stderr, _get_platform_error(errno), string(executable))
  201. panic("execve failed to replace process")
  202. }
  203. unreachable()
  204. }
  205. // in parent process
  206. if attr != nil && attr.stdin != nil {
  207. linux.close(stdin_fds[OUT])
  208. _construct_file(attr.stdin, uintptr(stdin_fds[IN]))
  209. }
  210. if attr != nil && attr.stdout != nil {
  211. linux.close(stdout_fds[IN])
  212. _construct_file(attr.stdout, uintptr(stdout_fds[OUT]))
  213. }
  214. if attr != nil && attr.stderr != nil {
  215. linux.close(stderr_fds[IN])
  216. _construct_file(attr.stderr, uintptr(stderr_fds[OUT]))
  217. }
  218. child.pid = int(pid)
  219. return child, nil
  220. }
  221. _process_release :: proc(p: ^Process) -> Error {
  222. // We didn't allocate...
  223. return nil
  224. }
  225. _process_kill :: proc(p: ^Process) -> Error {
  226. res := linux.kill(linux.Pid(p.pid), .SIGKILL)
  227. return _get_platform_error(res)
  228. }
  229. _process_signal :: proc(sig: Signal, h: Signal_Handler) -> Error {
  230. signo: linux.Signal
  231. switch sig {
  232. case .Abort: signo = .SIGABRT
  233. case .Floating_Point_Exception: signo = .SIGFPE
  234. case .Illegal_Instruction: signo = .SIGILL
  235. case .Interrupt: signo = .SIGINT
  236. case .Segmentation_Fault: signo = .SIGSEGV
  237. case .Termination: signo = .SIGTERM
  238. }
  239. sigact: linux.Sig_Action(int)
  240. old: ^linux.Sig_Action(int) = nil
  241. switch v in h {
  242. case Signal_Handler_Special:
  243. switch v {
  244. case .Default:
  245. sigact.special = .SIG_DFL
  246. case .Ignore:
  247. sigact.special = .SIG_IGN
  248. }
  249. case Signal_Handler_Proc:
  250. sigact.handler = (linux.Sig_Handler_Fn)(v)
  251. }
  252. return _get_platform_error(linux.rt_sigaction(signo, &sigact, old))
  253. }
  254. _process_wait :: proc(p: ^Process, t: time.Duration) -> (state: Process_State, err: Error) {
  255. safe_state :: proc(p: Process, state: Process_State = {}) -> (Process_State, Error) {
  256. // process_get_state can fail, so we don't want to return it directly.
  257. if new_state, err := _process_get_state(p); err == nil {
  258. return new_state, nil
  259. }
  260. return state, nil
  261. }
  262. state.pid = p.pid
  263. options: linux.Wait_Options
  264. big_if: if t == 0 {
  265. options += {.WNOHANG}
  266. } else if t != time.MAX_DURATION {
  267. ts: linux.Time_Spec = {
  268. time_sec = uint(t / time.Second),
  269. time_nsec = uint(t % time.Second),
  270. }
  271. @static has_pidfd_open: bool = true
  272. // pidfd_open is fairly new, so don't error out on ENOSYS
  273. pid_fd: linux.Pid_FD
  274. errno: linux.Errno
  275. if has_pidfd_open {
  276. pid_fd, errno = linux.pidfd_open(linux.Pid(p.pid), nil)
  277. if errno != .NONE && errno != .ENOSYS {
  278. return state, _get_platform_error(errno)
  279. }
  280. }
  281. if has_pidfd_open && errno != .ENOSYS {
  282. defer linux.close(linux.Fd(pid_fd))
  283. pollfd: [1]linux.Poll_Fd = {
  284. {
  285. fd = linux.Fd(pid_fd),
  286. events = {.IN},
  287. },
  288. }
  289. for {
  290. n, e := linux.ppoll(pollfd[:], &ts, nil)
  291. if e == .EINTR {
  292. continue
  293. }
  294. if e != .NONE {
  295. return state, _get_platform_error(errno)
  296. }
  297. if n == 0 {
  298. return safe_state(p^, state)
  299. }
  300. break
  301. }
  302. } else {
  303. has_pidfd_open = false
  304. mask: bit_set[0..=63]
  305. mask += { int(linux.Signal.SIGCHLD) - 1 }
  306. org_sigset: linux.Sig_Set
  307. sigset: linux.Sig_Set
  308. mem.copy(&sigset, &mask, size_of(mask))
  309. errno = linux.rt_sigprocmask(.SIG_BLOCK, &sigset, &org_sigset)
  310. if errno != .NONE {
  311. return state, _get_platform_error(errno)
  312. }
  313. defer linux.rt_sigprocmask(.SIG_SETMASK, &org_sigset, nil)
  314. // In case there was a signal handler on SIGCHLD, avoid race
  315. // condition by checking wait first.
  316. options += {.WNOHANG}
  317. waitid_options := options + {.WNOWAIT, .WEXITED}
  318. info: linux.Sig_Info
  319. errno = linux.waitid(.PID, linux.Id(p.pid), &info, waitid_options, nil)
  320. if errno == .NONE && info.code != 0 {
  321. break big_if
  322. }
  323. loop: for {
  324. sigset = {}
  325. mem.copy(&sigset, &mask, size_of(mask))
  326. _, errno = linux.rt_sigtimedwait(&sigset, &info, &ts)
  327. #partial switch errno {
  328. case .EAGAIN: // timeout
  329. return safe_state(p^, state)
  330. case .EINVAL:
  331. return state, _get_platform_error(errno)
  332. case .EINTR:
  333. continue
  334. case:
  335. if int(info.pid) == p.pid {
  336. break loop
  337. }
  338. }
  339. }
  340. }
  341. }
  342. state, _ = safe_state(p^, state)
  343. status: u32
  344. errno: linux.Errno = .EINTR
  345. for errno == .EINTR {
  346. _, errno = linux.wait4(linux.Pid(p.pid), &status, options, nil)
  347. if errno != .NONE {
  348. return state, _get_platform_error(errno)
  349. }
  350. }
  351. // terminated by exit
  352. if linux.WIFEXITED(status) {
  353. p.is_done = true
  354. state.exited = true
  355. state.exit_code = int(linux.WEXITSTATUS(status))
  356. state.success = state.exit_code == 0
  357. return state, nil
  358. }
  359. // terminated by signal
  360. if linux.WIFSIGNALED(status) {
  361. // NOTE: what's the correct behavior here??
  362. p.is_done = true
  363. state.exited = false
  364. state.exit_code = int(linux.WTERMSIG(status))
  365. state.success = false
  366. return state, nil
  367. }
  368. return safe_state(p^, state)
  369. }