diff --git a/crmd/notify.c b/crmd/notify.c index 649befcb47..40b34b4410 100644 --- a/crmd/notify.c +++ b/crmd/notify.c @@ -1,652 +1,381 @@ /* * Copyright (C) 2015 Andrew Beekhof * * 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 software 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "notify.h" #include "crmd_messages.h" +#include +#include static char *notify_script = NULL; static char *notify_target = NULL; -static GListPtr notify_list = NULL; static int alerts_inflight = 0; static gboolean draining_alerts = FALSE; -static guint max_alert_timeout = CRMD_NOTIFY_DEFAULT_TIMEOUT_MS; - -typedef struct { - char *name; - char *value; -} envvar_t; - -typedef struct { - char *id; - char *path; - int timeout; - char *tstamp_format; - char *recipient; - GListPtr envvars; -} notify_entry_t; - -enum notify_keys_e{ - CRM_notify_recipient = 0, - CRM_notify_node, - CRM_notify_nodeid, - CRM_notify_rsc, - CRM_notify_task, - CRM_notify_interval, - CRM_notify_desc, - CRM_notify_status, - CRM_notify_target_rc, - CRM_notify_rc, - CRM_notify_kind, - CRM_notify_version, - CRM_notify_node_sequence, - CRM_notify_timestamp -}; - -/* - * to allow script compatibility we can have more than one - * set of environment variables - */ - -static const char *notify_keys[][3] = -{ - [CRM_notify_recipient] = {"CRM_notify_recipient", "CRM_alert_recipient", NULL}, - [CRM_notify_node] = {"CRM_notify_node", "CRM_alert_node", NULL}, - [CRM_notify_nodeid] = {"CRM_notify_nodeid", "CRM_alert_nodeid", NULL}, - [CRM_notify_rsc] = {"CRM_notify_rsc", "CRM_alert_rsc", NULL}, - [CRM_notify_task] = {"CRM_notify_task", "CRM_alert_task", NULL}, - [CRM_notify_interval] = {"CRM_notify_interval", "CRM_alert_interval", NULL}, - [CRM_notify_desc] = {"CRM_notify_desc", "CRM_alert_desc", NULL}, - [CRM_notify_status] = {"CRM_notify_status", "CRM_alert_status", NULL}, - [CRM_notify_target_rc] = {"CRM_notify_target_rc", "CRM_alert_target_rc", NULL}, - [CRM_notify_rc] = {"CRM_notify_rc", "CRM_alert_rc", NULL}, - [CRM_notify_kind] = {"CRM_notify_kind", "CRM_alert_kind", NULL}, - [CRM_notify_version] = {"CRM_notify_version", "CRM_alert_version", NULL}, - [CRM_notify_node_sequence] = {"CRM_notify_node_sequence", "CRM_alert_node_sequence", NULL}, - [CRM_notify_timestamp] = {"CRM_notify_timestamp", "CRM_alert_timestamp", NULL} -}; - -#include - -/* - * end of possibly generic time-handling stuff - */ - /* * syncronize local data with cib */ -static void -free_envvar_entry(envvar_t *entry) -{ - free(entry->name); - free(entry->value); - free(entry); -} - -static void -free_notify_list_entry(notify_entry_t *entry) -{ - free(entry->id); - free(entry->path); - free(entry->tstamp_format); - free(entry->recipient); - if (entry->envvars) { - g_list_free_full(entry->envvars, - (GDestroyNotify) free_envvar_entry); - } - free(entry); -} - -static void -free_notify_list() -{ - if (notify_list) { - g_list_free_full(notify_list, (GDestroyNotify) free_notify_list_entry); - notify_list = NULL; - } -} - -static gpointer -copy_envvar_entry(envvar_t * src, - gpointer data) -{ - envvar_t *dst = calloc(1, sizeof(envvar_t)); - - CRM_ASSERT(dst); - dst->name = strdup(src->name); - dst->value = src->value?strdup(src->value):NULL; - return (gpointer) dst; -} - -static GListPtr -add_dup_envvar(GListPtr envvar_list, - envvar_t *entry) -{ - return g_list_prepend(envvar_list, copy_envvar_entry(entry, NULL)); -} - -static GListPtr -drop_envvars(GListPtr envvar_list, int count) -{ - int i; - - for (i = 0; - (envvar_list) && ((count < 0) || (i < count)); - i++) { - free_envvar_entry((envvar_t *) g_list_first(envvar_list)->data); - envvar_list = g_list_delete_link(envvar_list, - g_list_first(envvar_list)); - } - return envvar_list; -} - -static GListPtr -copy_envvar_list_remove_dupes(GListPtr src) -{ - GListPtr dst = NULL, ls, ld; - - /* we are adding to the front so variable dupes coming via - * recipient-section have got precedence over those in the - * global section - we don't expect that many variables here - * that it pays off to go for a hash-table to make dupe elimination - * more efficient - maybe later when we might decide to do more - * with the variables than cycling through them - */ - - for (ls = g_list_first(src); ls; ls = g_list_next(ls)) { - for (ld = g_list_first(dst); ld; ld = g_list_next(ld)) { - if (!strcmp(((envvar_t *)(ls->data))->name, - ((envvar_t *)(ld->data))->name)) { - break; - } - } - if (!ld) { - dst = g_list_prepend(dst, - copy_envvar_entry((envvar_t *)(ls->data), NULL)); - } - } - - return dst; -} - -static void -add_dup_notify_list_entry(notify_entry_t *entry) -{ - notify_entry_t *new_entry = - (notify_entry_t *) calloc(1, sizeof(notify_entry_t)); - - CRM_ASSERT(new_entry); - *new_entry = (notify_entry_t) { - .id = strdup(entry->id), - .path = strdup(entry->path), - .timeout = entry->timeout, - .tstamp_format = entry->tstamp_format?strdup(entry->tstamp_format):NULL, - .recipient = entry->recipient?strdup(entry->recipient):NULL, - .envvars = entry->envvars? - copy_envvar_list_remove_dupes(entry->envvars) - :NULL - }; - notify_list = g_list_prepend(notify_list, new_entry); -} - -static GListPtr -get_envvars_from_cib(xmlNode *basenode, GListPtr list, int *count) -{ - xmlNode *envvar; - xmlNode *pair; - - if ((!basenode) || - (!(envvar = first_named_child(basenode, XML_TAG_ATTR_SETS)))) { - return list; - } - - for (pair = first_named_child(envvar, XML_CIB_TAG_NVPAIR); - pair; pair = __xml_next(pair)) { - - envvar_t envvar_entry = (envvar_t) { - .name = (char *) crm_element_value(pair, XML_NVPAIR_ATTR_NAME), - .value = (char *) crm_element_value(pair, XML_NVPAIR_ATTR_VALUE) - }; - crm_trace("Found environment variable %s = '%s'", envvar_entry.name, - envvar_entry.value?envvar_entry.value:""); - (*count)++; - list = add_dup_envvar(list, &envvar_entry); - } - - return list; -} - static GHashTable * -get_meta_attrs_from_cib(xmlNode *basenode, notify_entry_t *entry, +get_meta_attrs_from_cib(xmlNode *basenode, crm_alert_entry_t *entry, guint *max_timeout) { GHashTable *config_hash = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); crm_time_t *now = crm_time_new(NULL); const char *value = NULL; unpack_instance_attributes(basenode, basenode, XML_TAG_META_SETS, NULL, config_hash, NULL, FALSE, now); value = g_hash_table_lookup(config_hash, XML_ALERT_ATTR_TIMEOUT); if (value) { entry->timeout = crm_get_msec(value); if (entry->timeout <= 0) { if (entry->timeout == 0) { crm_trace("Setting timeout to default %dmsec", - CRMD_NOTIFY_DEFAULT_TIMEOUT_MS); + CRM_ALERT_DEFAULT_TIMEOUT_MS); } else { crm_warn("Invalid timeout value setting to default %dmsec", - CRMD_NOTIFY_DEFAULT_TIMEOUT_MS); + CRM_ALERT_DEFAULT_TIMEOUT_MS); } - entry->timeout = CRMD_NOTIFY_DEFAULT_TIMEOUT_MS; + entry->timeout = CRM_ALERT_DEFAULT_TIMEOUT_MS; } else { crm_trace("Found timeout %dmsec", entry->timeout); } if (entry->timeout > *max_timeout) { *max_timeout = entry->timeout; } } value = g_hash_table_lookup(config_hash, XML_ALERT_ATTR_TSTAMP_FORMAT); if (value) { /* hard to do any checks here as merely anything can * can be a valid time-format-string */ entry->tstamp_format = (char *) value; crm_trace("Found timestamp format string '%s'", value); } crm_time_free(now); return config_hash; /* keep hash as long as strings are needed */ } void parse_notifications(xmlNode *notifications) { xmlNode *notify; - notify_entry_t entry; + crm_alert_entry_t entry; guint max_timeout = 0; - free_notify_list(); - max_alert_timeout = CRMD_NOTIFY_DEFAULT_TIMEOUT_MS; + crm_free_notify_list(); + crm_alert_max_alert_timeout = CRM_ALERT_DEFAULT_TIMEOUT_MS; if (notifications) { crm_info("We have an alerts section in the cib"); if (notify_script) { crm_warn("Cib contains configuration for Legacy Notifications " "which is overruled by alerts section"); } } else { crm_info("No optional alerts section in cib"); if (notify_script) { - entry = (notify_entry_t) { + entry = (crm_alert_entry_t) { .id = (char *) "legacy_notification", .path = notify_script, - .timeout = CRMD_NOTIFY_DEFAULT_TIMEOUT_MS, + .timeout = CRM_ALERT_DEFAULT_TIMEOUT_MS, .recipient = notify_target }; - add_dup_notify_list_entry(&entry); + crm_add_dup_notify_list_entry(&entry); crm_info("Legacy Notifications enabled"); } return; } for (notify = first_named_child(notifications, XML_CIB_TAG_ALERT); notify; notify = __xml_next(notify)) { xmlNode *recipient; int recipients = 0, envvars = 0; GHashTable *config_hash = NULL; - entry = (notify_entry_t) { + entry = (crm_alert_entry_t) { .id = (char *) crm_element_value(notify, XML_ATTR_ID), .path = (char *) crm_element_value(notify, XML_ALERT_ATTR_PATH), - .timeout = CRMD_NOTIFY_DEFAULT_TIMEOUT_MS, - .tstamp_format = (char *) CRMD_NOTIFY_DEFAULT_TSTAMP_FORMAT + .timeout = CRM_ALERT_DEFAULT_TIMEOUT_MS, + .tstamp_format = (char *) CRM_ALERT_DEFAULT_TSTAMP_FORMAT }; entry.envvars = - get_envvars_from_cib(notify, + crm_get_envvars_from_cib(notify, entry.envvars, &envvars); config_hash = get_meta_attrs_from_cib(notify, &entry, &max_timeout); crm_debug("Found alert: id=%s, path=%s, timeout=%d, " "tstamp_format=%s, %d additional environment variables", entry.id, entry.path, entry.timeout, entry.tstamp_format, envvars); for (recipient = first_named_child(notify, XML_CIB_TAG_ALERT_RECIPIENT); recipient; recipient = __xml_next(recipient)) { int envvars_added = 0; entry.recipient = (char *) crm_element_value(recipient, XML_ALERT_ATTR_REC_VALUE); recipients++; entry.envvars = - get_envvars_from_cib(recipient, + crm_get_envvars_from_cib(recipient, entry.envvars, &envvars_added); { - notify_entry_t recipient_entry = entry; + crm_alert_entry_t recipient_entry = entry; GHashTable *config_hash = get_meta_attrs_from_cib(recipient, &recipient_entry, &max_timeout); - add_dup_notify_list_entry(&recipient_entry); + crm_add_dup_notify_list_entry(&recipient_entry); crm_debug("Alert has recipient: id=%s, value=%s, " "%d additional environment variables", crm_element_value(recipient, XML_ATTR_ID), recipient_entry.recipient, envvars_added); g_hash_table_destroy(config_hash); } entry.envvars = - drop_envvars(entry.envvars, envvars_added); + crm_drop_envvars(entry.envvars, envvars_added); } if (recipients == 0) { - add_dup_notify_list_entry(&entry); + crm_add_dup_notify_list_entry(&entry); } - drop_envvars(entry.envvars, -1); + crm_drop_envvars(entry.envvars, -1); g_hash_table_destroy(config_hash); } if (max_timeout > 0) { - max_alert_timeout = max_timeout; + crm_alert_max_alert_timeout = max_timeout; } } /* * end of synchronization of local data with cib */ void crmd_enable_notifications(const char *script, const char *target) { free(notify_script); notify_script = ((script) && (strcmp(script,"/dev/null")))?strdup(script):NULL; free(notify_target); notify_target = (target != NULL)?strdup(target):NULL; } -static void -set_alert_key(enum notify_keys_e name, const char *value) -{ - const char **key; - - for (key = notify_keys[name]; *key; key++) { - crm_trace("Setting alert key %s = '%s'", *key, value); - if (value) { - setenv(*key, value, 1); - } else { - unsetenv(*key); - } - } -} - -static void -set_alert_key_int(enum notify_keys_e name, int value) -{ - char *s = crm_itoa(value); - - set_alert_key(name, s); - free(s); -} - -static void -unset_alert_keys() -{ - const char **key; - enum notify_keys_e name; - - for(name = 0; name < DIMOF(notify_keys); name++) { - for(key = notify_keys[name]; *key; key++) { - crm_trace("Unsetting alert key %s", *key); - unsetenv(*key); - } - } -} - -static void -set_envvar_list(GListPtr envvars) -{ - GListPtr l; - - for (l = g_list_first(envvars); l; l = g_list_next(l)) { - envvar_t *entry = (envvar_t *)(l->data); - - crm_trace("Setting environment variable %s = '%s'", entry->name, - entry->value?entry->value:""); - if (entry->value) { - setenv(entry->name, entry->value, 1); - } else { - unsetenv(entry->name); - } - } -} - -static void -unset_envvar_list(GListPtr envvars) -{ - GListPtr l; - - for (l = g_list_first(envvars); l; l = g_list_next(l)) { - envvar_t *entry = (envvar_t *)(l->data); - - crm_trace("Unsetting environment variable %s", entry->name); - unsetenv(entry->name); - } -} - static void crmd_notify_complete(svc_action_t *op) { alerts_inflight--; if(op->rc == 0) { crm_info("Alert %d (%s) complete", op->sequence, op->agent); } else { crm_warn("Alert %d (%s) failed: %d", op->sequence, op->agent, op->rc); } } static void send_notifications(const char *kind) { svc_action_t *notify = NULL; static int operations = 0; GListPtr l; crm_time_hr_t *now = crm_time_hr_new(NULL); - set_alert_key(CRM_notify_kind, kind); - set_alert_key(CRM_notify_version, VERSION); + crm_set_alert_key(CRM_alert_kind, kind); + crm_set_alert_key(CRM_alert_version, VERSION); - for (l = g_list_first(notify_list); l; l = g_list_next(l)) { - notify_entry_t *entry = (notify_entry_t *)(l->data); + for (l = g_list_first(crm_alert_list); l; l = g_list_next(l)) { + crm_alert_entry_t *entry = (crm_alert_entry_t *)(l->data); char *timestamp = crm_time_format_hr(entry->tstamp_format, now); operations++; if (!draining_alerts) { crm_debug("Sending '%s' alert to '%s' via '%s'", kind, entry->recipient, entry->path); - set_alert_key(CRM_notify_recipient, entry->recipient); - set_alert_key_int(CRM_notify_node_sequence, operations); - set_alert_key(CRM_notify_timestamp, timestamp); + crm_set_alert_key(CRM_alert_recipient, entry->recipient); + crm_set_alert_key_int(CRM_alert_node_sequence, operations); + crm_set_alert_key(CRM_alert_timestamp, timestamp); notify = services_action_create_generic(entry->path, NULL); notify->timeout = entry->timeout; notify->standard = strdup("event"); notify->id = strdup(entry->id); notify->agent = strdup(entry->path); notify->sequence = operations; - set_envvar_list(entry->envvars); + crm_set_envvar_list(entry->envvars); alerts_inflight++; if(services_action_async(notify, &crmd_notify_complete) == FALSE) { services_action_free(notify); alerts_inflight--; } - unset_envvar_list(entry->envvars); + crm_unset_envvar_list(entry->envvars); } else { crm_warn("Ignoring '%s' alert to '%s' via '%s' received " "while shutting down", kind, entry->recipient, entry->path); } free(timestamp); } - unset_alert_keys(); + crm_unset_alert_keys(); if (now) { free(now); } } void crmd_notify_node_event(crm_node_t *node) { - if(!notify_list) { + if(!crm_alert_list) { return; } - set_alert_key(CRM_notify_node, node->uname); - set_alert_key_int(CRM_notify_nodeid, node->id); - set_alert_key(CRM_notify_desc, node->state); + crm_set_alert_key(CRM_alert_node, node->uname); + crm_set_alert_key_int(CRM_alert_nodeid, node->id); + crm_set_alert_key(CRM_alert_desc, node->state); send_notifications("node"); } void crmd_notify_fencing_op(stonith_event_t * e) { char *desc = NULL; - if (!notify_list) { + if (!crm_alert_list) { return; } desc = crm_strdup_printf( "Operation %s of %s by %s for %s@%s: %s (ref=%s)", e->action, e->target, e->executioner ? e->executioner : "", e->client_origin, e->origin, pcmk_strerror(e->result), e->id); - set_alert_key(CRM_notify_node, e->target); - set_alert_key(CRM_notify_task, e->operation); - set_alert_key(CRM_notify_desc, desc); - set_alert_key_int(CRM_notify_rc, e->result); + crm_set_alert_key(CRM_alert_node, e->target); + crm_set_alert_key(CRM_alert_task, e->operation); + crm_set_alert_key(CRM_alert_desc, desc); + crm_set_alert_key_int(CRM_alert_rc, e->result); send_notifications("fencing"); free(desc); } void crmd_notify_resource_op(const char *node, lrmd_event_data_t * op) { int target_rc = 0; - if(!notify_list) { + if(!crm_alert_list) { return; } target_rc = rsc_op_expected_rc(op); if(op->interval == 0 && target_rc == op->rc && safe_str_eq(op->op_type, RSC_STATUS)) { /* Leave it up to the script if they want to notify for * 'failed' probes, only swallow ones for which the result was * unexpected. * * Even if we find a resource running, it was probably because * someone erased the status section. */ return; } - set_alert_key(CRM_notify_node, node); + crm_set_alert_key(CRM_alert_node, node); - set_alert_key(CRM_notify_rsc, op->rsc_id); - set_alert_key(CRM_notify_task, op->op_type); - set_alert_key_int(CRM_notify_interval, op->interval); + crm_set_alert_key(CRM_alert_rsc, op->rsc_id); + crm_set_alert_key(CRM_alert_task, op->op_type); + crm_set_alert_key_int(CRM_alert_interval, op->interval); - set_alert_key_int(CRM_notify_target_rc, target_rc); - set_alert_key_int(CRM_notify_status, op->op_status); - set_alert_key_int(CRM_notify_rc, op->rc); + crm_set_alert_key_int(CRM_alert_target_rc, target_rc); + crm_set_alert_key_int(CRM_alert_status, op->op_status); + crm_set_alert_key_int(CRM_alert_rc, op->rc); if(op->op_status == PCMK_LRM_OP_DONE) { - set_alert_key(CRM_notify_desc, services_ocf_exitcode_str(op->rc)); + crm_set_alert_key(CRM_alert_desc, services_ocf_exitcode_str(op->rc)); } else { - set_alert_key(CRM_notify_desc, services_lrm_status_str(op->op_status)); + crm_set_alert_key(CRM_alert_desc, services_lrm_status_str(op->op_status)); } send_notifications("resource"); } static gboolean alert_drain_timeout_callback(gpointer user_data) { gboolean *timeout_popped = (gboolean *) user_data; *timeout_popped = TRUE; return FALSE; } void crmd_drain_alerts(GMainContext *ctx) { guint timer; gboolean timeout_popped = FALSE; draining_alerts = TRUE; - timer = g_timeout_add(max_alert_timeout + 5000, + timer = g_timeout_add(crm_alert_max_alert_timeout + 5000, alert_drain_timeout_callback, (gpointer) &timeout_popped); while(alerts_inflight && !timeout_popped) { crm_trace("Draining mainloop while still %d alerts are in flight (timeout=%dms)", - alerts_inflight, max_alert_timeout + 5000); + alerts_inflight, crm_alert_max_alert_timeout + 5000); g_main_context_iteration(ctx, TRUE); } if (!timeout_popped && (timer > 0)) { g_source_remove(timer); } } diff --git a/crmd/notify.h b/crmd/notify.h index 141b4803e6..63beed7629 100644 --- a/crmd/notify.h +++ b/crmd/notify.h @@ -1,38 +1,32 @@ /* * Copyright (C) 2015 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This software 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 Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CRMD_NOTIFY__H # define CRMD_NOTIFY__H # include # include # include -/* Default-Timeout to use before killing a notification script (in milliseconds) */ -# define CRMD_NOTIFY_DEFAULT_TIMEOUT_MS (30000) - -/* Default-Format-String used to pass timestamps to the notification scripts */ -# define CRMD_NOTIFY_DEFAULT_TSTAMP_FORMAT "%H:%M:%S.%06N" - void crmd_enable_notifications(const char *script, const char *target); void crmd_notify_node_event(crm_node_t *node); void crmd_notify_fencing_op(stonith_event_t *e); void crmd_notify_resource_op(const char *node, lrmd_event_data_t *op); void crmd_drain_alerts(GMainContext *ctx); void parse_notifications(xmlNode *notifications); #endif diff --git a/include/crm/common/alerts_internal.h b/include/crm/common/alerts_internal.h new file mode 100644 index 0000000000..eaaef2ad50 --- /dev/null +++ b/include/crm/common/alerts_internal.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2015 Andrew Beekhof + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This software 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 Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ALERT_INTERNAL_H +#define ALERT_INTERNAL_H +/* Default-Timeout to use before killing a notification script (in milliseconds) */ +# define CRM_ALERT_DEFAULT_TIMEOUT_MS (30000) + +/* Default-Format-String used to pass timestamps to the notification scripts */ +# define CRM_ALERT_DEFAULT_TSTAMP_FORMAT "%H:%M:%S.%06N" + +typedef struct { + char *id; + char *path; + int timeout; + char *tstamp_format; + char *recipient; + GListPtr envvars; +} crm_alert_entry_t; + +enum crm_alert_keys_e { + CRM_alert_recipient = 0, + CRM_alert_node, + CRM_alert_nodeid, + CRM_alert_rsc, + CRM_alert_task, + CRM_alert_interval, + CRM_alert_desc, + CRM_alert_status, + CRM_alert_target_rc, + CRM_alert_rc, + CRM_alert_kind, + CRM_alert_version, + CRM_alert_node_sequence, + CRM_alert_timestamp +}; + +extern GListPtr crm_alert_list; +extern guint crm_alert_max_alert_timeout; +extern const char *crm_alert_keys[14][3]; + +void crm_free_notify_list(void); +GListPtr crm_drop_envvars(GListPtr envvar_list, int count); +void crm_add_dup_notify_list_entry(crm_alert_entry_t *entry); +GListPtr crm_get_envvars_from_cib(xmlNode *basenode, GListPtr list, int *count); +void crm_set_alert_key(enum crm_alert_keys_e name, const char *value); +void crm_set_alert_key_int(enum crm_alert_keys_e name, int value); +void crm_unset_alert_keys(void); +void crm_set_envvar_list(GListPtr envvars); +void crm_unset_envvar_list(GListPtr envvars); +#endif diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am index 7a68d1f924..b29a278c8b 100644 --- a/lib/common/Makefile.am +++ b/lib/common/Makefile.am @@ -1,49 +1,49 @@ # # Copyright (C) 2004 Andrew Beekhof # # 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # include $(top_srcdir)/Makefile.common AM_CPPFLAGS += -I$(top_builddir)/lib/gnu -I$(top_srcdir)/lib/gnu ## libraries lib_LTLIBRARIES = libcrmcommon.la # Can't use -Wcast-qual here because glib insists on pretending things are const # when they're not and thus we need the crm_element_value_const() hack # s390 needs -fPIC # s390-suse-linux/bin/ld: .libs/ipc.o: relocation R_390_PC32DBL against `__stack_chk_fail@@GLIBC_2.4' can not be used when making a shared object; recompile with -fPIC CFLAGS = $(CFLAGS_COPY:-Wcast-qual=) -fPIC libcrmcommon_la_LDFLAGS = -version-info 9:1:6 libcrmcommon_la_CFLAGS = $(CFLAGS_HARDENED_LIB) libcrmcommon_la_LDFLAGS += $(LDFLAGS_HARDENED_LIB) libcrmcommon_la_LIBADD = @LIBADD_DL@ $(GNUTLSLIBS) -lm libcrmcommon_la_SOURCES = compat.c digest.c ipc.c io.c procfs.c utils.c xml.c \ iso8601.c remote.c mainloop.c logging.c watchdog.c \ - schemas.c strings.c xpath.c attrd_client.c + schemas.c strings.c xpath.c attrd_client.c alerts.c if BUILD_CIBSECRETS libcrmcommon_la_SOURCES += cib_secrets.c endif libcrmcommon_la_SOURCES += $(top_builddir)/lib/gnu/md5.c clean-generic: rm -f *.log *.debug *.xml *~ diff --git a/lib/common/alerts.c b/lib/common/alerts.c new file mode 100644 index 0000000000..3cf4bf7a0b --- /dev/null +++ b/lib/common/alerts.c @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2015 Andrew Beekhof + * + * 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 software 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include + +typedef struct { + char *name; + char *value; +} envvar_t; + +GListPtr crm_alert_list = NULL; +guint crm_alert_max_alert_timeout = CRM_ALERT_DEFAULT_TIMEOUT_MS; + +/* + * to allow script compatibility we can have more than one + * set of environment variables + */ +const char *crm_alert_keys[14][3] = +{ + [CRM_alert_recipient] = {"CRM_notify_recipient", "CRM_alert_recipient", NULL}, + [CRM_alert_node] = {"CRM_notify_node", "CRM_alert_node", NULL}, + [CRM_alert_nodeid] = {"CRM_notify_nodeid", "CRM_alert_nodeid", NULL}, + [CRM_alert_rsc] = {"CRM_notify_rsc", "CRM_alert_rsc", NULL}, + [CRM_alert_task] = {"CRM_notify_task", "CRM_alert_task", NULL}, + [CRM_alert_interval] = {"CRM_notify_interval", "CRM_alert_interval", NULL}, + [CRM_alert_desc] = {"CRM_notify_desc", "CRM_alert_desc", NULL}, + [CRM_alert_status] = {"CRM_notify_status", "CRM_alert_status", NULL}, + [CRM_alert_target_rc] = {"CRM_notify_target_rc", "CRM_alert_target_rc", NULL}, + [CRM_alert_rc] = {"CRM_notify_rc", "CRM_alert_rc", NULL}, + [CRM_alert_kind] = {"CRM_notify_kind", "CRM_alert_kind", NULL}, + [CRM_alert_version] = {"CRM_notify_version", "CRM_alert_version", NULL}, + [CRM_alert_node_sequence] = {"CRM_notify_node_sequence", "CRM_alert_node_sequence", NULL}, + [CRM_alert_timestamp] = {"CRM_notify_timestamp", "CRM_alert_timestamp", NULL} +}; + +static void +free_envvar_entry(envvar_t *entry) +{ + free(entry->name); + free(entry->value); + free(entry); +} + +static void +crm_free_notify_list_entry(crm_alert_entry_t *entry) +{ + free(entry->id); + free(entry->path); + free(entry->tstamp_format); + free(entry->recipient); + if (entry->envvars) { + g_list_free_full(entry->envvars, + (GDestroyNotify) free_envvar_entry); + } + free(entry); +} + +void +crm_free_notify_list() +{ + if (crm_alert_list) { + g_list_free_full(crm_alert_list, (GDestroyNotify) crm_free_notify_list_entry); + crm_alert_list = NULL; + } +} + +static gpointer +copy_envvar_entry(envvar_t * src, + gpointer data) +{ + envvar_t *dst = calloc(1, sizeof(envvar_t)); + + CRM_ASSERT(dst); + dst->name = strdup(src->name); + dst->value = src->value?strdup(src->value):NULL; + return (gpointer) dst; +} + +static GListPtr +add_dup_envvar(GListPtr envvar_list, + envvar_t *entry) +{ + return g_list_prepend(envvar_list, copy_envvar_entry(entry, NULL)); +} + +GListPtr +crm_drop_envvars(GListPtr envvar_list, int count) +{ + int i; + + for (i = 0; + (envvar_list) && ((count < 0) || (i < count)); + i++) { + free_envvar_entry((envvar_t *) g_list_first(envvar_list)->data); + envvar_list = g_list_delete_link(envvar_list, + g_list_first(envvar_list)); + } + return envvar_list; +} + +static GListPtr +copy_envvar_list_remove_dupes(GListPtr src) +{ + GListPtr dst = NULL, ls, ld; + + /* we are adding to the front so variable dupes coming via + * recipient-section have got precedence over those in the + * global section - we don't expect that many variables here + * that it pays off to go for a hash-table to make dupe elimination + * more efficient - maybe later when we might decide to do more + * with the variables than cycling through them + */ + + for (ls = g_list_first(src); ls; ls = g_list_next(ls)) { + for (ld = g_list_first(dst); ld; ld = g_list_next(ld)) { + if (!strcmp(((envvar_t *)(ls->data))->name, + ((envvar_t *)(ld->data))->name)) { + break; + } + } + if (!ld) { + dst = g_list_prepend(dst, + copy_envvar_entry((envvar_t *)(ls->data), NULL)); + } + } + + return dst; +} + +void +crm_add_dup_notify_list_entry(crm_alert_entry_t *entry) +{ + crm_alert_entry_t *new_entry = + (crm_alert_entry_t *) calloc(1, sizeof(crm_alert_entry_t)); + + CRM_ASSERT(new_entry); + *new_entry = (crm_alert_entry_t) { + .id = strdup(entry->id), + .path = strdup(entry->path), + .timeout = entry->timeout, + .tstamp_format = entry->tstamp_format?strdup(entry->tstamp_format):NULL, + .recipient = entry->recipient?strdup(entry->recipient):NULL, + .envvars = entry->envvars? + copy_envvar_list_remove_dupes(entry->envvars) + :NULL + }; + crm_alert_list = g_list_prepend(crm_alert_list, new_entry); +} + +GListPtr +crm_get_envvars_from_cib(xmlNode *basenode, GListPtr list, int *count) +{ + xmlNode *envvar; + xmlNode *pair; + + if ((!basenode) || + (!(envvar = first_named_child(basenode, XML_TAG_ATTR_SETS)))) { + return list; + } + + for (pair = first_named_child(envvar, XML_CIB_TAG_NVPAIR); + pair; pair = __xml_next(pair)) { + + envvar_t envvar_entry = (envvar_t) { + .name = (char *) crm_element_value(pair, XML_NVPAIR_ATTR_NAME), + .value = (char *) crm_element_value(pair, XML_NVPAIR_ATTR_VALUE) + }; + crm_trace("Found environment variable %s = '%s'", envvar_entry.name, + envvar_entry.value?envvar_entry.value:""); + (*count)++; + list = add_dup_envvar(list, &envvar_entry); + } + + return list; +} + +void +crm_set_alert_key(enum crm_alert_keys_e name, const char *value) +{ + const char **key; + + for (key = crm_alert_keys[name]; *key; key++) { + crm_trace("Setting alert key %s = '%s'", *key, value); + if (value) { + setenv(*key, value, 1); + } else { + unsetenv(*key); + } + } +} + +void +crm_set_alert_key_int(enum crm_alert_keys_e name, int value) +{ + char *s = crm_itoa(value); + + crm_set_alert_key(name, s); + free(s); +} + +void +crm_unset_alert_keys() +{ + const char **key; + enum crm_alert_keys_e name; + + for(name = 0; name < DIMOF(crm_alert_keys); name++) { + for(key = crm_alert_keys[name]; *key; key++) { + crm_trace("Unsetting alert key %s", *key); + unsetenv(*key); + } + } +} + +void +crm_set_envvar_list(GListPtr envvars) +{ + GListPtr l; + + for (l = g_list_first(envvars); l; l = g_list_next(l)) { + envvar_t *entry = (envvar_t *)(l->data); + + crm_trace("Setting environment variable %s = '%s'", entry->name, + entry->value?entry->value:""); + if (entry->value) { + setenv(entry->name, entry->value, 1); + } else { + unsetenv(entry->name); + } + } +} + +void +crm_unset_envvar_list(GListPtr envvars) +{ + GListPtr l; + + for (l = g_list_first(envvars); l; l = g_list_next(l)) { + envvar_t *entry = (envvar_t *)(l->data); + + crm_trace("Unsetting environment variable %s", entry->name); + unsetenv(entry->name); + } +}