diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2008-11-10 21:59:37 +0000 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2008-11-10 21:59:37 +0000 |
commit | 78f764264af614ed4ede060aaeab5d430d7766e4 (patch) | |
tree | c5a7e28b67c1bd6962829dcaddf435cb1a481eb3 | |
parent | 39c3ada2c64847ef0df777e4c69b228d86e9614b (diff) | |
download | pies-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.am | 9 | ||||
-rw-r--r-- | pies/limits.c | 290 | ||||
-rw-r--r-- | pies/pies.c | 184 | ||||
-rw-r--r-- | pies/pies.h | 68 | ||||
-rw-r--r-- | pies/progman.c | 511 | ||||
-rw-r--r-- | pies/socket.c | 341 |
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 = 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); |