Coverage Report

Created: 2026-06-25 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pacemaker/lib/common/xml_element.c
Line
Count
Source
1
/*
2
 * Copyright 2004-2026 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 <stdarg.h>                     // va_start(), etc.
13
#include <stdbool.h>
14
#include <stdint.h>                     // uint32_t
15
#include <stdio.h>                      // NULL, etc.
16
#include <stdlib.h>                     // free(), etc.
17
#include <string.h>                     // strchr(), etc.
18
#include <sys/types.h>                  // time_t, etc.
19
20
#include <libxml/tree.h>                // xmlNode, etc.
21
#include <libxml/valid.h>               // xmlValidateNameValue()
22
#include <libxml/xmlstring.h>           // xmlChar
23
24
#include <crm/crm.h>
25
#include <crm/common/results.h>         // pcmk_rc_ok, etc.
26
#include <crm/common/xml.h>
27
#include "crmcommon_private.h"
28
29
/*!
30
 * \internal
31
 * \brief Call a function for each of an XML element's attributes
32
 *
33
 * \param[in,out] xml        XML element
34
 * \param[in]     fn         Function to call for each attribute (returns
35
 *                           \c true to continue iterating over attributes or
36
 *                           \c false to stop)
37
 * \param[in,out] user_data  User data argument for \p fn
38
 *
39
 * \return \c false if any \p fn call returned \c false, or \c true otherwise
40
 *
41
 * \note \p fn may remove its attribute argument.
42
 */
43
bool
44
pcmk__xe_foreach_attr(xmlNode *xml, bool (*fn)(xmlAttr *, void *),
45
                      void *user_data)
46
0
{
47
0
    xmlAttr *attr = pcmk__xe_first_attr(xml);
48
49
0
    pcmk__assert(fn != NULL);
50
51
0
    while (attr != NULL) {
52
0
        xmlAttr *next = attr->next;
53
54
0
        if (!fn(attr, user_data)) {
55
0
            return false;
56
0
        }
57
58
0
        attr = next;
59
0
    }
60
61
0
    return true;
62
0
}
63
64
/*!
65
 * \internal
66
 * \brief Call a function for each of a \c const XML element's attributes
67
 *
68
 * \param[in]     xml        XML element
69
 * \param[in]     fn         Function to call for each attribute (returns
70
 *                           \c true to continue iterating over attributes or
71
 *                           \c false to stop)
72
 * \param[in,out] user_data  User data argument for \p fn
73
 *
74
 * \return \c false if any \p fn call returned \c false, or \c true otherwise
75
 */
76
bool
77
pcmk__xe_foreach_const_attr(const xmlNode *xml,
78
                            bool (*fn)(const xmlAttr *, void *),
79
                            void *user_data)
80
0
{
81
0
    pcmk__assert(fn != NULL);
82
83
0
    for (const xmlAttr *attr = pcmk__xe_first_attr(xml); attr != NULL;
84
0
         attr = attr->next) {
85
86
0
        if (!fn(attr, user_data)) {
87
0
            return false;
88
0
        }
89
0
    }
90
91
0
    return true;
92
0
}
93
94
/*!
95
 * \internal
96
 * \brief Find first XML child element matching given criteria
97
 *
98
 * \param[in] parent     XML element to search (can be \c NULL)
99
 * \param[in] node_name  If not \c NULL, only match children of this type
100
 * \param[in] attr_n     If not \c NULL, only match children with an attribute
101
 *                       of this name.
102
 * \param[in] attr_v     If \p attr_n and this are not NULL, only match children
103
 *                       with an attribute named \p attr_n and this value
104
 *
105
 * \return Matching XML child element, or \c NULL if none found
106
 */
107
xmlNode *
108
pcmk__xe_first_child(const xmlNode *parent, const char *node_name,
109
                     const char *attr_n, const char *attr_v)
110
0
{
111
0
    xmlNode *child = NULL;
112
113
0
    CRM_CHECK((attr_v == NULL) || (attr_n != NULL), return NULL);
114
115
0
    if (parent == NULL) {
116
0
        return NULL;
117
0
    }
118
119
0
    child = parent->children;
120
0
    while ((child != NULL) && (child->type != XML_ELEMENT_NODE)) {
121
0
        child = child->next;
122
0
    }
123
124
0
    for (; child != NULL; child = pcmk__xe_next(child, NULL)) {
125
0
        const char *value = NULL;
126
127
0
        if ((node_name != NULL) && !pcmk__xe_is(child, node_name)) {
128
            // Node name mismatch
129
0
            continue;
130
0
        }
131
0
        if (attr_n == NULL) {
132
            // No attribute match needed
133
0
            return child;
134
0
        }
135
136
0
        value = pcmk__xe_get(child, attr_n);
137
138
0
        if ((attr_v == NULL) && (value != NULL)) {
139
            // attr_v == NULL: Attribute attr_n must be set (to any value)
140
0
            return child;
141
0
        }
142
0
        if ((attr_v != NULL) && (pcmk__str_eq(value, attr_v, pcmk__str_none))) {
143
            // attr_v != NULL: Attribute attr_n must be set to value attr_v
144
0
            return child;
145
0
        }
146
0
    }
147
148
0
    if (attr_n == NULL) {
149
0
        pcmk__trace("%s XML has no child element of %s type", parent->name,
150
0
                    pcmk__s(node_name, "any"));
151
0
    } else {
152
0
        pcmk__trace("%s XML has no child element of %s type with %s='%s'",
153
0
                    parent->name, pcmk__s(node_name, "any"), attr_n, attr_v);
154
0
    }
155
0
    return NULL;
156
0
}
157
158
/*!
159
 * \internal
160
 * \brief Return next sibling element of an XML element
161
 *
162
 * \param[in] xml           XML element to check
163
 * \param[in] element_name  If not NULL, get next sibling with this element name
164
 *
165
 * \return Next desired sibling of \p xml (or NULL if none)
166
 */
167
xmlNode *
168
pcmk__xe_next(const xmlNode *xml, const char *element_name)
169
0
{
170
0
    for (xmlNode *next = (xml == NULL)? NULL : xml->next;
171
0
         next != NULL; next = next->next) {
172
0
        if ((next->type == XML_ELEMENT_NODE)
173
0
            && ((element_name == NULL) || pcmk__xe_is(next, element_name))) {
174
0
            return next;
175
0
        }
176
0
    }
177
0
    return NULL;
178
0
}
179
180
/*!
181
 * \internal
182
 * \brief Parse an integer score from an XML attribute
183
 *
184
 * \param[in]  xml            XML element with attribute to parse
185
 * \param[in]  name           Name of attribute to parse
186
 * \param[out] score          Where to store parsed score (can be NULL to
187
 *                            just validate)
188
 * \param[in]  default_score  What to return if the attribute value is not
189
 *                            present or invalid
190
 *
191
 * \return Standard Pacemaker return code
192
 */
193
int
194
pcmk__xe_get_score(const xmlNode *xml, const char *name, int *score,
195
                   int default_score)
196
0
{
197
0
    const char *value = NULL;
198
199
0
    CRM_CHECK((xml != NULL) && (name != NULL), return EINVAL);
200
0
    value = pcmk__xe_get(xml, name);
201
0
    return pcmk_parse_score(value, score, default_score);
202
0
}
203
204
/*!
205
 * \internal
206
 * \brief Set an XML attribute, expanding \c ++ and \c += where appropriate
207
 *
208
 * If \p target already has an attribute named \p name set to an integer value
209
 * and \p value is an addition assignment expression on \p name, then expand
210
 * \p value to an integer and set attribute \p name to the expanded value in
211
 * \p target.
212
 *
213
 * Otherwise, set attribute \p name on \p target using the literal \p value.
214
 *
215
 * The original attribute value in \p target and the number in an assignment
216
 * expression in \p value are parsed and added as scores (that is, their values
217
 * are capped at \c INFINITY and \c -INFINITY). For more details, refer to
218
 * \c pcmk_parse_score().
219
 *
220
 * For example, suppose \p target has an attribute named \c "X" with value
221
 * \c "5", and that \p name is \c "X".
222
 * * If \p value is \c "X++", the new value of \c "X" in \p target is \c "6".
223
 * * If \p value is \c "X+=3", the new value of \c "X" in \p target is \c "8".
224
 * * If \p value is \c "val", the new value of \c "X" in \p target is \c "val".
225
 * * If \p value is \c "Y++", the new value of \c "X" in \p target is \c "Y++".
226
 *
227
 * \param[in,out] target  XML node whose attribute to set
228
 * \param[in]     name    Name of the attribute to set
229
 * \param[in]     value   New value of attribute to set (if NULL, initial value
230
 *                        will be left unchanged)
231
 *
232
 * \return Standard Pacemaker return code (specifically, \c EINVAL on invalid
233
 *         argument, or \c pcmk_rc_ok otherwise)
234
 */
235
int
236
pcmk__xe_set_score(xmlNode *target, const char *name, const char *value)
237
0
{
238
0
    const char *old_value = NULL;
239
240
0
    CRM_CHECK((target != NULL) && (name != NULL), return EINVAL);
241
242
0
    if (value == NULL) {
243
        // @TODO Maybe instead delete the attribute or set it to 0
244
0
        return pcmk_rc_ok;
245
0
    }
246
247
0
    old_value = pcmk__xe_get(target, name);
248
249
    // If no previous value, skip to default case and set the value unexpanded.
250
0
    if (old_value != NULL) {
251
0
        const char *n = name;
252
0
        const char *v = value;
253
254
        // Stop at first character that differs between name and value
255
0
        for (; (*n == *v) && (*n != '\0'); n++, v++);
256
257
        // If value begins with name followed by a "++" or "+="
258
0
        if ((*n == '\0')
259
0
            && (*v++ == '+')
260
0
            && ((*v == '+') || (*v == '='))) {
261
262
0
            int add = 1;
263
0
            int old_value_i = 0;
264
0
            int rc = pcmk_rc_ok;
265
266
            // If we're expanding ourselves, no previous value was set; use 0
267
0
            if (old_value != value) {
268
0
                rc = pcmk_parse_score(old_value, &old_value_i, 0);
269
0
                if (rc != pcmk_rc_ok) {
270
                    // @TODO This is inconsistent with old_value==NULL
271
0
                    pcmk__trace("Using 0 before incrementing %s because '%s' "
272
0
                                "is not a score",
273
0
                                name, old_value);
274
0
                }
275
0
            }
276
277
            /* value="X++": new value of X is old_value + 1
278
             * value="X+=Y": new value of X is old_value + Y (for some number Y)
279
             */
280
0
            if (*v != '+') {
281
0
                rc = pcmk_parse_score(++v, &add, 0);
282
0
                if (rc != pcmk_rc_ok) {
283
                    // @TODO We should probably skip expansion instead
284
0
                    pcmk__trace("Not incrementing %s because '%s' does not "
285
0
                                "have a valid increment",
286
0
                                name, value);
287
0
                }
288
0
            }
289
290
0
            pcmk__xe_set_int(target, name, pcmk__add_scores(old_value_i, add));
291
0
            return pcmk_rc_ok;
292
0
        }
293
0
    }
294
295
    // Default case: set the attribute unexpanded (with value treated literally)
296
0
    if (old_value != value) {
297
0
        pcmk__xe_set(target, name, value);
298
0
    }
299
0
    return pcmk_rc_ok;
300
0
}
301
302
/*!
303
 * \internal
304
 * \brief User data for \c copy_attr()
305
 */
306
struct copy_attr_data {
307
    xmlNode *target;    //!< Element to copy the attribute to
308
    uint32_t flags;     //!< Group of <tt>enum pcmk__xa_flags</tt>
309
};
310
311
/*!
312
 * \internal
313
 * \brief Copy an attribute to a target element
314
 *
315
 * \param[in]     attr       XML attribute
316
 * \param[in,out] user_data  User data (<tt>struct copy_attr_data *</tt>)
317
 *
318
 * \return \c true (to continue iterating)
319
 *
320
 * \note This is compatible with \c pcmk__xe_foreach_const_attr().
321
 */
322
static bool
323
copy_attr(const xmlAttr *attr, void *user_data)
324
0
{
325
0
    struct copy_attr_data *data = user_data;
326
0
    const char *name = (const char *) attr->name;
327
0
    const char *value = pcmk__xml_attr_value(attr);
328
329
0
    if (pcmk__is_set(data->flags, pcmk__xaf_no_overwrite)
330
0
        && (pcmk__xe_get(data->target, name) != NULL)) {
331
332
0
        return true;
333
0
    }
334
335
0
    if (pcmk__is_set(data->flags, pcmk__xaf_score_update)) {
336
0
        pcmk__xe_set_score(data->target, name, value);
337
0
        return true;
338
0
    }
339
340
0
    pcmk__xe_set(data->target, name, value);
341
0
    return true;
342
0
}
343
344
/*!
345
 * \internal
346
 * \brief Copy XML attributes from a source element to a target element
347
 *
348
 * This is similar to \c xmlCopyPropList() except that attributes are marked
349
 * as dirty for change tracking purposes.
350
 *
351
 * \param[in,out] target  XML element to receive copied attributes from \p src
352
 * \param[in]     src     XML element whose attributes to copy to \p target
353
 * \param[in]     flags   Group of <tt>enum pcmk__xa_flags</tt>
354
 *
355
 * \return Standard Pacemaker return code
356
 */
357
int
358
pcmk__xe_copy_attrs(xmlNode *target, const xmlNode *src, uint32_t flags)
359
0
{
360
0
    struct copy_attr_data data = {
361
0
        .target = target,
362
0
        .flags = flags,
363
0
    };
364
365
0
    CRM_CHECK((src != NULL) && (target != NULL), return EINVAL);
366
367
0
    pcmk__xe_foreach_const_attr(src, copy_attr, &data);
368
0
    return pcmk_rc_ok;
369
0
}
370
371
/*!
372
 * \internal
373
 * \brief Compare two XML attributes by name
374
 *
375
 * \param[in] a  First XML attribute to compare
376
 * \param[in] b  Second XML attribute to compare
377
 *
378
 * \retval  negative \c a->name is \c NULL or comes before \c b->name
379
 *                   lexicographically
380
 * \retval  0        \c a->name and \c b->name are equal
381
 * \retval  positive \c b->name is \c NULL or comes before \c a->name
382
 *                   lexicographically
383
 */
384
static int
385
compare_xml_attr(const void *a, const void *b)
386
0
{
387
0
    const xmlAttr *attr_a = a;
388
0
    const xmlAttr *attr_b = b;
389
390
0
    return pcmk__strcmp((const char *) attr_a->name,
391
0
                        (const char *) attr_b->name, pcmk__str_none);
392
0
}
393
394
/*!
395
 * \internal
396
 * \brief Prepend an attribute to a list
397
 *
398
 * \param[in]     attr       XML attribute
399
 * \param[in,out] user_data  List of attributes (<tt>GSList **</tt>)
400
 *
401
 * \return \c true (to continue iterating)
402
 *
403
 * \note The argument is const because it isn't modified here and it gets added
404
 *       as <tt>void *</tt> anyway. However, it will be used as non-const when
405
 *       we process the list later.
406
 * \note This is compatible with \c pcmk__xe_foreach_const_attr().
407
 */
408
static bool
409
prepend_attr(const xmlAttr *attr, void *user_data)
410
0
{
411
0
    GSList **attr_list = user_data;
412
413
0
    *attr_list = g_slist_prepend(*attr_list, (void *) attr);
414
0
    return true;
415
0
}
416
417
/*!
418
 * \internal
419
 * \brief Unlink an attribute and then re-add it to its parent element
420
 *
421
 * This moves the attribute to the end of its parent's attribute list.
422
 *
423
 * \param[in,out] data       XML attribute (<tt>xmlNode *</tt>)
424
 * \param[in,out] user_data  Parent element of \p data (<tt>xmlNode *</tt>)
425
 *
426
 * \note This is a \c GFunc compatible with \c g_slist_foreach().
427
 */
428
static void
429
unlink_and_add_attr(void *data, void *user_data)
430
0
{
431
    /* attr was added to the list as an xmlAttr *, but we need to cast it to
432
     * xmlNode * for xmlUnlinkNode() and xmlAddChild()
433
     */
434
0
    xmlNode *attr = data;
435
0
    xmlNode *xml = user_data;
436
437
0
    xmlUnlinkNode(attr);
438
0
    xmlAddChild(xml, attr);
439
0
}
440
441
/*!
442
 * \internal
443
 * \brief Sort an XML element's attributes by name
444
 *
445
 * This does not consider ACLs and does not mark the attributes as deleted or
446
 * dirty. Upon return, all attributes still exist and are set to the same values
447
 * as before the call. The only thing that may change is the order of the
448
 * attribute list.
449
 *
450
 * \param[in,out] xml  XML element whose attributes to sort
451
 */
452
void
453
pcmk__xe_sort_attrs(xmlNode *xml)
454
0
{
455
0
    GSList *attr_list = NULL;
456
457
0
    pcmk__xe_foreach_const_attr(xml, prepend_attr, &attr_list);
458
459
0
    attr_list = g_slist_sort(attr_list, compare_xml_attr);
460
461
0
    g_slist_foreach(attr_list, unlink_and_add_attr, xml);
462
463
0
    g_slist_free(attr_list);
464
0
}
465
466
/*!
467
 * \internal
468
 * \brief Remove a named attribute from an XML element
469
 *
470
 * \param[in,out] element  XML element to remove an attribute from
471
 * \param[in]     name     Name of attribute to remove
472
 */
473
void
474
pcmk__xe_remove_attr(xmlNode *element, const char *name)
475
0
{
476
0
    if (name != NULL) {
477
0
        pcmk__xa_remove(xmlHasProp(element, (const xmlChar *) name), false);
478
0
    }
479
0
}
480
481
/*!
482
 * \internal
483
 * \brief Remove a named attribute from an XML element
484
 *
485
 * This is a wrapper for \c pcmk__xe_remove_attr() for use with
486
 * \c pcmk__xml_tree_foreach().
487
 *
488
 * \param[in,out] xml        XML element to remove an attribute from
489
 * \param[in]     user_data  Name of attribute to remove
490
 *
491
 * \return \c true (to continue traversing the tree)
492
 *
493
 * \note This is compatible with \c pcmk__xml_tree_foreach().
494
 */
495
bool
496
pcmk__xe_remove_attr_cb(xmlNode *xml, void *user_data)
497
0
{
498
0
    const char *name = user_data;
499
500
0
    pcmk__xe_remove_attr(xml, name);
501
0
    return true;
502
0
}
503
504
/*!
505
 * \internal
506
 * \brief User data for \c remove_xa_if_matching()
507
 */
508
struct remove_xa_if_matching_data {
509
    //! \c force argument for \c pcmk__xa_remove()
510
    bool force;
511
512
    //! Match function to call for each attribute
513
    bool (*match)(xmlAttr *, void *);
514
515
    //! User data argument for match function
516
    void *match_data;
517
};
518
519
/*!
520
 * \internal
521
 * \brief Remove an attribute if a match function returns true for it
522
 *
523
 * Do nothing if any previous removal has failed.
524
 *
525
 * \param[in,out] attr       XML attribute
526
 * \param[in,out] user_data  User data
527
 *                           (<tt>struct remove_xa_if_matching_data *</tt>)
528
 *
529
 * \return \c true (to continue iterating) if \p attr was removed successfully
530
 *         or if we did not attempt to remove it; or \c false if removal failed
531
 *
532
 * \note This is compatible with \c pcmk__xe_foreach_attr().
533
 */
534
static bool
535
remove_xa_if_matching(xmlAttr *attr, void *user_data)
536
0
{
537
0
    struct remove_xa_if_matching_data *data = user_data;
538
539
0
    if ((data->match != NULL) && !data->match(attr, data->match_data)) {
540
        // attr is not a match
541
0
        return true;
542
0
    }
543
544
    // @TODO Why do we stop removing attributes if one removal fails?
545
0
    return (pcmk__xa_remove(attr, data->force) == pcmk_rc_ok);
546
0
}
547
548
/*!
549
 * \internal
550
 * \brief Remove an XML element's attributes that match some criteria
551
 *
552
 * \param[in,out] element    XML element to modify
553
 * \param[in]     force      If \c true, remove matching attributes immediately,
554
 *                           ignoring ACLs and change tracking
555
 * \param[in]     match      If not NULL, only remove attributes for which
556
 *                           this function returns true
557
 * \param[in,out] user_data  Data to pass to \p match
558
 */
559
void
560
pcmk__xe_remove_matching_attrs(xmlNode *element, bool force,
561
                               bool (*match)(xmlAttr *, void *),
562
                               void *user_data)
563
0
{
564
0
    struct remove_xa_if_matching_data data = {
565
0
        .force = force,
566
0
        .match = match,
567
0
        .match_data = user_data,
568
0
    };
569
570
0
    pcmk__xe_foreach_attr(element, remove_xa_if_matching, &data);
571
0
}
572
573
/*!
574
 * \internal
575
 * \brief Create a new XML element under a given parent
576
 *
577
 * \param[in,out] parent  XML element that will be the new element's parent
578
 *                        (\c NULL to create a new XML document with the new
579
 *                        node as root)
580
 * \param[in]     name    Name of new element
581
 *
582
 * \return Newly created XML element (guaranteed not to be \c NULL)
583
 */
584
xmlNode *
585
pcmk__xe_create(xmlNode *parent, const char *name)
586
0
{
587
0
    xmlNode *node = NULL;
588
589
0
    pcmk__assert(!pcmk__str_empty(name));
590
591
0
    if (parent == NULL) {
592
0
        xmlDoc *doc = pcmk__xml_new_doc();
593
594
0
        node = xmlNewDocRawNode(doc, NULL, (const xmlChar *) name, NULL);
595
0
        pcmk__mem_assert(node);
596
597
0
        xmlDocSetRootElement(doc, node);
598
599
0
    } else {
600
0
        node = xmlNewChild(parent, NULL, (const xmlChar *) name, NULL);
601
0
        pcmk__mem_assert(node);
602
0
    }
603
604
0
    pcmk__xml_new_private_data(node);
605
0
    return node;
606
0
}
607
608
/*!
609
 * \internal
610
 * \brief Set a formatted string as an XML node's content
611
 *
612
 * \param[in,out] node    Node whose content to set
613
 * \param[in]     format  <tt>printf(3)</tt>-style format string
614
 * \param[in]     ...     Arguments for \p format
615
 *
616
 * \note This function escapes special characters. \c xmlNodeSetContent() does
617
 *       not.
618
 */
619
G_GNUC_PRINTF(2, 3)
620
void
621
pcmk__xe_set_content(xmlNode *node, const char *format, ...)
622
0
{
623
0
    if (node != NULL) {
624
0
        const char *content = NULL;
625
0
        char *buf = NULL;
626
627
        /* xmlNodeSetContent() frees node->children and replaces it with new
628
         * text. If this function is called for a node that already has a non-
629
         * text child, it's a bug.
630
         */
631
0
        CRM_CHECK((node->children == NULL)
632
0
                  || (node->children->type == XML_TEXT_NODE),
633
0
                  return);
634
635
0
        if (strchr(format, '%') == NULL) {
636
            // Nothing to format
637
0
            content = format;
638
639
0
        } else {
640
0
            va_list ap;
641
642
0
            va_start(ap, format);
643
644
0
            if (pcmk__str_eq(format, "%s", pcmk__str_none)) {
645
                // No need to make a copy
646
0
                content = va_arg(ap, const char *);
647
648
0
            } else {
649
0
                pcmk__assert(vasprintf(&buf, format, ap) >= 0);
650
0
                content = buf;
651
0
            }
652
0
            va_end(ap);
653
0
        }
654
655
0
        xmlNodeSetContent(node, (const xmlChar *) content);
656
0
        free(buf);
657
0
    }
658
0
}
659
660
/*!
661
 * \internal
662
 * \brief Set a formatted string as an XML element's ID
663
 *
664
 * If the formatted string would not be a valid ID, it's first sanitized by
665
 * \c pcmk__xml_sanitize_id().
666
 *
667
 * \param[in,out] node    Node whose ID to set
668
 * \param[in]     format  <tt>printf(3)</tt>-style format string
669
 * \param[in]     ...     Arguments for \p format
670
 */
671
G_GNUC_PRINTF(2, 3)
672
void
673
pcmk__xe_set_id(xmlNode *node, const char *format, ...)
674
0
{
675
0
    char *id = NULL;
676
0
    va_list ap;
677
678
0
    pcmk__assert(!pcmk__str_empty(format));
679
680
0
    if (node == NULL) {
681
0
        return;
682
0
    }
683
684
0
    va_start(ap, format);
685
0
    pcmk__assert(vasprintf(&id, format, ap) >= 0);
686
0
    va_end(ap);
687
688
0
    if (!xmlValidateNameValue((const xmlChar *) id)) {
689
0
        pcmk__xml_sanitize_id(id);
690
0
    }
691
0
    pcmk__xe_set(node, PCMK_XA_ID, id);
692
0
    free(id);
693
0
}
694
695
/*!
696
 * \internal
697
 * \brief Add a "last written" attribute to an XML element, set to current time
698
 *
699
 * \param[in,out] xe  XML element to add attribute to
700
 *
701
 * \return Value that was set, or NULL on error
702
 */
703
const char *
704
pcmk__xe_add_last_written(xmlNode *xe)
705
0
{
706
0
    char *now_s = pcmk__epoch2str(NULL, 0);
707
708
0
    pcmk__xe_set(xe, PCMK_XA_CIB_LAST_WRITTEN,
709
0
                 pcmk__s(now_s, "Could not determine current time"));
710
0
    free(now_s);
711
0
    return pcmk__xe_get(xe, PCMK_XA_CIB_LAST_WRITTEN);
712
0
}
713
714
/*!
715
 * \internal
716
 * \brief Merge one XML tree into another
717
 *
718
 * Here, "merge" means:
719
 * 1. Copy attribute values from \p update to the target, overwriting in case of
720
 *    conflict.
721
 * 2. Descend through \p update and the target in parallel. At each level, for
722
 *    each child of \p update, look for a matching child of the target.
723
 *    a. For each child, if a match is found, go to step 1, recursively merging
724
 *       the child of \p update into the child of the target.
725
 *    b. Otherwise, copy the child of \p update as a child of the target.
726
 *
727
 * A match is defined as the first child of the same type within the target,
728
 * with:
729
 * * the \c PCMK_XA_ID attribute matching, if set in \p update; otherwise,
730
 * * the \c PCMK_XA_ID_REF attribute matching, if set in \p update
731
 *
732
 * This function does not delete any elements or attributes from the target. It
733
 * may add elements or overwrite attributes, as described above.
734
 *
735
 * \param[in,out] parent   If \p target is NULL and this is not, add or update
736
 *                         child of this XML node that matches \p update
737
 * \param[in,out] target   If not NULL, update this XML
738
 * \param[in]     update   Make the desired XML match this (must not be \c NULL)
739
 * \param[in]     flags    Group of <tt>enum pcmk__xa_flags</tt>
740
 *
741
 * \note At least one of \p parent and \p target must be non-<tt>NULL</tt>.
742
 * \note This function is recursive. For the top-level call, \p parent is
743
 *       \c NULL and \p target is not \c NULL. For recursive calls, \p target is
744
 *       \c NULL and \p parent is not \c NULL.
745
 */
746
static void
747
update_xe(xmlNode *parent, xmlNode *target, xmlNode *update, uint32_t flags)
748
0
{
749
    // @TODO Try to refactor further, possibly using pcmk__xml_tree_foreach()
750
0
    const char *update_name = NULL;
751
0
    const char *update_id_attr = NULL;
752
0
    const char *update_id_val = NULL;
753
0
    char *trace_s = NULL;
754
755
0
    pcmk__log_xml_trace(update, "update");
756
0
    pcmk__log_xml_trace(target, "target");
757
758
0
    CRM_CHECK(update != NULL, goto done);
759
760
0
    if (update->type == XML_COMMENT_NODE) {
761
0
        pcmk__xc_update(parent, target, update);
762
0
        goto done;
763
0
    }
764
765
0
    update_name = (const char *) update->name;
766
767
0
    CRM_CHECK(update_name != NULL, goto done);
768
0
    CRM_CHECK((target != NULL) || (parent != NULL), goto done);
769
770
0
    update_id_val = pcmk__xe_id(update);
771
0
    if (update_id_val != NULL) {
772
0
        update_id_attr = PCMK_XA_ID;
773
774
0
    } else {
775
0
        update_id_val = pcmk__xe_get(update, PCMK_XA_ID_REF);
776
0
        if (update_id_val != NULL) {
777
0
            update_id_attr = PCMK_XA_ID_REF;
778
0
        }
779
0
    }
780
781
0
    pcmk__if_tracing(
782
0
        {
783
0
            if (update_id_attr != NULL) {
784
0
                trace_s = pcmk__assert_asprintf("<%s %s=%s/>",
785
0
                                                update_name, update_id_attr,
786
0
                                                update_id_val);
787
0
            } else {
788
0
                trace_s = pcmk__assert_asprintf("<%s/>", update_name);
789
0
            }
790
0
        },
791
0
        {}
792
0
    );
793
794
0
    if (target == NULL) {
795
        // Recursive call
796
0
        target = pcmk__xe_first_child(parent, update_name, update_id_attr,
797
0
                                      update_id_val);
798
0
    }
799
800
0
    if (target == NULL) {
801
        // Recursive call with no existing matching child
802
0
        target = pcmk__xe_create(parent, update_name);
803
0
        pcmk__trace("Added %s", pcmk__s(trace_s, update_name));
804
805
0
    } else {
806
        // Either recursive call with match, or top-level call
807
0
        pcmk__trace("Found node %s to update", pcmk__s(trace_s, update_name));
808
0
    }
809
810
0
    CRM_CHECK(pcmk__xe_is(target, (const char *) update->name), return);
811
812
0
    pcmk__xe_copy_attrs(target, update, flags);
813
814
0
    for (xmlNode *child = pcmk__xml_first_child(update); child != NULL;
815
0
         child = pcmk__xml_next(child)) {
816
817
0
        pcmk__trace("Updating child of %s", pcmk__s(trace_s, update_name));
818
0
        update_xe(target, NULL, child, flags);
819
0
    }
820
821
0
    pcmk__trace("Finished with %s", pcmk__s(trace_s, update_name));
822
823
0
done:
824
0
    free(trace_s);
825
0
}
826
827
/*!
828
 * \internal
829
 * \brief Check whether an element's attribute value matches a reference value
830
 *
831
 * \param[in] attr       XML attribute
832
 * \param[in] user_data  XML element to match against (<tt>const xmlNode *</tt>)
833
 *
834
 * \return \c true (to continue iterating) if \p user_data has an attribute with
835
 *         the same name and value as \p attr, or \c false otherwise
836
 *
837
 * \note This is compatible with \c pcmk__xe_foreach_const_attr()
838
 */
839
static bool
840
match_attr_value(const xmlAttr *attr, void *user_data)
841
0
{
842
0
    const xmlNode *xml = user_data;
843
0
    const char *ref_val = pcmk__xml_attr_value(attr);
844
0
    const char *xml_val = pcmk__xe_get(xml, (const char *) attr->name);
845
846
    // Check whether attr's value matches the corresponding value in xml
847
0
    return pcmk__str_eq(ref_val, xml_val, pcmk__str_casei);
848
0
}
849
850
/*!
851
 * \internal
852
 * \brief Delete an XML subtree if it matches a search element
853
 *
854
 * A match is defined as follows:
855
 * * \p xml and \p user_data are both element nodes of the same type.
856
 * * If \p user_data has attributes set, \p xml has those attributes set to the
857
 *   same values. (\p xml may have additional attributes set to arbitrary
858
 *   values.)
859
 *
860
 * \param[in,out] xml        XML subtree to delete upon match
861
 * \param[in]     user_data  Search element
862
 *
863
 * \return \c true to continue traversing the tree, or \c false to stop (because
864
 *         \p xml was deleted)
865
 *
866
 * \note This is compatible with \c pcmk__xml_tree_foreach().
867
 */
868
static bool
869
delete_xe_if_matching(xmlNode *xml, void *user_data)
870
0
{
871
0
    const xmlNode *search = user_data;
872
873
0
    if (!pcmk__xe_is(search, (const char *) xml->name)) {
874
        // No match: either not both elements, or different element types
875
0
        return true;
876
0
    }
877
878
0
    if (!pcmk__xe_foreach_const_attr(search, match_attr_value, xml)) {
879
        // No match: mismatched attribute values
880
0
        return true;
881
0
    }
882
883
0
    pcmk__log_xml_trace(xml, "delete-match");
884
0
    pcmk__log_xml_trace(search, "delete-search");
885
0
    pcmk__xml_free(xml);
886
887
    // Found a match and deleted it; stop traversing tree
888
0
    return false;
889
0
}
890
891
/*!
892
 * \internal
893
 * \brief Search an XML tree depth-first and delete the first matching element
894
 *
895
 * This function does not attempt to match the tree root (\p xml).
896
 *
897
 * A match with a node \c node is defined as follows:
898
 * * \c node and \p search are both element nodes of the same type.
899
 * * If \p search has attributes set, \c node has those attributes set to the
900
 *   same values. (\c node may have additional attributes set to arbitrary
901
 *   values.)
902
 *
903
 * \param[in,out] xml     XML subtree to search
904
 * \param[in]     search  Element to match against
905
 *
906
 * \return Standard Pacemaker return code (specifically, \c pcmk_rc_ok on
907
 *         successful deletion and an error code otherwise)
908
 */
909
int
910
pcmk__xe_delete_match(xmlNode *xml, xmlNode *search)
911
0
{
912
    // See @COMPAT comment in pcmk__xe_replace_match()
913
0
    CRM_CHECK((xml != NULL) && (search != NULL), return EINVAL);
914
915
0
    for (xml = pcmk__xe_first_child(xml, NULL, NULL, NULL); xml != NULL;
916
0
         xml = pcmk__xe_next(xml, NULL)) {
917
918
0
        if (!pcmk__xml_tree_foreach(xml, delete_xe_if_matching, search)) {
919
            // Found and deleted an element
920
0
            return pcmk_rc_ok;
921
0
        }
922
0
    }
923
924
    // No match found in this subtree
925
0
    return ENXIO;
926
0
}
927
928
/*!
929
 * \internal
930
 * \brief Replace one XML subtree with a copy of another if the two match
931
 *
932
 * A match is defined as follows:
933
 * * \p xml and \p user_data are both element nodes of the same type.
934
 * * If \p user_data has the \c PCMK_XA_ID attribute set, then \p xml has
935
 *   \c PCMK_XA_ID set to the same value.
936
 *
937
 * \param[in,out] xml        XML subtree to replace with \p user_data upon match
938
 * \param[in]     user_data  XML to replace \p xml with a copy of upon match
939
 *
940
 * \return \c true to continue traversing the tree, or \c false to stop (because
941
 *         \p xml was replaced by \p user_data)
942
 *
943
 * \note This is compatible with \c pcmk__xml_tree_foreach().
944
 */
945
static bool
946
replace_xe_if_matching(xmlNode *xml, void *user_data)
947
0
{
948
0
    xmlNode *replace = user_data;
949
0
    const char *xml_id = NULL;
950
0
    const char *replace_id = NULL;
951
952
0
    xml_id = pcmk__xe_id(xml);
953
0
    replace_id = pcmk__xe_id(replace);
954
955
0
    if (!pcmk__xe_is(replace, (const char *) xml->name)) {
956
        // No match: either not both elements, or different element types
957
0
        return true;
958
0
    }
959
960
0
    if ((replace_id != NULL)
961
0
        && !pcmk__str_eq(replace_id, xml_id, pcmk__str_none)) {
962
963
        // No match: ID was provided in replace and doesn't match xml's ID
964
0
        return true;
965
0
    }
966
967
0
    pcmk__log_xml_trace(xml, "replace-match");
968
0
    pcmk__log_xml_trace(replace, "replace-with");
969
0
    pcmk__xml_replace_with_copy(xml, replace);
970
971
    // Found a match and replaced it; stop traversing tree
972
0
    return false;
973
0
}
974
975
/*!
976
 * \internal
977
 * \brief Search an XML tree depth-first and replace the first matching element
978
 *
979
 * This function does not attempt to match the tree root (\p xml).
980
 *
981
 * A match with a node \c node is defined as follows:
982
 * * \c node and \p replace are both element nodes of the same type.
983
 * * If \p replace has the \c PCMK_XA_ID attribute set, then \c node has
984
 *   \c PCMK_XA_ID set to the same value.
985
 *
986
 * \param[in,out] xml      XML tree to search
987
 * \param[in]     replace  XML to replace a matching element with a copy of
988
 *
989
 * \return Standard Pacemaker return code (specifically, \c pcmk_rc_ok on
990
 *         successful replacement and an error code otherwise)
991
 */
992
int
993
pcmk__xe_replace_match(xmlNode *xml, xmlNode *replace)
994
0
{
995
    /* @COMPAT Some of this behavior (like not matching the tree root, which is
996
     * allowed by pcmk__xe_update_match()) is questionable for general use but
997
     * required for backward compatibility by cib__process_replace() and
998
     * cib__process_delete(). Behavior can change at a major version release if
999
     * desired.
1000
     */
1001
0
    CRM_CHECK((xml != NULL) && (replace != NULL), return EINVAL);
1002
1003
0
    for (xml = pcmk__xe_first_child(xml, NULL, NULL, NULL); xml != NULL;
1004
0
         xml = pcmk__xe_next(xml, NULL)) {
1005
1006
0
        if (!pcmk__xml_tree_foreach(xml, replace_xe_if_matching, replace)) {
1007
            // Found and replaced an element
1008
0
            return pcmk_rc_ok;
1009
0
        }
1010
0
    }
1011
1012
    // No match found in this subtree
1013
0
    return ENXIO;
1014
0
}
1015
1016
//! User data for \c update_xe_if_matching()
1017
struct update_data {
1018
    xmlNode *update;    //!< Update source
1019
    uint32_t flags;     //!< Group of <tt>enum pcmk__xa_flags</tt>
1020
};
1021
1022
/*!
1023
 * \internal
1024
 * \brief Update one XML subtree with another if the two match
1025
 *
1026
 * "Update" means to merge a source subtree into a target subtree (see
1027
 * \c update_xe()).
1028
 *
1029
 * A match is defined as follows:
1030
 * * \p xml and \p user_data->update are both element nodes of the same type.
1031
 * * \p xml and \p user_data->update have the same \c PCMK_XA_ID attribute
1032
 *   value, or \c PCMK_XA_ID is unset in both
1033
 *
1034
 * \param[in,out] xml        XML subtree to update with \p user_data->update
1035
 *                           upon match
1036
 * \param[in]     user_data  <tt>struct update_data</tt> object
1037
 *
1038
 * \return \c true to continue traversing the tree, or \c false to stop (because
1039
 *         \p xml was updated by \p user_data->update)
1040
 *
1041
 * \note This is compatible with \c pcmk__xml_tree_foreach().
1042
 */
1043
static bool
1044
update_xe_if_matching(xmlNode *xml, void *user_data)
1045
0
{
1046
0
    struct update_data *data = user_data;
1047
0
    xmlNode *update = data->update;
1048
1049
0
    if (!pcmk__xe_is(update, (const char *) xml->name)) {
1050
        // No match: either not both elements, or different element types
1051
0
        return true;
1052
0
    }
1053
1054
0
    if (!pcmk__str_eq(pcmk__xe_id(xml), pcmk__xe_id(update), pcmk__str_none)) {
1055
        // No match: ID mismatch
1056
0
        return true;
1057
0
    }
1058
1059
0
    pcmk__log_xml_trace(xml, "update-match");
1060
0
    pcmk__log_xml_trace(update, "update-with");
1061
0
    update_xe(NULL, xml, update, data->flags);
1062
1063
    // Found a match and replaced it; stop traversing tree
1064
0
    return false;
1065
0
}
1066
1067
/*!
1068
 * \internal
1069
 * \brief Search an XML tree depth-first and update the first matching element
1070
 *
1071
 * "Update" means to merge a source subtree into a target subtree (see
1072
 * \c update_xe()).
1073
 *
1074
 * A match with a node \c node is defined as follows:
1075
 * * \c node and \p update are both element nodes of the same type.
1076
 * * \c node and \p update have the same \c PCMK_XA_ID attribute value, or
1077
 *   \c PCMK_XA_ID is unset in both
1078
 *
1079
 * \param[in,out] xml     XML tree to search
1080
 * \param[in]     update  XML to update a matching element with
1081
 * \param[in]     flags   Group of <tt>enum pcmk__xa_flags</tt>
1082
 *
1083
 * \return Standard Pacemaker return code (specifically, \c pcmk_rc_ok on
1084
 *         successful update and an error code otherwise)
1085
 */
1086
int
1087
pcmk__xe_update_match(xmlNode *xml, xmlNode *update, uint32_t flags)
1088
0
{
1089
    /* @COMPAT In pcmk__xe_delete_match() and pcmk__xe_replace_match(), we
1090
     * compare IDs only if the equivalent of the update argument has an ID.
1091
     * Here, we're stricter: we consider it a mismatch if only one element has
1092
     * an ID attribute, or if both elements have IDs but they don't match.
1093
     *
1094
     * Perhaps we should align the behavior at a major version release.
1095
     */
1096
0
    struct update_data data = {
1097
0
        .update = update,
1098
0
        .flags = flags,
1099
0
    };
1100
1101
0
    CRM_CHECK((xml != NULL) && (update != NULL), return EINVAL);
1102
1103
0
    if (!pcmk__xml_tree_foreach(xml, update_xe_if_matching, &data)) {
1104
        // Found and updated an element
1105
0
        return pcmk_rc_ok;
1106
0
    }
1107
1108
    // No match found in this subtree
1109
0
    return ENXIO;
1110
0
}
1111
1112
void
1113
pcmk__xe_set_propv(xmlNodePtr node, va_list pairs)
1114
0
{
1115
0
    while (true) {
1116
0
        const char *name, *value;
1117
1118
0
        name = va_arg(pairs, const char *);
1119
0
        if (name == NULL) {
1120
0
            return;
1121
0
        }
1122
1123
0
        value = va_arg(pairs, const char *);
1124
0
        pcmk__xe_set(node, name, value);
1125
0
    }
1126
0
}
1127
1128
void
1129
pcmk__xe_set_props(xmlNodePtr node, ...)
1130
0
{
1131
0
    va_list pairs;
1132
0
    va_start(pairs, node);
1133
0
    pcmk__xe_set_propv(node, pairs);
1134
0
    va_end(pairs);
1135
0
}
1136
1137
int
1138
pcmk__xe_foreach_child(xmlNode *xml, const char *child_element_name,
1139
                       int (*handler)(xmlNode *xml, void *userdata),
1140
                       void *userdata)
1141
0
{
1142
0
    xmlNode *children = (xml? xml->children : NULL);
1143
1144
0
    pcmk__assert(handler != NULL);
1145
1146
0
    for (xmlNode *node = children; node != NULL; node = node->next) {
1147
0
        if ((node->type == XML_ELEMENT_NODE)
1148
0
            && ((child_element_name == NULL)
1149
0
                || pcmk__xe_is(node, child_element_name))) {
1150
0
            int rc = handler(node, userdata);
1151
1152
0
            if (rc != pcmk_rc_ok) {
1153
0
                return rc;
1154
0
            }
1155
0
        }
1156
0
    }
1157
1158
0
    return pcmk_rc_ok;
1159
0
}
1160
1161
// XML attribute handling
1162
1163
/*!
1164
 * \internal
1165
 * \brief Retrieve the value of an XML attribute
1166
 *
1167
 * \param[in] xml        XML element whose attribute to get
1168
 * \param[in] attr_name  Attribute name
1169
 *
1170
 * \return Value of specified attribute (may be \c NULL)
1171
 */
1172
const char *
1173
pcmk__xe_get(const xmlNode *xml, const char *attr_name)
1174
0
{
1175
0
    xmlAttr *attr = NULL;
1176
1177
0
    CRM_CHECK((xml != NULL) && (attr_name != NULL), return NULL);
1178
1179
0
    attr = xmlHasProp(xml, (const xmlChar *) attr_name);
1180
0
    if ((attr == NULL) || (attr->children == NULL)) {
1181
0
        return NULL;
1182
0
    }
1183
1184
0
    return (const char *) attr->children->content;
1185
0
}
1186
1187
/*!
1188
 * \internal
1189
 * \brief Check whether a new attribute value would be a change
1190
 *
1191
 * \param[in] xml    XML element whose attributes to check
1192
 * \param[in] name   Attribute name
1193
 * \param[in] value  New value (must not be \c NULL)
1194
 *
1195
 * \return \c true if setting attribute \p name to \p value would change
1196
 *         or newly set its value in \p xml, or \c false otherwise
1197
 */
1198
static bool
1199
attr_changes(const xmlNode *xml, const char *name, const char *value)
1200
0
{
1201
0
    xmlAttr *attr = xmlHasProp(xml, (const xmlChar *) name);
1202
0
    xml_node_private_t *nodepriv = NULL;
1203
1204
0
    if (attr == NULL) {
1205
        // Attribute was previously unset
1206
0
        return true;
1207
0
    }
1208
1209
0
    nodepriv = (xml_node_private_t *) attr->_private;
1210
0
    if (pcmk__is_set(nodepriv->flags, pcmk__xf_deleted)) {
1211
        // Treat the attribute as unset if marked as deleted
1212
0
        return true;
1213
0
    }
1214
1215
    // Attribute is set, so check whether its value would change
1216
0
    return (attr->children == NULL)
1217
0
           || !pcmk__str_eq((const char *) attr->children->content, value,
1218
0
                            pcmk__str_none);
1219
0
}
1220
1221
/*!
1222
 * \internal
1223
 * \brief Set an XML attribute value
1224
 *
1225
 * This also performs change tracking if enabled and checks ACLs if enabled.
1226
 * Upon ACL denial, the attribute is not updated.
1227
 *
1228
 * \param[in,out] xml        XML element whose attribute to set
1229
 * \param[in]     attr_name  Attribute name
1230
 * \param[in]     value      Attribute value to set
1231
 *
1232
 * \return Standard Pacemaker return code
1233
 *
1234
 * \note This does nothing and returns \c pcmk_rc_ok if \p value is \c NULL.
1235
 */
1236
int
1237
pcmk__xe_set(xmlNode *xml, const char *attr_name, const char *value)
1238
0
{
1239
0
    bool dirty = false;
1240
0
    xmlAttr *attr = NULL;
1241
1242
0
    CRM_CHECK((xml != NULL) && (attr_name != NULL), return EINVAL);
1243
1244
0
    if (value == NULL) {
1245
0
        return pcmk_rc_ok;
1246
0
    }
1247
1248
    /* @TODO Can we return early if dirty is false? Or does anything rely on
1249
     * "cleanly" resetting the value? If so, try to preserve existing behavior
1250
     * for public API functions that depend on this.
1251
     */
1252
0
    dirty = pcmk__xml_doc_all_flags_set(xml->doc, pcmk__xf_tracking)
1253
0
            && attr_changes(xml, attr_name, value);
1254
1255
0
    if (dirty && !pcmk__check_acl(xml, attr_name, pcmk__xf_acl_create)) {
1256
0
        pcmk__trace("Cannot add %s=%s to %s", attr_name, value, xml->name);
1257
0
        return EACCES;
1258
0
    }
1259
1260
0
    attr = xmlSetProp(xml, (const xmlChar *) attr_name,
1261
0
                      (const xmlChar *) value);
1262
1263
    // These should never be NULL -- out of memory?
1264
0
    CRM_CHECK((attr != NULL) && (attr->children != NULL)
1265
0
              && (attr->children->content != NULL),
1266
0
              xmlRemoveProp(attr); return ENXIO);
1267
1268
    /* If the attribute already exists, this does nothing. Attribute values
1269
     * don't get private data.
1270
     */
1271
0
    pcmk__xml_new_private_data((xmlNode *) attr);
1272
1273
0
    if (dirty) {
1274
        // This also clears the pcmk__xf_deleted flag
1275
0
        pcmk__mark_xml_attr_dirty(attr);
1276
0
    }
1277
1278
0
    return pcmk_rc_ok;
1279
0
}
1280
1281
/*!
1282
 * \internal
1283
 * \brief Retrieve a flag group from an XML attribute value
1284
 *
1285
 * This is like \c pcmk__xe_get() but returns the value as a \c uint32_t.
1286
 *
1287
 * \param[in]  xml            XML node to check
1288
 * \param[in]  name           Attribute name to check (must not be NULL)
1289
 * \param[out] dest           Where to store flags (may be NULL to just
1290
 *                            validate type)
1291
 * \param[in]  default_value  What to use for missing or invalid value
1292
 *
1293
 * \return Standard Pacemaker return code
1294
 */
1295
int
1296
pcmk__xe_get_flags(const xmlNode *xml, const char *name, uint32_t *dest,
1297
                   uint32_t default_value)
1298
0
{
1299
0
    const char *value = NULL;
1300
0
    long long value_ll = 0LL;
1301
0
    int rc = pcmk_rc_ok;
1302
1303
0
    if (dest != NULL) {
1304
0
        *dest = default_value;
1305
0
    }
1306
1307
0
    if (name == NULL) {
1308
0
        return EINVAL;
1309
0
    }
1310
0
    if (xml == NULL) {
1311
0
        return pcmk_rc_ok;
1312
0
    }
1313
0
    value = pcmk__xe_get(xml, name);
1314
0
    if (value == NULL) {
1315
0
        return pcmk_rc_ok;
1316
0
    }
1317
1318
0
    rc = pcmk__scan_ll(value, &value_ll, default_value);
1319
0
    if ((value_ll < 0) || (value_ll > UINT32_MAX)) {
1320
0
        value_ll = default_value;
1321
0
        if (rc == pcmk_rc_ok) {
1322
0
            rc = pcmk_rc_bad_input;
1323
0
        }
1324
0
    }
1325
1326
0
    if (dest != NULL) {
1327
0
        *dest = (uint32_t) value_ll;
1328
0
    }
1329
0
    return rc;
1330
0
}
1331
1332
/*!
1333
 * \internal
1334
 * \brief Retrieve an <tt>unsigned int</tt> value from an XML attribute
1335
 *
1336
 * This is like \c pcmk__xe_get() but returns the value as an
1337
 * <tt>unsigned int</tt>.
1338
 *
1339
 * \param[in]  xml   XML element whose attribute to get
1340
 * \param[in]  attr  Attribute name
1341
 * \param[out] dest  Where to store attribute value (unchanged on error)
1342
 *
1343
 * \return Standard Pacemaker return code
1344
 */
1345
int
1346
pcmk__xe_get_uint(const xmlNode *xml, const char *attr, unsigned int *dest)
1347
0
{
1348
0
    long long value_ll = 0;
1349
0
    int rc = pcmk_rc_ok;
1350
1351
0
    CRM_CHECK((xml != NULL) && (attr != NULL) && (dest != NULL), return EINVAL);
1352
1353
0
    rc = pcmk__xe_get_ll(xml, attr, &value_ll);
1354
0
    if (rc != pcmk_rc_ok) {
1355
0
        return rc;
1356
0
    }
1357
1358
0
    if ((value_ll < 0) || (value_ll > UINT_MAX)) {
1359
0
        return ERANGE;
1360
0
    }
1361
0
    *dest = (unsigned int) value_ll;
1362
0
    return pcmk_rc_ok;
1363
0
}
1364
1365
/*!
1366
 * \internal
1367
 * \brief Set an XML attribute using an <tt>unsigned int</tt> value
1368
 *
1369
 * This is like \c pcmk__xe_set() but takes an <tt>unsigned int</tt>.
1370
 *
1371
 * \param[in,out] xml    XML node to modify
1372
 * \param[in]     attr   Attribute name
1373
 * \param[in]     value  Attribute value to set
1374
 */
1375
void
1376
pcmk__xe_set_uint(xmlNode *xml, const char *attr, unsigned int value)
1377
0
{
1378
0
    char *value_s = NULL;
1379
1380
0
    CRM_CHECK((xml != NULL) && (attr != NULL), return);
1381
1382
0
    value_s = pcmk__assert_asprintf("%u", value);
1383
0
    pcmk__xe_set(xml, attr, value_s);
1384
0
    free(value_s);
1385
0
}
1386
1387
/*!
1388
 * \internal
1389
 * \brief Retrieve an \c int value from an XML attribute
1390
 *
1391
 * This is like \c pcmk__xe_get() but returns the value as an \c int.
1392
 *
1393
 * \param[in]  xml   XML element whose attribute to get
1394
 * \param[in]  attr  Attribute name
1395
 * \param[out] dest  Where to store element value (unchanged on error)
1396
 *
1397
 * \return Standard Pacemaker return code
1398
 */
1399
int
1400
pcmk__xe_get_int(const xmlNode *xml, const char *attr, int *dest)
1401
0
{
1402
0
    long long value_ll = 0;
1403
0
    int rc = pcmk_rc_ok;
1404
1405
0
    CRM_CHECK((xml != NULL) && (attr != NULL) && (dest != NULL), return EINVAL);
1406
1407
0
    rc = pcmk__xe_get_ll(xml, attr, &value_ll);
1408
0
    if (rc != pcmk_rc_ok) {
1409
0
        return rc;
1410
0
    }
1411
1412
0
    if ((value_ll < INT_MIN) || (value_ll > INT_MAX)) {
1413
0
        return ERANGE;
1414
0
    }
1415
1416
0
    *dest = (int) value_ll;
1417
0
    return pcmk_rc_ok;
1418
0
}
1419
1420
/*!
1421
 * \internal
1422
 * \brief Set an XML attribute using an \c int value.
1423
 *
1424
 * This is like \c pcmk__xe_set() but takes an \c int.
1425
 *
1426
 * \param[in,out] xml    XML node to modify
1427
 * \param[in]     attr   Attribute name
1428
 * \param[in]     value  Attribute value to set
1429
 */
1430
void
1431
pcmk__xe_set_int(xmlNode *xml, const char *attr, int value)
1432
0
{
1433
0
    char *value_s = NULL;
1434
1435
0
    CRM_CHECK((xml != NULL) && (attr != NULL), return);
1436
1437
0
    value_s = pcmk__itoa(value);
1438
0
    pcmk__xe_set(xml, attr, value_s);
1439
0
    free(value_s);
1440
0
}
1441
1442
/*!
1443
 * \internal
1444
 * \brief Retrieve a <tt>long long</tt> value from an XML attribute
1445
 *
1446
 * This is like \c pcmk__xe_get() but returns the value as a <tt>long long</tt>.
1447
 *
1448
 * \param[in]  xml   XML element whose attribute to get
1449
 * \param[in]  attr  Attribute name
1450
 * \param[out] dest  Where to store element value (unchanged on error)
1451
 *
1452
 * \return Standard Pacemaker return code
1453
 */
1454
int
1455
pcmk__xe_get_ll(const xmlNode *xml, const char *attr, long long *dest)
1456
0
{
1457
0
    const char *value = NULL;
1458
0
    long long value_ll = 0;
1459
0
    int rc = pcmk_rc_ok;
1460
1461
0
    CRM_CHECK((xml != NULL) && (attr != NULL) && (dest != NULL), return EINVAL);
1462
1463
0
    value = pcmk__xe_get(xml, attr);
1464
0
    if (value == NULL) {
1465
0
        return ENXIO;
1466
0
    }
1467
1468
0
    rc = pcmk__scan_ll(value, &value_ll, PCMK__PARSE_INT_DEFAULT);
1469
0
    if (rc != pcmk_rc_ok) {
1470
0
        return rc;
1471
0
    }
1472
1473
0
    *dest = value_ll;
1474
0
    return pcmk_rc_ok;
1475
0
}
1476
1477
/*!
1478
 * \internal
1479
 * \brief Set an XML attribute using a <tt>long long</tt> value
1480
 *
1481
 * This is like \c pcmk__xe_set() but takes a <tt>long long</tt>.
1482
 *
1483
 * \param[in,out] xml    XML node to modify
1484
 * \param[in]     attr   Attribute name
1485
 * \param[in]     value  Attribute value to set
1486
 *
1487
 * \return Standard Pacemaker return code
1488
 */
1489
int
1490
pcmk__xe_set_ll(xmlNode *xml, const char *attr, long long value)
1491
0
{
1492
0
    char *value_s = NULL;
1493
0
    int rc = pcmk_rc_ok;
1494
1495
0
    CRM_CHECK((xml != NULL) && (attr != NULL), return EINVAL);
1496
1497
0
    value_s = pcmk__assert_asprintf("%lld", value);
1498
1499
0
    rc = pcmk__xe_set(xml, attr, value_s);
1500
0
    free(value_s);
1501
0
    return rc;
1502
0
}
1503
1504
/*!
1505
 * \internal
1506
 * \brief Retrieve a \c time_t value from an XML attribute
1507
 *
1508
 * This is like \c pcmk__xe_get() but returns the value as a \c time_t.
1509
 *
1510
 * \param[in]  xml   XML element whose attribute to get
1511
 * \param[in]  attr  Attribute name
1512
 * \param[out] dest  Where to store attribute value (unchanged on error)
1513
 *
1514
 * \return Standard Pacemaker return code
1515
 */
1516
int
1517
pcmk__xe_get_time(const xmlNode *xml, const char *attr, time_t *dest)
1518
0
{
1519
0
    long long value_ll = 0;
1520
0
    int rc = pcmk_rc_ok;
1521
1522
0
    CRM_CHECK((xml != NULL) && (attr != NULL) && (dest != NULL), return EINVAL);
1523
1524
0
    rc = pcmk__xe_get_ll(xml, attr, &value_ll);
1525
0
    if (rc != pcmk_rc_ok) {
1526
0
        return rc;
1527
0
    }
1528
1529
    /* We don't do any bounds checking, since there are no constants provided
1530
     * for the bounds of time_t, and calculating them isn't worth the effort. If
1531
     * there are XML values beyond the native sizes, there will likely be worse
1532
     * problems anyway.
1533
     */
1534
0
    *dest = (time_t) value_ll;
1535
0
    return pcmk_rc_ok;
1536
0
}
1537
1538
/*!
1539
 * \internal
1540
 * \brief Set an XML attribute using a \c time_t value
1541
 *
1542
 * This is like \c pcmk__xe_set() but takes a \c time_t.
1543
 *
1544
 * \param[in,out] xml    XML element whose attribute to set
1545
 * \param[in]     attr   Attribute name
1546
 * \param[in]     value  Attribute value to set (in seconds)
1547
 */
1548
void
1549
pcmk__xe_set_time(xmlNode *xml, const char *attr, time_t value)
1550
0
{
1551
    // Could be inline, but keep it underneath pcmk__xe_get_time()
1552
0
    CRM_CHECK((xml != NULL) && (attr != NULL), return);
1553
1554
0
    pcmk__xe_set_ll(xml, attr, (long long) value);
1555
0
}
1556
1557
/*!
1558
 * \internal
1559
 * \brief Retrieve the values of XML second/microsecond attributes as time
1560
 *
1561
 * This is like \c pcmk__xe_get() but returns the value as a
1562
 * <tt>struct timeval</tt>.
1563
 *
1564
 * \param[in]  xml        XML element whose attributes to get
1565
 * \param[in]  sec_attr   Name of XML attribute for seconds
1566
 * \param[in]  usec_attr  Name of XML attribute for microseconds
1567
 * \param[out] dest       Where to store result (unchanged on error)
1568
 *
1569
 * \return Standard Pacemaker return code
1570
 */
1571
int
1572
pcmk__xe_get_timeval(const xmlNode *xml, const char *sec_attr,
1573
                     const char *usec_attr, struct timeval *dest)
1574
0
{
1575
0
    long long value_ll = 0;
1576
0
    struct timeval result = { 0, 0 };
1577
0
    int rc = pcmk_rc_ok;
1578
1579
    // Could allow one of sec_attr and usec_attr to be NULL in the future
1580
0
    CRM_CHECK((xml != NULL) && (sec_attr != NULL) && (usec_attr != NULL)
1581
0
              && (dest != NULL), return EINVAL);
1582
1583
    // No bounds checking; see comment in pcmk__xe_get_time()
1584
1585
    // Parse seconds
1586
0
    rc = pcmk__xe_get_time(xml, sec_attr, &(result.tv_sec));
1587
0
    if (rc != pcmk_rc_ok) {
1588
0
        return rc;
1589
0
    }
1590
1591
    // Parse microseconds
1592
0
    rc = pcmk__xe_get_ll(xml, usec_attr, &value_ll);
1593
0
    if (rc != pcmk_rc_ok) {
1594
0
        return rc;
1595
0
    }
1596
0
    result.tv_usec = (suseconds_t) value_ll;
1597
1598
0
    *dest = result;
1599
0
    return pcmk_rc_ok;
1600
0
}
1601
1602
/*!
1603
 * \internal
1604
 * \brief Set XML attribute values for seconds and microseconds
1605
 *
1606
 * This is like \c pcmk__xe_set() but takes a <tt>struct timeval *</tt>.
1607
 *
1608
 * \param[in,out] xml        XML element whose attributes to set
1609
 * \param[in]     sec_attr   Name of XML attribute for seconds
1610
 * \param[in]     usec_attr  Name of XML attribute for microseconds
1611
 * \param[in]     value      Attribute values to set
1612
 *
1613
 * \note This does nothing if \p value is \c NULL.
1614
 */
1615
void
1616
pcmk__xe_set_timeval(xmlNode *xml, const char *sec_attr, const char *usec_attr,
1617
                     const struct timeval *value)
1618
0
{
1619
0
    CRM_CHECK((xml != NULL) && (sec_attr != NULL) && (usec_attr != NULL),
1620
0
              return);
1621
1622
0
    if (value == NULL) {
1623
0
        return;
1624
0
    }
1625
0
    if (pcmk__xe_set_ll(xml, sec_attr,
1626
0
                        (long long) value->tv_sec) != pcmk_rc_ok) {
1627
0
        return;
1628
0
    }
1629
1630
    /* Seconds were added successfully. Ignore any errors adding microseconds.
1631
     *
1632
     * It would be nice to make this atomic: revert the seconds attribute if
1633
     * adding the microseconds attribute fails. That's somewhat complicated due
1634
     * to change tracking: the chain of parents is already marked dirty, etc. In
1635
     * practice, microseconds should succeed if seconds succeeded, unless it's
1636
     * due to memory allocation failure. Nothing checks the return values of
1637
     * these setter functions at time of writing, anyway.
1638
     */
1639
0
    pcmk__xe_set_ll(xml, usec_attr, (long long) value->tv_usec);
1640
0
}
1641
1642
/*!
1643
 * \internal
1644
 * \brief Get a date/time object from an XML attribute value
1645
 *
1646
 * \param[in]  xml   XML with attribute to parse (from CIB)
1647
 * \param[in]  attr  Name of attribute to parse
1648
 * \param[out] t     Where to create date/time object
1649
 *                   (\p *t must be NULL initially)
1650
 *
1651
 * \return Standard Pacemaker return code
1652
 * \note The caller is responsible for freeing \p *t using crm_time_free().
1653
 */
1654
int
1655
pcmk__xe_get_datetime(const xmlNode *xml, const char *attr, crm_time_t **t)
1656
0
{
1657
0
    const char *value = NULL;
1658
1659
0
    if ((t == NULL) || (*t != NULL) || (xml == NULL) || (attr == NULL)) {
1660
0
        return EINVAL;
1661
0
    }
1662
1663
0
    value = pcmk__xe_get(xml, attr);
1664
0
    if (value != NULL) {
1665
0
        *t = crm_time_new(value);
1666
0
        if (*t == NULL) {
1667
0
            return pcmk_rc_unpack_error;
1668
0
        }
1669
0
    }
1670
0
    return pcmk_rc_ok;
1671
0
}
1672
1673
/*!
1674
 * \internal
1675
 * \brief Retrieve a boolean value from an XML attribute
1676
 *
1677
 * This is like \c pcmk__xe_get() but returns the value as a \c bool.
1678
 *
1679
 * \param[in]  xml   XML element whose attribute to get
1680
 * \param[in]  attr  Attribute name
1681
 * \param[out] dest  Where to store result (unchanged on error)
1682
 *
1683
 * \return Standard Pacemaker return code
1684
 */
1685
int
1686
pcmk__xe_get_bool(const xmlNode *xml, const char *attr, bool *dest)
1687
0
{
1688
0
    const char *xml_value = NULL;
1689
1690
0
    CRM_CHECK((xml != NULL) && (attr != NULL) && (dest != NULL), return EINVAL);
1691
1692
0
    xml_value = pcmk__xe_get(xml, attr);
1693
0
    if (xml_value == NULL) {
1694
0
        return ENXIO;
1695
0
    }
1696
1697
0
    return pcmk__parse_bool(xml_value, dest);
1698
0
}
1699
1700
/*!
1701
 * \internal
1702
 * \brief Set an XML attribute using a boolean value
1703
 *
1704
 * This is like \c pcmk__xe_set() but takes a \c bool.
1705
 *
1706
 * \param[in,out] xml    XML element whose attribute to set
1707
 * \param[in]     attr   Attribute name
1708
 * \param[in]     value  Attribute value to set
1709
 *
1710
 * \return Standard Pacemaker return code
1711
 */
1712
void
1713
pcmk__xe_set_bool(xmlNode *xml, const char *attr, bool value)
1714
0
{
1715
0
    pcmk__xe_set(xml, attr, pcmk__btoa(value));
1716
0
}
1717
1718
/*!
1719
 * \internal
1720
 * \brief Extract a boolean attribute's value from an XML element
1721
 *
1722
 * \param[in] node XML node to get attribute from
1723
 * \param[in] name XML attribute to get
1724
 *
1725
 * \return \c true if the given \p name is an attribute on \p node whose value
1726
 *         parses to \c true (see \c pcmk__parse_bool()), or \c false otherwise
1727
 */
1728
bool
1729
pcmk__xe_attr_is_true(const xmlNode *node, const char *name)
1730
0
{
1731
0
    bool value = false;
1732
1733
    // value remains false on error, so don't check return value
1734
0
    (void) pcmk__xe_get_bool(node, name, &value);
1735
0
    return value;
1736
0
}
1737
1738
// Deprecated functions kept only for backward API compatibility
1739
// LCOV_EXCL_START
1740
1741
#include <glib.h>                       // gboolean, GSList
1742
1743
#include <crm/common/nvpair_compat.h>   // pcmk_xml_attrs2nvpairs(), etc.
1744
#include <crm/common/xml_compat.h>      // crm_xml_sanitize_id()
1745
#include <crm/common/xml_element_compat.h>
1746
1747
xmlNode *
1748
expand_idref(xmlNode *input, xmlNode *top)
1749
0
{
1750
0
    return pcmk__xe_resolve_idref(input,
1751
0
                                  ((top != NULL)? top->doc : input->doc));
1752
0
}
1753
1754
const char *
1755
crm_xml_add(xmlNode *node, const char *name, const char *value)
1756
0
{
1757
0
    bool dirty = FALSE;
1758
0
    xmlAttr *attr = NULL;
1759
1760
0
    CRM_CHECK(node != NULL, return NULL);
1761
0
    CRM_CHECK(name != NULL, return NULL);
1762
1763
0
    if (value == NULL) {
1764
0
        return NULL;
1765
0
    }
1766
1767
0
    if (pcmk__xml_doc_all_flags_set(node->doc, pcmk__xf_tracking)) {
1768
0
        const char *old = pcmk__xe_get(node, name);
1769
1770
0
        if (old == NULL || value == NULL || strcmp(old, value) != 0) {
1771
0
            dirty = TRUE;
1772
0
        }
1773
0
    }
1774
1775
0
    if (dirty && (pcmk__check_acl(node, name, pcmk__xf_acl_create) == FALSE)) {
1776
0
        pcmk__trace("Cannot add %s=%s to %s", name, value, node->name);
1777
0
        return NULL;
1778
0
    }
1779
1780
0
    attr = xmlSetProp(node, (const xmlChar *) name, (const xmlChar *) value);
1781
1782
    /* If the attribute already exists, this does nothing. Attribute values
1783
     * don't get private data.
1784
     */
1785
0
    pcmk__xml_new_private_data((xmlNode *) attr);
1786
1787
0
    if (dirty) {
1788
0
        pcmk__mark_xml_attr_dirty(attr);
1789
0
    }
1790
1791
0
    CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
1792
0
    return (char *)attr->children->content;
1793
0
}
1794
1795
void
1796
crm_xml_set_id(xmlNode *xml, const char *format, ...)
1797
0
{
1798
0
    va_list ap;
1799
0
    int len = 0;
1800
0
    char *id = NULL;
1801
1802
    // Equivalent to pcmk__assert_asprintf()
1803
0
    va_start(ap, format);
1804
0
    len = vasprintf(&id, format, ap);
1805
0
    va_end(ap);
1806
0
    pcmk__assert(len > 0);
1807
1808
0
    crm_xml_sanitize_id(id);
1809
0
    crm_xml_add(xml, PCMK_XA_ID, id);
1810
0
    free(id);
1811
0
}
1812
1813
xmlNode *
1814
sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
1815
0
{
1816
0
    xmlNode *child = NULL;
1817
0
    GSList *nvpairs = NULL;
1818
0
    xmlNode *result = NULL;
1819
1820
0
    CRM_CHECK(input != NULL, return NULL);
1821
1822
0
    result = pcmk__xe_create(parent, (const char *) input->name);
1823
0
    nvpairs = pcmk_xml_attrs2nvpairs(input);
1824
0
    nvpairs = pcmk_sort_nvpairs(nvpairs);
1825
0
    pcmk_nvpairs2xml_attrs(nvpairs, result);
1826
0
    pcmk_free_nvpairs(nvpairs);
1827
1828
0
    for (child = pcmk__xe_first_child(input, NULL, NULL, NULL); child != NULL;
1829
0
         child = pcmk__xe_next(child, NULL)) {
1830
1831
0
        if (recursive) {
1832
0
            sorted_xml(child, result, recursive);
1833
0
        } else {
1834
0
            pcmk__xml_copy(result, child);
1835
0
        }
1836
0
    }
1837
1838
0
    return result;
1839
0
}
1840
1841
const char *
1842
crm_element_value(const xmlNode *data, const char *name)
1843
0
{
1844
0
    xmlAttr *attr = NULL;
1845
1846
0
    if (data == NULL) {
1847
0
        pcmk__err("Couldn't find %s in NULL", pcmk__s(name, "<null>"));
1848
0
        CRM_LOG_ASSERT(data != NULL);
1849
0
        return NULL;
1850
1851
0
    } else if (name == NULL) {
1852
0
        pcmk__err("Couldn't find NULL in %s", data->name);
1853
0
        return NULL;
1854
0
    }
1855
1856
0
    attr = xmlHasProp(data, (const xmlChar *) name);
1857
0
    if (!attr || !attr->children) {
1858
0
        return NULL;
1859
0
    }
1860
0
    return (const char *) attr->children->content;
1861
0
}
1862
1863
const char *
1864
crm_copy_xml_element(const xmlNode *obj1, xmlNode *obj2, const char *element)
1865
0
{
1866
0
    const char *value = pcmk__xe_get(obj1, element);
1867
1868
0
    crm_xml_add(obj2, element, value);
1869
0
    return value;
1870
0
}
1871
1872
int
1873
crm_element_value_ll(const xmlNode *data, const char *name, long long *dest)
1874
0
{
1875
0
    const char *value = NULL;
1876
1877
0
    CRM_CHECK(dest != NULL, return -1);
1878
0
    value = pcmk__xe_get(data, name);
1879
0
    if (value != NULL) {
1880
0
        int rc = pcmk__scan_ll(value, dest, PCMK__PARSE_INT_DEFAULT);
1881
1882
0
        if (rc == pcmk_rc_ok) {
1883
0
            return 0;
1884
0
        }
1885
0
        pcmk__warn("Using default for %s because '%s' is not a valid integer: "
1886
0
                   "%s",
1887
0
                   name, value, pcmk_rc_str(rc));
1888
0
    }
1889
0
    return -1;
1890
0
}
1891
1892
int
1893
crm_element_value_timeval(const xmlNode *xml, const char *name_sec,
1894
                          const char *name_usec, struct timeval *dest)
1895
0
{
1896
0
    long long value_i = 0;
1897
1898
0
    CRM_CHECK(dest != NULL, return -EINVAL);
1899
0
    dest->tv_sec = 0;
1900
0
    dest->tv_usec = 0;
1901
1902
0
    if (xml == NULL) {
1903
0
        return pcmk_ok;
1904
0
    }
1905
1906
    // No bounds checking; see comment in pcmk__xe_get_time()
1907
1908
    // Parse seconds
1909
0
    errno = 0;
1910
0
    if (crm_element_value_ll(xml, name_sec, &value_i) < 0) {
1911
0
        return -errno;
1912
0
    }
1913
0
    dest->tv_sec = (time_t) value_i;
1914
1915
    // Parse microseconds
1916
0
    if (crm_element_value_ll(xml, name_usec, &value_i) < 0) {
1917
0
        return -errno;
1918
0
    }
1919
0
    dest->tv_usec = (suseconds_t) value_i;
1920
1921
0
    return pcmk_ok;
1922
0
}
1923
1924
int
1925
crm_element_value_epoch(const xmlNode *xml, const char *name, time_t *dest)
1926
0
{
1927
0
    long long value_ll = 0;
1928
1929
0
    if (crm_element_value_ll(xml, name, &value_ll) < 0) {
1930
0
        return -1;
1931
0
    }
1932
1933
    // No bounds checking; see comment in pcmk__xe_get_time()
1934
0
    *dest = (time_t) value_ll;
1935
0
    return pcmk_ok;
1936
0
}
1937
1938
int
1939
crm_element_value_ms(const xmlNode *data, const char *name, unsigned int *dest)
1940
0
{
1941
0
    const char *value = NULL;
1942
0
    long long value_ll;
1943
0
    int rc = pcmk_rc_ok;
1944
1945
0
    CRM_CHECK(dest != NULL, return -1);
1946
0
    *dest = 0;
1947
0
    value = pcmk__xe_get(data, name);
1948
0
    rc = pcmk__scan_ll(value, &value_ll, 0LL);
1949
0
    if (rc != pcmk_rc_ok) {
1950
0
        pcmk__warn("Using default for %s because '%s' is not valid "
1951
0
                   "milliseconds: %s",
1952
0
                   name, value, pcmk_rc_str(rc));
1953
0
        return -1;
1954
0
    }
1955
0
    if ((value_ll < 0) || (value_ll > UINT_MAX)) {
1956
0
        pcmk__warn("Using default for %s because '%s' is out of range", name,
1957
0
                   value);
1958
0
        return -1;
1959
0
    }
1960
0
    *dest = (unsigned int) value_ll;
1961
0
    return pcmk_ok;
1962
0
}
1963
1964
int
1965
crm_element_value_int(const xmlNode *data, const char *name, int *dest)
1966
0
{
1967
0
    const char *value = NULL;
1968
1969
0
    CRM_CHECK(dest != NULL, return -1);
1970
0
    value = pcmk__xe_get(data, name);
1971
0
    if (value) {
1972
0
        long long value_ll;
1973
0
        int rc = pcmk__scan_ll(value, &value_ll, 0LL);
1974
1975
0
        *dest = PCMK__PARSE_INT_DEFAULT;
1976
0
        if (rc != pcmk_rc_ok) {
1977
0
            pcmk__warn("Using default for %s because '%s' is not a valid "
1978
0
                       "integer: %s",
1979
0
                       name, value, pcmk_rc_str(rc));
1980
0
        } else if ((value_ll < INT_MIN) || (value_ll > INT_MAX)) {
1981
0
            pcmk__warn("Using default for %s because '%s' is out of range",
1982
0
                       name, value);
1983
0
        } else {
1984
0
            *dest = (int) value_ll;
1985
0
            return 0;
1986
0
        }
1987
0
    }
1988
0
    return -1;
1989
0
}
1990
1991
char *
1992
crm_element_value_copy(const xmlNode *data, const char *name)
1993
0
{
1994
0
    CRM_CHECK((data != NULL) && (name != NULL), return NULL);
1995
0
    return pcmk__str_copy(pcmk__xe_get(data, name));
1996
0
}
1997
1998
const char *
1999
crm_xml_add_ll(xmlNode *xml, const char *name, long long value)
2000
0
{
2001
0
    char *str = pcmk__assert_asprintf("%lld", value);
2002
0
    const char *result = crm_xml_add(xml, name, str);
2003
2004
0
    free(str);
2005
0
    return result;
2006
0
}
2007
2008
const char *
2009
crm_xml_add_timeval(xmlNode *xml, const char *name_sec, const char *name_usec,
2010
                    const struct timeval *value)
2011
0
{
2012
0
    const char *added = NULL;
2013
2014
0
    if (xml && name_sec && value) {
2015
0
        added = crm_xml_add_ll(xml, name_sec, (long long) value->tv_sec);
2016
0
        if (added && name_usec) {
2017
            // Any error is ignored (we successfully added seconds)
2018
0
            crm_xml_add_ll(xml, name_usec, (long long) value->tv_usec);
2019
0
        }
2020
0
    }
2021
0
    return added;
2022
0
}
2023
2024
const char *
2025
crm_xml_add_ms(xmlNode *node, const char *name, unsigned int ms)
2026
0
{
2027
0
    char *number = pcmk__assert_asprintf("%u", ms);
2028
0
    const char *added = crm_xml_add(node, name, number);
2029
2030
0
    free(number);
2031
0
    return added;
2032
0
}
2033
2034
const char *
2035
crm_xml_add_int(xmlNode *node, const char *name, int value)
2036
0
{
2037
0
    char *number = pcmk__itoa(value);
2038
0
    const char *added = crm_xml_add(node, name, number);
2039
2040
0
    free(number);
2041
0
    return added;
2042
0
}
2043
2044
// LCOV_EXCL_STOP
2045
// End deprecated API