aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2007-05-31 12:39:44 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2007-05-31 12:39:44 +0000
commit20fe14d5c6e24b874c2ecbba7d3900d772406e69 (patch)
treec66cf3eeb50afc35d0deaa8d4cb36a7df237e006
parent0e67bf9bde952f85ae8a66a05af14378fc43916d (diff)
downloadgsc-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--ChangeLog8
-rw-r--r--Makefile.am3
-rw-r--r--configure.ac5
-rw-r--r--jabberd/Makefile.am3
-rw-r--r--jabberd/jabberd.h61
-rw-r--r--jabberd/main.c690
-rw-r--r--jabberd/progman.c273
7 files changed, 1040 insertions, 3 deletions
diff --git a/ChangeLog b/ChangeLog
index 6d29fca..0696532 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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);
+ }
+}

Return to:

Send suggestions and report system problems to the System administrator.