diff options
-rw-r--r-- | ChangeLog | 16 | ||||
-rw-r--r-- | README | 7 | ||||
-rw-r--r-- | doc/gsc.texi | 104 | ||||
-rw-r--r-- | jabberd/Makefile.am | 3 | ||||
-rw-r--r-- | jabberd/jabberd.h | 4 | ||||
-rw-r--r-- | jabberd/main.c | 412 | ||||
-rw-r--r-- | jabberd/progman.c | 228 |
7 files changed, 667 insertions, 107 deletions
@@ -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. @@ -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; |