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-2024 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/common/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("//" PCMK__XE_LRMD_ALERT, request,
 109                                           LOG_ERR);
 110     const char *alert_id = crm_element_value(alert_xml, PCMK__XA_LRMD_ALERT_ID);
 111     const char *alert_path = crm_element_value(alert_xml,
 112                                                PCMK__XA_LRMD_ALERT_PATH);
 113     svc_action_t *action = NULL;
 114     int alert_timeout = 0;
 115     int rc = pcmk_ok;
 116     GHashTable *params = NULL;
 117     struct alert_cb_s *cb_data = NULL;
 118 
 119     if ((alert_id == NULL) || (alert_path == NULL) ||
 120         (client == NULL) || (client->id == NULL)) { /* hint static analyzer */
 121         rc = -EINVAL;
 122         goto err;
 123     }
 124     if (draining_alerts) {
 125         return pcmk_ok;
 126     }
 127 
 128     crm_element_value_int(alert_xml, PCMK__XA_LRMD_TIMEOUT, &alert_timeout);
 129 
 130     crm_info("Executing alert %s for %s", alert_id, client->id);
 131 
 132     params = xml2list(alert_xml);
 133     pcmk__add_alert_key_int(params, PCMK__alert_key_node_sequence,
 134                             ++alert_sequence_no);
 135 
 136     cb_data = pcmk__assert_alloc(1, sizeof(struct alert_cb_s));
 137 
 138     cb_data->client_id = pcmk__str_copy(client->id);
 139 
 140     crm_element_value_int(request, PCMK__XA_LRMD_CALLID, &(cb_data->call_id));
 141 
 142     action = services_alert_create(alert_id, alert_path, alert_timeout, params,
 143                                    alert_sequence_no, cb_data);
 144     if (action->rc != PCMK_OCF_UNKNOWN) {
 145         rc = -E2BIG;
 146         goto err;
 147     }
 148 
 149     rc = services_action_user(action, CRM_DAEMON_USER);
 150     if (rc < 0) {
 151         goto err;
 152     }
 153 
 154     add_inflight_alert(cb_data->call_id, alert_timeout);
 155     if (services_alert_async(action, alert_complete) == FALSE) {
 156         services_action_free(action);
 157     }
 158     return pcmk_ok;
 159 
 160 err:
 161     if (cb_data) {
 162         free(cb_data->client_id);
 163         free(cb_data);
 164     }
 165     services_action_free(action);
 166     return rc;
 167 }
 168 
 169 static bool
 170 drain_check(guint remaining_timeout_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 171 {
 172     if (inflight_alerts != NULL) {
 173         guint count = g_hash_table_size(inflight_alerts);
 174 
 175         if (count > 0) {
 176             crm_trace("%d alerts pending (%.3fs timeout remaining)",
 177                       count, remaining_timeout_ms / 1000.0);
 178             return TRUE;
 179         }
 180     }
 181     return FALSE;
 182 }
 183 
 184 void
 185 lrmd_drain_alerts(GMainLoop *mloop)
     /* [previous][next][first][last][top][bottom][index][help] */
 186 {
 187     if (inflight_alerts != NULL) {
 188         guint timer_ms = max_inflight_timeout() + 5000;
 189 
 190         crm_trace("Draining in-flight alerts (timeout %.3fs)",
 191                   timer_ms / 1000.0);
 192         draining_alerts = TRUE;
 193         pcmk_drain_main_loop(mloop, timer_ms, drain_check);
 194         g_hash_table_destroy(inflight_alerts);
 195         inflight_alerts = NULL;
 196     }
 197 }

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