Line data Source code
1 : /*
2 : * Copyright 2020-2024 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 <stdlib.h>
13 : #include <time.h>
14 :
15 : #include <crm/crm.h>
16 : #include <crm/common/xml.h>
17 : #include <crm/common/ipc.h>
18 : #include <crm/common/ipc_internal.h>
19 : #include <crm/common/ipc_pacemakerd.h>
20 : #include "crmcommon_private.h"
21 :
22 : typedef struct pacemakerd_api_private_s {
23 : enum pcmk_pacemakerd_state state;
24 : char *client_uuid;
25 : } pacemakerd_api_private_t;
26 :
27 : static const char *pacemakerd_state_str[] = {
28 : PCMK__VALUE_INIT,
29 : PCMK__VALUE_STARTING_DAEMONS,
30 : PCMK__VALUE_WAIT_FOR_PING,
31 : PCMK__VALUE_RUNNING,
32 : PCMK__VALUE_SHUTTING_DOWN,
33 : PCMK__VALUE_SHUTDOWN_COMPLETE,
34 : PCMK_VALUE_REMOTE,
35 : };
36 :
37 : enum pcmk_pacemakerd_state
38 0 : pcmk_pacemakerd_api_daemon_state_text2enum(const char *state)
39 : {
40 : int i;
41 :
42 0 : if (state == NULL) {
43 0 : return pcmk_pacemakerd_state_invalid;
44 : }
45 0 : for (i=pcmk_pacemakerd_state_init; i <= pcmk_pacemakerd_state_max;
46 0 : i++) {
47 0 : if (pcmk__str_eq(state, pacemakerd_state_str[i], pcmk__str_none)) {
48 0 : return i;
49 : }
50 : }
51 0 : return pcmk_pacemakerd_state_invalid;
52 : }
53 :
54 : const char *
55 0 : pcmk_pacemakerd_api_daemon_state_enum2text(
56 : enum pcmk_pacemakerd_state state)
57 : {
58 0 : if ((state >= pcmk_pacemakerd_state_init) &&
59 : (state <= pcmk_pacemakerd_state_max)) {
60 0 : return pacemakerd_state_str[state];
61 : }
62 0 : return "invalid";
63 : }
64 :
65 : /*!
66 : * \internal
67 : * \brief Return a friendly string representation of a \p pacemakerd state
68 : *
69 : * \param[in] state \p pacemakerd state
70 : *
71 : * \return A user-friendly string representation of \p state, or
72 : * <tt>"Invalid pacemakerd state"</tt>
73 : */
74 : const char *
75 0 : pcmk__pcmkd_state_enum2friendly(enum pcmk_pacemakerd_state state)
76 : {
77 0 : switch (state) {
78 0 : case pcmk_pacemakerd_state_init:
79 0 : return "Initializing pacemaker";
80 0 : case pcmk_pacemakerd_state_starting_daemons:
81 0 : return "Pacemaker daemons are starting";
82 0 : case pcmk_pacemakerd_state_wait_for_ping:
83 0 : return "Waiting for startup trigger from SBD";
84 0 : case pcmk_pacemakerd_state_running:
85 0 : return "Pacemaker is running";
86 0 : case pcmk_pacemakerd_state_shutting_down:
87 0 : return "Pacemaker daemons are shutting down";
88 0 : case pcmk_pacemakerd_state_shutdown_complete:
89 : /* Assuming pacemakerd won't process messages while in
90 : * shutdown_complete state unless reporting to SBD
91 : */
92 0 : return "Pacemaker daemons are shut down (reporting to SBD)";
93 0 : case pcmk_pacemakerd_state_remote:
94 0 : return "pacemaker-remoted is running (on a Pacemaker Remote node)";
95 0 : default:
96 0 : return "Invalid pacemakerd state";
97 : }
98 : }
99 :
100 : /*!
101 : * \internal
102 : * \brief Get a string representation of a \p pacemakerd API reply type
103 : *
104 : * \param[in] reply \p pacemakerd API reply type
105 : *
106 : * \return String representation of a \p pacemakerd API reply type
107 : */
108 : const char *
109 0 : pcmk__pcmkd_api_reply2str(enum pcmk_pacemakerd_api_reply reply)
110 : {
111 0 : switch (reply) {
112 0 : case pcmk_pacemakerd_reply_ping:
113 0 : return "ping";
114 0 : case pcmk_pacemakerd_reply_shutdown:
115 0 : return "shutdown";
116 0 : default:
117 0 : return "unknown";
118 : }
119 : }
120 :
121 : // \return Standard Pacemaker return code
122 : static int
123 0 : new_data(pcmk_ipc_api_t *api)
124 : {
125 0 : struct pacemakerd_api_private_s *private = NULL;
126 :
127 0 : api->api_data = calloc(1, sizeof(struct pacemakerd_api_private_s));
128 :
129 0 : if (api->api_data == NULL) {
130 0 : return errno;
131 : }
132 :
133 0 : private = api->api_data;
134 0 : private->state = pcmk_pacemakerd_state_invalid;
135 : /* other as with cib, controld, ... we are addressing pacemakerd just
136 : from the local node -> pid is unique and thus sufficient as an ID
137 : */
138 0 : private->client_uuid = pcmk__getpid_s();
139 :
140 0 : return pcmk_rc_ok;
141 : }
142 :
143 : static void
144 0 : free_data(void *data)
145 : {
146 0 : free(((struct pacemakerd_api_private_s *) data)->client_uuid);
147 0 : free(data);
148 0 : }
149 :
150 : // \return Standard Pacemaker return code
151 : static int
152 0 : post_connect(pcmk_ipc_api_t *api)
153 : {
154 0 : struct pacemakerd_api_private_s *private = NULL;
155 :
156 0 : if (api->api_data == NULL) {
157 0 : return EINVAL;
158 : }
159 0 : private = api->api_data;
160 0 : private->state = pcmk_pacemakerd_state_invalid;
161 :
162 0 : return pcmk_rc_ok;
163 : }
164 :
165 : static void
166 0 : post_disconnect(pcmk_ipc_api_t *api)
167 : {
168 0 : struct pacemakerd_api_private_s *private = NULL;
169 :
170 0 : if (api->api_data == NULL) {
171 0 : return;
172 : }
173 0 : private = api->api_data;
174 0 : private->state = pcmk_pacemakerd_state_invalid;
175 :
176 0 : return;
177 : }
178 :
179 : static bool
180 0 : reply_expected(pcmk_ipc_api_t *api, const xmlNode *request)
181 : {
182 0 : const char *command = crm_element_value(request, PCMK__XA_CRM_TASK);
183 :
184 0 : if (command == NULL) {
185 0 : return false;
186 : }
187 :
188 : // We only need to handle commands that functions in this file can send
189 0 : return pcmk__str_any_of(command, CRM_OP_PING, CRM_OP_QUIT, NULL);
190 : }
191 :
192 : static bool
193 0 : dispatch(pcmk_ipc_api_t *api, xmlNode *reply)
194 : {
195 0 : crm_exit_t status = CRM_EX_OK;
196 0 : xmlNode *wrapper = NULL;
197 0 : xmlNode *msg_data = NULL;
198 0 : pcmk_pacemakerd_api_reply_t reply_data = {
199 : pcmk_pacemakerd_reply_unknown
200 : };
201 0 : const char *value = NULL;
202 0 : long long value_ll = 0;
203 :
204 0 : if (pcmk__xe_is(reply, PCMK__XE_ACK)) {
205 0 : long long int ack_status = 0;
206 0 : pcmk__scan_ll(crm_element_value(reply, PCMK_XA_STATUS), &ack_status,
207 : CRM_EX_OK);
208 0 : return ack_status == CRM_EX_INDETERMINATE;
209 : }
210 :
211 0 : value = crm_element_value(reply, PCMK__XA_SUBT);
212 0 : if (!pcmk__str_eq(value, PCMK__VALUE_RESPONSE, pcmk__str_none)) {
213 0 : crm_info("Unrecognizable message from %s: "
214 : "message type '%s' not '" PCMK__VALUE_RESPONSE "'",
215 : pcmk_ipc_name(api, true), pcmk__s(value, ""));
216 0 : status = CRM_EX_PROTOCOL;
217 0 : goto done;
218 : }
219 :
220 0 : if (pcmk__str_empty(crm_element_value(reply, PCMK_XA_REFERENCE))) {
221 0 : crm_info("Unrecognizable message from %s: no reference",
222 : pcmk_ipc_name(api, true));
223 0 : status = CRM_EX_PROTOCOL;
224 0 : goto done;
225 : }
226 :
227 0 : value = crm_element_value(reply, PCMK__XA_CRM_TASK);
228 :
229 : // Parse useful info from reply
230 0 : wrapper = pcmk__xe_first_child(reply, PCMK__XE_CRM_XML, NULL, NULL);
231 0 : msg_data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
232 :
233 0 : crm_element_value_ll(msg_data, PCMK_XA_CRM_TIMESTAMP, &value_ll);
234 :
235 0 : if (pcmk__str_eq(value, CRM_OP_PING, pcmk__str_none)) {
236 0 : reply_data.reply_type = pcmk_pacemakerd_reply_ping;
237 0 : reply_data.data.ping.state =
238 0 : pcmk_pacemakerd_api_daemon_state_text2enum(
239 : crm_element_value(msg_data, PCMK__XA_PACEMAKERD_STATE));
240 0 : reply_data.data.ping.status =
241 0 : pcmk__str_eq(crm_element_value(msg_data, PCMK_XA_RESULT), "ok",
242 0 : pcmk__str_casei)?pcmk_rc_ok:pcmk_rc_error;
243 0 : reply_data.data.ping.last_good = (value_ll < 0)? 0 : (time_t) value_ll;
244 0 : reply_data.data.ping.sys_from =
245 0 : crm_element_value(msg_data, PCMK__XA_CRM_SUBSYSTEM);
246 0 : } else if (pcmk__str_eq(value, CRM_OP_QUIT, pcmk__str_none)) {
247 0 : const char *op_status = crm_element_value(msg_data, PCMK__XA_OP_STATUS);
248 :
249 0 : reply_data.reply_type = pcmk_pacemakerd_reply_shutdown;
250 0 : reply_data.data.shutdown.status = atoi(op_status);
251 : } else {
252 0 : crm_info("Unrecognizable message from %s: unknown command '%s'",
253 : pcmk_ipc_name(api, true), pcmk__s(value, ""));
254 0 : status = CRM_EX_PROTOCOL;
255 0 : goto done;
256 : }
257 :
258 0 : done:
259 0 : pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data);
260 0 : return false;
261 : }
262 :
263 : pcmk__ipc_methods_t *
264 0 : pcmk__pacemakerd_api_methods(void)
265 : {
266 0 : pcmk__ipc_methods_t *cmds = calloc(1, sizeof(pcmk__ipc_methods_t));
267 :
268 0 : if (cmds != NULL) {
269 0 : cmds->new_data = new_data;
270 0 : cmds->free_data = free_data;
271 0 : cmds->post_connect = post_connect;
272 0 : cmds->reply_expected = reply_expected;
273 0 : cmds->dispatch = dispatch;
274 0 : cmds->post_disconnect = post_disconnect;
275 : }
276 0 : return cmds;
277 : }
278 :
279 : static int
280 0 : do_pacemakerd_api_call(pcmk_ipc_api_t *api, const char *ipc_name, const char *task)
281 : {
282 : pacemakerd_api_private_t *private;
283 : xmlNode *cmd;
284 : int rc;
285 :
286 0 : if (api == NULL) {
287 0 : return EINVAL;
288 : }
289 :
290 0 : private = api->api_data;
291 0 : CRM_ASSERT(private != NULL);
292 :
293 0 : cmd = create_request(task, NULL, NULL, CRM_SYSTEM_MCP,
294 : pcmk__ipc_sys_name(ipc_name, "client"),
295 : private->client_uuid);
296 :
297 0 : if (cmd) {
298 0 : rc = pcmk__send_ipc_request(api, cmd);
299 0 : if (rc != pcmk_rc_ok) {
300 0 : crm_debug("Couldn't send request to %s: %s rc=%d",
301 : pcmk_ipc_name(api, true), pcmk_rc_str(rc), rc);
302 : }
303 0 : free_xml(cmd);
304 : } else {
305 0 : rc = ENOMSG;
306 : }
307 :
308 0 : return rc;
309 : }
310 :
311 : int
312 0 : pcmk_pacemakerd_api_ping(pcmk_ipc_api_t *api, const char *ipc_name)
313 : {
314 0 : return do_pacemakerd_api_call(api, ipc_name, CRM_OP_PING);
315 : }
316 :
317 : int
318 0 : pcmk_pacemakerd_api_shutdown(pcmk_ipc_api_t *api, const char *ipc_name)
319 : {
320 0 : return do_pacemakerd_api_call(api, ipc_name, CRM_OP_QUIT);
321 : }
|