123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- //+private
- //+build darwin, netbsd, freebsd, openbsd
- package os2
- import "base:runtime"
- import "core:time"
- import "core:strings"
- import "core:path/filepath"
- import kq "core:sys/kqueue"
- import "core:sys/posix"
- _exit :: proc "contextless" (code: int) -> ! {
- posix.exit(i32(code))
- }
- _get_uid :: proc() -> int {
- return int(posix.getuid())
- }
- _get_euid :: proc() -> int {
- return int(posix.geteuid())
- }
- _get_gid :: proc() -> int {
- return int(posix.getgid())
- }
- _get_egid :: proc() -> int {
- return int(posix.getegid())
- }
- _get_pid :: proc() -> int {
- return int(posix.getpid())
- }
- _get_ppid :: proc() -> int {
- return int(posix.getppid())
- }
- _process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
- return _process_info_by_pid(process.pid, selection, allocator)
- }
- _current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
- return _process_info_by_pid(_get_pid(), selection, allocator)
- }
- _Sys_Process_Attributes :: struct {}
- _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
- if len(desc.command) == 0 {
- err = .Invalid_Path
- return
- }
- TEMP_ALLOCATOR_GUARD()
- // search PATH if just a plain name is provided.
- exe_builder := strings.builder_make(temp_allocator())
- exe_name := desc.command[0]
- if strings.index_byte(exe_name, '/') < 0 {
- path_env := get_env("PATH", temp_allocator())
- path_dirs := filepath.split_list(path_env, temp_allocator())
- found: bool
- for dir in path_dirs {
- strings.builder_reset(&exe_builder)
- strings.write_string(&exe_builder, dir)
- strings.write_byte(&exe_builder, '/')
- strings.write_string(&exe_builder, exe_name)
- if exe_fd := posix.open(strings.to_cstring(&exe_builder), {.CLOEXEC, .EXEC}); exe_fd == -1 {
- continue
- } else {
- posix.close(exe_fd)
- found = true
- break
- }
- }
- if !found {
- // check in cwd to match windows behavior
- strings.builder_reset(&exe_builder)
- strings.write_string(&exe_builder, desc.working_dir)
- if len(desc.working_dir) > 0 && desc.working_dir[len(desc.working_dir)-1] != '/' {
- strings.write_byte(&exe_builder, '/')
- }
- strings.write_string(&exe_builder, "./")
- strings.write_string(&exe_builder, exe_name)
- // "hello/./world" is fine right?
- if exe_fd := posix.open(strings.to_cstring(&exe_builder), {.CLOEXEC, .EXEC}); exe_fd == -1 {
- err = .Not_Exist
- return
- } else {
- posix.close(exe_fd)
- }
- }
- } else {
- strings.builder_reset(&exe_builder)
- strings.write_string(&exe_builder, exe_name)
- if exe_fd := posix.open(strings.to_cstring(&exe_builder), {.CLOEXEC, .EXEC}); exe_fd == -1 {
- err = .Not_Exist
- return
- } else {
- posix.close(exe_fd)
- }
- }
- cwd: cstring; if desc.working_dir != "" {
- cwd = temp_cstring(desc.working_dir)
- }
- cmd := make([]cstring, len(desc.command) + 1, temp_allocator())
- for part, i in desc.command {
- cmd[i] = temp_cstring(part)
- }
- env: [^]cstring
- if desc.env == nil {
- // take this process's current environment
- env = posix.environ
- } else {
- cenv := make([]cstring, len(desc.env) + 1, temp_allocator())
- for env, i in desc.env {
- cenv[i] = temp_cstring(env)
- }
- env = raw_data(cenv)
- }
- READ :: 0
- WRITE :: 1
- pipe: [2]posix.FD
- if posix.pipe(&pipe) != .OK {
- err = _get_platform_error()
- return
- }
- defer posix.close(pipe[READ])
- if posix.fcntl(pipe[READ], .SETFD, i32(posix.FD_CLOEXEC)) == -1 {
- posix.close(pipe[WRITE])
- err = _get_platform_error()
- return
- }
- if posix.fcntl(pipe[WRITE], .SETFD, i32(posix.FD_CLOEXEC)) == -1 {
- posix.close(pipe[WRITE])
- err = _get_platform_error()
- return
- }
- switch pid := posix.fork(); pid {
- case -1:
- posix.close(pipe[WRITE])
- err = _get_platform_error()
- return
- case 0:
- abort :: proc(parent_fd: posix.FD) -> ! {
- #assert(len(posix.Errno) < max(u8))
- errno := u8(posix.errno())
- posix.write(parent_fd, &errno, 1)
- runtime.trap()
- }
- null := posix.open("/dev/null", {.RDWR})
- if null == -1 { abort(pipe[WRITE]) }
- stderr := (^File_Impl)(desc.stderr.impl).fd if desc.stderr != nil else null
- stdout := (^File_Impl)(desc.stdout.impl).fd if desc.stdout != nil else null
- stdin := (^File_Impl)(desc.stdin.impl).fd if desc.stdin != nil else null
- if posix.dup2(stderr, posix.STDERR_FILENO) == -1 { abort(pipe[WRITE]) }
- if posix.dup2(stdout, posix.STDOUT_FILENO) == -1 { abort(pipe[WRITE]) }
- if posix.dup2(stdin, posix.STDIN_FILENO ) == -1 { abort(pipe[WRITE]) }
- if cwd != nil {
- if posix.chdir(cwd) != .OK { abort(pipe[WRITE]) }
- }
- res := posix.execve(strings.to_cstring(&exe_builder), raw_data(cmd), env)
- assert(res == -1)
- abort(pipe[WRITE])
- case:
- posix.close(pipe[WRITE])
- errno: posix.Errno
- for {
- errno_byte: u8
- switch posix.read(pipe[READ], &errno_byte, 1) {
- case 1:
- errno = posix.Errno(errno_byte)
- case -1:
- errno = posix.errno()
- if errno == .EINTR {
- continue
- } else {
- // If the read failed, something weird happened. Do not return the read
- // error so the user knows to wait on it.
- errno = nil
- }
- }
- break
- }
- if errno != nil {
- // We can assume it trapped here.
- for {
- info: posix.siginfo_t
- wpid := posix.waitid(.P_PID, posix.id_t(process.pid), &info, {.EXITED})
- if wpid == -1 && posix.errno() == .EINTR {
- continue
- }
- break
- }
- err = errno
- return
- }
- process.pid = int(pid)
- process, _ = _process_open(int(pid), {})
- return
- }
- }
- _process_wait :: proc(process: Process, timeout: time.Duration) -> (process_state: Process_State, err: Error) {
- process_state.pid = process.pid
- _process_handle_still_valid(process) or_return
- // timeout > 0 = use kqueue to wait (with a timeout) on process exit
- // timeout == 0 = use waitid with WNOHANG so it returns immediately
- // timeout > 0 = use waitid without WNOHANG so it waits indefinitely
- //
- // at the end use waitid to actually reap the process and get it's status
- if timeout > 0 {
- timeout := timeout
- queue := kq.kqueue() or_return
- defer posix.close(queue)
- changelist, eventlist: [1]kq.KEvent
- changelist[0] = {
- ident = uintptr(process.pid),
- filter = .Proc,
- flags = { .Add },
- fflags = {
- fproc = { .Exit },
- },
- }
- for {
- start := time.tick_now()
- n, kerr := kq.kevent(queue, changelist[:], eventlist[:], &{
- tv_sec = posix.time_t(timeout / time.Second),
- tv_nsec = i64(timeout % time.Second),
- })
- if kerr == .EINTR {
- timeout -= time.tick_since(start)
- continue
- } else if kerr != nil {
- err = kerr
- return
- } else if n == 0 {
- err = .Timeout
- _process_state_update_times(process, &process_state)
- return
- } else {
- _process_state_update_times(process, &process_state)
- break
- }
- }
- } else {
- flags := posix.Wait_Flags{.EXITED, .NOWAIT}
- if timeout == 0 {
- flags += {.NOHANG}
- }
- info: posix.siginfo_t
- for {
- wpid := posix.waitid(.P_PID, posix.id_t(process.pid), &info, flags)
- if wpid == -1 {
- if errno := posix.errno(); errno == .EINTR {
- continue
- } else {
- err = _get_platform_error()
- return
- }
- }
- break
- }
- _process_state_update_times(process, &process_state)
- if info.si_signo == nil {
- assert(timeout == 0)
- err = .Timeout
- return
- }
- }
- info: posix.siginfo_t
- for {
- wpid := posix.waitid(.P_PID, posix.id_t(process.pid), &info, {.EXITED})
- if wpid == -1 {
- if errno := posix.errno(); errno == .EINTR {
- continue
- } else {
- err = _get_platform_error()
- return
- }
- }
- break
- }
- switch info.si_code.chld {
- case: unreachable()
- case .CONTINUED, .STOPPED: unreachable()
- case .EXITED:
- process_state.exited = true
- process_state.exit_code = int(info.si_status)
- process_state.success = process_state.exit_code == 0
- case .KILLED, .DUMPED, .TRAPPED:
- process_state.exited = true
- process_state.exit_code = int(info.si_status)
- process_state.success = false
- }
- return
- }
- _process_close :: proc(process: Process) -> Error {
- return nil
- }
- _process_kill :: proc(process: Process) -> (err: Error) {
- _process_handle_still_valid(process) or_return
- if posix.kill(posix.pid_t(process.pid), .SIGKILL) != .OK {
- err = _get_platform_error()
- }
- return
- }
|