pacemaker 2.1.6-6fdc9deea29
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
digest.c
Go to the documentation of this file.
1/*
2 * Copyright 2015-2022 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#include <md5.h>
17
18#include <crm/crm.h>
19#include <crm/msg_xml.h>
20#include <crm/common/xml.h>
21#include "crmcommon_private.h"
22
23#define BEST_EFFORT_STATUS 0
24
33static GString *
34dump_xml_for_digest(xmlNodePtr xml)
35{
36 GString *buffer = g_string_sized_new(1024);
37
38 /* for compatibility with the old result which is used for v1 digests */
39 g_string_append_c(buffer, ' ');
40 pcmk__xml2text(xml, 0, buffer, 0);
41 g_string_append_c(buffer, '\n');
42
43 return buffer;
44}
45
56static char *
57calculate_xml_digest_v1(xmlNode *input, gboolean sort, gboolean ignored)
58{
59 char *digest = NULL;
60 GString *buffer = NULL;
61 xmlNode *copy = NULL;
62
63 if (sort) {
64 crm_trace("Sorting xml...");
65 copy = sorted_xml(input, NULL, TRUE);
66 crm_trace("Done");
67 input = copy;
68 }
69
70 buffer = dump_xml_for_digest(input);
71 CRM_CHECK(buffer->len > 0, free_xml(copy);
72 g_string_free(buffer, TRUE);
73 return NULL);
74
75 digest = crm_md5sum((const char *) buffer->str);
76 crm_log_xml_trace(input, "digest:source");
77
78 g_string_free(buffer, TRUE);
79 free_xml(copy);
80 return digest;
81}
82
91static char *
92calculate_xml_digest_v2(xmlNode *source, gboolean do_filter)
93{
94 char *digest = NULL;
95 GString *buffer = g_string_sized_new(1024);
96
97 crm_trace("Begin digest %s", do_filter?"filtered":"");
98 pcmk__xml2text(source, (do_filter? pcmk__xml_fmt_filtered : 0), buffer, 0);
99
100 CRM_ASSERT(buffer != NULL);
101 digest = crm_md5sum((const char *) buffer->str);
102
104 {
105 char *trace_file = crm_strdup_printf("%s/digest-%s",
106 pcmk__get_tmpdir(), digest);
107
108 crm_trace("Saving %s.%s.%s to %s",
112 trace_file);
113 save_xml_to_file(source, "digest input", trace_file);
114 free(trace_file);
115 },
116 {}
117 );
118 g_string_free(buffer, TRUE);
119 crm_trace("End digest");
120 return digest;
121}
122
130char *
132{
133 /* Always use the v1 format for on-disk digests
134 * a) it's a compatibility nightmare
135 * b) we only use this once at startup, all other
136 * invocations are in a separate child process
137 */
138 return calculate_xml_digest_v1(input, FALSE, FALSE);
139}
140
149char *
151{
152 /* We still need the sorting for operation digests */
153 return calculate_xml_digest_v1(input, TRUE, FALSE);
154}
155
166char *
167calculate_xml_versioned_digest(xmlNode *input, gboolean sort,
168 gboolean do_filter, const char *version)
169{
170 /*
171 * @COMPAT digests (on-disk or in diffs/patchsets) created <1.1.4;
172 * removing this affects even full-restart upgrades from old versions
173 *
174 * The sorting associated with v1 digest creation accounted for 23% of
175 * the CIB manager's CPU usage on the server. v2 drops this.
176 *
177 * The filtering accounts for an additional 2.5% and we may want to
178 * remove it in future.
179 *
180 * v2 also uses the xmlBuffer contents directly to avoid additional copying
181 */
182 if (version == NULL || compare_version("3.0.5", version) > 0) {
183 crm_trace("Using v1 digest algorithm for %s",
184 pcmk__s(version, "unknown feature set"));
185 return calculate_xml_digest_v1(input, sort, do_filter);
186 }
187 crm_trace("Using v2 digest algorithm for %s",
188 pcmk__s(version, "unknown feature set"));
189 return calculate_xml_digest_v2(input, do_filter);
190}
191
201bool
202pcmk__verify_digest(xmlNode *input, const char *expected)
203{
204 char *calculated = NULL;
205 bool passed;
206
207 if (input != NULL) {
208 calculated = calculate_on_disk_digest(input);
209 if (calculated == NULL) {
210 crm_perror(LOG_ERR, "Could not calculate digest for comparison");
211 return false;
212 }
213 }
214 passed = pcmk__str_eq(expected, calculated, pcmk__str_casei);
215 if (passed) {
216 crm_trace("Digest comparison passed: %s", calculated);
217 } else {
218 crm_err("Digest comparison failed: expected %s, calculated %s",
219 expected, calculated);
220 }
221 free(calculated);
222 return passed;
223}
224
233bool
235{
236 static const char *filter[] = {
242 };
243
244 for (int i = 0; i < PCMK__NELEM(filter); i++) {
245 if (strcmp(name, filter[i]) == 0) {
246 return true;
247 }
248 }
249 return false;
250}
251
252char *
253crm_md5sum(const char *buffer)
254{
255 int lpc = 0, len = 0;
256 char *digest = NULL;
257 unsigned char raw_digest[MD5_DIGEST_SIZE];
258
259 if (buffer == NULL) {
260 buffer = "";
261 }
262 len = strlen(buffer);
263
264 crm_trace("Beginning digest of %d bytes", len);
265 digest = malloc(2 * MD5_DIGEST_SIZE + 1);
266 if (digest) {
267 md5_buffer(buffer, len, raw_digest);
268 for (lpc = 0; lpc < MD5_DIGEST_SIZE; lpc++) {
269 sprintf(digest + (2 * lpc), "%02x", raw_digest[lpc]);
270 }
271 digest[(2 * MD5_DIGEST_SIZE)] = 0;
272 crm_trace("Digest %s.", digest);
273
274 } else {
275 crm_err("Could not create digest");
276 }
277 return digest;
278}
const char * name
Definition cib.c:24
#define PCMK__NELEM(a)
Definition internal.h:44
uint32_t version
Definition remote.c:1
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
int compare_version(const char *version1, const char *version2)
Definition utils.c:189
A dumping ground.
G_GNUC_INTERNAL void pcmk__xml2text(xmlNodePtr data, uint32_t options, GString *buffer, int depth)
Definition xml.c:1574
char * crm_md5sum(const char *buffer)
Definition digest.c:253
char * calculate_operation_digest(xmlNode *input, const char *version)
Calculate and return digest of XML operation.
Definition digest.c:150
bool pcmk__xa_filterable(const char *name)
Definition digest.c:234
char * calculate_on_disk_digest(xmlNode *input)
Calculate and return digest of XML tree, suitable for storing on disk.
Definition digest.c:131
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:167
bool pcmk__verify_digest(xmlNode *input, const char *expected)
Definition digest.c:202
const char * pcmk__get_tmpdir(void)
Definition io.c:541
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition logging.h:319
#define CRM_CHECK(expr, failure_action)
Definition logging.h:235
#define crm_err(fmt, args...)
Definition logging.h:375
#define crm_log_xml_trace(xml, text)
Definition logging.h:389
#define crm_trace(fmt, args...)
Definition logging.h:381
#define pcmk__if_tracing(if_action, else_action)
void * md5_buffer(const char *buffer, size_t len, void *resblock)
Definition md5.c:227
#define MD5_DIGEST_SIZE
Definition md5.h:30
#define XML_ATTR_UPDATE_CLIENT
Definition msg_xml.h:156
#define XML_ATTR_ORIGIN
Definition msg_xml.h:142
#define XML_ATTR_GENERATION_ADMIN
Definition msg_xml.h:139
#define XML_ATTR_NUMUPDATES
Definition msg_xml.h:140
#define XML_CIB_ATTR_WRITTEN
Definition msg_xml.h:144
#define XML_ATTR_UPDATE_ORIG
Definition msg_xml.h:155
#define XML_ATTR_UPDATE_USER
Definition msg_xml.h:157
#define XML_ATTR_GENERATION
Definition msg_xml.h:138
xmlNode * input
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:496
#define CRM_ASSERT(expr)
Definition results.h:42
@ pcmk__str_casei
Wrappers for and extensions to libxml2.
void free_xml(xmlNode *child)
Definition xml.c:813
void save_xml_to_file(xmlNode *xml, const char *desc, const char *filename)
Definition xml.c:1753
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition xml.c:2489
@ pcmk__xml_fmt_filtered
Exclude certain XML attributes (for calculating digests)