aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog16
-rw-r--r--README7
-rw-r--r--doc/gsc.texi104
-rw-r--r--jabberd/Makefile.am3
-rw-r--r--jabberd/jabberd.h4
-rw-r--r--jabberd/main.c412
-rw-r--r--jabberd/progman.c228
7 files changed, 667 insertions, 107 deletions
diff --git a/ChangeLog b/ChangeLog
index bdd2c2d..121e908 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2007-06-04 Sergey Poznyakoff <gray@gnu.org.ua>
+
+ * jabberd/main.c: Implement long options, running instance
+ control and component dependency checking
+ * jabberd/Makefile.am (AM_CPPFLAGS): Pass statedir.
+ * jabberd/jabberd.h (register_prog): Remove
+ (register_trasport): New proto
+ (progman_stop_component, progman_dump_stats): New proto
+ * jabberd/progman.c (struct prog.depend): New member
+ (TYPE_PROG): Rename to TYPE_CORE
+ (register_prog): Change signature. Store dependency information.
+ (register_transport): New function
+ (prog_stop): New function
+ (progman_stop_component,progman_dump_stats): New functions
+ * doc/gsc.texi: Update
+
2007-06-03 Sergey Poznyakoff <gray@gnu.org.ua>
* doc/gsc.texi: Document jabberd.
diff --git a/README b/README
index 6105362..abcd633 100644
--- a/README
+++ b/README
@@ -1,12 +1,12 @@
GSC README
-Copyright (C) 2005 Sergey Poznyakoff
+Copyright (C) 2005, 2007 Sergey Poznyakoff
See the end of file for copying conditions.
* Introduction
GSC stands for Gray's Script Collection. It is a collection of
shell scripts I use on my machines. Basically, it's for my own use,
-but you may find it useful too. All scripts are under GPL.
+but you may find it useful too. All scripts and programs are under GPL.
* Installation
@@ -16,6 +16,9 @@ but you may find it useful too. All scripts are under GPL.
make
make install
+Some C code relies on GNU libc functions, such as obstack, getline or
+getopt_long. If you lack these, you'll have to provide replacements.
+
* Documentation
Full documentation is shipped in doc subdirectory
diff --git a/doc/gsc.texi b/doc/gsc.texi
index 264004d..4e64ce3 100644
--- a/doc/gsc.texi
+++ b/doc/gsc.texi
@@ -1136,29 +1136,51 @@ precompiled defaults. Otherwise, the following command line options
are understood:
@table @option
-@item -c @var{file}
+@item --config-file=@var{file}
+@itemx -c @var{file}
Use @var{file} as the main configuration file.
-
-@item -D
+
+@item --debug
+@itemx -D
Increase debugging level.
-
-@item -f
+
+@item --foreground
+@itemx -f
Do not disconnect from the controlling terminal, but run in
foreground mode instead. This option is mainly useful for debugging.
It implies @option{-e} (see below).
-@item -e
+@item --force
+ Attempt to start up even if another instance of @command{jabberd}
+seems to be running.
+
+@item --stderr
+@itemx -e
Print all diagnostics on the standard output.
-
-@item -h
+
+@item --help
+@itemx -h
Display a terse usage summary.
-
-@item -p @var{file}
- Write master process @acronym{ID} to @var{file}. This option
-overrides @option{pdifile} configuration file statement (see the next
-section).
-@item -v
+@item --restart @var{tag} [@var{tags}...]
+@itemx -r @var{tag} [@var{tag}...]
+ Restart named components. Any number of arguments can be
+specified. Each @var{tag} must correspond to a valid tag in
+@file{jabberd.cfg} file.
+
+@item --status
+ Display information about the running instance. Return 0 if the
+instance is running, 1 otherwise.
+
+@item --stop
+ Stop the running instance by sending it the @acronym{SIGTERM} signal.
+
+@item --reload
+@item --hup
+ Restart the running instance by sending it the @acronym{SIGHUP} signal.
+
+@item --version
+@itemx -v
Print program version and licensing information and exit.
@end table
@@ -1198,13 +1220,14 @@ file, and will be shut down in the reverse order. The @code{prog}
statement is designed expressly to start jabber core programs, it
should not be used to start third-party programs.
- To start third-party programs, e.g. transports, use @code{exec}
+ To start third-party programs, e.g. transports, use @code{transport}
statement. It is a compound statement that has the following
structure:
@smallexample
-exec @var{tag}
+transport @var{tag}
command @var{command-line}
+ depend @var{modlist}
stdout @var{prio}
stderr @var{prio}
end
@@ -1226,8 +1249,9 @@ word of @var{command-line}.
following statement:
@smallexample
-exec ggtrans
+transport ggtrans
command /usr/local/sbin/jggtrans -f
+ depend all
stdout notice
stderr notice
end
@@ -1271,15 +1295,16 @@ statements in detail.
@node cfgstat
@subsubsection Configuration File Statements
-@deffn {Jabber Statement} exec [@var{tag}]
+@deffn {Jabber Statement} transport [@var{tag}]
Schedule a third-party program (e.g. a transport) for startup.
@var{tag} specifies the prefix to identify this program in the
-diagnostic poutput. The @code{exec} statement is a block statement
+diagnostic poutput. The @code{transport} statement is a block statement
that have the following structure:
@smallexample
-exec @var{tag}
+transport @var{tag}
command @var{command-line}
+ depend @var{component-list}
stdout @var{prio}
stderr @var{prio}
facility @var{fac}
@@ -1288,15 +1313,28 @@ end
The subordinate statements are:
-@deffn {exec statement} command @var{command-line}
+@deffn {transport statement} command @var{command-line}
Set the command line of the transport. @var{command-line} is parsed
much the same way as in shell, except that no variable substitution
takes place.
@end deffn
+@deffn {transport statement} depend @var{component-list}
+Declare that this module depends on the components listed in
+@var{component-list}. Whenever one of these components is restarted,
+the transport will be restarted as well.
+
+@var{component-list} is either a whitespace-separated list of
+components, or one of the following words: @samp{all}, meaning that
+this transport depends on all core components started before it, and
+@samp{none}, meaning that it does not depend on anything.
+
+The default is @samp{all}.
+@end deffn
+
@anchor{stdout}
-@deffn {exec statement} stdout @var{prio}
-@deffnx {exec statement} stderr @var{prio}
+@deffn {transport statement} stdout @var{prio}
+@deffnx {transport statement} stderr @var{prio}
Redirect program's standard output (or error, in case of
@code{stderr}) to the given syslog priority. Allowed values for
@var{prio} are: @samp{EMERG}, @samp{ALERT}, @samp{CRIT}, @samp{ERR},
@@ -1305,7 +1343,7 @@ optionally prefixed with @samp{LOG_}. The string matching is
case-insensitive.
@end deffn
-@deffn {exec statement} facility @var{facility}
+@deffn {transport statement} facility @var{facility}
This statement does nothing. It is reserved for future use.
@end deffn
@@ -1319,6 +1357,20 @@ privileges.
Write master process @acronym{ID} to @var{file}
@end deffn
+@deffn {Jabber Statement} ctlfile @var{file}
+Set location of the control file. This file is used to pass additional
+information to the running daemon program (e.g., when running
+@command{jabberd --restart @var{comp}}). The default value is
+@file{/usr/local/var/jabberd/.jabberd.ctl}.
+@end deffn
+
+@deffn {Jabber Statement} statfile @var{file}
+Set location of the statistics output file. This file is used to pass
+statistics information from the running daemon program (e.g., when running
+@command{jabberd --status}). The default value is
+@file{/usr/local/var/jabberd/.jabberd.stat}.
+@end deffn
+
@deffn {Jabber Statement} prog @var{command} [@var{config-file}]
Schedule a jabber core program for startup. The program name is given
by @var{command} argument. Optional @var{config-file} gives the
@@ -1381,14 +1433,14 @@ prog s2s /usr/local/etc/jabberd/s2s.xml
prog c2s /usr/local/etc/jabberd/c2s.xml
# @r{Start @acronym{GG} transport and capture its output:}
-exec ggtrans
+transport ggtrans
command /usr/local/sbin/jggtrans -f
stdout notice
stderr notice
end
# @r{Start @acronym{ICQ} transport and capture its output:}
-exec jit
+transport jit
command /usr/local/bin/jabberd-jit -c /usr/local/etc/jit.xml
stdout notice
stderr notice
diff --git a/jabberd/Makefile.am b/jabberd/Makefile.am
index b5667ba..8fe9158 100644
--- a/jabberd/Makefile.am
+++ b/jabberd/Makefile.am
@@ -1,3 +1,4 @@
bin_PROGRAMS = jabberd
jabberd_SOURCES = main.c progman.c jabber.h argcv.c
-AM_CPPFLAGS=-DSYSCONFDIR=\"$(sysconfdir)\" \ No newline at end of file
+AM_CPPFLAGS=-DSYSCONFDIR=\"$(sysconfdir)\"\
+ -DSTATEDIR=\"$(localstatedir)/jabberd\"
diff --git a/jabberd/jabberd.h b/jabberd/jabberd.h
index 6c9f480..336a023 100644
--- a/jabberd/jabberd.h
+++ b/jabberd/jabberd.h
@@ -48,7 +48,7 @@
#define SLEEPTIME 5*60
#define MAXSPAWN 10
-void register_prog (char *tag, char **argv, int retr[2]);
+void register_trasport (char *tag, char **argv, int retr[2], char **depv);
void register_jabber_process (char *cmd, char *cfg);
void logmsg(int prio, char *fmt, ...);
void signal_setup (RETSIGTYPE (*sf)(int));
@@ -59,6 +59,8 @@ void progman_start (void);
void progman_stop (void);
void progman_cleanup (int);
void progman_wake_disabled (void);
+void progman_stop_component (const char *);
+void progman_dump_stats (const char *);
extern int debug_level;
extern char *syslog_tag;
diff --git a/jabberd/main.c b/jabberd/main.c
index e2a00ac..476abd8 100644
--- a/jabberd/main.c
+++ b/jabberd/main.c
@@ -28,7 +28,9 @@ char *syslog_tag = "jabberd";
int log_facility = LOG_LOCAL7;
int x_argc;
char **x_argv;
-char *pidfile;
+char *pidfile = STATEDIR "/jabberd.pid";
+char *ctlfile = STATEDIR "/.jabberd.ctl";
+char *statfile = STATEDIR "/.jabberd.stat";
unsigned long shutdown_timeout = 5;
mode_t jabberd_umask = 037;
@@ -131,17 +133,27 @@ version ()
void
usage ()
{
- printf ("usage: jabberd [-Dfehv][-c config][-p pidfile]\n");
+ printf ("usage: jabberd [-Dfehv][-c config][-r tag [tag...]]\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 ("General-purpose options:\n");
+ printf (" -c, --config-file=FILE use this configuration file\n");
+ printf (" -D, --debug increase debugging level\n");
+ printf (" -e, --stderr use standard error for diagnostics output\n");
+ printf (" -f, --foreground run in foreground mode (implies -e)\n");
+ printf (" --force start up even if another copy seems to be running\n");
+
+ printf ("\nControlling running instance:\n");
+ printf (" --status display status information\n");
+ printf (" --stop stop the running copy\n");
+ printf (" --reload, --hup reload the running copy\n");
+ printf (" -r, --restart tag [tag...] restart named components\n");
+
+ printf ("\nInformational options:\n");
+ printf (" -h, --help display this help list\n");
+ printf (" -v, --version display program version\n");
+
+
printf ("\n");
printf ("Report bugs to <%s>\n", PACKAGE_BUGREPORT);
}
@@ -356,6 +368,18 @@ cfg_pidfile (struct cfg_file *file, char *kw, char *val, void *unused)
}
void
+cfg_ctlfile (struct cfg_file *file, char *kw, char *val, void *unused)
+{
+ ctlfile = strdup (val);
+}
+
+void
+cfg_statfile (struct cfg_file *file, char *kw, char *val, void *unused)
+{
+ ctlfile = strdup (val);
+}
+
+void
cfg_umask (struct cfg_file *file, char *kw, char *val, void *unused)
{
char *p;
@@ -390,60 +414,76 @@ cfg_prog (struct cfg_file *file, char *kw, char *val, void *unused)
register_jabber_process (prog, val);
}
-struct exec_rec {
+struct transport_rec
+{
char *tag;
char *command;
int facility;
int retr[2];
+ int depc;
+ char **depv;
};
static void
-cfg_exec_command (struct cfg_file *file, char *kw, char *val, void *data)
+cfg_transport_command (struct cfg_file *file, char *kw, char *val, void *data)
{
- struct exec_rec *prec = data;
+ struct transport_rec *prec = data;
prec->command = strdup (val);
}
void
-cfg_exec_facility (struct cfg_file *file, char *kw, char *val, void *data)
+cfg_transport_facility (struct cfg_file *file, char *kw, char *val, void *data)
{
- struct exec_rec *prec = data;
+ struct transport_rec *prec = data;
if (str_to_facility (val, &prec->facility))
logmsg (LOG_ERR, "%s:%u: Unknown facility `%s'",
config_file, file->line, val);
}
void
-cfg_exec_stdout (struct cfg_file *file, char *kw, char *val, void *data)
+cfg_transport_stdout (struct cfg_file *file, char *kw, char *val, void *data)
{
- struct exec_rec *prec = data;
+ struct transport_rec *prec = data;
if (str_to_priority (val, &prec->retr[RETR_OUT]))
logmsg (LOG_ERR, "%s:%u: Unknown priority `%s'",
config_file, file->line, val);
}
void
-cfg_exec_stderr (struct cfg_file *file, char *kw, char *val, void *data)
+cfg_transport_stderr (struct cfg_file *file, char *kw, char *val, void *data)
{
- struct exec_rec *prec = data;
+ struct transport_rec *prec = data;
if (str_to_priority (val, &prec->retr[RETR_ERR]))
logmsg (LOG_ERR, "%s:%u: Unknown priority `%s'",
config_file, file->line, val);
}
void
-cfg_exec (struct cfg_file *file, char *kw, char *val, void *unused)
+cfg_transport_depend (struct cfg_file *file, char *kw, char *val, void *data)
+{
+ struct transport_rec *prec = data;
+ int rc;
+ if (rc = argcv_get (val, NULL, NULL, &prec->depc, &prec->depv))
+ {
+ logmsg (LOG_ERR, "%s:%u: cannot split dependency line: %s",
+ config_file, file->line, strerror (rc));
+ }
+}
+
+void
+cfg_transport (struct cfg_file *file, char *kw, char *val, void *unused)
{
int rc;
int argc;
char **argv;
- struct exec_rec rec;
+ struct transport_rec rec;
static struct kw_handler kwtab[] = {
- { "command", cfg_exec_command },
- { "stdout", cfg_exec_stdout },
- { "stderr", cfg_exec_stderr },
- { "facility", cfg_exec_facility },
+ { "command", cfg_transport_command },
+ { "stdout", cfg_transport_stdout },
+ { "stderr", cfg_transport_stderr },
+ { "facility", cfg_transport_facility },
+ { "depend", cfg_transport_depend },
{ NULL }
};
memset (&rec, 0, sizeof rec);
@@ -459,9 +499,10 @@ cfg_exec (struct cfg_file *file, char *kw, char *val, void *unused)
config_file, file->line, strerror (rc));
return;
}
- register_prog (rec.tag, argv, rec.retr);
+ register_transport (rec.tag, argv, rec.retr, rec.depv);
free (rec.tag);
free (rec.command);
+ argcv_free (rec.depc, rec.depv);
argcv_free (argc, argv);
}
@@ -482,9 +523,12 @@ struct kw_handler kw_handler[] = {
{ "user", cfg_user },
{ "group", cfg_group },
{ "pidfile", cfg_pidfile },
+ { "ctlfile", cfg_ctlfile },
+ { "statfile", cfg_statfile },
{ "umask", cfg_umask },
{ "prog", cfg_prog },
- { "exec", cfg_exec },
+ { "transport", cfg_transport },
+ { "exec", cfg_transport },
{ "shutdown-timeout", cfg_shutdown_timeout },
{ NULL }
};
@@ -516,24 +560,59 @@ parse_config ()
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);
+}
+
+pid_t
+pidfile_read (int must_exist)
+{
+ int c;
+ pid_t n = 0;
+ FILE *fp = fopen (pidfile, "r");
+ if (!fp)
{
- FILE *fp = fopen (pidfile, "w");
- if (!fp)
+ if (must_exist && errno != ENOENT)
+ logmsg (LOG_ERR, "cannot open pid file `%s': %s",
+ pidfile,
+ strerror (errno));
+ return -1;
+ }
+
+ while ((c = fgetc (fp)) != EOF)
+ {
+ if (isdigit (c))
+ n = n * 10 + c - '0';
+ else if (c == '\n')
+ break;
+ else
{
- logmsg (LOG_CRIT, "cannot open pidfile `%s' for writing: %s",
- pidfile, strerror (errno));
- return;
+ logmsg (LOG_ERR, "unexpected character %#03o in pidfile `%s'",
+ c, pidfile);
+ return -1;
}
- fprintf (fp, "%lu\n", (unsigned long) getpid ());
- fclose (fp);
}
+ fclose (fp);
+ if (kill (n, 0))
+ {
+ logmsg (LOG_ERR, "Cannot signal master process %lu: %s",
+ (unsigned long) n, strerror (errno));
+ return -1;
+ }
+ return n;
}
void
pidfile_remove ()
{
- if (pidfile && unlink (pidfile))
+ if (unlink (pidfile))
logmsg (LOG_ERR, "cannot remove pidfile `%s': %s",
pidfile, strerror (errno));
}
@@ -547,7 +626,7 @@ switch_to_privs (uid_t uid, gid_t gid)
gid_t *emptygidset;
size_t size = 1, j = 1;
struct group_list *gp;
-
+
if (uid == 0)
{
logmsg(LOG_EMERG, "refusing to run as root");
@@ -688,9 +767,11 @@ priv_setup ()
}
-#define ACTION_CONT 0
-#define ACTION_STOP 1
-#define ACTION_RESTART 2
+#define ACTION_CONT 0
+#define ACTION_STOP 1
+#define ACTION_RESTART 2
+#define ACTION_COMPRELOAD 3
+#define ACTION_DUMPSTATS 4
int action = ACTION_CONT;
int children_cleanup = 0;
@@ -726,6 +807,15 @@ sig_handler(int sig)
case SIGALRM:
got_alarm = 1;
+ break;
+
+ case SIGUSR1:
+ action = ACTION_COMPRELOAD;
+ break;
+
+ case SIGUSR2:
+ action = ACTION_DUMPSTATS;
+ break;
}
signal (sig, sig_handler);
}
@@ -739,6 +829,8 @@ signal_setup (RETSIGTYPE (*sf)(int))
signal (SIGINT, sf);
signal (SIGHUP, sf);
signal (SIGALRM, sf);
+ signal (SIGUSR1, sf);
+ signal (SIGUSR2, sf);
}
void
@@ -748,13 +840,178 @@ syslog_setup ()
}
+
+void
+stop_components ()
+{
+ FILE *fp;
+ size_t size = 0;
+ char *buf = NULL;
+
+ logmsg (LOG_INFO, "stopping components");
+
+ fp = fopen (ctlfile, "r");
+ if (!fp)
+ {
+ logmsg (LOG_ERR, "cannot open control file `%s': %s",
+ ctlfile, strerror (errno));
+ return;
+ }
+ if (unlink (ctlfile))
+ {
+ logmsg (LOG_ERR, "cannot open control file `%s': %s",
+ ctlfile, strerror (errno));
+ fclose (fp);
+ return;
+ }
+
+ while (getline (&buf, &size, fp) > 0)
+ {
+ size_t len = strlen (buf);
+ if (len == 0)
+ continue;
+ if (buf[len-1] == '\n')
+ buf[len-1] = 0;
+ progman_stop_component (buf);
+ }
+
+ free (buf);
+ fclose (fp);
+}
+
+int
+request_restart_components (char **argv)
+{
+ FILE *fp;
+ pid_t pid = pidfile_read (1);
+
+ if (pid == -1)
+ return 1;
+
+ fp = fopen (ctlfile, "w");
+ if (!fp)
+ {
+ logmsg (LOG_ERR, "cannot open control file `%s': %s",
+ ctlfile, strerror (errno));
+ return 1;
+ }
+ for (; *argv; argv++)
+ fprintf (fp, "%s\n", *argv);
+ fclose (fp);
+
+ kill (pid, SIGUSR1);
+ return 0;
+}
+
+
+int
+jabberd_reload ()
+{
+ pid_t pid = pidfile_read (1);
+
+ if (pid == -1)
+ {
+ logmsg (LOG_CRIT, "jabberd is not running");
+ return 1;
+ }
+
+ logmsg (LOG_INFO, "reloading jabberd at PID %lu", (unsigned long) pid);
+ return kill (pid, SIGHUP) ? 2 : 0;
+}
+
+int
+jabberd_status ()
+{
+ FILE *fp;
+ pid_t pid = pidfile_read (0);
+ int i;
+
+ if (pid == -1)
+ {
+ logmsg (LOG_INFO, "jabberd is not running");
+ return 1;
+ }
+
+ if (kill (pid, SIGUSR2))
+ {
+ logmsg (LOG_INFO,
+ "jabberd is not running, but a pidfile is found (pid %lu)\n",
+ (unsigned long) pid);
+ return 1;
+ }
+ logmsg (LOG_INFO, "jabberd is running; PID %lu",
+ (unsigned long) pid);
+
+ for (i = 0; i < access (statfile, R_OK); i++)
+ sleep (1);
+
+ fp = fopen (statfile, "r");
+ if (!fp)
+ logmsg (LOG_ERR, "cannot open statfile `%s': %s",
+ statfile, strerror (errno));
+ else
+ {
+ char c;
+
+ if (unlink (statfile))
+ logmsg (LOG_ERR, "cannot unlink statfile `%s': %s",
+ statfile, strerror (errno));
+ while ((c = fgetc (fp)) != EOF)
+ fputc (c, stdout);
+ fclose (fp);
+ }
+ return 0;
+}
+
+int
+jabberd_stop ()
+{
+ pid_t pid = pidfile_read (1);
+
+ if (pid == -1)
+ {
+ logmsg (LOG_CRIT, "jabberd is not running");
+ return 1;
+ }
+
+ logmsg (LOG_INFO, "stopping jabberd at PID %lu", (unsigned long) pid);
+ return kill (pid, SIGTERM) ? 2 : 0;
+}
+
+
+enum {
+ OPTION_RELOAD = 256,
+ OPTION_STATUS,
+ OPTION_STOP,
+ OPTION_FORCE
+};
+
+struct option options[] = {
+ { "config-file", required_argument, NULL, 'c' },
+ { "debug", no_argument, NULL, 'D' },
+ { "foreground", no_argument, NULL, 'f' },
+ { "stderr", no_argument, NULL, 'e' },
+ { "help", no_argument, NULL, 'h' },
+ { "restart-module", no_argument, NULL, 'r' },
+ { "reload", no_argument, NULL, OPTION_RELOAD },
+ { "hup", no_argument, NULL, OPTION_RELOAD },
+ { "status", no_argument, NULL, OPTION_STATUS },
+ { "stop", no_argument, NULL, OPTION_STOP },
+ { "version", no_argument, NULL, 'v' },
+ { "force", no_argument, NULL, OPTION_FORCE },
+ { NULL }
+};
+
int
main(int argc, char **argv)
{
int c;
-
+ int mode = 0;
+ pid_t pid;
+ int force = 0;
+
progname = argv[0];
- while ((c = getopt(argc, argv, "c:Dfehp:v")) != EOF)
+ while ((c = getopt_long (argc, argv, "c:Dfehrv", options, NULL)) != EOF)
{
switch (c)
{
@@ -777,13 +1034,21 @@ main(int argc, char **argv)
usage ();
exit (0);
- case 'p':
- pidfile = optarg;
- break;
-
case 'v':
version ();
exit (0);
+
+ case 'r':
+ case OPTION_RELOAD:
+ case OPTION_STATUS:
+ case OPTION_STOP:
+ log_to_stderr = 1;
+ mode = c;
+ break;
+
+ case OPTION_FORCE:
+ force = 1;
+ break;
default:
logmsg (LOG_CRIT, "unknown option: %c", c);
@@ -791,7 +1056,7 @@ main(int argc, char **argv)
}
}
- if (argc != optind)
+ if (argc != optind && mode != 'r')
{
logmsg (LOG_CRIT, "extra command line arguments");
exit (1);
@@ -805,16 +1070,51 @@ main(int argc, char **argv)
log_printer = syslog_printer;
}
+ switch (mode)
+ {
+ case 'r':
+ priv_setup ();
+ umask (jabberd_umask);
+ exit (request_restart_components (argv + optind));
+
+ case OPTION_RELOAD:
+ exit (jabberd_reload ());
+
+ case OPTION_STATUS:
+ exit (jabberd_status ());
+
+ case OPTION_STOP:
+ exit (jabberd_stop ());
+
+ default:
+ priv_setup ();
+ umask (jabberd_umask);
+ }
+
+ if (!force)
+ {
+ pid = pidfile_read (0);
+ if (pid != -1)
+ {
+ logmsg (LOG_ALERT,
+ "jabberd seems to be already running at pid %lu",
+ (unsigned long) pid);
+ logmsg (LOG_INFO,
+ "use --status to verify or --force to start up anyway");
+ exit (1);
+ }
+ }
+
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");
+ logmsg (LOG_NOTICE,
+ "jabberd not started as an absolute pathname; "
+ "SIGHUP will not work");
- priv_setup ();
- umask (jabberd_umask);
logmsg (LOG_NOTICE, "jabberd started");
if (!foreground && daemon (0, 0) == -1)
{
@@ -841,6 +1141,18 @@ main(int argc, char **argv)
got_alarm = 0;
progman_wake_disabled ();
}
+ switch (action)
+ {
+ case ACTION_COMPRELOAD:
+ stop_components ();
+ action = ACTION_CONT;
+ break;
+
+ case ACTION_DUMPSTATS:
+ progman_dump_stats (statfile);
+ action = ACTION_CONT;
+ break;
+ }
}
progman_stop ();
diff --git a/jabberd/progman.c b/jabberd/progman.c
index f39dd62..26693e4 100644
--- a/jabberd/progman.c
+++ b/jabberd/progman.c
@@ -20,8 +20,9 @@
#include <sys/stat.h>
#include <fcntl.h>
-#define TYPE_PROG 0
-#define TYPE_RETR 1
+#define TYPE_CORE 0
+#define TYPE_TRANSPORT 1
+#define TYPE_RETR 2
struct prog
{
@@ -29,10 +30,12 @@ struct prog
int type;
pid_t pid; /* PID */
char *tag; /* Entry tag (for diagnostics purposes) */
+ char **depend;
union
{
struct
{
+ int argc;
char **argv; /* Command line arguments */
int retr[2];
time_t timestamp; /* Time of last startup */
@@ -48,6 +51,8 @@ struct prog
};
+#define IS_PROG(p) ((p)->type == TYPE_CORE || (p)->type == TYPE_TRANSPORT)
+
static struct prog *proghead, *progtail;
static size_t prognum;
@@ -92,42 +97,110 @@ register_retr (int type, struct prog *master, pid_t pid)
}
void
-register_prog (char *tag, char **argv, int retr[2])
+register_prog (int type, char *tag, char **argv, int retr[2], char **depv)
{
- struct prog *pp;
+ struct prog *p, *newp;
char *pstr;
int i;
size_t size = 0;
+ int dep_all = 0;
+ int depc = 0;
+
+ if (depv)
+ {
+ if (depv[0] && depv[1] == NULL)
+ {
+ if (strcmp (depv[0], "all") == 0)
+ {
+ dep_all = 1;
+ for (p = proghead; p; p = p->next)
+ if (p->type == TYPE_CORE)
+ {
+ depc++;
+ size += strlen (p->tag) + 1;
+ }
+ }
+ else if (strcmp (depv[0], "none") == 0)
+ depv = NULL;
+ }
+ if (depc == 0)
+ for (depc = 0; depv[depc]; depc++)
+ ;
+ size += sizeof (depv[0]) * (depc + 1);
+ }
+
for (i = 0; argv[i]; i++)
size += strlen (argv[i]);
- size += i + sizeof (argv[0]) * (i + 1) + sizeof (*pp)
+ size += i + sizeof (argv[0]) * (i + 1) + sizeof (*newp)
+ (tag ? (strlen (tag) + 1) : 0);
- pp = emalloc (size);
- memset (pp, 0, sizeof(*pp));
- pp->type = TYPE_PROG;
- pp->pid = 0;
- pp->v.p.argv = (char **) (pp + 1);
- pstr = (char*) (pp->v.p.argv + i + 1);
+ newp = emalloc (size);
+ memset (newp, 0, sizeof(*newp));
+ newp->type = type;
+ newp->pid = 0;
+ newp->v.p.argc = i;
+ newp->v.p.argv = (char **) (newp + 1);
+ pstr = (char*) (newp->v.p.argv + i + 1);
for (i = 0; argv[i]; i++)
{
strcpy (pstr, argv[i]);
- pp->v.p.argv[i] = pstr;
+ newp->v.p.argv[i] = pstr;
pstr += strlen (pstr) + 1;
}
- pp->v.p.argv[i] = NULL;
+ newp->v.p.argv[i] = NULL;
if (tag)
- pp->tag = strcpy (pstr, tag);
+ {
+ newp->tag = strcpy (pstr, tag);
+ pstr += strlen (pstr) + 1;
+ }
else
- pp->tag = pp->v.p.argv[0];
+ newp->tag = newp->v.p.argv[0];
- pp->v.p.retr[0] = retr[0];
- pp->v.p.retr[1] = retr[1];
+ if (depv)
+ {
+ newp->depend = (char**) pstr;
+ pstr = (char*) (newp->depend + depc + 1);
+ if (dep_all)
+ {
+ depc = 0;
+ for (p = proghead; p; p = p->next)
+ if (p->type == TYPE_CORE)
+ {
+ newp->depend[depc++] = pstr;
+ strcpy (pstr, p->tag);
+ pstr += strlen (pstr) + 1;
+ }
+ }
+ else
+ {
+ for (depc = 0; depv[depc]; depc++)
+ {
+ newp->depend[depc] = pstr;
+ strcpy (pstr, p->tag);
+ pstr += strlen (pstr) + 1;
+ }
+ }
+ depv[depc] = NULL;
+ }
+ else
+ newp->depend = NULL;
+
+ newp->v.p.retr[0] = retr[0];
+ newp->v.p.retr[1] = retr[1];
- link_prog (pp, 0);
+ link_prog (newp, 0);
+}
+
+void
+register_transport (char *tag, char **argv, int retr[2], char **depv)
+{
+ static char *std_dep[] = { "all", NULL };
+ if (!depv)
+ depv = std_dep;
+ register_prog (TYPE_TRANSPORT, tag, argv, retr, depv);
}
void
@@ -147,7 +220,7 @@ register_jabber_process (char *cmd, char *cfg)
argv[argc++] = "-D";
argv[argc] = NULL;
- register_prog (NULL, argv, retr);
+ register_prog (TYPE_CORE, NULL, argv, retr, NULL);
}
static struct prog *
@@ -316,7 +389,7 @@ progman_start ()
struct prog *prog;
for (prog = proghead; prog; prog = prog->next)
- if (prog->type == TYPE_PROG)
+ if (IS_PROG (prog))
prog_start (prog);
}
@@ -326,7 +399,7 @@ progman_wake_disabled ()
struct prog *prog;
for (prog = proghead; prog; prog = prog->next)
- if (prog->type == TYPE_PROG && prog->v.p.disabled)
+ if (IS_PROG (prog) && prog->v.p.disabled)
{
prog->v.p.disabled = 0;
prog->v.p.count = 0;
@@ -336,17 +409,23 @@ progman_wake_disabled ()
}
static void
-prog_stop_recursive (struct prog *prog, int sig)
+prog_stop (struct prog *prog, int sig)
{
- if (!prog)
- return;
- prog_stop_recursive (prog->next, sig);
if (prog->pid > 0)
{
logmsg (LOG_DEBUG, "Stopping %s",
prog->tag, (unsigned long) prog->pid);
kill (prog->pid, sig);
}
+}
+
+static void
+prog_stop_recursive (struct prog *prog, int sig)
+{
+ if (!prog)
+ return;
+ prog_stop_recursive (prog->next, sig);
+ prog_stop (prog, sig);
}
void
@@ -412,6 +491,21 @@ print_status (char *tag, pid_t pid, int status, int expect_term)
}
void
+prog_stop_dependent (struct prog *prog)
+{
+ struct prog *p;
+
+ for (p = proghead; p; p = p->next)
+ if (p->depend)
+ {
+ int i;
+ for (i = 0; p->depend[i]; i++)
+ if (strcmp (prog->tag, p->depend[i]) == 0)
+ prog_stop (p, SIGTERM);
+ }
+}
+
+void
progman_cleanup (int expect_term)
{
pid_t pid;
@@ -427,7 +521,87 @@ progman_cleanup (int expect_term)
}
print_status (prog->tag, prog->pid, status, expect_term);
prog->pid = 0;
- if (prog->type == TYPE_PROG && !expect_term)
- prog_start (prog);
+ if (IS_PROG (prog))
+ {
+ prog_stop_dependent (prog);
+ if (!expect_term)
+ prog_start (prog);
+ }
+ }
+}
+
+void
+progman_stop_component (const char *name)
+{
+ struct prog *prog;
+
+ logmsg (LOG_INFO, "stopping component `%s'", name);
+ for (prog = proghead; prog; prog = prog->next)
+ if (IS_PROG (prog) && strcmp (prog->tag, name) == 0)
+ {
+ if (prog->v.p.disabled)
+ logmsg (LOG_INFO, "stopping component `%s': component not started",
+ name);
+ else
+ prog_stop (prog, SIGTERM);
+ }
+}
+
+void
+progman_dump_stats (const char *filename)
+{
+ FILE *fp;
+ struct prog *prog;
+ char *s;
+ int rc;
+
+ logmsg (LOG_INFO, "dumping statistics to `%s'", filename);
+ fp = fopen (filename, "w");
+ if (!fp)
+ {
+ logmsg (LOG_ERR, "cannot open file `%s' for writing: %s",
+ filename, strerror (errno));
+ return;
+ }
+
+ for (prog = proghead; prog; prog = prog->next)
+ {
+ switch (prog->type)
+ {
+ case TYPE_CORE:
+ case TYPE_TRANSPORT:
+ fprintf (fp, "%s %s ",
+ prog->type == TYPE_CORE ? "core" : "transport",
+ prog->tag);
+ if (prog->pid)
+ fprintf (fp, "%lu", (unsigned long) prog->pid);
+ else if (prog->v.p.disabled)
+ {
+ char buf[48];
+
+ strftime (buf, sizeof buf, "%c",
+ localtime (&prog->v.p.timestamp));
+ fprintf (fp, "[disabled; scheduled for %s]", buf);
+ }
+ else
+ fprintf (fp, "[not running]");
+ if (rc = argcv_string (prog->v.p.argc, prog->v.p.argv, &s))
+ {
+ logmsg (LOG_ERR, "cannot convert argument list: %s",
+ strerror (rc));
+ }
+ else
+ {
+ fprintf (fp, " %s", s);
+ free (s);
+ }
+ fputc ('\n', fp);
+ break;