From 6a894195cbcf2caab2d84d75cc33f933e2390774 Mon Sep 17 00:00:00 2001 From: jason Date: Fri, 28 Jun 2024 09:45:22 -0400 Subject: [PATCH] revert os2/process --- core/os/os2/process.odin | 88 +++---- core/os/os2/process_linux.odin | 428 ------------------------------- core/os/os2/process_windows.odin | 67 ----- 3 files changed, 35 insertions(+), 548 deletions(-) delete mode 100644 core/os/os2/process_linux.odin delete mode 100644 core/os/os2/process_windows.odin diff --git a/core/os/os2/process.odin b/core/os/os2/process.odin index 099d1c19e..862434b7b 100644 --- a/core/os/os2/process.odin +++ b/core/os/os2/process.odin @@ -2,42 +2,36 @@ package os2 import "core:sync" import "core:time" -import "core:c" +import "base:runtime" -args: []string = _alloc_command_line_arguments() +args: []string exit :: proc "contextless" (code: int) -> ! { - _exit(code) + runtime.trap() } -@(require_results) get_uid :: proc() -> int { - return _get_uid() + return -1 } -@(require_results) get_euid :: proc() -> int { - return _get_euid() + return -1 } -@(require_results) get_gid :: proc() -> int { - return _get_gid() + return -1 } -@(require_results) get_egid :: proc() -> int { - return _get_euid() + return -1 } -@(require_results) get_pid :: proc() -> int { - return _get_pid() + return -1 } -@(require_results) get_ppid :: proc() -> int { - return _get_ppid() + return -1 } @@ -52,12 +46,16 @@ Process :: struct { Process_Attributes :: struct { dir: string, env: []string, - stdin: ^File, - stdout: ^File, - stderr: ^File, + files: []^File, sys: ^Process_Attributes_OS_Specific, } +Process_Attributes_OS_Specific :: struct{} + +Process_Error :: enum { + None, +} + Process_State :: struct { pid: int, exit_code: int, @@ -68,53 +66,37 @@ Process_State :: struct { sys: rawptr, } -Signal :: enum { - Abort, - Floating_Point_Exception, - Illegal_Instruction, - Interrupt, - Segmentation_Fault, - Termination, +Signal :: #type proc() + +Kill: Signal = nil +Interrupt: Signal = nil + + +find_process :: proc(pid: int) -> (^Process, Process_Error) { + return nil, .None } -Signal_Handler_Proc :: #type proc "c" (c.int) -Signal_Handler_Special :: enum { - Default, - Ignore, + +process_start :: proc(name: string, argv: []string, attr: ^Process_Attributes) -> (^Process, Process_Error) { + return nil, .None } -Signal_Handler :: union { - Signal_Handler_Proc, - Signal_Handler_Special, +process_release :: proc(p: ^Process) -> Process_Error { + return .None } -@(require_results) -process_find :: proc(pid: int) -> (Process, Error) { - return _process_find(pid) +process_kill :: proc(p: ^Process) -> Process_Error { + return .None } -@(require_results) -process_get_state :: proc(p: Process) -> (Process_State, Error) { - return _process_get_state(p) +process_signal :: proc(p: ^Process, sig: Signal) -> Process_Error { + return .None } -@(require_results) -process_start :: proc(name: string, argv: []string, attr: ^Process_Attributes = nil) -> (Process, Error) { - return _process_start(name, argv, attr) +process_wait :: proc(p: ^Process) -> (Process_State, Process_Error) { + return {}, .None } -process_release :: proc(p: ^Process) -> Error { - return _process_release(p) -} -process_kill :: proc(p: ^Process) -> Error { - return _process_kill(p) -} -process_signal :: proc(sig: Signal, h: Signal_Handler) -> Error { - return _process_signal(sig, h) -} -process_wait :: proc(p: ^Process, t: time.Duration = time.MAX_DURATION) -> (Process_State, Error) { - return _process_wait(p, t) -} diff --git a/core/os/os2/process_linux.odin b/core/os/os2/process_linux.odin deleted file mode 100644 index aec838d9d..000000000 --- a/core/os/os2/process_linux.odin +++ /dev/null @@ -1,428 +0,0 @@ -//+private -package os2 - -import "base:runtime" - -import "core:fmt" -import "core:mem" -import "core:time" -import "core:strings" -import "core:strconv" -import "core:sys/linux" -import "core:path/filepath" - -_alloc_command_line_arguments :: proc() -> []string { - res := make([]string, len(runtime.args__), heap_allocator()) - for arg, i in runtime.args__ { - res[i] = string(arg) - } - return res -} - -_exit :: proc "contextless" (code: int) -> ! { - linux.exit_group(i32(code)) -} - -_get_uid :: proc() -> int { - return int(linux.getuid()) -} - -_get_euid :: proc() -> int { - return int(linux.geteuid()) -} - -_get_gid :: proc() -> int { - return int(linux.getgid()) -} - -_get_egid :: proc() -> int { - return int(linux.getegid()) -} - -_get_pid :: proc() -> int { - return int(linux.getpid()) -} - -_get_ppid :: proc() -> int { - return int(linux.getppid()) -} - -Process_Attributes_OS_Specific :: struct {} - -_process_find :: proc(pid: int) -> (Process, Error) { - TEMP_ALLOCATOR_GUARD() - pid_path := fmt.ctprintf("/proc/%d", pid) - - p: Process - dir_fd: linux.Fd - errno: linux.Errno - - #partial switch dir_fd, errno = linux.open(pid_path, _OPENDIR_FLAGS); errno { - case .NONE: - linux.close(dir_fd) - p.pid = pid - return p, nil - case .ENOTDIR: - return p, .Invalid_Dir - case .ENOENT: - return p, .Not_Exist - } - return p, _get_platform_error(errno) -} - -_process_get_state :: proc(p: Process) -> (state: Process_State, err: Error) { - TEMP_ALLOCATOR_GUARD() - - stat_name := fmt.ctprintf("/proc/%d/stat", p.pid) - stat_buf: []u8 - stat_buf, err = _read_entire_pseudo_file(stat_name, temp_allocator()) - - if err != nil { - return - } - - idx := strings.last_index_byte(string(stat_buf), ')') - stats := string(stat_buf[idx + 2:]) - - // utime and stime are the 12 and 13th items, respectively - // skip the first 11 items here. - for i := 0; i < 11; i += 1 { - stats = stats[strings.index_byte(stats, ' ') + 1:] - } - - idx = strings.index_byte(stats, ' ') - utime_str := stats[:idx] - - stats = stats[idx + 1:] - stime_str := stats[:strings.index_byte(stats, ' ')] - - utime, _ := strconv.parse_int(utime_str, 10) - stime, _ := strconv.parse_int(stime_str, 10) - - // NOTE: Assuming HZ of 100, 1 jiffy == 10 ms - state.user_time = time.Duration(utime) * 10 * time.Millisecond - state.system_time = time.Duration(stime) * 10 * time.Millisecond - - return -} - -_process_start :: proc(name: string, argv: []string, attr: ^Process_Attributes) -> (child: Process, err: Error) { - TEMP_ALLOCATOR_GUARD() - - dir_fd := linux.AT_FDCWD - errno: linux.Errno - if attr != nil && attr.dir != "" { - dir_cstr := temp_cstring(attr.dir) or_return - if dir_fd, errno = linux.open(dir_cstr, _OPENDIR_FLAGS); errno != .NONE { - return child, _get_platform_error(errno) - } - } - - // search PATH if just a plain name is provided - executable: cstring - if !strings.contains_rune(name, '/') { - path_env := get_env("PATH", temp_allocator()) - path_dirs := filepath.split_list(path_env, temp_allocator()) - found: bool - for dir in path_dirs { - executable = fmt.ctprintf("%s/%s", dir, name) - fail: bool - if fail, errno = linux.faccessat(dir_fd, executable, linux.F_OK); errno == .NONE && !fail { - found = true - break - } - } - if !found { - // check in cwd to match windows behavior - executable = fmt.ctprintf("./%s", name) - fail: bool - if fail, errno = linux.faccessat(dir_fd, executable, linux.F_OK); errno != .NONE || fail { - return child, .Not_Exist - } - } - } else { - executable = temp_cstring(name) or_return - } - - not_exec: bool - if not_exec, errno = linux.faccessat(dir_fd, executable, linux.F_OK | linux.X_OK); errno != .NONE || not_exec { - return child, errno == .NONE ? .Permission_Denied : _get_platform_error(errno) - } - - // args and environment need to be a list of cstrings - // that are terminated by a nil pointer. - // The first argument is a copy of the executable name. - cargs := make([]cstring, len(argv) + 2, temp_allocator()) - cargs[0] = executable - for i := 0; i < len(argv); i += 1 { - cargs[i + 1] = temp_cstring(argv[i]) or_return - } - - // Use current process's environment if attributes not provided - env: [^]cstring - if attr == nil { - // take this process's current environment - env = raw_data(export_cstring_environment(temp_allocator())) - } else { - cenv := make([]cstring, len(attr.env) + 1, temp_allocator()) - for i := 0; i < len(attr.env); i += 1 { - cenv[i] = temp_cstring(attr.env[i]) or_return - } - env = &cenv[0] - } - - // TODO: This is the traditional textbook implementation with fork. - // A more efficient implementation with vfork: - // - // 1. retrieve signal handlers - // 2. block all signals - // 3. allocate some stack space - // 4. vfork (waits for child exit or execve); In child: - // a. set child signal handlers - // b. set up any necessary pipes - // c. execve - // 5. restore signal handlers - // - stdin_fds: [2]linux.Fd - stdout_fds: [2]linux.Fd - stderr_fds: [2]linux.Fd - if attr != nil && attr.stdin != nil { - if errno = linux.pipe2(&stdin_fds, nil); errno != .NONE { - return child, _get_platform_error(errno) - } - } - if attr != nil && attr.stdout != nil { - if errno = linux.pipe2(&stdout_fds, nil); errno != .NONE { - return child, _get_platform_error(errno) - } - } - if attr != nil && attr.stderr != nil { - if errno = linux.pipe2(&stderr_fds, nil); errno != .NONE { - return child, _get_platform_error(errno) - } - } - - pid: linux.Pid - if pid, errno = linux.fork(); errno != .NONE { - return child, _get_platform_error(errno) - } - - IN :: 1 - OUT :: 0 - - STDIN :: linux.Fd(0) - STDOUT :: linux.Fd(1) - STDERR :: linux.Fd(2) - - if pid == 0 { - // in child process now - if attr != nil && attr.stdin != nil { - if linux.close(stdin_fds[IN]) != .NONE { linux.exit(1) } - if _, errno = linux.dup2(stdin_fds[OUT], STDIN); errno != .NONE { linux.exit(1) } - if linux.close(stdin_fds[OUT]) != .NONE { linux.exit(1) } - } - if attr != nil && attr.stdout != nil { - if linux.close(stdout_fds[OUT]) != .NONE { linux.exit(1) } - if _, errno = linux.dup2(stdout_fds[IN], STDOUT); errno != .NONE { linux.exit(1) } - if linux.close(stdout_fds[IN]) != .NONE { linux.exit(1) } - } - if attr != nil && attr.stderr != nil { - if linux.close(stderr_fds[OUT]) != .NONE { linux.exit(1) } - if _, errno = linux.dup2(stderr_fds[IN], STDERR); errno != .NONE { linux.exit(1) } - if linux.close(stderr_fds[IN]) != .NONE { linux.exit(1) } - } - - if errno = linux.execveat(dir_fd, executable, &cargs[OUT], env); errno != .NONE { - print_error(stderr, _get_platform_error(errno), string(executable)) - panic("execve failed to replace process") - } - unreachable() - } - - // in parent process - if attr != nil && attr.stdin != nil { - linux.close(stdin_fds[OUT]) - _construct_file(attr.stdin, uintptr(stdin_fds[IN])) - } - if attr != nil && attr.stdout != nil { - linux.close(stdout_fds[IN]) - _construct_file(attr.stdout, uintptr(stdout_fds[OUT])) - } - if attr != nil && attr.stderr != nil { - linux.close(stderr_fds[IN]) - _construct_file(attr.stderr, uintptr(stderr_fds[OUT])) - } - - child.pid = int(pid) - return child, nil -} - -_process_release :: proc(p: ^Process) -> Error { - // We didn't allocate... - return nil -} - -_process_kill :: proc(p: ^Process) -> Error { - res := linux.kill(linux.Pid(p.pid), .SIGKILL) - return _get_platform_error(res) -} - -_process_signal :: proc(sig: Signal, h: Signal_Handler) -> Error { - signo: linux.Signal - switch sig { - case .Abort: signo = .SIGABRT - case .Floating_Point_Exception: signo = .SIGFPE - case .Illegal_Instruction: signo = .SIGILL - case .Interrupt: signo = .SIGINT - case .Segmentation_Fault: signo = .SIGSEGV - case .Termination: signo = .SIGTERM - } - - sigact: linux.Sig_Action(int) - old: ^linux.Sig_Action(int) = nil - - switch v in h { - case Signal_Handler_Special: - switch v { - case .Default: - sigact.special = .SIG_DFL - case .Ignore: - sigact.special = .SIG_IGN - } - case Signal_Handler_Proc: - sigact.handler = (linux.Sig_Handler_Fn)(v) - } - - return _get_platform_error(linux.rt_sigaction(signo, &sigact, old)) -} - -_process_wait :: proc(p: ^Process, t: time.Duration) -> (state: Process_State, err: Error) { - safe_state :: proc(p: Process, state: Process_State = {}) -> (Process_State, Error) { - // process_get_state can fail, so we don't want to return it directly. - if new_state, err := _process_get_state(p); err == nil { - return new_state, nil - } - return state, nil - } - - state.pid = p.pid - - options: linux.Wait_Options - big_if: if t == 0 { - options += {.WNOHANG} - } else if t != time.MAX_DURATION { - ts: linux.Time_Spec = { - time_sec = uint(t / time.Second), - time_nsec = uint(t % time.Second), - } - - @static has_pidfd_open: bool = true - - // pidfd_open is fairly new, so don't error out on ENOSYS - pid_fd: linux.Pid_FD - errno: linux.Errno - if has_pidfd_open { - pid_fd, errno = linux.pidfd_open(linux.Pid(p.pid), nil) - if errno != .NONE && errno != .ENOSYS { - return state, _get_platform_error(errno) - } - } - - if has_pidfd_open && errno != .ENOSYS { - defer linux.close(linux.Fd(pid_fd)) - pollfd: [1]linux.Poll_Fd = { - { - fd = linux.Fd(pid_fd), - events = {.IN}, - }, - } - for { - n, e := linux.ppoll(pollfd[:], &ts, nil) - if e == .EINTR { - continue - } - if e != .NONE { - return state, _get_platform_error(errno) - } - if n == 0 { - return safe_state(p^, state) - } - break - } - } else { - has_pidfd_open = false - mask: bit_set[0..=63] - mask += { int(linux.Signal.SIGCHLD) - 1 } - - 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 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(p.pid), &info, waitid_options, nil) - if errno == .NONE && info.code != 0 { - break big_if - } - - loop: for { - sigset = {} - mem.copy(&sigset, &mask, size_of(mask)) - - _, errno = linux.rt_sigtimedwait(&sigset, &info, &ts) - #partial switch errno { - case .EAGAIN: // timeout - return safe_state(p^, state) - case .EINVAL: - return state, _get_platform_error(errno) - case .EINTR: - continue - case: - if int(info.pid) == p.pid { - break loop - } - } - } - } - } - - state, _ = safe_state(p^, state) - - status: u32 - errno: linux.Errno = .EINTR - for errno == .EINTR { - _, errno = linux.wait4(linux.Pid(p.pid), &status, options, nil) - if errno != .NONE { - return state, _get_platform_error(errno) - } - } - - // terminated by exit - if linux.WIFEXITED(status) { - p.is_done = true - state.exited = true - state.exit_code = int(linux.WEXITSTATUS(status)) - state.success = state.exit_code == 0 - return state, nil - } - - // terminated by signal - if linux.WIFSIGNALED(status) { - // NOTE: what's the correct behavior here?? - p.is_done = true - state.exited = false - state.exit_code = int(linux.WTERMSIG(status)) - state.success = false - return state, nil - } - - return safe_state(p^, state) -} diff --git a/core/os/os2/process_windows.odin b/core/os/os2/process_windows.odin deleted file mode 100644 index 4b5e4bc02..000000000 --- a/core/os/os2/process_windows.odin +++ /dev/null @@ -1,67 +0,0 @@ -//+private -package os2 - -import "core:runtime" -import "core:time" - -_alloc_command_line_arguments :: proc() -> []string { - return nil -} - -_exit :: proc "contextless" (_: int) -> ! { - runtime.trap() -} - -_get_uid :: proc() -> int { - return -1 -} - -_get_euid :: proc() -> int { - return -1 -} - -_get_gid :: proc() -> int { - return -1 -} - -_get_egid :: proc() -> int { - return -1 -} - -_get_pid :: proc() -> int { - return -1 -} - -_get_ppid :: proc() -> int { - return -1 -} - -Process_Attributes_OS_Specific :: struct{} - -_process_find :: proc(pid: int) -> (Process, Error) { - return Process{}, nil -} - -_process_get_state :: proc(p: Process) -> (Process_State, Error) { - return Process_State{}, nil -} - -_process_start :: proc(name: string, argv: []string, attr: ^Process_Attributes) -> (Process, Error) { - return Process{}, nil -} - -_process_release :: proc(p: ^Process) -> Error { - return nil -} - -_process_kill :: proc(p: ^Process) -> Error { - return nil -} - -_process_signal :: proc(sig: Signal, handler: Signal_Handler) -> Error { - return nil -} - -_process_wait :: proc(p: ^Process, t: time.Duration) -> (Process_State, Error) { - return Process_State{}, nil -}