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

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