This source file includes following definitions.
- has_root_node
- add_root_node
- xml_free_priv
- xml_init
- add_error_node
- xml_finish
- xml_reset
- xml_subprocess_output
- xml_version
- G_GNUC_PRINTF
- G_GNUC_PRINTF
- xml_output_xml
- G_GNUC_PRINTF
- G_GNUC_PRINTF
- xml_increment_list
- xml_end_list
- xml_is_quiet
- xml_spacer
- xml_progress
- pcmk__mk_xml_output
- pcmk__output_xml_create_parent
- pcmk__output_xml_add_node_copy
- pcmk__output_create_xml_node
- pcmk__output_create_xml_text_node
- pcmk__output_xml_push_parent
- pcmk__output_xml_pop_parent
- pcmk__output_xml_peek_parent
- pcmk__output_get_legacy_xml
- pcmk__output_set_legacy_xml
- pcmk__output_enable_list_element
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <ctype.h>
13 #include <stdarg.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <crm/crm.h>
17
18 #include <glib.h>
19 #include <libxml/tree.h>
20 #include <libxml/xmlstring.h>
21
22 #include <crm/common/cmdline_internal.h>
23 #include <crm/common/output.h>
24 #include <crm/common/xml.h>
25 #include <crm/common/xml_internal.h>
26
27 typedef struct subst_s {
28 const char *from;
29 const char *to;
30 } subst_t;
31
32 static const subst_t substitutions[] = {
33 { "Active Resources",
34 PCMK_XE_RESOURCES, },
35 { "Assignment Scores",
36 PCMK_XE_ALLOCATIONS, },
37 { "Assignment Scores and Utilization Information",
38 PCMK_XE_ALLOCATIONS_UTILIZATIONS, },
39 { "Cluster Summary",
40 PCMK_XE_SUMMARY, },
41 { "Current cluster status",
42 PCMK_XE_CLUSTER_STATUS, },
43 { "Executing Cluster Transition",
44 PCMK_XE_TRANSITION, },
45 { "Failed Resource Actions",
46 PCMK_XE_FAILURES, },
47 { "Fencing History",
48 PCMK_XE_FENCE_HISTORY, },
49 { "Full List of Resources",
50 PCMK_XE_RESOURCES, },
51 { "Inactive Resources",
52 PCMK_XE_RESOURCES, },
53 { "Migration Summary",
54 PCMK_XE_NODE_HISTORY, },
55 { "Negative Location Constraints",
56 PCMK_XE_BANS, },
57 { "Node Attributes",
58 PCMK_XE_NODE_ATTRIBUTES, },
59 { "Operations",
60 PCMK_XE_NODE_HISTORY, },
61 { "Resource Config",
62 PCMK_XE_RESOURCE_CONFIG, },
63 { "Resource Operations",
64 PCMK_XE_OPERATIONS, },
65 { "Revised Cluster Status",
66 PCMK_XE_REVISED_CLUSTER_STATUS, },
67 { "Timings",
68 PCMK_XE_TIMINGS, },
69 { "Transition Summary",
70 PCMK_XE_ACTIONS, },
71 { "Utilization Information",
72 PCMK_XE_UTILIZATIONS, },
73
74 { NULL, NULL }
75 };
76
77
78
79
80
81
82
83 typedef struct private_data_s {
84
85 xmlNode *root;
86 GQueue *parent_q;
87 GSList *errors;
88
89 bool legacy_xml;
90 bool list_element;
91 } private_data_t;
92
93 static bool
94 has_root_node(pcmk__output_t *out)
95 {
96 private_data_t *priv = NULL;
97
98 pcmk__assert(out != NULL);
99
100 priv = out->priv;
101 return priv != NULL && priv->root != NULL;
102 }
103
104 static void
105 add_root_node(pcmk__output_t *out)
106 {
107 private_data_t *priv = NULL;
108
109
110 if (has_root_node(out)) {
111 return;
112 }
113
114 priv = out->priv;
115
116 if (priv->legacy_xml) {
117 priv->root = pcmk__xe_create(NULL, PCMK_XE_CRM_MON);
118 crm_xml_add(priv->root, PCMK_XA_VERSION, PACEMAKER_VERSION);
119 } else {
120 priv->root = pcmk__xe_create(NULL, PCMK_XE_PACEMAKER_RESULT);
121 crm_xml_add(priv->root, PCMK_XA_API_VERSION, PCMK__API_VERSION);
122 crm_xml_add(priv->root, PCMK_XA_REQUEST,
123 pcmk__s(out->request, "libpacemaker"));
124 }
125
126 priv->parent_q = g_queue_new();
127 g_queue_push_tail(priv->parent_q, priv->root);
128 }
129
130 static void
131 xml_free_priv(pcmk__output_t *out) {
132 private_data_t *priv = NULL;
133
134 if (out == NULL || out->priv == NULL) {
135 return;
136 }
137
138 priv = out->priv;
139
140 if (has_root_node(out)) {
141 pcmk__xml_free(priv->root);
142
143
144
145
146 g_queue_free(priv->parent_q);
147 }
148
149 g_slist_free_full(priv->errors, free);
150 free(priv);
151 out->priv = NULL;
152 }
153
154 static bool
155 xml_init(pcmk__output_t *out) {
156 private_data_t *priv = NULL;
157
158 pcmk__assert(out != NULL);
159
160
161 if (out->priv != NULL) {
162 return true;
163 } else {
164 out->priv = calloc(1, sizeof(private_data_t));
165 if (out->priv == NULL) {
166 return false;
167 }
168
169 priv = out->priv;
170 }
171
172 priv->errors = NULL;
173
174 return true;
175 }
176
177 static void
178 add_error_node(gpointer data, gpointer user_data) {
179 const char *str = (const char *) data;
180 xmlNodePtr node = (xmlNodePtr) user_data;
181
182 node = pcmk__xe_create(node, PCMK_XE_ERROR);
183 pcmk__xe_set_content(node, "%s", str);
184 }
185
186 static void
187 xml_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
188 private_data_t *priv = NULL;
189 xmlNodePtr node;
190
191 pcmk__assert(out != NULL);
192 priv = out->priv;
193
194 if (priv == NULL) {
195 return;
196 }
197
198 add_root_node(out);
199
200 if (priv->legacy_xml) {
201 GSList *node = priv->errors;
202
203 if (exit_status != CRM_EX_OK) {
204 fprintf(stderr, "%s\n", crm_exit_str(exit_status));
205 }
206
207 while (node != NULL) {
208 fprintf(stderr, "%s\n", (char *) node->data);
209 node = node->next;
210 }
211 } else {
212 char *rc_as_str = pcmk__itoa(exit_status);
213
214 node = pcmk__xe_create(priv->root, PCMK_XE_STATUS);
215 pcmk__xe_set_props(node,
216 PCMK_XA_CODE, rc_as_str,
217 PCMK_XA_MESSAGE, crm_exit_str(exit_status),
218 NULL);
219
220 if (g_slist_length(priv->errors) > 0) {
221 xmlNodePtr errors_node = pcmk__xe_create(node, PCMK_XE_ERRORS);
222 g_slist_foreach(priv->errors, add_error_node, (gpointer) errors_node);
223 }
224
225 free(rc_as_str);
226 }
227
228 if (print) {
229 pcmk__xml2fd(fileno(out->dest), priv->root);
230 }
231
232 if (copy_dest != NULL) {
233 *copy_dest = pcmk__xml_copy(NULL, priv->root);
234 }
235 }
236
237 static void
238 xml_reset(pcmk__output_t *out) {
239 pcmk__assert(out != NULL);
240
241 out->dest = freopen(NULL, "w", out->dest);
242 pcmk__assert(out->dest != NULL);
243
244 xml_free_priv(out);
245 xml_init(out);
246 }
247
248 static void
249 xml_subprocess_output(pcmk__output_t *out, int exit_status,
250 const char *proc_stdout, const char *proc_stderr) {
251 xmlNodePtr node, child_node;
252 char *rc_as_str = NULL;
253
254 pcmk__assert(out != NULL);
255
256 rc_as_str = pcmk__itoa(exit_status);
257
258 node = pcmk__output_xml_create_parent(out, PCMK_XE_COMMAND,
259 PCMK_XA_CODE, rc_as_str,
260 NULL);
261
262 if (proc_stdout != NULL) {
263 child_node = pcmk__xe_create(node, PCMK_XE_OUTPUT);
264 pcmk__xe_set_content(child_node, "%s", proc_stdout);
265 crm_xml_add(child_node, PCMK_XA_SOURCE, "stdout");
266 }
267
268 if (proc_stderr != NULL) {
269 child_node = pcmk__xe_create(node, PCMK_XE_OUTPUT);
270 pcmk__xe_set_content(child_node, "%s", proc_stderr);
271 crm_xml_add(child_node, PCMK_XA_SOURCE, "stderr");
272 }
273
274 free(rc_as_str);
275 }
276
277 static void
278 xml_version(pcmk__output_t *out, bool extended) {
279 const char *author = "Andrew Beekhof and the Pacemaker project "
280 "contributors";
281 pcmk__assert(out != NULL);
282
283 pcmk__output_create_xml_node(out, PCMK_XE_VERSION,
284 PCMK_XA_PROGRAM, "Pacemaker",
285 PCMK_XA_VERSION, PACEMAKER_VERSION,
286 PCMK_XA_AUTHOR, author,
287 PCMK_XA_BUILD, BUILD_VERSION,
288 PCMK_XA_FEATURES, CRM_FEATURES,
289 NULL);
290 }
291
292 G_GNUC_PRINTF(2, 3)
293 static void
294 xml_err(pcmk__output_t *out, const char *format, ...) {
295 private_data_t *priv = NULL;
296 int len = 0;
297 char *buf = NULL;
298 va_list ap;
299
300 pcmk__assert((out != NULL) && (out->priv != NULL));
301 priv = out->priv;
302
303 add_root_node(out);
304
305 va_start(ap, format);
306 len = vasprintf(&buf, format, ap);
307 pcmk__assert(len > 0);
308 va_end(ap);
309
310 priv->errors = g_slist_append(priv->errors, buf);
311 }
312
313 G_GNUC_PRINTF(2, 3)
314 static int
315 xml_info(pcmk__output_t *out, const char *format, ...) {
316 return pcmk_rc_no_output;
317 }
318
319 static void
320 xml_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
321 xmlNodePtr parent = NULL;
322 xmlNodePtr cdata_node = NULL;
323
324 pcmk__assert(out != NULL);
325
326 parent = pcmk__output_create_xml_node(out, name, NULL);
327 if (parent == NULL) {
328 return;
329 }
330 cdata_node = xmlNewCDataBlock(parent->doc, (const xmlChar *) buf,
331 strlen(buf));
332 xmlAddChild(parent, cdata_node);
333 }
334
335 G_GNUC_PRINTF(4, 5)
336 static void
337 xml_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
338 const char *format, ...) {
339 va_list ap;
340 char *name = NULL;
341 char *buf = NULL;
342 int len;
343 private_data_t *priv = NULL;
344
345 pcmk__assert((out != NULL) && (out->priv != NULL));
346 priv = out->priv;
347
348 va_start(ap, format);
349 len = vasprintf(&buf, format, ap);
350 pcmk__assert(len >= 0);
351 va_end(ap);
352
353 for (const subst_t *s = substitutions; s->from != NULL; s++) {
354 if (strcmp(s->from, buf) == 0) {
355 name = g_strdup(s->to);
356 break;
357 }
358 }
359
360 if (name == NULL) {
361 name = g_ascii_strdown(buf, -1);
362 }
363
364 if (priv->list_element) {
365 pcmk__output_xml_create_parent(out, PCMK_XE_LIST,
366 PCMK_XA_NAME, name,
367 NULL);
368 } else {
369 pcmk__output_xml_create_parent(out, name, NULL);
370 }
371
372 g_free(name);
373 free(buf);
374 }
375
376 G_GNUC_PRINTF(3, 4)
377 static void
378 xml_list_item(pcmk__output_t *out, const char *name, const char *format, ...) {
379 xmlNodePtr item_node = NULL;
380 va_list ap;
381 char *buf = NULL;
382 int len;
383
384 pcmk__assert(out != NULL);
385
386 va_start(ap, format);
387 len = vasprintf(&buf, format, ap);
388 pcmk__assert(len >= 0);
389 va_end(ap);
390
391 item_node = pcmk__output_create_xml_text_node(out, PCMK_XE_ITEM, buf);
392
393 if (name != NULL) {
394 crm_xml_add(item_node, PCMK_XA_NAME, name);
395 }
396
397 free(buf);
398 }
399
400 static void
401 xml_increment_list(pcmk__output_t *out) {
402
403 }
404
405 static void
406 xml_end_list(pcmk__output_t *out) {
407 private_data_t *priv = NULL;
408
409 pcmk__assert((out != NULL) && (out->priv != NULL));
410 priv = out->priv;
411
412 if (priv->list_element) {
413 char *buf = NULL;
414 xmlNodePtr node;
415
416
417 node = g_queue_pop_tail(priv->parent_q);
418 buf = crm_strdup_printf("%lu", xmlChildElementCount(node));
419 crm_xml_add(node, PCMK_XA_COUNT, buf);
420 free(buf);
421 } else {
422
423 g_queue_pop_tail(priv->parent_q);
424 }
425 }
426
427 static bool
428 xml_is_quiet(pcmk__output_t *out) {
429 return false;
430 }
431
432 static void
433 xml_spacer(pcmk__output_t *out) {
434
435 }
436
437 static void
438 xml_progress(pcmk__output_t *out, bool end) {
439
440 }
441
442 pcmk__output_t *
443 pcmk__mk_xml_output(char **argv) {
444 pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
445
446 if (retval == NULL) {
447 return NULL;
448 }
449
450 retval->fmt_name = "xml";
451 retval->request = pcmk__quote_cmdline(argv);
452
453 retval->init = xml_init;
454 retval->free_priv = xml_free_priv;
455 retval->finish = xml_finish;
456 retval->reset = xml_reset;
457
458 retval->register_message = pcmk__register_message;
459 retval->message = pcmk__call_message;
460
461 retval->subprocess_output = xml_subprocess_output;
462 retval->version = xml_version;
463 retval->info = xml_info;
464 retval->transient = xml_info;
465 retval->err = xml_err;
466 retval->output_xml = xml_output_xml;
467
468 retval->begin_list = xml_begin_list;
469 retval->list_item = xml_list_item;
470 retval->increment_list = xml_increment_list;
471 retval->end_list = xml_end_list;
472
473 retval->is_quiet = xml_is_quiet;
474 retval->spacer = xml_spacer;
475 retval->progress = xml_progress;
476 retval->prompt = pcmk__text_prompt;
477
478 return retval;
479 }
480
481 xmlNodePtr
482 pcmk__output_xml_create_parent(pcmk__output_t *out, const char *name, ...) {
483 va_list args;
484 xmlNodePtr node = NULL;
485
486 pcmk__assert(out != NULL);
487 CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL);
488
489 node = pcmk__output_create_xml_node(out, name, NULL);
490
491 va_start(args, name);
492 pcmk__xe_set_propv(node, args);
493 va_end(args);
494
495 pcmk__output_xml_push_parent(out, node);
496 return node;
497 }
498
499 void
500 pcmk__output_xml_add_node_copy(pcmk__output_t *out, xmlNodePtr node) {
501 private_data_t *priv = NULL;
502 xmlNodePtr parent = NULL;
503
504 pcmk__assert((out != NULL) && (out->priv != NULL) && (node != NULL));
505 CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return);
506
507 add_root_node(out);
508
509 priv = out->priv;
510 parent = g_queue_peek_tail(priv->parent_q);
511
512
513 CRM_CHECK(parent != NULL, return);
514
515 pcmk__xml_copy(parent, node);
516 }
517
518 xmlNodePtr
519 pcmk__output_create_xml_node(pcmk__output_t *out, const char *name, ...) {
520 xmlNodePtr node = NULL;
521 private_data_t *priv = NULL;
522 va_list args;
523
524 pcmk__assert((out != NULL) && (out->priv != NULL));
525 CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL);
526
527 add_root_node(out);
528
529 priv = out->priv;
530
531 node = pcmk__xe_create(g_queue_peek_tail(priv->parent_q), name);
532 va_start(args, name);
533 pcmk__xe_set_propv(node, args);
534 va_end(args);
535
536 return node;
537 }
538
539 xmlNodePtr
540 pcmk__output_create_xml_text_node(pcmk__output_t *out, const char *name, const char *content) {
541 xmlNodePtr node = NULL;
542
543 pcmk__assert(out != NULL);
544 CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL);
545
546 node = pcmk__output_create_xml_node(out, name, NULL);
547 pcmk__xe_set_content(node, "%s", content);
548 return node;
549 }
550
551 void
552 pcmk__output_xml_push_parent(pcmk__output_t *out, xmlNodePtr parent) {
553 private_data_t *priv = NULL;
554
555 pcmk__assert((out != NULL) && (out->priv != NULL) && (parent != NULL));
556 CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return);
557
558 add_root_node(out);
559
560 priv = out->priv;
561
562 g_queue_push_tail(priv->parent_q, parent);
563 }
564
565 void
566 pcmk__output_xml_pop_parent(pcmk__output_t *out) {
567 private_data_t *priv = NULL;
568
569 pcmk__assert((out != NULL) && (out->priv != NULL));
570 CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return);
571
572 add_root_node(out);
573
574 priv = out->priv;
575
576 pcmk__assert(g_queue_get_length(priv->parent_q) > 0);
577
578 g_queue_pop_tail(priv->parent_q);
579 }
580
581 xmlNodePtr
582 pcmk__output_xml_peek_parent(pcmk__output_t *out) {
583 private_data_t *priv = NULL;
584
585 pcmk__assert((out != NULL) && (out->priv != NULL));
586 CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL);
587
588 add_root_node(out);
589
590 priv = out->priv;
591
592
593 return g_queue_peek_tail(priv->parent_q);
594 }
595
596 bool
597 pcmk__output_get_legacy_xml(pcmk__output_t *out)
598 {
599 private_data_t *priv = NULL;
600
601 pcmk__assert(out != NULL);
602
603 if (!pcmk__str_eq(out->fmt_name, "xml", pcmk__str_none)) {
604 return false;
605 }
606
607 pcmk__assert(out->priv != NULL);
608
609 priv = out->priv;
610 return priv->legacy_xml;
611 }
612
613 void
614 pcmk__output_set_legacy_xml(pcmk__output_t *out)
615 {
616 private_data_t *priv = NULL;
617
618 pcmk__assert(out != NULL);
619
620 if (!pcmk__str_eq(out->fmt_name, "xml", pcmk__str_none)) {
621 return;
622 }
623
624 pcmk__assert(out->priv != NULL);
625
626 priv = out->priv;
627 priv->legacy_xml = true;
628 }
629
630 void
631 pcmk__output_enable_list_element(pcmk__output_t *out)
632 {
633 private_data_t *priv = NULL;
634
635 pcmk__assert(out != NULL);
636
637 if (!pcmk__str_eq(out->fmt_name, "xml", pcmk__str_none)) {
638 return;
639 }
640
641 pcmk__assert(out->priv != NULL);
642
643 priv = out->priv;
644 priv->list_element = true;
645 }