aboutsummaryrefslogtreecommitdiff
path: root/pies
diff options
context:
space:
mode:
Diffstat (limited to 'pies')
-rw-r--r--pies/Makefile.am39
-rw-r--r--pies/depmap.c170
-rw-r--r--pies/pies.c1010
-rw-r--r--pies/pies.h119
-rw-r--r--pies/pies.rcin25
-rw-r--r--pies/progman.c971
6 files changed, 2334 insertions, 0 deletions
diff --git a/pies/Makefile.am b/pies/Makefile.am
new file mode 100644
index 0000000..2c65baf
--- /dev/null
+++ b/pies/Makefile.am
@@ -0,0 +1,39 @@
+# This file is part of mailfrom filter.
+# Copyright (C) 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, 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/>.
+
+sbin_PROGRAMS = pies
+
+pies_SOURCES = depmap.c pies.c progman.c pies.h
+EXTRA_DIST = pies.rcin
+noinst_DATA = pies.rc
+DISTCLEANFILES = pies.rc
+.rcin.rc:
+ sed 's|SBINDIR|$(sbindir)|g' $< > $@
+
+INCLUDES = \
+ $(MAILUTILS_INCLUDES)\
+ $(MU_COMMON_INCLUDES)\
+ -I$(top_srcdir)/lib\
+ -I$(top_srcdir)/gnu\
+ -I../gnu
+
+LDADD = \
+ ../lib/libmf.a\
+ $(MAILUTILS_LIBS)\
+ ../gnu/libgnu.a
+
+AM_CPPFLAGS=-DSYSCONFDIR=\"$(sysconfdir)\"\
+ -DSTATEDIR=\"$(localstatedir)\"
diff --git a/pies/depmap.c b/pies/depmap.c
new file mode 100644
index 0000000..76b492f
--- /dev/null
+++ b/pies/depmap.c
@@ -0,0 +1,170 @@
+/* This file is part of Mailfromd.
+ Copyright (C) 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"
+
+#ifndef CHAR_BIT
+# define CHAR_BIT 8
+#endif
+#define BITS_PER_WORD (sizeof(unsigned)*CHAR_BIT)
+#define MAXTABLE 32767
+
+#define WORDSIZE(n) (((n) + BITS_PER_WORD - 1) / BITS_PER_WORD)
+#define SETBIT(x, i) ((x)[(i)/BITS_PER_WORD] |= (1<<((i) % BITS_PER_WORD)))
+#define RESETBIT(x, i) ((x)[(i)/BITS_PER_WORD] &= ~(1<<((i) % BITS_PER_WORD)))
+#define BITISSET(x, i) (((x)[(i)/BITS_PER_WORD] & (1<<((i) % BITS_PER_WORD))) != 0)
+
+void
+TC (unsigned *R, int n)
+{
+ register int rowsize;
+ register unsigned mask;
+ register unsigned *rowj;
+ register unsigned *rp;
+ register unsigned *rend;
+ register unsigned *ccol;
+
+ unsigned *relend;
+ unsigned *cword;
+ unsigned *rowi;
+
+ rowsize = WORDSIZE (n) * sizeof (unsigned);
+ relend = (unsigned *) ((char *) R + (n * rowsize));
+
+ cword = R;
+ mask = 1;
+ rowi = R;
+ while (rowi < relend)
+ {
+ ccol = cword;
+ rowj = R;
+
+ while (rowj < relend)
+ {
+ if (*ccol & mask)
+ {
+ rp = rowi;
+ rend = (unsigned *) ((char *) rowj + rowsize);
+
+ while (rowj < rend)
+ *rowj++ |= *rp++;
+ }
+ else
+ {
+ rowj = (unsigned *) ((char *) rowj + rowsize);
+ }
+
+ ccol = (unsigned *) ((char *) ccol + rowsize);
+ }
+
+ mask <<= 1;
+ if (mask == 0)
+ {
+ mask = 1;
+ cword++;
+ }
+ rowi = (unsigned *) ((char *) rowi + rowsize);
+ }
+}
+
+struct pies_depmap
+{
+ unsigned nrows;
+ unsigned rowlen;
+ unsigned r[1];
+};
+
+pies_depmap_t
+depmap_alloc (unsigned count)
+{
+ size_t size = (count + BITS_PER_WORD - 1) / BITS_PER_WORD;
+ pies_depmap_t dmap = xzalloc (sizeof (*dmap) - 1
+ + count * size * sizeof (unsigned));
+ dmap->nrows = count;
+ dmap->rowlen = size;
+ return dmap;
+}
+
+pies_depmap_t
+depmap_copy (pies_depmap_t dpm)
+{
+ pies_depmap_t copy = depmap_alloc (dpm->nrows);
+ memcpy (copy->r, dpm->r, dpm->nrows * dpm->rowlen * sizeof (unsigned));
+ return copy;
+}
+
+static unsigned *
+depmap_rowptr (pies_depmap_t dmap, unsigned row)
+{
+ return dmap->r + dmap->rowlen * row;
+}
+
+void
+depmap_set (pies_depmap_t dmap, unsigned row, unsigned col)
+{
+ unsigned *rptr = depmap_rowptr (dmap, row);
+ SETBIT (rptr, col);
+}
+
+int
+depmap_isset (pies_depmap_t dmap, unsigned row, unsigned col)
+{
+ unsigned *rptr = depmap_rowptr (dmap, row);
+ return BITISSET (rptr, col);
+}
+
+void
+depmap_tc (pies_depmap_t dmap)
+{
+ TC (dmap->r, dmap->nrows);
+}
+
+struct pies_depmap_pos
+{
+ enum pies_depmap_direction dir;
+ unsigned coord[2];
+};
+
+unsigned
+depmap_next (pies_depmap_t dmap, pies_depmap_pos_t pos)
+{
+ for (pos->coord[pos->dir]++; pos->coord[pos->dir] < dmap->nrows;
+ pos->coord[pos->dir]++)
+ if (depmap_isset (dmap, pos->coord[0], pos->coord[1]))
+ break;
+
+ return pos->coord[pos->dir] == dmap->nrows ?
+ (unsigned) -1 : pos->coord[pos->dir];
+}
+
+unsigned
+depmap_first (pies_depmap_t dmap, enum pies_depmap_direction dir,
+ unsigned coord, pies_depmap_pos_t *ppos)
+{
+ pies_depmap_pos_t pos = xmalloc (sizeof *pos);
+ *ppos = pos;
+ pos->dir = dir;
+ pos->coord[!pos->dir] = coord;
+ pos->coord[pos->dir] = -1;
+ return depmap_next (dmap, pos);
+}
+
+/*
+ Local Variables:
+ c-file-style: "gnu"
+ End:
+*/
+/* EOF */
diff --git a/pies/pies.c b/pies/pies.c
new file mode 100644
index 0000000..bdca2a4
--- /dev/null
+++ b/pies/pies.c
@@ -0,0 +1,1010 @@
+/* This file is part of Mailfromd.
+ Copyright (C) 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, 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"
+
+int log_to_stderr; /* Use stderr for logging */
+char *log_tag; /* override mu_log_tag */
+mu_log_level_t debug_level;
+mu_debug_t pmult_debug;
+struct pies_privs_data pies_user;
+int foreground;
+int command;
+char *pidfile = STATEDIR "/pies.pid";
+char *ctlfile = STATEDIR "/pies.ctl";
+char *statfile = STATEDIR "/pies.stat";
+mode_t pies_umask = 0;
+unsigned long shutdown_timeout = 5;
+
+
+/* Logging */
+void
+log_setup (int want_stderr)
+{
+ mu_debug_t debug;
+
+ mu_diag_get_debug (&debug);
+
+ if (log_tag)
+ mu_log_tag = log_tag;
+
+ if (!want_stderr)
+ {
+ openlog (MU_LOG_TAG (), LOG_PID, mu_log_facility);
+ mu_debug_set_print (debug, mu_diag_syslog_printer, NULL);
+ mu_debug_default_printer = mu_debug_syslog_printer;
+ }
+ else
+ mu_debug_default_printer = mu_debug_stderr_printer;
+}
+
+static int
+stderr_closed_p()
+{
+ int fd = dup (0);
+ if (fd < 0)
+ return 1;
+ close (fd);
+ return fd <= 2;
+}
+
+
+static int
+_cb_action (mu_debug_t debug, void *data, char *arg)
+{
+ enum return_action *pact = data;
+ static struct mu_kwd actab[] = {
+ { "disable", action_disable },
+ { "restart", action_restart },
+ { NULL }
+ };
+ int res;
+
+ if (mu_kwd_xlat_name (actab, arg, &res))
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("unknown action code: %s"), arg);
+ return 0;
+ }
+ *pact = res;
+ return 0;
+}
+
+static int
+_cb_notify_addr (mu_debug_t debug, void *data, char *arg)
+{
+ mu_address_t *paddr = data;
+ mu_address_t addr;
+ int rc;
+
+ rc = mu_address_create (&addr, arg);
+ if (rc)
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("%s: invalid e-mail address: %s"),
+ arg, mu_strerror (rc));
+ if (*paddr)
+ {
+ mu_address_union (paddr, addr);
+ mu_address_destroy (&addr);
+ }
+ else
+ *paddr = addr;
+ return 0;
+}
+
+struct mu_cfg_param return_code_cfg_param[] = {
+ { "action", mu_cfg_callback, NULL,
+ mu_offsetof (struct action, act), _cb_action,
+ N_("Specifies action to take when a component finishes with this "
+ "return code."),
+ N_("arg: {disable | restart}") },
+ { "notify", mu_cfg_callback, NULL,
+ mu_offsetof (struct action, addr), _cb_notify_addr,
+ N_("Notify this address when then component terminates."),
+ N_("arg: email-list") },
+ { "message", mu_cfg_string, NULL,
+ mu_offsetof (struct action, message), NULL,
+ N_("Notification message text (with headers).") },
+ { NULL }
+};
+
+static struct mu_kwd ex_kwtab[] = {
+#define S(s) { #s, s }
+ S (EX_OK),
+ S (EX_USAGE),
+ S (EX_DATAERR),
+ S (EX_NOINPUT),
+ S (EX_NOUSER),
+ S (EX_NOHOST),
+ S (EX_UNAVAILABLE),
+ S (EX_SOFTWARE),
+ S (EX_OSERR),
+ S (EX_OSFILE),
+ S (EX_CANTCREAT),
+ S (EX_IOERR),
+ S (EX_TEMPFAIL),
+ S (EX_PROTOCOL),
+ S (EX_NOPERM),
+ S (EX_CONFIG),
+ { NULL }
+};
+
+static int
+return_code_section_parser (enum mu_cfg_section_stage stage,
+ const mu_cfg_node_t *node,
+ const char *section_label, void **section_data,
+ void *call_data,
+ mu_cfg_tree_t *tree)
+{
+ struct component *comp = *section_data;
+ struct action *act;
+ int argc, i;
+ char **argv;
+ int *retv;
+ int retc = 0;
+ int allflag = 0;
+ int rc;
+
+ switch (stage)
+ {
+ case mu_cfg_section_start:
+ rc = mu_argcv_get (node->tag_label, NULL, NULL, &argc, &argv);
+ if (rc)
+ {
+ mu_cfg_format_error (tree->debug, MU_DEBUG_ERROR,
+ _("cannot parse return codes: %s"),
+ mu_strerror (rc));
+ return 1;
+ }
+ retv = xcalloc (argc, sizeof *retv);
+ if (argc == 0 || (argc == 1 && strcmp (argv[0], "*") == 0))
+ allflag = 1;
+ else
+ {
+ for (i = 0; i < argc; i++)
+ {
+ int n;
+
+ if (isdigit (argv[i][0]))
+ {
+ char *p;
+ n = strtoul (argv[i], &p, 0);
+ if (*p)
+ {
+ mu_cfg_format_error (tree->debug, MU_DEBUG_ERROR,
+ _("%s: not a number"), p);
+ continue;
+ }
+ }
+ else if (mu_kwd_xlat_name_ci (ex_kwtab, argv[i], &n))
+ {
+ mu_cfg_format_error (tree->debug, MU_DEBUG_ERROR,
+ _("%s: not a return code"), argv[i]);
+ continue;
+ }
+
+ if (n > MAX_RETURN_CODE)
+ mu_cfg_format_error (tree->debug, MU_DEBUG_ERROR,
+ _("%s: invalid return code"), argv[i]);
+ else
+ {
+ /* Alles in ordnung */
+ retv[retc++] = n;
+ }
+ }
+ }
+
+ mu_argcv_free (argc, argv);
+ if (retc == 0 && !allflag)
+ {
+ free (retv);
+ return 1;
+ }
+
+ act = xzalloc (sizeof *act);
+ if (allflag)
+ {
+ for (i = 0; i <= MAX_RETURN_CODE; i++)
+ if (comp->act[i] == NULL)
+ comp->act[i] = act;
+ }
+ else
+ for (i = 0; i < retc; i++)
+ comp->act[retv[i]] = act;
+ *section_data = act;
+ break;
+
+ case mu_cfg_section_end:
+ break;
+ }
+ return 0;
+}
+
+void
+return_code_cfg_init ()
+{
+ struct mu_cfg_section *section;
+
+ if (mu_create_canned_section ("return-code", &section))
+ exit (EX_SOFTWARE);
+ section->parser = return_code_section_parser;
+ section->label = N_("<tag: exit-code-list>");
+ mu_cfg_section_add_params (section, return_code_cfg_param);
+}
+
+
+static int
+_cb_group (mu_debug_t debug, void *data, char *arg)
+{
+ int argc, i;
+ char **argv;
+ mu_list_t *plist = data, list;
+ int rc;
+
+ rc = mu_argcv_get_np (arg, strlen (arg), ",", NULL, 0, &argc, &argv, NULL);
+ if (rc)
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ "mu_argcv_get: %s", mu_strerror (rc));
+ return 1;
+ }
+ if (*plist)
+ list = *plist;
+ else
+ {
+ mu_list_create (&list);
+ *plist = list;
+ }
+ for (i = 0; i < argc; i++)
+ {
+ struct group *group = getgrnam (argv[i]);
+ if (!group)
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR, _("Unknown group: %s"),
+ argv[i]);
+ continue;
+ }
+ mu_list_append (list, (void*)group->gr_gid);
+ }
+ mu_argcv_free (argc, argv);
+ return 0;
+}
+
+
+static int
+_cb_command (mu_debug_t debug, void *data, char *arg)
+{
+ int argc;
+ char **argv, ***pargv = data;
+ int rc;
+
+ rc = mu_argcv_get (arg, "", NULL, &argc, &argv);
+ if (rc)
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ "mu_argcv_get: %s", mu_strerror (rc));
+ return 1;
+ }
+ *pargv = argv;
+ return 0;
+}
+
+static int
+_cb_depend (mu_debug_t debug, void *data, char *arg)
+{
+ int argc;
+ char **argv, ***pargv = data;
+ int rc;
+
+ rc = mu_argcv_get_np (arg, strlen (arg), ",", NULL, 0, &argc, &argv, NULL);
+ if (rc)
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ "mu_argcv_get: %s", mu_strerror (rc));
+ return 1;
+ }
+ *pargv = argv;
+ return 0;
+}
+
+static int
+_cb_umask (mu_debug_t debug, void *data, char *arg)
+{
+ mode_t *pmode = data;
+ char *p;
+ unsigned long n = strtoul (arg, &p, 8);
+ if (*p)
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("Invalid octal number"));
+ return 1;
+ }
+ *pmode = n;
+ return 0;
+}
+
+static int
+_cb_env (mu_debug_t debug, void *data, char *arg)
+{
+ int rc;
+ int argc;
+ char **argv;
+ char ***penv = data;
+
+ rc = mu_argcv_get_np (arg, strlen (arg), "", NULL, 0, &argc, &argv, NULL);
+ if (rc)
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ "mu_argcv_get: %s", mu_strerror (rc));
+ return 1;
+ }
+ *penv = argv;
+ return 0;
+}
+
+static int
+_cb_retr (mu_debug_t debug, void *data, char *arg)
+{
+ if (mu_string_to_syslog_priority (arg, data))
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("Unknown syslog facility `%s'"),
+ arg);
+ return 1;
+ }
+ return 0;
+}
+
+struct mu_cfg_param component_cfg_param[] = {
+ { "command", mu_cfg_callback, NULL,
+ mu_offsetof (struct component, argv), _cb_command,
+ N_("Command line.") },
+ { "depend", mu_cfg_callback, NULL,
+ mu_offsetof (struct component, depend), _cb_depend,
+ N_("List of dependenices (whitespace separated)"),
+ N_("list") },
+ { "disable", mu_cfg_bool, NULL,
+ mu_offsetof (struct component, disabled), NULL,
+ N_("Disable this entry.") },
+ { "remove-file", mu_cfg_string, NULL,
+ mu_offsetof (struct component, rmfile), NULL,
+ N_("Remove file before starting the component.")
+ N_("file") },
+ { "stdout", mu_cfg_callback, NULL,
+ mu_offsetof (struct component, retr[RETR_OUT]), _cb_retr,
+ N_("Redirect program's standard output to the given syslog priority."),
+ N_("prio: {emerg | alert | crit | err | warning | notice | info | debug}")
+ },
+ { "stderr", mu_cfg_callback, NULL,
+ mu_offsetof (struct component, retr[RETR_ERR]), _cb_retr,
+ N_("Redirect program's standard output to the given syslog priority."),
+ N_("prio: {emerg | alert | crit | err | warning | notice | info | debug}")
+ },
+ { "user", mu_cfg_string, NULL,
+ mu_offsetof (struct component, privs.user), NULL,
+ N_("Run with this user privileges.") },
+ { "group", mu_cfg_callback, NULL,
+ mu_offsetof (struct component, privs.groups), _cb_group,
+ N_("Retain supplementary group.") },
+ { "umask", mu_cfg_callback, NULL,
+ mu_offsetof (struct component, umask), _cb_umask,
+ N_("Force this umask."),
+ N_("arg: number") },
+ { "env", mu_cfg_callback, NULL,
+ mu_offsetof (struct component, env), _cb_env,
+ N_("Set program environment. Argument is a list of assignments "
+ "separated by white space."),
+ N_("arg: list") },
+ { "chdir", mu_cfg_string, NULL,
+ mu_offsetof (struct component, dir), NULL,
+ N_("Change to this directory before executing the component."),
+ N_("dir") },
+ { "return-code", mu_cfg_section },
+ { NULL }
+};
+
+static int
+component_section_parser (enum mu_cfg_section_stage stage,
+ const mu_cfg_node_t *node,
+ const char *section_label, void **section_data,
+ void *call_data,
+ mu_cfg_tree_t *tree)
+{
+ static struct component comp;
+ switch (stage)
+ {
+ case mu_cfg_section_start:
+ memset (&comp, 0, sizeof comp);
+ comp.retr[RETR_OUT] = comp.retr[RETR_ERR] = -1;
+ comp.tag = node->tag_label ? xstrdup (node->tag_label) : NULL;
+ *section_data = &comp;
+ break;
+
+ case mu_cfg_section_end:
+ register_prog (&comp);
+ }
+ return 0;
+}
+
+void
+component_cfg_init ()
+{
+ struct mu_cfg_section *section;
+
+ if (mu_create_canned_section ("component", &section))
+ exit (EX_SOFTWARE);
+ section->parser = component_section_parser;
+ section->label = N_("<tag: string>");
+ mu_cfg_section_add_params (section, component_cfg_param);
+}
+
+
+
+struct component default_component;
+
+static int
+_cb_debug (mu_debug_t debug, void *data, char *arg)
+{
+ int rc;
+
+ rc = mu_debug_level_from_string (arg, &debug_level, debug);
+ if (rc)
+ return 0;
+ if (!pmult_debug)
+ mu_debug_create (&pmult_debug, NULL);
+ mu_debug_set_level (pmult_debug, debug_level);
+ return 0;
+}
+
+struct mu_cfg_param pies_cfg_param[] = {
+ { "component", mu_cfg_section },
+ { "debug", mu_cfg_callback, NULL, 0, _cb_debug,
+ N_("Set debug verbosity level.") },
+ { "pidfile", mu_cfg_string, &pidfile, 0, NULL,
+ N_("Write PID to this file.") },
+ { "control-file", mu_cfg_string, &ctlfile, 0, NULL,
+ N_("Set location of the control file.") },
+ { "stat-file", mu_cfg_string, &statfile, 0, NULL,
+ N_("Set location of the statistics output file.") },
+ { "user", mu_cfg_string, &pies_user.user, 0, NULL,
+ N_("Run with this user privileges.") },
+ { "group", mu_cfg_callback, &pies_user.groups, 0, _cb_group,
+ N_("Retain supplementary group.") },
+ { "umask", mu_cfg_callback, &pies_umask, 0, _cb_umask,
+ N_("Force this umask."),
+ N_("arg: number") },
+ { "shutdown-timeout", mu_cfg_uint, &shutdown_timeout, 0, NULL,
+ N_("Wait <n> seconds for all children to shut down."),
+ "n" },
+ { "return-code", mu_cfg_section, &default_component },
+ { NULL }
+};
+
+
+const char *program_version = "pies (" PACKAGE_STRING ")";
+const char *package_bugreport = "<" PACKAGE_BUGREPORT ">";
+static char doc[] = N_("pies -- process inspector and execution supervisor");
+static char args_doc[] = "";
+
+static const char *capa[] = {
+ "common",
+ "logging",
+ "mailer",
+ "debug",
+ NULL
+};
+
+enum {
+ OPT_FOREGROUND=256,
+ OPTION_LOG_TAG,
+ OPT_SYSLOG,
+ OPT_STDERR
+};
+
+#define OPT_RESTART 'R'
+#define OPT_RELOAD 'r'
+#define OPT_STATUS 's'
+#define OPT_STOP 'S'
+
+static struct argp_option options[] = {
+#define GRP 0
+ { "foreground", OPT_FOREGROUND, 0, 0, N_("Remain in foreground."), GRP+1},
+ { "stderr", OPT_STDERR, NULL, 0, N_("Log to stderr"), },
+ { "syslog", OPT_SYSLOG, NULL, 0, N_("Log to syslog"), },
+ { "log-tag", OPTION_LOG_TAG, N_("STRING"), 0,
+ N_("Set the identifier used in syslog messages to STRING"), GRP+1 },
+ { "debug", 'x', N_("LEVEL"), 0,
+ N_("Set debug verbosity level."), GRP+1 },
+#undef GRP
+
+#define GRP 10
+ { "restart-component", OPT_RESTART, NULL, 0,
+ N_("Restart components named in the command line."), GRP+1 },
+ { "reload", OPT_RELOAD, NULL, 0,
+ N_("Reload the running instance of pies."), GRP+1 },
+ { "hup", 0, NULL, OPTION_ALIAS },
+ { "status", OPT_STATUS, NULL, 0,
+ N_("Display info about the running instance."), GRP+1 },
+ { "stop", OPT_STOP, NULL, 0,
+ N_("Stop the running instance."), GRP+1 },
+#undef GRP
+ { NULL }
+};
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ static struct mu_argp_node_list lst;
+
+ switch (key)
+ {
+ case OPT_FOREGROUND:
+ foreground = 1;
+ break;
+
+ case OPT_RELOAD:
+ case OPT_STATUS:
+ case OPT_STOP:
+ case OPT_RESTART:
+ log_to_stderr = 1;
+ command = key;
+ break;
+
+ case OPT_SYSLOG:
+ log_to_stderr = 0;
+ break;
+
+ case OPT_STDERR:
+ log_to_stderr = 1;
+ break;
+
+ case 'x':
+ mu_argp_node_list_new (&lst, "debug", arg);
+ break;
+
+ case OPTION_LOG_TAG:
+ log_tag = arg;
+ break;
+
+ case ARGP_KEY_INIT:
+ mu_argp_node_list_init (&lst);
+ break;
+
+ case ARGP_KEY_FINI:
+ mu_argp_node_list_finish (&lst, NULL, NULL);
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static struct argp argp = {
+ options,
+ parse_opt,
+ args_doc,
+ doc,
+ NULL,
+ NULL,
+ NULL
+};
+
+
+static void
+version (FILE *stream, struct argp_state *state)
+{
+ mailfromd_version ("pies", stream);
+}
+
+
+void
+priv_setup (struct pies_privs_data *pr)
+{
+ if (pr->user)
+ {
+ struct passwd *pw = getpwnam (pr->user);
+ if (!pw)
+ {
+ mu_error (_("No such user: %s"), pr->user);
+ exit (EX_CONFIG);
+ }
+ if (pw && switch_to_privs (pw->pw_uid, pw->pw_gid, pr->groups))
+ exit (EX_SOFTWARE);
+ }
+}
+
+
+#define ACTION_CONT 0
+#define ACTION_STOP 1
+#define ACTION_RESTART 2
+#define ACTION_COMPRELOAD 3
+#define ACTION_DUMPSTATS 4
+
+int action = ACTION_CONT;
+int children_cleanup = 0;
+int got_alarm = 0;
+
+RETSIGTYPE
+sig_handler (int sig)
+{
+ switch (sig)
+ {
+ case SIGCHLD:
+ children_cleanup = 1;
+ break;
+
+ case SIGTERM:
+ case SIGINT:
+ case SIGQUIT:
+ action = ACTION_STOP;
+ mu_diag_output (MU_DIAG_NOTICE, "received signal %d", sig);
+ break;
+
+ case SIGHUP:
+ mu_diag_output (MU_DIAG_NOTICE, "received signal %d", sig);
+ action = ACTION_RESTART;
+ break;
+
+ case SIGALRM:
+ got_alarm = 1;
+ break;
+
+ case SIGUSR1:
+ action = ACTION_COMPRELOAD;
+ break;
+
+ case SIGUSR2:
+ action = ACTION_DUMPSTATS;
+ break;
+ }
+ signal (sig, sig_handler);
+}
+
+void
+signal_setup (RETSIGTYPE (*sf)(int))
+{
+ signal (SIGCHLD, sf);
+ signal (SIGTERM, sf);
+ signal (SIGQUIT, sf);
+ signal (SIGINT, sf);
+ signal (SIGHUP, sf);
+ signal (SIGALRM, sf);
+ signal (SIGUSR1, sf);
+ signal (SIGUSR2, sf);
+}
+
+
+pid_t
+pidfile_read (int must_exist)
+{
+ int c;
+ pid_t n = 0;
+ FILE *fp = fopen (pidfile, "r");
+ if (!fp)
+ {
+ if (must_exist && errno != ENOENT)
+ mu_diag_output (MU_DIAG_ERR,
+ _("cannot open pid file `%s': %s"),
+ pidfile,
+ mu_strerror (errno));
+ return -1;
+ }
+
+ while ((c = fgetc (fp)) != EOF)
+ {
+ if (isdigit (c))
+ n = n * 10 + c - '0';
+ else if (c == '\n')
+ break;
+ else
+ {
+ mu_diag_output (MU_DIAG_ERR,
+ _("unexpected character %#03o in pidfile `%s'"),
+ c, pidfile);
+ return -1;
+ }
+ }
+ fclose (fp);
+ if (kill (n, 0))
+ {
+ mu_diag_output (MU_DIAG_ERR,
+ _("cannot signal master process %lu: %s"),
+ (unsigned long) n, mu_strerror (errno));
+ if (errno == EPERM)
+ return n; /* be on the safe side */
+ return -1;
+ }
+ return n;
+}
+
+
+void
+stop_components ()
+{
+ FILE *fp;
+ size_t size = 0;
+ char *buf = NULL;
+
+ mu_diag_output (MU_DIAG_INFO, _("stopping components"));
+
+ fp = fopen (ctlfile, "r");
+ if (!fp)
+ {
+ mu_error (_("cannot open control file `%s': %s"),
+ ctlfile, mu_strerror (errno));
+ return;
+ }
+ if (unlink (ctlfile))
+ {
+ mu_error (_("cannot unlink control file `%s': %s"),
+ ctlfile, mu_strerror (errno));
+ fclose (fp);
+ return;
+ }
+
+ while (getline (&buf, &size, fp) > 0)
+ {
+ size_t len = strlen (buf);
+ if (len == 0)
+ continue;
+ if (buf[len-1] == '\n')
+ buf[len-1] = 0;
+ progman_stop_component (buf);
+ }
+
+ free (buf);
+ fclose (fp);
+}
+
+int
+request_restart_components (char **argv)
+{
+ FILE *fp;
+ pid_t pid = pidfile_read (1);
+
+ if (pid == -1)
+ return 1;
+
+ fp = fopen (ctlfile, "w");
+ if (!fp)
+ {
+ mu_error (_("cannot open control file `%s': %s"),
+ ctlfile, mu_strerror (errno));
+ return 1;
+ }
+ for (; *argv; argv++)
+ fprintf (fp, "%s\n", *argv);
+ fclose (fp);
+
+ kill (pid, SIGUSR1);
+ return 0;
+}
+
+
+int
+pies_reload ()
+{
+ pid_t pid = pidfile_read (1);
+
+ if (pid == -1)
+ {
+ mu_diag_output (MU_DIAG_CRIT, _("pies is not running"));
+ return 1;
+ }
+
+ mu_diag_output (MU_DIAG_INFO, _("reloading pies at PID %lu"),
+ (unsigned long) pid);
+ return kill (pid, SIGHUP) ? EX_SOFTWARE : 0;
+}
+
+int
+pies_status ()
+{
+ FILE *fp;
+ pid_t pid = pidfile_read (0);
+ int i;
+
+ if (pid == -1)
+ {
+ mu_diag_output (MU_DIAG_INFO, _("pies is not running"));
+ return EX_USAGE;
+ }
+
+ if (kill (pid, SIGUSR2))
+ {
+ mu_diag_output (MU_DIAG_INFO,
+ _("pies is not running, but a pidfile "
+ "is found (pid %lu)"),
+ (unsigned long) pid);
+ return EX_USAGE;
+ }
+
+ mu_diag_output (MU_DIAG_INFO,
+ _("pies is running; PID %lu"),
+ (unsigned long) pid);
+
+ for (i = 0; i < 4 && access (statfile, R_OK); i++)
+ sleep (1);
+
+ fp = fopen (statfile, "r");
+ if (!fp)
+ mu_diag_output (MU_DIAG_ERR, _("cannot open statfile `%s': %s"),
+ statfile, mu_strerror (errno));
+ else
+ {
+ char c;
+
+ if (unlink (statfile))
+ mu_diag_output (MU_DIAG_ERR, _("cannot unlink statfile `%s': %s"),
+ statfile, mu_strerror (errno));
+ while ((c = fgetc (fp)) != EOF)
+ fputc (c, stdout);
+ fclose (fp);
+ }
+ return 0;
+}
+
+int
+pies_stop ()
+{
+ pid_t pid = pidfile_read (1);
+
+ if (pid == -1)
+ {
+ mu_diag_output (MU_DIAG_CRIT, _("pies is not running"));
+ return EX_USAGE;
+ }
+
+ mu_diag_output (MU_DIAG_INFO,
+ _("stopping pies at PID %lu"), (unsigned long) pid);
+ return kill (pid, SIGTERM) ? EX_SOFTWARE : 0;
+}
+
+
+int
+main (int argc, char **argv)
+{
+ int rc;
+ int index;
+
+ mf_init_nls ();
+ if (!program_invocation_short_name)
+ program_invocation_short_name = argv[0];
+ argp_program_version_hook = version;
+ /* Set default logging */
+ log_setup (!stderr_closed_p ());
+ return_code_cfg_init ();
+ component_cfg_init ();
+ mu_argp_init (program_version, package_bugreport);
+ mu_register_all_mailer_formats ();
+ rc = mu_app_init (&argp, capa, pies_cfg_param, argc, argv, 0, &index, NULL);
+ if (rc)
+ exit (EX_CONFIG);
+
+ log_setup (log_to_stderr);
+
+ if (argc != index && command != 'R')
+ {
+ mu_error ("extra command line arguments");
+ exit (EX_CONFIG);
+ }
+
+ switch (command)
+ {
+ case OPT_RESTART:
+ priv_setup (&pies_user);
+ if (pies_umask)
+ umask (pies_umask);
+ exit (request_restart_components (argv + index));
+
+ case OPT_RELOAD:
+ exit (pies_reload ());
+
+ case OPT_STATUS:
+ exit (pies_status ());
+
+ case OPT_STOP:
+ exit (pies_stop ());
+
+ default:
+ priv_setup (&pies_user);
+ if (pies_umask)
+ umask (pies_umask);
+ }
+
+ mu_diag_output (MU_DIAG_INFO, _("%s starting"), program_version);
+
+ if (!foreground && daemon (0, 0) == -1)
+ {
+ mu_error (_("cannot become a daemon: %s"),
+ mu_strerror (errno));
+ exit (EX_SOFTWARE);
+ }
+
+ rc = mu_daemon_create_pidfile (pidfile);
+ if (rc)
+ mu_error (_("Cannot create PID file `%s': %s"),
+ pidfile, mu_strerror (rc));
+
+ if (argv[0][0] != '/')
+ mu_diag_output (MU_DIAG_NOTICE,
+ N_("not started as an absolute pathname; "
+ "SIGHUP will not work"));
+
+ signal_setup (sig_handler);
+
+ progman_start ();
+
+ while (action == ACTION_CONT)
+ {
+ if (!children_cleanup)
+ pause ();
+ if (children_cleanup)
+ {
+ children_cleanup = 0;
+ progman_cleanup (0);
+ }
+ if (got_alarm)
+ {
+ got_alarm = 0;
+ progman_wake_sleeping ();
+ }
+ switch (action)
+ {
+ case ACTION_COMPRELOAD:
+ stop_components ();
+ action = ACTION_CONT;
+ break;
+
+ case ACTION_DUMPSTATS:
+ progman_dump_stats (statfile);
+ action = ACTION_CONT;
+ break;
+ }
+ }
+ progman_stop ();
+
+ if (action == ACTION_RESTART && argv[0][0] == '/')
+ {
+ int i;
+
+ for (i = getmaxfd (); i > 0; i--)
+ close (i);
+
+ mu_daemon_remove_pidfile ();
+ signal_setup (SIG_DFL);
+
+ execv (argv[0], argv);
+ }
+
+ mu_diag_output (MU_DIAG_INFO, _("%s terminated"), program_version);
+ exit (EX_OK);
+}
+
+void
+xalloc_die ()
+{
+ mu_error ("not enough memory");
+ abort ();
+}
+
+/*
+ Local Variables:
+ c-file-style: "gnu"
+ End:
+*/
+/* EOF */
diff --git a/pies/pies.h b/pies/pies.h
new file mode 100644
index 0000000..8145f51
--- /dev/null
+++ b/pies/pies.h
@@ -0,0 +1,119 @@
+/* This file is part of Mailfromd.
+ Copyright (C) 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, 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/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <getopt.h>
+#include <errno.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <signal.h>
+#include <sysexits.h>
+#include <mailutils/mailutils.h>
+#include <mailutils/daemon.h>
+#include <mailutils/libargp.h>
+#include <mailutils/syslog.h>
+
+#include "xalloc.h"
+#include "libmf.h"
+
+#define RETR_OUT 0
+#define RETR_ERR 1
+
+#define TESTTIME 2*60
+#define SLEEPTIME 5*60
+#define MAXSPAWN 10
+
+struct pies_privs_data
+{
+ char *user;
+ mu_list_t groups;
+};
+
+#define MAX_RETURN_CODE 127
+
+enum return_action
+{
+ action_restart,
+ action_disable,
+};
+
+struct action
+{
+ enum return_action act; /* Action to take when the component terminates */
+ mu_address_t addr; /* Addresses to notify about it. */
+ char *message; /* Notification mail. */
+};
+
+struct component
+{
+ char *tag; /* Entry tag (for diagnostics purposes) */
+ char **argv; /* Program command line */
+ char **env; /* Program environment */
+ char *dir; /* Working directory */
+ char **depend; /* Dependencies */
+ int disabled;
+ char *rmfile;
+ int retr[2];
+ struct pies_privs_data privs;
+ mode_t umask;
+ struct action *act[MAX_RETURN_CODE+1];
+};
+
+void register_prog (struct component *comp);
+size_t progman_running_count (void);
+void progman_start (void);
+void progman_wake_sleeping (void);
+void progman_stop (void);
+void progman_cleanup (int expect_term);
+void progman_stop_component (const char *name);
+void progman_dump_stats (const char *filename);
+
+void log_setup (int want_stderr);
+void signal_setup (RETSIGTYPE (*sf)(int));
+void priv_setup (struct pies_privs_data *pr);
+
+typedef struct pies_depmap *pies_depmap_t;
+typedef struct pies_depmap_pos *pies_depmap_pos_t;
+enum pies_depmap_direction
+ {
+ depmap_row = 0,
+ depmap_col = !depmap_row
+ };
+
+pies_depmap_t depmap_alloc (unsigned count);
+pies_depmap_t depmap_copy (pies_depmap_t dpm);
+void depmap_set (pies_depmap_t dmap, unsigned row, unsigned col);
+int depmap_isset (pies_depmap_t dmap, unsigned row, unsigned col);
+void depmap_tc (pies_depmap_t dmap);
+unsigned depmap_first (pies_depmap_t dmap, enum pies_depmap_direction dir,
+ unsigned coord, pies_depmap_pos_t *ppos);
+unsigned depmap_next (pies_depmap_t dmap, pies_depmap_pos_t pos);
+
+extern char *syslog_tag;
+extern unsigned long shutdown_timeout;
+extern struct component default_component;
diff --git a/pies/pies.rcin b/pies/pies.rcin
new file mode 100644
index 0000000..d1ae197
--- /dev/null
+++ b/pies/pies.rcin
@@ -0,0 +1,25 @@
+# Sample pies configuration for running pmult.
+
+# Special handling for exit codes that mean the program was used incorrectly
+# or misconfigured.
+return-code EX_USAGE EX_CONFIG {
+ action disable;
+ notify root;
+ message
+ "From: <>\n"
+ "X-Agent: ${canonical-program-name} (${package} ${version})\n"
+ "Subject: Component ${component} disabled.\n"
+ "\n"
+ "Component \"${component}\" terminated with code ${retcode}, which means\n"
+ "some configuration problem.\n"
+ "It will not be restarted automatically. Please fix its configuration\n"
+ "and restart it at your earliest convenience.";
+};
+
+component pmult {
+ command "SBINDIR/pmult";
+ user meta1s;
+ stderr info;
+ stdout info;
+};
+
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.