aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2008-11-10 21:59:37 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2008-11-10 21:59:37 +0000
commit78f764264af614ed4ede060aaeab5d430d7766e4 (patch)
treec5a7e28b67c1bd6962829dcaddf435cb1a481eb3
parent39c3ada2c64847ef0df777e4c69b228d86e9614b (diff)
downloadpies-78f764264af614ed4ede060aaeab5d430d7766e4.tar.gz
pies-78f764264af614ed4ede060aaeab5d430d7766e4.tar.bz2
Rewrite pies to incorporate into it the functionality of inetd and mcp.
* pies/pies.c (pmult_debug): Rename to pies_debug. (pies_acl): New global. (component_cfg_param): New statements: mode, socket, pass-fd-socket, acl, limits. (component_section_parser): Allocate comp. Check its consistency on mu_cfg_section_end. (pies_cfg_param): New statement: acl. (main): Call mu_acl_cfg_init. Use pies_pause instead of pause. * pies/limits.c: New file. * pies/progman.c (enum prog_status): New status: status_listener. (struct prog): Keep a pointer to the original struct component, instead of copying its fields (except for depend, which is copied anyway). All uses updated. (prog_lookup_by_socket, unlink_prog, register_prog0): New functions. (prog_start, progman_cleanup, progman_stop_component): Rewrite to take into account various component modes. (pies_check_acl): New function. (progman_accept): New function. * pies/pies.h (limits_record_t): New typedef. (enum pies_comp_mode): New constants. (struct component): New members: mode, limits, socket_url, pass_fd_socket, acl. (progman_accept): New proto. (pies_pause, register_listener, pass_fd, open_socket) (parse_limits, set_limits): New protos. * pies/Makefile.am (pies_SOURCES): Add limits.c and socket.c * pies/socket.c: New file. * configure.ac: Check for msg_control and msg_accrights in struct msghdr. * acinclude.m4 (IU_CHECK_MEMBER, IU_CHECK_MEMBERS): New macros (from inetutils).
-rw-r--r--pies/Makefile.am9
-rw-r--r--pies/limits.c290
-rw-r--r--pies/pies.c184
-rw-r--r--pies/pies.h68
-rw-r--r--pies/progman.c511
-rw-r--r--pies/socket.c341
6 files changed, 1221 insertions, 182 deletions
diff --git a/pies/Makefile.am b/pies/Makefile.am
index 2c65baf..70c33cd 100644
--- a/pies/Makefile.am
+++ b/pies/Makefile.am
@@ -16,7 +16,14 @@
sbin_PROGRAMS = pies
-pies_SOURCES = depmap.c pies.c progman.c pies.h
+pies_SOURCES = \
+ depmap.c\
+ limits.c\
+ pies.c\
+ progman.c\
+ pies.h\
+ socket.c
+
EXTRA_DIST = pies.rcin
noinst_DATA = pies.rc
DISTCLEANFILES = pies.rc
diff --git a/pies/limits.c b/pies/limits.c
new file mode 100644
index 0000000..6c0d48e
--- /dev/null
+++ b/pies/limits.c
@@ -0,0 +1,290 @@
+/* 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>
+
+#define SET_LIMIT_AS 0x0001
+#define SET_LIMIT_CPU 0x0002
+#define SET_LIMIT_DATA 0x0004
+#define SET_LIMIT_FSIZE 0x0008
+#define SET_LIMIT_NPROC 0x0010
+#define SET_LIMIT_CORE 0x0020
+#define SET_LIMIT_MEMLOCK 0x0040
+#define SET_LIMIT_NOFILE 0x0080
+#define SET_LIMIT_RSS 0x0100
+#define SET_LIMIT_STACK 0x0200
+#define SET_LIMIT_LOGINS 0x0400
+#define SET_LIMIT_PRIO 0x0800
+
+struct limits_rec {
+ unsigned set;
+ rlim_t limit_as;
+ rlim_t limit_cpu;
+ rlim_t limit_data;
+ rlim_t limit_fsize;
+ rlim_t limit_nproc;
+ rlim_t limit_core;
+ rlim_t limit_memlock;
+ rlim_t limit_nofile;
+ rlim_t limit_rss;
+ rlim_t limit_stack;
+ int limit_logins;
+ int limit_prio;
+};
+
+int
+do_set_limit (int rlimit, rlim_t limit)
+{
+ struct rlimit rlim;
+
+ MU_DEBUG2 (pies_debug, MU_DEBUG_TRACE1,
+ "Setting limit %d to %lu", rlimit, (unsigned long) limit);
+ rlim.rlim_cur = limit;
+ rlim.rlim_max = limit;
+
+ if (setrlimit(rlimit, &rlim))
+ {
+ mu_diag_output (MU_DIAG_NOTICE, _("error setting limit: %s"),
+ mu_strerror (errno));
+ return 1;
+ }
+ return 0;
+}
+
+static int
+set_prio (int prio)
+{
+ MU_DEBUG1 (pies_debug, MU_DEBUG_TRACE2, "Setting priority to %d", prio);
+ if (setpriority (PRIO_PROCESS, 0, prio))
+ {
+ mu_diag_output (MU_DIAG_NOTICE, _("error setting priority: %s"),
+ mu_strerror (errno));
+ return 1;
+ }
+ return 0;
+}
+
+/* Counts the number of user logins and check against the limit */
+static int
+check_logins (const char *name, int limit)
+{
+ mu_diag_output (MU_DIAG_NOTICE, _("L limit is not implemented"));
+ return 0;
+}
+
+int
+set_limits (const char *name, struct limits_rec *lrec)
+{
+ int rc = 0;
+
+ if (!lrec)
+ return 0;
+
+ MU_DEBUG1 (pies_debug, MU_DEBUG_TRACE2, "Setting limits for %s", name);
+
+#if defined(RLIMIT_AS)
+ if (lrec->set & SET_LIMIT_AS)
+ rc |= do_set_limit(RLIMIT_AS, lrec->limit_as);
+#endif
+#if defined(RLIMIT_CPU)
+ if (lrec->set & SET_LIMIT_CPU)
+ rc |= do_set_limit(RLIMIT_CPU, lrec->limit_cpu);
+#endif
+#if defined(RLIMIT_DATA)
+ if (lrec->set & SET_LIMIT_DATA)
+ rc |= do_set_limit(RLIMIT_DATA, lrec->limit_data);
+#endif
+#if defined(RLIMIT_FSIZE)
+ if (lrec->set & SET_LIMIT_FSIZE)
+ rc |= do_set_limit(RLIMIT_FSIZE, lrec->limit_fsize);
+#endif
+#if defined(RLIMIT_NPROC)
+ if (lrec->set & SET_LIMIT_NPROC)
+ rc |= do_set_limit(RLIMIT_NPROC, lrec->limit_nproc);
+#endif
+#if defined(RLIMIT_CORE)
+ if (lrec->set & SET_LIMIT_CORE)
+ rc |= do_set_limit(RLIMIT_CORE, lrec->limit_core);
+#endif
+#if defined(RLIMIT_MEMLOCK)
+ if (lrec->set & SET_LIMIT_MEMLOCK)
+ rc |= do_set_limit(RLIMIT_MEMLOCK, lrec->limit_memlock);
+#endif
+#if defined(RLIMIT_NOFILE)
+ if (lrec->set & SET_LIMIT_NOFILE)
+ rc |= do_set_limit(RLIMIT_NOFILE, lrec->limit_nofile);
+#endif
+#if defined(RLIMIT_RSS)
+ if (lrec->set & SET_LIMIT_RSS)
+ rc |= do_set_limit(RLIMIT_RSS, lrec->limit_rss);
+#endif
+#if defined(RLIMIT_STACK)
+ if (lrec->set & SET_LIMIT_STACK)
+ rc |= do_set_limit(RLIMIT_STACK, lrec->limit_stack);
+#endif
+ if (lrec->set & SET_LIMIT_LOGINS)
+ rc |= check_logins(name, lrec->limit_logins);
+ if (lrec->set & SET_LIMIT_PRIO)
+ rc |= set_prio(lrec->limit_logins);
+ return rc;
+}
+
+int
+getlimit (char **ptr, rlim_t *rlim, int mul)
+{
+ unsigned long val;
+
+ val = strtoul (*ptr, ptr, 10);
+ if (val == 0)
+ return 1;
+ *rlim = val * mul;
+ return 0;
+}
+
+/* Parse limits string and fill appropriate fields in lrec.
+
+ The string consists of _commands_, optionally separated by any amount
+ of whitespace. A command has the following form:
+
+ [AaCcDdFfMmNnRrSsTtUuLlPp][0-9]+
+
+ i.e. a letter followed by number, and is interpreted as follows:
+
+ Command ulimit setrlimit() The limit it sets
+ option arg
+ -------------------------------------------------------------
+ [Aa] a RLIMIT_AS max address space (KB)
+ [Cc] c RLIMIT_CORE max core file size (KB)
+ [Dd] d RLIMIT_DATA max data size (KB)
+ [Ff] f RLIMIT_FSIZE Maximum filesize (KB)
+ [Mm] m RLIMIT_MEMLOCK max locked-in-memory address
+ space (KB)
+ [Nn] n RLIMIT_NOFILE max number of open files
+ [Rr] r RLIMIT_RSS max resident set size (KB)
+ [Ss] s RLIMIT_STACK max stack size (KB)
+ [Tt] t RLIMIT_CPU max CPU time (MIN)
+ [Uu] u RLIMIT_NPROC max number of processes
+ [Ll] l (none) max number of logins (N/A)
+ [Pp] p (none) process priority -20..20
+ (negative = high priority)
+ */
+int
+parse_limits (limits_record_t *plrec, char *str, char **endp)
+{
+ int c;
+ struct limits_rec *lrec = xmalloc (sizeof (*lrec));
+ *plrec = lrec;
+ lrec->set = 0;
+ while ((c = *str++))
+ {
+ if (c == ' ' || c == '\t')
+ continue;
+ switch (c)
+ {
+ case 'a':
+ case 'A':
+ /* RLIMIT_AS - max address space (KB) */
+ if (!getlimit (&str, &lrec->limit_as, 1024))
+ lrec->set |= SET_LIMIT_AS;
+ break;
+
+ case 't':
+ case 'T':
+ /* RLIMIT_CPU - max CPU time (MIN) */
+ if (!getlimit (&str, &lrec->limit_cpu, 60))
+ lrec->set |= SET_LIMIT_CPU;
+ break;
+
+ case 'd':
+ case 'D':
+ /* RLIMIT_DATA - max data size (KB) */
+ if (!getlimit (&str, &lrec->limit_data, 1024))
+ lrec->set |= SET_LIMIT_DATA;
+ break;
+
+ case 'f':
+ case 'F':
+ /* RLIMIT_FSIZE - Maximum filesize (KB) */
+ if (!getlimit (&str, &lrec->limit_fsize, 1024))
+ lrec->set |= SET_LIMIT_FSIZE;
+ break;
+
+ case 'u':
+ case 'U':
+ /* RLIMIT_NPROC - max number of processes */
+ if (!getlimit (&str, &lrec->limit_nproc, 1))
+ lrec->set |= SET_LIMIT_NPROC;
+ break;
+
+ case 'c':
+ case 'C':
+ /* RLIMIT_CORE - max core file size (KB) */
+ if (!getlimit (&str, &lrec->limit_core, 1024))
+ lrec->set |= SET_LIMIT_CORE;
+ break;
+
+ case 'm':
+ case 'M':
+ /* RLIMIT_MEMLOCK - max locked-in-memory
+ * address space (KB)
+ */
+ if (!getlimit (&str, &lrec->limit_memlock, 1024))
+ lrec->set |= SET_LIMIT_MEMLOCK;
+ break;
+
+ case 'n':
+ case 'N':
+ /* RLIMIT_NOFILE - max number of open files */
+ if (!getlimit (&str, &lrec->limit_nofile, 1))
+ lrec->set |= SET_LIMIT_NOFILE;
+ break;
+
+ case 'r':
+ case 'R':
+ /* RLIMIT_RSS - max resident set size (KB) */
+ if (!getlimit (&str, &lrec->limit_rss, 1024))
+ lrec->set |= SET_LIMIT_RSS;
+ break;
+
+ case 's':
+ case 'S':
+ /* RLIMIT_STACK - max stack size (KB) */
+ if (!getlimit (&str, &lrec->limit_stack, 1024))
+ lrec->set |= SET_LIMIT_STACK;
+ break;
+
+ case 'l':
+ case 'L':
+ lrec->limit_logins = strtol (str, &str, 10);
+ if (lrec->limit_logins >= 0)
+ lrec->set |= SET_LIMIT_LOGINS;
+ break;
+
+ case 'p':
+ case 'P':
+ lrec->limit_prio = strtol (str, &str, 10);
+ if (lrec->limit_prio > 0)
+ lrec->set |= SET_LIMIT_PRIO;
+ break;
+
+ default:
+ *endp = str-1;
+ return 1;
+ }
+ }
+ return 0;
+}
+
diff --git a/pies/pies.c b/pies/pies.c
index 77844d8..a3800eb 100644
--- a/pies/pies.c
+++ b/pies/pies.c
@@ -19,7 +19,7 @@
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;
+mu_debug_t pies_debug;
struct pies_privs_data pies_user;
int foreground;
int command;
@@ -28,6 +28,7 @@ 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;
/* Logging */
@@ -496,7 +497,87 @@ _cb_retr (mu_debug_t debug, void *data, mu_config_value_t *arg)
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}")
+ },
{ "command", mu_cfg_callback, NULL,
mu_offsetof (struct component, argv), _cb_command,
N_("Command line.") },
@@ -510,6 +591,16 @@ struct mu_cfg_param component_cfg_param[] = {
{ "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.")
@@ -521,15 +612,15 @@ struct mu_cfg_param component_cfg_param[] = {
{ "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."),
- /* TRANSLATORS: Do not translate the words between '{' and '}'. These
- are keywords. */
+ /* TRANSLATORS: The words between '{' and '}' are keywords, do not
+ translate them. */
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 error to the given syslog priority."),
- /* TRANSLATORS: Do not translate the words between '{' and '}'. These
- are keywords. */
+ /* TRANSLATORS: The words between '{' and '}' are keywords, do not
+ translate them. */
N_("prio: {emerg | alert | crit | err | warning | notice | info | debug}")
},
{ "user", mu_cfg_string, NULL,
@@ -542,6 +633,9 @@ struct mu_cfg_param component_cfg_param[] = {
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 "
@@ -562,7 +656,8 @@ component_section_parser (enum mu_cfg_section_stage stage,
void *call_data,
mu_cfg_tree_t *tree)
{
- static struct component comp;
+ struct component *comp;
+
switch (stage)
{
case mu_cfg_section_start:
@@ -570,15 +665,69 @@ component_section_parser (enum mu_cfg_section_stage stage,
&& mu_cfg_assert_value_type (node->label, MU_CFG_STRING,
tree->debug))
return 1;
- memset (&comp, 0, sizeof comp);
- comp.facility = mu_log_facility;
- comp.retr[RETR_OUT] = comp.retr[RETR_ERR] = -1;
- comp.tag = node->label ? xstrdup (node->label->v.string) : NULL;
- *section_data = &comp;
+ comp = xzalloc (sizeof (*comp));
+ comp->facility = mu_log_facility;
+ comp->retr[RETR_OUT] = comp->retr[RETR_ERR] = -1;
+ comp->tag = node->label ? xstrdup (node->label->v.string) : NULL;
+ *section_data = comp;
break;
case mu_cfg_section_end:
- register_prog (&comp);
+ comp = *(struct component **) section_data;
+ if (comp->pass_fd_socket && comp->mode != pies_comp_pass_fd)
+ mu_cfg_format_error (tree->debug, MU_DEBUG_ERROR,
+ _("pass-fd-socket ignored: wrong mode"));
+ switch (comp->mode)
+ {
+ case pies_comp_exec:
+ if (comp->socket_url)
+ mu_cfg_format_error (tree->debug, MU_DEBUG_ERROR,
+ _("socket ignored: wrong mode"));
+ break;
+
+ case pies_comp_pass_fd:
+ if (!comp->pass_fd_socket)
+ mu_cfg_format_error (tree->debug, MU_DEBUG_ERROR,
+ _("must supply pass-fd-socket in this mode"));
+ else if (comp->pass_fd_socket[0] != '/')
+ {
+ if (comp->dir)
+ {
+ char *p;
+ size_t len = strlen (comp->dir);
+
+ while (len > 0 && comp->dir[len-1] == '/')
+ len--;
+ p = xmalloc (len + 1 + strlen (comp->pass_fd_socket) + 1);
+ memcpy (p, comp->dir, len);
+ p[len++] = '/';
+ strcpy (p + len, comp->pass_fd_socket);
+ }
+ else
+ mu_cfg_format_error (tree->debug, MU_DEBUG_ERROR,
+ _("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)
+ {
+ mu_cfg_format_error (tree->debug, MU_DEBUG_ERROR,
+ _("socket must be specified in this mode"));
+ /* FIXME: Memory leak */
+ return 1;
+ }
+ }
+
+ if (comp->mode != pies_comp_exec && comp->retr[RETR_OUT] != -1)
+ {
+ mu_cfg_format_error (tree->debug, MU_DEBUG_ERROR,
+ _("stdout retranslation invalid in this mode"));
+ comp->retr[RETR_OUT] = -1;
+ }
+ register_prog (comp);
}
return 0;
}
@@ -609,9 +758,9 @@ _cb_debug (mu_debug_t debug, void *data, mu_config_value_t *arg)
rc = mu_debug_level_from_string (arg->v.string, &debug_level, debug);
if (rc)
return 0;
- if (!pmult_debug)
- mu_debug_create (&pmult_debug, NULL);
- mu_debug_set_level (pmult_debug, debug_level);
+ if (!pies_debug)
+ mu_debug_create (&pies_debug, NULL);
+ mu_debug_set_level (pies_debug, debug_level);
return 0;
}
@@ -636,6 +785,8 @@ struct mu_cfg_param pies_cfg_param[] = {
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") },
{ NULL }
};
@@ -1033,6 +1184,7 @@ main (int argc, char **argv)
argp_program_version_hook = version;
/* Set default logging */
log_setup (!stderr_closed_p ());
+ mu_acl_cfg_init ();
return_code_cfg_init ();
component_cfg_init ();
mu_argp_init (program_version, package_bugreport);
@@ -1098,7 +1250,7 @@ main (int argc, char **argv)
while (action == ACTION_CONT)
{
if (!children_cleanup)
- pause ();
+ pies_pause ();
switch (action)
{
case ACTION_COMPRELOAD:
diff --git a/pies/pies.h b/pies/pies.h
index 7ab85ac..bc8105e 100644
--- a/pies/pies.h
+++ b/pies/pies.h
@@ -20,6 +20,8 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
@@ -34,6 +36,7 @@
#include <grp.h>
#include <signal.h>
#include <sysexits.h>
+
#include <mailutils/mailutils.h>
#include <mailutils/daemon.h>
#include <mailutils/libargp.h>
@@ -51,6 +54,8 @@
#define SLEEPTIME 5*60
#define MAXSPAWN 10
+typedef struct limits_rec *limits_record_t;
+
struct pies_privs_data
{
char *user;
@@ -72,23 +77,58 @@ struct action
char *message; /* Notification mail. */
};
+enum pies_comp_mode
+ {
+ /* Execute the component, no sockets are opened. This is the default
+ Pies mode. */
+ pies_comp_exec,
+ /* Open a socket and start a component with stdin/stdout bound to that
+ socket. Corresponds to MeTA1 notion of `start_action = accept'.
+ */
+ pies_comp_accept,
+ /* Inetd mode: like above, but start the component only when an
+ incoming connection is requested. Corresponds to
+ `start_action = nostartaccept' in MeTA1.
+ */
+ pies_comp_inetd,
+ /* Open a socket, start a component, and pass the socket fd to the
+ component via the UNIX domain socket. Corresponds to
+ `start_action = pass' in MeTA1. */
+ pies_comp_pass_fd
+ };
+
struct component
{
+ enum pies_comp_mode mode;
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;
- int precious;
- char *rmfile;
- int facility;
- int retr[2];
- struct pies_privs_data privs;
- mode_t umask;
+ /* FIXME: disabled and precios can be encoded as bits in mode */
+ int disabled; /* The componenet is disabled */
+ int precious; /* The component is precious (cannot be disabled) */
+ char *rmfile; /* Try to remove this file before starting */
+ struct pies_privs_data privs; /* UID/GIDS+groups to run under */
+ mode_t umask; /* Umask to install before starting */
+ limits_record_t limits;/* System limits */
+ mu_url_t socket_url; /* Socket to listen on (if mode != pies_comp_exec) */
+ char *pass_fd_socket; /* Socket to pass fd on
+ (if mode == pies_comp_pass_fd) */
+ mu_acl_t acl;
+ /* Retranslators: */
+ int facility; /* Syslog facility. */
+ int retr[2]; /* Priorities for stdout and stderr */
+ /* Actions to execute on various exit codes: */
struct action *act[MAX_RETURN_CODE+1];
};
+extern char *syslog_tag;
+extern unsigned long shutdown_timeout;
+extern struct component default_component;
+extern mu_acl_t pies_acl;
+extern mu_debug_t pies_debug;
+
void register_prog (struct component *comp);
size_t progman_running_count (void);
void progman_start (void);
@@ -97,6 +137,7 @@ void progman_stop (void);
void progman_cleanup (int expect_term);
void progman_stop_component (const char *name);
void progman_dump_stats (const char *filename);
+int progman_accept (int socket);
void log_setup (int want_stderr);
void signal_setup (RETSIGTYPE (*sf)(int));
@@ -119,6 +160,13 @@ 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;
+
+void pies_pause (void);
+int register_listener (int fd);
+int pass_fd (const char *socket, int fd);
+int open_socket (mu_url_t url);
+
+
+int parse_limits (limits_record_t *plrec, char *str, char **endp);
+int set_limits (const char *name, limits_record_t lrec);
+
diff --git a/pies/progman.c b/pies/progman.c
index 3c9b71b..ee15974 100644
--- a/pies/progman.c
+++ b/pies/progman.c
@@ -24,6 +24,7 @@ enum prog_status
status_enabled, /* Component enabled. prog->pid!=0 shows if it is
actually running */
status_disabled, /* Component is disabled. */
+ status_listener, /* Component is an inetd listener */
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 */
@@ -42,19 +43,12 @@ struct prog
{
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;
+ struct component *comp;
+ int argc; /* Number of elements in comp->argv */
+ int socket;
time_t timestamp; /* Time of last startup */
size_t count; /* Number of failed starts since timestamp */
enum prog_status status; /* Current component status */
- int precious;
- struct action *act[MAX_RETURN_CODE+1];
} p;
struct
@@ -81,6 +75,17 @@ prog_lookup_by_pid (pid_t pid)
return prog;
}
+static struct prog *
+prog_lookup_by_socket (int fd)
+{
+ struct prog *prog;
+
+ for (prog = proghead; prog; prog = prog->next)
+ if (IS_PROG (prog) && prog->v.p.socket == fd)
+ break;
+ return prog;
+}
+
struct prog *
prog_lookup_by_tag (const char *tag)
{
@@ -129,6 +134,20 @@ link_prog (struct prog *pp, int prepend)
}
}
+void
+unlink_prog (struct prog *pp)
+{
+ struct prog *x;
+ if (x = pp->prev)
+ x->next = pp->next;
+ else
+ proghead = pp->next;
+ if (x = pp->next)
+ x->prev = pp->prev;
+ else
+ progtail = pp->prev;
+}
+
static char *
retr_tag (const char *tag, int type)
{
@@ -175,11 +194,10 @@ update_retr (int type, struct prog *master, pid_t pid)
pp->pid = pid;
}
-void
-register_prog (struct component *comp)
+static struct prog *
+register_prog0 (struct component *comp)
{
struct prog *p, *newp;
- char *pstr;
int i;
size_t size = 0;
int dep_all = 0;
@@ -200,7 +218,7 @@ register_prog (struct component *comp)
comp->depend = NULL;
}
- if (depc == 0)
+ if (depc == 0 && comp->depend)
for (depc = 0; comp->depend[depc]; depc++)
;
}
@@ -208,58 +226,31 @@ register_prog (struct component *comp)
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++)
- ;
+ size += sizeof (*newp);
newp = xzalloc (size);
newp->type = TYPE_COMPONENT;
+ newp->tag = comp->tag;
newp->pid = 0;
newp->idx = numprog++;
newp->facility = comp->facility;
- newp->v.p.precious = comp->precious;
+ newp->v.p.comp = comp;
+ for (i = 0; comp->argv[i]; i++)
+ ;
newp->v.p.argc = i;
- newp->v.p.argv = comp->argv;
- newp->v.p.env = comp->env;
+ newp->v.p.socket = -1;
if (comp->disabled)
newp->v.p.status = status_disabled;
+ else if (comp->mode == pies_comp_inetd)
+ newp->v.p.status = status_listener;
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->act[i])
+ comp->act[i] = default_component.act[i];
- 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)
{
+ char *pstr = (char*) (newp + 1);
newp->depend = (char**) pstr;
pstr = (char*) (newp->depend + depc + 1);
@@ -282,16 +273,36 @@ register_prog (struct component *comp)
}
else
newp->depend = NULL;
+
+ if (comp->mode != pies_comp_exec)
+ comp->retr[RETR_OUT] = -1;
- 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;
-
+ if (comp->retr[RETR_OUT] != -1)
+ register_retr (RETR_OUT, newp);
+ if (comp->retr[RETR_ERR] != -1)
+ register_retr (RETR_ERR, newp);
+
link_prog (newp, 0);
+ return newp;
+}
+
+void
+register_prog (struct component *comp)
+{
+ struct prog *prog = register_prog0 (comp);
+ if (comp->mode == pies_comp_inetd)
+ {
+ int fd = open_socket (comp->socket_url);
+ if (fd == -1)
+ prog->v.p.status = status_disabled;
+ else if (register_listener (fd))
+ {
+ close (fd);
+ prog->v.p.status = status_disabled;
+ }
+ else
+ prog->v.p.socket = fd;
+ }
}
size_t
@@ -321,7 +332,7 @@ open_retranslator (struct prog *master, int type)
size_t size = 0;
pid_t pid;
- if (master->v.p.retr[type] == -1)
+ if (master->v.p.comp->retr[type] == -1)
return -1;
pipe (p);
switch (pid = fork ())
@@ -338,7 +349,7 @@ open_retranslator (struct prog *master, int type)
exit (1);
openlog (master->tag, LOG_PID, master->facility);
while (getline (&buf, &size, fp) > 0)
- syslog (master->v.p.retr[type], "%s", buf);
+ syslog (master->v.p.comp->retr[type], "%s", buf);
exit (0);
case -1:
@@ -357,35 +368,35 @@ open_retranslator (struct prog *master, int type)
extern char **environ;
static char *
-find_env(char *name, int val)
+find_env (const char *name, int val)
{
- int nlen = strcspn(name, "+=");
+ int nlen = strcspn (name, "+=");
int i;
for (i = 0; environ[i]; i++)
{
- size_t elen = strcspn(environ[i], "=");
- if (elen == nlen && memcmp(name, environ[i], nlen) == 0)
+ size_t elen = strcspn (environ[i], "=");
+ if (elen == nlen && memcmp (name, environ[i], nlen) == 0)
return val ? environ[i] + elen + 1 : environ[i];
}
return NULL;
}
static int
-locate_unset(char **env, const char *name)
+locate_unset (char **env, const char *name)
{
int i;
- int nlen = strcspn(name, "=");
+ int nlen = strcspn (name, "=");
for (i = 0; env[i]; i++)
{
if (env[i][0] == '-')
{
- size_t elen = strcspn(env[i] + 1, "=");
- if (elen == nlen && memcmp(name, env[i] + 1, nlen) == 0)
+ size_t elen = strcspn (env[i] + 1, "=");
+ if (elen == nlen && memcmp (name, env[i] + 1, nlen) == 0)
{
if (env[i][nlen + 1])
- return strcmp(name + nlen, env[i] + 1 + nlen) == 0;
+ return strcmp (name + nlen, env[i] + 1 + nlen) == 0;
else
return 1;
}
@@ -395,7 +406,7 @@ locate_unset(char **env, const char *name)
}
static char *
-env_concat (char *name, size_t namelen, char *a, char *b)
+env_concat (const char *name, size_t namelen, const char *a, const char *b)
{
char *res;
size_t len;
@@ -508,6 +519,34 @@ prog_start (struct prog *prog)
if (prog->pid > 0 || !IS_PROG (prog))
return;
+ switch (prog->v.p.comp->mode)
+ {
+ case pies_comp_exec:
+ break;
+
+ case pies_comp_pass_fd:
+ if (unlink (prog->v.p.comp->pass_fd_socket) && errno != ENOENT)
+ {
+ mu_error (_("cannot unlink %s: %s"), prog->v.p.comp->pass_fd_socket,
+ mu_strerror (errno));
+ return;
+ }
+ /* fall through */
+
+ case pies_comp_accept:
+ prog->v.p.socket = open_socket (prog->v.p.comp->socket_url);
+ if (prog->v.p.socket == -1)
+ {
+ prog->v.p.status = status_disabled;
+ return;
+ }
+ break;
+
+ case pies_comp_inetd:
+ /* Wait until an incoming connection is requested */
+ if (prog->v.p.socket == -1)
+ return;
+ }
time (&now);
@@ -541,13 +580,15 @@ prog_start (struct prog *prog)
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);
+ MU_DEBUG1 (pies_debug, MU_DEBUG_TRACE1, _("starting %s\n"), prog->tag);
- if (prog->v.p.rmfile)
+ if (prog->v.p.comp->rmfile)
{
- if (unlink (prog->v.p.rmfile) && errno != ENOENT)
+ MU_DEBUG1 (pies_debug, MU_DEBUG_TRACE1, _("unlinking %s\n"),
+ prog->v.p.comp->rmfile);
+ if (unlink (prog->v.p.comp->rmfile) && errno != ENOENT)
mu_error (_("%s: cannot remove file `%s': %s"),
- prog->tag, prog->v.p.rmfile, mu_strerror (errno));
+ prog->tag, prog->v.p.comp->rmfile, mu_strerror (errno));
}
retr[RETR_OUT] = open_retranslator (prog, RETR_OUT);
@@ -557,51 +598,88 @@ prog_start (struct prog *prog)
{
/* 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);