pacemaker  2.0.4-2deceaa
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
digest.c
Go to the documentation of this file.
1 /*
2  * Copyright 2015-2020 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 <stdio.h>
13 #include <unistd.h>
14 #include <string.h>
15 #include <stdlib.h>
16 
17 #include <crm/crm.h>
18 #include <crm/msg_xml.h>
19 #include <crm/common/xml.h>
20 
21 #define BEST_EFFORT_STATUS 0
22 
30 static char *
31 dump_xml_for_digest(xmlNode * an_xml_node)
32 {
33  char *buffer = NULL;
34  int offset = 0, max = 0;
35 
36  /* for compatibility with the old result which is used for v1 digests */
37  crm_buffer_add_char(&buffer, &offset, &max, ' ');
38  crm_xml_dump(an_xml_node, 0, &buffer, &offset, &max, 0);
39  crm_buffer_add_char(&buffer, &offset, &max, '\n');
40 
41  return buffer;
42 }
43 
54 static char *
55 calculate_xml_digest_v1(xmlNode * input, gboolean sort, gboolean ignored)
56 {
57  char *digest = NULL;
58  char *buffer = NULL;
59  xmlNode *copy = NULL;
60 
61  if (sort) {
62  crm_trace("Sorting xml...");
63  copy = sorted_xml(input, NULL, TRUE);
64  crm_trace("Done");
65  input = copy;
66  }
67 
68  buffer = dump_xml_for_digest(input);
69  CRM_CHECK(buffer != NULL && strlen(buffer) > 0, free_xml(copy);
70  free(buffer);
71  return NULL);
72 
73  digest = crm_md5sum(buffer);
74  crm_log_xml_trace(input, "digest:source");
75 
76  free(buffer);
77  free_xml(copy);
78  return digest;
79 }
80 
89 static char *
90 calculate_xml_digest_v2(xmlNode * source, gboolean do_filter)
91 {
92  char *digest = NULL;
93  char *buffer = NULL;
94  int offset, max;
95 
96  static struct qb_log_callsite *digest_cs = NULL;
97 
98  crm_trace("Begin digest %s", do_filter?"filtered":"");
99  if (do_filter && BEST_EFFORT_STATUS) {
100  /* Exclude the status calculation from the digest
101  *
102  * This doesn't mean it won't be sync'd, we just won't be paranoid
103  * about it being an _exact_ copy
104  *
105  * We don't need it to be exact, since we throw it away and regenerate
106  * from our peers whenever a new DC is elected anyway
107  *
108  * Importantly, this reduces the amount of XML to copy+export as
109  * well as the amount of data for MD5 needs to operate on
110  */
111 
112  } else {
113  crm_xml_dump(source, do_filter ? xml_log_option_filtered : 0, &buffer, &offset, &max, 0);
114  }
115 
116  CRM_ASSERT(buffer != NULL);
117  digest = crm_md5sum(buffer);
118 
119  if (digest_cs == NULL) {
120  digest_cs = qb_log_callsite_get(__func__, __FILE__, "cib-digest", LOG_TRACE, __LINE__,
122  }
123  if (digest_cs && digest_cs->targets) {
124  char *trace_file = crm_strdup_printf("%s/digest-%s",
125  pcmk__get_tmpdir(), digest);
126 
127  crm_trace("Saving %s.%s.%s to %s",
130  crm_element_value(source, XML_ATTR_NUMUPDATES), trace_file);
131  save_xml_to_file(source, "digest input", trace_file);
132  free(trace_file);
133  }
134 
135  free(buffer);
136  crm_trace("End digest");
137  return digest;
138 }
139 
147 char *
148 calculate_on_disk_digest(xmlNode * input)
149 {
150  /* Always use the v1 format for on-disk digests
151  * a) it's a compatibility nightmare
152  * b) we only use this once at startup, all other
153  * invocations are in a separate child process
154  */
155  return calculate_xml_digest_v1(input, FALSE, FALSE);
156 }
157 
166 char *
167 calculate_operation_digest(xmlNode *input, const char *version)
168 {
169  /* We still need the sorting for operation digests */
170  return calculate_xml_digest_v1(input, TRUE, FALSE);
171 }
172 
183 char *
184 calculate_xml_versioned_digest(xmlNode * input, gboolean sort, gboolean do_filter,
185  const char *version)
186 {
187  /*
188  * @COMPAT digests (on-disk or in diffs/patchsets) created <1.1.4;
189  * removing this affects even full-restart upgrades from old versions
190  *
191  * The sorting associated with v1 digest creation accounted for 23% of
192  * the CIB manager's CPU usage on the server. v2 drops this.
193  *
194  * The filtering accounts for an additional 2.5% and we may want to
195  * remove it in future.
196  *
197  * v2 also uses the xmlBuffer contents directly to avoid additional copying
198  */
199  if (version == NULL || compare_version("3.0.5", version) > 0) {
200  crm_trace("Using v1 digest algorithm for %s", crm_str(version));
201  return calculate_xml_digest_v1(input, sort, do_filter);
202  }
203  crm_trace("Using v2 digest algorithm for %s", crm_str(version));
204  return calculate_xml_digest_v2(input, do_filter);
205 }
206 
216 bool
217 pcmk__verify_digest(xmlNode *input, const char *expected)
218 {
219  char *calculated = NULL;
220  bool passed;
221 
222  if (input != NULL) {
223  calculated = calculate_on_disk_digest(input);
224  if (calculated == NULL) {
225  crm_perror(LOG_ERR, "Could not calculate digest for comparison");
226  return false;
227  }
228  }
229  passed = safe_str_eq(expected, calculated);
230  if (passed) {
231  crm_trace("Digest comparison passed: %s", calculated);
232  } else {
233  crm_err("Digest comparison failed: expected %s, calculated %s",
234  expected, calculated);
235  }
236  free(calculated);
237  return passed;
238 }
#define LOG_TRACE
Definition: logging.h:36
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:233
A dumping ground.
#define XML_ATTR_NUMUPDATES
Definition: msg_xml.h:89
void crm_buffer_add_char(char **buffer, int *offset, int *max, char c)
Definition: xml.c:3293
bool pcmk__verify_digest(xmlNode *input, const char *expected)
Definition: digest.c:217
unsigned int crm_trace_nonlog
Definition: logging.c:39
#define XML_ATTR_GENERATION
Definition: msg_xml.h:87
char * calculate_operation_digest(xmlNode *local_cib, const char *version)
Calculate and return digest of XML operation.
Definition: digest.c:167
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:522
#define crm_trace(fmt, args...)
Definition: logging.h:369
Wrappers for and extensions to libxml2.
void free_xml(xmlNode *child)
Definition: xml.c:2136
char * calculate_on_disk_digest(xmlNode *local_cib)
Calculate and return digest of XML tree, suitable for storing on disk.
Definition: digest.c:148
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition: xml.c:4367
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition: logging.h:314
#define crm_err(fmt, args...)
Definition: logging.h:363
#define CRM_ASSERT(expr)
Definition: results.h:42
const char * pcmk__get_tmpdir(void)
Definition: io.c:559
char * calculate_xml_versioned_digest(xmlNode *input, gboolean sort, gboolean do_filter, const char *version)
Calculate and return digest of XML tree.
Definition: digest.c:184
int compare_version(const char *version1, const char *version2)
Definition: utils.c:227
#define BEST_EFFORT_STATUS
Definition: digest.c:21
#define XML_ATTR_GENERATION_ADMIN
Definition: msg_xml.h:88
#define crm_str(x)
Definition: logging.h:389
void save_xml_to_file(xmlNode *xml, const char *desc, const char *filename)
Definition: xml.c:3374
#define crm_log_xml_trace(xml, text)
Definition: logging.h:377
char * crm_md5sum(const char *buffer)
Definition: utils.c:570
#define safe_str_eq(a, b)
Definition: util.h:65
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
uint32_t version
Definition: remote.c:147
void crm_xml_dump(xmlNode *data, int options, char **buffer, int *offset, int *max, int depth)
Definition: xml.c:3186