summaryrefslogtreecommitdiffabout
path: root/jabberd/main.c
authorSergey Poznyakoff <gray@gnu.org.ua>2007-05-31 12:39:44 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2007-05-31 12:39:44 (GMT)
commit20fe14d5c6e24b874c2ecbba7d3900d772406e69 (patch) (side-by-side diff)
treec66cf3eeb50afc35d0deaa8d4cb36a7df237e006 /jabberd/main.c
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
Diffstat (limited to 'jabberd/main.c') (more/less context) (ignore whitespace changes)
-rw-r--r--jabberd/main.c690
1 files changed, 690 insertions, 0 deletions
diff --git a/jabberd/main.c b/jabberd/main.c
new file mode 100644
index 0000000..f195f72
--- a/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);
+}

Return to:

Send suggestions and report system problems to the System administrator.