1 /*
2 * Copyright 2012-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 Lesser General Public License
7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8 */
9
10 #include <crm_internal.h>
11
12 #include <errno.h> // EINPROGRESS, ENODEV
13 #include <stdbool.h> // bool
14 #include <stddef.h> // NULL
15 #include <stdlib.h> // free
16
17 #include <glib.h> // g_hash_table_destroy
18 #include <libxml/parser.h> // xmlNode
19 #include <qb/qblog.h> // QB_XS
20
21 #include <crm/crm.h> // CRM_OP_*, CRM_SYSTEM_LRMD
22 #include <crm/common/internal.h> // pcmk__process_request, pcmk__xml_free
23 #include <crm/common/results.h> // pcmk_exec_status, pcmk_rc_*, pcmk_rc_str
24 #include <crm/lrmd.h> // LRMD_OP_*
25
26 #include "pacemaker-execd.h" // execd_*
27
28
29 static GHashTable *execd_handlers = NULL;
30 static int lrmd_call_id = 0;
31
32 static xmlNode *
33 handle_ipc_fwd_request(pcmk__request_t *request)
34 {
35 int call_id = 0;
36 int rc = pcmk_rc_ok;
37 xmlNode *reply = NULL;
38
39 #ifdef PCMK__COMPILE_REMOTE
40 bool allowed = pcmk__is_set(request->ipc_client->flags,
41 pcmk__client_privileged);
42
43 if (!allowed) {
44 pcmk__set_result(&request->result, CRM_EX_INSUFFICIENT_PRIV,
45 PCMK_EXEC_ERROR, NULL);
46 pcmk__warn("Rejecting IPC request '%s' from unprivileged client %s",
47 request->op, pcmk__client_name(request->ipc_client));
48 return NULL;
49 }
50
51 rc = ipc_proxy_forward_client(request->ipc_client, request->xml);
52 #else
53 rc = EPROTONOSUPPORT;
54 #endif
55
56 if (rc == pcmk_rc_ok) {
57 /* Coverity gets confused by the #ifdef above and thinks this block
58 * is unreachable due to rc always being EPROTONOSUPPORT.
59 */
60 // coverity[dead_error_line]
61 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
62 } else {
63 pcmk__set_result(&request->result, pcmk_rc2exitc(rc), PCMK_EXEC_ERROR,
64 pcmk_rc_str(rc));
65 }
66
67 pcmk__xe_get_int(request->xml, PCMK__XA_LRMD_CALLID, &call_id);
68
69 /* Create a generic reply since forwarding doesn't create a more specific one */
70 reply = execd_create_reply(pcmk_rc2legacy(rc), call_id);
71 return reply;
72 }
73
74 static xmlNode *
75 handle_register_request(pcmk__request_t *request)
76 {
77 int call_id = 0;
78 int rc = pcmk_rc_ok;
79 xmlNode *reply = NULL;
80
81 pcmk__xe_get_int(request->xml, PCMK__XA_LRMD_CALLID, &call_id);
82 rc = execd_process_signon(request->ipc_client, request->xml, call_id, &reply);
83
84 if (rc != pcmk_rc_ok) {
85 pcmk__set_result(&request->result, pcmk_rc2exitc(rc), PCMK_EXEC_ERROR,
86 pcmk_rc_str(rc));
87 return NULL;
88 }
89
90 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
91 return reply;
92 }
93
94 static xmlNode *
95 handle_alert_exec_request(pcmk__request_t *request)
96 {
97 int call_id = 0;
98 int rc = pcmk_rc_ok;
99 bool allowed = pcmk__is_set(request->ipc_client->flags,
100 pcmk__client_privileged);
101 xmlNode *reply = NULL;
102
103 if (!allowed) {
104 pcmk__set_result(&request->result, CRM_EX_INSUFFICIENT_PRIV,
105 PCMK_EXEC_ERROR, NULL);
106 pcmk__warn("Rejecting IPC request '%s' from unprivileged client %s",
107 request->op, pcmk__client_name(request->ipc_client));
108 return NULL;
109 }
110
111 pcmk__xe_get_int(request->xml, PCMK__XA_LRMD_CALLID, &call_id);
112
113 rc = execd_process_alert_exec(request->ipc_client, request->xml);
114
115 if (rc == pcmk_rc_ok) {
116 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
117 } else {
118 pcmk__set_result(&request->result, pcmk_rc2exitc(rc), PCMK_EXEC_ERROR,
119 pcmk_rc_str(rc));
120 }
121
122 /* Create a generic reply since executing an alert doesn't create a
123 * more specific one.
124 */
125 reply = execd_create_reply(pcmk_rc2legacy(rc), call_id);
126 return reply;
127 }
128
129 static xmlNode *
130 handle_check_request(pcmk__request_t *request)
131 {
132 bool allowed = pcmk__is_set(request->ipc_client->flags,
133 pcmk__client_privileged);
134 xmlNode *wrapper = NULL;
135 xmlNode *data = NULL;
136 const char *timeout = NULL;
137
138 if (!allowed) {
139 pcmk__set_result(&request->result, CRM_EX_INSUFFICIENT_PRIV,
140 PCMK_EXEC_ERROR, NULL);
141 pcmk__warn("Rejecting IPC request '%s' from unprivileged client %s",
142 request->op, pcmk__client_name(request->ipc_client));
143 return NULL;
144 }
145
146 wrapper = pcmk__xe_first_child(request->xml,
147 PCMK__XE_LRMD_CALLDATA,
148 NULL, NULL);
149 data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
150
151 if (data == NULL) {
152 pcmk__set_result(&request->result, CRM_EX_SOFTWARE, PCMK_EXEC_INVALID,
153 NULL);
154 return NULL;
155 }
156
157 timeout = pcmk__xe_get(data, PCMK__XA_LRMD_WATCHDOG);
158 /* FIXME: This just exits on certain conditions, which seems like a pretty
159 * extreme reaction for a daemon to take.
160 */
161 pcmk__valid_fencing_watchdog_timeout(timeout);
162
163 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
164 return NULL;
165 }
166
167 static xmlNode *
168 handle_get_recurring_request(pcmk__request_t *request)
169 {
170 int call_id = 0;
171 int rc = pcmk_rc_ok;
172 bool allowed = pcmk__is_set(request->ipc_client->flags,
173 pcmk__client_privileged);
174 xmlNode *reply = NULL;
175
176 if (!allowed) {
177 pcmk__set_result(&request->result, CRM_EX_INSUFFICIENT_PRIV,
178 PCMK_EXEC_ERROR, NULL);
179 pcmk__warn("Rejecting IPC request '%s' from unprivileged client %s",
180 request->op, pcmk__client_name(request->ipc_client));
181 return NULL;
182 }
183
184 pcmk__xe_get_int(request->xml, PCMK__XA_LRMD_CALLID, &call_id);
185
186 rc = execd_process_get_recurring(request->xml, call_id, &reply);
187
188 if (rc == pcmk_rc_ok) {
189 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
190 } else {
191 pcmk__set_result(&request->result, pcmk_rc2exitc(rc), PCMK_EXEC_ERROR,
192 pcmk_rc_str(rc));
193 }
194
195 return reply;
196 }
197
198 static xmlNode *
199 handle_poke_request(pcmk__request_t *request)
200 {
201 int call_id = 0;
202 xmlNode *reply = NULL;
203
204 pcmk__xe_get_int(request->xml, PCMK__XA_LRMD_CALLID, &call_id);
205
206 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
207
208 /* Create a generic reply since this doesn't create a more specific one */
209 reply = execd_create_reply(pcmk_ok, call_id);
210 return reply;
211 }
212
213 static xmlNode *
214 handle_rsc_cancel_request(pcmk__request_t *request)
215 {
216 int call_id = 0;
217 int rc = pcmk_rc_ok;
218 bool allowed = pcmk__is_set(request->ipc_client->flags,
219 pcmk__client_privileged);
220 xmlNode *reply = NULL;
221
222 if (!allowed) {
223 pcmk__set_result(&request->result, CRM_EX_INSUFFICIENT_PRIV,
224 PCMK_EXEC_ERROR, NULL);
225 pcmk__warn("Rejecting IPC request '%s' from unprivileged client %s",
226 request->op, pcmk__client_name(request->ipc_client));
227 return NULL;
228 }
229
230 pcmk__xe_get_int(request->xml, PCMK__XA_LRMD_CALLID, &call_id);
231
232 rc = execd_process_rsc_cancel(request->ipc_client, request->xml);
233
234 if (rc == pcmk_rc_ok) {
235 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
236 } else {
237 pcmk__set_result(&request->result, pcmk_rc2exitc(rc), PCMK_EXEC_ERROR,
238 pcmk_rc_str(rc));
239 }
240
241 /* Create a generic reply since canceling a resource doesn't create a
242 * more specific one.
243 */
244 reply = execd_create_reply(pcmk_rc2legacy(rc), call_id);
245 return reply;
246 }
247
248 static xmlNode *
249 handle_rsc_exec_request(pcmk__request_t *request)
250 {
251 int call_id = 0;
252 int rc = pcmk_rc_ok;
253 bool allowed = pcmk__is_set(request->ipc_client->flags,
254 pcmk__client_privileged);
255 xmlNode *reply = NULL;
256
257 if (!allowed) {
258 pcmk__set_result(&request->result, CRM_EX_INSUFFICIENT_PRIV,
259 PCMK_EXEC_ERROR, NULL);
260 pcmk__warn("Rejecting IPC request '%s' from unprivileged client %s",
261 request->op, pcmk__client_name(request->ipc_client));
262 return NULL;
263 }
264
265 pcmk__xe_get_int(request->xml, PCMK__XA_LRMD_CALLID, &call_id);
266
267 rc = execd_process_rsc_exec(request->ipc_client, request->xml);
268
269 if (rc == pcmk_rc_ok) {
270 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
271
272 /* This looks redundant, but it's unfortunately necessary. The first
273 * argument is set as the PCMK__XA_LRMD_RC attribute in the response.
274 * On the other side of the connection, lrmd_send_command will read
275 * this and use it as its return value, which passes back up to the
276 * public API function lrmd_api_exec.
277 */
278 reply = execd_create_reply(call_id, call_id);
279 } else {
280 pcmk__set_result(&request->result, pcmk_rc2exitc(rc), PCMK_EXEC_ERROR,
281 pcmk_rc_str(rc));
282 reply = execd_create_reply(pcmk_rc2legacy(rc), call_id);
283 }
284
285 return reply;
286 }
287
288 static xmlNode *
289 handle_rsc_info_request(pcmk__request_t *request)
290 {
291 int call_id = 0;
292 int rc = pcmk_rc_ok;
293 bool allowed = pcmk__is_set(request->ipc_client->flags,
294 pcmk__client_privileged);
295 xmlNode *reply = NULL;
296
297 if (!allowed) {
298 pcmk__set_result(&request->result, CRM_EX_INSUFFICIENT_PRIV,
299 PCMK_EXEC_ERROR, NULL);
300 pcmk__warn("Rejecting IPC request '%s' from unprivileged client %s",
301 request->op, pcmk__client_name(request->ipc_client));
302 return NULL;
303 }
304
305 pcmk__xe_get_int(request->xml, PCMK__XA_LRMD_CALLID, &call_id);
306
307 /* This returns ENODEV if the resource isn't in the cache which will be
308 * logged as an error. However, this isn't fatal to the client - it may
309 * be querying to see if the resource exists before deciding to register it.
310 * Thus, we'll ignore an ENODEV to prevent a warning message from being
311 * logged.
312 */
313 rc = execd_process_get_rsc_info(request->xml, call_id, &reply);
314
315 if ((rc == pcmk_rc_ok) || (rc == ENODEV)) {
316 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
317 } else {
318 pcmk__set_result(&request->result, pcmk_rc2exitc(rc), PCMK_EXEC_ERROR,
319 pcmk_rc_str(rc));
320 }
321
322 return reply;
323 }
324
325 static xmlNode *
326 handle_rsc_reg_request(pcmk__request_t *request)
327 {
328 int call_id = 0;
329 bool allowed = pcmk__is_set(request->ipc_client->flags,
330 pcmk__client_privileged);
331 xmlNode *reply = NULL;
332
333 if (!allowed) {
334 pcmk__set_result(&request->result, CRM_EX_INSUFFICIENT_PRIV,
335 PCMK_EXEC_ERROR, NULL);
336 pcmk__warn("Rejecting IPC request '%s' from unprivileged client %s",
337 request->op, pcmk__client_name(request->ipc_client));
338 return NULL;
339 }
340
341 pcmk__xe_get_int(request->xml, PCMK__XA_LRMD_CALLID, &call_id);
342
343 execd_process_rsc_register(request->ipc_client, request->ipc_id, request->xml);
344
345 /* Create a generic reply since registering a resource doesn't create
346 * a more specific one.
347 */
348 reply = execd_create_reply(pcmk_ok, call_id);
349 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
350 return reply;
351 }
352
353 static xmlNode *
354 handle_rsc_unreg_request(pcmk__request_t *request)
355 {
356 int call_id = 0;
357 int rc = pcmk_rc_ok;
358 bool allowed = pcmk__is_set(request->ipc_client->flags,
359 pcmk__client_privileged);
360 xmlNode *reply = NULL;
361
362 if (!allowed) {
363 pcmk__set_result(&request->result, CRM_EX_INSUFFICIENT_PRIV,
364 PCMK_EXEC_ERROR, NULL);
365 pcmk__warn("Rejecting IPC request '%s' from unprivileged client %s",
366 request->op, pcmk__client_name(request->ipc_client));
367 return NULL;
368 }
369
370 pcmk__xe_get_int(request->xml, PCMK__XA_LRMD_CALLID, &call_id);
371
372 rc = execd_process_rsc_unregister(request->ipc_client, request->xml);
373
374 /* Create a generic reply since unregistering a resource doesn't create
375 * a more specific one.
376 */
377 reply = execd_create_reply(pcmk_rc2legacy(rc), call_id);
378 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
379 return reply;
380 }
381
382 static bool
383 requires_notify(const char *command, int rc)
384 {
385 if (pcmk__str_eq(command, LRMD_OP_RSC_UNREG, pcmk__str_none)) {
386 /* Don't notify about failed unregisters */
387 return (rc == pcmk_ok) || (rc == -EINPROGRESS);
388 } else {
389 return pcmk__str_any_of(command, LRMD_OP_POKE, LRMD_OP_RSC_REG, NULL);
390 }
391 }
392
393
394 static xmlNode *
395 handle_unknown_request(pcmk__request_t *request)
396 {
397 pcmk__ipc_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags,
398 NULL, CRM_EX_PROTOCOL);
399
400 pcmk__format_result(&request->result, CRM_EX_PROTOCOL, PCMK_EXEC_INVALID,
401 "Unknown request type '%s' (bug?)",
402 pcmk__s(request->op, ""));
403 return NULL;
404 }
405
406 static void
407 execd_register_handlers(void)
408 {
409 pcmk__server_command_t handlers[] = {
410 { CRM_OP_IPC_FWD, handle_ipc_fwd_request },
411 { CRM_OP_REGISTER, handle_register_request },
412 { LRMD_OP_ALERT_EXEC, handle_alert_exec_request },
413 { LRMD_OP_CHECK, handle_check_request },
414 { LRMD_OP_GET_RECURRING, handle_get_recurring_request },
415 { LRMD_OP_POKE, handle_poke_request },
416 { LRMD_OP_RSC_CANCEL, handle_rsc_cancel_request },
417 { LRMD_OP_RSC_EXEC, handle_rsc_exec_request },
418 { LRMD_OP_RSC_INFO, handle_rsc_info_request },
419 { LRMD_OP_RSC_REG, handle_rsc_reg_request },
420 { LRMD_OP_RSC_UNREG, handle_rsc_unreg_request },
421 { NULL, handle_unknown_request },
422 };
423
424 execd_handlers = pcmk__register_handlers(handlers);
425 }
426
427 void
428 execd_unregister_handlers(void)
429 {
|
CID (unavailable; MK=624b0da03e396bfbc6eb631a5f30613a) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS): |
|
(1) Event assign_union_field: |
The union field "in" of "_pp" is written. |
|
(2) Event inconsistent_union_field_access: |
In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in". |
430 g_clear_pointer(&execd_handlers, g_hash_table_destroy);
431 }
432
433 bool
434 execd_invalid_msg(xmlNode *msg)
435 {
436 const char *to = NULL;
437 bool invalid = true;
438
439 CRM_CHECK(msg != NULL, return invalid);
440
441 to = pcmk__xe_get(msg, PCMK__XA_T);
442
443 /* IPC proxy messages do not get a t="" attribute set on them. */
444 invalid = !pcmk__str_eq(to, CRM_SYSTEM_LRMD, pcmk__str_none)
445 && !pcmk__xe_is(msg, PCMK__XE_LRMD_IPC_PROXY);
446
447 if (invalid) {
448 pcmk__info("Ignoring invalid IPC message: to '%s' not " CRM_SYSTEM_LRMD,
449 pcmk__s(to, ""));
450 pcmk__log_xml_info(msg, "[Invalid]");
451 }
452
453 return invalid;
454 }
455
456 void
457 execd_handle_request(pcmk__request_t *request)
458 {
459 char *log_msg = NULL;
460 const char *reason = NULL;
461 const char *exec_status_s = NULL;
462 xmlNode *reply = NULL;
463
464 if (execd_handlers == NULL) {
465 execd_register_handlers();
466 }
467
468 if (request->ipc_client->name == NULL) {
469 request->ipc_client->name = pcmk__xe_get_copy(request->xml,
470 PCMK__XA_LRMD_CLIENTNAME);
471 }
472
473 lrmd_call_id++;
474 if (lrmd_call_id < 1) {
475 lrmd_call_id = 1;
476 }
477
478 pcmk__xe_set(request->xml, PCMK__XA_LRMD_CLIENTID, request->ipc_client->id);
479 pcmk__xe_set(request->xml, PCMK__XA_LRMD_CLIENTNAME,
480 request->ipc_client->name);
481 pcmk__xe_set_int(request->xml, PCMK__XA_LRMD_CALLID, lrmd_call_id);
482
483 reply = pcmk__process_request(request, execd_handlers);
484
485 if (reply != NULL) {
486 int rc = pcmk_rc_ok;
487 int reply_rc = pcmk_ok;
488
489 pcmk__log_xml_trace(reply, "Reply");
490
491 rc = lrmd_server_send_reply(request->ipc_client, request->ipc_id, reply);
492 if (rc != pcmk_rc_ok) {
493 pcmk__warn("Reply to client %s failed: %s " QB_XS " rc=%d",
494 pcmk__client_name(request->ipc_client), pcmk_rc_str(rc),
495 rc);
496 }
497
498 pcmk__xe_get_int(reply, PCMK__XA_LRMD_RC, &reply_rc);
499 if (requires_notify(request->op, reply_rc)) {
500 execd_send_generic_notify(reply_rc, request->xml);
501 }
502
503 pcmk__xml_free(reply);
504 }
505
506 exec_status_s = pcmk_exec_status_str(request->result.execution_status);
507 reason = request->result.exit_reason;
508
509 log_msg = pcmk__assert_asprintf("Processed %s request from %s %s: "
510 "%s%s%s%s",
511 request->op,
512 pcmk__request_origin_type(request),
513 pcmk__request_origin(request),
514 exec_status_s,
515 ((reason == NULL)? "" : " ("),
516 pcmk__s(reason, ""),
517 ((reason == NULL)? "" : ")"));
518
519 if (!pcmk__result_ok(&request->result)) {
520 pcmk__warn("%s", log_msg);
521 } else {
522 pcmk__debug("%s", log_msg);
523 }
524
525 free(log_msg);
526 pcmk__reset_request(request);
527 }
528