aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2008-01-31 18:30:42 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2008-01-31 18:30:42 +0000
commit65aa5e539e3cd72b8b6c6d9b2656cb6d522bea53 (patch)
treec918f6378c2404b67fffadcf59f3c467b8838bb7
parentdcaa7a0c3c2d14e6027066ac24869273fa5c8b37 (diff)
downloadmailfromd-65aa5e539e3cd72b8b6c6d9b2656cb6d522bea53.tar.gz
mailfromd-65aa5e539e3cd72b8b6c6d9b2656cb6d522bea53.tar.bz2
Implement configurable actions depending on the exit code of
a component. * pies/pies.c: New configuration file section "return-code". (capa): Add mailer and debug. (main): Register "return-code" section and all mailer formats. * pies/progman.c (enum prog_status): New type. (struct prog): Remove `disabled'. New fields `status' and `act'. (register_prog): Initialize act. (prog_start): Exit with EX_SOFTWARE code if the component cannot be started. (progman_start): Do not start disabled components. (progman_wake_disabled): Wake only components marked as status_stopped. (notify): New function. (progman_cleanup): Act in accordance with the `act' field if the component terminates. (progman_stop_component,progman_dump_stats): Take into account prog->v.p.status. * pies/pies.h (MAX_RETURN_CODE): New define. (enum return_action): New type. (struct component.act): New member. (default_component): New global. git-svn-id: file:///svnroot/mailfromd/branches/gmach@1600 7a8a7f39-df28-0410-adc6-e0d955640f24
-rw-r--r--ChangeLog26
-rw-r--r--pies/pies.c155
-rw-r--r--pies/pies.h20
-rw-r--r--pies/progman.c167
4 files changed, 356 insertions, 12 deletions
diff --git a/ChangeLog b/ChangeLog
index a93ef6bb..b1eb2200 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,29 @@
+2008-01-31 Sergey Poznyakoff <gray@gnu.org.ua>
+
+ Implement configurable actions depending on the exit code of
+ a component.
+
+ * pies/pies.c: New configuration file section "return-code".
+ (capa): Add mailer and debug.
+ (main): Register "return-code" section and all mailer formats.
+ * pies/progman.c (enum prog_status): New type.
+ (struct prog): Remove `disabled'. New fields `status' and `act'.
+ (register_prog): Initialize act.
+ (prog_start): Exit with EX_SOFTWARE code if the component cannot
+ be started.
+ (progman_start): Do not start disabled components.
+ (progman_wake_disabled): Wake only components marked as
+ status_stopped.
+ (notify): New function.
+ (progman_cleanup): Act in accordance with the `act' field if the
+ component terminates.
+ (progman_stop_component,progman_dump_stats): Take into account
+ prog->v.p.status.
+ * pies/pies.h (MAX_RETURN_CODE): New define.
+ (enum return_action): New type.
+ (struct component.act): New member.
+ (default_component): New global.
+
2008-01-28 Sergey Poznyakoff <gray@gnu.org.ua>
* mfd/bi_db.m4 (dbdel): Fix format arguments.
diff --git a/pies/pies.c b/pies/pies.c
index f922fc18..4b5fa762 100644
--- a/pies/pies.c
+++ b/pies/pies.c
@@ -63,6 +63,154 @@ stderr_closed_p()
static int
+_cb_action (mu_debug_t debug, void *data, char *arg)
+{
+ enum return_action *pact = data;
+ static struct mu_kwd actab[] = {
+ { "disable", action_disable },
+ { "restart", action_restart },
+ { NULL }
+ };
+ int res;
+
+ if (mu_kwd_xlat_name (actab, arg, &res))
+ {
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("unknown action code: %s"), arg);
+ return 0;
+ }
+ *pact = res;
+ return 0;
+}
+
+static int
+_cb_notify_addr (mu_debug_t debug, void *data, char *arg)
+{
+ mu_address_t *paddr = data;
+ mu_address_t addr;
+ int rc;
+
+ rc = mu_address_create (&addr, arg);
+ if (rc)
+ mu_cfg_format_error (debug, MU_DEBUG_ERROR,
+ _("%s: invalid e-mail address: %s"),
+ arg, mu_strerror (rc));
+ if (*paddr)
+ {
+ mu_address_union (paddr, addr);
+ mu_address_destroy (&addr);
+ }
+ else
+ *paddr = addr;
+ return 0;
+}
+
+struct mu_cfg_param return_code_cfg_param[] = {
+ { "action", mu_cfg_callback, NULL,
+ mu_offsetof (struct action, act), _cb_action,
+ N_("Specifies action to take when a component finishes with this "
+ "return code."),
+ N_("arg: {disable | restart}") },
+ { "notify", mu_cfg_callback, NULL,
+ mu_offsetof (struct action, 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,
+ N_("Notification message text (with headers).") },
+ { NULL }
+};
+
+static int
+return_code_section_parser (enum mu_cfg_section_stage stage,
+ const mu_cfg_node_t *node,
+ const char *section_label, void **section_data,
+ void *call_data,
+ mu_cfg_tree_t *tree)
+{
+ struct component *comp = *section_data;
+ struct action *act;
+ int argc, i;
+ char **argv;
+ int *retv;
+ int retc = 0;
+ int allflag = 0;
+ int rc;
+
+ switch (stage)
+ {
+ case mu_cfg_section_start:
+ rc = mu_argcv_get (node->tag_label, NULL, NULL, &argc, &argv);
+ if (rc)
+ {
+ mu_cfg_format_error (tree->debug, MU_DEBUG_ERROR,
+ _("cannot parse return codes: %s"),
+ mu_strerror (rc));
+ return 1;
+ }
+ retv = xcalloc (argc, sizeof *retv);
+ if (argc == 0 || (argc == 1 && strcmp (argv[0], "*") == 0))
+ allflag = 1;
+ else
+ {
+ for (i = 0; i < argc; i++)
+ {
+ char *p;
+ int n = strtoul (argv[i], &p, 0);
+ if (*p)
+ mu_cfg_format_error (tree->debug, MU_DEBUG_ERROR,
+ _("%s: not a number"), p);
+ else if (n > MAX_RETURN_CODE)
+ mu_cfg_format_error (tree->debug, MU_DEBUG_ERROR,
+ _("%s: invalid return code"), p);
+ else
+ {
+ /* Alles in ordnung */
+ retv[retc++] = n;
+ }
+ }
+ }
+
+ mu_argcv_free (argc, argv);
+ if (retc == 0 && !allflag)
+ {
+ free (retv);
+ return 1;
+ }
+
+ act = xzalloc (sizeof *act);
+ if (allflag)
+ {
+ for (i = 0; i <= MAX_RETURN_CODE; i++)
+ if (comp->act[i] == NULL)
+ comp->act[i] = act;
+ }
+ else
+ for (i = 0; i < retc; i++)
+ comp->act[retv[i]] = act;
+ *section_data = act;
+ break;
+
+ case mu_cfg_section_end:
+ break;
+ }
+ return 0;
+}
+
+void
+return_code_cfg_init ()
+{
+ struct mu_cfg_section *section;
+
+ if (mu_create_canned_section ("return-code", &section))
+ exit (EX_SOFTWARE);
+ section->parser = return_code_section_parser;
+ section->label = N_("<tag: exit-code-list>");
+ mu_cfg_section_add_params (section, return_code_cfg_param);
+}
+
+
+static int
_cb_group (mu_debug_t debug, void *data, char *arg)
{
int argc, i;
@@ -224,6 +372,7 @@ struct mu_cfg_param component_cfg_param[] = {
N_("Set program environment. Argument is a list of assignments "
"separated by white space."),
N_("arg: list") },
+ { "return-code", mu_cfg_section },
{ NULL }
};
@@ -264,6 +413,7 @@ component_cfg_init ()
+struct component default_component;
static int
_cb_debug (mu_debug_t debug, void *data, char *arg)
@@ -299,6 +449,7 @@ struct mu_cfg_param pies_cfg_param[] = {
{ "shutdown-timeout", mu_cfg_uint, &shutdown_timeout, 0, NULL,
N_("Wait <n> seconds for all children to shut down."),
"n" },
+ { "return-code", mu_cfg_section, &default_component },
{ NULL }
};
@@ -311,6 +462,8 @@ static char args_doc[] = "";
static const char *capa[] = {
"common",
"logging",
+ "mailer",
+ "debug",
NULL
};
@@ -694,8 +847,10 @@ main (int argc, char **argv)
argp_program_version_hook = version;
/* Set default logging */
log_setup (!stderr_closed_p ());
+ return_code_cfg_init ();
component_cfg_init ();
mu_argp_init (program_version, package_bugreport);
+ mu_register_all_mailer_formats ();
rc = mu_app_init (&argp, capa, pies_cfg_param, argc, argv, 0, &index, NULL);
if (rc)
exit (EX_CONFIG);
diff --git a/pies/pies.h b/pies/pies.h
index 66badb6c..2f27b802 100644
--- a/pies/pies.h
+++ b/pies/pies.h
@@ -54,6 +54,21 @@ struct pies_privs_data
mu_list_t groups;
};
+#define MAX_RETURN_CODE 127
+
+enum return_action
+{
+ action_restart,
+ action_disable,
+};
+
+struct action
+{
+ enum return_action act; /* Action to take when the component terminates */
+ mu_address_t addr; /* Addresses to notify about it. */
+ char *message; /* Notification mail. */
+};
+
struct component
{
char *tag; /* Entry tag (for diagnostics purposes) */
@@ -64,7 +79,8 @@ struct component
char *rmfile;
int retr[2];
struct pies_privs_data privs;
- mode_t umask;
+ mode_t umask;
+ struct action *act[MAX_RETURN_CODE+1];
};
void register_prog (struct component *comp);
@@ -82,4 +98,4 @@ void priv_setup (struct pies_privs_data *pr);
extern char *syslog_tag;
extern unsigned long shutdown_timeout;
-
+extern struct component default_component;
diff --git a/pies/progman.c b/pies/progman.c
index bfc9512e..7d029392 100644
--- a/pies/progman.c
+++ b/pies/progman.c
@@ -15,10 +15,19 @@
with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "pies.h"
+/* FIXME */
+const char *mu_umaxtostr (unsigned slot, uintmax_t val);
#define TYPE_COMPONENT 0
#define TYPE_RETR 1
+enum prog_status
+ {
+ status_enabled,
+ status_stopped,
+ status_disabled
+ };
+
struct prog
{
struct prog *next;
@@ -39,7 +48,8 @@ struct prog
mode_t umask;
time_t timestamp; /* Time of last startup */
size_t count; /* Number of failed starts since timestamp */
- int disabled; /* 1 if this entry is disabled */
+ enum prog_status status; /* Current component status */
+ struct action *act[MAX_RETURN_CODE+1];
} p;
struct
@@ -159,6 +169,10 @@ register_prog (struct component *comp)
newp->v.p.argc = i;
newp->v.p.argv = comp->argv;
newp->v.p.env = comp->env;
+ if (comp->disabled)
+ newp->v.p.status = status_disabled;
+ for (i = 0; i <= MAX_RETURN_CODE; i++)
+ newp->v.p.act[i] = comp->act[i] ? comp->act[i] : default_component.act[i];
pstr = (char*) (newp + 1);
@@ -384,7 +398,7 @@ prog_start (struct prog *prog)
mu_error (_("%s is respawning too fast, disabled for %d minutes"),
prog->tag, SLEEPTIME / 60);
prog->v.p.timestamp = now;
- prog->v.p.disabled = 1;
+ prog->v.p.status = status_disabled;
old_alarm = alarm (0);
if (old_alarm > SLEEPTIME || old_alarm <= 0)
@@ -445,7 +459,7 @@ prog_start (struct prog *prog)
openlog (MU_LOG_TAG(), LOG_PID, mu_log_facility);
syslog (LOG_CRIT, _("cannot start `%s': %s"), prog->tag,
mu_strerror (errno));
- exit (1);
+ exit (EX_SOFTWARE);
case -1:
mu_diag_output (MU_DIAG_CRIT,
@@ -464,8 +478,8 @@ progman_start ()
struct prog *prog;
for (prog = proghead; prog; prog = prog->next)
- if (IS_PROG (prog))
- prog_start (prog);
+ if (IS_PROG (prog) && prog->v.p.status != status_disabled)
+ prog_start (prog);
}
void
@@ -474,9 +488,9 @@ progman_wake_disabled ()
struct prog *prog;
for (prog = proghead; prog; prog = prog->next)
- if (IS_PROG (prog) && prog->v.p.disabled)
+ if (IS_PROG (prog) && prog->v.p.status == status_stopped)
{
- prog->v.p.disabled = 0;
+ prog->v.p.status = status_enabled;
prog->v.p.count = 0;
prog->v.p.timestamp = 0;
prog_start (prog);
@@ -580,6 +594,109 @@ prog_stop_dependent (struct prog *prog)
}
}
+static void
+send_msg (mu_address_t rcpt, mu_message_t msg)
+{
+ mu_mailer_t mailer;
+ int rc;
+
+ rc = mu_mailer_create (&mailer, NULL);
+ if (rc)
+ {
+ const char *mailer_url;
+ mu_mailer_get_url_default (&mailer_url);
+
+ mu_error (_("Cannot create mailer `%s': %s"),
+ mailer_url, mu_strerror (rc));
+ return;
+ }
+
+ /* FIXME: mailer flags? */
+ rc = mu_mailer_open (mailer, 0);
+ if (rc)
+ {
+ const char *mailer_url;
+ mu_mailer_get_url_default (&mailer_url);
+
+ mu_mailer_destroy(&mailer);
+ mu_error (_("Opening mailer `%s' failed: %s"),
+ mailer_url, mu_strerror (rc));
+ return;
+ }
+
+ rc = mu_mailer_send_message (mailer, msg, NULL, rcpt);
+ mu_mailer_destroy (&mailer);
+ if (rc)
+ mu_error (_("Cannot send message: %s"), mu_strerror (rc));
+}
+
+static const char default_termination_message[] =
+"From: <>\n"
+"X-Agent: ${canonical-program-name} (${package} ${version})\n"
+"Subject: Component ${component} terminated with code ${retcode}.\n"
+"\n";
+
+static void
+vartab_define_project (mu_vartab_t vtab)
+{
+ static struct {
+ char *name;
+ char *value;
+ } ptab[] = {
+ { "canonical-program-name", "pies" },
+ { "package", PACKAGE },
+ { "version", PACKAGE_VERSION },
+ { "mu-version", MAILUTILS_VERSION },
+ { NULL }
+ };
+ int i;
+
+ for (i = 0; ptab[i].name; i++)
+ mu_vartab_define (vtab, ptab[i].name, ptab[i].value, 1);
+}
+
+
+static void
+notify (const char *tag, int status, struct action *act)
+{
+ mu_vartab_t vtab;
+ char *msg_text = NULL;
+ mu_message_t msg = NULL;
+ mu_stream_t stream = NULL;
+ int rc;
+
+ mu_vartab_create (&vtab);
+ mu_vartab_define (vtab, "component", tag, 1);
+ mu_vartab_define (vtab, "retcode", mu_umaxtostr (0, status), 1);
+ mu_vartab_define (vtab, "program-name", mu_program_name, 1);
+ vartab_define_project (vtab);
+ rc = mu_vartab_expand (vtab,
+ act->message ?
+ act->message : default_termination_message,
+ &msg_text);
+ mu_vartab_destroy (&vtab);
+ if (rc)
+ {
+ mu_error (_("cannot expand message body: %s"), mu_strerror (rc));
+ /* FIXME: Notify anyway? */
+ return;
+ }
+
+ rc = mu_message_create (&msg, NULL);
+ if (rc)
+ {
+ mu_error (_("cannot create message: %s"), mu_strerror (rc));
+ free (msg_text);
+ }
+
+ mu_message_get_stream (msg, &stream);
+ mu_stream_write (stream, msg_text, strlen (msg_text), 0, NULL);
+ free (msg_text);
+
+ send_msg (act->addr, msg);
+ mu_message_destroy (&msg, mu_message_get_owner (msg));
+}
+
void
progman_cleanup (int expect_term)
{
@@ -601,7 +718,35 @@ progman_cleanup (int expect_term)
{
prog_stop_dependent (prog);
if (!expect_term)
- prog_start (prog);
+ {
+ if (WIFEXITED (status))
+ {
+ struct action *act;
+ status = WEXITSTATUS (status);
+ if (status <= MAX_RETURN_CODE
+ && prog->v.p.act && (act = prog->v.p.act[status]))
+ {
+ switch (act->act)
+ {
+ case action_restart:
+ prog_start (prog);
+ break;
+
+ case action_disable:
+ mu_diag_output (MU_DIAG_NOTICE,
+ _("Disabling component %s: exited "
+ "with code %d"),
+ prog->tag, status);
+ prog->v.p.status = status_disabled;
+ }
+ if (act->addr)
+ notify (prog->tag, status, act);
+ continue;
+ }
+ }
+ /* Default action: */
+ prog_start (prog);
+ }
}
}
}
@@ -615,7 +760,7 @@ progman_stop_component (const char *name)
for (prog = proghead; prog; prog = prog->next)
if (IS_PROG (prog) && strcmp (prog->tag, name) == 0)
{
- if (prog->v.p.disabled)
+ if (prog->v.p.status != status_enabled)
mu_diag_output (MU_DIAG_INFO,
_("stopping component `%s': component not started"),
name);
@@ -650,7 +795,7 @@ progman_dump_stats (const char *filename)
prog->tag);
if (prog->pid)
fprintf (fp, "%lu", (unsigned long) prog->pid);
- else if (prog->v.p.disabled)
+ else if (prog->v.p.status == status_stopped)
{
char buf[48];
time_t t = prog->v.p.timestamp + SLEEPTIME;
@@ -658,6 +803,8 @@ progman_dump_stats (const char *filename)
localtime (&t));
fprintf (fp, _("[disabled; scheduled for %s]"), buf);
}
+ else if (prog->v.p.status == status_disabled)
+ fprintf (fp, _("[disabled]"));
else
fprintf (fp, "[not running]");
if (rc = mu_argcv_string (prog->v.p.argc, prog->v.p.argv, &s))

Return to:

Send suggestions and report system problems to the System administrator.