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
(1) Event assignment: Assigning: "rc" = "93".
Also see events: [const][dead_error_condition][const][dead_error_condition][dead_error_begin]
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):
(6) Event dead_error_begin: Execution cannot reach this statement: "pcmk__set_result(&request->...".
Also see events: [assignment][const][dead_error_condition][const][dead_error_condition]
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