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