Mercurial > sci
view sciworkerd/task.c @ 59:835d52f72786
lib: call closelog
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 18 Aug 2022 10:12:46 +0200 |
parents | e52c762d8ba8 |
children | 67470b67e460 |
line wrap: on
line source
/* * task.c -- worker task management * * Copyright (c) 2021-2022 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #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 "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; 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); int fd = -1, ret = -1; snprintf(self->scriptpath, sizeof (self->scriptpath), "/tmp/sciworkerd-XXXXXX"); if ((fd = mkstemp(self->scriptpath)) < 0) goto failed; if (fchmod(fd, MODE) < 0) goto failed; if (write(fd, script, len) != (ssize_t)len) goto failed; ret = 0; failed: if (ret == -1) log_warn("%s", strerror(errno)); close(fd); return ret; } 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 self->child; } 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) != (size_t)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); }