diff options
Diffstat (limited to 'source/kit/process.posix.c')
-rw-r--r-- | source/kit/process.posix.c | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/source/kit/process.posix.c b/source/kit/process.posix.c new file mode 100644 index 0000000..961f058 --- /dev/null +++ b/source/kit/process.posix.c @@ -0,0 +1,319 @@ +#include "process.h" + +#if !defined(_WIN32) || defined(__CYGWIN__) +# include "status.h" + +# include <assert.h> +# include <fcntl.h> +# include <signal.h> +# include <sys/wait.h> + +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_; + + 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_; + + 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 |