root/daemons/execd/execd_alerts.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. add_inflight_alert
  2. remove_inflight_alert
  3. max_inflight_timeout
  4. alert_complete
  5. process_lrmd_alert_exec
  6. drain_check
  7. lrmd_drain_alerts

   1 /*
   2  * Copyright 2016-2022 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <glib.h>
  13 
  14 #include <crm/crm.h>
  15 #include <crm/services.h>
  16 #include <crm/services_internal.h>
  17 #include <crm/common/ipc.h>
  18 #include <crm/common/ipc_internal.h>
  19 #include <crm/common/alerts_internal.h>
  20 #include <crm/msg_xml.h>
  21 
  22 #include "pacemaker-execd.h"
  23 
  24 /* Track in-flight alerts so we can wait for them at shutdown */
  25 static GHashTable *inflight_alerts; /* key = call_id, value = timeout */
  26 static gboolean draining_alerts = FALSE;
  27 
  28 static inline void
  29 add_inflight_alert(int call_id, int timeout)
     /* [previous][next][first][last][top][bottom][index][help] */
  30 {
  31     if (inflight_alerts == NULL) {
  32         inflight_alerts = pcmk__intkey_table(NULL);
  33     }
  34     pcmk__intkey_table_insert(inflight_alerts, call_id,
  35                               GINT_TO_POINTER(timeout));
  36 }
  37 
  38 static inline void
  39 remove_inflight_alert(int call_id)
     /* [previous][next][first][last][top][bottom][index][help] */
  40 {
  41     if (inflight_alerts != NULL) {
  42         pcmk__intkey_table_remove(inflight_alerts, call_id);
  43     }
  44 }
  45 
  46 static int
  47 max_inflight_timeout(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  48 {
  49     GHashTableIter iter;
  50     gpointer timeout;
  51     int max_timeout = 0;
  52 
  53     if (inflight_alerts) {
  54         g_hash_table_iter_init(&iter, inflight_alerts);
  55         while (g_hash_table_iter_next(&iter, NULL, &timeout)) {
  56             if (GPOINTER_TO_INT(timeout) > max_timeout) {
  57                 max_timeout = GPOINTER_TO_INT(timeout);
  58             }
  59         }
  60     }
  61     return max_timeout;
  62 }
  63 
  64 struct alert_cb_s {
  65     char *client_id;
  66     int call_id;
  67 };
  68 
  69 static void
  70 alert_complete(svc_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
  71 {
  72     struct alert_cb_s *cb_data = (struct alert_cb_s *) (action->cb_data);
  73 
  74     CRM_CHECK(cb_data != NULL, return);
  75 
  76     remove_inflight_alert(cb_data->call_id);
  77 
  78     if (action->status != PCMK_EXEC_DONE) {
  79         const char *reason = services__exit_reason(action);
  80 
  81         crm_notice("Could not send alert: %s%s%s%s " CRM_XS " client=%s",
  82                    pcmk_exec_status_str(action->status),
  83                    (reason == NULL)? "" : " (",
  84                    (reason == NULL)? "" : reason,
  85                    (reason == NULL)? "" : ")",
  86                    cb_data->client_id);
  87 
  88     } else if (action->rc != 0) {
  89         crm_notice("Alert [%d] completed but exited with status %d "
  90                    CRM_XS " client=%s",
  91                    action->pid, action->rc, cb_data->client_id);
  92 
  93     } else {
  94         crm_debug("Alert [%d] completed " CRM_XS " client=%s",
  95                   action->pid, cb_data->client_id);
  96     }
  97 
  98     free(cb_data->client_id);
  99     free(action->cb_data);
 100     action->cb_data = NULL;
 101 }
 102 
 103 int
 104 process_lrmd_alert_exec(pcmk__client_t *client, uint32_t id, xmlNode *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 105 {
 106     static int alert_sequence_no = 0;
 107 
 108     xmlNode *alert_xml = get_xpath_object("//" F_LRMD_ALERT, request, LOG_ERR);
 109     const char *alert_id = crm_element_value(alert_xml, F_LRMD_ALERT_ID);
 110     const char *alert_path = crm_element_value(alert_xml, F_LRMD_ALERT_PATH);
 111     svc_action_t *action = NULL;
 112     int alert_timeout = 0;
 113     int rc = pcmk_ok;
 114     GHashTable *params = NULL;
 115     struct alert_cb_s *cb_data = NULL;
 116 
 117     if ((alert_id == NULL) || (alert_path == NULL) ||
 118         (client == NULL) || (client->id == NULL)) { /* hint static analyzer */
 119         return -EINVAL;
 120     }
 121     if (draining_alerts) {
 122         return pcmk_ok;
 123     }
 124 
 125     crm_element_value_int(alert_xml, F_LRMD_TIMEOUT, &alert_timeout);
 126 
 127     crm_info("Executing alert %s for %s", alert_id, client->id);
 128 
 129     params = xml2list(alert_xml);
 130     pcmk__add_alert_key_int(params, PCMK__alert_key_node_sequence,
 131                             ++alert_sequence_no);
 132 
 133     cb_data = calloc(1, sizeof(struct alert_cb_s));
 134     if (cb_data == NULL) {
 135         rc = -errno;
 136         goto err;
 137     }
 138 
 139     /* coverity[deref_ptr] False Positive */
 140     cb_data->client_id = strdup(client->id);
 141     if (cb_data->client_id == NULL) {
 142         rc = -errno;
 143         goto err;
 144     }
 145 
 146     crm_element_value_int(request, F_LRMD_CALLID, &(cb_data->call_id));
 147 
 148     action = services_alert_create(alert_id, alert_path, alert_timeout, params,
 149                                    alert_sequence_no, cb_data);
 150     if (action->rc != PCMK_OCF_UNKNOWN) {
 151         rc = -E2BIG;
 152         goto err;
 153     }
 154 
 155     rc = services_action_user(action, CRM_DAEMON_USER);
 156     if (rc < 0) {
 157         goto err;
 158     }
 159 
 160     add_inflight_alert(cb_data->call_id, alert_timeout);
 161     if (services_alert_async(action, alert_complete) == FALSE) {
 162         services_action_free(action);
 163     }
 164     return pcmk_ok;
 165 
 166 err:
 167     if (cb_data) {
 168         if (cb_data->client_id) {
 169             free(cb_data->client_id);
 170         }
 171         free(cb_data);
 172     }
 173     services_action_free(action);
 174     return rc;
 175 }
 176 
 177 static bool
 178 drain_check(guint remaining_timeout_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 179 {
 180     if (inflight_alerts != NULL) {
 181         guint count = g_hash_table_size(inflight_alerts);
 182 
 183         if (count > 0) {
 184             crm_trace("%d alerts pending (%.3fs timeout remaining)",
 185                       count, remaining_timeout_ms / 1000.0);
 186             return TRUE;
 187         }
 188     }
 189     return FALSE;
 190 }
 191 
 192 void
 193 lrmd_drain_alerts(GMainLoop *mloop)
     /* [previous][next][first][last][top][bottom][index][help] */
 194 {
 195     if (inflight_alerts != NULL) {
 196         guint timer_ms = max_inflight_timeout() + 5000;
 197 
 198         crm_trace("Draining in-flight alerts (timeout %.3fs)",
 199                   timer_ms / 1000.0);
 200         draining_alerts = TRUE;
 201         pcmk_drain_main_loop(mloop, timer_ms, drain_check);
 202         g_hash_table_destroy(inflight_alerts);
 203         inflight_alerts = NULL;
 204     }
 205 }

/* [previous][next][first][last][top][bottom][index][help] */