Mercurial > sci
view sciworkerd.c @ 1:5afdb14df924
sci: add support for storing results
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 08 Jun 2021 08:40:01 +0200 |
parents | f1de39079243 |
children | 5fa3d2f479b2 |
line wrap: on
line source
#include <sys/queue.h> #include <sys/types.h> #include <sys/wait.h> #include <assert.h> #include <err.h> #include <errno.h> #include <fcntl.h> #include <poll.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "project.h" #include "config.h" #include "log.h" #include "util.h" struct job { pid_t child; int running; int pipe[2]; char project[PROJECT_NAME_MAX]; char out[SCI_CONSOLE_MAX]; TAILQ_ENTRY(job) link; }; struct fds { struct pollfd *list; size_t listsz; }; struct result { pid_t pid; int status; }; TAILQ_HEAD(jobs, job); static struct jobs running = TAILQ_HEAD_INITIALIZER(running); static struct jobs queue = TAILQ_HEAD_INITIALIZER(queue); static int sigpipe[2]; static struct job * find(const char *project) { struct job *j; TAILQ_FOREACH(j, &running, link) if (strcmp(j->project, project) == 0) return j; return NULL; } static struct job * find_by_fd(int fd) { struct job *j; TAILQ_FOREACH(j, &running, link) if (j->pipe[0] == fd) return j; return NULL; } static struct job * find_by_pid(pid_t pid) { struct job *j; TAILQ_FOREACH(j, &running, link) if (j->child == pid) return j; return NULL; } static int spawn(const char *project, const char *script) { struct job *j; if (find(project)) return -1; j = util_calloc(1, sizeof (*j)); j->pipe[0] = j->pipe[1] = -1; j->running = 1; strlcpy(j->project, project, sizeof (j->project)); if (pipe(j->pipe) < 0) goto cleanup; switch ((j->child = fork())) { case -1: goto cleanup; case 0: /* Child. */ dup2(j->pipe[1], STDOUT_FILENO); dup2(j->pipe[1], STDERR_FILENO); close(j->pipe[0]); close(j->pipe[1]); if (execl(script, script, NULL) < 0) exit(0); break; default: /* Parent */ TAILQ_INSERT_TAIL(&running, j, link); break; } return 0; cleanup: if (j->pipe[0] != -1) close(j->pipe[0]); if (j->pipe[1] != -1) close(j->pipe[1]); free(j); return -1; } static void complete(int signum, siginfo_t *sinfo, void *ctx) { (void)ctx; (void)signum; struct result r; if (sinfo->si_code != CLD_EXITED) return; r.pid = sinfo->si_pid; r.status = 0; if (waitpid(sinfo->si_pid, &r.status, 0) < 0) { log_warn("waitpid: %s", strerror(errno)); return; } /* * Signal may happen at any time from any thread so we can't use * mutexes so use the good old self-pipe trick. Yes, signals are * probably the most fundamental broken UNIX feature. */ if (write(sigpipe[1], &r, sizeof (r)) < 0) err(1, "write"); } static void init(void) { struct sigaction sa; int flags; sa.sa_flags = SA_SIGINFO; sa.sa_sigaction = complete; sigemptyset(&sa.sa_mask); if (sigaction(SIGCHLD, &sa, NULL) < 0) err(1, "sigaction"); log_open("sciworkerd"); if (pipe(sigpipe) < 0) err(1, "pipe"); if ((flags = fcntl(sigpipe[1], F_GETFL, 0)) < 0 || fcntl(sigpipe[1], F_SETFL, flags | O_NONBLOCK) < 0) err(1, "fcntl"); } static struct fds prepare(void) { struct fds fds = {0}; struct job *j; size_t i = 1; TAILQ_FOREACH(j, &running, link) fds.listsz++; fds.list = util_calloc(++fds.listsz, sizeof (*fds.list)); fds.list[0].fd = sigpipe[0]; fds.list[0].events = POLLIN; TAILQ_FOREACH(j, &running, link) { fds.list[i].fd = j->pipe[0]; fds.list[i++].events = POLLIN | POLLPRI; } return fds; } static void finished(pid_t pid) { struct job *job; if (!(job = find_by_pid(pid))) return; /* TODO: send response. */ TAILQ_REMOVE(&running, job, link); free(job); } static void run(void) { struct fds fds; struct result r; struct job *job; char buf[BUFSIZ]; ssize_t nr; fds = prepare(); if (poll(fds.list, fds.listsz, -1) < 0 && errno != EINTR) err(1, "poll"); for (size_t i = 1; i < fds.listsz; ++i) { if (fds.list[i].revents == 0) continue; if (!(job = find_by_fd(fds.list[i].fd))) continue; if ((nr = read(fds.list[i].fd, buf, sizeof (buf) - 1)) <= 0) { finished(job->child); } else { buf[nr] = 0; strlcat(job->out, buf, sizeof (job->out)); } } if (fds.list->revents) { r.pid = 0; r.status = 0; if (read(sigpipe[0], &r, sizeof (r)) <= 0 && errno != EINTR) err(1, "read"); finished(r.pid); } } int main(int argc, char **argv) { (void)argc; (void)argv; init(); for (;;) run(); }