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   	
(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):
(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]
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