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