summaryrefslogtreecommitdiffabout
path: root/src/job.c
Side-by-side diff
Diffstat (limited to 'src/job.c') (more/less context) (ignore whitespace changes)
-rw-r--r--src/job.c278
1 files changed, 278 insertions, 0 deletions
diff --git a/src/job.c b/src/job.c
new file mode 100644
index 0000000..9678139
--- a/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);
+}

Return to:

Send suggestions and report system problems to the System administrator.