process_posix.odin 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. #+private
  2. #+build darwin, netbsd, freebsd, openbsd
  3. package os2
  4. import "base:runtime"
  5. import "core:time"
  6. import "core:strings"
  7. import kq "core:sys/kqueue"
  8. import "core:sys/posix"
  9. _exit :: proc "contextless" (code: int) -> ! {
  10. posix.exit(i32(code))
  11. }
  12. _get_uid :: proc() -> int {
  13. return int(posix.getuid())
  14. }
  15. _get_euid :: proc() -> int {
  16. return int(posix.geteuid())
  17. }
  18. _get_gid :: proc() -> int {
  19. return int(posix.getgid())
  20. }
  21. _get_egid :: proc() -> int {
  22. return int(posix.getegid())
  23. }
  24. _get_pid :: proc() -> int {
  25. return int(posix.getpid())
  26. }
  27. _get_ppid :: proc() -> int {
  28. return int(posix.getppid())
  29. }
  30. _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
  31. return _process_info_by_pid(process.pid, selection, allocator)
  32. }
  33. _current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
  34. return _process_info_by_pid(_get_pid(), selection, allocator)
  35. }
  36. _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
  37. if len(desc.command) == 0 {
  38. err = .Invalid_Path
  39. return
  40. }
  41. temp_allocator := TEMP_ALLOCATOR_GUARD({})
  42. // search PATH if just a plain name is provided.
  43. exe_builder := strings.builder_make(temp_allocator)
  44. exe_name := desc.command[0]
  45. if strings.index_byte(exe_name, '/') < 0 {
  46. path_env := get_env("PATH", temp_allocator)
  47. path_dirs := split_path_list(path_env, temp_allocator) or_return
  48. found: bool
  49. for dir in path_dirs {
  50. strings.builder_reset(&exe_builder)
  51. strings.write_string(&exe_builder, dir)
  52. strings.write_byte(&exe_builder, '/')
  53. strings.write_string(&exe_builder, exe_name)
  54. if exe_fd := posix.open(strings.to_cstring(&exe_builder) or_return, {.CLOEXEC, .EXEC}); exe_fd == -1 {
  55. continue
  56. } else {
  57. posix.close(exe_fd)
  58. found = true
  59. break
  60. }
  61. }
  62. if !found {
  63. // check in cwd to match windows behavior
  64. strings.builder_reset(&exe_builder)
  65. strings.write_string(&exe_builder, desc.working_dir)
  66. if len(desc.working_dir) > 0 && desc.working_dir[len(desc.working_dir)-1] != '/' {
  67. strings.write_byte(&exe_builder, '/')
  68. }
  69. strings.write_string(&exe_builder, "./")
  70. strings.write_string(&exe_builder, exe_name)
  71. // "hello/./world" is fine right?
  72. if exe_fd := posix.open(strings.to_cstring(&exe_builder) or_return, {.CLOEXEC, .EXEC}); exe_fd == -1 {
  73. err = .Not_Exist
  74. return
  75. } else {
  76. posix.close(exe_fd)
  77. }
  78. }
  79. } else {
  80. strings.builder_reset(&exe_builder)
  81. strings.write_string(&exe_builder, exe_name)
  82. if exe_fd := posix.open(strings.to_cstring(&exe_builder) or_return, {.CLOEXEC, .EXEC}); exe_fd == -1 {
  83. err = .Not_Exist
  84. return
  85. } else {
  86. posix.close(exe_fd)
  87. }
  88. }
  89. cwd: cstring; if desc.working_dir != "" {
  90. cwd = clone_to_cstring(desc.working_dir, temp_allocator) or_return
  91. }
  92. cmd := make([]cstring, len(desc.command) + 1, temp_allocator)
  93. for part, i in desc.command {
  94. cmd[i] = clone_to_cstring(part, temp_allocator) or_return
  95. }
  96. env: [^]cstring
  97. if desc.env == nil {
  98. // take this process's current environment
  99. env = posix.environ
  100. } else {
  101. cenv := make([]cstring, len(desc.env) + 1, temp_allocator)
  102. for env, i in desc.env {
  103. cenv[i] = clone_to_cstring(env, temp_allocator) or_return
  104. }
  105. env = raw_data(cenv)
  106. }
  107. READ :: 0
  108. WRITE :: 1
  109. pipe: [2]posix.FD
  110. if posix.pipe(&pipe) != .OK {
  111. err = _get_platform_error()
  112. return
  113. }
  114. defer posix.close(pipe[READ])
  115. if posix.fcntl(pipe[READ], .SETFD, i32(posix.FD_CLOEXEC)) == -1 {
  116. posix.close(pipe[WRITE])
  117. err = _get_platform_error()
  118. return
  119. }
  120. if posix.fcntl(pipe[WRITE], .SETFD, i32(posix.FD_CLOEXEC)) == -1 {
  121. posix.close(pipe[WRITE])
  122. err = _get_platform_error()
  123. return
  124. }
  125. switch pid := posix.fork(); pid {
  126. case -1:
  127. posix.close(pipe[WRITE])
  128. err = _get_platform_error()
  129. return
  130. case 0:
  131. abort :: proc(parent_fd: posix.FD) -> ! {
  132. #assert(len(posix.Errno) < max(u8))
  133. errno := u8(posix.errno())
  134. posix.write(parent_fd, &errno, 1)
  135. posix.exit(126)
  136. }
  137. null := posix.open("/dev/null", {.RDWR})
  138. if null == -1 { abort(pipe[WRITE]) }
  139. stderr := (^File_Impl)(desc.stderr.impl).fd if desc.stderr != nil else null
  140. stdout := (^File_Impl)(desc.stdout.impl).fd if desc.stdout != nil else null
  141. stdin := (^File_Impl)(desc.stdin.impl).fd if desc.stdin != nil else null
  142. if posix.dup2(stderr, posix.STDERR_FILENO) == -1 { abort(pipe[WRITE]) }
  143. if posix.dup2(stdout, posix.STDOUT_FILENO) == -1 { abort(pipe[WRITE]) }
  144. if posix.dup2(stdin, posix.STDIN_FILENO ) == -1 { abort(pipe[WRITE]) }
  145. if cwd != nil {
  146. if posix.chdir(cwd) != .OK { abort(pipe[WRITE]) }
  147. }
  148. res := posix.execve(strings.to_cstring(&exe_builder) or_return, raw_data(cmd), env)
  149. assert(res == -1)
  150. abort(pipe[WRITE])
  151. case:
  152. posix.close(pipe[WRITE])
  153. errno: posix.Errno
  154. for {
  155. errno_byte: u8
  156. switch posix.read(pipe[READ], &errno_byte, 1) {
  157. case 1:
  158. errno = posix.Errno(errno_byte)
  159. case -1:
  160. errno = posix.errno()
  161. if errno == .EINTR {
  162. continue
  163. } else {
  164. // If the read failed, something weird happened. Do not return the read
  165. // error so the user knows to wait on it.
  166. errno = nil
  167. }
  168. }
  169. break
  170. }
  171. if errno != nil {
  172. // We can assume it trapped here.
  173. for {
  174. info: posix.siginfo_t
  175. wpid := posix.waitid(.P_PID, posix.id_t(process.pid), &info, {.EXITED})
  176. if wpid == -1 && posix.errno() == .EINTR {
  177. continue
  178. }
  179. break
  180. }
  181. err = errno
  182. return
  183. }
  184. process, _ = _process_open(int(pid), {})
  185. return
  186. }
  187. }
  188. _process_wait :: proc(process: Process, timeout: time.Duration) -> (process_state: Process_State, err: Error) {
  189. process_state.pid = process.pid
  190. _process_handle_still_valid(process) or_return
  191. // timeout > 0 = use kqueue to wait (with a timeout) on process exit
  192. // timeout == 0 = use waitid with WNOHANG so it returns immediately
  193. // timeout > 0 = use waitid without WNOHANG so it waits indefinitely
  194. //
  195. // at the end use waitid to actually reap the process and get it's status
  196. if timeout > 0 {
  197. timeout := timeout
  198. queue := kq.kqueue() or_return
  199. defer posix.close(queue)
  200. changelist, eventlist: [1]kq.KEvent
  201. changelist[0] = {
  202. ident = uintptr(process.pid),
  203. filter = .Proc,
  204. flags = { .Add },
  205. fflags = {
  206. fproc = { .Exit },
  207. },
  208. }
  209. for {
  210. start := time.tick_now()
  211. n, kerr := kq.kevent(queue, changelist[:], eventlist[:], &{
  212. tv_sec = posix.time_t(timeout / time.Second),
  213. tv_nsec = i64(timeout % time.Second),
  214. })
  215. if kerr == .EINTR {
  216. timeout -= time.tick_since(start)
  217. continue
  218. } else if kerr != nil {
  219. err = kerr
  220. return
  221. } else if n == 0 {
  222. err = .Timeout
  223. _process_state_update_times(process, &process_state)
  224. return
  225. } else {
  226. _process_state_update_times(process, &process_state)
  227. break
  228. }
  229. }
  230. } else {
  231. flags := posix.Wait_Flags{.EXITED, .NOWAIT}
  232. if timeout == 0 {
  233. flags += {.NOHANG}
  234. }
  235. info: posix.siginfo_t
  236. for {
  237. wpid := posix.waitid(.P_PID, posix.id_t(process.pid), &info, flags)
  238. if wpid == -1 {
  239. if errno := posix.errno(); errno == .EINTR {
  240. continue
  241. } else {
  242. err = _get_platform_error()
  243. return
  244. }
  245. }
  246. break
  247. }
  248. _process_state_update_times(process, &process_state)
  249. if info.si_signo == nil {
  250. assert(timeout == 0)
  251. err = .Timeout
  252. return
  253. }
  254. }
  255. info: posix.siginfo_t
  256. for {
  257. wpid := posix.waitid(.P_PID, posix.id_t(process.pid), &info, {.EXITED})
  258. if wpid == -1 {
  259. if errno := posix.errno(); errno == .EINTR {
  260. continue
  261. } else {
  262. err = _get_platform_error()
  263. return
  264. }
  265. }
  266. break
  267. }
  268. switch info.si_code.chld {
  269. case: unreachable()
  270. case .CONTINUED, .STOPPED: unreachable()
  271. case .EXITED:
  272. process_state.exited = true
  273. process_state.exit_code = int(info.si_status)
  274. process_state.success = process_state.exit_code == 0
  275. case .KILLED, .DUMPED, .TRAPPED:
  276. process_state.exited = true
  277. process_state.exit_code = int(info.si_status)
  278. process_state.success = false
  279. }
  280. return
  281. }
  282. _process_close :: proc(process: Process) -> Error {
  283. return nil
  284. }
  285. _process_kill :: proc(process: Process) -> (err: Error) {
  286. _process_handle_still_valid(process) or_return
  287. if posix.kill(posix.pid_t(process.pid), .SIGKILL) != .OK {
  288. err = _get_platform_error()
  289. }
  290. return
  291. }