diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-06-14 21:44:53 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-06-14 21:44:53 +0300 |
commit | 923d8c2d2a728d40c46bdc4b381308bf0b50048d (patch) | |
tree | 26440749d8b87ad98276f67a259c260240997afd | |
parent | a3de6c9ab6a862ede6235b4d34dbf9386627c4d4 (diff) | |
download | mailfromd-923d8c2d2a728d40c46bdc4b381308bf0b50048d.tar.gz mailfromd-923d8c2d2a728d40c46bdc4b381308bf0b50048d.tar.bz2 |
Fix and improve termination actions in pies.
* pies/pies.h (MAX_RETURN_CODE): Remove.
(STATUS_SIG_BIT, STATUS_CODE): New defines
(struct action): New fields next, nstat, status, command.
(struct component): Keep singly-linked list of termination
actions.
* pies/progman.c (run_command): New function.
(progman_cleanup): Redo iteration over termination actions.
* pies/pies.c (return_code_cfg_param): Pass offsets in
struct component.
(create_action): Accept signal numbers (SIG.* or SIG\+[0-9]+)
in tag.
(return_code_section_parser): Update.
(pies_check_status): Set *pid before returning pies_status_stale.
* doc/pies.texi: Document changes.
* NEWS: Updated.
-rw-r--r-- | NEWS | 26 | ||||
-rw-r--r-- | doc/pies.texi | 47 | ||||
-rw-r--r-- | pies/pies.c | 156 | ||||
-rw-r--r-- | pies/pies.h | 12 | ||||
-rw-r--r-- | pies/progman.c | 109 |
5 files changed, 299 insertions, 51 deletions
@@ -108,6 +108,32 @@ The sockmap.mf module provides functions for interfacing with MeTA1 - string sockmap_lookup(number FD, string MAP, string ARG) - string sockmap_single_lookup(string URL, string MAP, string ARG) +* pies + +Signals can be specified in tag list of `return-code' statement. +A signal is given either by its name, or as SIG+n, where n is +its number. Valid signal names are: SIGHUP, SIGINT, SIGQUIT, SIGILL, +SIGTRAP, SIGABRT, SIGIOT, SIGBUS, SIGFPE, SIGKILL, SIGUSR1, SIGSEGV, +SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, SIGSTKFLT, SIGCHLD, SIGCONT, +SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, +SIGVTALRM, SIGPROF, SIGWINCH, SIGPOLL, SIGIO, SIGPWR, SIGSYS. + +Examples of usage: + + return-code (SIGABRT, EX_USAGE) { + ... + } + + return-code SIG+6, EX_USAGE { + ... + } + +New action is allowed in `return-code' block: + + exec COMMAND; + +It executes the given COMMAND before other actions. + * Debugging New function mailutils_set_debug_level allows to set global Mailutils diff --git a/doc/pies.texi b/doc/pies.texi index 1d9df839..7f020f11 100644 --- a/doc/pies.texi +++ b/doc/pies.texi @@ -379,8 +379,9 @@ return-code @var{codes} @{ @} @end smallexample - Its argument is a list of exit codes. Exit codes can be specified -as decimal numbers, or as symbolic code names from the table below: + The @var{codes} argument is a list of exit codes or signal names. +Exit codes can be specified either as decimal numbers or as symbolic code +names from the table below: @multitable @columnfractions 0.5 0.3 @headitem Name @tab Numeric value @@ -402,9 +403,45 @@ as decimal numbers, or as symbolic code names from the table below: @item EX_CONFIG @tab 78 @end multitable - If the component exits with an exit code listed in @var{codes}, -@command{pies} executes actions specified by its substatements. These -are: +Signal codes can be given either as @samp{SIG+@var{n}}, where @var{n} +is the signal number, or as signal names from the following list: +@samp{SIGHUP}, @samp{SIGINT}, @samp{SIGQUIT}, @samp{SIGILL}, +@samp{SIGTRAP}, @samp{SIGABRT}, @samp{SIGIOT}, @samp{SIGBUS}, +@samp{SIGFPE}, @samp{SIGKILL}, @samp{SIGUSR1}, @samp{SIGSEGV}, +@samp{SIGUSR2}, @samp{SIGPIPE}, @samp{SIGALRM}, @samp{SIGTERM}, +@samp{SIGSTKFLT}, @samp{SIGCHLD}, @samp{SIGCONT}, @samp{SIGSTOP}, +@samp{SIGTSTP}, @samp{SIGTTIN}, @samp{SIGTTOU}, @samp{SIGURG}, +@samp{SIGXCPU}, @samp{SIGXFSZ}, @samp{SIGVTALRM}, @samp{SIGPROF}, +@samp{SIGWINCH}, @samp{SIGPOLL}, @samp{SIGIO}, @samp{SIGPWR}, +@samp{SIGSYS}. + + If the component exits with an exit code listed in @var{codes} +or is terminated on a signal listed in @var{codes}, +@command{pies} executes actions specified by its substatements. +They are executed in the order of their appearance below: + +@deffn {Pies Conf} exec @var{command} +Execute external command. Prior to execution of @var{command} all +file descriptors are closed. It inherits the environment from the +main @command{pies} process with the following additional variables: + +@table @env +@item PIES_VERSION +The @command{pies} version number (@value{VERSION}). + +@item PIES_COMPONENT +Tag of the terminated component (@pxref{Component Statement, tag}). + +@item PIES_PID +PID of the terminated component. + +@item PIES_SIGNAL +If the component terminated on signal, the number of that signal. + +@item PIES_STATUS +Program exit code. +@end table +@end deffn @deffn {Pies Conf} action @samp{disable | restart} If @samp{restart} is given, restart the component. This is the diff --git a/pies/pies.c b/pies/pies.c index de46cac2..8948bcee 100644 --- a/pies/pies.c +++ b/pies/pies.c @@ -115,23 +115,26 @@ _cb_notify_addr (mu_debug_t debug, void *data, mu_config_value_t *arg) struct mu_cfg_param return_code_cfg_param[] = { { "action", mu_cfg_callback, NULL, - mu_offsetof (struct action, act), _cb_action, + mu_offsetof (struct component, act_temp.act), _cb_action, N_("Specifies action to take when a component finishes with this " "return code."), /* TRANSLATORS: disable and restart are keywords, do not translate them. */ N_("arg: {disable | restart}") }, { "notify", mu_cfg_callback, NULL, - mu_offsetof (struct action, addr), _cb_notify_addr, + mu_offsetof (struct component, act_temp.addr), _cb_notify_addr, N_("Notify this address when then component terminates."), N_("arg: email-list") }, { "message", mu_cfg_string, NULL, - mu_offsetof (struct action, message), NULL, + mu_offsetof (struct component, act_temp.message), NULL, N_("Notification message text (with headers).") }, + { "exec", mu_cfg_string, NULL, + mu_offsetof (struct component, act_temp.command), NULL, + N_("Execute this command.") }, { NULL } }; -static struct mu_kwd ex_kwtab[] = { #define S(s) { #s, s } +static struct mu_kwd ex_kwtab[] = { S (EX_OK), S (EX_USAGE), S (EX_DATAERR), @@ -151,13 +154,73 @@ static struct mu_kwd ex_kwtab[] = { { NULL } }; +static struct mu_kwd sig_kwtab[] = { + S (SIGHUP), + S (SIGINT), + S (SIGQUIT), + S (SIGILL), + S (SIGTRAP), + S (SIGABRT), + S (SIGIOT), + S (SIGBUS), + S (SIGFPE), + S (SIGKILL), + S (SIGUSR1), + S (SIGSEGV), + S (SIGUSR2), + S (SIGPIPE), + S (SIGALRM), + S (SIGTERM), +#ifdef SIGSTKFLT + S (SIGSTKFLT), +#endif + S (SIGCHLD), + S (SIGCONT), + S (SIGSTOP), + S (SIGTSTP), + S (SIGTTIN), + S (SIGTTOU), +#ifdef SIGURG + S (SIGURG), +#endif +#ifdef SIGXCPU + S (SIGXCPU), +#endif +#ifdef SIGXFSZ + S (SIGXFSZ), +#endif +#ifdef SIGVTALRM + S (SIGVTALRM), +#endif +#ifdef SIGPROF + S (SIGPROF), +#endif +#ifdef SIGWINCH + S (SIGWINCH), +#endif +#ifdef SIGPOLL + S (SIGPOLL), +#endif +#ifdef SIGIO + S (SIGIO), +#endif +#ifdef SIGPWR + S (SIGPWR), +#endif +#ifdef SIGSYS + S (SIGSYS), +#endif + { NULL } +}; +#undef S + static struct action * create_action(struct component *comp, mu_debug_t debug, mu_config_value_t *val, int argc, const char *(*getarg) (mu_config_value_t *, int, mu_debug_t)) { int i; - int *retv; + unsigned *retv; int retc = 0; int allflag = 0; struct action *act; @@ -169,8 +232,9 @@ create_action(struct component *comp, { for (i = 0; i < argc; i++) { - int n; + unsigned n; const char *arg = getarg(val, i, debug); + size_t len = strlen (arg); if (isdigit (arg[0])) { @@ -183,6 +247,27 @@ create_action(struct component *comp, continue; } } + else if (len > 3 && memcmp (arg, "SIG", 3) == 0) + { + if (arg[4] == '+') + { + char *p; + n = strtoul (arg + 4, &p, 0); + if (*p) + { + mu_cfg_format_error (debug, MU_DEBUG_ERROR, + _("%s: not a number"), p); + continue; + } + } + else if (mu_kwd_xlat_name_ci (sig_kwtab, arg, &n)) + { + mu_cfg_format_error (debug, MU_DEBUG_ERROR, + _("%s: not a signal code"), arg); + continue; + } + n |= STATUS_SIG_BIT; + } else if (mu_kwd_xlat_name_ci (ex_kwtab, arg, &n)) { mu_cfg_format_error (debug, MU_DEBUG_ERROR, @@ -190,14 +275,8 @@ create_action(struct component *comp, continue; } - if (n > MAX_RETURN_CODE) - mu_cfg_format_error (debug, MU_DEBUG_ERROR, - _("%s: invalid return code"), arg); - else - { - /* Alles in ordnung */ - retv[retc++] = n; - } + /* Alles in ordnung */ + retv[retc++] = n; } } @@ -208,16 +287,16 @@ create_action(struct component *comp, } act = xzalloc (sizeof *act); - if (allflag) + if (!allflag) { - for (i = 0; i <= MAX_RETURN_CODE; i++) - if (comp->act[i] == NULL) - comp->act[i] = act; + act->nstat = retc; + act->status = retv; } + if (comp->act_tail) + comp->act_tail->next = act; else - for (i = 0; i < retc; i++) - comp->act[retv[i]] = act; - free (retv); + comp->act_head = act; + comp->act_tail = act; return act; } @@ -267,6 +346,12 @@ return_code_section_parser (enum mu_cfg_section_stage stage, struct component *comp = *section_data; size_t count; struct action *act; + + if (!node->label) + { + mu_cfg_format_error (tree->debug, MU_DEBUG_ERROR, _("missing tag")); + return 1; + } switch (stage) { @@ -274,30 +359,35 @@ return_code_section_parser (enum mu_cfg_section_stage stage, switch (node->label->type) { case MU_CFG_STRING: - act = create_action(comp, tree->debug, node->label, - 1, - _get_string_arg); + act = create_action (comp, tree->debug, node->label, + 1, + _get_string_arg); break; case MU_CFG_ARRAY: - act = create_action(comp, tree->debug, node->label, - node->label->v.arg.c, - _get_array_arg); + act = create_action (comp, tree->debug, node->label, + node->label->v.arg.c, + _get_array_arg); break; case MU_CFG_LIST: mu_list_count (node->label->v.list, &count); - act = create_action(comp, tree->debug, node->label, - count, - _get_list_arg); + act = create_action (comp, tree->debug, node->label, + count, + _get_list_arg); } if (!act) return 1; - *section_data = act; + memset (&comp->act_temp, 0, sizeof (comp->act_temp)); break; case mu_cfg_section_end: + act = comp->act_tail; + act->act = comp->act_temp.act; + act->addr = comp->act_temp.addr; + act->message = comp->act_temp.message; + act->command = comp->act_temp.command; break; } return 0; @@ -1151,11 +1241,11 @@ pies_check_status (pid_t *ppid) if (pid == -1) return pies_status_ctr; + *ppid = pid; + if (kill (pid, SIGUSR2)) return pies_status_stale; - *ppid = pid; - for (i = 0; i < 4 && (rc = access (statfile, R_OK)); i++) sleep (1); diff --git a/pies/pies.h b/pies/pies.h index 56597495..69683dd9 100644 --- a/pies/pies.h +++ b/pies/pies.h @@ -73,19 +73,24 @@ struct redirector typedef struct limits_rec *limits_record_t; -#define MAX_RETURN_CODE 127 - enum return_action { action_restart, action_disable, }; +#define STATUS_SIG_BIT 0x80000000 +#define STATUS_CODE(c) ((c) & ~STATUS_SIG_BIT) + struct action { + struct action *next; + size_t nstat; + unsigned *status; enum return_action act; /* Action to take when the component terminates */ mu_address_t addr; /* Addresses to notify about it. */ char *message; /* Notification mail. */ + char *command; /* Execute this command */ }; enum pies_comp_mode @@ -136,7 +141,8 @@ struct component int facility; /* Syslog facility. */ struct redirector redir[2]; /* Repeaters for stdout and stderr */ /* Actions to execute on various exit codes: */ - struct action *act[MAX_RETURN_CODE+1]; + struct action *act_head, *act_tail; + struct action act_temp; /* Auxiliary object used during configuration */ }; extern char *syslog_tag; diff --git a/pies/progman.c b/pies/progman.c index 31820f7b..50db9137 100644 --- a/pies/progman.c +++ b/pies/progman.c @@ -243,9 +243,6 @@ register_prog0 (struct component *comp, int index) newp->v.p.status = status_disabled; else if (comp->mode == pies_comp_inetd) newp->v.p.status = status_listener; - for (i = 0; i <= MAX_RETURN_CODE; i++) - if (!comp->act[i]) - comp->act[i] = default_component.act[i]; if (comp->mode != pies_comp_exec) comp->redir[RETR_OUT].type = redir_null; @@ -1461,6 +1458,78 @@ notify (const char *tag, int status, struct action *act) mu_message_destroy (&msg, mu_message_get_owner (msg)); } +static int +status_matches_p (struct action *act, unsigned status) +{ + int i; + + if (act->nstat == 0) + return 1; + for (i = 0; i < act->nstat; i++) + if (act->status[i] == status) + return 1; + return 0; +} + +static void +run_command (struct action *act, struct prog *prog, unsigned retcode, + pid_t child_pid) +{ + pid_t pid; + char *argv[4]; + char buf[INT_BUFSIZE_BOUND (uintmax_t)]; + static RETSIGTYPE (*saved_handler) (int sig); + int status; + + saved_handler = signal (SIGPIPE, SIG_IGN); + + pid = fork (); + + if (pid == (pid_t) -1) + { + mu_error ("fork: %s", mu_strerror (errno)); + return; + } + + if (pid == 0) + { + int i; + + MU_DEBUG1 (pies_debug, MU_DEBUG_TRACE1, + _("executing %s\n"), act->command); + /* Child */ + setenv ("PIES_VERSION", PACKAGE_VERSION, 1); + setenv ("PIES_COMPONENT", prog->tag, 1); + setenv ("PIES_PID", umaxtostr (child_pid, buf), 1); + if (retcode & STATUS_SIG_BIT) + setenv ("PIES_SIGNAL", umaxtostr (STATUS_CODE (retcode), buf), 1); + else + setenv ("PIES_STATUS", umaxtostr (STATUS_CODE (retcode), buf), 1); + + for (i = getmaxfd (); i >= 0; i--) + close (i); + + argv[0] = "/bin/sh"; + argv[1] = "-c"; + argv[2] = act->command; + argv[3] = NULL; + + execv (argv[0], argv); + exit (127); + } + + /* Master */ + while (waitpid (pid, &status, 0) == -1) + if (errno != EINTR) + { + mu_error ("waitpid: %s", mu_strerror (errno)); + break; + } + signal (SIGPIPE, saved_handler); + + print_status (act->command, pid, status, 1); +} + void progman_cleanup (int expect_term) { @@ -1491,13 +1560,31 @@ progman_cleanup (int expect_term) prog_stop_dependents (prog); if (!expect_term) { + unsigned retcode; + struct action *act = prog->v.p.comp->act_head; + + if (!act) + act = default_component.act_head; + if (WIFEXITED (status)) + retcode = WEXITSTATUS (status); + else if (WIFSIGNALED (status)) + retcode = STATUS_SIG_BIT | WTERMSIG (status); + else { - struct action *act; - status = WEXITSTATUS (status); - if (status <= MAX_RETURN_CODE - && (act = prog->v.p.comp->act[status])) + /* Enforce default action: */ + act = NULL; + } + + for (; act; act = act->next) + { + if (status_matches_p (act, retcode)) { + if (act->command) + { + run_command (act, prog, retcode, pid); + } + switch (act->act) { case action_restart: @@ -1513,11 +1600,13 @@ progman_cleanup (int expect_term) } if (act->addr) notify (prog->tag, status, act); - continue; + break; } } - /* Default action: */ - prog_start (prog); + + if (!act) + /* Default action: */ + prog_start (prog); } } } |