1 /*
2 * Copyright 2016-2026 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 <stdbool.h>
13
14 #include <glib.h>
15 #include <libxml/tree.h> // xmlNode
16
17 #include <crm/crm.h>
18 #include <crm/services.h>
19 #include <crm/services_internal.h>
20 #include <crm/common/ipc.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)
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)
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)
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)
72 {
73 struct alert_cb_s *cb_data = (struct alert_cb_s *) (action->cb_data);
74
|
(1) Event path: |
Condition "!(cb_data != NULL)", taking false branch. |
75 CRM_CHECK(cb_data != NULL, return);
76
77 remove_inflight_alert(cb_data->call_id);
78
|
(2) Event path: |
Condition "action->status != PCMK_EXEC_DONE", taking true branch. |
79 if (action->status != PCMK_EXEC_DONE) {
80 const char *reason = services__exit_reason(action);
81
|
(3) Event path: |
Condition "reason != NULL", taking true branch. |
|
(4) Event path: |
Condition "reason != NULL", taking true branch. |
82 pcmk__notice("Could not send alert: %s%s%s%s " QB_XS " client=%s",
83 pcmk_exec_status_str(action->status),
84 (reason != NULL)? " (" : "", pcmk__s(reason, ""),
85 (reason != NULL)? ")" : "", cb_data->client_id);
86
|
(5) Event path: |
Falling through to end of if statement. |
87 } else if (action->rc != 0) {
88 pcmk__notice("Alert [%d] completed but exited with status %d "
89 QB_XS " client=%s",
90 action->pid, action->rc, cb_data->client_id);
91
92 } else {
93 pcmk__debug("Alert [%d] completed " QB_XS " client=%s", action->pid,
94 cb_data->client_id);
95 }
96
97 free(cb_data->client_id);
|
CID (unavailable; MK=58a5caf6eb6cd81fb2ec4677f85e8f50) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS): |
|
(6) Event assign_union_field: |
The union field "in" of "_pp" is written. |
|
(7) Event inconsistent_union_field_access: |
In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in". |
98 g_clear_pointer(&action->cb_data, free);
99 }
100
101 int
102 execd_process_alert_exec(pcmk__client_t *client, xmlNode *request)
103 {
104 static int alert_sequence_no = 0;
105
106 xmlNode *alert_xml = pcmk__xpath_find_one(request->doc,
107 "//" PCMK__XE_LRMD_ALERT,
108 LOG_ERR);
109 const char *alert_id = pcmk__xe_get(alert_xml, PCMK__XA_LRMD_ALERT_ID);
110 const char *alert_path = pcmk__xe_get(alert_xml, PCMK__XA_LRMD_ALERT_PATH);
111 svc_action_t *action = NULL;
112 int alert_timeout = 0;
113 int rc = pcmk_rc_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 rc = EINVAL;
120 goto err;
121 }
122 if (draining_alerts) {
123 return pcmk_rc_ok;
124 }
125
126 pcmk__xe_get_int(alert_xml, PCMK__XA_LRMD_TIMEOUT, &alert_timeout);
127
128 pcmk__info("Executing alert %s for %s", alert_id, client->id);
129
130 params = xml2list(alert_xml);
131 pcmk__add_alert_key_int(params, PCMK__alert_key_node_sequence,
132 ++alert_sequence_no);
133
134 cb_data = pcmk__assert_alloc(1, sizeof(struct alert_cb_s));
135
136 cb_data->client_id = pcmk__str_copy(client->id);
137
138 pcmk__xe_get_int(request, PCMK__XA_LRMD_CALLID, &(cb_data->call_id));
139
140 action = services_alert_create(alert_id, alert_path, alert_timeout, params,
141 alert_sequence_no, cb_data);
142 if (action->rc != PCMK_OCF_UNKNOWN) {
143 rc = E2BIG;
144 goto err;
145 }
146
147 rc = services_action_user(action, CRM_DAEMON_USER);
148 if (rc < 0) {
149 rc = pcmk_legacy2rc(rc);
150 goto err;
151 }
152
153 add_inflight_alert(cb_data->call_id, alert_timeout);
154 if (services_alert_async(action, alert_complete) == FALSE) {
155 services_action_free(action);
156 }
157 return pcmk_rc_ok;
158
159 err:
160 if (cb_data) {
161 free(cb_data->client_id);
162 free(cb_data);
163 }
164 services_action_free(action);
165 return rc;
166 }
167
168 static bool
169 drain_check(guint remaining_timeout_ms)
170 {
171 if (inflight_alerts != NULL) {
172 guint count = g_hash_table_size(inflight_alerts);
173
174 if (count > 0) {
175 pcmk__trace("%d alerts pending (%.3fs timeout remaining)",
176 count, (remaining_timeout_ms / 1000.0));
177 return TRUE;
178 }
179 }
180 return FALSE;
181 }
182
183 void
184 lrmd_drain_alerts(GMainLoop *mloop)
185 {
186 if (inflight_alerts != NULL) {
187 guint timer_ms = max_inflight_timeout() + 5000;
188
189 pcmk__trace("Draining in-flight alerts (timeout %.3fs)",
190 (timer_ms / 1000.0));
191 draining_alerts = TRUE;
192 pcmk_drain_main_loop(mloop, timer_ms, drain_check);
193 g_clear_pointer(&inflight_alerts, g_hash_table_destroy);
194 }
195 }
196