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