pacemaker 3.0.1-16e74fc4da
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
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
19static 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
24void
25pcmk__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
58static int
59show_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
90static int
91show_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
192static int
193show_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
220int
221pcmk__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
257static int
258show_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)) {
310
311 } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_modified)) {
313
314 } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_moved)) {
315 prefix = PCMK__XML_PREFIX_MOVED;
316
317 } else {
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
361int
362pcmk__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}
const char * name
Definition cib.c:26
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:80
char data[0]
Definition cpg.c:10
A dumping ground.
#define PCMK__XML_PREFIX_MOVED
XML has been moved.
#define PCMK__XML_PREFIX_CREATED
XML is newly created.
#define PCMK__XML_PREFIX_DELETED
XML has been deleted.
#define PCMK__XML_PREFIX_MODIFIED
XML has been modified.
#define CRM_CHECK(expr, failure_action)
Definition logging.h:213
#define pcmk__if_tracing(if_action, else_action)
@ pcmk_rc_no_output
Definition results.h:128
void crm_abort(const char *file, const char *function, int line, const char *condition, gboolean do_core, gboolean do_fork)
Definition results.c:215
#define pcmk__assert(expr)
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1299
int position
Position of the deleted node among its siblings.
gchar * path
XPath expression identifying the deleted node.
This structure contains everything that makes up a single output formatter.
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
GList * deleted_objs
XML nodes marked as deleted (list of pcmk__deleted_xml_t)
uint32_t flags
Group of enum pcmk__xml_flags
Wrappers for and extensions to libxml2.
void pcmk__log_xmllib_err(void *ctx, const char *fmt,...)
Definition xml_display.c:25
int pcmk__xml_show_changes(pcmk__output_t *out, const xmlNode *xml)
int pcmk__xml_show(pcmk__output_t *out, const char *prefix, const xmlNode *data, int depth, uint32_t options)
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
@ pcmk__xml_fmt_close
Include the closing tag of an XML element.
@ pcmk__xml_fmt_children
Include the children of an XML element.
@ pcmk__xml_fmt_open
Include the opening tag of an XML element, and include XML comments.
@ pcmk__xml_fmt_pretty
Include indentation and newlines.
@ pcmk__xf_deleted
Node was deleted (set for attribute only)
@ pcmk__xf_created
Node was created.
@ pcmk__xf_dirty
@ pcmk__xf_modified
Node was modified.
@ pcmk__xf_moved
Node was moved.
#define PCMK__XML_LOG_BASE(priority, dechunk, postemit, prefix, fmt, ap)
Base for directing lib{xml2,xslt} log into standard libqb backend.
char * pcmk__xml_escape(const char *text, enum pcmk__xml_escape_type type)
Definition xml.c:991
#define PCMK__XA_HIDDEN