process_posix.odin 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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. posix.exit(126)
  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, _ = _process_open(int(pid), {})
  187. return
  188. }
  189. }
  190. _process_wait :: proc(process: Process, timeout: time.Duration) -> (process_state: Process_State, err: Error) {
  191. process_state.pid = process.pid
  192. _process_handle_still_valid(process) or_return
  193. // timeout > 0 = use kqueue to wait (with a timeout) on process exit
  194. // timeout == 0 = use waitid with WNOHANG so it returns immediately
  195. // timeout > 0 = use waitid without WNOHANG so it waits indefinitely
  196. //
  197. // at the end use waitid to actually reap the process and get it's status
  198. if timeout > 0 {
  199. timeout := timeout
  200. queue := kq.kqueue() or_return
  201. defer posix.close(queue)
  202. changelist, eventlist: [1]kq.KEvent
  203. changelist[0] = {
  204. ident = uintptr(process.pid),
  205. filter = .Proc,
  206. flags = { .Add },
  207. fflags = {
  208. fproc = { .Exit },
  209. },
  210. }
  211. for {
  212. start := time.tick_now()
  213. n, kerr := kq.kevent(queue, changelist[:], eventlist[:], &{
  214. tv_sec = posix.time_t(timeout / time.Second),
  215. tv_nsec = i64(timeout % time.Second),
  216. })
  217. if kerr == .EINTR {
  218. timeout -= time.tick_since(start)
  219. continue
  220. } else if kerr != nil {
  221. err = kerr
  222. return
  223. } else if n == 0 {
  224. err = .Timeout
  225. _process_state_update_times(process, &process_state)
  226. return
  227. } else {
  228. _process_state_update_times(process, &process_state)
  229. break
  230. }
  231. }
  232. } else {
  233. flags := posix.Wait_Flags{.EXITED, .NOWAIT}
  234. if timeout == 0 {
  235. flags += {.NOHANG}
  236. }
  237. info: posix.siginfo_t
  238. for {
  239. wpid := posix.waitid(.P_PID, posix.id_t(process.pid), &info, flags)
  240. if wpid == -1 {
  241. if errno := posix.errno(); errno == .EINTR {
  242. continue
  243. } else {
  244. err = _get_platform_error()
  245. return
  246. }
  247. }
  248. break
  249. }
  250. _process_state_update_times(process, &process_state)
  251. if info.si_signo == nil {
  252. assert(timeout == 0)
  253. err = .Timeout
  254. return
  255. }
  256. }
  257. info: posix.siginfo_t
  258. for {
  259. wpid := posix.waitid(.P_PID, posix.id_t(process.pid), &info, {.EXITED})
  260. if wpid == -1 {
  261. if errno := posix.errno(); errno == .EINTR {
  262. continue
  263. } else {
  264. err = _get_platform_error()
  265. return
  266. }
  267. }
  268. break
  269. }
  270. switch info.si_code.chld {
  271. case: unreachable()
  272. case .CONTINUED, .STOPPED: unreachable()
  273. case .EXITED:
  274. process_state.exited = true
  275. process_state.exit_code = int(info.si_status)
  276. process_state.success = process_state.exit_code == 0
  277. case .KILLED, .DUMPED, .TRAPPED:
  278. process_state.exited = true
  279. process_state.exit_code = int(info.si_status)
  280. process_state.success = false
  281. }
  282. return
  283. }
  284. _process_close :: proc(process: Process) -> Error {
  285. return nil
  286. }
  287. _process_kill :: proc(process: Process) -> (err: Error) {
  288. _process_handle_still_valid(process) or_return
  289. if posix.kill(posix.pid_t(process.pid), .SIGKILL) != .OK {
  290. err = _get_platform_error()
  291. }
  292. return
  293. }