From a593789a46ceb27d26186db7b3215b2a046ff865 Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Fri, 26 Aug 2022 05:07:32 +0200 Subject: worker: capture process output --- src/ci.c | 30 ++++++++------------- src/ci.h | 11 +++----- src/file.c | 38 +++++++++++++++++++++++++++ src/file.h | 4 +++ src/process.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++------ src/process.h | 15 +++++++++++ src/worker.c | 18 +++++++++++-- 7 files changed, 163 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/ci.c b/src/ci.c index 1932008..ffc298a 100644 --- a/src/ci.c +++ b/src/ci.c @@ -18,30 +18,23 @@ static const char *ci_scripts[] = { }; /* clang-format on */ -static run_result ci_run_script(const char *script, int *ec) +static int ci_run_script(const char *script, struct proc_output *result) { const char *args[] = {script, NULL}; - int ret = 0; - - ret = proc_spawn(args, ec); - if (ret < 0) - return RUN_ERROR; - if (*ec) - return RUN_FAILURE; - return RUN_SUCCESS; + return proc_capture(args, result); } -run_result ci_run(int *ec) +int ci_run(struct proc_output *result) { for (const char **script = ci_scripts; *script; ++script) { if (!file_exists(*script)) continue; print_log("Going to run: %s\n", *script); - return ci_run_script(*script, ec); + return ci_run_script(*script, result); } print_log("Couldn't find any CI scripts to run\n"); - return RUN_NO; + return -1; } static void ci_cleanup_git_repo(git_repository *repo) @@ -60,21 +53,20 @@ static int ci_prepare_git_repo(git_repository **repo, const char *url, const cha ret = libgit_checkout(*repo, rev); if (ret < 0) - goto free_repo; + goto cleanup_repo; return ret; -free_repo: +cleanup_repo: ci_cleanup_git_repo(*repo); return ret; } -run_result ci_run_git_repo(const char *url, const char *rev, int *ec) +int ci_run_git_repo(const char *url, const char *rev, struct proc_output *output) { char *oldpwd; git_repository *repo; - run_result result = RUN_ERROR; int ret = 0; ret = ci_prepare_git_repo(&repo, url, rev); @@ -85,8 +77,8 @@ run_result ci_run_git_repo(const char *url, const char *rev, int *ec) if (ret < 0) goto free_repo; - result = ci_run(ec); - if (result < 0) + ret = ci_run(output); + if (ret < 0) goto oldpwd; oldpwd: @@ -97,5 +89,5 @@ free_repo: ci_cleanup_git_repo(repo); exit: - return result; + return ret; } diff --git a/src/ci.h b/src/ci.h index 4267b89..f890796 100644 --- a/src/ci.h +++ b/src/ci.h @@ -1,14 +1,9 @@ #ifndef __CI_H__ #define __CI_H__ -typedef enum { - RUN_ERROR = -1, - RUN_SUCCESS, - RUN_FAILURE, - RUN_NO, -} run_result; +#include "process.h" -run_result ci_run(int *ec); +int ci_run(struct proc_output *); /* * This is a high-level function. It's basically equivalent to the following @@ -23,6 +18,6 @@ run_result ci_run(int *ec); * rm -rf "$dir" * */ -run_result ci_run_git_repo(const char *url, const char *rev, int *ec); +int ci_run_git_repo(const char *url, const char *rev, struct proc_output *); #endif diff --git a/src/file.c b/src/file.c index ede640b..0273e71 100644 --- a/src/file.c +++ b/src/file.c @@ -59,3 +59,41 @@ int file_exists(const char *path) int ret = lstat(path, &stat); return !ret && S_ISREG(stat.st_mode); } + +int file_read(int fd, char **output, size_t *len) +{ + char buf[128]; + size_t buf_len = sizeof(buf) / sizeof(buf[0]); + int ret = 0; + + *output = NULL; + *len = 0; + + while (1) { + ssize_t read_now = read(fd, buf, buf_len); + + if (read_now < 0) { + print_errno("read"); + ret = read_now; + goto free_output; + } + + if (!read_now) + goto exit; + + *output = realloc(*output, *len + read_now + 1); + if (!*output) { + print_errno("realloc"); + return -1; + } + memcpy(*output + *len, buf, read_now); + *len += read_now; + *(*output + *len) = '\0'; + } + +free_output: + free(*output); + +exit: + return ret; +} diff --git a/src/file.h b/src/file.h index b89fca1..03d5e9a 100644 --- a/src/file.h +++ b/src/file.h @@ -1,10 +1,14 @@ #ifndef __FILE_H__ #define __FILE_H__ +#include + int rm_rf(const char *dir); int my_chdir(const char *dir, char **old); int file_exists(const char *path); +int file_read(int fd, char **output, size_t *len); + #endif diff --git a/src/process.c b/src/process.c index 9d95037..7d62a88 100644 --- a/src/process.c +++ b/src/process.c @@ -1,10 +1,23 @@ #include "process.h" +#include "file.h" #include "log.h" +#include #include #include #include +static int exec_child(const char *args[]) +{ + int ret = execv(args[0], (char *const *)args); + if (ret < 0) { + print_errno("execv"); + return ret; + } + + return ret; +} + static int wait_for_child(pid_t pid, int *ec) { int status; @@ -25,22 +38,77 @@ static int wait_for_child(pid_t pid, int *ec) int proc_spawn(const char *args[], int *ec) { - int ret = 0; - pid_t child_pid = fork(); if (child_pid < 0) { print_errno("fork"); return child_pid; } - if (child_pid) - return wait_for_child(child_pid, ec); + if (!child_pid) + exit(exec_child(args)); + + return wait_for_child(child_pid, ec); +} + +static int redirect_and_exec_child(int pipe_fds[2], const char *args[]) +{ + int ret = 0; + + close(pipe_fds[0]); - ret = execv(args[0], (char *const *)args); + ret = dup2(pipe_fds[1], STDOUT_FILENO); if (ret < 0) { - print_errno("execv"); - exit(ret); + print_errno("dup2"); + return ret; } - return 0; + ret = dup2(pipe_fds[1], STDERR_FILENO); + if (ret < 0) { + print_errno("dup2"); + return ret; + } + + return exec_child(args); +} + +int proc_capture(const char *args[], struct proc_output *result) +{ + int pipe_fds[2]; + int ret = 0; + + ret = pipe2(pipe_fds, O_CLOEXEC); + if (ret < 0) { + print_errno("pipe2"); + return -1; + } + + pid_t child_pid = fork(); + if (child_pid < 0) { + print_errno("fork"); + goto close_pipe; + } + + if (!child_pid) + exit(redirect_and_exec_child(pipe_fds, args)); + + close(pipe_fds[1]); + + ret = file_read(pipe_fds[0], &result->output, &result->output_len); + if (ret < 0) + goto close_pipe; + + ret = wait_for_child(child_pid, &result->ec); + if (ret < 0) + goto free_output; + + goto close_pipe; + +free_output: + free(result->output); + +close_pipe: + close(pipe_fds[0]); + close(pipe_fds[1]); + + return ret; } diff --git a/src/process.h b/src/process.h index da0c421..455d4ec 100644 --- a/src/process.h +++ b/src/process.h @@ -1,6 +1,21 @@ #ifndef __PROCESS_H__ #define __PROCESS_H__ +#include + +struct proc_output { + int ec; + char *output; + size_t output_len; +}; + +/* The exit code is only valid if the functions returns a non-negative number. */ int proc_spawn(const char *args[], int *ec); +/* Similarly, the contents of the proc_output structure is only valid if the function returns a + * non-negative number. + * + * In that case, you'll need to free the output. */ +int proc_capture(const char *args[], struct proc_output *result); + #endif diff --git a/src/worker.c b/src/worker.c index 687e647..0dc64d3 100644 --- a/src/worker.c +++ b/src/worker.c @@ -4,6 +4,7 @@ #include "log.h" #include "msg.h" #include "net.h" +#include "process.h" #include #include @@ -82,9 +83,22 @@ static int msg_body_ci_run(const struct msg *msg) { const char *url = msg->argv[1]; const char *rev = msg->argv[2]; - int ec = 0; + struct proc_output result; + int ret = 0; - return ci_run_git_repo(url, rev, &ec); + ret = ci_run_git_repo(url, rev, &result); + if (ret < 0) { + print_error("Run failed with an error\n"); + return ret; + } + + print_log("Process exit code: %d\n", result.ec); + print_log("Process output:\n%s", result.output); + if (!result.output || !result.output_len || result.output[result.output_len - 1] != '\n') + print_log("\n"); + free(result.output); + + return ret; } typedef worker_task_body (*worker_msg_parser)(const struct msg *); -- cgit v1.2.3