#include "process.h" #if !defined(_WIN32) || defined(__CYGWIN__) # include "status.h" # include # include # include # include static char *kit_process_argv_null_[] = { "", NULL }; static char *kit_process_env_null_[] = { NULL }; static char *kit_process_str_(kit_str_t s) { // FIXME // return BS(s); } static char **kit_init_argv_(kit_process_args_t args, u32 flags) { // TODO // if ((flags & KIT_PROCESS_NO_ARGUMENTS) != 0) return kit_process_argv_null_; (void) args; return NULL; } static char **kit_init_envp_(kit_process_env_t env, u32 flags) { // TODO // if ((flags & KIT_PROCESS_NO_ENVIRONMENT) != 0) return kit_process_env_null_; (void) env; return NULL; } s32 kit_process_init(kit_process_t *p, kit_process_info_t info) { assert(p != NULL); assert(info.working_directory.size == 0 || info.working_directory.values != NULL); if (p == NULL || (info.working_directory.size != 0 && info.working_directory.values == NULL)) return KIT_ERROR_INVALID_ARGUMENT; memset(p, 0, sizeof *p); p->_stdin = -1; p->_stdout = -1; p->_stderr = -1; signal(SIGCHLD, SIG_IGN); signal(SIGALRM, SIG_IGN); // pipes i32 pipe_in[2]; i32 pipe_out[2]; i32 pipe_err[2]; if ((info.flags & KIT_PROCESS_NO_PIPES) == 0) { if (pipe(pipe_in) == -1) { assert(0); return KIT_ERROR_PIPE_FAILED; } if (pipe(pipe_out) == -1) { assert(0); close(pipe_in[0]); close(pipe_in[1]); return KIT_ERROR_PIPE_FAILED; } if (pipe(pipe_err) == -1) { assert(0); close(pipe_in[0]); close(pipe_in[1]); close(pipe_out[0]); close(pipe_out[1]); return KIT_ERROR_PIPE_FAILED; } // non-blocking writing for stdout, stderr if (fcntl(pipe_out[1], F_SETFL, O_NONBLOCK) == -1 || fcntl(pipe_err[1], F_SETFL, O_NONBLOCK) == -1) { assert(0); close(pipe_in[0]); close(pipe_in[1]); close(pipe_out[0]); close(pipe_out[1]); close(pipe_err[0]); close(pipe_err[1]); return KIT_ERROR_PIPE_FAILED; } } pid_t id = fork(); switch (id) { case -1: return KIT_ERROR_FORK_FAILED; case 0: // Child process // p->status = KIT_OK; p->current_is_forked = 1; if ((info.flags & KIT_PROCESS_NO_PIPES) == 0) { // Redirect IO if (dup2(pipe_in[0], STDIN_FILENO) == -1 || dup2(pipe_out[1], STDOUT_FILENO) == -1 || dup2(pipe_err[1], STDERR_FILENO) == -1) { assert(0); close(pipe_in[0]); close(pipe_in[1]); close(pipe_out[0]); close(pipe_out[1]); close(pipe_err[0]); close(pipe_err[1]); return KIT_ERROR_DUP2_FAILED; } // Close pipes close(pipe_in[0]); close(pipe_in[1]); close(pipe_out[0]); close(pipe_out[1]); close(pipe_err[0]); close(pipe_err[1]); } // Change working directory if (info.working_directory.size != 0 && chdir(kit_process_str_(info.working_directory)) == -1) { assert(0); return KIT_ERROR_CHDIR_FAILED; } if ((info.flags & KIT_PROCESS_FORK) == 0) { execve(kit_process_str_(info.file_name), kit_init_argv_(info.command_line, info.flags), kit_init_envp_(info.environment, info.flags)); // Doesn't return on success return KIT_ERROR_EXECVE_FAILED; } return KIT_OK; default: // Parent process // p->status = KIT_OK; p->current_is_forked = 0; p->_ready = 1; p->_running = 1; p->_id = id; if ((info.flags & KIT_PROCESS_NO_PIPES) == 0) { p->_stdin = pipe_in[1]; p->_stdout = pipe_out[0]; p->_stderr = pipe_err[0]; // Close unused pipes close(pipe_in[0]); close(pipe_out[1]); close(pipe_err[1]); } } return KIT_OK; } void kit_process_cleanup(kit_process_t *p) { assert(p != NULL); assert(p->_ready); if (p == NULL || !p->_ready) return; if (p->_stdin != -1) close(p->_stdin); if (p->_stdout != -1) close(p->_stdout); if (p->_stderr != -1) close(p->_stderr); memset(p, 0, sizeof *p); } i64 kit_process_write_stdin(kit_process_t *p, kit_str_t in_data) { assert(p != NULL && (in_data.size == 0 || in_data.values != NULL) && p->_running); if (p == NULL || (in_data.size != 0 && in_data.values == NULL)) return KIT_ERROR_INVALID_ARGUMENT; if (in_data.size == 0 || !p->_running || p->_stdin == -1) return 0; i64 n = write(p->_stdin, in_data.values, in_data.size); assert(n >= 0); if (n < 0) return 0; return n; } i64 kit_process_read_stdout(kit_process_t *p, kit_str_t out_data) { assert(p != NULL && (out_data.size == 0 || out_data.values != NULL) && p->_ready); if (p == NULL || (out_data.size != 0 && out_data.values == NULL)) return KIT_ERROR_INVALID_ARGUMENT; if (out_data.size == 0 || !p->_ready || p->_stdout == -1) return 0; i64 n = read(p->_stdout, out_data.values, out_data.size); assert(n >= 0); if (n < 0) return 0; return n; } i64 kit_process_read_stderr(kit_process_t *p, kit_str_t out_data) { assert(p != NULL && (out_data.size == 0 || out_data.values != NULL) && p->_ready); if (p == NULL || (out_data.size != 0 && out_data.values == NULL)) return KIT_ERROR_INVALID_ARGUMENT; if (out_data.size == 0 || !p->_ready || p->_stderr == -1) return 0; i64 n = read(p->_stderr, out_data.values, out_data.size); assert(n >= 0); if (n < 0) return 0; return n; } s32 kit_process_terminate(kit_process_t *p) { assert(p != NULL && p->_running); if (p == NULL || !p->_running) return KIT_ERROR_INVALID_ARGUMENT; if (kill(p->_id, SIGTERM) == -1) return KIT_ERROR_KILL_FAILED; return KIT_OK; } b8 kit_process_alive(kit_process_t *p) { assert(p != NULL); if (p == NULL || p->status != KIT_OK) return 0; if (!p->_running) return 0; int status; pid_t id = waitpid(p->_id, &status, WNOHANG); if (id == -1) { p->status = KIT_ERROR_WAITPID_FAILED; return 0; } if (id == 0) return 1; if (WIFEXITED(status)) { p->exit_code = WEXITSTATUS(status); p->_running = 0; return 0; } return 1; } s32 kit_process_wait(kit_process_t *p) { assert(p != NULL); if (p == NULL) return KIT_ERROR_INVALID_ARGUMENT; if (p->status != KIT_OK) return p->status; if (!p->_running) return KIT_OK; for (;;) { int status; pid_t id = waitpid(p->_id, &status, 0); if (id == -1) return KIT_ERROR_WAITPID_FAILED; if (WIFEXITED(status)) { p->exit_code = WEXITSTATUS(status); p->_running = 0; break; } } return KIT_OK; } #endif