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 CRM_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 CRM_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 CRM_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 CRM_ASSERT(out != NULL);
237
238 out->dest = freopen(NULL, "w", out->dest);
239 CRM_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 CRM_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 CRM_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 CRM_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 CRM_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 CRM_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 CRM_ASSERT(out != NULL && out->priv != NULL);
342 priv = out->priv;
343
344 va_start(ap, format);
345 len = vasprintf(&buf, format, ap);
346 CRM_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 CRM_ASSERT(out != NULL);
381
382 va_start(ap, format);
383 len = vasprintf(&buf, format, ap);
384 CRM_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 CRM_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 CRM_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 CRM_ASSERT(out != NULL && out->priv != NULL);
501 CRM_ASSERT(node != NULL);
502 CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return);
503
504 add_root_node(out);
505
506 priv = out->priv;
507 parent = g_queue_peek_tail(priv->parent_q);
508
509
510 CRM_CHECK(parent != NULL, return);
511
512 pcmk__xml_copy(parent, node);
513 }
514
515 xmlNodePtr
516 pcmk__output_create_xml_node(pcmk__output_t *out, const char *name, ...) {
517 xmlNodePtr node = NULL;
518 private_data_t *priv = NULL;
519 va_list args;
520
521 CRM_ASSERT(out != NULL && out->priv != NULL);
522 CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL);
523
524 add_root_node(out);
525
526 priv = out->priv;
527
528 node = pcmk__xe_create(g_queue_peek_tail(priv->parent_q), name);
529 va_start(args, name);
530 pcmk__xe_set_propv(node, args);
531 va_end(args);
532
533 return node;
534 }
535
536 xmlNodePtr
537 pcmk__output_create_xml_text_node(pcmk__output_t *out, const char *name, const char *content) {
538 xmlNodePtr node = NULL;
539
540 CRM_ASSERT(out != NULL);
541 CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL);
542
543 node = pcmk__output_create_xml_node(out, name, NULL);
544 pcmk__xe_set_content(node, "%s", content);
545 return node;
546 }
547
548 void
549 pcmk__output_xml_push_parent(pcmk__output_t *out, xmlNodePtr parent) {
550 private_data_t *priv = NULL;
551
552 CRM_ASSERT(out != NULL && out->priv != NULL);
553 CRM_ASSERT(parent != NULL);
554 CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return);
555
556 add_root_node(out);
557
558 priv = out->priv;
559
560 g_queue_push_tail(priv->parent_q, parent);
561 }
562
563 void
564 pcmk__output_xml_pop_parent(pcmk__output_t *out) {
565 private_data_t *priv = NULL;
566
567 CRM_ASSERT(out != NULL && out->priv != NULL);
568 CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return);
569
570 add_root_node(out);
571
572 priv = out->priv;
573
574 CRM_ASSERT(g_queue_get_length(priv->parent_q) > 0);
575
576 g_queue_pop_tail(priv->parent_q);
577 }
578
579 xmlNodePtr
580 pcmk__output_xml_peek_parent(pcmk__output_t *out) {
581 private_data_t *priv = NULL;
582
583 CRM_ASSERT(out != NULL && out->priv != NULL);
584 CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL);
585
586 add_root_node(out);
587
588 priv = out->priv;
589
590
591 return g_queue_peek_tail(priv->parent_q);
592 }
593
594 bool
595 pcmk__output_get_legacy_xml(pcmk__output_t *out)
596 {
597 private_data_t *priv = NULL;
598
599 CRM_ASSERT(out != NULL);
600
601 if (!pcmk__str_eq(out->fmt_name, "xml", pcmk__str_none)) {
602 return false;
603 }
604
605 CRM_ASSERT(out->priv != NULL);
606
607 priv = out->priv;
608 return priv->legacy_xml;
609 }
610
611 void
612 pcmk__output_set_legacy_xml(pcmk__output_t *out)
613 {
614 private_data_t *priv = NULL;
615
616 CRM_ASSERT(out != NULL);
617
618 if (!pcmk__str_eq(out->fmt_name, "xml", pcmk__str_none)) {
619 return;
620 }
621
622 CRM_ASSERT(out->priv != NULL);
623
624 priv = out->priv;
625 priv->legacy_xml = true;
626 }
627
628 void
629 pcmk__output_enable_list_element(pcmk__output_t *out)
630 {
631 private_data_t *priv = NULL;
632
633 CRM_ASSERT(out != NULL);
634
635 if (!pcmk__str_eq(out->fmt_name, "xml", pcmk__str_none)) {
636 return;
637 }
638
639 CRM_ASSERT(out->priv != NULL);
640
641 priv = out->priv;
642 priv->list_element = true;
643 }