diff options
Diffstat (limited to 'src/job.c')
-rw-r--r-- | src/job.c | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/src/job.c b/src/job.c new file mode 100644 index 0000000..9678139 --- /dev/null +++ b/src/job.c @@ -0,0 +1,278 @@ +/* wydawca - automatic release submission daemon + Copyright (C) 2009 Sergey Poznyakoff + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 3 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "wydawca.h" + +#define STATE_FINISHED 0x01 +#define STATE_QUEUED 0x02 +#define STATE_ACTIVE 0x04 + +struct job +{ + struct job *next, *prev; + int state; + const struct spool *spool; + uid_t uid; + pid_t pid; + time_t timestamp; + int exit_status; +}; + +struct job *queue; +size_t jobmax; +size_t jobcnt; + +struct job * +job_locate (const struct spool *spool, uid_t uid) +{ + struct job *p; + for (p = queue; p; p = p->next) + if (p->spool == spool && p->uid == uid) + break; + return p; +} + +size_t +job_active_count () +{ + struct job *job; + size_t count = 0; + for (job = queue; job; job = job->next) + if (job->state & STATE_ACTIVE) + count++; + return count; +} + +void +wydawca_scanner (struct job *job) +{ + scan_spool (job->spool, 1, &job->uid); + mail_finish (); +} + +int +job_start (struct job *job) +{ + pid_t pid; + + if (jobmax && jobcnt == jobmax) + { + logmsg (LOG_NOTICE, "maximum number of processes active"); + return 1; + } + + if (debug_level) + logmsg (LOG_DEBUG, _("starting job: %s, %lu"), job->spool->tag, job->uid); + + if (single_process) + { + wydawca_scanner (job); + return 0; + } + + pid = fork (); + if (pid == 0) + { + wydawca_scanner (job); + exit (0); + } + else if (pid == -1) + { + logmsg (LOG_CRIT, "fork: %s", strerror (errno)); + return -1; + } + else + { + job->state = STATE_ACTIVE; + job->pid = pid; + jobcnt++; + } + return 0; +} + +void +job_remove (struct job *job) +{ + struct job *p; + + if (debug_level) + logmsg (LOG_DEBUG, _("removing job: %s, %lu"), job->spool->tag, job->uid); + p = job->prev; + if (p) + p->next = job->next; + else + queue = job->next; + + p = job->next; + if (p) + p->prev = job->prev; +} + +void +job_insert (struct job *job, struct job *elt) +{ + struct job *p; + + job->prev = elt; + if (!elt) + { + if (queue) + queue->prev = job; + job->next = queue; + queue = job; + return; + } + + p = elt->next; + elt->next = job; + + if (p) + p->prev = job; +} + +int +schedule_job (const struct spool *spool, uid_t uid) +{ + struct job *job; + + if (debug_level) + logmsg (LOG_DEBUG, _("scheduling job: %s, %lu"), spool->tag, uid); + + job = job_locate (spool, uid); + if (!job) + { + job = xzalloc (sizeof (*job)); + job->spool = spool; + job->uid = uid; + job->pid = -1; + time (&job->timestamp); + job_insert (job, NULL); + } + + job->state |= STATE_QUEUED; + job_start (job); +} + +static void +print_status (struct job *job, int expect_term) +{ + struct passwd *pw = getpwuid (job->uid); + int status = job->exit_status; + + if (WIFEXITED (status)) + { + if (WEXITSTATUS (status) == 0) + { + if (debug_level) + logmsg (LOG_DEBUG, + _("%lu (%s, %s) exited successfully"), + (unsigned long) job->pid, job->spool->tag, pw->pw_name); + } + else + logmsg (LOG_ERR, + _("%lu (%s, %s) failed with status %d"), + (unsigned long) job->pid, job->spool->tag, pw->pw_name, + WEXITSTATUS (status)); + } + else if (WIFSIGNALED (status)) + { + int prio; + if (expect_term && WTERMSIG (status) == SIGTERM) + { + if (!debug_level) + return; + prio = LOG_DEBUG; + } + else + prio = LOG_ERR; + + logmsg (prio, + _("%lu (%s, %s) terminated on signal %d"), + job->pid, job->spool->tag, pw->pw_name, WTERMSIG (status)); + } + else if (WIFSTOPPED (status)) + logmsg (LOG_NOTICE, + _("%lu (%s, %s) stopped on signal %d"), + job->pid, job->spool->tag, pw->pw_name, WSTOPSIG (status)); +#ifdef WCOREDUMP + else if (WCOREDUMP (status)) + logmsg (LOG_NOTICE, + _("%lu (%s, %s) dumped core"), + job->pid, job->spool->tag, pw->pw_name); +#endif + else + logmsg (LOG_ERR, + _("%lu (%s, %s) terminated with unrecognized status"), + job->pid, job->spool->tag, pw->pw_name); +} + +static int wakeup; + +RETSIGTYPE +queue_signal (int sig) +{ + wakeup = 1; + signal (sig, queue_signal); +} + +void +job_queue_runner () +{ + int status; + struct job *job; + + if (!wakeup) + return; + wakeup = 0; + + for (;;) + { + pid_t pid = waitpid ((pid_t)-1, &status, WNOHANG); + if (pid <= 0) + break; + for (job = queue; job; job = job->next) + { + if ((job->state & STATE_ACTIVE) && job->pid == pid) + { + job->state &= ~STATE_ACTIVE; + job->state |= STATE_FINISHED; + job->exit_status = status; + jobcnt--; + } + } + } + + for (job = queue; job;) + { + struct job *next = job->next; + if (job->state & STATE_FINISHED) + { + print_status (job, 0); + if ((job->state &= ~STATE_FINISHED) == 0) + job_remove (job); + } + if (job->state == STATE_QUEUED) + if (job_start (job)) + pause (); + job = next; + } +} + +void +job_init () +{ + signal (SIGCHLD, queue_signal); +} |