Mercurial > sci
view sciworkerd/task.c @ 22:dd078aea5d02
misc: use project/worker name as primary key
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 21 Jul 2022 20:23:22 +0200 |
parents | de4bf839b565 |
children | 34cbbd215ef7 |
line wrap: on
line source
#include <sys/stat.h> #include <sys/wait.h> #include <assert.h> #include <errno.h> #include <limits.h> #include <poll.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> #include "log.h" #include "task.h" #include "types.h" #include "util.h" #define MODE S_IRUSR | S_IWUSR | S_IXUSR struct task { enum taskstatus status; pid_t child; int pipe[2]; int exitcode; int sigcode; char *job_tag; FILE *fp; char *console; size_t consolesz; int scriptfd; char scriptpath[PATH_MAX]; time_t startup; }; struct task * task_new(const char *tag) { struct task *task; task = util_calloc(1, sizeof (*task)); task->job_tag = util_strdup(tag); task->pipe[0] = task->pipe[1] = -1; task->child = -1; if (!(task->fp = open_memstream(&task->console, &task->consolesz))) { task_free(task); return NULL; } return task; } int task_setup(struct task *self, const char *script) { assert(self); assert(script); const size_t len = strlen(script); snprintf(self->scriptpath, sizeof (self->scriptpath), "/tmp/sciworkerd-XXXXXX"); if ((self->scriptfd = mkstemp(self->scriptpath)) < 0) goto failed; if (fchmod(self->scriptfd, MODE) < 0) goto failed; if (write(self->scriptfd, script, len) != len) goto failed; return 0; failed: log_warn("%s", strerror(errno)); unlink(self->scriptpath); return -1; } pid_t task_start(struct task *self) { assert(self); assert(self->status == TASKSTATUS_PENDING); if (pipe(self->pipe) < 0) return -1; switch ((self->child = fork())) { case -1: return -1; case 0: dup2(self->pipe[1], STDOUT_FILENO); dup2(self->pipe[1], STDERR_FILENO); close(self->pipe[0]); close(self->pipe[1]); close(STDIN_FILENO); if (execl(self->scriptpath, self->scriptpath, self->job_tag, NULL) < 0) _exit(1); break; default: close(self->pipe[1]); self->pipe[1] = -1; self->status = TASKSTATUS_RUNNING; self->startup = time(NULL); break; } return 0; } int task_wait(struct task *self) { assert(self); assert(self->status == TASKSTATUS_RUNNING); int status; pid_t ret; while ((ret = waitpid(self->child, &status, 0)) < 0 && errno == EINTR) continue; if (ret < 0) return ret; if (WIFEXITED(status)) { self->exitcode = WEXITSTATUS(status); self->status = TASKSTATUS_EXITED; } else if (WIFSIGNALED(status)) { self->sigcode = WTERMSIG(status); self->status = TASKSTATUS_KILLED; } self->child = -1; /* Close file output so user can get access to the console. */ fclose(self->fp); self->fp = NULL; return 0; } int task_kill(struct task *self) { assert(self); assert(self->status == TASKSTATUS_RUNNING); if (kill(self->child, SIGTERM) < 0) return -1; return 0; } time_t task_uptime(const struct task *self) { assert(self); return self->startup; } pid_t task_pid(const struct task *self) { assert(self); assert(self->status == TASKSTATUS_RUNNING); return self->child; } const char * task_console(const struct task *self) { assert(self); return self->console; } void task_prepare(struct task *self, struct pollfd *fd) { assert(self); assert(self->status == TASKSTATUS_RUNNING); assert(fd); fd->fd = self->pipe[0]; fd->events = POLLIN | POLLPRI; } int task_sync(struct task *self, const struct pollfd *fd) { assert(self); assert(self->status == TASKSTATUS_RUNNING); assert(fd->fd == self->pipe[0]); char buf[BUFSIZ]; ssize_t nr; if (fd->revents & POLLHUP) return 0; /* If we read EOF, it usually means the process has exited correctly. */ if (fd->revents & POLLIN) { if ((nr = read(self->pipe[0], buf, sizeof (buf))) <= 0) return nr; if (fwrite(buf, 1, nr, self->fp) != nr) return -1; } return 1; } enum taskstatus task_status(const struct task *self) { assert(self); return self->status; } struct taskcode task_code(const struct task *self) { assert(self); return (struct taskcode) { .exitcode = self->exitcode, .sigcode = self->sigcode }; } void task_free(struct task *self) { assert(self); assert(self->status != TASKSTATUS_RUNNING); if (self->pipe[0]) close(self->pipe[0]); if (self->pipe[1]) close(self->pipe[1]); if (self->fp) fclose(self->fp); if (self->scriptpath[0]) unlink(self->scriptpath); free(self->job_tag); free(self->console); free(self); }