| 1 | module os |
| 2 | |
| 3 | fn C.setpgid(pid i32, pgid i32) i32 |
| 4 | |
| 5 | fn env_value_from_entries(env []string, name string) ?string { |
| 6 | prefix := '${name}=' |
| 7 | for entry in env { |
| 8 | if entry.starts_with(prefix) { |
| 9 | return entry[prefix.len..] |
| 10 | } |
| 11 | } |
| 12 | return none |
| 13 | } |
| 14 | |
| 15 | fn (p &Process) unix_resolve_filename() !string { |
| 16 | if is_abs_path(p.filename) { |
| 17 | return p.filename |
| 18 | } |
| 19 | if p.filename.contains(path_separator) { |
| 20 | if p.work_folder != '' { |
| 21 | return abs_path(p.filename) |
| 22 | } |
| 23 | return p.filename |
| 24 | } |
| 25 | path := env_value_from_entries(p.env, 'PATH') or { return error_failed_to_find_executable() } |
| 26 | return find_abs_path_of_executable_in_path_env(p.filename, path) |
| 27 | } |
| 28 | |
| 29 | fn (mut p Process) unix_spawn_process() int { |
| 30 | mut pipeset := [6]int{} |
| 31 | if p.use_stdio_ctl { |
| 32 | mut dont_care := C.pipe(&pipeset[0]) // pipe read end 0 <- 1 pipe write end |
| 33 | dont_care = C.pipe(&pipeset[2]) // pipe read end 2 <- 3 pipe write end |
| 34 | dont_care = C.pipe(&pipeset[4]) // pipe read end 4 <- 5 pipe write end |
| 35 | _ = dont_care // using `_` directly on each above `pipe` fails to avoid C compiler generate an `-Wunused-result` warning |
| 36 | } |
| 37 | pid := fork() |
| 38 | if pid != 0 { |
| 39 | // This is the parent process after the fork. |
| 40 | // Note: pid contains the process ID of the child process |
| 41 | if p.use_stdio_ctl { |
| 42 | p.stdio_fd[0] = pipeset[1] // store the write end of child's in |
| 43 | p.stdio_fd[1] = pipeset[2] // store the read end of child's out |
| 44 | p.stdio_fd[2] = pipeset[4] // store the read end of child's err |
| 45 | // close the rest of the pipe fds, the parent does not need them |
| 46 | fd_close(pipeset[0]) |
| 47 | fd_close(pipeset[3]) |
| 48 | fd_close(pipeset[5]) |
| 49 | } |
| 50 | return pid |
| 51 | } |
| 52 | // |
| 53 | // Here, we are in the child process. |
| 54 | // It still shares file descriptors with the parent process, |
| 55 | // but it is otherwise independent and can do stuff *without* |
| 56 | // affecting the parent process. |
| 57 | // |
| 58 | if p.use_pgroup { |
| 59 | C.setpgid(0, 0) |
| 60 | } |
| 61 | if p.use_stdio_ctl { |
| 62 | // Redirect the child standard in/out/err to the pipes that |
| 63 | // were created in the parent. |
| 64 | // Close the parent's pipe fds, the child do not need them: |
| 65 | fd_close(pipeset[1]) |
| 66 | fd_close(pipeset[2]) |
| 67 | fd_close(pipeset[4]) |
| 68 | // redirect the pipe fds to the child's in/out/err fds: |
| 69 | C.dup2(pipeset[0], 0) |
| 70 | C.dup2(pipeset[3], 1) |
| 71 | C.dup2(pipeset[5], 2) |
| 72 | // close the pipe fdsx after the redirection |
| 73 | fd_close(pipeset[0]) |
| 74 | fd_close(pipeset[3]) |
| 75 | fd_close(pipeset[5]) |
| 76 | } |
| 77 | p.filename = p.unix_resolve_filename() or { |
| 78 | eprintln(err) |
| 79 | exit(1) |
| 80 | } |
| 81 | if p.work_folder != '' { |
| 82 | chdir(p.work_folder) or {} |
| 83 | } |
| 84 | execve(p.filename, p.args, p.env) or { |
| 85 | eprintln(err) |
| 86 | exit(1) |
| 87 | } |
| 88 | return 0 |
| 89 | } |
| 90 | |
| 91 | fn (mut p Process) unix_stop_process() { |
| 92 | C.kill(p.pid, C.SIGSTOP) |
| 93 | } |
| 94 | |
| 95 | fn (mut p Process) unix_resume_process() { |
| 96 | C.kill(p.pid, C.SIGCONT) |
| 97 | } |
| 98 | |
| 99 | fn (mut p Process) unix_term_process() { |
| 100 | C.kill(p.pid, C.SIGTERM) |
| 101 | } |
| 102 | |
| 103 | fn (mut p Process) unix_kill_process() { |
| 104 | C.kill(p.pid, C.SIGKILL) |
| 105 | } |
| 106 | |
| 107 | fn (mut p Process) unix_kill_pgroup() { |
| 108 | C.kill(-p.pid, C.SIGKILL) |
| 109 | } |
| 110 | |
| 111 | fn (mut p Process) unix_wait() { |
| 112 | p.impl_check_pid_status(false, 0) |
| 113 | } |
| 114 | |
| 115 | fn (mut p Process) unix_is_alive() bool { |
| 116 | return p.impl_check_pid_status(true, C.WNOHANG) |
| 117 | } |
| 118 | |
| 119 | fn (mut p Process) impl_check_pid_status(exit_early_on_ret0 bool, waitpid_options int) bool { |
| 120 | mut cstatus := 0 |
| 121 | mut ret := -1 |
| 122 | $if !emscripten ? { |
| 123 | ret = C.waitpid(p.pid, &cstatus, waitpid_options) |
| 124 | } |
| 125 | p.code = ret |
| 126 | if ret == -1 { |
| 127 | p.err = posix_get_error_msg(C.errno) |
| 128 | return false |
| 129 | } |
| 130 | if exit_early_on_ret0 && ret == 0 { |
| 131 | return true |
| 132 | } |
| 133 | mut pret, is_signaled := posix_wait4_to_exit_status(cstatus) |
| 134 | if is_signaled { |
| 135 | p.status = .aborted |
| 136 | p.err = 'Terminated by signal ${pret:2d} (${sigint_to_signal_name(pret)})' |
| 137 | pret += 128 |
| 138 | } else { |
| 139 | p.status = .exited |
| 140 | } |
| 141 | p.code = pret |
| 142 | return false |
| 143 | } |
| 144 | |
| 145 | // these are here to make v_win.c/v.c generation work in all cases: |
| 146 | fn (mut p Process) win_spawn_process() int { |
| 147 | return 0 |
| 148 | } |
| 149 | |
| 150 | fn (mut p Process) win_stop_process() { |
| 151 | } |
| 152 | |
| 153 | fn (mut p Process) win_resume_process() { |
| 154 | } |
| 155 | |
| 156 | fn (mut p Process) win_term_process() { |
| 157 | } |
| 158 | |
| 159 | fn (mut p Process) win_kill_process() { |
| 160 | } |
| 161 | |
| 162 | fn (mut p Process) win_kill_pgroup() { |
| 163 | } |
| 164 | |
| 165 | fn (mut p Process) win_wait() { |
| 166 | } |
| 167 | |
| 168 | fn (mut p Process) win_is_alive() bool { |
| 169 | return false |
| 170 | } |
| 171 | |
| 172 | fn (mut p Process) win_write_string(_idx int, _s string) { |
| 173 | } |
| 174 | |
| 175 | fn (mut p Process) win_read_string(_idx int, _maxbytes int) (string, int) { |
| 176 | return '', 0 |
| 177 | } |
| 178 | |
| 179 | fn (mut p Process) win_is_pending(_idx int) bool { |
| 180 | return false |
| 181 | } |
| 182 | |
| 183 | fn (mut p Process) win_slurp(_idx int) string { |
| 184 | return '' |
| 185 | } |
| 186 | |