diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2007-05-31 12:39:44 +0000 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2007-05-31 12:39:44 +0000 |
commit | 20fe14d5c6e24b874c2ecbba7d3900d772406e69 (patch) | |
tree | c66cf3eeb50afc35d0deaa8d4cb36a7df237e006 | |
parent | 0e67bf9bde952f85ae8a66a05af14378fc43916d (diff) | |
download | gsc-20fe14d5c6e24b874c2ecbba7d3900d772406e69.tar.gz gsc-20fe14d5c6e24b874c2ecbba7d3900d772406e69.tar.bz2 |
Add jabberd
git-svn-id: file:///svnroot/gsc/trunk@246 d2de0444-eb31-0410-8365-af798a554d48
-rw-r--r-- | ChangeLog | 8 | ||||
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | configure.ac | 5 | ||||
-rw-r--r-- | jabberd/Makefile.am | 3 | ||||
-rw-r--r-- | jabberd/jabberd.h | 61 | ||||
-rw-r--r-- | jabberd/main.c | 690 | ||||
-rw-r--r-- | jabberd/progman.c | 273 |
7 files changed, 1040 insertions, 3 deletions
@@ -1,3 +1,11 @@ +2007-05-31 Sergey Poznyakoff <gray@gnu.org.ua> + + * Makefile.am, configure.ac: Add jabberd + * jabberd/Makefile.am: New file + * jabberd/jabberd.h: New file + * jabberd/main.c: New file + * jabberd/progman.c: New file + 2007-03-17 Sergey Poznyakoff <gray@gnu.org.ua> * etc/links.txt: Updated diff --git a/Makefile.am b/Makefile.am index ff57a39..32d435c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,4 +15,5 @@ SUBDIRS=\ maint\ mc\ ppp\ - rc.d + rc.d\ + jabberd diff --git a/configure.ac b/configure.ac index 86da1f5..6bdb7a5 100644 --- a/configure.ac +++ b/configure.ac @@ -36,7 +36,7 @@ AC_FUNC_MALLOC AC_FUNC_MEMCMP AC_FUNC_STAT AC_FUNC_VPRINTF -AC_CHECK_FUNCS([memset strchr strdup strerror strrchr setegid setregid setresgid seteuid setreuid]) +AC_CHECK_FUNCS([memset strchr strdup strerror strrchr setegid setregid setresgid seteuid setreuid vsyslog sysconf getdtablesize]) # Sendmail version or cfdir AC_SUBST(SENDMAIL_VERSION) @@ -110,6 +110,7 @@ AC_CONFIG_FILES([Makefile maint/Makefile mc/Makefile ppp/Makefile - rc.d/Makefile]) + rc.d/Makefile + jabberd/Makefile]) AC_OUTPUT diff --git a/jabberd/Makefile.am b/jabberd/Makefile.am new file mode 100644 index 0000000..872b15c --- /dev/null +++ b/jabberd/Makefile.am @@ -0,0 +1,3 @@ +bin_PROGRAMS = jabberd +jabberd_SOURCES = main.c progman.c jabber.h +AM_CPPFLAGS=-DSYSCONFDIR=\"$(sysconfdir)\"
\ No newline at end of file diff --git a/jabberd/jabberd.h b/jabberd/jabberd.h new file mode 100644 index 0000000..2387132 --- /dev/null +++ b/jabberd/jabberd.h @@ -0,0 +1,61 @@ +/* jabberd - a dispatcher program for jabber 2.x + Copyright (C) 2007 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <sys/types.h> +#include <sys/wait.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <syslog.h> +#include <getopt.h> +#include <errno.h> +#include <string.h> +#include <pwd.h> +#include <grp.h> +#include <signal.h> + +#if defined HAVE_SYSCONF && defined _SC_OPEN_MAX +# define getmaxfd() sysconf(_SC_OPEN_MAX) +#elif defined (HAVE_GETDTABLESIZE) +# define getmaxfd() getdtablesize() +#else +# define getmaxfd() 256 +#endif + +#define TESTTIME 2*60 +#define SLEEPTIME 5*60 +#define MAXSPAWN 10 + +void register_prog (char *cmd, char *cfg); +void logmsg(int prio, char *fmt, ...); +void signal_setup (RETSIGTYPE (*sf)(int)); + +void *emalloc (size_t size); + +void progman_start (void); +void progman_stop (void); +void progman_cleanup (int); +void progman_wake_disabled (void); + +extern int debug_level; +extern char *syslog_tag; +extern int log_facility; diff --git a/jabberd/main.c b/jabberd/main.c new file mode 100644 index 0000000..f195f72 --- /dev/null +++ b/jabberd/main.c @@ -0,0 +1,690 @@ +/* jabberd - a dispatcher program for jabber 2.x + Copyright (C) 2007 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. */ + +#include "jabberd.h" + +char *progname; +char *config_file = SYSCONFDIR "/jabberd.cfg"; +int debug_level = 0; +int foreground = 0; +int log_to_stderr = 0; +char *user = "jabber"; +char *syslog_tag = "jabberd"; +int log_facility = LOG_LOCAL7; +int x_argc; +char **x_argv; +char *pidfile; + +void +syslog_printer (int prio, const char *fmt, va_list ap) +{ +#ifdef USE_SYSLOG_ASYNC + vsyslog_async (prio, fmt, ap); +#elif HAVE_VSYSLOG + vsyslog (prio, fmt, ap); +#else + char buf[128]; + vsnprintf (buf, sizeof buf, fmt, ap); + syslog (prio, "%s", buf); +#endif +} + +void +stderr_printer (int prio, const char *fmt, va_list ap) +{ + char *p = NULL; + fprintf (stderr, "%s: ", progname); + + switch (prio) { + case LOG_EMERG: + p = "EMERG"; + break; + + case LOG_ALERT: + p = "ALERT"; + break; + + case LOG_CRIT: + p = "CRIT"; + break; + + case LOG_ERR: + p = "ERR"; + break; + + case LOG_WARNING: + p = "WARN"; + break; + + case LOG_NOTICE: + p = "NOTICE"; + break; + + case LOG_INFO: + p = "INFO"; + break; + + case LOG_DEBUG: + p = "DEBUG"; + break; + } + + if (p) + fprintf (stderr, "[%s] ", p); + + vfprintf (stderr, fmt, ap); + fputc ('\n', stderr); +} + + +static void (*log_printer) (int prio, const char *fmt, va_list ap) = + stderr_printer; + +void +logmsg(int prio, char *fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + log_printer (prio, fmt, ap); + va_end (ap); +} + +void * +emalloc (size_t size) +{ + char *p = malloc (size); + if (!p) + { + logmsg (LOG_EMERG, "%s", strerror (errno)); + exit (1); + } + return p; +} + +void +version () +{ + printf ("jabberd (%s)\n", PACKAGE_STRING); + printf ("Copyright (C) 2007 Sergey Poznyakoff\n"); + printf ("License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n"); + printf ("This is free software: you are free to change and redistribute it.\n"); + printf ("There is NO WARRANTY, to the extent permitted by law.\n"); +} + +void +usage () +{ + printf ("usage: jabberd [-Dfehv][-c config][-p pidfile]\n"); + printf ("jabberd -- A dispatcher program for jabber 2.x\n"); + printf ("\n"); + printf ("Options:\n"); + printf (" -c FILE use this configuration file\n"); + printf (" -D increase debugging level\n"); + printf (" -f run in foreground mode (implies -e)\n"); + printf (" -e use standard error for diagnostics output\n"); + printf (" -h display this help list\n"); + printf (" -p FILE write master process ID to FILE\n"); + printf (" -v display program version\n"); + printf ("\n"); + printf ("Report bugs to <%s>\n", PACKAGE_BUGREPORT); +} + + +/* Configuration file handling */ + +typedef void (*cfg_fun) (unsigned line, char *kw, char *val); + +void +cfg_syslog_tag (unsigned line, char *kw, char *val) +{ + syslog_tag = strdup (val); +} + +void +cfg_syslog_facility (unsigned line, char *kw, char *val) +{ + int i; + static struct { + char *name; + int facility; + } syslog_kw[] = { + { "USER", LOG_USER }, + { "DAEMON", LOG_DAEMON }, + { "AUTH", LOG_AUTH }, + { "LOCAL0", LOG_LOCAL0 }, + { "LOCAL1", LOG_LOCAL1 }, + { "LOCAL2", LOG_LOCAL2 }, + { "LOCAL3", LOG_LOCAL3 }, + { "LOCAL4", LOG_LOCAL4 }, + { "LOCAL5", LOG_LOCAL5 }, + { "LOCAL6", LOG_LOCAL6 }, + { "LOCAL7", LOG_LOCAL7 }, + { "MAIL", LOG_MAIL } + }; + + if (strncasecmp (val, "LOG_", 4) == 0) + val += 4; + + for (i = 0; i < sizeof (syslog_kw) / sizeof (syslog_kw[0]); i++) + if (strcasecmp (syslog_kw[i].name, val) == 0) + { + log_facility = syslog_kw[i].facility; + return; + } + logmsg (LOG_ERR, "%s:%u: Unknown facility `%s'", config_file, line, val); +} + +void +cfg_user (unsigned line, char *kw, char *val) +{ + struct passwd *pwd = getpwnam (val); + if (!pwd) + logmsg (LOG_ERR, "%s:%u: no such user `%s'", config_file, line, val); + else if (pwd->pw_uid == 0) + logmsg (LOG_ERR, "%s:%u: user `%s' has zero UID", config_file, line, val); + else + user = strdup (val); +} + +struct group_list +{ + struct group_list *next; + gid_t gid; +}; + +static struct group_list *group_list; + +void +cfg_group (unsigned line, char *kw, char *val) +{ + struct group *group = getgrnam (val); + if (group) + { + struct group_list *p = emalloc (sizeof *p); + p->gid = group->gr_gid; + p->next = group_list; + group_list = p; + } + else + logmsg(LOG_ERR, "%s:%u: unknown group `%s'", config_file, + line, val); +} + +void +cfg_pidfile (unsigned line, char *kw, char *val) +{ + pidfile = strdup (val); +} + +void +cfg_prog (unsigned line, char *kw, char *val) +{ + char *prog = val; + char *p = val; + + for (; *p && !isspace (*p); p++) + ; + + if (*p) + { + *p++ = 0; + for (; *p && isspace (*p); p++) + ; + + val = p; + } + else + val = 0; + + register_prog (prog, val); +} + +struct kw_handler +{ + char *kw; + cfg_fun handler; +}; + +struct kw_handler kw_handler[] = { + { "syslog-tag", cfg_syslog_tag }, + { "syslog-facility", cfg_syslog_facility }, + { "user", cfg_user }, + { "group", cfg_group }, + { "pidfile", cfg_pidfile }, + { "prog", cfg_prog }, + { NULL } +}; + +cfg_fun +find_cfg_fun (char *kw) +{ + struct kw_handler *p; + for (p = kw_handler; p->kw; p++) + if (strcmp (p->kw, kw) == 0) + return p->handler; + return NULL; +} + +void +parse_config () +{ + FILE *fp = fopen (config_file, "r"); + char *buf = NULL; + size_t size = 0; + unsigned line = 0; + + if (!fp) + { + logmsg (LOG_EMERG, "cannot open config file `%s': %s", + config_file, strerror (errno)); + exit (1); + } + + while (getline (&buf, &size, fp) > 0) + { + size_t len; + char *p, *kw, *val = NULL; + cfg_fun fun; + + line++; + for (p = buf; *p && isspace (*p); p++) + ; + if (*p == '#' || *p == 0) + continue; + + len = strlen (p); + if (p[len-1] == '\n') + p[--len] = 0; + + for (;len > 0 && isspace (p[len-1]); len--) + ; + + if (len > 0) + p[len] = 0; + else + continue; + + kw = p; + for (; *p && !isspace (*p); p++) + ; + + if (*p) + { + *p++ = 0; + for (; *p && isspace (*p); p++) + ; + + val = p; + } + + fun = find_cfg_fun (kw); + if (fun) + fun(line, kw, val); + else + { + logmsg(LOG_WARNING, "%s:%u: legacy line", config_file, line); + register_prog (kw, val); + } + } + free (buf); + fclose (fp); +} + + +void +pidfile_write () +{ + if (pidfile) + { + FILE *fp = fopen (pidfile, "w"); + if (!fp) + { + logmsg (LOG_CRIT, "cannot open pidfile `%s' for writing: %s", + pidfile, strerror (errno)); + return; + } + fprintf (fp, "%lu\n", (unsigned long) getpid ()); + fclose (fp); + } +} + +void +pidfile_remove () +{ + if (pidfile && unlink (pidfile)) + logmsg (LOG_ERR, "cannot remove pidfile `%s': %s", + pidfile, strerror (errno)); +} + + +/* Switch to the given UID/GID */ +int +switch_to_privs (uid_t uid, gid_t gid) +{ + int rc = 0; + gid_t *emptygidset; + size_t size = 1, j = 1; + struct group_list *gp; + + if (uid == 0) + { + logmsg(LOG_EMERG, "refusing to run as root"); + return 1; + } + + /* Create a list of supplementary groups */ + for (gp = group_list; gp; gp = gp->next) + size++; + emptygidset = emalloc (size * sizeof emptygidset[0]); + emptygidset[0] = gid ? gid : getegid (); + + for (gp = group_list; gp; gp = gp->next) + emptygidset[j++] = gp->gid; + + /* Reset group permissions */ + if (geteuid () == 0 && setgroups (j, emptygidset)) + { + logmsg (LOG_ERR, "setgroups(1, %lu) failed: %s", + (unsigned long) emptygidset[0], + strerror (errno)); + rc = 1; + } + free (emptygidset); + + /* Switch to the user's gid. On some OSes the effective gid must + be reset first */ + +#if defined(HAVE_SETEGID) + if ((rc = setegid (gid)) < 0) + logmsg (LOG_ERR, "setegid(%lu) failed: %s", + (unsigned long) gid, strerror (errno)); +#elif defined(HAVE_SETREGID) + if ((rc = setregid (gid, gid)) < 0) + logmsg (LOG_ERR, "setregid(%lu,%lu) failed: %s", + (unsigned long) gid, (unsigned long) gid, + strerror (errno)); +#elif defined(HAVE_SETRESGID) + if ((rc = setresgid (gid, gid, gid)) < 0) + logmsg (LOG_ERR, "setresgid(%lu,%lu,%lu) failed: %s", + (unsigned long) gid, + (unsigned long) gid, + (unsigned long) gid, + strerror (errno)); +#endif + + if (rc == 0 && gid != 0) + { + if ((rc = setgid (gid)) < 0 && getegid () != gid) + logmsg (LOG_ERR, "setgid(%lu) failed: %s", + (unsigned long) gid, strerror (errno)); + if (rc == 0 && getegid () != gid) + { + logmsg (LOG_ERR, "Cannot set effective gid to %lu", + (unsigned long) gid); + rc = 1; + } + } + + /* Now reset uid */ + if (rc == 0 && uid != 0) + { + uid_t euid; + + if (setuid (uid) + || geteuid () != uid + || (getuid () != uid + && (geteuid () == 0 || getuid () == 0))) + { + +#if defined(HAVE_SETREUID) + if (geteuid () != uid) + { + if (setreuid (uid, -1) < 0) + { + logmsg (LOG_ERR, "setreuid(%lu,-1) failed", + (unsigned long) uid, + strerror (errno)); + rc = 1; + } + if (setuid (uid) < 0) + { + logmsg (LOG_ERR, "second setuid(%lu) failed", + (unsigned long) uid, + strerror (errno)); + rc = 1; + } + } + else +#endif + { + logmsg (LOG_ERR, "setuid(%lu) failed", + (unsigned long) uid, + strerror (errno)); + rc = 1; + } + } + + euid = geteuid (); + if (uid != 0 && setuid (0) == 0) + { + logmsg (LOG_ERR, "seteuid(0) succeeded when it should not"); + rc = 1; + } + else if (uid != euid && setuid (euid) == 0) + { + logmsg (LOG_ERR, "Cannot drop non-root setuid privileges"); + rc = 1; + } + + } + + return rc; +} + +void +priv_setup () +{ + if (getuid () == 0) + { + if (!user) + { + logmsg (LOG_ERR, "non-privileged user not given"); + exit (1); + } + else + { + struct passwd *pw = getpwnam (user); + if (!pw) + { + logmsg (LOG_ERR, "No such user: %s", user); + exit (1); + } + if (pw && switch_to_privs (pw->pw_uid, pw->pw_gid)) + exit (1); + } + } +} + + +#define ACTION_CONT 0 +#define ACTION_STOP 1 +#define ACTION_RESTART 2 + +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; + logmsg (LOG_NOTICE, "received signal %d", sig); + break; + + case SIGHUP: + logmsg (LOG_NOTICE, "received signal %d", sig); + if (x_argc == 0) + { + logmsg (LOG_NOTICE, + "SIGHUP: not started with an absolute pathname"); + action = ACTION_STOP; + } + else + action = ACTION_RESTART; + break; + + case SIGALRM: + got_alarm = 1; + } + 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); +} + + +int +main(int argc, char **argv) +{ + int c; + + progname = argv[0]; + while ((c = getopt(argc, argv, "c:Dfehp:v")) != EOF) + { + switch (c) + { + case 'c': + config_file = optarg; + break; + + case 'D': + debug_level++; + break; + + case 'f': + foreground = 1; + + case 'e': + log_to_stderr = 1; + break; + + case 'h': + usage (); + exit (0); + + case 'p': + pidfile = optarg; + break; + + case 'v': + version (); + exit (0); + + default: + logmsg (LOG_CRIT, "unknown option: %c", c); + exit (1); + } + } + + if (argc != optind) + { + logmsg (LOG_CRIT, "extra command line arguments"); + exit (1); + } + + parse_config(); + + if (!log_to_stderr) + { + openlog (syslog_tag, LOG_PID, log_facility); + log_printer = syslog_printer; + } + + if (argv[0][0] == '/') + { + x_argc = argc; + x_argv = argv; + } + else + logmsg (LOG_NOTICE, "jabberd not started as an absolute pathname; SIGHUP will not work"); + + priv_setup (); + umask (037); + logmsg (LOG_NOTICE, "jabberd started"); + if (!foreground && daemon (0, 0) == -1) + { + logmsg (LOG_CRIT, "cannot become a daemon: %s", + strerror (errno)); + exit (1); + } + pidfile_write (); + + signal_setup (sig_handler); + + progman_start (); + + while (action == ACTION_CONT) + { + pause (); + if (children_cleanup) + { + children_cleanup = 0; + progman_cleanup (0); + } + if (got_alarm) + { + got_alarm = 0; + progman_wake_disabled (); + } + } + progman_stop (); + + pidfile_remove (); + if (action == ACTION_RESTART) + { + int i; + + for (i = getmaxfd (); i > 0; i--) + close (i); + + signal_setup (SIG_DFL); + + execv (x_argv[0], x_argv); + openlog (syslog_tag, LOG_PID, log_facility); + logmsg (LOG_EMERG, "cannot restart: %s", strerror (errno)); + } + logmsg (LOG_NOTICE, "jabberd finished"); + exit (0); +} diff --git a/jabberd/progman.c b/jabberd/progman.c new file mode 100644 index 0000000..274118a --- /dev/null +++ b/jabberd/progman.c @@ -0,0 +1,273 @@ +/* jabberd - a dispatcher program for jabber 2.x + Copyright (C) 2007 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. */ + +#include "jabberd.h" + +struct prog +{ + struct prog *next; + pid_t pid; + char *cmd; + char *cfg; + time_t timestamp; + size_t count; + int disabled; +}; + + +static struct prog *proghead, *progtail; +static size_t prognum; + +void +register_prog (char *cmd, char *cfg) +{ + size_t cmdlen = strlen (cmd) + 1; + size_t cfglen = cfg ? (strlen (cfg) + 1) : 0; + struct prog *pp = emalloc (sizeof (*pp) + cmdlen + cfglen); + memset(pp, 0, sizeof(*pp)); + pp->pid = 0; + pp->cmd = (char *) (pp + 1); + strcpy (pp->cmd, cmd); + if (cfglen) + { + pp->cfg = (char *) (pp + 1) + cmdlen + 1; + strcpy (pp->cfg, cfg); + } + else + pp->cfg = NULL; + pp->next = NULL; + if (progtail) + progtail->next = pp; + else + proghead = pp; + progtail = pp; +} + +static struct prog * +find_prog (pid_t pid) +{ + struct prog *prog; + + for (prog = proghead; prog; prog = prog->next) + if (prog->pid == pid) + break; + return prog; +} + +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; +} + +static void +prog_start (struct prog *prog) +{ + int i; + pid_t pid; + char *argv[5]; + int argc = 0; + time_t now; + + argv[argc++] = prog->cmd; + if (prog->cfg) + { + argv[argc++] = "-c"; + argv[argc++] = prog->cfg; + } + if (debug_level > 2) + argv[argc++] = "-D"; + argv[argc] = NULL; + + time (&now); + + if (prog->timestamp + TESTTIME > now) + prog->count++; + else + { + prog->count = 0; + prog->timestamp = now; + } + + if (prog->count > MAXSPAWN) + { + int old_alarm; + + logmsg(LOG_ERR, "%s is respawning too fast, disabled for %d minutes", + prog->cmd, SLEEPTIME / 60); + prog->timestamp = now; + prog->disabled = 1; + + old_alarm = alarm (0); + if (old_alarm > SLEEPTIME || old_alarm <= 0) + old_alarm = SLEEPTIME; + alarm (old_alarm); + return; + } + + logmsg (LOG_DEBUG, "starting %s", prog->cmd); + + switch (pid = fork ()) + { + /* The child branch. */ + case 0: + /* Close unneded descripitors */ + for (i = getmaxfd (); i > 2; i--) + close (i); + + signal_setup (SIG_DFL); + + execvp(argv[0], argv); + openlog (syslog_tag, LOG_PID, log_facility); + logmsg (LOG_CRIT, "cannot start `%s': %s", prog->cmd, + strerror (errno)); + exit (1); + + case -1: + logmsg (LOG_CRIT, "cannot runt `%s': fork failed: %s", + prog->cmd, strerror (errno)); + break; + + default: + prog->pid = pid; + } +} + +void +progman_start () +{ + struct prog *prog; + + for (prog = proghead; prog; prog = prog->next) + prog_start (prog); +} + +void +progman_wake_disabled () +{ + struct prog *prog; + + for (prog = proghead; prog; prog = prog->next) + if (prog->disabled) + { + prog->disabled = 0; + prog->count = 0; + prog->timestamp = 0; + prog_start (prog); + } +} + +static void +prog_stop_recursive (struct prog *prog, int sig) +{ + if (!prog) + return; + prog_stop_recursive (prog->next, sig); + if (prog->pid > 0) + { + logmsg (LOG_DEBUG, "Stopping %s", + prog->cmd, (unsigned long) prog->pid); + kill (prog->pid, sig); + } +} + +void +progman_stop () +{ + int i; + + prog_stop_recursive (proghead, SIGTERM); + for (i = 0; i < 3; i++) + { + progman_cleanup (1); + if (progman_running_count () == 0) + return; + sleep (1); + } + prog_stop_recursive (proghead, SIGKILL); +} + +static void +print_status (struct prog *prog, int status, int expect_term) +{ + if (WIFEXITED (status)) + { + if (WEXITSTATUS (status) == 0) + logmsg (LOG_DEBUG, + "%s (%lu) exited successfully", + prog->cmd, (unsigned long) prog->pid); + else + logmsg (LOG_ERR, + "%s (%lu) failed with status %d", + prog->cmd, (unsigned long) prog->pid, + WEXITSTATUS (status)); + } + else if (WIFSIGNALED (status)) + { + int prio; + + if (expect_term && WTERMSIG (status) == SIGTERM) + prio = LOG_DEBUG; + else + prio = LOG_ERR; + + logmsg(prio, + "%s (%lu) terminated on signal %d", + prog->cmd, (unsigned long) prog->pid, + WTERMSIG (status)); + } + else if (WIFSTOPPED (status)) + logmsg(LOG_ERR, + "%s (%lu) stopped on signal %d", + prog->cmd, (unsigned long) prog->pid, + WSTOPSIG (status)); +#ifdef WCOREDUMP + else if (WCOREDUMP (status)) + logmsg (LOG_ERR, + "%s (%lu) dumped core", + prog->cmd, (unsigned long) prog->pid); +#endif + else + logmsg(LOG_ERR, + "%s (%lu) terminated with unrecognized status", + prog->cmd, (unsigned long) prog->pid); +} + +void +progman_cleanup (int expect_term) +{ + pid_t pid; + int status; + while ((pid = waitpid (-1, &status, WNOHANG)) > 0) + { + struct prog *prog = find_prog (pid); + if (!prog) + logmsg (LOG_NOTICE, "unknown child %lu finished", + (unsigned long) pid); + print_status (prog, status, expect_term); + prog->pid = 0; + if (!expect_term) + prog_start (prog); + } +} |