aboutsummaryrefslogtreecommitdiff
path: root/pies/progman.c
diff options
context:
space:
mode:
Diffstat (limited to 'pies/progman.c')
-rw-r--r--pies/progman.c971
1 files changed, 971 insertions, 0 deletions
diff --git a/pies/progman.c b/pies/progman.c
new file mode 100644
index 0000000..d2bb6f0
--- /dev/null
+++ b/pies/progman.c
@@ -0,0 +1,971 @@
+/* This file is part of Mailfromd.
+ Copyright (C) 2007, 2008 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 "pies.h"
+/* FIXME */
+const char *mu_umaxtostr (unsigned slot, uintmax_t val);
+
+#define TYPE_COMPONENT 0
+#define TYPE_RETR 1
+
+enum prog_status
+ {
+ status_enabled, /* Component enabled. prog->pid!=0 shows if it is
+ actually running */
+ status_disabled, /* Component is disabled. */
+ status_sleeping, /* Component is sleeping. An attempt to start it will
+ be made at prog->v.p.timestamp + SLEEPTIME */
+ status_stopping, /* Component is being stopped */
+ };
+
+struct prog
+{
+ struct prog *next;
+ int type;
+ pid_t pid; /* PID */
+ char *tag; /* Entry tag (for diagnostics purposes) */
+ int idx; /* Numeric identifier */
+ char **depend;
+ union
+ {
+ struct
+ {
+ int argc;
+ char **argv; /* Command line arguments */
+ char **env; /* Environment */
+ char *dir; /* Working directory */
+ int retr[2];
+ char *rmfile; /* Rmfile location */
+ struct pies_privs_data privs;
+ mode_t umask;
+ time_t timestamp; /* Time of last startup */
+ size_t count; /* Number of failed starts since timestamp */
+ enum prog_status status; /* Current component status */
+ struct action *act[MAX_RETURN_CODE+1];
+ } p;
+
+ struct
+ {
+ struct prog *master;
+ } r;
+ } v;
+};
+
+#define IS_PROG(p) ((p)->type == TYPE_COMPONENT)
+
+static int numprog;
+static struct prog *proghead, *progtail;
+static pies_depmap_t depmap;
+
+static struct prog *
+prog_lookup_by_pid (pid_t pid)
+{
+ struct prog *prog;
+
+ for (prog = proghead; prog; prog = prog->next)
+ if (prog->pid == pid)
+ break;
+ return prog;
+}
+
+struct prog *
+prog_lookup_by_tag (const char *tag)
+{
+ struct prog *prog;
+ for (prog = proghead; prog; prog = prog->next)
+ if (strcmp (prog->tag, tag) == 0)
+ break;
+ return prog;
+}
+
+struct prog *
+prog_lookup_by_idx (unsigned idx)
+{
+ struct prog *prog;
+ for (prog = proghead; prog; prog = prog->next)
+ if (prog->idx == idx)
+ break;
+ return prog;
+}
+
+static void prog_stop (struct prog *prog, int sig);
+void prog_start_dependencies (struct prog *prog);
+
+void
+link_prog (struct prog *pp, int prepend)
+{
+ if (prepend)
+ {
+ pp->next = proghead;
+ proghead = pp;
+ if (!progtail)
+ progtail = pp;
+ }
+ else
+ {
+ pp->next = NULL;
+ if (progtail)
+ progtail->next = pp;
+ else
+ proghead = pp;
+ progtail = pp;
+ }
+}
+
+static char *
+retr_tag (const char *tag, int type)
+{
+ static char *retrstr[2] = { "stdout", "stderr" };
+ size_t taglen = strlen (tag) + 1 + strlen (retrstr[type]);
+ char *retr_tag = xmalloc (taglen + 1);
+
+ strcpy (retr_tag, tag);
+ strcat (retr_tag, "/");
+ strcat (retr_tag, retrstr[type]);
+ return retr_tag;
+}
+
+void
+register_retr (int type, struct prog *master)
+{
+ char *tag = retr_tag (master->tag, type);
+ char *pstr;
+ struct prog *pp = xzalloc (sizeof (*pp) + strlen (tag) + 1 +
+ 2 * sizeof(char**));
+
+ pp->type = TYPE_RETR;
+ pstr = (char *) (pp + 1);
+ pp->tag = pstr;
+ strcpy (pp->tag, tag);
+ pstr += strlen (pp->tag) + 1;
+ free (tag);
+ pp->v.r.master = master;
+ pp->idx = numprog++;
+ pp->depend = (char**)pstr;
+ pp->depend[0] = master->tag;
+ pp->depend[1] = NULL;
+ link_prog (pp, 1);
+}
+
+void
+update_retr (int type, struct prog *master, pid_t pid)
+{
+ char *tag = retr_tag (master->tag, type);
+ struct prog *pp = prog_lookup_by_tag (tag);
+
+ free (tag);
+ prog_stop (pp, SIGKILL); /* Just in case */
+ pp->pid = pid;
+}
+
+void
+register_prog (struct component *comp)
+{
+ struct prog *p, *newp;
+ char *pstr;
+ int i;
+ size_t size = 0;
+ int dep_all = 0;
+ int depc = 0;
+
+ if (comp->depend)
+ {
+ if (comp->depend[0] && comp->depend[1] == NULL)
+ {
+ if (strcmp (comp->depend[0], "all") == 0)
+ {
+ dep_all = 1;
+ for (p = proghead; p; p = p->next)
+ if (p->type == TYPE_COMPONENT)
+ depc++;
+ }
+ else if (strcmp (comp->depend[0], "none") == 0)
+ comp->depend = NULL;
+ }
+
+ if (depc == 0)
+ for (depc = 0; comp->depend[depc]; depc++)
+ ;
+ }
+
+ if (depc)
+ size += sizeof (comp->depend[0]) * (depc + 1);
+
+ if (comp->dir)
+ size += strlen (comp->dir) + 1;
+
+ size += sizeof (*newp)
+ + (comp->tag ? (strlen (comp->tag) + 1) : 0);
+ + (comp->rmfile ? (strlen (comp->rmfile) + 1) : 0);
+
+ for (i = 0; comp->argv[i]; i++)
+ ;
+
+ newp = xzalloc (size);
+ newp->type = TYPE_COMPONENT;
+ newp->pid = 0;
+ newp->idx = numprog++;
+ newp->v.p.argc = i;
+ newp->v.p.argv = comp->argv;
+ newp->v.p.env = comp->env;
+ if (comp->disabled)
+ newp->v.p.status = status_disabled;
+ for (i = 0; i <= MAX_RETURN_CODE; i++)
+ newp->v.p.act[i] = comp->act[i] ? comp->act[i] : default_component.act[i];
+
+ pstr = (char*) (newp + 1);
+
+ if (comp->tag)
+ {
+ newp->tag = strcpy (pstr, comp->tag);
+ pstr += strlen (pstr) + 1;
+ }
+ else
+ newp->tag = newp->v.p.argv[0];
+
+ if (comp->dir)
+ {
+ newp->v.p.dir = strcpy (pstr, comp->dir);
+ pstr += strlen (pstr) + 1;
+ }
+ else
+ newp->v.p.dir = NULL;
+
+ if (comp->rmfile)
+ {
+ newp->v.p.rmfile = strcpy (pstr, comp->rmfile);
+ pstr += strlen (pstr) + 1;
+ }
+ else
+ newp->v.p.rmfile = NULL;
+
+ if (depc)
+ {
+ newp->depend = (char**) pstr;
+ pstr = (char*) (newp->depend + depc + 1);
+
+ depc = 0;
+ if (comp->depend)
+ {
+ if (dep_all)
+ {
+ for (p = proghead; p; p = p->next)
+ if (p->type == TYPE_COMPONENT)
+ newp->depend[depc++] = p->tag;
+ }
+ else
+ {
+ for (; comp->depend[depc]; depc++)
+ newp->depend[depc] = comp->depend[depc];
+ }
+ }
+ newp->depend[depc] = NULL;
+ }
+ else
+ newp->depend = NULL;
+
+ if ((newp->v.p.retr[0] = comp->retr[0]) != -1)
+ register_retr (0, newp);
+ if ((newp->v.p.retr[1] = comp->retr[1]) != -1)
+ register_retr (1, newp);
+
+ newp->v.p.privs = comp->privs;
+ newp->v.p.umask = comp->umask;
+
+ link_prog (newp, 0);
+}
+
+size_t
+progman_running_count ()
+{
+ size_t size = 0;
+ struct prog *prog;
+
+ for (prog = proghead; prog; prog = prog->next)
+ if (prog->pid > 0)
+ size++;
+ return size;
+}
+
+RETSIGTYPE
+retr_exit (int sig)
+{
+ exit (0);
+}
+
+int
+open_retranslator (struct prog *master, int type)
+{
+ int p[2];
+ FILE *fp;
+ char *buf = NULL;
+ size_t size = 0;
+ pid_t pid;
+
+ if (master->v.p.retr[type] == -1)
+ return -1;
+ pipe (p);
+ switch (pid = fork ())
+ {
+ case 0:
+ /* Retranslator process */
+
+ log_setup (0);
+ signal_setup (retr_exit);
+
+ close (p[1]);
+ fp = fdopen (p[0], "r");
+ if (fp == NULL)
+ exit (1);
+
+ openlog (master->tag, LOG_PID, mu_log_facility);
+
+ while (getline (&buf, &size, fp) > 0)
+ syslog (master->v.p.retr[type], "%s", buf);
+ exit (0);
+
+ case -1:
+ mu_diag_output (MU_DIAG_CRIT,
+ _("cannot run retranslator `%s': fork failed: %s"),
+ master->tag, mu_strerror (errno));
+ return -1;
+
+ default:
+ update_retr (type, master, pid);
+ close (p[0]);
+ return p[1];
+ }
+}
+
+extern char **environ;
+
+static char *
+find_env (char *name)
+{
+ int nlen = strlen (name);
+ int i;
+
+ for (i = 0; environ[i]; i++)
+ {
+ char *p = strchr (environ[i], '=');
+ int elen = p - environ[i];
+ if (elen == nlen && memcmp (name, environ[i], nlen) == 0)
+ return environ[i];
+ }
+ return NULL;
+}
+
+static void
+env_setup (char **env)
+{
+ char **old_env = environ;
+ char **new_env;
+ int count, i, n;
+
+ if (!env)
+ return;
+
+ if (strcmp (env[0], "-") == 0)
+ {
+ old_env = NULL;
+ env++;
+ }
+
+ /* Count new environment size */
+ count = 0;
+ if (old_env)
+ for (; old_env[count]; count++)
+ ;
+
+ for (; env[count]; count++)
+ ;
+
+ /* Allocate the new environment. */
+ new_env = xcalloc (count + 1, sizeof new_env[0]);
+
+ /* Populate the environment. */
+ n = 0;
+
+ if (old_env)
+ for (i = 0; old_env[i]; i++)
+ new_env[n++] = old_env[i];
+
+ for (i = 0; env[i]; i++)
+ {
+ if (strchr (env[i], '='))
+ new_env[n++] = env[i];
+ else
+ {
+ char *p = find_env (env[i]);
+ if (p)
+ new_env[n++] = p;
+ }
+ }
+ new_env[n] = NULL;
+ /* FIXME: The old environment is not freed. Should it be? */
+ environ = new_env;
+}
+
+static void
+prog_start (struct prog *prog)
+{
+ int i;
+ pid_t pid;
+ time_t now;
+ int retr[2];
+
+ if (prog->pid > 0 || !IS_PROG (prog))
+ return;
+
+ time (&now);
+
+ if (prog->v.p.timestamp + TESTTIME > now)
+ prog->v.p.count++;
+ else
+ {
+ prog->v.p.count = 0;
+ prog->v.p.timestamp = now;
+ }
+
+ if (prog->v.p.count > MAXSPAWN)
+ {
+ int old_alarm;
+
+ mu_error (_("%s is respawning too fast, disabled for %d minutes"),
+ prog->tag, SLEEPTIME / 60);
+ prog->v.p.timestamp = now;
+ prog->v.p.status = status_disabled;
+
+ old_alarm = alarm (0);
+ if (old_alarm > SLEEPTIME || old_alarm <= 0)
+ old_alarm = SLEEPTIME;
+ alarm (old_alarm);
+ return;
+ }
+
+ prog_start_dependencies (prog);
+ if (prog->v.p.status == status_disabled)
+ return; /* FIXME: other statuses */
+ mu_diag_output (MU_DIAG_DEBUG, _("starting %s"), prog->tag);
+
+ if (prog->v.p.rmfile)
+ {
+ if (unlink (prog->v.p.rmfile) && errno != ENOENT)
+ mu_error (_("%s: cannot remove file `%s': %s"),
+ prog->tag, prog->v.p.rmfile, mu_strerror (errno));
+ }
+
+ retr[RETR_OUT] = open_retranslator (prog, RETR_OUT);
+ retr[RETR_ERR] = open_retranslator (prog, RETR_ERR);
+
+ switch (pid = fork ())
+ {
+ /* The child branch. */
+ case 0:
+ if (retr[RETR_OUT] == -1)
+ {
+ close (1);
+ open ("/dev/null", O_RDWR);
+ }
+ else if (retr[RETR_OUT] != 1)
+ {
+ close (1);
+ dup2 (retr[RETR_OUT], 1);
+ }
+
+ if (retr[RETR_ERR] == -1)
+ {
+ close (2);
+ open ("/dev/null", O_RDWR);
+ }
+ else if (retr[RETR_ERR] != 1)
+ {
+ close (2);
+ dup2 (retr[RETR_ERR], 2);
+ }
+
+ /* Close unneded descripitors */
+ for (i = getmaxfd (); i > 2; i--)
+ close (i);
+
+ signal_setup (SIG_DFL);
+ if (prog->v.p.dir)
+ {
+ if (chdir (prog->v.p.dir))
+ mu_error (_("%s: cannot change to directory %s: %s"),
+ prog->tag, prog->v.p.dir, mu_strerror (errno));
+ }
+
+ env_setup (prog->v.p.env);
+ priv_setup (&prog->v.p.privs);
+ if (prog->v.p.umask)
+ umask (prog->v.p.umask);
+
+ execvp (prog->v.p.argv[0], prog->v.p.argv);
+ openlog (MU_LOG_TAG(), LOG_PID, mu_log_facility);
+ syslog (LOG_CRIT, _("cannot start `%s': %s"), prog->tag,
+ mu_strerror (errno));
+ exit (EX_SOFTWARE);
+
+ case -1:
+ mu_diag_output (MU_DIAG_CRIT,
+ _("cannot run `%s': fork failed: %s"),
+ prog->tag, mu_strerror (errno));
+ break;
+
+ default:
+ prog->pid = pid;
+ }
+}
+
+static void
+build_depmap ()
+{
+ unsigned i;
+ struct prog *prog;
+ pies_depmap_t dp;
+
+ depmap = depmap_alloc (numprog);
+ for (prog = proghead; prog; prog = prog->next)
+ if (prog->depend)
+ {
+ for (i = 0; prog->depend[i]; i++)
+ {
+ struct prog *dep = prog_lookup_by_tag (prog->depend[i]);
+ if (!dep)
+ {
+ prog->v.p.status = status_disabled;
+ mu_error (_("component %s depends on %s, "
+ "which is not declared"),
+ prog->tag, prog->depend[i]);
+ }
+ else
+ depmap_set (depmap, prog->idx, dep->idx);
+ }
+ }
+ dp = depmap_copy (depmap);
+ depmap_tc (dp);
+ for (i = 0; i < numprog; i++)
+ if (depmap_isset (dp, i, i))
+ {
+ prog = prog_lookup_by_idx (i);
+ mu_error (_("component %s depends on itself"), prog->tag);
+ prog->v.p.status = status_disabled;
+ }
+ free (dp);
+}
+
+void
+progman_start ()
+{
+ struct prog *prog;
+
+ build_depmap ();
+
+ for (prog = proghead; prog; prog = prog->next)
+ if (IS_PROG (prog) && prog->v.p.status != status_disabled)
+ prog_start (prog);
+}
+
+void
+progman_wake_sleeping ()
+{
+ struct prog *prog;
+
+ for (prog = proghead; prog; prog = prog->next)
+ if (IS_PROG (prog) && prog->v.p.status == status_sleeping)
+ {
+ prog->v.p.status = status_enabled;
+ prog->v.p.count = 0;
+ prog->v.p.timestamp = 0;
+ prog_start (prog);
+ }
+}
+
+void
+prog_start_dependencies (struct prog *prog)
+{
+ int i;
+
+ if (!prog->depend)
+ return;
+ for (i = 0; prog->depend[i]; i++)
+ {
+ struct prog *dp = prog_lookup_by_tag (prog->depend[i]);
+ if (!IS_PROG (dp)) /* Skip retranslators */
+ continue;
+ switch (dp->v.p.status)
+ {
+ case status_disabled:
+ prog->v.p.status = status_disabled;
+ return;
+ /* FIXME: other states */
+ }
+ }
+ for (i = 0; prog->depend[i]; i++)
+ prog_start (prog_lookup_by_tag (prog->depend[i]));
+}
+
+void
+prog_stop_dependent (struct prog *prog)
+{
+ pies_depmap_pos_t pos;
+ unsigned n;
+ int warned = 0;
+
+ for (n = depmap_first (depmap, depmap_row, prog->idx, &pos);
+ n != (unsigned)-1;
+ n = depmap_next (depmap, pos))
+ {
+ struct prog *dp = prog_lookup_by_idx (n);
+ if (!dp) /* should not happen */
+ continue;
+ if (!warned)
+ {
+ mu_diag_output (MU_DIAG_DEBUG, "Stopping dependencies of %s",
+ prog->tag);
+ warned = 1;
+ }
+ prog_stop (dp, SIGTERM);
+ }
+ free (pos);
+}
+
+static void
+prog_stop (struct prog *prog, int sig)
+{
+ if (prog->v.p.status == status_enabled && prog->pid > 0)
+ {
+ prog->v.p.status = status_stopping;
+ prog_stop_dependent (prog);
+ mu_diag_output (MU_DIAG_DEBUG, "Stopping %s (%lu)",
+ prog->tag, (unsigned long) prog->pid);
+ kill (prog->pid, sig);
+ }
+}
+
+static void
+prog_stop_all (int sig)
+{
+ struct prog *prog;
+
+ for (prog = proghead; prog; prog = prog->next)
+ if (IS_PROG (prog) && prog->v.p.status == status_enabled)
+ prog_stop (prog, sig);
+}
+
+void
+progman_stop ()
+{
+ unsigned long i;
+
+ prog_stop_all (SIGTERM);
+ for (i = 0; i < shutdown_timeout; i++)
+ {
+ progman_cleanup (1);
+ if (progman_running_count () == 0)
+ return;
+ sleep (1);
+ }
+ prog_stop_all (SIGKILL);
+}
+
+static void
+print_status (char *tag, pid_t pid, int status, int expect_term)
+{
+ if (WIFEXITED (status))
+ {
+ if (WEXITSTATUS (status) == 0)
+ mu_diag_output (MU_DIAG_DEBUG,
+ _("%s (%lu) exited successfully"),
+ tag, (unsigned long) pid);
+ else
+ mu_diag_output (MU_DIAG_ERR,
+ _("%s (%lu) failed with status %d"),
+ tag, (unsigned long) pid,
+ WEXITSTATUS (status));
+ }
+ else if (WIFSIGNALED (status))
+ {
+ int prio;
+
+ if (expect_term && WTERMSIG (status) == SIGTERM)
+ prio = MU_DIAG_DEBUG;
+ else
+ prio = MU_DIAG_ERR;
+
+ mu_diag_output (prio,
+ _("%s (%lu) terminated on signal %d"),
+ tag, (unsigned long) pid,
+ WTERMSIG (status));
+ }
+ else if (WIFSTOPPED (status))
+ mu_diag_output (MU_DIAG_ERR,
+ _("%s (%lu) stopped on signal %d"),
+ tag, (unsigned long) pid,
+ WSTOPSIG (status));
+#ifdef WCOREDUMP
+ else if (WCOREDUMP (status))
+ mu_diag_output (MU_DIAG_ERR,
+ _("%s (%lu) dumped core"),
+ tag, (unsigned long) pid);
+#endif
+ else
+ mu_diag_output (MU_DIAG_ERR,
+ _("%s (%lu) terminated with unrecognized status"),
+ tag, (unsigned long) pid);
+}
+
+static void
+send_msg (mu_address_t rcpt, mu_message_t msg)
+{
+ mu_mailer_t mailer;
+ int rc;
+
+ rc = mu_mailer_create (&mailer, NULL);
+ if (rc)
+ {
+ const char *mailer_url;
+ mu_mailer_get_url_default (&mailer_url);
+
+ mu_error (_("Cannot create mailer `%s': %s"),
+ mailer_url, mu_strerror (rc));
+ return;
+ }
+
+ /* FIXME: mailer flags? */
+ rc = mu_mailer_open (mailer, 0);
+ if (rc)
+ {
+ const char *mailer_url;
+ mu_mailer_get_url_default (&mailer_url);
+
+ mu_mailer_destroy(&mailer);
+ mu_error (_("Opening mailer `%s' failed: %s"),
+ mailer_url, mu_strerror (rc));
+ return;
+ }
+
+ rc = mu_mailer_send_message (mailer, msg, NULL, rcpt);
+ mu_mailer_destroy (&mailer);
+ if (rc)
+ mu_error (_("Cannot send message: %s"), mu_strerror (rc));
+}
+
+static const char default_termination_message[] =
+"From: <>\n"
+"X-Agent: ${canonical-program-name} (${package} ${version})\n"
+"Subject: Component ${component} terminated with code ${retcode}.\n"
+"\n";
+
+static void
+vartab_define_project (mu_vartab_t vtab)
+{
+ static struct {
+ char *name;
+ char *value;
+ } ptab[] = {
+ { "canonical-program-name", "pies" },
+ { "package", PACKAGE },
+ { "version", PACKAGE_VERSION },
+ { "mu-version", MAILUTILS_VERSION },
+ { NULL }
+ };
+ int i;
+
+ for (i = 0; ptab[i].name; i++)
+ mu_vartab_define (vtab, ptab[i].name, ptab[i].value, 1);
+}
+
+
+static void
+notify (const char *tag, int status, struct action *act)
+{
+ mu_vartab_t vtab;
+ char *msg_text = NULL;
+ mu_message_t msg = NULL;
+ mu_stream_t stream = NULL;
+ int rc;
+
+ mu_vartab_create (&vtab);
+ mu_vartab_define (vtab, "component", tag, 1);
+ mu_vartab_define (vtab, "retcode", mu_umaxtostr (0, status), 1);
+ mu_vartab_define (vtab, "program-name", mu_program_name, 1);
+ vartab_define_project (vtab);
+ rc = mu_vartab_expand (vtab,
+ act->message ?
+ act->message : default_termination_message,
+ &msg_text);
+ mu_vartab_destroy (&vtab);
+ if (rc)
+ {
+ mu_error (_("cannot expand message body: %s"), mu_strerror (rc));
+ /* FIXME: Notify anyway? */
+ return;
+ }
+
+ rc = mu_message_create (&msg, NULL);
+ if (rc)
+ {
+ mu_error (_("cannot create message: %s"), mu_strerror (rc));
+ free (msg_text);
+ }
+
+ mu_message_get_stream (msg, &stream);
+ mu_stream_write (stream, msg_text, strlen (msg_text), 0, NULL);
+ free (msg_text);
+
+ send_msg (act->addr, msg);
+ mu_message_destroy (&msg, mu_message_get_owner (msg));
+}
+
+void
+progman_cleanup (int expect_term)
+{
+ pid_t pid;
+ int status;
+ while ((pid = waitpid (-1, &status, WNOHANG)) > 0)
+ {
+ struct prog *prog = prog_lookup_by_pid (pid);
+ if (!prog)
+ {
+ mu_diag_output (MU_DIAG_NOTICE,
+ _("subprocess %lu finished"),
+ (unsigned long) pid);
+ continue;
+ }
+ print_status (prog->tag, prog->pid, status, expect_term);
+ prog->pid = 0;
+ if (IS_PROG (prog))
+ {
+ prog_stop_dependent (prog);
+ if (!expect_term)
+ {
+ if (WIFEXITED (status))
+ {
+ struct action *act;
+ status = WEXITSTATUS (status);
+ if (status <= MAX_RETURN_CODE
+ && prog->v.p.act && (act = prog->v.p.act[status]))
+ {
+ switch (act->act)
+ {
+ case action_restart:
+ prog_start (prog);
+ break;
+
+ case action_disable:
+ mu_diag_output (MU_DIAG_NOTICE,
+ _("Disabling component %s: exited "
+ "with code %d"),
+ prog->tag, status);
+ prog->v.p.status = status_disabled;
+ }
+ if (act->addr)
+ notify (prog->tag, status, act);
+ continue;
+ }
+ }
+ /* Default action: */
+ prog_start (prog);
+ }
+ }
+ }
+}
+
+void
+progman_stop_component (const char *name)
+{
+ struct prog *prog;
+
+ mu_diag_output (MU_DIAG_INFO, _("stopping component `%s'"), name);
+ for (prog = proghead; prog; prog = prog->next)
+ if (IS_PROG (prog) && strcmp (prog->tag, name) == 0)
+ {
+ if (prog->v.p.status != status_enabled)
+ mu_diag_output (MU_DIAG_INFO,
+ _("stopping component `%s': component not started"),
+ name);
+ else
+ prog_stop (prog, SIGTERM);
+ }
+}
+
+void
+progman_dump_stats (const char *filename)
+{
+ FILE *fp;
+ struct prog *prog;
+ char *s;
+ int rc;
+
+ mu_diag_output (MU_DIAG_INFO, _("dumping statistics to `%s'"), filename);
+ fp = fopen (filename, "w");
+ if (!fp)
+ {
+ mu_error (_("cannot open file `%s' for writing: %s"),
+ filename, mu_strerror (errno));
+ return;
+ }
+
+ for (prog = proghead; prog; prog = prog->next)
+ {
+ switch (prog->type)
+ {
+ case TYPE_COMPONENT:
+ fprintf (fp, "component %s ",
+ prog->tag);
+ if (prog->pid)
+ fprintf (fp, "%lu", (unsigned long) prog->pid);
+ else if (prog->v.p.status == status_sleeping)
+ {
+ char buf[48];
+ time_t t = prog->v.p.timestamp + SLEEPTIME;
+ strftime (buf, sizeof buf, "%c",
+ localtime (&t));
+ fprintf (fp, _("[disabled; scheduled for %s]"), buf);
+ }
+ else if (prog->v.p.status == status_disabled)
+ fprintf (fp, _("[disabled]"));
+ else
+ fprintf (fp, "[not running]");
+ if (rc = mu_argcv_string (prog->v.p.argc, prog->v.p.argv, &s))
+ {
+ mu_error (_("cannot convert argument list: %s"),
+ mu_strerror (rc));
+ }
+ else
+ {
+ fprintf (fp, " %s", s);
+ free (s);
+ }
+ fputc ('\n', fp);
+ break;
+
+ case TYPE_RETR:
+ fprintf (fp, _("retranslator %s %lu\n"), prog->tag,
+ (unsigned long) prog->pid);
+ }
+ }
+ fclose (fp);
+}
+
+/*
+ Local Variables:
+ c-file-style: "gnu"
+ End:
+*/
+/* EOF */

Return to:

Send suggestions and report system problems to the System administrator.