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 <stdbool.h>
13   	#include <stdint.h>             // uint32_t
14   	#include <errno.h>              // EINVAL
15   	#include <glib.h>               // gboolean, FALSE, etc.
16   	#include <libxml/tree.h>        // xmlNode
17   	
18   	#include <crm/common/scheduler.h>
19   	
20   	uint32_t pcmk__warnings = 0;
21   	
22   	/*!
23   	 * \brief Create a new object to hold scheduler data
24   	 *
25   	 * \return New, initialized scheduler data, or NULL on memory error
26   	 * \note Only pcmk_scheduler_t objects created with this function (as opposed
27   	 *       to statically declared or directly allocated) should be used with the
28   	 *       functions in this library, to allow for future extensions to the
29   	 *       data type. The caller is responsible for freeing the memory with
30   	 *       pcmk_free_scheduler() when the instance is no longer needed.
31   	 */
32   	pcmk_scheduler_t *
33   	pcmk_new_scheduler(void)
34   	{
35   	    pcmk_scheduler_t *scheduler = calloc(1, sizeof(pcmk_scheduler_t));
36   	
37   	    if (scheduler == NULL) {
38   	        return NULL;
39   	    }
40   	    scheduler->priv = calloc(1, sizeof(pcmk__scheduler_private_t));
41   	    if (scheduler->priv == NULL) {
42   	        free(scheduler);
43   	        return NULL;
44   	    }
45   	    pcmk__set_scheduler_defaults(scheduler);
46   	    return scheduler;
47   	}
48   	
49   	/*!
50   	 * \internal
51   	 * \brief Set non-zero default values in scheduler data
52   	 *
53   	 * \param[in,out] scheduler  Scheduler data to modify
54   	 *
55   	 * \note Values that default to NULL or 0 will remain unchanged
56   	 */
57   	void
58   	pcmk__set_scheduler_defaults(pcmk_scheduler_t *scheduler)
59   	{
60   	    pcmk__assert(scheduler != NULL);
61   	    scheduler->flags = 0U;
62   	#if PCMK__CONCURRENT_FENCING_DEFAULT_TRUE
63   	    pcmk__set_scheduler_flags(scheduler,
64   	                              pcmk__sched_symmetric_cluster
65   	                              |pcmk__sched_concurrent_fencing
66   	                              |pcmk__sched_stop_removed_resources
67   	                              |pcmk__sched_cancel_removed_actions);
68   	#else
69   	    pcmk__set_scheduler_flags(scheduler,
70   	                              pcmk__sched_symmetric_cluster
71   	                              |pcmk__sched_stop_removed_resources
72   	                              |pcmk__sched_cancel_removed_actions);
73   	#endif
74   	    scheduler->no_quorum_policy = pcmk_no_quorum_stop;
75   	    scheduler->priv->next_action_id = 1;
76   	    scheduler->priv->next_ordering_id = 1;
77   	}
78   	
79   	/*!
80   	 * \brief Reset scheduler data to defaults
81   	 *
82   	 * Free scheduler data except the local node name and output object, and reset
83   	 * all other values to defaults, so the data is suitable for rerunning status
84   	 *
85   	 * \param[in,out] scheduler  Scheduler data to reset
86   	 */
87   	void
88   	pcmk_reset_scheduler(pcmk_scheduler_t *scheduler)
89   	{
(1) Event path: Condition "scheduler == NULL", taking false branch.
90   	    if (scheduler == NULL) {
91   	        return;
92   	    }
93   	
94   	    /* Be careful about the order of freeing members. Many contain references to
95   	     * other members that will become dangling if those members are freed first.
96   	     * For example, the node name and ID of Pacemaker Remote nodes are pointers
97   	     * into resource objects. Ensure that earlier-freed members are not needed
98   	     * by any of the free functions for later-freed members.
99   	     */
100  	
101  	    scheduler->dc_node = NULL;
102  	
103  	    g_list_free_full(scheduler->nodes, pcmk__free_node);
104  	    scheduler->nodes = NULL;
105  	
106  	    // Do not reset local_node_name or out
107  	
(2) Event path: Condition "_p", taking true branch.
108  	    g_clear_pointer(&scheduler->priv->now, crm_time_free);
(3) Event path: Condition "_p", taking true branch.
109  	    g_clear_pointer(&scheduler->priv->options, g_hash_table_destroy);
110  	
111  	    scheduler->priv->fence_action = NULL;
112  	    scheduler->priv->fence_timeout_ms = 0U;
113  	    scheduler->priv->priority_fencing_ms = 0U;
114  	    scheduler->priv->shutdown_lock_ms = 0U;
115  	    scheduler->priv->node_pending_ms = 0U;
116  	    scheduler->priv->placement_strategy = NULL;
117  	    scheduler->priv->rsc_defaults = NULL;
118  	    scheduler->priv->op_defaults = NULL;
119  	
120  	    g_list_free_full(scheduler->priv->resources, pcmk__free_resource);
121  	    scheduler->priv->resources = NULL;
122  	
(4) Event path: Condition "_p", taking true branch.
123  	    g_clear_pointer(&scheduler->priv->templates, g_hash_table_destroy);
(5) Event path: Condition "_p", taking true branch.
124  	    g_clear_pointer(&scheduler->priv->tags, g_hash_table_destroy);
125  	
126  	    g_list_free_full(scheduler->priv->actions, pcmk__free_action);
127  	    scheduler->priv->actions = NULL;
128  	
(6) Event path: Condition "_p", taking true branch.
129  	    g_clear_pointer(&scheduler->priv->singletons, g_hash_table_destroy);
(7) Event path: Condition "_p", taking true branch.
130  	    g_clear_pointer(&scheduler->priv->failed, pcmk__xml_free);
131  	
132  	    pcmk__free_param_checks(scheduler);
133  	
CID (unavailable; MK=8cbdd523287e02a2db6daa7153e1d849) (#7 of 10): 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".
134  	    g_clear_pointer(&scheduler->priv->stop_needed, g_list_free);
135  	
136  	    g_list_free_full(scheduler->priv->location_constraints,
137  	                     pcmk__free_location);
138  	    scheduler->priv->location_constraints = NULL;
139  	
140  	    g_list_free_full(scheduler->priv->colocation_constraints, free);
141  	    scheduler->priv->colocation_constraints = NULL;
142  	
143  	    g_list_free_full(scheduler->priv->ordering_constraints,
144  	                     pcmk__free_action_relation);
145  	    scheduler->priv->ordering_constraints = NULL;
146  	
147  	    g_clear_pointer(&scheduler->priv->ticket_constraints, g_hash_table_destroy);
148  	
149  	    scheduler->priv->ninstances = 0;
150  	    scheduler->priv->blocked_resources = 0;
151  	    scheduler->priv->disabled_resources = 0;
152  	    scheduler->priv->recheck_by = 0;
153  	
154  	    g_clear_pointer(&scheduler->priv->graph, pcmk__xml_free);
155  	
156  	    scheduler->priv->synapse_count = 0;
157  	
158  	    g_clear_pointer(&scheduler->input, pcmk__xml_free);
159  	
160  	    pcmk__set_scheduler_defaults(scheduler);
161  	
162  	    pcmk__config_has_error = false;
163  	    pcmk__config_has_warning = false;
164  	}
165  	
166  	/*!
167  	 * \brief Free scheduler data
168  	 *
169  	 * \param[in,out] scheduler  Scheduler data to free
170  	 */
171  	void
172  	pcmk_free_scheduler(pcmk_scheduler_t *scheduler)
173  	{
174  	    if (scheduler != NULL) {
175  	        pcmk_reset_scheduler(scheduler);
176  	        free(scheduler->priv->local_node_name);
177  	        free(scheduler->priv);
178  	        free(scheduler);
179  	    }
180  	}
181  	
182  	/*!
183  	 * \internal
184  	 * \brief Get the Designated Controller node from scheduler data
185  	 *
186  	 * \param[in] scheduler  Scheduler data
187  	 *
188  	 * \return Designated Controller node from scheduler data, or NULL if none
189  	 */
190  	pcmk_node_t *
191  	pcmk_get_dc(const pcmk_scheduler_t *scheduler)
192  	{
193  	    return (scheduler == NULL)? NULL : scheduler->dc_node;
194  	}
195  	
196  	/*!
197  	 * \internal
198  	 * \brief Get the no quorum policy from scheduler data
199  	 *
200  	 * \param[in] scheduler  Scheduler data
201  	 *
202  	 * \return No quorum policy from scheduler data
203  	 */
204  	enum pe_quorum_policy
205  	pcmk_get_no_quorum_policy(const pcmk_scheduler_t *scheduler)
206  	{
207  	    if (scheduler == NULL) {
208  	        return pcmk_no_quorum_stop; // The default
209  	    }
210  	    return scheduler->no_quorum_policy;
211  	}
212  	
213  	/*!
214  	 * \internal
215  	 * \brief Set CIB XML as scheduler input in scheduler data
216  	 *
217  	 * \param[out] scheduler  Scheduler data
218  	 * \param[in]  cib        CIB XML to set as scheduler input
219  	 *
220  	 * \return Standard Pacemaker return code (EINVAL if \p scheduler is NULL,
221  	 *         otherwise pcmk_rc_ok)
222  	 * \note This will not free any previously set scheduler CIB.
223  	 */
224  	int
225  	pcmk_set_scheduler_cib(pcmk_scheduler_t *scheduler, xmlNode *cib)
226  	{
227  	    if (scheduler == NULL) {
228  	        return EINVAL;
229  	    }
230  	    scheduler->input = cib;
231  	    return pcmk_rc_ok;
232  	}
233  	
234  	/*!
235  	 * \internal
236  	 * \brief Check whether cluster has quorum
237  	 *
238  	 * \param[in] scheduler  Scheduler data
239  	 *
240  	 * \return true if cluster has quorum, otherwise false
241  	 */
242  	bool
243  	pcmk_has_quorum(const pcmk_scheduler_t *scheduler)
244  	{
245  	    if (scheduler == NULL) {
246  	        return false;
247  	    }
248  	    return pcmk__is_set(scheduler->flags, pcmk__sched_quorate);
249  	}
250  	
251  	/*!
252  	 * \brief Find a node by name in scheduler data
253  	 *
254  	 * \param[in] scheduler  Scheduler data
255  	 * \param[in] node_name  Name of node to find
256  	 *
257  	 * \return Node from scheduler data that matches \p node_name if any,
258  	 *         otherwise NULL
259  	 */
260  	pcmk_node_t *
261  	pcmk_find_node(const pcmk_scheduler_t *scheduler, const char *node_name)
262  	{
263  	    if ((scheduler == NULL) || (node_name == NULL)) {
264  	        return NULL;
265  	    }
266  	    return pcmk__find_node_in_list(scheduler->nodes, node_name);
267  	}
268  	
269  	/*!
270  	 * \internal
271  	 * \brief Get scheduler data's "now" in epoch time
272  	 *
273  	 * \param[in,out] scheduler  Scheduler data
274  	 *
275  	 * \return Scheduler data's "now" as seconds since epoch (defaulting to current
276  	 *         time)
277  	 */
278  	time_t
279  	pcmk__scheduler_epoch_time(pcmk_scheduler_t *scheduler)
280  	{
281  	    if (scheduler == NULL) {
282  	        return time(NULL);
283  	    }
284  	    if (scheduler->priv->now == NULL) {
285  	        pcmk__trace("Scheduler 'now' set to current time");
286  	        scheduler->priv->now = crm_time_new(NULL);
287  	    }
288  	    return crm_time_get_seconds_since_epoch(scheduler->priv->now);
289  	}
290  	
291  	/*!
292  	 * \internal
293  	 * \brief Update "recheck by" time in scheduler data
294  	 *
295  	 * \param[in]     recheck    Epoch time when recheck should happen
296  	 * \param[in,out] scheduler  Scheduler data
297  	 * \param[in]     reason     What time is being updated for (for logs)
298  	 */
299  	void
300  	pcmk__update_recheck_time(time_t recheck, pcmk_scheduler_t *scheduler,
301  	                          const char *reason)
302  	{
303  	    pcmk__assert(scheduler != NULL);
304  	
305  	    if ((recheck > pcmk__scheduler_epoch_time(scheduler))
306  	        && ((scheduler->priv->recheck_by == 0)
307  	            || (scheduler->priv->recheck_by > recheck))) {
308  	
309  	        scheduler->priv->recheck_by = recheck;
310  	        pcmk__debug("Updated next scheduler recheck to %s for %s",
311  	                    g_strchomp(ctime(&recheck)),
312  	                    pcmk__s(reason, "some reason"));
313  	    }
314  	}
315  	
316  	/* Fail count clearing for parameter changes normally happens when unpacking
317  	 * history, before resources are unpacked. However, for bundles using the
318  	 * REMOTE_CONTAINER_HACK, we can't check the conditions until after unpacking
319  	 * the bundle, so those parameter checks are deferred using the APIs below.
320  	 */
321  	
322  	// History entry to be checked later for fail count clearing
323  	struct param_check {
324  	    const xmlNode *rsc_history; // History entry XML
325  	    pcmk_resource_t *rsc;       // Resource corresponding to history entry
326  	    pcmk_node_t *node;          // Node corresponding to history entry
327  	    enum pcmk__check_parameters check_type; // What needs checking
328  	};
329  	
330  	/*!
331  	 * \internal
332  	 * \brief Add a deferred parameter check
333  	 *
334  	 * \param[in]     rsc_history  Resource history XML to check later
335  	 * \param[in,out] rsc          Resource that history is for
336  	 * \param[in]     node         Node that history is for
337  	 * \param[in]     flag         What needs to be checked later
338  	 */
339  	void
340  	pcmk__add_param_check(const xmlNode *rsc_history, pcmk_resource_t *rsc,
341  	                      pcmk_node_t *node, enum pcmk__check_parameters flag)
342  	{
343  	    struct param_check *param_check = NULL;
344  	
345  	    CRM_CHECK((rsc_history != NULL) && (rsc != NULL) && (node != NULL), return);
346  	
347  	    pcmk__trace("Deferring checks of %s until after assignment",
348  	                pcmk__xe_id(rsc_history));
349  	    param_check = pcmk__assert_alloc(1, sizeof(struct param_check));
350  	    param_check->rsc_history = rsc_history;
351  	    param_check->rsc = rsc;
352  	    param_check->node = node;
353  	    param_check->check_type = flag;
354  	
355  	    rsc->priv->scheduler->priv->param_check =
356  	        g_list_prepend(rsc->priv->scheduler->priv->param_check, param_check);
357  	}
358  	
359  	/*!
360  	 * \internal
361  	 * \brief Call a function for each deferred parameter check
362  	 *
363  	 * \param[in,out] scheduler  Scheduler data
364  	 * \param[in]     cb         Function to be called
365  	 */
366  	void
367  	pcmk__foreach_param_check(pcmk_scheduler_t *scheduler,
368  	                          void (*cb)(pcmk_resource_t*, pcmk_node_t*,
369  	                                     const xmlNode*,
370  	                                     enum pcmk__check_parameters))
371  	{
372  	    pcmk__assert(cb != NULL);
373  	    CRM_CHECK(scheduler != NULL, return);
374  	
375  	    for (GList *item = scheduler->priv->param_check;
376  	         item != NULL; item = item->next) {
377  	        struct param_check *param_check = item->data;
378  	
379  	        cb(param_check->rsc, param_check->node, param_check->rsc_history,
380  	           param_check->check_type);
381  	    }
382  	}
383  	
384  	/*!
385  	 * \internal
386  	 * \brief Free all deferred parameter checks
387  	 *
388  	 * \param[in,out] scheduler  Scheduler data
389  	 */
390  	void
391  	pcmk__free_param_checks(pcmk_scheduler_t *scheduler)
392  	{
393  	    if (scheduler == NULL) {
394  	        return;
395  	    }
396  	
397  	    g_list_free_full(scheduler->priv->param_check, free);
398  	    scheduler->priv->param_check = NULL;
399  	}
400