From 4dc976bc793af39ebad715ef669b335657926f65 Mon Sep 17 00:00:00 2001 From: pommicket Date: Mon, 5 Dec 2022 11:05:47 -0500 Subject: start lsp --- lsp.c | 14 +++++++ process-posix.c | 124 +++++++++++++++++++++++++++++++++++++++----------------- process.h | 14 +++++++ 3 files changed, 114 insertions(+), 38 deletions(-) create mode 100644 lsp.c diff --git a/lsp.c b/lsp.c new file mode 100644 index 0000000..2c94dfa --- /dev/null +++ b/lsp.c @@ -0,0 +1,14 @@ +typedef struct { + Process process; +} LSP; + +static void send_message(LSP *lsp, const char *content, size_t content_size) { +} + +void lsp_create(LSP *lsp, const char *analyzer_command) { + ProcessSettings settings = { + .stdin_blocking = true, + .stdout_blocking = true + }; + process_run_ex(&lsp->process, analyzer_command, &settings); +} diff --git a/process-posix.c b/process-posix.c index f35017f..81c3ff5 100644 --- a/process-posix.c +++ b/process-posix.c @@ -6,55 +6,97 @@ struct Process { pid_t pid; - int pipe; + int stdout_pipe; + int stdin_pipe; char error[64]; }; -bool process_run(Process *proc, char const *command) { +bool process_run_ex(Process *proc, const char *command, const ProcessSettings *settings) { memset(proc, 0, sizeof *proc); + int stdin_pipe[2] = {0}, stdout_pipe[2] = {0}; + if (pipe(stdin_pipe) != 0) { + strbuf_printf(proc->error, "%s", strerror(errno)); + return false; + } + if (pipe(stdout_pipe) != 0) { + strbuf_printf(proc->error, "%s", strerror(errno)); + close(stdin_pipe[0]); + close(stdin_pipe[1]); + return false; + } + bool success = false; - int pipefd[2]; - if (pipe(pipefd) == 0) { - pid_t pid = fork(); - if (pid == 0) { - // child process - // put child in its own group. it will be in this group with all of its descendents, - // so by killing everything in the group, we kill all the descendents of this process. - // if we didn't do this, we would just be killing the sh process in process_kill. - setpgid(0, 0); - // send stdout and stderr to pipe - dup2(pipefd[1], STDOUT_FILENO); - dup2(pipefd[1], STDERR_FILENO); - close(pipefd[0]); - close(pipefd[1]); - char *program = "/bin/sh"; - char *argv[] = {program, "-c", (char *)command, NULL}; - if (execv(program, argv) == -1) { - dprintf(STDERR_FILENO, "%s: %s\n", program, strerror(errno)); - exit(127); - } - } else if (pid > 0) { - // parent process - close(pipefd[1]); - fcntl(pipefd[0], F_SETFL, fcntl(pipefd[0], F_GETFL) | O_NONBLOCK); // set pipe to non-blocking - proc->pid = pid; - proc->pipe = pipefd[0]; - success = true; + pid_t pid = fork(); + if (pid == 0) { + // child process + // put child in its own group. it will be in this group with all of its descendents, + // so by killing everything in the group, we kill all the descendents of this process. + // if we didn't do this, we would just be killing the sh process in process_kill. + setpgid(0, 0); + // pipe stuff + dup2(stdout_pipe[1], STDOUT_FILENO); + dup2(stdout_pipe[1], STDERR_FILENO); + dup2(stdin_pipe[0], STDIN_FILENO); + // don't need these file descriptors anymore + close(stdout_pipe[0]); + close(stdout_pipe[1]); + close(stdin_pipe[0]); + close(stdin_pipe[1]); + + char *program = "/bin/sh"; + char *argv[] = {program, "-c", (char *)command, NULL}; + if (execv(program, argv) == -1) { + dprintf(STDERR_FILENO, "%s: %s\n", program, strerror(errno)); + exit(127); } - } else { - strbuf_printf(proc->error, "%s", strerror(errno)); + } else if (pid > 0) { + // parent process + + // we're reading from (the child's) stdout and writing to stdin, + // so we don't need the write end of the stdout pipe or the + // read end of the stdin pipe. + close(stdout_pipe[1]); + close(stdin_pipe[0]); + // set pipes to non-blocking + if (!settings->stdout_blocking) + fcntl(stdout_pipe[0], F_SETFL, fcntl(stdout_pipe[0], F_GETFL) | O_NONBLOCK); + if (!settings->stdin_blocking) + fcntl(stdin_pipe[1], F_SETFL, fcntl(stdin_pipe[1], F_GETFL) | O_NONBLOCK); + proc->pid = pid; + proc->stdout_pipe = stdout_pipe[0]; + proc->stdin_pipe = stdin_pipe[1]; + success = true; } return success; } +bool process_run(Process *proc, char const *command) { + const ProcessSettings settings = {0}; + return process_run_ex(proc, command, &settings); +} + + char const *process_geterr(Process *p) { return *p->error ? p->error : NULL; } +long long process_write(Process *proc, const char *data, size_t size) { + assert(proc->stdin_pipe); // check that process hasn't been killed + ssize_t bytes_written = write(proc->stdin_pipe, data, size); + if (bytes_written >= 0) { + return (long long)bytes_written; + } else if (errno == EAGAIN) { + return 0; + } else { + strbuf_printf(proc->error, "%s", strerror(errno)); + return -2; + } +} + long long process_read(Process *proc, char *data, size_t size) { - assert(proc->pipe); - ssize_t bytes_read = read(proc->pipe, data, size); + assert(proc->stdout_pipe); + ssize_t bytes_read = read(proc->stdout_pipe, data, size); if (bytes_read >= 0) { return (long long)bytes_read; } else if (errno == EAGAIN) { @@ -66,13 +108,19 @@ long long process_read(Process *proc, char *data, size_t size) { } +static void process_close_pipes(Process *proc) { + close(proc->stdout_pipe); + close(proc->stdin_pipe); + proc->stdout_pipe = 0; + proc->stdin_pipe = 0; +} + void process_kill(Process *proc) { kill(-proc->pid, SIGKILL); // kill everything in process group // get rid of zombie process waitpid(proc->pid, NULL, 0); - close(proc->pipe); proc->pid = 0; - proc->pipe = 0; + process_close_pipes(proc); } int process_check_status(Process *proc, char *message, size_t message_size) { @@ -83,7 +131,7 @@ int process_check_status(Process *proc, char *message, size_t message_size) { return 0; } else if (ret > 0) { if (WIFEXITED(wait_status)) { - close(proc->pipe); proc->pipe = 0; + process_close_pipes(proc); int code = WEXITSTATUS(wait_status); if (code == 0) { str_printf(message, message_size, "exited successfully"); @@ -93,14 +141,14 @@ int process_check_status(Process *proc, char *message, size_t message_size) { return -1; } } else if (WIFSIGNALED(wait_status)) { - close(proc->pipe); + process_close_pipes(proc); str_printf(message, message_size, "terminated by signal %d", WTERMSIG(wait_status)); return -1; } return 0; } else { // this process is gone or something? - close(proc->pipe); proc->pipe = 0; + process_close_pipes(proc); str_printf(message, message_size, "process ended unexpectedly"); return -1; } diff --git a/process.h b/process.h index 4db864e..d8bb6ec 100644 --- a/process.h +++ b/process.h @@ -4,11 +4,25 @@ typedef struct Process Process; +// zero everything except what you're using +typedef struct { + bool stdin_blocking; + bool stdout_blocking; +} ProcessSettings; + // execute the given command (like if it was passed to system()). // returns false on failure +bool process_run_ex(Process *proc, const char *command, const ProcessSettings *props); +// like process_run_ex, but with the default settings bool process_run(Process *process, char const *command); // returns the error last error produced, or NULL if there was no error. char const *process_geterr(Process *process); +// write to stdin +// returns: +// -2 on error +// or a non-negative number indicating the number of bytes written. +long long process_write(Process *process, const char *data, size_t size); +// read from stdout+stderr // returns: // -2 on error // -1 if no data is available right now -- cgit v1.2.3