diff options
author | Mattias Andrée <m@maandree.se> | 2025-02-10 17:50:58 +0100 |
---|---|---|
committer | Mattias Andrée <m@maandree.se> | 2025-02-10 17:52:46 +0100 |
commit | ec1bcdcd0dd6e196303e8d9a30b3b2740e32c502 (patch) | |
tree | dcc759aaf897c915827659e00644f12503cf1268 /libcoopgamma_query__.c | |
parent | Improve makefile (diff) | |
download | libcoopgamma-ec1bcdcd0dd6e196303e8d9a30b3b2740e32c502.tar.gz libcoopgamma-ec1bcdcd0dd6e196303e8d9a30b3b2740e32c502.tar.bz2 libcoopgamma-ec1bcdcd0dd6e196303e8d9a30b3b2740e32c502.tar.xz |
Minor code improvements and split into multiple c files
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to 'libcoopgamma_query__.c')
-rw-r--r-- | libcoopgamma_query__.c | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/libcoopgamma_query__.c b/libcoopgamma_query__.c new file mode 100644 index 0000000..706a3cf --- /dev/null +++ b/libcoopgamma_query__.c @@ -0,0 +1,119 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/** + * Run coopgammad with -q or -qq and return the response + * + * SIGCHLD must not be ignored or blocked + * + * @param method The adjustment method, `NULL` for automatic + * @param site The site, `NULL` for automatic + * @param arg "-q" or "-qq", which shall be passed to coopgammad? + * @return The output of coopgammad, `NULL` on error. This + * will be NUL-terminated and will not contain any + * other `NUL` bytes. + */ +char * +libcoopgamma_query__(const char *restrict method, const char *restrict site, const char *restrict arg) +{ + const char *(args[7]) = {COOPGAMMAD, arg}; + size_t i = 2, n = 0, size = 0; + int pipe_rw[2] = { -1, -1 }; + pid_t pid; + int saved_errno, status; + char *msg = NULL; + ssize_t got; + void *new; + + if (method) args[i++] = "-m", args[i++] = method; + if (site) args[i++] = "-s", args[i++] = site; + args[i] = NULL; + + if (pipe(pipe_rw) < 0) + goto fail; + + switch ((pid = fork())) { + case -1: + goto fail; + + case 0: + /* Child */ + close(pipe_rw[0]); + if (pipe_rw[1] != STDOUT_FILENO) { + close(STDOUT_FILENO); + if (dup2(pipe_rw[1], STDOUT_FILENO) < 0) + goto fail_child; + close(pipe_rw[1]); + } +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-qual" +#endif + execvp(COOPGAMMAD, (char* const*)(args)); +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + fail_child: + saved_errno = errno; + perror(NAME_OF_THE_PROCESS); + if (write(STDOUT_FILENO, &saved_errno, sizeof(int)) != sizeof(int)) + perror(NAME_OF_THE_PROCESS); + exit(1); + + default: + /* Parent */ + close(pipe_rw[1]), pipe_rw[1] = -1; + for (;;) { + if (n == size) { + new = realloc(msg, size = (n ? (n << 1) : 256U)); + if (!new) + goto fail; + msg = new; + } + got = read(pipe_rw[0], &msg[n], size - n); + if (got < 0) { + if (errno == EINTR) + continue; + goto fail; + } else if (got == 0) { + break; + } + n += (size_t)got; + } + close(pipe_rw[0]), pipe_rw[0] = -1; + if (waitpid(pid, &status, 0) < 0) + goto fail; + if (status) { + errno = EINVAL; + if (n == sizeof(int) && *(int *)msg) + errno = *(int*)msg; + } + break; + } + + if (n == size) { + new = realloc(msg, n + 1U); + if (!new) + goto fail; + msg = new; + } + msg[n] = '\0'; + + if (strchr(msg, '\0') != msg + n) { + errno = EBADMSG; + goto fail; + } + + return msg; + +fail: + saved_errno = errno; + if (pipe_rw[0] >= 0) + close(pipe_rw[0]); + if (pipe_rw[1] >= 0) + close(pipe_rw[1]); + free(msg); + errno = saved_errno; + return NULL; +} |