From 2a7db08c20811139c9410b65499d27eca47e793c Mon Sep 17 00:00:00 2001 From: jason Date: Sun, 4 Aug 2024 00:59:40 -0400 Subject: Remove returned bool from access and faccessat in sys/linux. Switch to using AT_EMPTY_PATH to execve with file descriptors. --- core/sys/linux/sys.odin | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'core/sys/linux') diff --git a/core/sys/linux/sys.odin b/core/sys/linux/sys.odin index ec7357c48..d756f0ece 100644 --- a/core/sys/linux/sys.odin +++ b/core/sys/linux/sys.odin @@ -279,13 +279,13 @@ writev :: proc "contextless" (fd: Fd, iov: []IO_Vec) -> (int, Errno) { Available since Linux 1.0. For ARM64 available since Linux 2.6.16. */ -access :: proc "contextless" (name: cstring, mode: Mode = F_OK) -> (bool, Errno) { +access :: proc "contextless" (name: cstring, mode: Mode = F_OK) -> (Errno) { when ODIN_ARCH == .arm64 { ret := syscall(SYS_faccessat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode) - return errno_unwrap(ret, bool) + return Errno(-ret) } else { ret := syscall(SYS_access, cast(rawptr) name, transmute(u32) mode) - return errno_unwrap(ret, bool) + return Errno(-ret) } } @@ -2616,9 +2616,9 @@ fchmodat :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode, flags: FD_ Checks the user permissions for a file at specified dirfd. Available since Linux 2.6.16. */ -faccessat :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode = F_OK) -> (bool, Errno) { +faccessat :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode = F_OK) -> (Errno) { ret := syscall(SYS_faccessat, dirfd, cast(rawptr) name, transmute(u32) mode) - return errno_unwrap(ret, bool) + return Errno(-ret) } /* @@ -2916,9 +2916,9 @@ pidfd_getfd :: proc "contextless" (pidfd: Pid_FD, fd: Fd, flags: i32 = 0) -> (Fd Checks the user permissions for a file at specified dirfd (with flags). Available since Linux 5.8. */ -faccessat2 :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode = F_OK, flags: FD_Flags = FD_Flags{}) -> (bool, Errno) { +faccessat2 :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode = F_OK, flags: FD_Flags = FD_Flags{}) -> (Errno) { ret := syscall(SYS_faccessat2, dirfd, cast(rawptr) name, transmute(u32) mode, transmute(i32) flags) - return errno_unwrap(ret, bool) + return Errno(-ret) } // TODO(flysand): process_madvise -- cgit v1.2.3 From c4d43bbab081ae1480be8f07f88572d8315bd94a Mon Sep 17 00:00:00 2001 From: jason Date: Fri, 9 Aug 2024 09:16:44 -0400 Subject: os2 linux process_wait rework; add Sig_Child_Code to sys/linux bits --- core/os/os2/process_linux.odin | 243 ++++++++++++++++++++++++++--------------- core/sys/linux/bits.odin | 14 +++ 2 files changed, 167 insertions(+), 90 deletions(-) (limited to 'core/sys/linux') diff --git a/core/os/os2/process_linux.odin b/core/os/os2/process_linux.odin index 346497289..0fa96a5a5 100644 --- a/core/os/os2/process_linux.odin +++ b/core/os/os2/process_linux.odin @@ -6,7 +6,6 @@ import "base:runtime" import "base:intrinsics" import "core:fmt" -import "core:mem" import "core:time" import "core:slice" import "core:strings" @@ -504,11 +503,11 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) { return } -_process_state_update_times :: proc(p: Process, state: ^Process_State) -> (err: Error) { +_process_state_update_times :: proc(state: ^Process_State) -> (err: Error) { TEMP_ALLOCATOR_GUARD() stat_path_buf: [32]u8 - _ = fmt.bprintf(stat_path_buf[:], "/proc/%d/stat", p.pid) + _ = fmt.bprintf(stat_path_buf[:], "/proc/%d/stat", state.pid) stat_buf: []u8 stat_buf, err = _read_entire_pseudo_file(cstring(&stat_path_buf[0]), temp_allocator()) if err != nil { @@ -547,115 +546,179 @@ _process_state_update_times :: proc(p: Process, state: ^Process_State) -> (err: return } -@(private="package") -_process_wait :: proc(process: Process, timeout: time.Duration) -> (process_state: Process_State, err: Error) { +_reap_terminated :: proc(process: Process) -> (state: Process_State, err: Error) { + state.pid = process.pid + _process_state_update_times(&state) + + info: linux.Sig_Info + errno := linux.Errno.EINTR + for errno == .EINTR { + errno = linux.waitid(.PID, linux.Id(process.pid), &info, {.WEXITED}, nil) + } + err = _get_platform_error(errno) + + switch linux.Sig_Child_Code(info.code) { + case .NONE, .CONTINUED, .STOPPED: + case .EXITED: + state.exited = true + state.exit_code = int(info.status) + state.success = state.exit_code == 0 + case .KILLED, .DUMPED, .TRAPPED: + state.exited = true + state.exit_code = int(info.status) + state.success = false + } + return +} + +_timed_wait_on_handle :: proc(process: Process, timeout: time.Duration) -> (process_state: Process_State, err: Error) { + timeout := timeout + process_state.pid = process.pid + pidfd := linux.Fd(process.handle) + pollfd: [1]linux.Poll_Fd = { + { + fd = pidfd, + events = {.IN}, + }, + } + + start_tick := time.tick_now() + + mask: bit_set[0..<64; u64] + mask += { int(linux.Signal.SIGCHLD) - 1 } + sigchld_set := transmute(linux.Sig_Set)(mask) + + info: linux.Sig_Info + for { + if timeout <= 0 { + _process_state_update_times(&process_state) + return + } - errno: linux.Errno - options: linux.Wait_Options - big_if: if timeout == 0 { - options += {.WNOHANG} - } else if timeout > 0 { ts: linux.Time_Spec = { time_sec = uint(timeout / time.Second), time_nsec = uint(timeout % time.Second), } - if false {//process.handle != PIDFD_UNASSIGNED { - pollfd: [1]linux.Poll_Fd = { - { - fd = linux.Fd(process.handle), - events = {.IN}, - }, + n, errno := linux.ppoll(pollfd[:], &ts, &sigchld_set) + if errno != .NONE { + if errno == .EINTR { + new_tick := time.tick_now() + timeout -= time.tick_diff(start_tick, new_tick) + start_tick = new_tick + continue } + return process_state, _get_platform_error(errno) + } - for { - n, e := linux.ppoll(pollfd[:], &ts, nil) - if e == .EINTR { - continue - } - if e != .NONE { - return process_state, _get_platform_error(errno) - } - if n == 0 { - _process_state_update_times(process, &process_state) - return - } - break - } - } else { - mask: bit_set[0..<64; u64] - mask += { int(linux.Signal.SIGCHLD) - 1 } + if n == 0 { // timeout with no events + _process_state_update_times(&process_state) + return + } - org_sigset: linux.Sig_Set - sigset: linux.Sig_Set - mem.copy(&sigset, &mask, size_of(mask)) - errno = linux.rt_sigprocmask(.SIG_BLOCK, &sigset, &org_sigset) - if errno != .NONE { - return process_state, _get_platform_error(errno) - } - defer linux.rt_sigprocmask(.SIG_SETMASK, &org_sigset, nil) - - // In case there was a signal handler on SIGCHLD, avoid race - // condition by checking wait first. - options += {.WNOHANG} - waitid_options := options + {.WNOWAIT, .WEXITED} - info: linux.Sig_Info - errno = linux.waitid(.PID, linux.Id(process.pid), &info, waitid_options, nil) - if errno == .NONE && info.code != 0 { - break big_if - } + // This throws EBADF with pidfd + if errno = linux.waitid(.PID, linux.Id(process.pid), &info, {.WEXITED, .WNOHANG, .WNOWAIT}, nil); errno != .NONE { + return process_state, _get_platform_error(errno) + } - loop: for { - sigset = {} - mem.copy(&sigset, &mask, size_of(mask)) - - _, errno = linux.rt_sigtimedwait(&sigset, &info, &ts) - #partial switch errno { - case .EAGAIN: // timeout - _process_state_update_times(process, &process_state) - return - case .EINVAL: - return process_state, _get_platform_error(errno) - case .EINTR: - continue - case: - if info.pid == linux.Pid(process.pid) { - break loop - } - } - } + if info.signo == .SIGCHLD { + break } + + new_tick := time.tick_now() + timeout -= time.tick_diff(start_tick, new_tick) + start_tick = new_tick } - status: u32 - errno = .EINTR - for errno == .EINTR { - _, errno = linux.wait4(linux.Pid(process.pid), &status, options, nil) - if errno != .NONE { - _process_state_update_times(process, &process_state) + return _reap_terminated(process) +} + +_timed_wait_on_pid :: proc(process: Process, timeout: time.Duration) -> (process_state: Process_State, err: Error) { + timeout := timeout + process_state.pid = process.pid + + mask: bit_set[0..<64; u64] + mask += { int(linux.Signal.SIGCHLD) - 1 } + sigchld_set := transmute(linux.Sig_Set)(mask) + + start_tick := time.tick_now() + + org_sigset: linux.Sig_Set + errno := linux.rt_sigprocmask(.SIG_BLOCK, &sigchld_set, &org_sigset) + if errno != .NONE { + return process_state, _get_platform_error(errno) + } + defer linux.rt_sigprocmask(.SIG_SETMASK, &org_sigset, nil) + + // In case there was a signal handler on SIGCHLD, avoid race + // condition by checking wait first. + info: linux.Sig_Info + errno = linux.waitid(.PID, linux.Id(process.pid), &info, {.WNOWAIT, .WEXITED, .WNOHANG}, nil) + + for errno != .NONE || info.code == 0 || info.pid != linux.Pid(process.pid) { + if timeout <= 0 { + _process_state_update_times(&process_state) + return + } + + ts: linux.Time_Spec = { + time_sec = uint(timeout / time.Second), + time_nsec = uint(timeout % time.Second), + } + + _, errno = linux.rt_sigtimedwait(&sigchld_set, &info, &ts) + #partial switch errno { + case .EAGAIN: // timeout + _process_state_update_times(&process_state) + return + case .EINTR: + new_tick := time.tick_now() + timeout -= time.tick_diff(start_tick, new_tick) + start_tick = new_tick + case .EINVAL: return process_state, _get_platform_error(errno) } } - _process_state_update_times(process, &process_state) + return _reap_terminated(process) +} - // terminated by exit - if linux.WIFEXITED(status) { - process_state.exited = true - process_state.exit_code = int(linux.WEXITSTATUS(status)) - process_state.success = process_state.exit_code == 0 - return +@(private="package") +_process_wait :: proc(process: Process, timeout: time.Duration) -> (Process_State, Error) { + if timeout > 0 { + if process.handle == PIDFD_UNASSIGNED { + return _timed_wait_on_pid(process, timeout) + } else { + return _timed_wait_on_handle(process, timeout) + } } - // terminated by signal - if linux.WIFSIGNALED(status) { - process_state.exited = false - process_state.exit_code = int(linux.WTERMSIG(status)) - process_state.success = false - return + process_state: Process_State = { + pid = process.pid, } - return + + errno: linux.Errno + options: linux.Wait_Options = {.WEXITED} + if timeout == 0 { + options += {.WNOHANG} + } + + info: linux.Sig_Info + + errno = .EINTR + for errno == .EINTR { + errno = linux.waitid(.PID, linux.Id(process.pid), &info, options + {.WNOWAIT}, nil) + } + if errno == .EAGAIN || (errno == .NONE && info.signo != .SIGCHLD) { + _process_state_update_times(&process_state) + return process_state, nil + } + if errno != .NONE { + return process_state, _get_platform_error(errno) + } + + return _reap_terminated(process) } @(private="package") diff --git a/core/sys/linux/bits.odin b/core/sys/linux/bits.odin index e10edf558..b8ec3c133 100644 --- a/core/sys/linux/bits.odin +++ b/core/sys/linux/bits.odin @@ -983,6 +983,20 @@ Sig_Action_Flag :: enum u32 { RESETHAND = 31, } +/* + Translation of code in Sig_Info for when signo is SIGCHLD +*/ +Sig_Child_Code :: enum { + NONE, + EXITED, + KILLED, + DUMPED, + TRAPPED, + STOPPED, + CONTINUED, +} + + /* Type of socket to create - For TCP you want to use SOCK_STREAM -- cgit v1.2.3