This source file includes following definitions.
- validate_reply_event
- validate_controld_reply
- validate_pcmkd_reply
- controller_status_event_cb
- designated_controller_event_cb
- node_info_event_cb
- pacemakerd_event_cb
- ipc_connect
- poll_until_reply
- pcmk__controller_status
- pcmk_controller_status
- pcmk__designated_controller
- pcmk_designated_controller
- pcmk__query_node_info
- pcmk_query_node_info
- pcmk__pacemakerd_status
- pcmk_pacemakerd_status
- remote_node_print_helper
- pcmk__list_nodes
- pcmk_list_nodes
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <libxml/tree.h>
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
28 typedef struct {
29
30
31
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
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
53
54
55
56
57
58
59
60
61
62 static int
63 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 pcmk__output_t *out = data->out;
67
68 switch (event_type) {
69 case pcmk_ipc_event_reply:
70 break;
71
72 case pcmk_ipc_event_disconnect:
73 if (data->rc == ECONNRESET) {
74 out->err(out, "error: Lost connection to %s",
75 pcmk_ipc_name(api, true));
76 }
77
78 return ENOTSUP;
79
80 default:
81
82 return ENOTSUP;
83 }
84
85 if (status != CRM_EX_OK) {
86 out->err(out, "error: Bad reply from %s: %s",
87 pcmk_ipc_name(api, true), crm_exit_str(status));
88 data->rc = EBADMSG;
89 return data->rc;
90 }
91 return pcmk_rc_ok;
92 }
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107 static int
108 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 pcmk__output_t *out = data->out;
114 int rc = pcmk_rc_ok;
115 const pcmk_controld_api_reply_t *reply = NULL;
116
117 rc = validate_reply_event(data, api, event_type, status);
118 if (rc != pcmk_rc_ok) {
119 return rc;
120 }
121
122 reply = (const pcmk_controld_api_reply_t *) event_data;
123
124 if (reply->reply_type != expected_type) {
125 out->err(out, "error: Unexpected reply type '%s' from controller",
126 pcmk__controld_api_reply2str(reply->reply_type));
127 data->rc = EBADMSG;
128 return data->rc;
129 }
130
131 return pcmk_rc_ok;
132 }
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148 static int
149 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 pcmk__output_t *out = data->out;
155 const pcmk_pacemakerd_api_reply_t *reply = NULL;
156 int rc = validate_reply_event(data, api, event_type, status);
157
158 if (rc != pcmk_rc_ok) {
159 return rc;
160 }
161
162 reply = (const pcmk_pacemakerd_api_reply_t *) event_data;
163
164 if (reply->reply_type != expected_type) {
165 out->err(out, "error: Unexpected reply type '%s' from pacemakerd",
166 pcmk__pcmkd_api_reply2str(reply->reply_type));
167 data->rc = EBADMSG;
168 return data->rc;
169 }
170
171 return pcmk_rc_ok;
172 }
173
174
175
176
177
178
179
180
181
182
183
184
185 static void
186 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 data_t *data = (data_t *) user_data;
191 pcmk__output_t *out = data->out;
192 const pcmk_controld_api_reply_t *reply = NULL;
193
194 int rc = validate_controld_reply(data, controld_api, event_type, status,
195 event_data, pcmk_controld_reply_ping);
196
197 if (rc != pcmk_rc_ok) {
198 return;
199 }
200
201 reply = (const pcmk_controld_api_reply_t *) event_data;
202 out->message(out, "health",
203 reply->data.ping.sys_from, reply->host_from,
204 reply->data.ping.fsa_state, reply->data.ping.result);
205 data->rc = pcmk_rc_ok;
206 }
207
208
209
210
211
212
213
214
215
216
217
218
219 static void
220 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 data_t *data = (data_t *) user_data;
226 pcmk__output_t *out = data->out;
227 const pcmk_controld_api_reply_t *reply = NULL;
228
229 int rc = validate_controld_reply(data, controld_api, event_type, status,
230 event_data, pcmk_controld_reply_ping);
231
232 if (rc != pcmk_rc_ok) {
233 return;
234 }
235
236 reply = (const pcmk_controld_api_reply_t *) event_data;
237 out->message(out, "dc", reply->host_from);
238 data->rc = pcmk_rc_ok;
239 }
240
241
242
243
244
245
246
247
248
249
250
251
252 static void
253 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 data_t *data = (data_t *) user_data;
257 pcmk__output_t *out = data->out;
258
259 const pcmk_controld_api_reply_t *reply = NULL;
260
261 int rc = validate_controld_reply(data, controld_api, event_type, status,
262 event_data, pcmk_controld_reply_info);
263
264 if (rc != pcmk_rc_ok) {
265 return;
266 }
267
268 reply = (const pcmk_controld_api_reply_t *) event_data;
269
270 if (reply->data.node_info.uname == NULL) {
271 out->err(out, "Node is not known to cluster");
272 data->rc = pcmk_rc_node_unknown;
273 return;
274 }
275
276 data->node_info.have_quorum = reply->data.node_info.have_quorum;
277 data->node_info.is_remote = reply->data.node_info.is_remote;
278 data->node_info.id = (uint32_t) reply->data.node_info.id;
279
280 pcmk__str_update(data->node_info.node_name, reply->data.node_info.uname);
281 pcmk__str_update(data->node_info.uuid, reply->data.node_info.uuid);
282 pcmk__str_update(data->node_info.state, reply->data.node_info.state);
283
284 if (data->show_output) {
285 out->message(out, "node-info",
286 (uint32_t) reply->data.node_info.id, reply->data.node_info.uname,
287 reply->data.node_info.uuid, reply->data.node_info.state,
288 reply->data.node_info.have_quorum,
289 reply->data.node_info.is_remote);
290 }
291
292 data->rc = pcmk_rc_ok;
293 }
294
295
296
297
298
299
300
301
302
303
304
305
306 static void
307 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 data_t *data = user_data;
312 pcmk__output_t *out = data->out;
313 const pcmk_pacemakerd_api_reply_t *reply = NULL;
314
315 int rc = validate_pcmkd_reply(data, pacemakerd_api, event_type, status,
316 event_data, pcmk_pacemakerd_reply_ping);
317
318 if (rc != pcmk_rc_ok) {
319 return;
320 }
321
322
323 reply = (const pcmk_pacemakerd_api_reply_t *) event_data;
324
325 data->pcmkd_state = reply->data.ping.state;
326 data->rc = pcmk_rc_ok;
327
328 if (!data->show_output) {
329 return;
330 }
331
332 if (reply->data.ping.status == pcmk_rc_ok) {
333 out->message(out, "pacemakerd-health",
334 reply->data.ping.sys_from, reply->data.ping.state, NULL,
335 reply->data.ping.last_good);
336 } else {
337 out->message(out, "pacemakerd-health",
338 reply->data.ping.sys_from, reply->data.ping.state,
339 "query failed", time(NULL));
340 }
341 }
342
343 static pcmk_ipc_api_t *
344 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 pcmk__output_t *out = data->out;
349 pcmk_ipc_api_t *api = NULL;
350
351 rc = pcmk_new_ipc_api(&api, server);
352 if (api == NULL) {
353 out->err(out, "error: Could not connect to %s: %s",
354 pcmk_ipc_name(api, true),
355 pcmk_rc_str(rc));
356 data->rc = rc;
357 return NULL;
358 }
359 if (cb != NULL) {
360 pcmk_register_ipc_callback(api, cb, data);
361 }
362
363 rc = pcmk__connect_ipc(api, dispatch_type, 5);
364 if (rc != pcmk_rc_ok) {
365 if (rc == EREMOTEIO) {
366 data->pcmkd_state = pcmk_pacemakerd_state_remote;
367 if (eremoteio_ok) {
368
369
370
371 crm_debug("Ignoring %s connection failure: No "
372 "Pacemaker Remote connection",
373 pcmk_ipc_name(api, true));
374 rc = pcmk_rc_ok;
375 } else {
376 out->err(out, "error: Could not connect to %s: %s",
377 pcmk_ipc_name(api, true), pcmk_rc_str(rc));
378 }
379 }
380 data->rc = rc;
381 pcmk_free_ipc_api(api);
382 return NULL;
383 }
384
385 return api;
386 }
387
388
389
390
391
392
393
394
395
396
397
398
399 static void
400 poll_until_reply(data_t *data, pcmk_ipc_api_t *api, const char *on_node)
401 {
402 pcmk__output_t *out = data->out;
403
404 uint64_t start_nsec = qb_util_nano_current_get();
405 uint64_t end_nsec = 0;
406 uint64_t elapsed_ms = 0;
407 uint64_t remaining_ms = data->message_timeout_ms;
408
409 while (remaining_ms > 0) {
410 int rc = pcmk_poll_ipc(api, remaining_ms);
411
412 if (rc == EAGAIN) {
413
414 break;
415 }
416
417 if (rc != pcmk_rc_ok) {
418 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 data->rc = rc;
422 return;
423 }
424
425 pcmk_dispatch_ipc(api);
426
427 if (data->rc != EAGAIN) {
428
429 return;
430 }
431 end_nsec = qb_util_nano_current_get();
432 elapsed_ms = (end_nsec - start_nsec) / QB_TIME_NS_IN_MSEC;
433 remaining_ms = data->message_timeout_ms - elapsed_ms;
434 }
435
436 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 data->rc = EAGAIN;
441 }
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458 int
459 pcmk__controller_status(pcmk__output_t *out, const char *node_name,
460 unsigned int message_timeout_ms)
461 {
462 data_t data = {
463 .out = out,
464 .rc = EAGAIN,
465 .message_timeout_ms = message_timeout_ms,
466 };
467 enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
468 pcmk_ipc_api_t *controld_api = NULL;
469
470 if (message_timeout_ms == 0) {
471 dispatch_type = pcmk_ipc_dispatch_sync;
472 }
473 controld_api = ipc_connect(&data, pcmk_ipc_controld,
474 controller_status_event_cb, dispatch_type,
475 false);
476
477 if (controld_api != NULL) {
478 int rc = pcmk_controld_api_ping(controld_api, node_name);
479 if (rc != pcmk_rc_ok) {
480 out->err(out, "error: Could not ping controller API on %s: %s",
481 pcmk__s(node_name, "DC"), pcmk_rc_str(rc));
482 data.rc = rc;
483 }
484
485 if (dispatch_type == pcmk_ipc_dispatch_poll) {
486 poll_until_reply(&data, controld_api, pcmk__s(node_name, "DC"));
487 }
488 pcmk_free_ipc_api(controld_api);
489 }
490
491 return data.rc;
492 }
493
494
495
496 int
497 pcmk_controller_status(xmlNodePtr *xml, const char *node_name,
498 unsigned int message_timeout_ms)
499 {
500 pcmk__output_t *out = NULL;
501 int rc = pcmk_rc_ok;
502
503 rc = pcmk__xml_output_new(&out, xml);
504 if (rc != pcmk_rc_ok) {
505 return rc;
506 }
507
508 pcmk__register_lib_messages(out);
509
510 rc = pcmk__controller_status(out, node_name, message_timeout_ms);
511 pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
512 return rc;
513 }
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528 int
529 pcmk__designated_controller(pcmk__output_t *out,
530 unsigned int message_timeout_ms)
531 {
532 data_t data = {
533 .out = out,
534 .rc = EAGAIN,
535 .message_timeout_ms = message_timeout_ms,
536 };
537 enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
538 pcmk_ipc_api_t *controld_api = NULL;
539
540 if (message_timeout_ms == 0) {
541 dispatch_type = pcmk_ipc_dispatch_sync;
542 }
543 controld_api = ipc_connect(&data, pcmk_ipc_controld,
544 designated_controller_event_cb, dispatch_type,
545 false);
546
547 if (controld_api != NULL) {
548 int rc = pcmk_controld_api_ping(controld_api, NULL);
549 if (rc != pcmk_rc_ok) {
550 out->err(out, "error: Could not ping controller API on DC: %s",
551 pcmk_rc_str(rc));
552 data.rc = rc;
553 }
554
555 if (dispatch_type == pcmk_ipc_dispatch_poll) {
556 poll_until_reply(&data, controld_api, "DC");
557 }
558 pcmk_free_ipc_api(controld_api);
559 }
560
561 return data.rc;
562 }
563
564
565 int
566 pcmk_designated_controller(xmlNodePtr *xml, unsigned int message_timeout_ms)
567 {
568 pcmk__output_t *out = NULL;
569 int rc = pcmk_rc_ok;
570
571 rc = pcmk__xml_output_new(&out, xml);
572 if (rc != pcmk_rc_ok) {
573 return rc;
574 }
575
576 pcmk__register_lib_messages(out);
577
578 rc = pcmk__designated_controller(out, message_timeout_ms);
579 pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
580 return rc;
581 }
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615 int
616 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 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 .id = (node_id == NULL)? 0 : *node_id,
628 .node_name = node_name,
629 .uuid = uuid,
630 .state = state,
631 },
632 };
633 enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
634 pcmk_ipc_api_t *controld_api = NULL;
635
636 if (node_name != NULL) {
637 *node_name = NULL;
638 }
639 if (uuid != NULL) {
640 *uuid = NULL;
641 }
642 if (state != NULL) {
643 *state = NULL;
644 }
645
646 if (message_timeout_ms == 0) {
647 dispatch_type = pcmk_ipc_dispatch_sync;
648 }
649 controld_api = ipc_connect(&data, pcmk_ipc_controld, node_info_event_cb,
650 dispatch_type, false);
651
652 if (controld_api != NULL) {
653 int rc = pcmk_controld_api_node_info(controld_api,
654 (node_id != NULL)? *node_id : 0);
655
656 if (rc != pcmk_rc_ok) {
657 out->err(out,
658 "error: Could not send request to controller API on local "
659 "node: %s", pcmk_rc_str(rc));
660 data.rc = rc;
661 }
662
663 if (dispatch_type == pcmk_ipc_dispatch_poll) {
664 poll_until_reply(&data, controld_api, "local node");
665 }
666 pcmk_free_ipc_api(controld_api);
667 }
668
669 if (data.rc != pcmk_rc_ok) {
670 return data.rc;
671 }
672
673
674 if (node_id != NULL) {
675 *node_id = data.node_info.id;
676 }
677 if (have_quorum != NULL) {
678 *have_quorum = data.node_info.have_quorum;
679 }
680 if (is_remote != NULL) {
681 *is_remote = data.node_info.is_remote;
682 }
683
684 return data.rc;
685 }
686
687
688 int
689 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 pcmk__output_t *out = NULL;
695 int rc = pcmk_rc_ok;
696
697 CRM_ASSERT(node_name != NULL);
698
699 rc = pcmk__xml_output_new(&out, xml);
700 if (rc != pcmk_rc_ok) {
701 return rc;
702 }
703
704 pcmk__register_lib_messages(out);
705
706 rc = pcmk__query_node_info(out, node_id, node_name, uuid, state,
707 have_quorum, is_remote, show_output,
708 message_timeout_ms);
709 pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
710 return rc;
711 }
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736 int
737 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 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 enum pcmk_ipc_dispatch dispatch_type = pcmk_ipc_dispatch_poll;
749 pcmk_ipc_api_t *pacemakerd_api = NULL;
750
751 if (message_timeout_ms == 0) {
752 dispatch_type = pcmk_ipc_dispatch_sync;
753 }
754 pacemakerd_api = ipc_connect(&data, pcmk_ipc_pacemakerd,
755 pacemakerd_event_cb, dispatch_type, true);
756
757 if (pacemakerd_api != NULL) {
758 int rc = pcmk_pacemakerd_api_ping(pacemakerd_api, ipc_name);
759 if (rc != pcmk_rc_ok) {
760 out->err(out, "error: Could not ping launcher API: %s",
761 pcmk_rc_str(rc));
762 data.rc = rc;
763 }
764
765 if (dispatch_type == pcmk_ipc_dispatch_poll) {
766 poll_until_reply(&data, pacemakerd_api, NULL);
767 }
768 pcmk_free_ipc_api(pacemakerd_api);
769
770 } else if ((data.pcmkd_state == pcmk_pacemakerd_state_remote)
771 && show_output) {
772
773 out->message(out, "pacemakerd-health",
774 NULL, data.pcmkd_state, NULL, time(NULL));
775 }
776
777 if (state != NULL) {
778 *state = data.pcmkd_state;
779 }
780 return data.rc;
781 }
782
783
784 int
785 pcmk_pacemakerd_status(xmlNodePtr *xml, const char *ipc_name,
786 unsigned int message_timeout_ms)
787 {
788 pcmk__output_t *out = NULL;
789 int rc = pcmk_rc_ok;
790
791 rc = pcmk__xml_output_new(&out, xml);
792 if (rc != pcmk_rc_ok) {
793 return rc;
794 }
795
796 pcmk__register_lib_messages(out);
797
798 rc = pcmk__pacemakerd_status(out, ipc_name, message_timeout_ms, true, NULL);
799 pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
800 return rc;
801 }
802
803
804 struct node_data {
805 pcmk__output_t *out;
806 int found;
807 const char *field;
808 const char *type;
809 bool bash_export;
810 };
811
812 static void
813 remote_node_print_helper(xmlNode *result, void *user_data)
814 {
815 struct node_data *data = user_data;
816 pcmk__output_t *out = data->out;
817 const char *name = crm_element_value(result, PCMK_XA_UNAME);
818 const char *id = crm_element_value(result, data->field);
819
820
821 out->message(out, "crmadmin-node", data->type,
822 pcmk__s(name, id), id, data->bash_export);
823 data->found++;
824 }
825
826
827 int
828 pcmk__list_nodes(pcmk__output_t *out, const char *node_types, bool bash_export)
829 {
830 xmlNode *xml_node = NULL;
831 int rc;
832
833 rc = cib__signon_query(out, NULL, &xml_node);
834
835 if (rc == pcmk_rc_ok) {
836 struct node_data data = {
837 .out = out,
838 .found = 0,
839 .bash_export = bash_export
840 };
841
842
843
844
845
846 out->begin_list(out, NULL, NULL, PCMK_XE_NODES);
847
848 if (!pcmk__str_empty(node_types) && strstr(node_types, "all")) {
849 node_types = NULL;
850 }
851
852 if (pcmk__str_empty(node_types) || strstr(node_types, "cluster")) {
853 data.field = PCMK_XA_ID;
854 data.type = "cluster";
855 crm_foreach_xpath_result(xml_node, PCMK__XP_MEMBER_NODE_CONFIG,
856 remote_node_print_helper, &data);
857 }
858
859 if (pcmk__str_empty(node_types) || strstr(node_types, "guest")) {
860 data.field = PCMK_XA_VALUE;
861 data.type = "guest";
862 crm_foreach_xpath_result(xml_node, PCMK__XP_GUEST_NODE_CONFIG,
863 remote_node_print_helper, &data);
864 }
865
866 if (pcmk__str_empty(node_types)
867 || pcmk__str_eq(node_types, ",|^remote", pcmk__str_regex)) {
868 data.field = PCMK_XA_ID;
869 data.type = "remote";
870 crm_foreach_xpath_result(xml_node, PCMK__XP_REMOTE_NODE_CONFIG,
871 remote_node_print_helper, &data);
872 }
873
874 out->end_list(out);
875
876 if (data.found == 0) {
877 out->info(out, "No nodes configured");
878 }
879
880 free_xml(xml_node);
881 }
882
883 return rc;
884 }
885
886 int
887 pcmk_list_nodes(xmlNodePtr *xml, const char *node_types)
888 {
889 pcmk__output_t *out = NULL;
890 int rc = pcmk_rc_ok;
891
892 rc = pcmk__xml_output_new(&out, xml);
893 if (rc != pcmk_rc_ok) {
894 return rc;
895 }
896
897 pcmk__register_lib_messages(out);
898
899 rc = pcmk__list_nodes(out, node_types, FALSE);
900 pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
901 return rc;
902 }