This source file includes following definitions.
- pcmk__new_nvpair
- pcmk__free_nvpair
- pcmk_prepend_nvpair
- pcmk_free_nvpairs
- pcmk__compare_nvpair
- pcmk_sort_nvpairs
- pcmk_xml_attrs2nvpairs
- pcmk__nvpair_add_xml_attr
- pcmk_nvpairs2xml_attrs
- pcmk__scan_nvpair
- pcmk__format_nvpair
- pcmk__xe_set_attr_force
- crm_xml_add
- crm_xml_add_int
- crm_xml_add_ms
- crm_xml_add_ll
- crm_xml_add_timeval
- crm_element_value
- crm_element_value_int
- pcmk__xe_get_flags
- crm_element_value_ll
- crm_element_value_ms
- crm_element_value_epoch
- crm_element_value_timeval
- pcmk__xe_get_datetime
- crm_element_value_copy
- hash2smartfield
- hash2field
- hash2metafield
- crm_create_nvpair_xml
- hash2nvpair
- xml2list
- pcmk__xe_set_bool_attr
- pcmk__xe_get_bool_attr
- pcmk__xe_attr_is_true
- crm_meta_name
- crm_meta_value
- pcmk_scan_nvpair
- pcmk_format_nvpair
- pcmk_format_named_time
- crm_xml_replace
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <stdio.h>
13 #include <stdint.h>
14 #include <inttypes.h>
15 #include <sys/types.h>
16 #include <string.h>
17 #include <ctype.h>
18 #include <glib.h>
19 #include <libxml/tree.h>
20
21 #include <crm/crm.h>
22 #include <crm/common/xml.h>
23 #include <crm/common/xml_internal.h>
24 #include "crmcommon_private.h"
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 static pcmk_nvpair_t *
49 pcmk__new_nvpair(const char *name, const char *value)
50 {
51 pcmk_nvpair_t *nvpair = NULL;
52
53 pcmk__assert(name);
54
55 nvpair = pcmk__assert_alloc(1, sizeof(pcmk_nvpair_t));
56
57 nvpair->name = pcmk__str_copy(name);
58 nvpair->value = pcmk__str_copy(value);
59 return nvpair;
60 }
61
62
63
64
65
66
67
68 static void
69 pcmk__free_nvpair(gpointer data)
70 {
71 if (data) {
72 pcmk_nvpair_t *nvpair = data;
73
74 free(nvpair->name);
75 free(nvpair->value);
76 free(nvpair);
77 }
78 }
79
80
81
82
83
84
85
86
87
88
89
90
91 GSList *
92 pcmk_prepend_nvpair(GSList *nvpairs, const char *name, const char *value)
93 {
94 return g_slist_prepend(nvpairs, pcmk__new_nvpair(name, value));
95 }
96
97
98
99
100
101
102 void
103 pcmk_free_nvpairs(GSList *nvpairs)
104 {
105 g_slist_free_full(nvpairs, pcmk__free_nvpair);
106 }
107
108
109
110
111
112
113
114
115
116
117 static gint
118 pcmk__compare_nvpair(gconstpointer a, gconstpointer b)
119 {
120 int rc = 0;
121 const pcmk_nvpair_t *pair_a = a;
122 const pcmk_nvpair_t *pair_b = b;
123
124 pcmk__assert((pair_a != NULL) && (pair_a->name != NULL)
125 && (pair_b != NULL) && (pair_b->name != NULL));
126
127 rc = strcmp(pair_a->name, pair_b->name);
128 if (rc < 0) {
129 return -1;
130 } else if (rc > 0) {
131 return 1;
132 }
133 return 0;
134 }
135
136
137
138
139
140
141
142
143 GSList *
144 pcmk_sort_nvpairs(GSList *list)
145 {
146 return g_slist_sort(list, pcmk__compare_nvpair);
147 }
148
149
150
151
152
153
154
155
156
157
158 GSList *
159 pcmk_xml_attrs2nvpairs(const xmlNode *xml)
160 {
161 GSList *result = NULL;
162
163 for (xmlAttrPtr iter = pcmk__xe_first_attr(xml); iter != NULL;
164 iter = iter->next) {
165
166 result = pcmk_prepend_nvpair(result,
167 (const char *) iter->name,
168 (const char *) pcmk__xml_attr_value(iter));
169 }
170 return result;
171 }
172
173
174
175
176
177
178
179
180
181
182
183 static void
184 pcmk__nvpair_add_xml_attr(gpointer data, gpointer user_data)
185 {
186 pcmk_nvpair_t *pair = data;
187 xmlNode *parent = user_data;
188
189 crm_xml_add(parent, pair->name, pair->value);
190 }
191
192
193
194
195
196
197
198 void
199 pcmk_nvpairs2xml_attrs(GSList *list, xmlNode *xml)
200 {
201 g_slist_foreach(list, pcmk__nvpair_add_xml_attr, xml);
202 }
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218 int
219 pcmk__scan_nvpair(const char *input, char **name, char **value)
220 {
221 #ifdef HAVE_SSCANF_M
222 *name = NULL;
223 *value = NULL;
224 if (sscanf(input, "%m[^=]=%m[^\n]", name, value) <= 0) {
225 return -pcmk_err_bad_nvpair;
226 }
227 #else
228 char *sep = NULL;
229 *name = NULL;
230 *value = NULL;
231
232 sep = strstr(optarg, "=");
233 if (sep == NULL) {
234 return -pcmk_err_bad_nvpair;
235 }
236
237 *name = strndup(input, sep-input);
238
239 if (*name == NULL) {
240 return -ENOMEM;
241 }
242
243
244
245
246 if (*(sep+1) != '\0') {
247 *value = strdup(sep+1);
248
249 if (*value == NULL) {
250 return -ENOMEM;
251 }
252 }
253 #endif
254
255 if (*name != NULL && *value != NULL) {
256 return 2;
257 } else if (*name != NULL || *value != NULL) {
258 return 1;
259 } else {
260 return -pcmk_err_bad_nvpair;
261 }
262 }
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281 char *
282 pcmk__format_nvpair(const char *name, const char *value, const char *units)
283 {
284 return crm_strdup_printf("%s=\"%s%s\"", name, value, units ? units : "");
285 }
286
287
288
289 xmlAttr *
290 pcmk__xe_set_attr_force(xmlNode *node, const char *name, const char *value)
291 {
292 xmlAttr *attr = xmlSetProp(node, (pcmkXmlStr) name, (pcmkXmlStr) value);
293
294
295
296
297 pcmk__xml_new_private_data((xmlNode *) attr);
298
299 return attr;
300 }
301
302
303
304
305
306
307
308
309
310
311
312 const char *
313 crm_xml_add(xmlNode *node, const char *name, const char *value)
314 {
315 bool dirty = FALSE;
316 xmlAttr *attr = NULL;
317
318 CRM_CHECK(node != NULL, return NULL);
319 CRM_CHECK(name != NULL, return NULL);
320
321 if (value == NULL) {
322 return NULL;
323 }
324
325 if (pcmk__tracking_xml_changes(node, FALSE)) {
326 const char *old = crm_element_value(node, name);
327
328 if (old == NULL || value == NULL || strcmp(old, value) != 0) {
329 dirty = TRUE;
330 }
331 }
332
333 if (dirty && (pcmk__check_acl(node, name, pcmk__xf_acl_create) == FALSE)) {
334 crm_trace("Cannot add %s=%s to %s", name, value, node->name);
335 return NULL;
336 }
337
338 attr = pcmk__xe_set_attr_force(node, name, value);
339 if (dirty) {
340 pcmk__mark_xml_attr_dirty(attr);
341 }
342
343 CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
344 return (char *)attr->children->content;
345 }
346
347
348
349
350
351
352
353
354
355
356
357
358
359 const char *
360 crm_xml_add_int(xmlNode *node, const char *name, int value)
361 {
362 char *number = pcmk__itoa(value);
363 const char *added = crm_xml_add(node, name, number);
364
365 free(number);
366 return added;
367 }
368
369
370
371
372
373
374
375
376
377
378
379
380
381 const char *
382 crm_xml_add_ms(xmlNode *node, const char *name, guint ms)
383 {
384 char *number = crm_strdup_printf("%u", ms);
385 const char *added = crm_xml_add(node, name, number);
386
387 free(number);
388 return added;
389 }
390
391
392
393 #define LLSTRSIZE 21
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409 const char *
410 crm_xml_add_ll(xmlNode *xml, const char *name, long long value)
411 {
412 char s[LLSTRSIZE] = { '\0', };
413
414 if (snprintf(s, LLSTRSIZE, "%lld", (long long) value) == LLSTRSIZE) {
415 return NULL;
416 }
417 return crm_xml_add(xml, name, s);
418 }
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433 const char *
434 crm_xml_add_timeval(xmlNode *xml, const char *name_sec, const char *name_usec,
435 const struct timeval *value)
436 {
437 const char *added = NULL;
438
439 if (xml && name_sec && value) {
440 added = crm_xml_add_ll(xml, name_sec, (long long) value->tv_sec);
441 if (added && name_usec) {
442
443 crm_xml_add_ll(xml, name_usec, (long long) value->tv_usec);
444 }
445 }
446 return added;
447 }
448
449
450
451
452
453
454
455
456
457 const char *
458 crm_element_value(const xmlNode *data, const char *name)
459 {
460 xmlAttr *attr = NULL;
461
462 if (data == NULL) {
463 crm_err("Couldn't find %s in NULL", name ? name : "<null>");
464 CRM_LOG_ASSERT(data != NULL);
465 return NULL;
466
467 } else if (name == NULL) {
468 crm_err("Couldn't find NULL in %s", data->name);
469 return NULL;
470 }
471
472
473
474
475 attr = xmlHasProp((xmlNode *) data, (pcmkXmlStr) name);
476 if (!attr || !attr->children) {
477 return NULL;
478 }
479 return (const char *) attr->children->content;
480 }
481
482
483
484
485
486
487
488
489
490
491
492
493 int
494 crm_element_value_int(const xmlNode *data, const char *name, int *dest)
495 {
496 const char *value = NULL;
497
498 CRM_CHECK(dest != NULL, return -1);
499 value = crm_element_value(data, name);
500 if (value) {
501 long long value_ll;
502 int rc = pcmk__scan_ll(value, &value_ll, 0LL);
503
504 *dest = PCMK__PARSE_INT_DEFAULT;
505 if (rc != pcmk_rc_ok) {
506 crm_warn("Using default for %s "
507 "because '%s' is not a valid integer: %s",
508 name, value, pcmk_rc_str(rc));
509 } else if ((value_ll < INT_MIN) || (value_ll > INT_MAX)) {
510 crm_warn("Using default for %s because '%s' is out of range",
511 name, value);
512 } else {
513 *dest = (int) value_ll;
514 return 0;
515 }
516 }
517 return -1;
518 }
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534 int
535 pcmk__xe_get_flags(const xmlNode *xml, const char *name, uint32_t *dest,
536 uint32_t default_value)
537 {
538 const char *value = NULL;
539 long long value_ll = 0LL;
540 int rc = pcmk_rc_ok;
541
542 if (dest != NULL) {
543 *dest = default_value;
544 }
545
546 if (name == NULL) {
547 return EINVAL;
548 }
549 if (xml == NULL) {
550 return pcmk_rc_ok;
551 }
552 value = crm_element_value(xml, name);
553 if (value == NULL) {
554 return pcmk_rc_ok;
555 }
556
557 rc = pcmk__scan_ll(value, &value_ll, default_value);
558 if ((value_ll < 0) || (value_ll > UINT32_MAX)) {
559 value_ll = default_value;
560 if (rc == pcmk_rc_ok) {
561 rc = pcmk_rc_bad_input;
562 }
563 }
564
565 if (dest != NULL) {
566 *dest = (uint32_t) value_ll;
567 }
568 return rc;
569 }
570
571
572
573
574
575
576
577
578
579
580
581
582 int
583 crm_element_value_ll(const xmlNode *data, const char *name, long long *dest)
584 {
585 const char *value = NULL;
586
587 CRM_CHECK(dest != NULL, return -1);
588 value = crm_element_value(data, name);
589 if (value != NULL) {
590 int rc = pcmk__scan_ll(value, dest, PCMK__PARSE_INT_DEFAULT);
591
592 if (rc == pcmk_rc_ok) {
593 return 0;
594 }
595 crm_warn("Using default for %s "
596 "because '%s' is not a valid integer: %s",
597 name, value, pcmk_rc_str(rc));
598 }
599 return -1;
600 }
601
602
603
604
605
606
607
608
609
610
611
612
613 int
614 crm_element_value_ms(const xmlNode *data, const char *name, guint *dest)
615 {
616 const char *value = NULL;
617 long long value_ll;
618 int rc = pcmk_rc_ok;
619
620 CRM_CHECK(dest != NULL, return -1);
621 *dest = 0;
622 value = crm_element_value(data, name);
623 rc = pcmk__scan_ll(value, &value_ll, 0LL);
624 if (rc != pcmk_rc_ok) {
625 crm_warn("Using default for %s "
626 "because '%s' is not valid milliseconds: %s",
627 name, value, pcmk_rc_str(rc));
628 return -1;
629 }
630 if ((value_ll < 0) || (value_ll > G_MAXUINT)) {
631 crm_warn("Using default for %s because '%s' is out of range",
632 name, value);
633 return -1;
634 }
635 *dest = (guint) value_ll;
636 return pcmk_ok;
637 }
638
639
640
641
642
643
644
645
646
647
648
649
650 int
651 crm_element_value_epoch(const xmlNode *xml, const char *name, time_t *dest)
652 {
653 long long value_ll = 0;
654
655 if (crm_element_value_ll(xml, name, &value_ll) < 0) {
656 return -1;
657 }
658
659
660
661
662 *dest = (time_t) value_ll;
663 return pcmk_ok;
664 }
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679 int
680 crm_element_value_timeval(const xmlNode *xml, const char *name_sec,
681 const char *name_usec, struct timeval *dest)
682 {
683 long long value_i = 0;
684
685 CRM_CHECK(dest != NULL, return -EINVAL);
686 dest->tv_sec = 0;
687 dest->tv_usec = 0;
688
689 if (xml == NULL) {
690 return pcmk_ok;
691 }
692
693
694
695
696
697
698
699
700 errno = 0;
701 if (crm_element_value_ll(xml, name_sec, &value_i) < 0) {
702 return -errno;
703 }
704 dest->tv_sec = (time_t) value_i;
705
706
707 if (crm_element_value_ll(xml, name_usec, &value_i) < 0) {
708 return -errno;
709 }
710 dest->tv_usec = (suseconds_t) value_i;
711
712 return pcmk_ok;
713 }
714
715
716
717
718
719
720
721
722
723
724
725
726
727 int
728 pcmk__xe_get_datetime(const xmlNode *xml, const char *attr, crm_time_t **t)
729 {
730 const char *value = NULL;
731
732 if ((t == NULL) || (*t != NULL) || (xml == NULL) || (attr == NULL)) {
733 return EINVAL;
734 }
735
736 value = crm_element_value(xml, attr);
737 if (value != NULL) {
738 *t = crm_time_new(value);
739 if (*t == NULL) {
740 return pcmk_rc_unpack_error;
741 }
742 }
743 return pcmk_rc_ok;
744 }
745
746
747
748
749
750
751
752
753
754
755
756
757 char *
758 crm_element_value_copy(const xmlNode *data, const char *name)
759 {
760 return pcmk__str_copy(crm_element_value(data, name));
761 }
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777 void
778 hash2smartfield(gpointer key, gpointer value, gpointer user_data)
779 {
780
781
782
783
784
785
786
787
788 const char *name = key;
789 const char *s_value = value;
790
791 xmlNode *xml_node = user_data;
792
793 if (isdigit(name[0])) {
794 xmlNode *tmp = pcmk__xe_create(xml_node, PCMK__XE_PARAM);
795
796 crm_xml_add(tmp, PCMK_XA_NAME, name);
797 crm_xml_add(tmp, PCMK_XA_VALUE, s_value);
798
799 } else if (crm_element_value(xml_node, name) == NULL) {
800 crm_xml_add(xml_node, name, s_value);
801 crm_trace("dumped: %s=%s", name, s_value);
802
803 } else {
804 crm_trace("duplicate: %s=%s", name, s_value);
805 }
806 }
807
808
809
810
811
812
813
814
815
816
817
818
819 void
820 hash2field(gpointer key, gpointer value, gpointer user_data)
821 {
822 const char *name = key;
823 const char *s_value = value;
824
825 xmlNode *xml_node = user_data;
826
827 if (crm_element_value(xml_node, name) == NULL) {
828 crm_xml_add(xml_node, name, s_value);
829
830 } else {
831 crm_trace("duplicate: %s=%s", name, s_value);
832 }
833 }
834
835
836
837
838
839
840
841
842
843
844
845
846
847 void
848 hash2metafield(gpointer key, gpointer value, gpointer user_data)
849 {
850 char *crm_name = NULL;
851
852 if (key == NULL || value == NULL) {
853 return;
854 }
855
856
857
858
859 for (crm_name = key; *crm_name; ++crm_name) {
860 if ((*crm_name == '#') || (*crm_name == ':')) {
861 return;
862 }
863 }
864
865 crm_name = crm_meta_name(key);
866 hash2field(crm_name, value, user_data);
867 free(crm_name);
868 }
869
870
871
872
873
874
875
876
877
878
879
880
881
882 xmlNode *
883 crm_create_nvpair_xml(xmlNode *parent, const char *id, const char *name,
884 const char *value)
885 {
886 xmlNode *nvp;
887
888
889
890
891 CRM_CHECK(id || name, return NULL);
892
893 nvp = pcmk__xe_create(parent, PCMK_XE_NVPAIR);
894
895 if (id) {
896 crm_xml_add(nvp, PCMK_XA_ID, id);
897 } else {
898 crm_xml_set_id(nvp, "%s-%s",
899 pcmk__s(pcmk__xe_id(parent), PCMK_XE_NVPAIR), name);
900 }
901 crm_xml_add(nvp, PCMK_XA_NAME, name);
902 crm_xml_add(nvp, PCMK_XA_VALUE, value);
903 return nvp;
904 }
905
906
907
908
909
910
911
912
913
914
915
916
917 void
918 hash2nvpair(gpointer key, gpointer value, gpointer user_data)
919 {
920 const char *name = key;
921 const char *s_value = value;
922 xmlNode *xml_node = user_data;
923
924 crm_create_nvpair_xml(xml_node, name, name, s_value);
925 crm_trace("dumped: name=%s value=%s", name, s_value);
926 }
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942 GHashTable *
943 xml2list(const xmlNode *parent)
944 {
945 xmlNode *child = NULL;
946 xmlAttrPtr pIter = NULL;
947 xmlNode *nvpair_list = NULL;
948 GHashTable *nvpair_hash = pcmk__strkey_table(free, free);
949
950 CRM_CHECK(parent != NULL, return nvpair_hash);
951
952 nvpair_list = pcmk__xe_first_child(parent, PCMK__XE_ATTRIBUTES, NULL, NULL);
953 if (nvpair_list == NULL) {
954 crm_trace("No attributes in %s", parent->name);
955 crm_log_xml_trace(parent, "No attributes for resource op");
956 }
957
958 crm_log_xml_trace(nvpair_list, "Unpacking");
959
960 for (pIter = pcmk__xe_first_attr(nvpair_list); pIter != NULL;
961 pIter = pIter->next) {
962
963 const char *p_name = (const char *)pIter->name;
964 const char *p_value = pcmk__xml_attr_value(pIter);
965
966 crm_trace("Added %s=%s", p_name, p_value);
967
968 pcmk__insert_dup(nvpair_hash, p_name, p_value);
969 }
970
971 for (child = pcmk__xe_first_child(nvpair_list, PCMK__XE_PARAM, NULL, NULL);
972 child != NULL; child = pcmk__xe_next_same(child)) {
973
974 const char *key = crm_element_value(child, PCMK_XA_NAME);
975 const char *value = crm_element_value(child, PCMK_XA_VALUE);
976
977 crm_trace("Added %s=%s", key, value);
978 if (key != NULL && value != NULL) {
979 pcmk__insert_dup(nvpair_hash, key, value);
980 }
981 }
982
983 return nvpair_hash;
984 }
985
986 void
987 pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value)
988 {
989 crm_xml_add(node, name, pcmk__btoa(value));
990 }
991
992 int
993 pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value)
994 {
995 const char *xml_value = NULL;
996 int ret, rc;
997
998 if (node == NULL) {
999 return ENODATA;
1000 } else if (name == NULL || value == NULL) {
1001 return EINVAL;
1002 }
1003
1004 xml_value = crm_element_value(node, name);
1005
1006 if (xml_value == NULL) {
1007 return ENODATA;
1008 }
1009
1010 rc = crm_str_to_boolean(xml_value, &ret);
1011 if (rc == 1) {
1012 *value = ret;
1013 return pcmk_rc_ok;
1014 } else {
1015 return pcmk_rc_bad_input;
1016 }
1017 }
1018
1019 bool
1020 pcmk__xe_attr_is_true(const xmlNode *node, const char *name)
1021 {
1022 bool value = false;
1023 int rc;
1024
1025 rc = pcmk__xe_get_bool_attr(node, name, &value);
1026 return rc == pcmk_rc_ok && value == true;
1027 }
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042 char *
1043 crm_meta_name(const char *attr_name)
1044 {
1045 char *env_name = NULL;
1046
1047 pcmk__assert(!pcmk__str_empty(attr_name));
1048
1049 env_name = crm_strdup_printf(CRM_META "_%s", attr_name);
1050 for (char *c = env_name; *c != '\0'; ++c) {
1051 if (*c == '-') {
1052 *c = '_';
1053 }
1054 }
1055 return env_name;
1056 }
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070 const char *
1071 crm_meta_value(GHashTable *meta, const char *attr_name)
1072 {
1073 if ((meta != NULL) && (attr_name != NULL)) {
1074 char *key = crm_meta_name(attr_name);
1075 const char *value = g_hash_table_lookup(meta, key);
1076
1077 free(key);
1078 return value;
1079 }
1080 return NULL;
1081 }
1082
1083
1084
1085
1086 #include <crm/common/util_compat.h>
1087
1088 int
1089 pcmk_scan_nvpair(const char *input, char **name, char **value)
1090 {
1091 return pcmk__scan_nvpair(input, name, value);
1092 }
1093
1094 char *
1095 pcmk_format_nvpair(const char *name, const char *value,
1096 const char *units)
1097 {
1098 return pcmk__format_nvpair(name, value, units);
1099 }
1100
1101 char *
1102 pcmk_format_named_time(const char *name, time_t epoch_time)
1103 {
1104 char *now_s = pcmk__epoch2str(&epoch_time, 0);
1105 char *result = crm_strdup_printf("%s=\"%s\"", name, pcmk__s(now_s, ""));
1106
1107 free(now_s);
1108 return result;
1109 }
1110
1111 const char *
1112 crm_xml_replace(xmlNode *node, const char *name, const char *value)
1113 {
1114 bool dirty = FALSE;
1115 xmlAttr *attr = NULL;
1116 const char *old_value = NULL;
1117
1118 CRM_CHECK(node != NULL, return NULL);
1119 CRM_CHECK(name != NULL && name[0] != 0, return NULL);
1120
1121 old_value = crm_element_value(node, name);
1122
1123
1124 CRM_CHECK(old_value != value, return value);
1125
1126 if (pcmk__check_acl(node, name, pcmk__xf_acl_write) == FALSE) {
1127
1128 crm_trace("Cannot replace %s=%s to %s", name, value, node->name);
1129 return NULL;
1130
1131 } else if (old_value && !value) {
1132 pcmk__xe_remove_attr(node, name);
1133 return NULL;
1134 }
1135
1136 if (pcmk__tracking_xml_changes(node, FALSE)) {
1137 if (!old_value || !value || !strcmp(old_value, value)) {
1138 dirty = TRUE;
1139 }
1140 }
1141
1142 attr = pcmk__xe_set_attr_force(node, name, value);
1143 if (dirty) {
1144 pcmk__mark_xml_attr_dirty(attr);
1145 }
1146 CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
1147 return (char *) attr->children->content;
1148 }
1149
1150
1151