pacemaker  2.0.3-4b1f869f0f
Scalable High-Availability cluster resource manager
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  char *rc_as_str = crm_itoa(exit_status);
121 
122  node = create_xml_node(priv->root, "status");
123  xmlSetProp(node, (pcmkXmlStr) "code", (pcmkXmlStr) rc_as_str);
124  xmlSetProp(node, (pcmkXmlStr) "message", (pcmkXmlStr) crm_exit_str(exit_status));
125 
126  if (g_slist_length(priv->errors) > 0) {
127  xmlNodePtr errors_node = create_xml_node(node, "errors");
128  g_slist_foreach(priv->errors, add_error_node, (gpointer) errors_node);
129  }
130 
131  free(rc_as_str);
132  }
133 
134  if (print) {
135  char *buf = dump_xml_formatted_with_text(priv->root);
136  fprintf(out->dest, "%s", buf);
137  free(buf);
138  }
139 
140  if (copy_dest != NULL) {
141  *copy_dest = copy_xml(priv->root);
142  }
143 }
144 
145 static void
146 xml_reset(pcmk__output_t *out) {
147  char *buf = NULL;
148  private_data_t *priv = out->priv;
149 
150  CRM_ASSERT(priv != NULL);
151 
152  buf = dump_xml_formatted_with_text(priv->root);
153  fprintf(out->dest, "%s", buf);
154 
155  free(buf);
156  xml_free_priv(out);
157  xml_init(out);
158 }
159 
160 static void
161 xml_subprocess_output(pcmk__output_t *out, int exit_status,
162  const char *proc_stdout, const char *proc_stderr) {
163  xmlNodePtr node, child_node;
164  char *rc_as_str = NULL;
165 
166  rc_as_str = crm_itoa(exit_status);
167 
168  node = pcmk__output_xml_create_parent(out, "command");
169  xmlSetProp(node, (pcmkXmlStr) "code", (pcmkXmlStr) rc_as_str);
170 
171  if (proc_stdout != NULL) {
172  child_node = pcmk_create_xml_text_node(node, "output", proc_stdout);
173  xmlSetProp(child_node, (pcmkXmlStr) "source", (pcmkXmlStr) "stdout");
174  }
175 
176  if (proc_stderr != NULL) {
177  child_node = pcmk_create_xml_text_node(node, "output", proc_stderr);
178  xmlSetProp(child_node, (pcmkXmlStr) "source", (pcmkXmlStr) "stderr");
179  }
180 
181  pcmk__output_xml_add_node(out, node);
182  free(rc_as_str);
183 }
184 
185 static void
186 xml_version(pcmk__output_t *out, bool extended) {
187  xmlNodePtr node;
188  private_data_t *priv = out->priv;
189  CRM_ASSERT(priv != NULL);
190 
191  node = pcmk__output_create_xml_node(out, "version");
192  xmlSetProp(node, (pcmkXmlStr) "program", (pcmkXmlStr) "Pacemaker");
193  xmlSetProp(node, (pcmkXmlStr) "version", (pcmkXmlStr) PACEMAKER_VERSION);
194  xmlSetProp(node, (pcmkXmlStr) "author", (pcmkXmlStr) "Andrew Beekhof");
195  xmlSetProp(node, (pcmkXmlStr) "build", (pcmkXmlStr) BUILD_VERSION);
196  xmlSetProp(node, (pcmkXmlStr) "features", (pcmkXmlStr) CRM_FEATURES);
197 }
198 
199 G_GNUC_PRINTF(2, 3)
200 static void
201 xml_err(pcmk__output_t *out, const char *format, ...) {
202  private_data_t *priv = out->priv;
203  int len = 0;
204  char *buf = NULL;
205  va_list ap;
206 
207  CRM_ASSERT(priv != NULL);
208  va_start(ap, format);
209  len = vasprintf(&buf, format, ap);
210  CRM_ASSERT(len > 0);
211  va_end(ap);
212 
213  priv->errors = g_slist_append(priv->errors, buf);
214 }
215 
216 G_GNUC_PRINTF(2, 3)
217 static void
218 xml_info(pcmk__output_t *out, const char *format, ...) {
219  /* This function intentially left blank */
220 }
221 
222 static void
223 xml_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
224  xmlNodePtr parent = NULL;
225  xmlNodePtr cdata_node = NULL;
226  private_data_t *priv = out->priv;
227 
228  CRM_ASSERT(priv != NULL);
229 
230  parent = pcmk__output_create_xml_node(out, name);
231  cdata_node = xmlNewCDataBlock(getDocPtr(parent), (pcmkXmlStr) buf, strlen(buf));
232  xmlAddChild(parent, cdata_node);
233 }
234 
235 G_GNUC_PRINTF(4, 5)
236 static void
237 xml_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
238  const char *format, ...) {
239  va_list ap;
240  char *buf = NULL;
241  int len;
242 
243  va_start(ap, format);
244  len = vasprintf(&buf, format, ap);
245  CRM_ASSERT(len >= 0);
246  va_end(ap);
247 
248  if (legacy_xml || simple_list) {
250  } else {
251  xmlNodePtr list_node = NULL;
252 
253  list_node = pcmk__output_xml_create_parent(out, "list");
254  xmlSetProp(list_node, (pcmkXmlStr) "name", (pcmkXmlStr) buf);
255  }
256 
257  free(buf);
258 }
259 
260 G_GNUC_PRINTF(3, 4)
261 static void
262 xml_list_item(pcmk__output_t *out, const char *name, const char *format, ...) {
263  private_data_t *priv = out->priv;
264  xmlNodePtr item_node = NULL;
265  va_list ap;
266  char *buf = NULL;
267  int len;
268 
269  CRM_ASSERT(priv != NULL);
270 
271  va_start(ap, format);
272  len = vasprintf(&buf, format, ap);
273  CRM_ASSERT(len >= 0);
274  va_end(ap);
275 
276  item_node = pcmk__output_create_xml_text_node(out, "item", buf);
277  free(buf);
278 
279  xmlSetProp(item_node, (pcmkXmlStr) "name", (pcmkXmlStr) name);
280 }
281 
282 static void
283 xml_increment_list(pcmk__output_t *out) {
284  /* This function intentially left blank */
285 }
286 
287 static void
288 xml_end_list(pcmk__output_t *out) {
289  private_data_t *priv = out->priv;
290 
291  CRM_ASSERT(priv != NULL);
292 
293  if (priv->legacy_xml || simple_list) {
294  g_queue_pop_tail(priv->parent_q);
295  } else {
296  char *buf = NULL;
297  xmlNodePtr node;
298 
299  node = g_queue_pop_tail(priv->parent_q);
300  buf = crm_strdup_printf("%lu", xmlChildElementCount(node));
301  xmlSetProp(node, (pcmkXmlStr) "count", (pcmkXmlStr) buf);
302  free(buf);
303  }
304 }
305 
307 pcmk__mk_xml_output(char **argv) {
308  pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
309 
310  if (retval == NULL) {
311  return NULL;
312  }
313 
314  retval->fmt_name = "xml";
315  retval->request = g_strjoinv(" ", argv);
316  retval->supports_quiet = false;
317 
318  retval->init = xml_init;
319  retval->free_priv = xml_free_priv;
320  retval->finish = xml_finish;
321  retval->reset = xml_reset;
322 
324  retval->message = pcmk__call_message;
325 
326  retval->subprocess_output = xml_subprocess_output;
327  retval->version = xml_version;
328  retval->info = xml_info;
329  retval->err = xml_err;
330  retval->output_xml = xml_output_xml;
331 
332  retval->begin_list = xml_begin_list;
333  retval->list_item = xml_list_item;
334  retval->increment_list = xml_increment_list;
335  retval->end_list = xml_end_list;
336 
337  return retval;
338 }
339 
340 xmlNodePtr
342  xmlNodePtr node = pcmk__output_create_xml_node(out, name);
343  pcmk__output_xml_push_parent(out, node);
344  return node;
345 }
346 
347 void
349  private_data_t *priv = out->priv;
350 
351  CRM_ASSERT(priv != NULL);
352  CRM_ASSERT(node != NULL);
353 
354  xmlAddChild(g_queue_peek_tail(priv->parent_q), node);
355 }
356 
357 xmlNodePtr
359  private_data_t *priv = out->priv;
360 
361  CRM_ASSERT(priv != NULL);
362 
363  return create_xml_node(g_queue_peek_tail(priv->parent_q), name);
364 }
365 
366 xmlNodePtr
367 pcmk__output_create_xml_text_node(pcmk__output_t *out, const char *name, const char *content) {
368  xmlNodePtr node = pcmk__output_create_xml_node(out, name);
369  xmlNodeSetContent(node, (pcmkXmlStr) content);
370  return node;
371 }
372 
373 void
375  private_data_t *priv = out->priv;
376 
377  CRM_ASSERT(priv != NULL);
378  CRM_ASSERT(parent != NULL);
379 
380  g_queue_push_tail(priv->parent_q, parent);
381 }
382 
383 void
385  private_data_t *priv = out->priv;
386 
387  CRM_ASSERT(priv != NULL);
388  CRM_ASSERT(g_queue_get_length(priv->parent_q) > 0);
389 
390  g_queue_pop_tail(priv->parent_q);
391 }
392 
393 xmlNodePtr
395  private_data_t *priv = out->priv;
396 
397  CRM_ASSERT(priv != NULL);
398 
399  /* If queue is empty NULL will be returned */
400  return g_queue_peek_tail(priv->parent_q);
401 }
pcmk__output_s::finish
void(* finish)(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest)
Definition: output.h:259
pcmk__output_s::init
bool(* init)(pcmk__output_t *out)
Definition: output.h:212
pcmk__output_s::output_xml
void(*) void(*) void(* output_xml)(pcmk__output_t *out, const char *name, const char *buf)
Definition: output.h:368
pcmk__output_xml_create_parent
xmlNodePtr pcmk__output_xml_create_parent(pcmk__output_t *out, const char *name)
Definition: output_xml.c:341
pcmk__output_s::free_priv
void(* free_priv)(pcmk__output_t *out)
Definition: output.h:223
BUILD_VERSION
#define BUILD_VERSION
Definition: config.h:8
data
char data[0]
Definition: internal.h:90
pcmk__output_s::subprocess_output
void(* subprocess_output)(pcmk__output_t *out, int exit_status, const char *proc_stdout, const char *proc_stderr)
Definition: output.h:319
getDocPtr
xmlDoc * getDocPtr(xmlNode *node)
Definition: xml.c:1932
pcmk__register_message
void pcmk__register_message(pcmk__output_t *out, const char *message_id, pcmk__message_fn_t fn)
Definition: output.c:127
pcmk__output_s::priv
void * priv
Implementation-specific private data.
Definition: output.h:196
create_xml_node
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:1970
private_data_t
struct private_data_s private_data_t
copy_xml
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:2136
pcmk__output_s::request
char * request
A copy of the request that generated this output.
Definition: output.h:162
pcmk__output_s::version
void(* version)(pcmk__output_t *out, bool extended)
Definition: output.h:330
pcmk__output_s::list_item
void(*) void(* list_item)(pcmk__output_t *out, const char *name, const char *format,...) G_GNUC_PRINTF(3
Definition: output.h:402
crm_exit_t
enum crm_exit_e crm_exit_t
xml.h
Wrappers for and extensions to libxml2.
pcmk__output_s::message
int(* message)(pcmk__output_t *out, const char *message_id,...)
Definition: output.h:308
crm_exit_str
const char * crm_exit_str(crm_exit_t exit_code)
Definition: results.c:308
pcmk__call_message
int pcmk__call_message(pcmk__output_t *out, const char *message_id,...)
Definition: output.c:109
pcmk__output_s::err
void(*) void(* err)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
Definition: output.h:358
pcmk__mk_xml_output
pcmk__output_t * pcmk__mk_xml_output(char **argv)
Definition: output_xml.c:307
pcmk__output_s::end_list
void(* end_list)(pcmk__output_t *out)
Definition: output.h:429
crm_strdup_printf
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
pcmk__output_s::increment_list
void(*) void(*) void(* increment_list)(pcmk__output_t *out)
Definition: output.h:417
CRM_FEATURES
#define CRM_FEATURES
Definition: config.h:35
pcmk__output_s::begin_list
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:389
pcmk__output_xml_peek_parent
xmlNodePtr pcmk__output_xml_peek_parent(pcmk__output_t *out)
Definition: output_xml.c:394
pcmk__output_s::reset
void(* reset)(pcmk__output_t *out)
Definition: output.h:277
private_data_t
struct private_data_s private_data_t
pcmk__output_create_xml_text_node
xmlNodePtr pcmk__output_create_xml_text_node(pcmk__output_t *out, const char *name, const char *content)
Definition: output_xml.c:367
pcmk__output_s
This structure contains everything that makes up a single output formatter.
Definition: output.h:150
dump_xml_formatted_with_text
char * dump_xml_formatted_with_text(xmlNode *msg)
Definition: xml.c:3284
pcmkXmlStr
const typedef xmlChar * pcmkXmlStr
Definition: xml.h:51
pcmk__output_s::supports_quiet
bool supports_quiet
Does this formatter support a special quiet mode?
Definition: output.h:171
pcmk__output_s::fmt_name
const char * fmt_name
The name of this output formatter.
Definition: output.h:154
pcmk__output_s::dest
FILE * dest
Where output should be written.
Definition: output.h:179
CRM_ASSERT
#define CRM_ASSERT(expr)
Definition: results.h:42
pcmk__output_xml_add_node
void pcmk__output_xml_add_node(pcmk__output_t *out, xmlNodePtr node)
Definition: output_xml.c:348
pcmk__output_s::info
void(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
Definition: output.h:344
config.h
VERSION
#define VERSION
Definition: config.h:627
pcmk__output_create_xml_node
xmlNodePtr pcmk__output_create_xml_node(pcmk__output_t *out, const char *name)
Definition: output_xml.c:358
pcmk__output_xml_pop_parent
void pcmk__output_xml_pop_parent(pcmk__output_t *out)
Definition: output_xml.c:384
pcmk__xml_output_entries
GOptionEntry pcmk__xml_output_entries[]
Definition: output_xml.c:27
PACEMAKER_VERSION
#define PACEMAKER_VERSION
Definition: config.h:517
PCMK__API_VERSION
#define PCMK__API_VERSION
Definition: output.h:30
pcmk__output_xml_push_parent
void pcmk__output_xml_push_parent(pcmk__output_t *out, xmlNodePtr parent)
Definition: output_xml.c:374
pcmk_create_xml_text_node
xmlNode * pcmk_create_xml_text_node(xmlNode *parent, const char *name, const char *content)
Definition: xml.c:1995
crm.h
A dumping ground.
output.h
Formatted output for pacemaker tools.
pcmk__output_s::register_message
void(* register_message)(pcmk__output_t *out, const char *message_id, pcmk__message_fn_t fn)
Definition: output.h:290