aboutsummaryrefslogtreecommitdiff
path: root/src/pies.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pies.c')
-rw-r--r--src/pies.c1581
1 files changed, 1581 insertions, 0 deletions
diff --git a/src/pies.c b/src/pies.c
new file mode 100644
index 0000000..10862c2
--- /dev/null
+++ b/src/pies.c
@@ -0,0 +1,1581 @@
+/* This file is part of Mailfromd.
+ Copyright (C) 2008, 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, 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"
+#include "meta1lex.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 pies_debug;
+struct mf_privs pies_privs;
+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;
+mu_acl_t pies_acl;
+limits_record_t pies_limits;
+int force_option;
+
+
+/* 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, mu_config_value_t *arg)
+{
+ enum return_action *pact = data;
+ static struct mu_kwd actab[] = {
+ { "disable", action_disable },
+ { "restart", action_restart },
+ { NULL }
+ };
+ int res;
+
+ if (mu_cfg_assert_value_type (arg, MU_CFG_STRING, debug))
+ return 1;
+ if (mu_kwd_xlat_name (actab, arg->v.string, &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, mu_config_value_t *arg)
+{
+ mu_address_t *paddr = data;
+ mu_address_t addr;
+ int rc;
+
+ if (mu_cfg_assert_value_type (arg, MU_CFG_STRING, debug))
+ return 1;
+ rc = mu_address_create (&addr, arg->v.string);
+ if (rc)
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("%s: invalid e-mail address: %s"),
+ arg->v.string, 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 component, act_temp.act), _cb_action,
+ N_("Specifies action to take when a component finishes with this "
+ "return code."),
+ /* TRANSLATORS: disable and restart are keywords, do not translate them. */
+ N_("arg: {disable | restart}") },
+ { "notify", mu_cfg_callback, NULL,
+ mu_offsetof (struct component, act_temp.addr), _cb_notify_addr,
+ N_("Notify this address when then component terminates."),
+ N_("arg: email-list") },
+ { "message", mu_cfg_string, NULL,
+ mu_offsetof (struct component, act_temp.message), NULL,
+ N_("Notification message text (with headers).") },
+ { "exec", mu_cfg_string, NULL,
+ mu_offsetof (struct component, act_temp.command), NULL,
+ N_("Execute this command.") },
+ { NULL }
+};
+
+#define S(s) { #s, s }
+static struct mu_kwd ex_kwtab[] = {
+ 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 struct mu_kwd sig_kwtab[] = {
+ S (SIGHUP),
+ S (SIGINT),
+ S (SIGQUIT),
+ S (SIGILL),
+ S (SIGTRAP),
+ S (SIGABRT),
+ S (SIGIOT),
+ S (SIGBUS),
+ S (SIGFPE),
+ S (SIGKILL),
+ S (SIGUSR1),
+ S (SIGSEGV),
+ S (SIGUSR2),
+ S (SIGPIPE),
+ S (SIGALRM),
+ S (SIGTERM),
+#ifdef SIGSTKFLT
+ S (SIGSTKFLT),
+#endif
+ S (SIGCHLD),
+ S (SIGCONT),
+ S (SIGSTOP),
+ S (SIGTSTP),
+ S (SIGTTIN),
+ S (SIGTTOU),
+#ifdef SIGURG
+ S (SIGURG),
+#endif
+#ifdef SIGXCPU
+ S (SIGXCPU),
+#endif
+#ifdef SIGXFSZ
+ S (SIGXFSZ),
+#endif
+#ifdef SIGVTALRM
+ S (SIGVTALRM),
+#endif
+#ifdef SIGPROF
+ S (SIGPROF),
+#endif
+#ifdef SIGWINCH
+ S (SIGWINCH),
+#endif
+#ifdef SIGPOLL
+ S (SIGPOLL),
+#endif
+#ifdef SIGIO
+ S (SIGIO),
+#endif
+#ifdef SIGPWR
+ S (SIGPWR),
+#endif
+#ifdef SIGSYS
+ S (SIGSYS),
+#endif
+ { NULL }
+};
+#undef S
+
+static struct action *
+create_action(struct component *comp,
+ mu_debug_t debug, mu_config_value_t *val, int argc,
+ const char *(*getarg) (mu_config_value_t *, int, mu_debug_t))
+{
+ int i;
+ unsigned *retv;
+ int retc = 0;
+ int allflag = 0;
+ struct action *act;
+
+ retv = xcalloc (argc, sizeof *retv);
+ if (argc == 0 || (argc == 1 && strcmp (getarg(val, 0, debug), "*") == 0))
+ allflag = 1;
+ else
+ {
+ for (i = 0; i < argc; i++)
+ {
+ unsigned n;
+ const char *arg = getarg(val, i, debug);
+ size_t len = strlen (arg);
+
+ if (isdigit (arg[0]))
+ {
+ char *p;
+ n = strtoul (arg, &p, 0);
+ if (*p)
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("%s: not a number"), p);
+ continue;
+ }
+ }
+ else if (len > 3 && memcmp (arg, "SIG", 3) == 0)
+ {
+ if (arg[4] == '+')
+ {
+ char *p;
+ n = strtoul (arg + 4, &p, 0);
+ if (*p)
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("%s: not a number"), p);
+ continue;
+ }
+ }
+ else if (mu_kwd_xlat_name_ci (sig_kwtab, arg, &n))
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("%s: not a signal code"), arg);
+ continue;
+ }
+ n |= STATUS_SIG_BIT;
+ }
+ else if (mu_kwd_xlat_name_ci (ex_kwtab, arg, &n))
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("%s: not a return code"), arg);
+ continue;
+ }
+
+ /* Alles in ordnung */
+ retv[retc++] = n;
+ }
+ }
+
+ if (retc == 0 && !allflag)
+ {
+ free (retv);
+ return NULL;
+ }
+
+ act = xzalloc (sizeof *act);
+ if (!allflag)
+ {
+ act->nstat = retc;
+ act->status = retv;
+ }
+ if (comp->act_tail)
+ comp->act_tail->next = act;
+ else
+ comp->act_head = act;
+ comp->act_tail = act;
+ return act;
+}
+
+const char *
+_get_string_arg (mu_config_value_t *val, int num, mu_debug_t debug)
+{
+ if (num != 0)
+ return NULL;
+ return val->v.string;
+}
+
+const char *
+_get_array_arg (mu_config_value_t *val, int num, mu_debug_t debug)
+{
+ if (num < val->v.arg.c)
+ {
+ if (mu_cfg_assert_value_type (&val->v.arg.v[num], MU_CFG_STRING,
+ debug) == 0)
+ return val->v.arg.v[num].v.string;
+ }
+ return NULL;
+}
+
+const char *
+_get_list_arg (mu_config_value_t *val, int num, mu_debug_t debug)
+{
+ mu_config_value_t *elt;
+ int rc = mu_list_get (val->v.list, num, (void**)&elt);
+ if (rc)
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("cannot get list item: %s"),
+ mu_strerror (rc));
+ }
+ else if (mu_cfg_assert_value_type (elt, MU_CFG_STRING, debug) == 0)
+ return elt->v.string;
+ return 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;
+ size_t count;
+ struct action *act;
+
+ if (!node->label)
+ {
+ mu_cfg_format_error (tree->debug, MU_DEBUG_ERROR, _("missing tag"));
+ return 1;
+ }
+
+ switch (stage)
+ {
+ case mu_cfg_section_start:
+ switch (node->label->type)
+ {
+ case MU_CFG_STRING:
+ act = create_action (comp, tree->debug, node->label,
+ 1,
+ _get_string_arg);
+ break;
+
+ case MU_CFG_ARRAY:
+ act = create_action (comp, tree->debug, node->label,
+ node->label->v.arg.c,
+ _get_array_arg);
+ break;
+
+ case MU_CFG_LIST:
+ mu_list_count (node->label->v.list, &count);
+ act = create_action (comp, tree->debug, node->label,
+ count,
+ _get_list_arg);
+ }
+
+ if (!act)
+ return 1;
+ memset (&comp->act_temp, 0, sizeof (comp->act_temp));
+ break;
+
+ case mu_cfg_section_end:
+ act = comp->act_tail;
+ act->act = comp->act_temp.act;
+ act->addr = comp->act_temp.addr;
+ act->message = comp->act_temp.message;
+ act->command = comp->act_temp.command;
+ 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_command (mu_debug_t debug, void *data, mu_config_value_t *val)
+{
+ int argc;
+ char **argv, ***pargv = data;
+ int rc;
+
+ switch (val->type)
+ {
+ case MU_CFG_STRING:
+ rc = mu_argcv_get (val->v.string, "", NULL, &argc, &argv);
+ if (rc)
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ "mu_argcv_get: %s", mu_strerror (rc));
+ return 1;
+ }
+ break;
+
+ case MU_CFG_ARRAY:
+ argv = config_array_to_argv (val, debug);
+ break;
+
+ case MU_CFG_LIST:
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("unexpected list"));
+ return 1;
+ }
+ *pargv = argv;
+ return 0;
+}
+
+static int
+_cb_umask (mu_debug_t debug, void *data, mu_config_value_t *arg)
+{
+ mode_t *pmode = data;
+ char *p;
+ unsigned long n;
+
+ if (mu_cfg_assert_value_type (arg, MU_CFG_STRING, debug))
+ return 1;
+ n = strtoul (arg->v.string, &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, mu_config_value_t *val)
+{
+ int rc;
+ int argc;
+ char **argv;
+ char ***penv = data;
+
+ switch (val->type)
+ {
+ case MU_CFG_STRING:
+ rc = mu_argcv_get_np (val->v.string, strlen (val->v.string), "",
+ NULL, 0, &argc, &argv, NULL);
+ if (rc)
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ "mu_argcv_get: %s", mu_strerror (rc));
+ return 1;
+ }
+ break;
+
+ case MU_CFG_ARRAY:
+ argv = config_array_to_argv (val, debug);
+ break;
+
+ case MU_CFG_LIST:
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR, _("unexpected list"));
+ return 1;
+ }
+
+ *penv = argv;
+ return 0;
+}
+
+static int
+_cb_facility (mu_debug_t debug, void *data, mu_config_value_t *val)
+{
+ if (mu_cfg_assert_value_type (val, MU_CFG_STRING, debug))
+ return 1;
+
+ if (mu_string_to_syslog_facility (val->v.string, data))
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("unknown syslog facility `%s'"),
+ val->v.string);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+_cb_redir (mu_debug_t debug, void *data, mu_config_value_t *arg)
+{
+ struct redirector *rp = data;
+ static struct mu_kwd redirtab[] = {
+ { "null", redir_null },
+ { "syslog", redir_syslog },
+ { "file", redir_file },
+ { NULL }
+ };
+ int res;
+
+ switch (arg->type)
+ {
+ case MU_CFG_STRING:
+ if (strcmp (arg->v.string, "null") == 0)
+ {
+ rp->type = redir_null;
+ break;
+ }
+ rp->type = redir_syslog;
+ if (mu_string_to_syslog_priority (arg->v.string, &rp->v.prio))
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("unknown syslog priority `%s'"),
+ arg);
+ return 0;
+ }
+ break;
+
+ case MU_CFG_ARRAY:
+ if (mu_cfg_assert_value_type (&arg->v.arg.v[0], MU_CFG_STRING, debug))
+ return 0;
+ if (mu_kwd_xlat_name (redirtab, arg->v.arg.v[0].v.string, &res))
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("%s: unrecognised redirector type"),
+ arg->v.arg.v[0].v.string);
+ else
+ {
+ if (res != redir_null)
+ {
+ if (arg->v.arg.c != 2)
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("wrong number of arguments"));
+ return 0;
+ }
+ if (mu_cfg_assert_value_type (&arg->v.arg.v[1], MU_CFG_STRING,
+ debug))
+ return 0;
+
+ switch (res)
+ {
+ case redir_null:
+ break;
+
+ case redir_syslog:
+ if (mu_string_to_syslog_priority (arg->v.arg.v[1].v.string,
+ &rp->v.prio))
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("unknown syslog priority `%s'"),
+ arg->v.arg.v[1].v.string);
+ return 0;
+ }
+ break;
+
+ case redir_file:
+ rp->v.file = xstrdup (arg->v.arg.v[1].v.string);
+ break;
+ }
+ }
+ rp->type = res;
+ }
+ break;
+
+ default:
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR, _("unexpected list"));
+ }
+
+ return 0;
+}
+
+static int
+_cb_url (mu_debug_t debug, void *data, mu_config_value_t *arg)
+{
+ int rc;
+ mu_url_t url;
+
+ if (mu_cfg_assert_value_type (arg, MU_CFG_STRING, debug))
+ return 1;
+ rc = mu_url_create (&url, arg->v.string);
+ if (rc)
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("%s: cannot create URL: %s"),
+ arg->v.string,
+ mu_strerror (rc));
+ return 0;
+ }
+ rc = mu_url_parse (url);
+ if (rc)
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("%s: cannot parse URL: %s"),
+ arg->v.string,
+ mu_strerror (rc));
+ mu_url_destroy (&url);
+ return 0;
+ }
+ *(mu_url_t*) data = url;
+ return 0;
+}
+
+static struct mu_kwd modetab[] = {
+ { "exec", pies_comp_exec },
+ { "wait", pies_comp_exec },
+ { "accept", pies_comp_accept },
+ { "inetd", pies_comp_inetd },
+ { "nostartaccept", pies_comp_inetd },
+ { "pass-fd", pies_comp_pass_fd },
+ { "pass", pies_comp_pass_fd },
+ { NULL }
+};
+
+static int
+_cb_mode (mu_debug_t debug, void *data, mu_config_value_t *arg)
+{
+ int res;
+
+ if (mu_cfg_assert_value_type (arg, MU_CFG_STRING, debug))
+ return 1;
+ if (mu_kwd_xlat_name (modetab, arg->v.string, &res))
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("%s: unrecognised mode"),
+ arg->v.string);
+ else
+ *(enum pies_comp_mode *)data = res;
+ return 0;
+}
+
+static int
+_cb_limits (mu_debug_t debug, void *data, mu_config_value_t *arg)
+{
+ limits_record_t *plrec = data;
+ char *p;
+
+ if (mu_cfg_assert_value_type (arg, MU_CFG_STRING, debug))
+ return 1;
+ if (parse_limits (plrec, (char*) arg->v.string, &p))
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("invalid limit string (near %s)"),
+ p);
+ return 0;
+}
+
+struct mu_cfg_param component_cfg_param[] = {
+ { "mode", mu_cfg_callback, NULL,
+ mu_offsetof (struct component, mode), _cb_mode,
+ N_("Component execution mode."),
+ /* TRANSLATORS: The words between '{' and '}' are keywords, do not
+ translate them. */
+ N_("mode: {exec | wait | accept | inetd | nostartaccept | pass-fd | pass}")
+ },
+ { "program", mu_cfg_string, NULL,
+ mu_offsetof (struct component, program), NULL,
+ N_("Full name of the program.") },
+ { "command", mu_cfg_callback, NULL,
+ mu_offsetof (struct component, argv), _cb_command,
+ N_("Command line.") },
+ { "prerequisites", MU_CFG_LIST_OF(mu_cfg_string), NULL,
+ mu_offsetof (struct component, prereq), NULL,
+ N_("List of prerequisites."),
+ N_("list") },
+ { "dependents", MU_CFG_LIST_OF(mu_cfg_string), NULL,
+ mu_offsetof (struct component, depend), NULL,
+ N_("List of components for which this one is a prerequisite."),
+ N_("list") },
+ { "disable", mu_cfg_bool, NULL,
+ mu_offsetof (struct component, disabled), NULL,
+ N_("Disable this entry.") },
+ { "settle-timeout", mu_cfg_uint, NULL,
+ mu_offsetof (struct component, settle_timeout), NULL,
+ N_("Time to wait before starting this component.") },
+ { "precious", mu_cfg_bool, NULL,
+ mu_offsetof (struct component, precious), NULL,
+ N_("Mark this entry as precious.") },
+ { "socket", mu_cfg_callback, NULL,
+ mu_offsetof (struct component, socket_url), _cb_url,
+ N_("Listen on the given url."),
+ N_("url: string") },
+ { "pass-fd-socket", mu_cfg_string, NULL,
+ mu_offsetof (struct component, pass_fd_socket), NULL,
+ N_("Pass fd through this socket."),
+ N_("name") },
+ { "acl", mu_cfg_section, NULL, mu_offsetof (struct component, acl), NULL,
+ N_("Per-component access control list") },
+ { "remove-file", mu_cfg_string, NULL,
+ mu_offsetof (struct component, rmfile), NULL,
+ N_("Remove file before starting the component."),
+ N_("file") },
+ { "facility", mu_cfg_callback, NULL,
+ mu_offsetof (struct component, facility), _cb_facility,
+ N_("Override default syslog facility for this component."),
+ N_("arg") },
+ { "stdout", mu_cfg_callback, NULL,
+ mu_offsetof (struct component, redir[RETR_OUT]), _cb_redir,
+ N_("Redirect program's standard output to the given file or "
+ "syslog priority."),
+ /* TRANSLATORS: file and syslog are keywords. Do not translate them. */
+ N_("type: {file | syslog}> <channel: string")
+ },
+ { "stderr", mu_cfg_callback, NULL,
+ mu_offsetof (struct component, redir[RETR_ERR]), _cb_redir,
+ N_("Redirect program's standard error to the given file or "
+ "syslog priority."),
+ /* TRANSLATORS: file and syslog are keywords. Do not translate them. */
+ N_("type: {file | syslog}> <channel: string")
+ },
+ { "user", mu_cfg_string, NULL,
+ mu_offsetof (struct component, privs.user), NULL,
+ N_("Run with this user privileges.") },
+ { "group", MU_CFG_LIST_OF(mu_cfg_string), NULL,
+ mu_offsetof (struct component, privs.groups), NULL,
+ N_("Retain supplementary group.") },
+ { "allgroups", mu_cfg_bool, NULL,
+ mu_offsetof (struct component, privs.allgroups), NULL,
+ N_("Retain all supplementary groups of which user is a member.") },
+ { "umask", mu_cfg_callback, NULL,
+ mu_offsetof (struct component, umask), _cb_umask,
+ N_("Force this umask."),
+ N_("arg: number") },
+ { "limits", mu_cfg_callback, NULL,
+ mu_offsetof (struct component, limits), _cb_limits,
+ N_("Set system limits") },
+ { "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 char *
+make_full_name (const char *dir, const char *file)
+{
+ char *p;
+ size_t len = strlen (dir);
+
+ while (len > 0 && dir[len-1] == '/')
+ len--;
+ p = xmalloc (len + 1 + strlen (file) + 1);
+ memcpy (p, dir, len);
+ p[len++] = '/';
+ strcpy (p + len, file);
+ return p;
+}
+
+static int
+component_verify (struct component *comp, mu_debug_t debug)
+{
+ int header = 0;
+ int i;
+#define COMPERR(fmt, arg) \
+ do \
+ { \
+ if (!header) \
+ { \
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR, \
+ _("in component %s:"), comp->tag); \
+ header = 1; \
+ } \
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR, fmt, arg); \
+ } \
+ while (0)
+
+ if (!comp->argv)
+ COMPERR ("%s", _("missing command line"));
+ if (comp->pass_fd_socket && comp->mode != pies_comp_pass_fd)
+ COMPERR ("%s", _("pass-fd-socket ignored: wrong mode"));
+ switch (comp->mode)
+ {
+ case pies_comp_exec:
+ if (comp->socket_url)
+ COMPERR ("%s", _("socket ignored: wrong mode"));
+ break;
+
+ case pies_comp_pass_fd:
+ if (!comp->pass_fd_socket)
+ COMPERR ("%s", _("must supply pass-fd-socket in this mode"));
+ else if (comp->pass_fd_socket[0] != '/')
+ {
+ if (comp->dir)
+ {
+ char *p = make_full_name (comp->dir, comp->pass_fd_socket);
+ free (comp->pass_fd_socket);
+ comp->pass_fd_socket = p;
+ }
+ else
+ COMPERR ("%s", _("pass-fd-socket must be an absolute "
+ "file name or chdir must be specified"));
+ }
+ /* Fall through */
+
+ case pies_comp_accept:
+ case pies_comp_inetd:
+ if (!comp->socket_url)
+ {
+ COMPERR ("%s", _("socket must be specified in this mode"));
+ /* FIXME: Memory leak */
+ return 1;
+ }
+ }
+
+ if (comp->mode != pies_comp_exec
+ && comp->redir[RETR_OUT].type != redir_null)
+ {
+ COMPERR ("%s", _("stdout translation invalid in this mode"));
+ comp->redir[RETR_OUT].type = redir_null;
+ }
+
+ for (i = RETR_OUT; i <= RETR_ERR; i++)
+ {
+ if (comp->redir[i].type == redir_file && comp->redir[i].v.file[0] != '/')
+ {
+ if (comp->dir)
+ {
+ char *p = make_full_name (comp->dir, comp->redir[i].v.file);
+ free (comp->redir[i].v.file);
+ comp->redir[i].v.file = p;
+ }
+ else
+ COMPERR (_("%s: must be an absolute "
+ "file name or chdir must be specified"),
+ comp->redir[i].v.file);
+ }
+ }
+
+ return header;
+#undef COMPERR
+}
+
+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)
+{
+ struct component *comp;
+
+ switch (stage)
+ {
+ case mu_cfg_section_start:
+ if (node->label
+ && mu_cfg_assert_value_type (node->label, MU_CFG_STRING,
+ tree->debug))
+ return 1;
+ comp = progman_lookup_component (node->label->v.string);
+ if (!comp)
+ {
+ comp = xzalloc (sizeof (*comp));
+ comp->facility = mu_log_facility;
+ comp->redir[RETR_OUT].type = comp->redir[RETR_ERR].type = redir_null;
+ comp->tag = node->label ? xstrdup (node->label->v.string) : NULL;
+ }
+ *section_data = comp;
+ break;
+
+ case mu_cfg_section_end:
+ comp = *(struct component **) section_data;
+ if (component_verify (comp, tree->debug) == 0)
+ {
+ /* FIXME: The prog list is traversed twice for each component
+ statement, this is suboptimal. */
+ if (progman_lookup_component (comp->tag) == NULL)
+ 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, mu_config_value_t *arg)
+{
+ int rc;
+
+ if (mu_cfg_assert_value_type (arg, MU_CFG_STRING, debug))
+ return 1;
+ rc = mu_debug_level_from_string (arg->v.string, &debug_level, debug);
+ if (rc)
+ return 0;
+ if (!pies_debug)
+ mu_debug_create (&pies_debug, NULL);
+ mu_debug_set_level (pies_debug, debug_level);
+ return 0;
+}
+
+static int _cm_include_meta1 (mu_debug_t, void *, mu_config_value_t *);
+
+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_privs.user, 0, NULL,
+ N_("Run with this user privileges.") },
+ { "group", MU_CFG_LIST_OF(mu_cfg_string), &pies_privs.groups, 0, NULL,
+ N_("Retain supplementary group.") },
+ { "allgroups", mu_cfg_bool, &pies_privs.allgroups, 0, NULL,
+ N_("Retain all supplementary groups of which user is a member.") },
+ { "umask", mu_cfg_callback, &pies_umask, 0, _cb_umask,
+ N_("Force this umask."),
+ N_("arg: number") },
+ { "limits", mu_cfg_callback, &pies_limits, 0, _cb_limits,
+ N_("Set global system limits.") },
+ { "shutdown-timeout", mu_cfg_uint, &shutdown_timeout, 0, NULL,
+ N_("Wait <n> seconds for all components to shut down."),
+ "n" },
+ { "return-code", mu_cfg_section, &default_component },
+ { "acl", mu_cfg_section, &pies_acl, 0, NULL,
+ N_("Global access control list") },
+ { "include-meta1", mu_cfg_callback, NULL, 0, _cm_include_meta1,
+ N_("Include components from the specified MeTA1 configuration file."),
+ N_("file: string") },
+ { "meta1-queue-dir", mu_cfg_string, &meta1_queue_dir, 0, NULL,
+ N_("Set name of MeTA1 queue directory (default /var/spool/meta1).") },
+ { NULL }
+};
+
+static int
+_cm_include_meta1 (mu_debug_t debug, void *data, mu_config_value_t *arg)
+{
+ int flags = 0;
+
+ if (mu_cfg_assert_value_type (arg, MU_CFG_STRING, debug))
+ return 1;
+ if (meta1_config_parse (arg->v.string) == 0)
+ {
+ if (mu_cfg_parser_verbose)
+ flags |= MU_PARSE_CONFIG_VERBOSE;
+ if (mu_cfg_parser_verbose > 1)
+ flags |= MU_PARSE_CONFIG_DUMP;
+ mu_cfg_tree_reduce (meta1_parse_tree, mu_program_name, pies_cfg_param,
+ flags, NULL);
+ }
+ return 0;
+}
+
+
+
+const char *program_version = "pies (" PACKAGE_STRING ")";
+const char *package_bugreport = "<" PACKAGE_BUGREPORT ">";
+static char doc[] = N_("pies -- process invocation and execution supervisor");
+static char args_doc[] = "";
+
+static const char *capa[] = {
+ "common",
+ "logging",
+ "mailer",
+ "debug",
+ NULL
+};
+
+enum {
+ OPT_FOREGROUND=256,
+ OPT_LOG_TAG,
+ OPT_SYSLOG,
+ OPT_STDERR,
+ OPT_DUMP_PREREQ,
+ OPT_DUMP_DEPMAP,
+ OPT_FORCE
+};
+
+#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", OPT_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 },
+ { "force", OPT_FORCE, NULL, 0,
+ N_("force startup even if another instance may be running"), 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
+
+#define GRP 20
+ { "dump-prereq", OPT_DUMP_PREREQ, NULL, 0,
+ N_("dump prerequisite charts"), GRP+1 },
+ { "dump-depmap", OPT_DUMP_DEPMAP, NULL, 0,
+ N_("dump dependency map"), 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:
+ case OPT_DUMP_PREREQ:
+ case OPT_DUMP_DEPMAP:
+ 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 OPT_FORCE:
+ force_option = 1;
+ break;
+
+ case OPT_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
+};
+
+
+#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;
+}
+
+
+enum pies_status
+ {
+ pies_status_ctr, /* clear to run */
+ pies_status_stale,
+ pies_status_noresp,
+ pies_status_running
+ };
+
+enum pies_status
+pies_check_status (pid_t *ppid)
+{
+ pid_t pid = pidfile_read (0);
+ int i;
+ int rc;
+
+ if (pid == -1)
+ return pies_status_ctr;
+
+ *ppid = pid;
+
+ if (kill (pid, SIGUSR2))
+ return pies_status_stale;
+
+ for (i = 0; i < 4 && (rc = access (statfile, R_OK)); i++)
+ sleep (1);
+
+ if (rc)
+ return pies_status_noresp;
+ return pies_status_running;
+}
+
+
+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"),