1    	/*
2    	 * Copyright 2010-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 General Public License version 2
7    	 * or later (GPLv2+) WITHOUT ANY WARRANTY.
8    	 */
9    	
10   	#include <crm_internal.h>
11   	
12   	#include <glib.h>                       // g_hash_table_destroy
13   	#include <stdbool.h>                    // bool
14   	#include <stdlib.h>                     // free
15   	#include <string.h>                     // NULL, strstr
16   	
17   	#include <libxml/tree.h>                // xmlNode
18   	
19   	#include <crm/crm.h>                    // CRM_OP_PING, CRM_OP_QUIT
20   	#include <crm/common/ipc.h>             // crm_ipc_flags
21   	#include <crm/common/mainloop.h>        // mainloop_set_trigger
22   	#include <crm/common/results.h>         // CRM_EX_*, pcmk_exec_status_str
23   	#include <crm/common/xml.h>             // PCMK_XA_*
24   	
25   	#include "pacemakerd.h"                 // pacemakerd_*
26   	
27   	static GHashTable *pacemakerd_handlers = NULL;
28   	
29   	static xmlNode *
30   	handle_node_cache_request(pcmk__request_t *request)
31   	{
32   	    pcmk__trace("Ignoring request from client %s to purge node because peer "
33   	                "cache is not used",
34   	                pcmk__client_name(request->ipc_client));
35   	
36   	    pcmk__ipc_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags,
37   	                       NULL, CRM_EX_OK);
38   	    return NULL;
39   	}
40   	
41   	static xmlNode *
42   	handle_ping_request(pcmk__request_t *request)
43   	{
44   	    xmlNode *msg = request->xml;
45   	
46   	    const char *value = NULL;
47   	    xmlNode *ping = NULL;
48   	    xmlNode *reply = NULL;
49   	    const char *from = pcmk__xe_get(msg, PCMK__XA_CRM_SYS_FROM);
50   	
51   	    /* Pinged for status */
52   	    pcmk__trace("Pinged from " PCMK__XA_CRM_SYS_FROM "='%s' "
53   	                PCMK_XA_ORIGIN "='%s'",
54   	                pcmk__s(from, ""),
55   	                pcmk__s(pcmk__xe_get(msg, PCMK_XA_ORIGIN), ""));
56   	
57   	    pcmk__ipc_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags,
58   	                       NULL, CRM_EX_INDETERMINATE);
59   	
60   	    ping = pcmk__xe_create(NULL, PCMK__XE_PING_RESPONSE);
61   	    value = pcmk__xe_get(msg, PCMK__XA_CRM_SYS_TO);
62   	    pcmk__xe_set(ping, PCMK__XA_CRM_SUBSYSTEM, value);
63   	    pcmk__xe_set(ping, PCMK__XA_PACEMAKERD_STATE, pacemakerd_state);
64   	    pcmk__xe_set_time(ping, PCMK__XA_CRM_TIMESTAMP, subdaemon_check_progress);
65   	    pcmk__xe_set(ping, PCMK_XA_RESULT, "ok");
66   	    reply = pcmk__new_reply(msg, ping);
67   	
68   	    pcmk__xml_free(ping);
69   	
70   	    if (reply == NULL) {
71   	        pcmk__format_result(&request->result, CRM_EX_ERROR, PCMK_EXEC_ERROR,
72   	                            "Failed building ping reply for client %s",
73   	                            pcmk__client_name(request->ipc_client));
74   	    } else {
75   	        pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
76   	    }
77   	
78   	    /* Just proceed state on sbd pinging us.
79   	     * The "from" string is in the format "<pid>_sbd:pcmk".
80   	     */
81   	    if ((from != NULL) && (strstr(from, "sbd") != NULL)) {
82   	        if (pcmk__str_eq(pacemakerd_state, PCMK__VALUE_SHUTDOWN_COMPLETE,
83   	                         pcmk__str_none)) {
84   	            if (pcmk__get_sbd_sync_resource_startup()) {
85   	                pcmk__notice("Shutdown-complete-state passed to SBD");
86   	            }
87   	
88   	            shutdown_complete_state_reported_to = request->ipc_client->pid;
89   	
90   	        } else if (pcmk__str_eq(pacemakerd_state, PCMK__VALUE_WAIT_FOR_PING,
91   	                                pcmk__str_none)) {
92   	            pcmk__notice("Received startup-trigger from SBD");
93   	            pacemakerd_state = PCMK__VALUE_STARTING_DAEMONS;
94   	            mainloop_set_trigger(startup_trigger);
95   	        }
96   	    }
97   	
98   	    return reply;
99   	}
100  	
101  	static xmlNode *
102  	handle_shutdown_request(pcmk__request_t *request)
103  	{
104  	    xmlNode *msg = request->xml;
105  	
106  	    xmlNode *shutdown = NULL;
107  	    xmlNode *reply = NULL;
108  	
109  	    /* Only allow privileged users (i.e. root or hacluster) to shut down
110  	     * Pacemaker from the command line (or direct IPC), so that other users
111  	     * are forced to go through the CIB and have ACLs applied.
112  	     */
113  	    bool allowed = pcmk__is_set(request->ipc_client->flags,
114  	                                pcmk__client_privileged);
115  	
116  	    pcmk__ipc_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags,
117  	                       NULL, CRM_EX_INDETERMINATE);
118  	
119  	    shutdown = pcmk__xe_create(NULL, PCMK__XE_SHUTDOWN);
120  	
121  	    if (allowed) {
122  	        pcmk__notice("Shutting down in response to IPC request %s from %s",
123  	                     pcmk__xe_get(msg, PCMK_XA_REFERENCE),
124  	                     pcmk__xe_get(msg, PCMK_XA_ORIGIN));
125  	        pcmk__xe_set_int(shutdown, PCMK__XA_OP_STATUS, CRM_EX_OK);
126  	    } else {
127  	        pcmk__warn("Ignoring shutdown request from unprivileged client %s",
128  	                   pcmk__client_name(request->ipc_client));
129  	        pcmk__xe_set_int(shutdown, PCMK__XA_OP_STATUS,
130  	                         CRM_EX_INSUFFICIENT_PRIV);
131  	    }
132  	
133  	    reply = pcmk__new_reply(msg, shutdown);
134  	    pcmk__xml_free(shutdown);
135  	
136  	    if (reply == NULL) {
137  	        pcmk__format_result(&request->result, CRM_EX_ERROR, PCMK_EXEC_ERROR,
138  	                            "Failed building shutdown reply for client %s",
139  	                            pcmk__client_name(request->ipc_client));
140  	    } else {
141  	        pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
142  	    }
143  	
144  	    if (allowed) {
145  	        pcmk_shutdown(15);
146  	    }
147  	
148  	    return reply;
149  	}
150  	
151  	static xmlNode *
152  	handle_unknown_request(pcmk__request_t *request)
153  	{
154  	    pcmk__ipc_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags,
155  	                       NULL, CRM_EX_PROTOCOL);
156  	
157  	    pcmk__format_result(&request->result, CRM_EX_PROTOCOL, PCMK_EXEC_INVALID,
158  	                        "Unknown request type '%s' (bug?)",
159  	                        pcmk__s(request->op, ""));
160  	    return NULL;
161  	}
162  	
163  	static void
164  	pacemakerd_register_handlers(void)
165  	{
166  	    pcmk__server_command_t handlers[] = {
167  	        { CRM_OP_RM_NODE_CACHE, handle_node_cache_request },
168  	        { CRM_OP_PING, handle_ping_request },
169  	        { CRM_OP_QUIT, handle_shutdown_request },
170  	        { NULL, handle_unknown_request },
171  	    };
172  	
173  	    pacemakerd_handlers = pcmk__register_handlers(handlers);
174  	}
175  	
176  	void
177  	pacemakerd_unregister_handlers(void)
178  	{
CID (unavailable; MK=620304d58511ca76af2ae25985984dd7) (#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".
179  	    g_clear_pointer(&pacemakerd_handlers, g_hash_table_destroy);
180  	}
181  	
182  	void
183  	pacemakerd_handle_request(pcmk__request_t *request)
184  	{
185  	    xmlNode *reply = NULL;
186  	    char *log_msg = NULL;
187  	    const char *exec_status_s = NULL;
188  	    const char *reason = NULL;
189  	
190  	    if (pacemakerd_handlers == NULL) {
191  	        pacemakerd_register_handlers();
192  	    }
193  	
194  	    reply = pcmk__process_request(request, pacemakerd_handlers);
195  	
196  	    if (reply != NULL) {
197  	        pcmk__log_xml_trace(reply, "Reply");
198  	
199  	        pcmk__ipc_send_xml(request->ipc_client, request->ipc_id, reply,
200  	                           crm_ipc_server_event);
201  	        pcmk__xml_free(reply);
202  	    }
203  	
204  	    exec_status_s = pcmk_exec_status_str(request->result.execution_status);
205  	    reason = request->result.exit_reason;
206  	
207  	    log_msg = pcmk__assert_asprintf("Processed %s request from %s %s: %s%s%s%s",
208  	                                    request->op,
209  	                                    pcmk__request_origin_type(request),
210  	                                    pcmk__request_origin(request),
211  	                                    exec_status_s,
212  	                                    (reason == NULL)? "" : " (",
213  	                                    pcmk__s(reason, ""),
214  	                                    (reason == NULL)? "" : ")");
215  	
216  	    if (!pcmk__result_ok(&request->result)) {
217  	        pcmk__warn("%s", log_msg);
218  	    } else {
219  	        pcmk__debug("%s", log_msg);
220  	    }
221  	
222  	    free(log_msg);
223  	    pcmk__reset_request(request);
224  	}
225