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