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