pacemaker  2.0.4-2deceaa
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
output_xml.c
Go to the documentation of this file.
1 /*
2  * Copyright 2019 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU Lesser General Public License
7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8  */
9 
10 #ifndef _GNU_SOURCE
11 # define _GNU_SOURCE
12 #endif
13 
14 #include <config.h>
15 #include <ctype.h>
16 #include <stdarg.h>
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <crm/crm.h>
20 #include <crm/common/output.h>
21 #include <crm/common/xml.h>
22 #include <glib.h>
23 
24 static gboolean legacy_xml = FALSE;
25 static gboolean simple_list = FALSE;
26 
27 GOptionEntry pcmk__xml_output_entries[] = {
28  { "xml-legacy", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &legacy_xml,
29  NULL,
30  NULL },
31  { "xml-simple-list", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &simple_list,
32  NULL,
33  NULL },
34 
35  { NULL }
36 };
37 
38 typedef struct private_data_s {
39  xmlNode *root;
40  GQueue *parent_q;
41  GSList *errors;
42  bool legacy_xml;
44 
45 static void
46 xml_free_priv(pcmk__output_t *out) {
47  private_data_t *priv = out->priv;
48 
49  if (priv == NULL) {
50  return;
51  }
52 
53  xmlFreeNode(priv->root);
54  g_queue_free(priv->parent_q);
55  g_slist_free(priv->errors);
56  free(priv);
57 }
58 
59 static bool
60 xml_init(pcmk__output_t *out) {
61  private_data_t *priv = NULL;
62 
63  /* If xml_init was previously called on this output struct, just return. */
64  if (out->priv != NULL) {
65  return true;
66  } else {
67  out->priv = calloc(1, sizeof(private_data_t));
68  if (out->priv == NULL) {
69  return false;
70  }
71 
72  priv = out->priv;
73  }
74 
75  if (legacy_xml) {
76  priv->root = create_xml_node(NULL, "crm_mon");
77  xmlSetProp(priv->root, (pcmkXmlStr) "version", (pcmkXmlStr) VERSION);
78  } else {
79  priv->root = create_xml_node(NULL, "pacemaker-result");
80  xmlSetProp(priv->root, (pcmkXmlStr) "api-version", (pcmkXmlStr) PCMK__API_VERSION);
81 
82  if (out->request != NULL) {
83  xmlSetProp(priv->root, (pcmkXmlStr) "request", (pcmkXmlStr) out->request);
84  }
85  }
86 
87  priv->parent_q = g_queue_new();
88  priv->errors = NULL;
89  g_queue_push_tail(priv->parent_q, priv->root);
90 
91  /* Copy this from the file-level variable. This means that it is only settable
92  * as a command line option, and that pcmk__output_new must be called after all
93  * command line processing is completed.
94  */
95  priv->legacy_xml = legacy_xml;
96 
97  return true;
98 }
99 
100 static void
101 add_error_node(gpointer data, gpointer user_data) {
102  char *str = (char *) data;
103  xmlNodePtr node = (xmlNodePtr) user_data;
104  pcmk_create_xml_text_node(node, "error", str);
105 }
106 
107 static void
108 xml_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
109  xmlNodePtr node;
110  private_data_t *priv = out->priv;
111 
112  /* If root is NULL, xml_init failed and we are being called from pcmk__output_free
113  * in the pcmk__output_new path.
114  */
115  if (priv == NULL || priv->root == NULL) {
116  return;
117  }
118 
119  if (legacy_xml) {
120  GSList *node = priv->errors;
121 
122  if (exit_status != CRM_EX_OK) {
123  fprintf(stderr, "%s\n", crm_exit_str(exit_status));
124  }
125 
126  while (node != NULL) {
127  fprintf(stderr, "%s\n", (char *) node->data);
128  node = node->next;
129  }
130  } else {
131  char *rc_as_str = crm_itoa(exit_status);
132 
133  node = create_xml_node(priv->root, "status");
134  xmlSetProp(node, (pcmkXmlStr) "code", (pcmkXmlStr) rc_as_str);
135  xmlSetProp(node, (pcmkXmlStr) "message", (pcmkXmlStr) crm_exit_str(exit_status));
136 
137  if (g_slist_length(priv->errors) > 0) {
138  xmlNodePtr errors_node = create_xml_node(node, "errors");
139  g_slist_foreach(priv->errors, add_error_node, (gpointer) errors_node);
140  }
141 
142  free(rc_as_str);
143  }
144 
145  if (print) {
146  char *buf = dump_xml_formatted_with_text(priv->root);
147  fprintf(out->dest, "%s", buf);
148  free(buf);
149  }
150 
151  if (copy_dest != NULL) {
152  *copy_dest = copy_xml(priv->root);
153  }
154 }
155 
156 static void
157 xml_reset(pcmk__output_t *out) {
158  char *buf = NULL;
159 
160  CRM_ASSERT(out != NULL);
161 
162  if (out->priv != NULL) {
163  private_data_t *priv = out->priv;
164  buf = dump_xml_formatted_with_text(priv->root);
165  fprintf(out->dest, "%s", buf);
166  free(buf);
167  }
168 
169  xml_free_priv(out);
170  xml_init(out);
171 }
172 
173 static void
174 xml_subprocess_output(pcmk__output_t *out, int exit_status,
175  const char *proc_stdout, const char *proc_stderr) {
176  xmlNodePtr node, child_node;
177  char *rc_as_str = NULL;
178 
179  rc_as_str = crm_itoa(exit_status);
180 
181  node = pcmk__output_xml_create_parent(out, "command");
182  xmlSetProp(node, (pcmkXmlStr) "code", (pcmkXmlStr) rc_as_str);
183 
184  if (proc_stdout != NULL) {
185  child_node = pcmk_create_xml_text_node(node, "output", proc_stdout);
186  xmlSetProp(child_node, (pcmkXmlStr) "source", (pcmkXmlStr) "stdout");
187  }
188 
189  if (proc_stderr != NULL) {
190  child_node = pcmk_create_xml_text_node(node, "output", proc_stderr);
191  xmlSetProp(child_node, (pcmkXmlStr) "source", (pcmkXmlStr) "stderr");
192  }
193 
194  pcmk__output_xml_add_node(out, node);
195  free(rc_as_str);
196 }
197 
198 static void
199 xml_version(pcmk__output_t *out, bool extended) {
200  xmlNodePtr node;
201  private_data_t *priv = out->priv;
202  CRM_ASSERT(priv != NULL);
203 
204  node = pcmk__output_create_xml_node(out, "version");
205  xmlSetProp(node, (pcmkXmlStr) "program", (pcmkXmlStr) "Pacemaker");
206  xmlSetProp(node, (pcmkXmlStr) "version", (pcmkXmlStr) PACEMAKER_VERSION);
207  xmlSetProp(node, (pcmkXmlStr) "author", (pcmkXmlStr) "Andrew Beekhof");
208  xmlSetProp(node, (pcmkXmlStr) "build", (pcmkXmlStr) BUILD_VERSION);
209  xmlSetProp(node, (pcmkXmlStr) "features", (pcmkXmlStr) CRM_FEATURES);
210 }
211 
212 G_GNUC_PRINTF(2, 3)
213 static void
214 xml_err(pcmk__output_t *out, const char *format, ...) {
215  private_data_t *priv = out->priv;
216  int len = 0;
217  char *buf = NULL;
218  va_list ap;
219 
220  CRM_ASSERT(priv != NULL);
221  va_start(ap, format);
222  len = vasprintf(&buf, format, ap);
223  CRM_ASSERT(len > 0);
224  va_end(ap);
225 
226  priv->errors = g_slist_append(priv->errors, buf);
227 }
228 
229 G_GNUC_PRINTF(2, 3)
230 static void
231 xml_info(pcmk__output_t *out, const char *format, ...) {
232  /* This function intentially left blank */
233 }
234 
235 static void
236 xml_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
237  xmlNodePtr parent = NULL;
238  xmlNodePtr cdata_node = NULL;
239  private_data_t *priv = out->priv;
240 
241  CRM_ASSERT(priv != NULL);
242 
243  parent = pcmk__output_create_xml_node(out, name);
244  cdata_node = xmlNewCDataBlock(getDocPtr(parent), (pcmkXmlStr) buf, strlen(buf));
245  xmlAddChild(parent, cdata_node);
246 }
247 
248 G_GNUC_PRINTF(4, 5)
249 static void
250 xml_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
251  const char *format, ...) {
252  va_list ap;
253  char *buf = NULL;
254  int len;
255 
256  va_start(ap, format);
257  len = vasprintf(&buf, format, ap);
258  CRM_ASSERT(len >= 0);
259  va_end(ap);
260 
261  if (legacy_xml || simple_list) {
263  } else {
264  xmlNodePtr list_node = NULL;
265 
266  list_node = pcmk__output_xml_create_parent(out, "list");
267  xmlSetProp(list_node, (pcmkXmlStr) "name", (pcmkXmlStr) buf);
268  }
269 
270  free(buf);
271 }
272 
273 G_GNUC_PRINTF(3, 4)
274 static void
275 xml_list_item(pcmk__output_t *out, const char *name, const char *format, ...) {
276  private_data_t *priv = out->priv;
277  xmlNodePtr item_node = NULL;
278  va_list ap;
279  char *buf = NULL;
280  int len;
281 
282  CRM_ASSERT(priv != NULL);
283 
284  va_start(ap, format);
285  len = vasprintf(&buf, format, ap);
286  CRM_ASSERT(len >= 0);
287  va_end(ap);
288 
289  item_node = pcmk__output_create_xml_text_node(out, "item", buf);
290  free(buf);
291 
292  xmlSetProp(item_node, (pcmkXmlStr) "name", (pcmkXmlStr) name);
293 }
294 
295 static void
296 xml_increment_list(pcmk__output_t *out) {
297  /* This function intentially left blank */
298 }
299 
300 static void
301 xml_end_list(pcmk__output_t *out) {
302  private_data_t *priv = out->priv;
303 
304  CRM_ASSERT(priv != NULL);
305 
306  if (priv->legacy_xml || simple_list) {
307  g_queue_pop_tail(priv->parent_q);
308  } else {
309  char *buf = NULL;
310  xmlNodePtr node;
311 
312  node = g_queue_pop_tail(priv->parent_q);
313  buf = crm_strdup_printf("%lu", xmlChildElementCount(node));
314  xmlSetProp(node, (pcmkXmlStr) "count", (pcmkXmlStr) buf);
315  free(buf);
316  }
317 }
318 
320 pcmk__mk_xml_output(char **argv) {
321  pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
322 
323  if (retval == NULL) {
324  return NULL;
325  }
326 
327  retval->fmt_name = "xml";
328  retval->request = argv == NULL ? NULL : g_strjoinv(" ", argv);
329  retval->supports_quiet = false;
330 
331  retval->init = xml_init;
332  retval->free_priv = xml_free_priv;
333  retval->finish = xml_finish;
334  retval->reset = xml_reset;
335 
337  retval->message = pcmk__call_message;
338 
339  retval->subprocess_output = xml_subprocess_output;
340  retval->version = xml_version;
341  retval->info = xml_info;
342  retval->err = xml_err;
343  retval->output_xml = xml_output_xml;
344 
345  retval->begin_list = xml_begin_list;
346  retval->list_item = xml_list_item;
347  retval->increment_list = xml_increment_list;
348  retval->end_list = xml_end_list;
349 
350  return retval;
351 }
352 
353 xmlNodePtr
355  xmlNodePtr node = pcmk__output_create_xml_node(out, name);
356  pcmk__output_xml_push_parent(out, node);
357  return node;
358 }
359 
360 void
362  private_data_t *priv = out->priv;
363 
364  CRM_ASSERT(priv != NULL);
365  CRM_ASSERT(node != NULL);
366 
367  xmlAddChild(g_queue_peek_tail(priv->parent_q), node);
368 }
369 
370 xmlNodePtr
372  private_data_t *priv = out->priv;
373 
374  CRM_ASSERT(priv != NULL);
375 
376  return create_xml_node(g_queue_peek_tail(priv->parent_q), name);
377 }
378 
379 xmlNodePtr
380 pcmk__output_create_xml_text_node(pcmk__output_t *out, const char *name, const char *content) {
381  xmlNodePtr node = pcmk__output_create_xml_node(out, name);
382  xmlNodeSetContent(node, (pcmkXmlStr) content);
383  return node;
384 }
385 
386 void
388  private_data_t *priv = out->priv;
389 
390  CRM_ASSERT(priv != NULL);
391  CRM_ASSERT(parent != NULL);
392 
393  g_queue_push_tail(priv->parent_q, parent);
394 }
395 
396 void
398  private_data_t *priv = out->priv;
399 
400  CRM_ASSERT(priv != NULL);
401  CRM_ASSERT(g_queue_get_length(priv->parent_q) > 0);
402 
403  g_queue_pop_tail(priv->parent_q);
404 }
405 
406 xmlNodePtr
408  private_data_t *priv = out->priv;
409 
410  CRM_ASSERT(priv != NULL);
411 
412  /* If queue is empty NULL will be returned */
413  return g_queue_peek_tail(priv->parent_q);
414 }
bool(* init)(pcmk__output_t *out)
Definition: output.h:215
A dumping ground.
pcmk__output_t * pcmk__mk_xml_output(char **argv)
Definition: output_xml.c:320
Formatted output for pacemaker tools.
void pcmk__output_xml_pop_parent(pcmk__output_t *out)
Definition: output_xml.c:397
xmlNodePtr pcmk__output_create_xml_node(pcmk__output_t *out, const char *name)
Definition: output_xml.c:371
void(* register_message)(pcmk__output_t *out, const char *message_id, pcmk__message_fn_t fn)
Definition: output.h:293
const char * fmt_name
The name of this output formatter.
Definition: output.h:157
void(* reset)(pcmk__output_t *out)
Definition: output.h:280
void(*) void(* err)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
Definition: output.h:361
xmlNodePtr pcmk__output_xml_peek_parent(pcmk__output_t *out)
Definition: output_xml.c:407
#define VERSION
Definition: config.h:619
bool supports_quiet
Does this formatter support a special quiet mode?
Definition: output.h:174
int(* message)(pcmk__output_t *out, const char *message_id,...)
Definition: output.h:311
enum crm_exit_e crm_exit_t
#define PACEMAKER_VERSION
Definition: config.h:509
xmlDoc * getDocPtr(xmlNode *node)
Definition: xml.c:1938
void * priv
Implementation-specific private data.
Definition: output.h:199
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:2142
int pcmk__call_message(pcmk__output_t *out, const char *message_id,...)
Definition: output.c:116
void(* end_list)(pcmk__output_t *out)
Definition: output.h:432
const char * crm_exit_str(crm_exit_t exit_code)
Definition: results.c:519
void(* begin_list)(pcmk__output_t *out, const char *singular_noun, const char *plural_noun, const char *format,...) G_GNUC_PRINTF(4
Definition: output.h:392
xmlNode * pcmk_create_xml_text_node(xmlNode *parent, const char *name, const char *content)
Definition: xml.c:2001
#define BUILD_VERSION
Definition: config.h:8
struct private_data_s private_data_t
void(* subprocess_output)(pcmk__output_t *out, int exit_status, const char *proc_stdout, const char *proc_stderr)
Definition: output.h:322
xmlNodePtr pcmk__output_create_xml_text_node(pcmk__output_t *out, const char *name, const char *content)
Definition: output_xml.c:380
Wrappers for and extensions to libxml2.
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:1976
FILE * dest
Where output should be written.
Definition: output.h:182
const xmlChar * pcmkXmlStr
Definition: xml.h:51
void(*) void(*) void(* increment_list)(pcmk__output_t *out)
Definition: output.h:420
void(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
Definition: output.h:347
#define CRM_ASSERT(expr)
Definition: results.h:42
void(* finish)(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest)
Definition: output.h:262
void pcmk__output_xml_push_parent(pcmk__output_t *out, xmlNodePtr node)
Definition: output_xml.c:387
void(* free_priv)(pcmk__output_t *out)
Definition: output.h:226
gchar * request
A copy of the request that generated this output.
Definition: output.h:165
This structure contains everything that makes up a single output formatter.
Definition: output.h:153
void void xmlNodePtr pcmk__output_xml_create_parent(pcmk__output_t *out, const char *name)
Definition: output_xml.c:354
char data[0]
Definition: internal.h:90
void(* version)(pcmk__output_t *out, bool extended)
Definition: output.h:333
void(*) void(*) void(* output_xml)(pcmk__output_t *out, const char *name, const char *buf)
Definition: output.h:371
GOptionEntry pcmk__xml_output_entries[]
Definition: output_xml.c:27
char * name
Definition: pcmk_fence.c:30
void pcmk__output_xml_add_node(pcmk__output_t *out, xmlNodePtr node)
Definition: output_xml.c:361
#define CRM_FEATURES
Definition: config.h:35
void pcmk__register_message(pcmk__output_t *out, const char *message_id, pcmk__message_fn_t fn)
Definition: output.c:134
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
char * dump_xml_formatted_with_text(xmlNode *msg)
Definition: xml.c:3299
#define PCMK__API_VERSION
Definition: output.h:30
void(*) void(* list_item)(pcmk__output_t *out, const char *name, const char *format,...) G_GNUC_PRINTF(3
Definition: output.h:405