pacemaker 2.1.8-2.1.8
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 // @COMPAT Remove when v1 patchsets are removed
119 if (pcmk_any_flags_set(options,
122 && (strcmp(PCMK__XA_CRM_DIFF_MARKER, p_name) == 0)) {
123 continue;
124 }
125
126 if ((hidden != NULL) && (p_name[0] != '\0')
127 && (strstr(hidden, p_name) != NULL)) {
128
129 p_value = "*****";
130
131 } else {
132 p_copy = pcmk__xml_escape(p_value, true);
133 p_value = p_copy;
134 }
135
136 pcmk__g_strcat(buffer, " ", p_name, "=\"",
137 pcmk__s(p_value, "<null>"), "\"", NULL);
138 g_free(p_copy);
139 }
140
141 if ((data->children != NULL)
142 && pcmk_is_set(options, pcmk__xml_fmt_children)) {
143 g_string_append_c(buffer, '>');
144
145 } else {
146 g_string_append(buffer, "/>");
147 }
148
149 rc = out->info(out, "%s%s%s",
150 pcmk__s(prefix, ""), pcmk__str_empty(prefix)? "" : " ",
151 buffer->str);
152 }
153
154 if (data->children == NULL) {
155 return rc;
156 }
157
158 if (pcmk_is_set(options, pcmk__xml_fmt_children)) {
159 for (const xmlNode *child = pcmk__xml_first_child(data); child != NULL;
160 child = pcmk__xml_next(child)) {
161
162 int temp_rc = show_xml_node(out, buffer, prefix, child, depth + 1,
163 options
166 rc = pcmk__output_select_rc(rc, temp_rc);
167 }
168 }
169
170 if (pcmk_is_set(options, pcmk__xml_fmt_close)) {
171 int temp_rc = out->info(out, "%s%s%*s</%s>",
172 pcmk__s(prefix, ""),
173 pcmk__str_empty(prefix)? "" : " ",
174 spaces, "", data->name);
175 rc = pcmk__output_select_rc(rc, temp_rc);
176 }
177
178 return rc;
179}
180
200static int
201show_xml_node(pcmk__output_t *out, GString *buffer, const char *prefix,
202 const xmlNode *data, int depth, uint32_t options)
203{
204 switch (data->type) {
205 case XML_COMMENT_NODE:
206 return show_xml_comment(out, data, depth, options);
207 case XML_ELEMENT_NODE:
208 return show_xml_element(out, buffer, prefix, data, depth, options);
209 default:
210 return pcmk_rc_no_output;
211 }
212}
213
228int
229pcmk__xml_show(pcmk__output_t *out, const char *prefix, const xmlNode *data,
230 int depth, uint32_t options)
231{
232 int rc = pcmk_rc_no_output;
233 GString *buffer = NULL;
234
235 CRM_ASSERT(out != NULL);
236 CRM_CHECK(depth >= 0, depth = 0);
237
238 if (data == NULL) {
239 return rc;
240 }
241
242 /* Allocate a buffer once, for show_xml_node() to truncate and reuse in
243 * recursive calls
244 */
245 buffer = g_string_sized_new(1024);
246 rc = show_xml_node(out, buffer, prefix, data, depth, options);
247 g_string_free(buffer, TRUE);
248
249 return rc;
250}
251
265static int
266show_xml_changes_recursive(pcmk__output_t *out, const xmlNode *data, int depth,
267 uint32_t options)
268{
269 /* @COMPAT: When log_data_element() is removed, we can remove the options
270 * argument here and instead hard-code pcmk__xml_log_pretty.
271 */
272 xml_node_private_t *nodepriv = (xml_node_private_t *) data->_private;
273 int rc = pcmk_rc_no_output;
274 int temp_rc = pcmk_rc_no_output;
275
276 if (pcmk_all_flags_set(nodepriv->flags, pcmk__xf_dirty|pcmk__xf_created)) {
277 // Newly created
278 return pcmk__xml_show(out, PCMK__XML_PREFIX_CREATED, data, depth,
279 options
283 }
284
285 if (pcmk_is_set(nodepriv->flags, pcmk__xf_dirty)) {
286 // Modified or moved
287 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
288 int spaces = pretty? (2 * depth) : 0;
289 const char *prefix = PCMK__XML_PREFIX_MODIFIED;
290
291 if (pcmk_is_set(nodepriv->flags, pcmk__xf_moved)) {
292 prefix = PCMK__XML_PREFIX_MOVED;
293 }
294
295 // Log opening tag
296 rc = pcmk__xml_show(out, prefix, data, depth,
297 options|pcmk__xml_fmt_open);
298
299 // Log changes to attributes
300 for (const xmlAttr *attr = pcmk__xe_first_attr(data); attr != NULL;
301 attr = attr->next) {
302 const char *name = (const char *) attr->name;
303
304 nodepriv = attr->_private;
305
306 if (pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
307 const char *value = pcmk__xml_attr_value(attr);
308
309 temp_rc = out->info(out, "%s %*s @%s=%s",
310 PCMK__XML_PREFIX_DELETED, spaces, "", name,
311 value);
312
313 } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_dirty)) {
314 const char *value = pcmk__xml_attr_value(attr);
315
316 if (pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
318
319 } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_modified)) {
321
322 } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_moved)) {
323 prefix = PCMK__XML_PREFIX_MOVED;
324
325 } else {
327 }
328
329 temp_rc = out->info(out, "%s %*s @%s=%s",
330 prefix, spaces, "", name, value);
331 }
332 rc = pcmk__output_select_rc(rc, temp_rc);
333 }
334
335 // Log changes to children
336 for (const xmlNode *child = pcmk__xml_first_child(data); child != NULL;
337 child = pcmk__xml_next(child)) {
338 temp_rc = show_xml_changes_recursive(out, child, depth + 1,
339 options);
340 rc = pcmk__output_select_rc(rc, temp_rc);
341 }
342
343 // Log closing tag
344 temp_rc = pcmk__xml_show(out, PCMK__XML_PREFIX_MODIFIED, data, depth,
345 options|pcmk__xml_fmt_close);
346 return pcmk__output_select_rc(rc, temp_rc);
347 }
348
349 // This node hasn't changed, but check its children
350 for (const xmlNode *child = pcmk__xml_first_child(data); child != NULL;
351 child = pcmk__xml_next(child)) {
352 temp_rc = show_xml_changes_recursive(out, child, depth + 1, options);
353 rc = pcmk__output_select_rc(rc, temp_rc);
354 }
355 return rc;
356}
357
369int
370pcmk__xml_show_changes(pcmk__output_t *out, const xmlNode *xml)
371{
372 xml_doc_private_t *docpriv = NULL;
373 int rc = pcmk_rc_no_output;
374 int temp_rc = pcmk_rc_no_output;
375
376 CRM_ASSERT(out != NULL);
377 CRM_ASSERT(xml != NULL);
378 CRM_ASSERT(xml->doc != NULL);
379
380 docpriv = xml->doc->_private;
381 if (!pcmk_is_set(docpriv->flags, pcmk__xf_dirty)) {
382 return rc;
383 }
384
385 for (const GList *iter = docpriv->deleted_objs; iter != NULL;
386 iter = iter->next) {
387 const pcmk__deleted_xml_t *deleted_obj = iter->data;
388
389 if (deleted_obj->position >= 0) {
390 temp_rc = out->info(out, PCMK__XML_PREFIX_DELETED " %s (%d)",
391 deleted_obj->path, deleted_obj->position);
392 } else {
393 temp_rc = out->info(out, PCMK__XML_PREFIX_DELETED " %s",
394 deleted_obj->path);
395 }
396 rc = pcmk__output_select_rc(rc, temp_rc);
397 }
398
399 temp_rc = show_xml_changes_recursive(out, xml, 0, pcmk__xml_fmt_pretty);
400 return pcmk__output_select_rc(rc, temp_rc);
401}
402
403// Deprecated functions kept only for backward API compatibility
404// LCOV_EXCL_START
405
408
409void
410log_data_element(int log_level, const char *file, const char *function,
411 int line, const char *prefix, const xmlNode *data, int depth,
412 int legacy_options)
413{
414 uint32_t options = 0;
415 pcmk__output_t *out = NULL;
416
417 // Confine log_level to uint8_t range
418 log_level = pcmk__clip_log_level(log_level);
419
420 if (data == NULL) {
421 do_crm_log(log_level, "%s%sNo data to dump as XML",
422 pcmk__s(prefix, ""), pcmk__str_empty(prefix)? "" : " ");
423 return;
424 }
425
426 switch (log_level) {
427 case LOG_NEVER:
428 return;
429 case LOG_STDOUT:
430 CRM_CHECK(pcmk__text_output_new(&out, NULL) == pcmk_rc_ok, return);
431 break;
432 default:
433 CRM_CHECK(pcmk__log_output_new(&out) == pcmk_rc_ok, return);
434 pcmk__output_set_log_level(out, log_level);
435 break;
436 }
437
438 /* Map xml_log_options to pcmk__xml_fmt_options so that we can go ahead and
439 * start using the pcmk__xml_fmt_options in all the internal functions.
440 *
441 * xml_log_option_dirty_add and xml_log_option_diff_all are ignored by
442 * internal code and only used here, so they don't need to be addressed.
443 */
444 if (pcmk_is_set(legacy_options, xml_log_option_filtered)) {
445 options |= pcmk__xml_fmt_filtered;
446 }
447 if (pcmk_is_set(legacy_options, xml_log_option_formatted)) {
448 options |= pcmk__xml_fmt_pretty;
449 }
450 if (pcmk_is_set(legacy_options, xml_log_option_open)) {
451 options |= pcmk__xml_fmt_open;
452 }
453 if (pcmk_is_set(legacy_options, xml_log_option_children)) {
454 options |= pcmk__xml_fmt_children;
455 }
456 if (pcmk_is_set(legacy_options, xml_log_option_close)) {
457 options |= pcmk__xml_fmt_close;
458 }
459 if (pcmk_is_set(legacy_options, xml_log_option_text)) {
460 options |= pcmk__xml_fmt_text;
461 }
462 if (pcmk_is_set(legacy_options, xml_log_option_diff_plus)) {
463 options |= pcmk__xml_fmt_diff_plus;
464 }
465 if (pcmk_is_set(legacy_options, xml_log_option_diff_minus)) {
466 options |= pcmk__xml_fmt_diff_minus;
467 }
468 if (pcmk_is_set(legacy_options, xml_log_option_diff_short)) {
469 options |= pcmk__xml_fmt_diff_short;
470 }
471
472 // Log element based on options
473 if (pcmk_is_set(legacy_options, xml_log_option_dirty_add)) {
474 CRM_CHECK(depth >= 0, depth = 0);
475 show_xml_changes_recursive(out, data, depth, options);
476 goto done;
477 }
478
480 && ((data->children == NULL)
482
483 if (pcmk_is_set(options, pcmk__xml_fmt_diff_plus)) {
484 legacy_options |= xml_log_option_diff_all;
486
487 } else if (pcmk_is_set(options, pcmk__xml_fmt_diff_minus)) {
488 legacy_options |= xml_log_option_diff_all;
490 }
491 }
492
494 && !pcmk_is_set(legacy_options, xml_log_option_diff_all)) {
495
496 if (!pcmk_any_flags_set(options,
499 // Nothing will ever be logged
500 goto done;
501 }
502
503 // Keep looking for the actual change
504 for (const xmlNode *child = pcmk__xml_first_child(data); child != NULL;
505 child = pcmk__xml_next(child)) {
506 log_data_element(log_level, file, function, line, prefix, child,
507 depth + 1, options);
508 }
509
510 } else {
511 pcmk__xml_show(out, prefix, data, depth,
512 options
516 }
517
518done:
519 out->finish(out, CRM_EX_OK, true, NULL);
521}
522
523void
524xml_log_changes(uint8_t log_level, const char *function, const xmlNode *xml)
525{
526 pcmk__output_t *out = NULL;
527 int rc = pcmk_rc_ok;
528
529 switch (log_level) {
530 case LOG_NEVER:
531 return;
532 case LOG_STDOUT:
533 CRM_CHECK(pcmk__text_output_new(&out, NULL) == pcmk_rc_ok, return);
534 break;
535 default:
536 CRM_CHECK(pcmk__log_output_new(&out) == pcmk_rc_ok, return);
537 pcmk__output_set_log_level(out, log_level);
538 break;
539 }
540 rc = pcmk__xml_show_changes(out, xml);
541 out->finish(out, pcmk_rc2exitc(rc), true, NULL);
543}
544
545// LCOV_EXCL_STOP
546// End deprecated API
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 utils.c:356
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:98
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 do_crm_log(level, fmt, args...)
Log a message.
Definition logging.h:181
#define LOG_STDOUT
Definition logging.h:43
#define CRM_CHECK(expr, failure_action)
Definition logging.h:245
#define LOG_NEVER
Definition logging.h:48
Deprecated Pacemaker logging API.
@ xml_log_option_diff_minus
@ xml_log_option_filtered
@ xml_log_option_diff_all
@ xml_log_option_diff_short
@ xml_log_option_text
@ xml_log_option_diff_plus
@ xml_log_option_dirty_add
@ xml_log_option_close
@ xml_log_option_formatted
@ xml_log_option_children
@ xml_log_option_open
#define pcmk__if_tracing(if_action, else_action)
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:446
void pcmk__output_set_log_level(pcmk__output_t *out, uint8_t log_level)
Definition output_log.c:390
void pcmk__output_free(pcmk__output_t *out)
Definition output.c:30
int pcmk__log_output_new(pcmk__output_t **out)
Definition output.c:291
int pcmk__text_output_new(pcmk__output_t **out, const char *filename)
Definition output.c:320
#define CRM_ASSERT(expr)
Definition results.h:42
@ CRM_EX_OK
Success.
Definition results.h:255
@ pcmk_rc_no_output
Definition results.h:131
@ pcmk_rc_ok
Definition results.h:162
crm_exit_t pcmk_rc2exitc(int rc)
Map a function return code to the most similar exit code.
Definition results.c:702
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1296
This structure contains everything that makes up a single output formatter.
void(* finish)(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest)
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
Wrappers for and extensions to libxml2.
Deprecated Pacemaker XML API.
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)
void xml_log_changes(uint8_t log_level, const char *function, const xmlNode *xml)
void log_data_element(int log_level, const char *file, const char *function, int line, const char *prefix, const xmlNode *data, int depth, int legacy_options)
int pcmk__xml_show(pcmk__output_t *out, const char *prefix, const xmlNode *data, int depth, uint32_t options)
@ pcmk__xml_fmt_close
Include the closing tag of an XML element.
@ pcmk__xml_fmt_diff_plus
Log a created XML subtree.
@ 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_diff_minus
Log a removed XML subtree.
@ pcmk__xml_fmt_pretty
Include indentation and newlines.
@ pcmk__xml_fmt_filtered
Exclude certain XML attributes (for calculating digests)
@ pcmk__xml_fmt_text
Include XML text nodes.
@ pcmk__xml_fmt_diff_short
Log a minimal version of an XML diff (only showing the changes)
@ pcmk__xf_deleted
@ pcmk__xf_created
@ pcmk__xf_dirty
@ pcmk__xf_modified
@ pcmk__xf_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:1110
#define PCMK__XA_HIDDEN
#define PCMK__XA_CRM_DIFF_MARKER