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 General Public License version 2
7    	 * or later (GPLv2+) WITHOUT ANY WARRANTY.
8    	 */
9    	
10   	#include <crm_internal.h>
11   	
12   	#include <stdio.h>
13   	#include <stdbool.h>
14   	#include <errno.h>
15   	#include <glib.h>
16   	#include <regex.h>
17   	#include <sys/types.h>
18   	
19   	#include <crm/crm.h>
20   	#include <crm/common/mainloop.h>
21   	#include <crm/common/xml.h>
22   	
23   	#include "pacemaker-attrd.h"
24   	
25   	cib_t *the_cib = NULL;
26   	
27   	static bool shutting_down = false;
28   	static GMainLoop *mloop = NULL;
29   	
30   	/* A hash table storing information on the protocol version of each peer attrd.
31   	 * The key is the peer's uname, and the value is the protocol version number.
32   	 */
33   	GHashTable *peer_protocol_vers = NULL;
34   	
35   	/*!
36   	 * \internal
37   	 * \brief Check whether local attribute manager is shutting down
38   	 *
39   	 * \return \c true if local attribute manager has begun shutdown sequence,
40   	 *         otherwise \c false
41   	 */
42   	bool
43   	attrd_shutting_down(void)
44   	{
45   	    return shutting_down;
46   	}
47   	
48   	/*!
49   	 * \internal
50   	 * \brief  Exit (using mainloop or not, as appropriate)
51   	 *
52   	 * \param[in] nsig  Ignored
53   	 */
54   	void
55   	attrd_shutdown(int nsig)
56   	{
57   	    // Tell various functions not to do anthing
58   	    shutting_down = true;
59   	
60   	    // Don't respond to signals while shutting down
61   	    mainloop_destroy_signal(SIGTERM);
62   	    mainloop_destroy_signal(SIGCHLD);
63   	    mainloop_destroy_signal(SIGPIPE);
64   	    mainloop_destroy_signal(SIGUSR1);
65   	    mainloop_destroy_signal(SIGUSR2);
66   	    mainloop_destroy_signal(SIGTRAP);
67   	
68   	    attrd_free_waitlist();
69   	    attrd_free_confirmations();
70   	
CID (unavailable; MK=6b1c8f84eb6a333f6cd7a1c9b8ba4334) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(1) Event assign_union_field: The union field "in" of "_pp" is written.
(2) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
71   	    g_clear_pointer(&peer_protocol_vers, g_hash_table_destroy);
72   	
73   	    if ((mloop == NULL) || !g_main_loop_is_running(mloop)) {
74   	        /* If there's no main loop active, just exit. This should be possible
75   	         * only if we get SIGTERM in brief windows at start-up and shutdown.
76   	         */
77   	        crm_exit(CRM_EX_OK);
78   	    } else {
79   	        g_main_loop_quit(mloop);
80   	        g_main_loop_unref(mloop);
81   	    }
82   	}
83   	
84   	/*!
85   	 * \internal
86   	 * \brief Create a main loop for attrd
87   	 */
88   	void
89   	attrd_init_mainloop(void)
90   	{
91   	    mloop = g_main_loop_new(NULL, FALSE);
92   	}
93   	
94   	/*!
95   	 * \internal
96   	 * \brief Run attrd main loop
97   	 */
98   	void
99   	attrd_run_mainloop(void)
100  	{
101  	    g_main_loop_run(mloop);
102  	}
103  	
104  	/* strlen("value") */
105  	#define plus_plus_len (5)
106  	
107  	/*!
108  	 * \internal
109  	 * \brief  Check whether an attribute value should be expanded
110  	 *
111  	 * \param[in] value  Attribute value to check
112  	 *
113  	 * \return true if value needs expansion, false otherwise
114  	 */
115  	bool
116  	attrd_value_needs_expansion(const char *value)
117  	{
118  	    return ((strlen(value) >= (plus_plus_len + 2))
119  	           && (value[plus_plus_len] == '+')
120  	           && ((value[plus_plus_len + 1] == '+')
121  	               || (value[plus_plus_len + 1] == '=')));
122  	}
123  	
124  	/*!
125  	 * \internal
126  	 * \brief Expand an increment expression into an integer
127  	 *
128  	 * \param[in] value      Attribute increment expression to expand
129  	 * \param[in] old_value  Previous value of attribute
130  	 *
131  	 * \return Expanded value
132  	 */
133  	int
134  	attrd_expand_value(const char *value, const char *old_value)
135  	{
136  	    int increment = 1;
137  	    int score = 0;
138  	
139  	    if (pcmk_parse_score(old_value, &score, 0) != pcmk_rc_ok) {
140  	        return 0; // Original value is not a score
141  	    }
142  	
143  	    // value++ means increment by one, value+=OFFSET means incremement by OFFSET
144  	    if ((value[plus_plus_len + 1] != '+')
145  	        && (pcmk_parse_score(value + plus_plus_len + 2, &increment,
146  	                             0) != pcmk_rc_ok)) {
147  	        increment = 0; // Invalid increment
148  	    }
149  	
150  	    if (increment < 0) {
151  	        return QB_MAX(score + increment, -PCMK_SCORE_INFINITY);
152  	    }
153  	    return QB_MIN(score + increment, PCMK_SCORE_INFINITY);
154  	}
155  	
156  	/*!
157  	 * \internal
158  	 * \brief Create regular expression matching failure-related attributes
159  	 *
160  	 * \param[out] regex  Where to store created regular expression
161  	 * \param[in]  rsc    Name of resource to clear (or NULL for all)
162  	 * \param[in]  op     Operation to clear if rsc is specified (or NULL for all)
163  	 * \param[in]  interval_ms  Interval of operation to clear if op is specified
164  	 *
165  	 * \return pcmk_ok on success, -EINVAL if arguments are invalid
166  	 *
167  	 * \note The caller is responsible for freeing the result with regfree().
168  	 */
169  	int
170  	attrd_failure_regex(regex_t *regex, const char *rsc, const char *op,
171  	                    guint interval_ms)
172  	{
173  	    char *pattern = NULL;
174  	    int rc;
175  	
176  	    /* Create a pattern that matches desired attributes */
177  	
178  	    if (rsc == NULL) {
179  	        pattern = pcmk__str_copy(ATTRD_RE_CLEAR_ALL);
180  	    } else if (op == NULL) {
181  	        pattern = pcmk__assert_asprintf(ATTRD_RE_CLEAR_ONE, rsc);
182  	    } else {
183  	        pattern = pcmk__assert_asprintf(ATTRD_RE_CLEAR_OP, rsc, op,
184  	                                        interval_ms);
185  	    }
186  	
187  	    /* Compile pattern into regular expression */
188  	    pcmk__trace("Clearing attributes matching %s", pattern);
189  	    rc = regcomp(regex, pattern, REG_EXTENDED|REG_NOSUB);
190  	    free(pattern);
191  	
192  	    return (rc == 0)? pcmk_ok : -EINVAL;
193  	}
194  	
195  	void
196  	attrd_free_attribute_value(gpointer data)
197  	{
198  	    attribute_value_t *v = data;
199  	
200  	    free(v->nodename);
201  	    free(v->current);
202  	    free(v->requested);
203  	    free(v);
204  	}
205  	
206  	void
207  	attrd_free_attribute(gpointer data)
208  	{
209  	    attribute_t *a = data;
210  	    if(a) {
211  	        free(a->id);
212  	        free(a->set_id);
213  	        free(a->set_type);
214  	        free(a->user);
215  	
216  	        mainloop_timer_del(a->timer);
217  	        g_hash_table_destroy(a->values);
218  	
219  	        free(a);
220  	    }
221  	}
222  	
223  	/*!
224  	 * \internal
225  	 * \brief When a peer node leaves the cluster, stop tracking its protocol version.
226  	 *
227  	 * \param[in] host  The peer node's uname to be removed
228  	 */
229  	void
230  	attrd_remove_peer_protocol_ver(const char *host)
231  	{
232  	    if (peer_protocol_vers != NULL) {
233  	        g_hash_table_remove(peer_protocol_vers, host);
234  	    }
235  	}
236  	
237  	/*!
238  	 * \internal
239  	 * \brief When a peer node broadcasts a message with its protocol version, keep
240  	 *        track of that information.
241  	 *
242  	 * We keep track of each peer's protocol version so we know which peers to
243  	 * expect confirmation messages from when handling cluster-wide sync points.
244  	 * We additionally keep track of the lowest protocol version supported by all
245  	 * peers so we know when we can send IPC messages containing more than one
246  	 * request.
247  	 *
248  	 * \param[in] host  The peer node's uname to be tracked
249  	 * \param[in] value The peer node's protocol version
250  	 */
251  	void
252  	attrd_update_minimum_protocol_ver(const char *host, const char *value)
253  	{
254  	    int ver;
255  	
256  	    if (peer_protocol_vers == NULL) {
257  	        peer_protocol_vers = pcmk__strkey_table(free, NULL);
258  	    }
259  	
260  	    pcmk__scan_min_int(value, &ver, 0);
261  	
262  	    if (ver > 0) {
263  	        /* Record the peer attrd's protocol version. */
264  	        g_hash_table_insert(peer_protocol_vers, pcmk__str_copy(host),
265  	                            GINT_TO_POINTER(ver));
266  	
267  	        /* If the protocol version is a new minimum, record it as such. */
268  	        if (minimum_protocol_version == -1 || ver < minimum_protocol_version) {
269  	            minimum_protocol_version = ver;
270  	            pcmk__trace("Set minimum attrd protocol version to %d",
271  	                        minimum_protocol_version);
272  	        }
273  	    }
274  	}
275