Coverage Report

Created: 2026-09-29 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pacemaker/lib/common/xml_idref.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 <stdio.h>              // NULL
13
#include <stdlib.h>             // free()
14
#include <glib.h>               // GList, GHashTable, etc.
15
#include <libxml/tree.h>        // xmlNode
16
17
#include <crm/crm.h>
18
#include <crm/common/xml.h>     // PCMK_XA_ID_REF
19
20
/*!
21
 * \internal
22
 * \brief Add an XML ID reference to a table
23
 *
24
 * \param[in,out] table      Table of ID references to add to
25
 * \param[in]     id         ID of primary element being referred to
26
 * \param[in]     referrer   ID of element referring to \p id
27
 *
28
 * \note This refers to an ID reference in general, not necessarily connected to
29
 *       an id-ref attribute.
30
 */
31
void
32
pcmk__add_idref(GHashTable *table, const char *id, const char *referrer)
33
0
{
34
0
    pcmk__idref_t *idref = NULL;
35
36
0
    pcmk__assert((table != NULL) && (id != NULL) && (referrer != NULL));
37
38
0
    idref = g_hash_table_lookup(table, id);
39
0
    if (idref == NULL) {
40
0
        idref = pcmk__assert_alloc(1, sizeof(pcmk__idref_t));
41
0
        idref->id = pcmk__str_copy(id);
42
0
        g_hash_table_insert(table, pcmk__str_copy(id), idref);
43
0
    }
44
0
    for (GList *iter = idref->refs; iter != NULL; iter = iter->next) {
45
0
        if (pcmk__str_eq(referrer, (const char *) iter->data,
46
0
                         pcmk__str_none)) {
47
0
            return; // Already present
48
0
        }
49
0
    }
50
0
    idref->refs = g_list_append(idref->refs, pcmk__str_copy(referrer));
51
0
    pcmk__trace("Added ID %s referrer %s", id, referrer);
52
0
}
53
54
/*!
55
 * \internal
56
 * \brief Free a pcmk__idref_t
57
 *
58
 * \param[in,out] data  pcmk__idref_t to free
59
 */
60
void
61
pcmk__free_idref(void *data)
62
0
{
63
0
    pcmk__idref_t *idref = data;
64
65
0
    if (idref != NULL) {
66
0
        free(idref->id);
67
0
        g_list_free_full(idref->refs, free);
68
0
        free(idref);
69
0
    }
70
0
}
71
72
/*!
73
 * \internal
74
 * \brief Get the XML element whose \c PCMK_XA_ID matches an \c PCMK_XA_ID_REF
75
 *
76
 * \param[in] xml  Element whose \c PCMK_XA_ID_REF attribute to check
77
 * \param[in] doc  Document to search for node with matching \c PCMK_XA_ID
78
 *
79
 * \return If \p xml has a \c PCMK_XA_ID_REF attribute, node in \p doc whose
80
 *         \c PCMK_XA_ID attribute matches; otherwise, \p xml
81
 */
82
xmlNode *
83
pcmk__xe_resolve_idref(xmlNode *xml, xmlDoc *doc)
84
0
{
85
0
    char *xpath = NULL;
86
0
    const char *ref = NULL;
87
0
    xmlNode *result = NULL;
88
89
0
    if ((xml == NULL) || (doc == NULL)) {
90
0
        return NULL;
91
0
    }
92
93
0
    ref = pcmk__xe_get(xml, PCMK_XA_ID_REF);
94
0
    if (ref == NULL) {
95
0
        return xml;
96
0
    }
97
98
0
    xpath = pcmk__assert_asprintf("//%s[@" PCMK_XA_ID "='%s']", xml->name, ref);
99
0
    result = pcmk__xpath_find_one(doc, xpath, LOG_DEBUG);
100
0
    if (result == NULL) {
101
        // Not possible with schema validation enabled
102
0
        pcmk__config_err("Ignoring invalid %s configuration: "
103
0
                         PCMK_XA_ID_REF " '%s' does not reference "
104
0
                         "a valid object " QB_XS " xpath=%s",
105
0
                         xml->name, ref, xpath);
106
0
    }
107
0
    free(xpath);
108
0
    return result;
109
0
}
110
111
/*!
112
 * \internal
113
 * \brief Get list of resolved ID references for child elements of given element
114
 *
115
 * \param[in] xml           XML element to get list for
116
 * \param[in] element_name  If not NULL, list only children of this element type
117
 * \param[in] doc           XML document to use for resolving references
118
 *
119
 * \return Unordered list of XML elements corresponding to child elements of
120
 *         \p xml with any ID references resolved to the referenced elements
121
 */
122
GList *
123
pcmk__xe_dereference_children(const xmlNode *xml, const char *element_name,
124
                              xmlDoc *doc)
125
0
{
126
0
    GList *result = NULL;
127
128
0
    if (xml == NULL) {
129
0
        return NULL;
130
0
    }
131
0
    for (xmlNode *child = pcmk__xe_first_child(xml, element_name, NULL, NULL);
132
0
         child != NULL; child = pcmk__xe_next(child, element_name)) {
133
134
0
        xmlNode *resolved = pcmk__xe_resolve_idref(child, doc);
135
136
0
        if (resolved == NULL) {
137
0
            continue; // Not possible with schema validation enabled
138
0
        }
139
0
        result = g_list_prepend(result, resolved);
140
0
    }
141
0
    return result;
142
0
}