/* 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 . */ #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); }