summaryrefslogtreecommitdiff
path: root/kit/process.posix.c
diff options
context:
space:
mode:
Diffstat (limited to 'kit/process.posix.c')
-rw-r--r--kit/process.posix.c323
1 files changed, 323 insertions, 0 deletions
diff --git a/kit/process.posix.c b/kit/process.posix.c
new file mode 100644
index 0000000..d9bec1f
--- /dev/null
+++ b/kit/process.posix.c
@@ -0,0 +1,323 @@
+#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_;
+
+ (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