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 <libxml/tree.h> // xmlNode
13 :
14 : #include <pacemaker.h>
15 : #include <pacemaker-internal.h>
16 :
17 : #include <crm/crm.h>
18 : #include <crm/cib.h>
19 : #include <crm/cib/internal.h>
20 : #include <crm/common/output_internal.h>
21 : #include <crm/common/xml.h>
22 : #include <crm/common/xml_internal.h>
23 : #include <crm/common/iso8601.h>
24 : #include <crm/common/ipc_controld.h>
25 : #include <crm/common/ipc_pacemakerd.h>
26 :
27 : //! Object to store node info from the controller API
28 : typedef struct {
29 : /* Adapted from pcmk_controld_api_reply_t:data:node_info.
30 : * (char **) are convenient here for use within callbacks: we can skip
31 : * copying strings unless the caller passes a non-NULL value.
32 : */
33 : uint32_t id;
34 : char **node_name;
35 : char **uuid;
36 : char **state;
37 : bool have_quorum;
38 : bool is_remote;
39 : } node_info_t;
40 :
41 : //! Object to store API results, a timeout, and an output object
42 : typedef struct {
43 : pcmk__output_t *out;
44 : bool show_output;
45 : int rc;
46 : unsigned int message_timeout_ms;
47 : enum pcmk_pacemakerd_state pcmkd_state;
48 : node_info_t node_info;
49 : } data_t;
50 :
51 : /*!
52 : * \internal
53 : * \brief Validate that an IPC API event is a good reply
54 : *
55 : * \param[in,out] data API results and options
56 : * \param[in] api IPC API connection
57 : * \param[in] event_type Type of event that occurred
58 : * \param[in] status Event status
59 : *
60 : * \return Standard Pacemaker return code
61 : */
62 : static int
63 0 : validate_reply_event(data_t *data, const pcmk_ipc_api_t *api,
64 : enum pcmk_ipc_event event_type, crm_exit_t status)
65 : {
66 0 : pcmk__output_t *out = data->out;
67 :
68 0 : switch (event_type) {
69 0 : case pcmk_ipc_event_reply:
70 0 : break;
71 :
72 0 : case pcmk_ipc_event_disconnect:
73 0 : if (data->rc == ECONNRESET) { // Unexpected
74 0 : out->err(out, "error: Lost connection to %s",
75 : pcmk_ipc_name(api, true));
76 : }
77 : // Nothing bad but not the reply we're looking for
78 0 : return ENOTSUP;
79 :
80 0 : default:
81 : // Ditto
82 0 : return ENOTSUP;
83 : }
84 :
85 0 : if (status != CRM_EX_OK) {
86 0 : out->err(out, "error: Bad reply from %s: %s",
87 : pcmk_ipc_name(api, true), crm_exit_str(status));
88 0 : data->rc = EBADMSG;
89 0 : return data->rc;
90 : }
91 0 : return pcmk_rc_ok;
92 : }
93 :
94 : /*!
95 : * \internal
96 : * \brief Validate that a controller API event is a good reply of expected type
97 : *
98 : * \param[in,out] data API results and options
99 : * \param[in] api Controller connection
100 : * \param[in] event_type Type of event that occurred
101 : * \param[in] status Event status
102 : * \param[in] event_data Event-specific data
103 : * \param[in] expected_type Expected reply type
104 : *
105 : * \return Standard Pacemaker return code
106 : */
107 : static int
108 0 : validate_controld_reply(data_t *data, const pcmk_ipc_api_t *api,
109 : enum pcmk_ipc_event event_type, crm_exit_t status,
110 : const void *event_data,
111 : enum pcmk_controld_api_reply expected_type)
112 : {
113 0 : pcmk__output_t *out = data->out;
114 0 : int rc = pcmk_rc_ok;
115 0 : const pcmk_controld_api_reply_t *reply = NULL;
116 :
117 0 : rc = validate_reply_event(data, api, event_type, status);
118 0 : if (rc != pcmk_rc_ok) {
119 0 : return rc;
120 : }
121 :
122 0 : reply = (const pcmk_controld_api_reply_t *) event_data;
123 :
124 0 : if (reply->reply_type != expected_type) {
125 0 : out->err(out, "error: Unexpected reply type '%s' from controller",
126 0 : pcmk__controld_api_reply2str(reply->reply_type));
127 0 : data->rc = EBADMSG;
128 0 : return data->rc;
129 : }
130 :
131 0 : return pcmk_rc_ok;
132 : }
133 :
134 : /*!
135 : * \internal
136 : * \brief Validate that a \p pacemakerd API event is a good reply of expected
137 : * type
138 : *
139 : * \param[in,out] data API results and options
140 : * \param[in] api \p pacemakerd connection
141 : * \param[in] event_type Type of event that occurred
142 : * \param[in] status Event status
143 : * \param[in] event_data Event-specific data
144 : * \param[in] expected_type Expected reply type
145 : *
146 : * \return Standard Pacemaker return code
147 : */
148 : static int
149 0 : validate_pcmkd_reply(data_t *data, const pcmk_ipc_api_t *api,
150 : enum pcmk_ipc_event event_type, crm_exit_t status,
151 : const void *event_data,
152 : enum pcmk_pacemakerd_api_reply expected_type)
153 : {
154 0 : pcmk__output_t *out = data->out;
155 0 : const pcmk_pacemakerd_api_reply_t *reply = NULL;
156 0 : int rc = validate_reply_event(data, api, event_type, status);
157 :
158 0 : if (rc != pcmk_rc_ok) {
159 0 : return rc;
160 : }
161 :
162 0 : reply = (const pcmk_pacemakerd_api_reply_t *) event_data;
163 :
164 0 : if (reply->reply_type != expected_type) {
165 0 : out->err(out, "error: Unexpected reply type '%s' from pacemakerd",
166 0 : pcmk__pcmkd_api_reply2str(reply->reply_type));
167 0 : data->rc = EBADMSG;
168 0 : return data->rc;
169 : }
170 :
171 0 : return pcmk_rc_ok;
172 : }
173 :
174 : /*!
175 : * \internal
176 : * \brief Process a controller status IPC event
177 : *
178 : * \param[in,out] controld_api Controller connection
179 : * \param[in] event_type Type of event that occurred
180 : * \param[in] status Event status
181 : * \param[in,out] event_data \p pcmk_controld_api_reply_t object containing
182 : * event-specific data
183 : * \param[in,out] user_data \p data_t object for API results and options
184 : */
185 : static void
186 0 : controller_status_event_cb(pcmk_ipc_api_t *controld_api,
187 : enum pcmk_ipc_event event_type, crm_exit_t status,
188 : void *event_data, void *user_data)
189 : {
190 0 : data_t *data = (data_t *) user_data;
191 0 : pcmk__output_t *out = data->out;
192 0 : const pcmk_controld_api_reply_t *reply = NULL;
193 :
194 0 : int rc = validate_controld_reply(data, controld_api, event_type, status,
195 : event_data, pcmk_controld_reply_ping);
196 :
197 0 : if (rc != pcmk_rc_ok) {
198 0 : return;
199 : }
200 :
201 0 : reply = (const pcmk_controld_api_reply_t *) event_data;
202 0 : out->message(out, "health",
203 0 : reply->data.ping.sys_from, reply->host_from,
204 0 : reply->data.ping.fsa_state, reply->data.ping.result);
205 0 : data->rc = pcmk_rc_ok;
206 : }
207 :
208 : /*!
209 : * \internal
210 : * \brief Process a designated controller IPC event
211 : *
212 : * \param[in,out] controld_api Controller connection
213 : * \param[in] event_type Type of event that occurred
214 : * \param[in] status Event status
215 : * \param[in,out] event_data \p pcmk_controld_api_reply_t object containing
216 : * event-specific data
217 : * \param[in,out] user_data \p data_t object for API results and options
218 : */
219 : static void
220 0 : designated_controller_event_cb(pcmk_ipc_api_t *controld_api,
221 : enum pcmk_ipc_event event_type,
222 : crm_exit_t status, void *event_data,
223 : void *user_data)
224 : {
225 0 : data_t *data = (data_t *) user_data;
226 0 : pcmk__output_t *out = data->out;
227 0 : const pcmk_controld_api_reply_t *reply = NULL;
228 :
229 0 : int rc = validate_controld_reply(data, controld_api, event_type, status,
230 : event_data, pcmk_controld_reply_ping);
231 :
232 0 : if (rc != pcmk_rc_ok) {
233 0 : return;
234 : }
235 :
236 0 : reply = (const pcmk_controld_api_reply_t *) event_data;
237 0 : out->message(out, "dc", reply->host_from);
238 0 : data->rc = pcmk_rc_ok;
239 : }
240 :
241 : /*!
242 : * \internal
243 : * \brief Process a node info IPC event
244 : *
245 : * \param[in,out] controld_api Controller connection
246 : * \param[in] event_type Type of event that occurred
247 : * \param[in] status Event status
248 : * \param[in,out] event_data \p pcmk_controld_api_reply_t object containing
249 : * event-specific data
250 : * \param[in,out] user_data \p data_t object for API results and options
251 : */
252 : static void
253 0 : node_info_event_cb(pcmk_ipc_api_t *controld_api, enum pcmk_ipc_event event_type,
254 : crm_exit_t status, void *event_data, void *user_data)
255 : {
256 0 : data_t *data = (data_t *) user_data;
257 0 : pcmk__output_t *out = data->out;
258 :
259 0 : const pcmk_controld_api_reply_t *reply = NULL;
260 :
261 0 : int rc = validate_controld_reply(data, controld_api, event_type, status,
262 : event_data, pcmk_controld_reply_info);
263 :
264 0 : if (rc != pcmk_rc_ok) {
265 0 : return;
266 : }
267 :
268 0 : reply = (const pcmk_controld_api_reply_t *) event_data;
269 :
270 0 : if (reply->data.node_info.uname == NULL) {
271 0 : out->err(out, "Node is not known to cluster");
272 0 : data->rc = pcmk_rc_node_unknown;
273 0 : return;
274 : }
275 :
276 0 : data->node_info.have_quorum = reply->data.node_info.have_quorum;
277 0 : data->node_info.is_remote = reply->data.node_info.is_remote;
278 0 : data->node_info.id = (uint32_t) reply->data.node_info.id;
279 :
280 0 : pcmk__str_update(data->node_info.node_name, reply->data.node_info.uname);
281 0 : pcmk__str_update(data->node_info.uuid, reply->data.node_info.uuid);
282 0 : pcmk__str_update(data->node_info.state, reply->data.node_info.state);
283 :
284 0 : if (data->show_output) {
285 0 : out->message(out, "node-info",
286 0 : (uint32_t) reply->data.node_info.id, reply->data.node_info.uname,
287 0 : reply->data.node_info.uuid, reply->data.node_info.state,
288 0 : reply->data.node_info.have_quorum,
289 0 : reply->data.node_info.is_remote);
290 : }
291 :
292 0 : data->rc = pcmk_rc_ok;
293 : }
294 :
295 : /*!
296 : * \internal
297 : * \brief Process a \p pacemakerd status IPC event
298 : *
299 : * \param[in,out] pacemakerd_api \p pacemakerd connection
300 : * \param[in] event_type Type of event that occurred
301 : * \param[in] status Event status
302 : * \param[in,out] event_data \p pcmk_pacemakerd_api_reply_t object
303 : * containing event-specific data
304 : * \param[in,out] user_data \p data_t object for API results and options
305 : */
306 : static void
307 0 : pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
308 : enum pcmk_ipc_event event_type, crm_exit_t status,
309 : void *event_data, void *user_data)
310 : {
311 0 : data_t *data = user_data;
312 0 : pcmk__output_t *out = data->out;
313 0 : const pcmk_pacemakerd_api_reply_t *reply = NULL;
314 :
315 0 : int rc = validate_pcmkd_reply(data, pacemakerd_api, event_type, status,
316 : event_data, pcmk_pacemakerd_reply_ping);
317 :
318 0 : if (rc != pcmk_rc_ok) {
319 0 : return;
320 : }
321 :
322 : // Parse desired information from reply
323 0 : reply = (const pcmk_pacemakerd_api_reply_t *) event_data;
324 :
325 0 : data->pcmkd_state = reply->data.ping.state;
326 0 : data->rc = pcmk_rc_ok;
327 :
328 0 : if (!data->show_output) {
329 0 : return;
330 : }
331 :
332 0 : if (reply->data.ping.status == pcmk_rc_ok) {
333 0 : out->message(out, "pacemakerd-health",
334 0 : reply->data.ping.sys_from, reply->data.ping.state, NULL,
335 0 : reply->data.ping.last_good);
336 : } else {
337 0 : out->message(out, "pacemakerd-health",
338 0 : reply->data.ping.sys_from, reply->data.ping.state,
339 : "query failed", time(NULL));
340 : }
341 : }
342 :
343 : static pcmk_ipc_api_t *
344 0 : ipc_connect(data_t *data, enum pcmk_ipc_server server, pcmk_ipc_callback_t cb,
345 : enum pcmk_ipc_dispatch dispatch_type, bool eremoteio_ok)
346 : {
347 : int rc;
348 0 : pcmk__output_t *out = data->out;
349 0 : pcmk_ipc_api_t *api = NULL;
350 :
351 0 : rc = pcmk_new_ipc_api(&api, server);
352 0 : if (api == NULL) {
353 0 : out->err(out, "error: Could not connect to %s: %s",
354 : pcmk_ipc_name(api, true),
355 : pcmk_rc_str(rc));
356 0 : data->rc = rc;
357 0 : return NULL;
358 : }
359 0 : if (cb != NULL) {
360 0 : pcmk_register_ipc_callback(api, cb, data);
361 : }
362 :
363 0 : rc = pcmk__connect_ipc(api, dispatch_type, 5);
364 0 : if (rc != pcmk_rc_ok) {
365 0 : if (rc == EREMOTEIO) {
366 0 : data->pcmkd_state = pcmk_pacemakerd_state_remote;
367 0 : if (eremoteio_ok) {
368 : /* EREMOTEIO may be expected and acceptable for some callers
369 : * on a Pacemaker Remote node
370 : */
371 0 : crm_debug("Ignoring %s connection failure: No "
372 : "Pacemaker Remote connection",
373 : pcmk_ipc_name(api, true));
374 0 : rc = pcmk_rc_ok;
375 : } else {
376 0 : out->err(out, "error: Could not connect to %s: %s",
377 : pcmk_ipc_name(api, true), pcmk_rc_str(rc));
378 : }
379 : }
380 0 : data->rc = rc;
381 0 : pcmk_free_ipc_api(api);
382 0 : return NULL;
383 : }
384 :
385 0 : return api;
386 : }
387 :
388 : /*!
389 : * \internal
390 : * \brief Poll an IPC API connection until timeout or a reply is received
391 : *
392 : * \param[in,out] data API results and options
393 : * \param[in,out] api IPC API connection
394 : * \param[in] on_node If not \p NULL, name of the node to poll (used only
395 : * for logging)
396 : *
397 : * \note Sets the \p rc member of \p data on error
398 : */
399 : static void
400 0 : poll_until_reply(data_t *data, pcmk_ipc_api_t *api, const char *on_node)
401 : {
402 0 : pcmk__output_t *out = data->out;
403 :
404 0 : uint64_t start_nsec = qb_util_nano_current_get();
405 0 : uint64_t end_nsec = 0;
406 0 : uint64_t elapsed_ms = 0;
407 0 : uint64_t remaining_ms = data->message_timeout_ms;
408 :
409 0 : while (remaining_ms > 0) {
410 0 : int rc = pcmk_poll_ipc(api, remaining_ms);
411 :
412 0 : if (rc == EAGAIN) {
413 : // Poll timed out
414 0 : break;
415 : }
416 :
417 0 : if (rc != pcmk_rc_ok) {
418 0 : out->err(out, "error: Failed to poll %s API%s%s: %s",
419 : pcmk_ipc_name(api, true), (on_node != NULL)? " on " : "",
420 : pcmk__s(on_node, ""), pcmk_rc_str(rc));
421 0 : data->rc = rc;
422 0 : return;
423 : }
424 :
425 0 : pcmk_dispatch_ipc(api);
426 :
427 0 : if (data->rc != EAGAIN) {
428 : // Received a reply
429 0 : return;
430 : }
431 0 : end_nsec = qb_util_nano_current_get();
432 0 : elapsed_ms = (end_nsec - start_nsec) / QB_TIME_NS_IN_MSEC;
433 0 : remaining_ms = data->message_timeout_ms - elapsed_ms;
434 : }
435 :
436 0 : out->err(out,
437 : "error: Timed out after %ums waiting for reply from %s API%s%s",
438 : data->message_timeout_ms, pcmk_ipc_name(api, true),
439 : (on_node != NULL)? " on " : "", pcmk__s(on_node, ""));
440 0 : data->rc = EAGAIN;
441 : }
442 :
443 : /*!
444 : * \internal
445 : * \brief Get and output controller status
446 : *
447 : * \param[in,out] out Output object
448 : * \param[in] node_name Name of node whose status is desired
449 : * (\p NULL for DC)
450 : * \param[in] message_timeout_ms How long to wait for a reply from the
451 : * \p pacemaker-controld API. If 0,
452 : * \p pcmk_ipc_dispatch_sync will be used.
453 : * Otherwise, \p pcmk_ipc_dispatch_poll will
454 : * be used.
455 : *
456 : * \return Standard Pacemaker return code
457 : */
458 : int
459 0 : pcmk__controller_status(pcmk__output_t *out, const char *node_name,
460 : unsigned int message_timeout_ms)
461 : {
462 0 : data_t data = {
463 : .out = out,
464 : .rc = EAGAIN,
465 : .message_timeout_ms = message_timeout_ms,
466 : };
467 0 : enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
468 0 : pcmk_ipc_api_t *controld_api = NULL;
469 :
470 0 : if (message_timeout_ms == 0) {
471 0 : dispatch_type = pcmk_ipc_dispatch_sync;
472 : }
473 0 : controld_api = ipc_connect(&data, pcmk_ipc_controld,
474 : controller_status_event_cb, dispatch_type,
475 : false);
476 :
477 0 : if (controld_api != NULL) {
478 0 : int rc = pcmk_controld_api_ping(controld_api, node_name);
479 0 : if (rc != pcmk_rc_ok) {
480 0 : out->err(out, "error: Could not ping controller API on %s: %s",
481 : pcmk__s(node_name, "DC"), pcmk_rc_str(rc));
482 0 : data.rc = rc;
483 : }
484 :
485 0 : if (dispatch_type == pcmk_ipc_dispatch_poll) {
486 0 : poll_until_reply(&data, controld_api, pcmk__s(node_name, "DC"));
487 : }
488 0 : pcmk_free_ipc_api(controld_api);
489 : }
490 :
491 0 : return data.rc;
492 : }
493 :
494 :
495 : // Documented in header
496 : int
497 0 : pcmk_controller_status(xmlNodePtr *xml, const char *node_name,
498 : unsigned int message_timeout_ms)
499 : {
500 0 : pcmk__output_t *out = NULL;
501 0 : int rc = pcmk_rc_ok;
502 :
503 0 : rc = pcmk__xml_output_new(&out, xml);
504 0 : if (rc != pcmk_rc_ok) {
505 0 : return rc;
506 : }
507 :
508 0 : pcmk__register_lib_messages(out);
509 :
510 0 : rc = pcmk__controller_status(out, node_name, message_timeout_ms);
511 0 : pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
512 0 : return rc;
513 : }
514 :
515 : /*!
516 : * \internal
517 : * \brief Get and output designated controller node name
518 : *
519 : * \param[in,out] out Output object
520 : * \param[in] message_timeout_ms How long to wait for a reply from the
521 : * \p pacemaker-controld API. If 0,
522 : * \p pcmk_ipc_dispatch_sync will be used.
523 : * Otherwise, \p pcmk_ipc_dispatch_poll will
524 : * be used.
525 : *
526 : * \return Standard Pacemaker return code
527 : */
528 : int
529 0 : pcmk__designated_controller(pcmk__output_t *out,
530 : unsigned int message_timeout_ms)
531 : {
532 0 : data_t data = {
533 : .out = out,
534 : .rc = EAGAIN,
535 : .message_timeout_ms = message_timeout_ms,
536 : };
537 0 : enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
538 0 : pcmk_ipc_api_t *controld_api = NULL;
539 :
540 0 : if (message_timeout_ms == 0) {
541 0 : dispatch_type = pcmk_ipc_dispatch_sync;
542 : }
543 0 : controld_api = ipc_connect(&data, pcmk_ipc_controld,
544 : designated_controller_event_cb, dispatch_type,
545 : false);
546 :
547 0 : if (controld_api != NULL) {
548 0 : int rc = pcmk_controld_api_ping(controld_api, NULL);
549 0 : if (rc != pcmk_rc_ok) {
550 0 : out->err(out, "error: Could not ping controller API on DC: %s",
551 : pcmk_rc_str(rc));
552 0 : data.rc = rc;
553 : }
554 :
555 0 : if (dispatch_type == pcmk_ipc_dispatch_poll) {
556 0 : poll_until_reply(&data, controld_api, "DC");
557 : }
558 0 : pcmk_free_ipc_api(controld_api);
559 : }
560 :
561 0 : return data.rc;
562 : }
563 :
564 : // Documented in header
565 : int
566 0 : pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms)
567 : {
568 0 : pcmk__output_t *out = NULL;
569 0 : int rc = pcmk_rc_ok;
570 :
571 0 : rc = pcmk__xml_output_new(&out, xml);
572 0 : if (rc != pcmk_rc_ok) {
573 0 : return rc;
574 : }
575 :
576 0 : pcmk__register_lib_messages(out);
577 :
578 0 : rc = pcmk__designated_controller(out, message_timeout_ms);
579 0 : pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
580 0 : return rc;
581 : }
582 :
583 : /*!
584 : * \internal
585 : * \brief Get and optionally output node info corresponding to a node ID from
586 : * the controller
587 : *
588 : * \param[in,out] out Output object
589 : * \param[in,out] node_id ID of node whose name to get. If \p NULL
590 : * or 0, get the local node name. If not
591 : * \p NULL, store the true node ID here on
592 : * success.
593 : * \param[out] node_name If not \p NULL, where to store the node
594 : * name
595 : * \param[out] uuid If not \p NULL, where to store the node
596 : * UUID
597 : * \param[out] state If not \p NULL, where to store the
598 : * membership state
599 : * \param[out] is_remote If not \p NULL, where to store whether the
600 : * node is a Pacemaker Remote node
601 : * \param[out] have_quorum If not \p NULL, where to store whether the
602 : * node has quorum
603 : * \param[in] show_output Whether to show the node info
604 : * \param[in] message_timeout_ms How long to wait for a reply from the
605 : * \p pacemaker-controld API. If 0,
606 : * \p pcmk_ipc_dispatch_sync will be used.
607 : * Otherwise, \p pcmk_ipc_dispatch_poll will
608 : * be used.
609 : *
610 : * \return Standard Pacemaker return code
611 : *
612 : * \note The caller is responsible for freeing \p *node_name, \p *uuid, and
613 : * \p *state using \p free().
614 : */
615 : int
616 0 : pcmk__query_node_info(pcmk__output_t *out, uint32_t *node_id, char **node_name,
617 : char **uuid, char **state, bool *have_quorum,
618 : bool *is_remote, bool show_output,
619 : unsigned int message_timeout_ms)
620 : {
621 0 : data_t data = {
622 : .out = out,
623 : .show_output = show_output,
624 : .rc = EAGAIN,
625 : .message_timeout_ms = message_timeout_ms,
626 : .node_info = {
627 0 : .id = (node_id == NULL)? 0 : *node_id,
628 : .node_name = node_name,
629 : .uuid = uuid,
630 : .state = state,
631 : },
632 : };
633 0 : enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
634 0 : pcmk_ipc_api_t *controld_api = NULL;
635 :
636 0 : if (node_name != NULL) {
637 0 : *node_name = NULL;
638 : }
639 0 : if (uuid != NULL) {
640 0 : *uuid = NULL;
641 : }
642 0 : if (state != NULL) {
643 0 : *state = NULL;
644 : }
645 :
646 0 : if (message_timeout_ms == 0) {
647 0 : dispatch_type = pcmk_ipc_dispatch_sync;
648 : }
649 0 : controld_api = ipc_connect(&data, pcmk_ipc_controld, node_info_event_cb,
650 : dispatch_type, false);
651 :
652 0 : if (controld_api != NULL) {
653 0 : int rc = pcmk_controld_api_node_info(controld_api,
654 : (node_id != NULL)? *node_id : 0);
655 :
656 0 : if (rc != pcmk_rc_ok) {
657 0 : out->err(out,
658 : "error: Could not send request to controller API on local "
659 : "node: %s", pcmk_rc_str(rc));
660 0 : data.rc = rc;
661 : }
662 :
663 0 : if (dispatch_type == pcmk_ipc_dispatch_poll) {
664 0 : poll_until_reply(&data, controld_api, "local node");
665 : }
666 0 : pcmk_free_ipc_api(controld_api);
667 : }
668 :
669 0 : if (data.rc != pcmk_rc_ok) {
670 0 : return data.rc;
671 : }
672 :
673 : // String outputs are set in callback
674 0 : if (node_id != NULL) {
675 0 : *node_id = data.node_info.id;
676 : }
677 0 : if (have_quorum != NULL) {
678 0 : *have_quorum = data.node_info.have_quorum;
679 : }
680 0 : if (is_remote != NULL) {
681 0 : *is_remote = data.node_info.is_remote;
682 : }
683 :
684 0 : return data.rc;
685 : }
686 :
687 : // Documented in header
688 : int
689 0 : pcmk_query_node_info(xmlNodePtr *xml, uint32_t *node_id, char **node_name,
690 : char **uuid, char **state, bool *have_quorum,
691 : bool *is_remote, bool show_output,
692 : unsigned int message_timeout_ms)
693 : {
694 0 : pcmk__output_t *out = NULL;
695 0 : int rc = pcmk_rc_ok;
696 :
697 0 : CRM_ASSERT(node_name != NULL);
698 :
699 0 : rc = pcmk__xml_output_new(&out, xml);
700 0 : if (rc != pcmk_rc_ok) {
701 0 : return rc;
702 : }
703 :
704 0 : pcmk__register_lib_messages(out);
705 :
706 0 : rc = pcmk__query_node_info(out, node_id, node_name, uuid, state,
707 : have_quorum, is_remote, show_output,
708 : message_timeout_ms);
709 0 : pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
710 0 : return rc;
711 : }
712 :
713 : /*!
714 : * \internal
715 : * \brief Get and optionally output \p pacemakerd status
716 : *
717 : * \param[in,out] out Output object
718 : * \param[in] ipc_name IPC name for request
719 : * \param[in] message_timeout_ms How long to wait for a reply from the
720 : * \p pacemakerd API. If 0,
721 : * \p pcmk_ipc_dispatch_sync will be used.
722 : * Otherwise, \p pcmk_ipc_dispatch_poll will
723 : * be used.
724 : * \param[in] show_output Whether to output the \p pacemakerd state
725 : * \param[out] state Where to store the \p pacemakerd state, if
726 : * not \p NULL
727 : *
728 : * \return Standard Pacemaker return code
729 : *
730 : * \note This function sets \p state to \p pcmk_pacemakerd_state_remote and
731 : * returns \p pcmk_rc_ok if the IPC connection attempt returns
732 : * \p EREMOTEIO. That code indicates that this is a Pacemaker Remote node
733 : * with \p pacemaker-remoted running. The node may be connected to the
734 : * cluster.
735 : */
736 : int
737 0 : pcmk__pacemakerd_status(pcmk__output_t *out, const char *ipc_name,
738 : unsigned int message_timeout_ms, bool show_output,
739 : enum pcmk_pacemakerd_state *state)
740 : {
741 0 : data_t data = {
742 : .out = out,
743 : .show_output = show_output,
744 : .rc = EAGAIN,
745 : .message_timeout_ms = message_timeout_ms,
746 : .pcmkd_state = pcmk_pacemakerd_state_invalid,
747 : };
748 0 : enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
749 0 : pcmk_ipc_api_t *pacemakerd_api = NULL;
750 :
751 0 : if (message_timeout_ms == 0) {
752 0 : dispatch_type = pcmk_ipc_dispatch_sync;
753 : }
754 0 : pacemakerd_api = ipc_connect(&data, pcmk_ipc_pacemakerd,
755 : pacemakerd_event_cb, dispatch_type, true);
756 :
757 0 : if (pacemakerd_api != NULL) {
758 0 : int rc = pcmk_pacemakerd_api_ping(pacemakerd_api, ipc_name);
759 0 : if (rc != pcmk_rc_ok) {
760 0 : out->err(out, "error: Could not ping launcher API: %s",
761 : pcmk_rc_str(rc));
762 0 : data.rc = rc;
763 : }
764 :
765 0 : if (dispatch_type == pcmk_ipc_dispatch_poll) {
766 0 : poll_until_reply(&data, pacemakerd_api, NULL);
767 : }
768 0 : pcmk_free_ipc_api(pacemakerd_api);
769 :
770 0 : } else if ((data.pcmkd_state == pcmk_pacemakerd_state_remote)
771 0 : && show_output) {
772 : // No API connection so the callback wasn't run
773 0 : out->message(out, "pacemakerd-health",
774 0 : NULL, data.pcmkd_state, NULL, time(NULL));
775 : }
776 :
777 0 : if (state != NULL) {
778 0 : *state = data.pcmkd_state;
779 : }
780 0 : return data.rc;
781 : }
782 :
783 : // Documented in header
784 : int
785 0 : pcmk_pacemakerd_status(xmlNodePtr *xml, const char *ipc_name,
786 : unsigned int message_timeout_ms)
787 : {
788 0 : pcmk__output_t *out = NULL;
789 0 : int rc = pcmk_rc_ok;
790 :
791 0 : rc = pcmk__xml_output_new(&out, xml);
792 0 : if (rc != pcmk_rc_ok) {
793 0 : return rc;
794 : }
795 :
796 0 : pcmk__register_lib_messages(out);
797 :
798 0 : rc = pcmk__pacemakerd_status(out, ipc_name, message_timeout_ms, true, NULL);
799 0 : pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
800 0 : return rc;
801 : }
802 :
803 : /* user data for looping through remote node xpath searches */
804 : struct node_data {
805 : pcmk__output_t *out;
806 : int found;
807 : const char *field; /* XML attribute to check for node name */
808 : const char *type;
809 : bool bash_export;
810 : };
811 :
812 : static void
813 0 : remote_node_print_helper(xmlNode *result, void *user_data)
814 : {
815 0 : struct node_data *data = user_data;
816 0 : pcmk__output_t *out = data->out;
817 0 : const char *name = crm_element_value(result, PCMK_XA_UNAME);
818 0 : const char *id = crm_element_value(result, data->field);
819 :
820 : // node name and node id are the same for remote/guest nodes
821 0 : out->message(out, "crmadmin-node", data->type,
822 0 : pcmk__s(name, id), id, data->bash_export);
823 0 : data->found++;
824 0 : }
825 :
826 : // \return Standard Pacemaker return code
827 : int
828 0 : pcmk__list_nodes(pcmk__output_t *out, const char *node_types, bool bash_export)
829 : {
830 0 : xmlNode *xml_node = NULL;
831 : int rc;
832 :
833 0 : rc = cib__signon_query(out, NULL, &xml_node);
834 :
835 0 : if (rc == pcmk_rc_ok) {
836 0 : struct node_data data = {
837 : .out = out,
838 : .found = 0,
839 : .bash_export = bash_export
840 : };
841 :
842 : /* PCMK_XE_NODES acts as the list's element name for CLI tools that
843 : * use pcmk__output_enable_list_element. Otherwise PCMK_XE_NODES is
844 : * the value of the list's PCMK_XA_NAME attribute.
845 : */
846 0 : out->begin_list(out, NULL, NULL, PCMK_XE_NODES);
847 :
848 0 : if (!pcmk__str_empty(node_types) && strstr(node_types, "all")) {
849 0 : node_types = NULL;
850 : }
851 :
852 0 : if (pcmk__str_empty(node_types) || strstr(node_types, "cluster")) {
853 0 : data.field = PCMK_XA_ID;
854 0 : data.type = "cluster";
855 0 : crm_foreach_xpath_result(xml_node, PCMK__XP_MEMBER_NODE_CONFIG,
856 : remote_node_print_helper, &data);
857 : }
858 :
859 0 : if (pcmk__str_empty(node_types) || strstr(node_types, "guest")) {
860 0 : data.field = PCMK_XA_VALUE;
861 0 : data.type = "guest";
862 0 : crm_foreach_xpath_result(xml_node, PCMK__XP_GUEST_NODE_CONFIG,
863 : remote_node_print_helper, &data);
864 : }
865 :
866 0 : if (pcmk__str_empty(node_types)
867 0 : || pcmk__str_eq(node_types, ",|^remote", pcmk__str_regex)) {
868 0 : data.field = PCMK_XA_ID;
869 0 : data.type = "remote";
870 0 : crm_foreach_xpath_result(xml_node, PCMK__XP_REMOTE_NODE_CONFIG,
871 : remote_node_print_helper, &data);
872 : }
873 :
874 0 : out->end_list(out);
875 :
876 0 : if (data.found == 0) {
877 0 : out->info(out, "No nodes configured");
878 : }
879 :
880 0 : free_xml(xml_node);
881 : }
882 :
883 0 : return rc;
884 : }
885 :
886 : int
887 0 : pcmk_list_nodes(xmlNodePtr *xml, const char *node_types)
888 : {
889 0 : pcmk__output_t *out = NULL;
890 0 : int rc = pcmk_rc_ok;
891 :
892 0 : rc = pcmk__xml_output_new(&out, xml);
893 0 : if (rc != pcmk_rc_ok) {
894 0 : return rc;
895 : }
896 :
897 0 : pcmk__register_lib_messages(out);
898 :
899 0 : rc = pcmk__list_nodes(out, node_types, FALSE);
900 0 : pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
901 0 : return rc;
902 : }
|