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

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