1    	/*
2    	 * Copyright 2022-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 <stdbool.h>
13   	
14   	#include <libxml/tree.h>        // xmlNode
15   	
16   	#include <crm/common/nvpair.h>
17   	
18   	/*!
19   	 * \internal
20   	 * \brief Free a node object
21   	 *
22   	 * \param[in,out] user_data  Node object to free
23   	 */
24   	void
25   	pcmk__free_node(gpointer user_data)
26   	{
27   	    pcmk_node_t *node = user_data;
28   	
(1) Event path: Condition "node == NULL", taking false branch.
29   	    if (node == NULL) {
30   	        return;
31   	    }
32   	
33   	    /* This may be called after freeing resources, which means that we can't
34   	     * use node->private->name for Pacemaker Remote nodes.
35   	     */
(2) Event path: Switch case default.
(3) Event path: Condition "trace_cs == NULL", taking true branch.
(4) Event path: Condition "crm_is_callsite_active(trace_cs, _level, 0)", taking false branch.
(5) Event path: Breaking from switch.
36   	    pcmk__trace("Freeing node %s", pcmk__node_name(node));
37   	
(6) Event path: Condition "node->details != NULL", taking true branch.
38   	    if (node->details != NULL) {
39   	        g_list_free(node->details->running_rsc);
40   	        free(node->details);
41   	    }
42   	
(7) Event path: Condition "node->priv != NULL", taking true branch.
43   	    if (node->priv != NULL) {
44   	        free(node->priv->id);
45   	        free(node->priv->name);
(8) Event path: Condition "_p", taking true branch.
46   	        g_clear_pointer(&node->priv->attrs, g_hash_table_destroy);
(9) Event path: Condition "_p", taking true branch.
47   	        g_clear_pointer(&node->priv->utilization, g_hash_table_destroy);
CID (unavailable; MK=cfd2ff71bbe260566cb58e4dc5633223) (#3 of 3): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(10) Event assign_union_field: The union field "in" of "_pp" is written.
(11) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
48   	        g_clear_pointer(&node->priv->digest_cache, g_hash_table_destroy);
49   	        g_list_free(node->priv->assigned_resources);
50   	        free(node->priv);
51   	    }
52   	
53   	    free(node->assign);
54   	    free(node);
55   	}
56   	
57   	/*!
58   	 * \internal
59   	 * \brief Free a copy of a node object
60   	 *
61   	 * \param[in] data  Node copy (created by pe__copy_node()) to free
62   	 */
63   	void
64   	pcmk__free_node_copy(void *data)
65   	{
66   	    if (data != NULL) {
67   	        pcmk_node_t *node = data;
68   	
69   	        if (node->assign != NULL) {
70   	            // This is the only member allocated separately for a node copy
71   	            free(node->assign);
72   	        }
73   	        free(node);
74   	    }
75   	}
76   	
77   	/*!
78   	 * \internal
79   	 * \brief Check whether a node is online
80   	 *
81   	 * \param[in] node  Node to check
82   	 *
83   	 * \return true if \p node is online, otherwise false
84   	 */
85   	bool
86   	pcmk_node_is_online(const pcmk_node_t *node)
87   	{
88   	    return (node != NULL) && node->details->online;
89   	}
90   	
91   	/*!
92   	 * \internal
93   	 * \brief Check whether a node is pending
94   	 *
95   	 * Check whether a node is pending. A node is pending if it is a member of the
96   	 * cluster but not the controller group, which means it is in the process of
97   	 * either joining or leaving the cluster.
98   	 *
99   	 * \param[in] node  Node to check
100  	 *
101  	 * \return true if \p node is pending, otherwise false
102  	 */
103  	bool
104  	pcmk_node_is_pending(const pcmk_node_t *node)
105  	{
106  	    return (node != NULL) && node->details->pending;
107  	}
108  	
109  	/*!
110  	 * \internal
111  	 * \brief Check whether a node is clean
112  	 *
113  	 * Check whether a node is clean. A node is clean if it is a cluster node or
114  	 * remote node that has been seen by the cluster at least once, or the
115  	 * startup-fencing cluster option is false; and the node, and its host if a
116  	 * guest or bundle node, are not scheduled to be fenced.
117  	 *
118  	 * \param[in] node  Node to check
119  	 *
120  	 * \return true if \p node is clean, otherwise false
121  	 */
122  	bool
123  	pcmk_node_is_clean(const pcmk_node_t *node)
124  	{
125  	    return (node != NULL) && !(node->details->unclean);
126  	}
127  	
128  	/*!
129  	 * \internal
130  	 * \brief Check whether a node is shutting down
131  	 *
132  	 * \param[in] node  Node to check
133  	 *
134  	 * \return true if \p node is shutting down, otherwise false
135  	 */
136  	bool
137  	pcmk_node_is_shutting_down(const pcmk_node_t *node)
138  	{
139  	    return (node != NULL) && node->details->shutdown;
140  	}
141  	
142  	/*!
143  	 * \internal
144  	 * \brief Check whether a node is in maintenance mode
145  	 *
146  	 * \param[in] node  Node to check
147  	 *
148  	 * \return true if \p node is in maintenance mode, otherwise false
149  	 */
150  	bool
151  	pcmk_node_is_in_maintenance(const pcmk_node_t *node)
152  	{
153  	    return (node != NULL) && node->details->maintenance;
154  	}
155  	
156  	/*!
157  	 * \internal
158  	 * \brief Call a function for each resource active on a node
159  	 *
160  	 * Call a caller-supplied function with a caller-supplied argument for each
161  	 * resource that is active on a given node. If the function returns false, this
162  	 * function will return immediately without processing any remaining resources.
163  	 *
164  	 * \param[in] node  Node to check
165  	 *
166  	 * \return Result of last call of \p fn (or false if none)
167  	 */
168  	bool
169  	pcmk_foreach_active_resource(pcmk_node_t *node,
170  	                             bool (*fn)(pcmk_resource_t *, void *),
171  	                             void *user_data)
172  	{
173  	    bool result = false;
174  	
175  	    if ((node != NULL) && (fn != NULL)) {
176  	        for (GList *item = node->details->running_rsc; item != NULL;
177  	             item = item->next) {
178  	
179  	            result = fn((pcmk_resource_t *) item->data, user_data);
180  	            if (!result) {
181  	                break;
182  	            }
183  	        }
184  	    }
185  	    return result;
186  	}
187  	
188  	/*!
189  	 * \internal
190  	 * \brief Find a node by name in a list of nodes
191  	 *
192  	 * \param[in] nodes      List of nodes (as pcmk_node_t*)
193  	 * \param[in] node_name  Name of node to find
194  	 *
195  	 * \return Node from \p nodes that matches \p node_name if any, otherwise NULL
196  	 */
197  	pcmk_node_t *
198  	pcmk__find_node_in_list(const GList *nodes, const char *node_name)
199  	{
200  	    if (node_name != NULL) {
201  	        for (const GList *iter = nodes; iter != NULL; iter = iter->next) {
202  	            pcmk_node_t *node = (pcmk_node_t *) iter->data;
203  	
204  	            if (pcmk__str_eq(node->priv->name, node_name, pcmk__str_casei)) {
205  	                return node;
206  	            }
207  	        }
208  	    }
209  	    return NULL;
210  	}
211  	
212  	#define XP_SHUTDOWN "//" PCMK__XE_NODE_STATE "[@" PCMK_XA_UNAME "='%s']/"   \
213  	    PCMK__XE_TRANSIENT_ATTRIBUTES "/" PCMK_XE_INSTANCE_ATTRIBUTES "/"       \
214  	    PCMK_XE_NVPAIR "[@" PCMK_XA_NAME "='" PCMK__NODE_ATTR_SHUTDOWN "']"
215  	
216  	/*!
217  	 * \brief Get value of a node's shutdown attribute from CIB, if present
218  	 *
219  	 * \param[in] cib   CIB to check
220  	 * \param[in] node  Name of node to check
221  	 *
222  	 * \return Value of shutdown attribute for \p node in \p cib if any,
223  	 *         otherwise NULL
224  	 * \note The return value is a pointer into \p cib and so is valid only for the
225  	 *       lifetime of that object.
226  	 */
227  	const char *
228  	pcmk_cib_node_shutdown(xmlNode *cib, const char *node)
229  	{
230  	    if ((cib != NULL) && (node != NULL)) {
231  	        char *xpath = pcmk__assert_asprintf(XP_SHUTDOWN, node);
232  	        xmlNode *match = pcmk__xpath_find_one(cib->doc, xpath, LOG_TRACE);
233  	
234  	        free(xpath);
235  	        if (match != NULL) {
236  	            return pcmk__xe_get(match, PCMK_XA_VALUE);
237  	        }
238  	    }
239  	    return NULL;
240  	}
241