This source file includes following definitions.
- decompress_file
- pcmk__xml_read
- pcmk__xml_parse
- dump_xml_element
- dump_xml_text
- dump_xml_cdata
- dump_xml_comment
- pcmk__xml_string
- write_compressed_stream
- write_xml_stream
- pcmk__xml_write_fd
- pcmk__xml_write_file
- pcmk__xml2fd
- save_xml_to_file
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/types.h>
16
17 #include <bzlib.h>
18 #include <libxml/parser.h>
19 #include <libxml/tree.h>
20 #include <libxml/xmlIO.h>
21 #include <libxml/xmlstring.h>
22
23 #include <crm/crm.h>
24 #include <crm/common/xml.h>
25 #include <crm/common/xml_io.h>
26 #include "crmcommon_private.h"
27
28
29
30
31
32
33
34
35
36
37
38
39 static char *
40 decompress_file(const char *filename)
41 {
42 char *buffer = NULL;
43 int rc = pcmk_rc_ok;
44 size_t length = 0;
45 BZFILE *bz_file = NULL;
46 FILE *input = fopen(filename, "r");
47
48 if (input == NULL) {
49 crm_perror(LOG_ERR, "Could not open %s for reading", filename);
50 return NULL;
51 }
52
53 bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0);
54 rc = pcmk__bzlib2rc(rc);
55 if (rc != pcmk_rc_ok) {
56 crm_err("Could not prepare to read compressed %s: %s "
57 QB_XS " rc=%d", filename, pcmk_rc_str(rc), rc);
58 goto done;
59 }
60
61 do {
62 int read_len = 0;
63
64 buffer = pcmk__realloc(buffer, length + PCMK__BUFFER_SIZE + 1);
65 read_len = BZ2_bzRead(&rc, bz_file, buffer + length, PCMK__BUFFER_SIZE);
66
67 if ((rc == BZ_OK) || (rc == BZ_STREAM_END)) {
68 crm_trace("Read %ld bytes from file: %d", (long) read_len, rc);
69 length += read_len;
70 }
71 } while (rc == BZ_OK);
72
73 rc = pcmk__bzlib2rc(rc);
74 if (rc != pcmk_rc_ok) {
75 rc = pcmk__bzlib2rc(rc);
76 crm_err("Could not read compressed %s: %s " QB_XS " rc=%d",
77 filename, pcmk_rc_str(rc), rc);
78 free(buffer);
79 buffer = NULL;
80 } else {
81 buffer[length] = '\0';
82 }
83
84 done:
85 BZ2_bzReadClose(&rc, bz_file);
86 fclose(input);
87 return buffer;
88 }
89
90
91
92
93
94
95
96
97
98
99
100 xmlNode *
101 pcmk__xml_read(const char *filename)
102 {
103 bool use_stdin = pcmk__str_eq(filename, "-", pcmk__str_null_matches);
104 xmlNode *xml = NULL;
105 xmlDoc *output = NULL;
106 xmlParserCtxt *ctxt = NULL;
107 const xmlError *last_error = NULL;
108
109
110 ctxt = xmlNewParserCtxt();
111 CRM_CHECK(ctxt != NULL, return NULL);
112
113 xmlCtxtResetLastError(ctxt);
114 xmlSetGenericErrorFunc(ctxt, pcmk__log_xmllib_err);
115
116 if (use_stdin) {
117 output = xmlCtxtReadFd(ctxt, STDIN_FILENO, NULL, NULL,
118 XML_PARSE_NOBLANKS);
119
120 } else if (pcmk__ends_with_ext(filename, ".bz2")) {
121 char *input = decompress_file(filename);
122
123 if (input != NULL) {
124 output = xmlCtxtReadDoc(ctxt, (const xmlChar *) input, NULL, NULL,
125 XML_PARSE_NOBLANKS);
126 free(input);
127 }
128
129 } else {
130 output = xmlCtxtReadFile(ctxt, filename, NULL, XML_PARSE_NOBLANKS);
131 }
132
133 if (output != NULL) {
134 pcmk__xml_new_private_data((xmlNode *) output);
135 xml = xmlDocGetRootElement(output);
136 if (xml != NULL) {
137
138
139
140
141
142
143 pcmk__strip_xml_text(xml);
144 }
145 }
146
147 last_error = xmlCtxtGetLastError(ctxt);
148 if ((last_error != NULL) && (xml != NULL)) {
149 crm_log_xml_debug(xml, "partial");
150 pcmk__xml_free(xml);
151 xml = NULL;
152 }
153
154 xmlFreeParserCtxt(ctxt);
155 return xml;
156 }
157
158
159
160
161
162
163
164
165
166 xmlNode *
167 pcmk__xml_parse(const char *input)
168 {
169 xmlNode *xml = NULL;
170 xmlDoc *output = NULL;
171 xmlParserCtxt *ctxt = NULL;
172 const xmlError *last_error = NULL;
173
174 if (input == NULL) {
175 return NULL;
176 }
177
178 ctxt = xmlNewParserCtxt();
179 if (ctxt == NULL) {
180 return NULL;
181 }
182
183 xmlCtxtResetLastError(ctxt);
184 xmlSetGenericErrorFunc(ctxt, pcmk__log_xmllib_err);
185
186 output = xmlCtxtReadDoc(ctxt, (const xmlChar *) input, NULL, NULL,
187 XML_PARSE_NOBLANKS);
188 if (output != NULL) {
189 pcmk__xml_new_private_data((xmlNode *) output);
190 xml = xmlDocGetRootElement(output);
191 }
192
193 last_error = xmlCtxtGetLastError(ctxt);
194 if ((last_error != NULL) && (xml != NULL)) {
195 crm_log_xml_debug(xml, "partial");
196 pcmk__xml_free(xml);
197 xml = NULL;
198 }
199
200 xmlFreeParserCtxt(ctxt);
201 return xml;
202 }
203
204
205
206
207
208
209
210
211
212
213 static void
214 dump_xml_element(const xmlNode *data, uint32_t options, GString *buffer,
215 int depth)
216 {
217 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
218 bool filtered = pcmk_is_set(options, pcmk__xml_fmt_filtered);
219 int spaces = pretty? (2 * depth) : 0;
220
221 for (int lpc = 0; lpc < spaces; lpc++) {
222 g_string_append_c(buffer, ' ');
223 }
224
225 pcmk__g_strcat(buffer, "<", data->name, NULL);
226
227 for (const xmlAttr *attr = pcmk__xe_first_attr(data); attr != NULL;
228 attr = attr->next) {
229
230 if (!filtered || !pcmk__xa_filterable((const char *) (attr->name))) {
231 pcmk__dump_xml_attr(attr, buffer);
232 }
233 }
234
235 if (data->children == NULL) {
236 g_string_append(buffer, "/>");
237
238 } else {
239 g_string_append_c(buffer, '>');
240 }
241
242 if (pretty) {
243 g_string_append_c(buffer, '\n');
244 }
245
246 if (data->children) {
247 for (const xmlNode *child = data->children; child != NULL;
248 child = child->next) {
249 pcmk__xml_string(child, options, buffer, depth + 1);
250 }
251
252 for (int lpc = 0; lpc < spaces; lpc++) {
253 g_string_append_c(buffer, ' ');
254 }
255
256 pcmk__g_strcat(buffer, "</", data->name, ">", NULL);
257
258 if (pretty) {
259 g_string_append_c(buffer, '\n');
260 }
261 }
262 }
263
264
265
266
267
268
269
270
271
272
273 static void
274 dump_xml_text(const xmlNode *data, uint32_t options, GString *buffer,
275 int depth)
276 {
277 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
278 int spaces = pretty? (2 * depth) : 0;
279 const char *content = (const char *) data->content;
280 gchar *content_esc = NULL;
281
282 if (pcmk__xml_needs_escape(content, pcmk__xml_escape_text)) {
283 content_esc = pcmk__xml_escape(content, pcmk__xml_escape_text);
284 content = content_esc;
285 }
286
287 for (int lpc = 0; lpc < spaces; lpc++) {
288 g_string_append_c(buffer, ' ');
289 }
290
291 g_string_append(buffer, content);
292
293 if (pretty) {
294 g_string_append_c(buffer, '\n');
295 }
296 g_free(content_esc);
297 }
298
299
300
301
302
303
304
305
306
307
308 static void
309 dump_xml_cdata(const xmlNode *data, uint32_t options, GString *buffer,
310 int depth)
311 {
312 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
313 int spaces = pretty? (2 * depth) : 0;
314
315 for (int lpc = 0; lpc < spaces; lpc++) {
316 g_string_append_c(buffer, ' ');
317 }
318
319 pcmk__g_strcat(buffer, "<![CDATA[", (const char *) data->content, "]]>",
320 NULL);
321
322 if (pretty) {
323 g_string_append_c(buffer, '\n');
324 }
325 }
326
327
328
329
330
331
332
333
334
335
336 static void
337 dump_xml_comment(const xmlNode *data, uint32_t options, GString *buffer,
338 int depth)
339 {
340 bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
341 int spaces = pretty? (2 * depth) : 0;
342
343 for (int lpc = 0; lpc < spaces; lpc++) {
344 g_string_append_c(buffer, ' ');
345 }
346
347 pcmk__g_strcat(buffer, "<!--", (const char *) data->content, "-->", NULL);
348
349 if (pretty) {
350 g_string_append_c(buffer, '\n');
351 }
352 }
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369 void
370 pcmk__xml_string(const xmlNode *data, uint32_t options, GString *buffer,
371 int depth)
372 {
373 if (data == NULL) {
374 crm_trace("Nothing to dump");
375 return;
376 }
377
378 pcmk__assert(buffer != NULL);
379 CRM_CHECK(depth >= 0, depth = 0);
380
381 switch(data->type) {
382 case XML_ELEMENT_NODE:
383
384 dump_xml_element(data, options, buffer, depth);
385 break;
386 case XML_TEXT_NODE:
387 if (pcmk_is_set(options, pcmk__xml_fmt_text)) {
388 dump_xml_text(data, options, buffer, depth);
389 }
390 break;
391 case XML_COMMENT_NODE:
392 dump_xml_comment(data, options, buffer, depth);
393 break;
394 case XML_CDATA_SECTION_NODE:
395 dump_xml_cdata(data, options, buffer, depth);
396 break;
397 default:
398 crm_warn("Cannot convert XML %s node to text " QB_XS " type=%d",
399 pcmk__xml_element_type_text(data->type), data->type);
400 break;
401 }
402 }
403
404
405
406
407
408
409
410
411
412
413
414
415 static int
416 write_compressed_stream(char *text, const char *filename, FILE *stream,
417 unsigned int *bytes_out)
418 {
419 unsigned int bytes_in = 0;
420 int rc = pcmk_rc_ok;
421
422
423 BZFILE *bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 0);
424
425 rc = pcmk__bzlib2rc(rc);
426 if (rc != pcmk_rc_ok) {
427 crm_warn("Not compressing %s: could not prepare file stream: %s "
428 QB_XS " rc=%d",
429 filename, pcmk_rc_str(rc), rc);
430 goto done;
431 }
432
433 BZ2_bzWrite(&rc, bz_file, text, strlen(text));
434 rc = pcmk__bzlib2rc(rc);
435 if (rc != pcmk_rc_ok) {
436 crm_warn("Not compressing %s: could not compress data: %s "
437 QB_XS " rc=%d errno=%d",
438 filename, pcmk_rc_str(rc), rc, errno);
439 goto done;
440 }
441
442 BZ2_bzWriteClose(&rc, bz_file, 0, &bytes_in, bytes_out);
443 bz_file = NULL;
444 rc = pcmk__bzlib2rc(rc);
445 if (rc != pcmk_rc_ok) {
446 crm_warn("Not compressing %s: could not write compressed data: %s "
447 QB_XS " rc=%d errno=%d",
448 filename, pcmk_rc_str(rc), rc, errno);
449 goto done;
450 }
451
452 crm_trace("Compressed XML for %s from %u bytes to %u",
453 filename, bytes_in, *bytes_out);
454
455 done:
456 if (bz_file != NULL) {
457 BZ2_bzWriteClose(&rc, bz_file, 0, NULL, NULL);
458 }
459 return rc;
460 }
461
462
463
464
465
466
467
468
469
470
471
472
473
474 static int
475 write_xml_stream(const xmlNode *xml, const char *filename, FILE *stream,
476 bool compress)
477 {
478 GString *buffer = g_string_sized_new(1024);
479 unsigned int bytes_out = 0;
480 int rc = pcmk_rc_ok;
481
482 pcmk__xml_string(xml, pcmk__xml_fmt_pretty, buffer, 0);
483 CRM_CHECK(!pcmk__str_empty(buffer->str),
484 crm_log_xml_info(xml, "dump-failed");
485 rc = pcmk_rc_error;
486 goto done);
487
488 crm_log_xml_trace(xml, "writing");
489
490 if (compress
491 && (write_compressed_stream(buffer->str, filename, stream,
492 &bytes_out) == pcmk_rc_ok)) {
493 goto done;
494 }
495
496 rc = fprintf(stream, "%s", buffer->str);
497 if (rc < 0) {
498 rc = EIO;
499 crm_perror(LOG_ERR, "writing %s", filename);
500 goto done;
501 }
502 bytes_out = (unsigned int) rc;
503 rc = pcmk_rc_ok;
504
505 done:
506 if (fflush(stream) != 0) {
507 rc = errno;
508 crm_perror(LOG_ERR, "flushing %s", filename);
509 }
510
511
512 if ((fsync(fileno(stream)) < 0) && (errno != EROFS) && (errno != EINVAL)) {
513 rc = errno;
514 crm_perror(LOG_ERR, "synchronizing %s", filename);
515 }
516
517 fclose(stream);
518 crm_trace("Saved %u bytes to %s as XML", bytes_out, filename);
519
520 g_string_free(buffer, TRUE);
521 return rc;
522 }
523
524
525
526
527
528
529
530
531
532
533
534 int
535 pcmk__xml_write_fd(const xmlNode *xml, const char *filename, int fd)
536 {
537 FILE *stream = NULL;
538
539 CRM_CHECK((xml != NULL) && (fd > 0), return EINVAL);
540 stream = fdopen(fd, "w");
541 if (stream == NULL) {
542 return errno;
543 }
544
545 return write_xml_stream(xml, pcmk__s(filename, "unnamed file"), stream,
546 false);
547 }
548
549
550
551
552
553
554
555
556
557
558
559 int
560 pcmk__xml_write_file(const xmlNode *xml, const char *filename, bool compress)
561 {
562 FILE *stream = NULL;
563
564 CRM_CHECK((xml != NULL) && (filename != NULL), return EINVAL);
565 stream = fopen(filename, "w");
566 if (stream == NULL) {
567 return errno;
568 }
569
570 return write_xml_stream(xml, filename, stream, compress);
571 }
572
573
574
575
576
577
578
579
580
581
582 int
583 pcmk__xml2fd(int fd, xmlNode *cur)
584 {
585 bool success;
586
587 xmlOutputBuffer *fd_out = xmlOutputBufferCreateFd(fd, NULL);
588 pcmk__mem_assert(fd_out);
589 xmlNodeDumpOutput(fd_out, cur->doc, cur, 0, pcmk__xml_fmt_pretty, NULL);
590
591 success = xmlOutputBufferWrite(fd_out, sizeof("\n") - 1, "\n") != -1;
592
593 success = xmlOutputBufferClose(fd_out) != -1 && success;
594
595 if (!success) {
596 return EIO;
597 }
598
599 fsync(fd);
600 return pcmk_rc_ok;
601 }
602
603 void
604 save_xml_to_file(const xmlNode *xml, const char *desc, const char *filename)
605 {
606 char *f = NULL;
607
608 if (filename == NULL) {
609 char *uuid = crm_generate_uuid();
610
611 f = crm_strdup_printf("%s/%s", pcmk__get_tmpdir(), uuid);
612 filename = f;
613 free(uuid);
614 }
615
616 crm_info("Saving %s to %s", desc, filename);
617 pcmk__xml_write_file(xml, filename, false);
618 free(f);
619 }