pacemaker  3.0.0-d8340737c4
Scalable High-Availability cluster resource manager
xml_display.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2024 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 #include <crm_internal.h>
11 
12 #include <libxml/tree.h>
13 
14 #include <crm/crm.h>
15 #include <crm/common/xml.h>
16 #include <crm/common/xml_internal.h> // PCMK__XML_LOG_BASE, etc.
17 #include "crmcommon_private.h"
18 
19 static int show_xml_node(pcmk__output_t *out, GString *buffer,
20  const char *prefix, const xmlNode *data, int depth,
21  uint32_t options);
22 
23 // Log an XML library error
24 void
25 pcmk__log_xmllib_err(void *ctx, const char *fmt, ...)
26 {
27  va_list ap;
28 
29  va_start(ap, fmt);
31  {
32  PCMK__XML_LOG_BASE(LOG_ERR, TRUE,
33  crm_abort(__FILE__, __PRETTY_FUNCTION__,
34  __LINE__, "xml library error", TRUE,
35  TRUE),
36  "XML Error: ", fmt, ap);
37  },
38  {
39  PCMK__XML_LOG_BASE(LOG_ERR, TRUE, 0, "XML Error: ", fmt, ap);
40  }
41  );
42  va_end(ap);
43 }
44 
58 static int
59 show_xml_comment(pcmk__output_t *out, const xmlNode *data, int depth,
60  uint32_t options)
61 {
62  if (pcmk_is_set(options, pcmk__xml_fmt_open)) {
63  int width = pcmk_is_set(options, pcmk__xml_fmt_pretty)? (2 * depth) : 0;
64 
65  return out->info(out, "%*s<!--%s-->",
66  width, "", (const char *) data->content);
67  }
68  return pcmk_rc_no_output;
69 }
70 
90 static int
91 show_xml_element(pcmk__output_t *out, GString *buffer, const char *prefix,
92  const xmlNode *data, int depth, uint32_t options)
93 {
94  int spaces = pcmk_is_set(options, pcmk__xml_fmt_pretty)? (2 * depth) : 0;
95  int rc = pcmk_rc_no_output;
96 
97  if (pcmk_is_set(options, pcmk__xml_fmt_open)) {
98  const char *hidden = crm_element_value(data, PCMK__XA_HIDDEN);
99 
100  g_string_truncate(buffer, 0);
101 
102  for (int lpc = 0; lpc < spaces; lpc++) {
103  g_string_append_c(buffer, ' ');
104  }
105  pcmk__g_strcat(buffer, "<", data->name, NULL);
106 
107  for (const xmlAttr *attr = pcmk__xe_first_attr(data); attr != NULL;
108  attr = attr->next) {
109  xml_node_private_t *nodepriv = attr->_private;
110  const char *p_name = (const char *) attr->name;
111  const char *p_value = pcmk__xml_attr_value(attr);
112  gchar *p_copy = NULL;
113 
114  if (pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
115  continue;
116  }
117 
118  if ((hidden != NULL) && (p_name[0] != '\0')
119  && (strstr(hidden, p_name) != NULL)) {
120 
121  p_value = "*****";
122 
123  } else {
124  p_copy = pcmk__xml_escape(p_value, true);
125  p_value = p_copy;
126  }
127 
128  pcmk__g_strcat(buffer, " ", p_name, "=\"",
129  pcmk__s(p_value, "<null>"), "\"", NULL);
130  g_free(p_copy);
131  }
132 
133  if ((data->children != NULL)
134  && pcmk_is_set(options, pcmk__xml_fmt_children)) {
135  g_string_append_c(buffer, '>');
136 
137  } else {
138  g_string_append(buffer, "/>");
139  }
140 
141  rc = out->info(out, "%s%s%s",
142  pcmk__s(prefix, ""), pcmk__str_empty(prefix)? "" : " ",
143  buffer->str);
144  }
145 
146  if (data->children == NULL) {
147  return rc;
148  }
149 
150  if (pcmk_is_set(options, pcmk__xml_fmt_children)) {
151  for (const xmlNode *child = pcmk__xml_first_child(data); child != NULL;
152  child = pcmk__xml_next(child)) {
153 
154  int temp_rc = show_xml_node(out, buffer, prefix, child, depth + 1,
155  options
158  rc = pcmk__output_select_rc(rc, temp_rc);
159  }
160  }
161 
162  if (pcmk_is_set(options, pcmk__xml_fmt_close)) {
163  int temp_rc = out->info(out, "%s%s%*s</%s>",
164  pcmk__s(prefix, ""),
165  pcmk__str_empty(prefix)? "" : " ",
166  spaces, "", data->name);
167  rc = pcmk__output_select_rc(rc, temp_rc);
168  }
169 
170  return rc;
171 }
172 
192 static int
193 show_xml_node(pcmk__output_t *out, GString *buffer, const char *prefix,
194  const xmlNode *data, int depth, uint32_t options)
195 {
196  switch (data->type) {
197  case XML_COMMENT_NODE:
198  return show_xml_comment(out, data, depth, options);
199  case XML_ELEMENT_NODE:
200  return show_xml_element(out, buffer, prefix, data, depth, options);
201  default:
202  return pcmk_rc_no_output;
203  }
204 }
205 
220 int
221 pcmk__xml_show(pcmk__output_t *out, const char *prefix, const xmlNode *data,
222  int depth, uint32_t options)
223 {
224  int rc = pcmk_rc_no_output;
225  GString *buffer = NULL;
226 
227  pcmk__assert(out != NULL);
228  CRM_CHECK(depth >= 0, depth = 0);
229 
230  if (data == NULL) {
231  return rc;
232  }
233 
234  /* Allocate a buffer once, for show_xml_node() to truncate and reuse in
235  * recursive calls
236  */
237  buffer = g_string_sized_new(1024);
238  rc = show_xml_node(out, buffer, prefix, data, depth, options);
239  g_string_free(buffer, TRUE);
240 
241  return rc;
242 }
243 
257 static int
258 show_xml_changes_recursive(pcmk__output_t *out, const xmlNode *data, int depth,
259  uint32_t options)
260 {
261  /* @COMPAT: When log_data_element() is removed, we can remove the options
262  * argument here and instead hard-code pcmk__xml_log_pretty.
263  */
264  xml_node_private_t *nodepriv = (xml_node_private_t *) data->_private;
265  int rc = pcmk_rc_no_output;
266  int temp_rc = pcmk_rc_no_output;
267 
268  if (pcmk_all_flags_set(nodepriv->flags, pcmk__xf_dirty|pcmk__xf_created)) {
269  // Newly created
270  return pcmk__xml_show(out, PCMK__XML_PREFIX_CREATED, data, depth,
271  options
275  }
276 
277  if (pcmk_is_set(nodepriv->flags, pcmk__xf_dirty)) {
278  // Modified or moved
279  bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
280  int spaces = pretty? (2 * depth) : 0;
281  const char *prefix = PCMK__XML_PREFIX_MODIFIED;
282 
283  if (pcmk_is_set(nodepriv->flags, pcmk__xf_moved)) {
284  prefix = PCMK__XML_PREFIX_MOVED;
285  }
286 
287  // Log opening tag
288  rc = pcmk__xml_show(out, prefix, data, depth,
289  options|pcmk__xml_fmt_open);
290 
291  // Log changes to attributes
292  for (const xmlAttr *attr = pcmk__xe_first_attr(data); attr != NULL;
293  attr = attr->next) {
294  const char *name = (const char *) attr->name;
295 
296  nodepriv = attr->_private;
297 
298  if (pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
299  const char *value = pcmk__xml_attr_value(attr);
300 
301  temp_rc = out->info(out, "%s %*s @%s=%s",
302  PCMK__XML_PREFIX_DELETED, spaces, "", name,
303  value);
304 
305  } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_dirty)) {
306  const char *value = pcmk__xml_attr_value(attr);
307 
308  if (pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
309  prefix = PCMK__XML_PREFIX_CREATED;
310 
311  } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_modified)) {
312  prefix = PCMK__XML_PREFIX_MODIFIED;
313 
314  } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_moved)) {
315  prefix = PCMK__XML_PREFIX_MOVED;
316 
317  } else {
318  prefix = PCMK__XML_PREFIX_MODIFIED;
319  }
320 
321  temp_rc = out->info(out, "%s %*s @%s=%s",
322  prefix, spaces, "", name, value);
323  }
324  rc = pcmk__output_select_rc(rc, temp_rc);
325  }
326 
327  // Log changes to children
328  for (const xmlNode *child = pcmk__xml_first_child(data); child != NULL;
329  child = pcmk__xml_next(child)) {
330  temp_rc = show_xml_changes_recursive(out, child, depth + 1,
331  options);
332  rc = pcmk__output_select_rc(rc, temp_rc);
333  }
334 
335  // Log closing tag
336  temp_rc = pcmk__xml_show(out, PCMK__XML_PREFIX_MODIFIED, data, depth,
337  options|pcmk__xml_fmt_close);
338  return pcmk__output_select_rc(rc, temp_rc);
339  }
340 
341  // This node hasn't changed, but check its children
342  for (const xmlNode *child = pcmk__xml_first_child(data); child != NULL;
343  child = pcmk__xml_next(child)) {
344  temp_rc = show_xml_changes_recursive(out, child, depth + 1, options);
345  rc = pcmk__output_select_rc(rc, temp_rc);
346  }
347  return rc;
348 }
349 
361 int
362 pcmk__xml_show_changes(pcmk__output_t *out, const xmlNode *xml)
363 {
364  xml_doc_private_t *docpriv = NULL;
365  int rc = pcmk_rc_no_output;
366  int temp_rc = pcmk_rc_no_output;
367 
368  pcmk__assert((out != NULL) && (xml != NULL) && (xml->doc != NULL));
369 
370  docpriv = xml->doc->_private;
371  if (!pcmk_is_set(docpriv->flags, pcmk__xf_dirty)) {
372  return rc;
373  }
374 
375  for (const GList *iter = docpriv->deleted_objs; iter != NULL;
376  iter = iter->next) {
377  const pcmk__deleted_xml_t *deleted_obj = iter->data;
378 
379  if (deleted_obj->position >= 0) {
380  temp_rc = out->info(out, PCMK__XML_PREFIX_DELETED " %s (%d)",
381  deleted_obj->path, deleted_obj->position);
382  } else {
383  temp_rc = out->info(out, PCMK__XML_PREFIX_DELETED " %s",
384  deleted_obj->path);
385  }
386  rc = pcmk__output_select_rc(rc, temp_rc);
387  }
388 
389  temp_rc = show_xml_changes_recursive(out, xml, 0, pcmk__xml_fmt_pretty);
390  return pcmk__output_select_rc(rc, temp_rc);
391 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:213
A dumping ground.
char * pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type)
Definition: xml.c:964
char data[0]
Definition: cpg.c:58
#define pcmk__if_tracing(if_action, else_action)
const char * name
Definition: cib.c:26
void crm_abort(const char *file, const char *function, int line, const char *condition, gboolean do_core, gboolean do_fork)
Definition: results.c:215
Include the opening tag of an XML element, and include XML comments.
Definition: xml_internal.h:152
#define PCMK__XML_PREFIX_MOVED
XML has been moved.
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
Include indentation and newlines.
Definition: xml_internal.h:149
#define PCMK__XML_LOG_BASE(priority, dechunk, postemit, prefix, fmt, ap)
Base for directing lib{xml2,xslt} log into standard libqb backend.
Definition: xml_internal.h:81
Include the children of an XML element.
Definition: xml_internal.h:155
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: xml_element.c:1168
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1297
int pcmk__xml_show(pcmk__output_t *out, const char *prefix, const xmlNode *data, int depth, uint32_t options)
Definition: xml_display.c:221
#define PCMK__XA_HIDDEN
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:80
int pcmk__xml_show_changes(pcmk__output_t *out, const xmlNode *xml)
Definition: xml_display.c:362
Wrappers for and extensions to libxml2.
void pcmk__log_xmllib_err(void *ctx, const char *fmt,...)
Definition: xml_display.c:25
#define PCMK__XML_PREFIX_CREATED
XML is newly created.
#define pcmk__assert(expr)
Include the closing tag of an XML element.
Definition: xml_internal.h:158
#define PCMK__XML_PREFIX_DELETED
XML has been deleted.
This structure contains everything that makes up a single output formatter.
#define PCMK__XML_PREFIX_MODIFIED
XML has been modified.