process_posix.odin 8.1 KB

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