This source file includes following definitions.
- 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
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 <crm/common/output.h>
18 #include <crm/common/xml.h>
19 #include <crm/common/xml_internal.h>
20 #include <glib.h>
21
22 #include <crm/common/cmdline_internal.h>
23 #include <crm/common/xml.h>
24
25 static gboolean legacy_xml = FALSE;
26 static gboolean simple_list = FALSE;
27 static gboolean substitute = FALSE;
28
29 GOptionEntry pcmk__xml_output_entries[] = {
30 { "xml-legacy", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &legacy_xml,
31 NULL,
32 NULL },
33 { "xml-simple-list", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &simple_list,
34 NULL,
35 NULL },
36 { "xml-substitute", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &substitute,
37 NULL,
38 NULL },
39
40 { NULL }
41 };
42
43 typedef struct subst_s {
44 const char *from;
45 const char *to;
46 } subst_t;
47
48 static subst_t substitutions[] = {
49 { "Active Resources", "resources" },
50 { "Assignment Scores", "allocations" },
51 { "Assignment Scores and Utilization Information", "allocations_utilizations" },
52 { "Cluster Summary", "summary" },
53 { "Current cluster status", "cluster_status" },
54 { "Executing Cluster Transition", "transition" },
55 { "Failed Resource Actions", "failures" },
56 { "Fencing History", "fence_history" },
57 { "Full List of Resources", "resources" },
58 { "Inactive Resources", "resources" },
59 { "Migration Summary", "node_history" },
60 { "Negative Location Constraints", "bans" },
61 { "Node Attributes", "node_attributes" },
62 { "Operations", "node_history" },
63 { "Resource Config", "resource_config" },
64 { "Resource Operations", "operations" },
65 { "Revised Cluster Status", "revised_cluster_status" },
66 { "Transition Summary", "actions" },
67 { "Utilization Information", "utilizations" },
68
69 { NULL, NULL }
70 };
71
72
73
74
75
76
77
78 typedef struct private_data_s {
79
80 xmlNode *root;
81 GQueue *parent_q;
82 GSList *errors;
83
84 bool legacy_xml;
85 } private_data_t;
86
87 static void
88 xml_free_priv(pcmk__output_t *out) {
89 private_data_t *priv = NULL;
90
91 if (out == NULL || out->priv == NULL) {
92 return;
93 }
94
95 priv = out->priv;
96
97 free_xml(priv->root);
98 g_queue_free(priv->parent_q);
99 g_slist_free(priv->errors);
100 free(priv);
101 out->priv = NULL;
102 }
103
104 static bool
105 xml_init(pcmk__output_t *out) {
106 private_data_t *priv = NULL;
107
108 CRM_ASSERT(out != NULL);
109
110
111 if (out->priv != NULL) {
112 return true;
113 } else {
114 out->priv = calloc(1, sizeof(private_data_t));
115 if (out->priv == NULL) {
116 return false;
117 }
118
119 priv = out->priv;
120 }
121
122 if (legacy_xml) {
123 priv->root = create_xml_node(NULL, "crm_mon");
124 crm_xml_add(priv->root, "version", PACEMAKER_VERSION);
125 } else {
126 priv->root = create_xml_node(NULL, "pacemaker-result");
127 crm_xml_add(priv->root, "api-version", PCMK__API_VERSION);
128
129 if (out->request != NULL) {
130 crm_xml_add(priv->root, "request", out->request);
131 }
132 }
133
134 priv->parent_q = g_queue_new();
135 priv->errors = NULL;
136 g_queue_push_tail(priv->parent_q, priv->root);
137
138
139
140
141
142 priv->legacy_xml = legacy_xml;
143
144 return true;
145 }
146
147 static void
148 add_error_node(gpointer data, gpointer user_data) {
149 char *str = (char *) data;
150 xmlNodePtr node = (xmlNodePtr) user_data;
151 pcmk_create_xml_text_node(node, "error", str);
152 }
153
154 static void
155 xml_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
156 private_data_t *priv = NULL;
157 xmlNodePtr node;
158
159 CRM_ASSERT(out != NULL);
160 priv = out->priv;
161
162
163
164
165 if (priv == NULL || priv->root == NULL) {
166 return;
167 }
168
169 if (legacy_xml) {
170 GSList *node = priv->errors;
171
172 if (exit_status != CRM_EX_OK) {
173 fprintf(stderr, "%s\n", crm_exit_str(exit_status));
174 }
175
176 while (node != NULL) {
177 fprintf(stderr, "%s\n", (char *) node->data);
178 node = node->next;
179 }
180 } else {
181 char *rc_as_str = pcmk__itoa(exit_status);
182
183 node = create_xml_node(priv->root, "status");
184 pcmk__xe_set_props(node, "code", rc_as_str,
185 "message", crm_exit_str(exit_status),
186 NULL);
187
188 if (g_slist_length(priv->errors) > 0) {
189 xmlNodePtr errors_node = create_xml_node(node, "errors");
190 g_slist_foreach(priv->errors, add_error_node, (gpointer) errors_node);
191 }
192
193 free(rc_as_str);
194 }
195
196 if (print) {
197 pcmk__xml2fd(fileno(out->dest), priv->root);
198 }
199
200 if (copy_dest != NULL) {
201 *copy_dest = copy_xml(priv->root);
202 }
203 }
204
205 static void
206 xml_reset(pcmk__output_t *out) {
207 CRM_ASSERT(out != NULL);
208
209 out->dest = freopen(NULL, "w", out->dest);
210 CRM_ASSERT(out->dest != NULL);
211
212 xml_free_priv(out);
213 xml_init(out);
214 }
215
216 static void
217 xml_subprocess_output(pcmk__output_t *out, int exit_status,
218 const char *proc_stdout, const char *proc_stderr) {
219 xmlNodePtr node, child_node;
220 char *rc_as_str = NULL;
221
222 CRM_ASSERT(out != NULL);
223
224 rc_as_str = pcmk__itoa(exit_status);
225
226 node = pcmk__output_xml_create_parent(out, "command",
227 "code", rc_as_str,
228 NULL);
229
230 if (proc_stdout != NULL) {
231 child_node = pcmk_create_xml_text_node(node, "output", proc_stdout);
232 crm_xml_add(child_node, "source", "stdout");
233 }
234
235 if (proc_stderr != NULL) {
236 child_node = pcmk_create_xml_text_node(node, "output", proc_stderr);
237 crm_xml_add(child_node, "source", "stderr");
238 }
239
240 free(rc_as_str);
241 }
242
243 static void
244 xml_version(pcmk__output_t *out, bool extended) {
245 CRM_ASSERT(out != NULL);
246
247 pcmk__output_create_xml_node(out, "version",
248 "program", "Pacemaker",
249 "version", PACEMAKER_VERSION,
250 "author", "Andrew Beekhof and the "
251 "Pacemaker project contributors",
252 "build", BUILD_VERSION,
253 "features", CRM_FEATURES,
254 NULL);
255 }
256
257 G_GNUC_PRINTF(2, 3)
258 static void
259 xml_err(pcmk__output_t *out, const char *format, ...) {
260 private_data_t *priv = NULL;
261 int len = 0;
262 char *buf = NULL;
263 va_list ap;
264
265 CRM_ASSERT(out != NULL && out->priv != NULL);
266 priv = out->priv;
267
268 va_start(ap, format);
269 len = vasprintf(&buf, format, ap);
270 CRM_ASSERT(len > 0);
271 va_end(ap);
272
273 priv->errors = g_slist_append(priv->errors, buf);
274 }
275
276 G_GNUC_PRINTF(2, 3)
277 static int
278 xml_info(pcmk__output_t *out, const char *format, ...) {
279 return pcmk_rc_no_output;
280 }
281
282 static void
283 xml_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
284 xmlNodePtr parent = NULL;
285 xmlNodePtr cdata_node = NULL;
286
287 CRM_ASSERT(out != NULL);
288
289 parent = pcmk__output_create_xml_node(out, name, NULL);
290 if (parent == NULL) {
291 return;
292 }
293 cdata_node = xmlNewCDataBlock(parent->doc, (pcmkXmlStr) buf, strlen(buf));
294 xmlAddChild(parent, cdata_node);
295 }
296
297 G_GNUC_PRINTF(4, 5)
298 static void
299 xml_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
300 const char *format, ...) {
301 va_list ap;
302 char *name = NULL;
303 char *buf = NULL;
304 int len;
305
306 CRM_ASSERT(out != NULL);
307
308 va_start(ap, format);
309 len = vasprintf(&buf, format, ap);
310 CRM_ASSERT(len >= 0);
311 va_end(ap);
312
313 if (substitute) {
314 for (subst_t *s = substitutions; s->from != NULL; s++) {
315 if (!strcmp(s->from, buf)) {
316 name = g_strdup(s->to);
317 break;
318 }
319 }
320 }
321
322 if (name == NULL) {
323 name = g_ascii_strdown(buf, -1);
324 }
325
326 if (legacy_xml || simple_list) {
327 pcmk__output_xml_create_parent(out, name, NULL);
328 } else {
329 pcmk__output_xml_create_parent(out, "list",
330 "name", name,
331 NULL);
332 }
333
334 g_free(name);
335 free(buf);
336 }
337
338 G_GNUC_PRINTF(3, 4)
339 static void
340 xml_list_item(pcmk__output_t *out, const char *name, const char *format, ...) {
341 xmlNodePtr item_node = NULL;
342 va_list ap;
343 char *buf = NULL;
344 int len;
345
346 CRM_ASSERT(out != NULL);
347
348 va_start(ap, format);
349 len = vasprintf(&buf, format, ap);
350 CRM_ASSERT(len >= 0);
351 va_end(ap);
352
353 item_node = pcmk__output_create_xml_text_node(out, "item", buf);
354
355 if (name != NULL) {
356 crm_xml_add(item_node, "name", name);
357 }
358
359 free(buf);
360 }
361
362 static void
363 xml_increment_list(pcmk__output_t *out) {
364
365 }
366
367 static void
368 xml_end_list(pcmk__output_t *out) {
369 private_data_t *priv = NULL;
370
371 CRM_ASSERT(out != NULL && out->priv != NULL);
372 priv = out->priv;
373
374 if (priv->legacy_xml || simple_list) {
375 g_queue_pop_tail(priv->parent_q);
376 } else {
377 char *buf = NULL;
378 xmlNodePtr node;
379
380 node = g_queue_pop_tail(priv->parent_q);
381 buf = crm_strdup_printf("%lu", xmlChildElementCount(node));
382 crm_xml_add(node, "count", buf);
383 free(buf);
384 }
385 }
386
387 static bool
388 xml_is_quiet(pcmk__output_t *out) {
389 return false;
390 }
391
392 static void
393 xml_spacer(pcmk__output_t *out) {
394
395 }
396
397 static void
398 xml_progress(pcmk__output_t *out, bool end) {
399
400 }
401
402 pcmk__output_t *
403 pcmk__mk_xml_output(char **argv) {
404 pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
405
406 if (retval == NULL) {
407 return NULL;
408 }
409
410 retval->fmt_name = "xml";
411 retval->request = pcmk__quote_cmdline(argv);
412
413 retval->init = xml_init;
414 retval->free_priv = xml_free_priv;
415 retval->finish = xml_finish;
416 retval->reset = xml_reset;
417
418 retval->register_message = pcmk__register_message;
419 retval->message = pcmk__call_message;
420
421 retval->subprocess_output = xml_subprocess_output;
422 retval->version = xml_version;
423 retval->info = xml_info;
424 retval->transient = xml_info;
425 retval->err = xml_err;
426 retval->output_xml = xml_output_xml;
427
428 retval->begin_list = xml_begin_list;
429 retval->list_item = xml_list_item;
430 retval->increment_list = xml_increment_list;
431 retval->end_list = xml_end_list;
432
433 retval->is_quiet = xml_is_quiet;
434 retval->spacer = xml_spacer;
435 retval->progress = xml_progress;
436 retval->prompt = pcmk__text_prompt;
437
438 return retval;
439 }
440
441 xmlNodePtr
442 pcmk__output_xml_create_parent(pcmk__output_t *out, const char *name, ...) {
443 va_list args;
444 xmlNodePtr node = NULL;
445
446 CRM_ASSERT(out != NULL);
447 CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL);
448
449 node = pcmk__output_create_xml_node(out, name, NULL);
450
451 va_start(args, name);
452 pcmk__xe_set_propv(node, args);
453 va_end(args);
454
455 pcmk__output_xml_push_parent(out, node);
456 return node;
457 }
458
459 void
460 pcmk__output_xml_add_node_copy(pcmk__output_t *out, xmlNodePtr node) {
461 private_data_t *priv = NULL;
462 xmlNodePtr parent = NULL;
463
464 CRM_ASSERT(out != NULL && out->priv != NULL);
465 CRM_ASSERT(node != NULL);
466 CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return);
467
468 priv = out->priv;
469 parent = g_queue_peek_tail(priv->parent_q);
470
471
472 CRM_CHECK(parent != NULL, return);
473
474 add_node_copy(parent, node);
475 }
476
477 xmlNodePtr
478 pcmk__output_create_xml_node(pcmk__output_t *out, const char *name, ...) {
479 xmlNodePtr node = NULL;
480 private_data_t *priv = NULL;
481 va_list args;
482
483 CRM_ASSERT(out != NULL && out->priv != NULL);
484 CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL);
485
486 priv = out->priv;
487
488 node = create_xml_node(g_queue_peek_tail(priv->parent_q), name);
489 va_start(args, name);
490 pcmk__xe_set_propv(node, args);
491 va_end(args);
492
493 return node;
494 }
495
496 xmlNodePtr
497 pcmk__output_create_xml_text_node(pcmk__output_t *out, const char *name, const char *content) {
498 xmlNodePtr node = NULL;
499
500 CRM_ASSERT(out != NULL);
501 CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL);
502
503 node = pcmk__output_create_xml_node(out, name, NULL);
504 xmlNodeSetContent(node, (pcmkXmlStr) content);
505 return node;
506 }
507
508 void
509 pcmk__output_xml_push_parent(pcmk__output_t *out, xmlNodePtr parent) {
510 private_data_t *priv = NULL;
511
512 CRM_ASSERT(out != NULL && out->priv != NULL);
513 CRM_ASSERT(parent != NULL);
514 CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return);
515
516 priv = out->priv;
517
518 g_queue_push_tail(priv->parent_q, parent);
519 }
520
521 void
522 pcmk__output_xml_pop_parent(pcmk__output_t *out) {
523 private_data_t *priv = NULL;
524
525 CRM_ASSERT(out != NULL && out->priv != NULL);
526 CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return);
527
528 priv = out->priv;
529
530 CRM_ASSERT(g_queue_get_length(priv->parent_q) > 0);
531 g_queue_pop_tail(priv->parent_q);
532 }
533
534 xmlNodePtr
535 pcmk__output_xml_peek_parent(pcmk__output_t *out) {
536 private_data_t *priv = NULL;
537
538 CRM_ASSERT(out != NULL && out->priv != NULL);
539 CRM_CHECK(pcmk__str_any_of(out->fmt_name, "xml", "html", NULL), return NULL);
540
541 priv = out->priv;
542
543
544 return g_queue_peek_tail(priv->parent_q);
545 }