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