1    	/*
2    	 * Copyright 2004-2023 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/ipc_internal.h>
21   	#include <crm/common/mainloop.h>
22   	#include <crm/msg_xml.h>
23   	
24   	#include "pacemaker-attrd.h"
25   	
26   	cib_t *the_cib = NULL;
27   	
28   	static bool requesting_shutdown = false;
29   	static bool shutting_down = false;
30   	static GMainLoop *mloop = NULL;
31   	
32   	/* A hash table storing information on the protocol version of each peer attrd.
33   	 * The key is the peer's uname, and the value is the protocol version number.
34   	 */
35   	GHashTable *peer_protocol_vers = NULL;
36   	
37   	/*!
38   	 * \internal
39   	 * \brief  Set requesting_shutdown state
40   	 */
41   	void
42   	attrd_set_requesting_shutdown(void)
43   	{
44   	    requesting_shutdown = true;
45   	}
46   	
47   	/*!
48   	 * \internal
49   	 * \brief  Clear requesting_shutdown state
50   	 */
51   	void
52   	attrd_clear_requesting_shutdown(void)
53   	{
54   	    requesting_shutdown = false;
55   	}
56   	
57   	/*!
58   	 * \internal
59   	 * \brief Check whether local attribute manager is shutting down
60   	 *
61   	 * \param[in] if_requested  Also consider presence of "shutdown" attribute
62   	 *
63   	 * \return \c true if local attribute manager has begun shutdown sequence
64   	 *         or (if \p if_requested is \c true) whether local node has a nonzero
65   	 *         "shutdown" attribute set, otherwise \c false
66   	 * \note Most callers should pass \c false for \p if_requested, because the
67   	 *       attribute manager needs to continue performing while the controller is
68   	 *       shutting down, and even needs to be eligible for election in case all
69   	 *       nodes are shutting down.
70   	 */
71   	bool
72   	attrd_shutting_down(bool if_requested)
73   	{
74   	    return shutting_down || (if_requested && requesting_shutdown);
75   	}
76   	
77   	/*!
78   	 * \internal
79   	 * \brief  Exit (using mainloop or not, as appropriate)
80   	 *
81   	 * \param[in] nsig  Ignored
82   	 */
83   	void
84   	attrd_shutdown(int nsig)
85   	{
86   	    // Tell various functions not to do anthing
87   	    shutting_down = true;
88   	
89   	    // Don't respond to signals while shutting down
90   	    mainloop_destroy_signal(SIGTERM);
91   	    mainloop_destroy_signal(SIGCHLD);
92   	    mainloop_destroy_signal(SIGPIPE);
93   	    mainloop_destroy_signal(SIGUSR1);
94   	    mainloop_destroy_signal(SIGUSR2);
95   	    mainloop_destroy_signal(SIGTRAP);
96   	
97   	    attrd_free_waitlist();
98   	    attrd_free_confirmations();
99   	
100  	    if (peer_protocol_vers != NULL) {
101  	        g_hash_table_destroy(peer_protocol_vers);
102  	        peer_protocol_vers = NULL;
103  	    }
104  	
105  	    if ((mloop == NULL) || !g_main_loop_is_running(mloop)) {
106  	        /* If there's no main loop active, just exit. This should be possible
107  	         * only if we get SIGTERM in brief windows at start-up and shutdown.
108  	         */
109  	        crm_exit(CRM_EX_OK);
110  	    } else {
111  	        g_main_loop_quit(mloop);
112  	        g_main_loop_unref(mloop);
113  	    }
114  	}
115  	
116  	/*!
117  	 * \internal
118  	 * \brief Create a main loop for attrd
119  	 */
120  	void
121  	attrd_init_mainloop(void)
122  	{
123  	    mloop = g_main_loop_new(NULL, FALSE);
124  	}
125  	
126  	/*!
127  	 * \internal
128  	 * \brief Run attrd main loop
129  	 */
130  	void
131  	attrd_run_mainloop(void)
132  	{
133  	    g_main_loop_run(mloop);
134  	}
135  	
136  	/* strlen("value") */
137  	#define plus_plus_len (5)
138  	
139  	/*!
140  	 * \internal
141  	 * \brief  Check whether an attribute value should be expanded
142  	 *
143  	 * \param[in] value  Attribute value to check
144  	 *
145  	 * \return true if value needs expansion, false otherwise
146  	 */
147  	bool
148  	attrd_value_needs_expansion(const char *value)
149  	{
150  	    return ((strlen(value) >= (plus_plus_len + 2))
151  	           && (value[plus_plus_len] == '+')
152  	           && ((value[plus_plus_len + 1] == '+')
153  	               || (value[plus_plus_len + 1] == '=')));
154  	}
155  	
156  	/*!
157  	 * \internal
158  	 * \brief Expand an increment expression into an integer
159  	 *
160  	 * \param[in] value      Attribute increment expression to expand
161  	 * \param[in] old_value  Previous value of attribute
162  	 *
163  	 * \return Expanded value
164  	 */
165  	int
166  	attrd_expand_value(const char *value, const char *old_value)
167  	{
168  	    int offset = 1;
169  	    int int_value = char2score(old_value);
170  	
(1) Event path: Condition "value[6 /* 5 + 1 */] != '+'", taking true branch.
171  	    if (value[plus_plus_len + 1] != '+') {
172  	        const char *offset_s = value + (plus_plus_len + 2);
173  	
(2) Event tainted_data_return: Called function "char2score(offset_s)", and a possible return value may be less than zero.
(3) Event assign: Assigning: "offset" = "char2score(offset_s)".
Also see events: [overflow][return_overflow]
174  	        offset = char2score(offset_s);
175  	    }
(4) Event overflow: The expression "int_value" is considered to have possibly overflowed.
Also see events: [tainted_data_return][assign][return_overflow]
176  	    int_value += offset;
177  	
(5) Event path: Condition "int_value > 1000000", taking false branch.
178  	    if (int_value > INFINITY) {
179  	        int_value = INFINITY;
180  	    }
CID (unavailable; MK=f353cd120ebd56816403e4f5b951df53) (#1 of 1): Overflowed return value (INTEGER_OVERFLOW):
(6) Event return_overflow: "int_value", which might have overflowed, is returned from the function.
Also see events: [tainted_data_return][assign][overflow]
181  	    return int_value;
182  	}
183  	
184  	/*!
185  	 * \internal
186  	 * \brief Create regular expression matching failure-related attributes
187  	 *
188  	 * \param[out] regex  Where to store created regular expression
189  	 * \param[in]  rsc    Name of resource to clear (or NULL for all)
190  	 * \param[in]  op     Operation to clear if rsc is specified (or NULL for all)
191  	 * \param[in]  interval_ms  Interval of operation to clear if op is specified
192  	 *
193  	 * \return pcmk_ok on success, -EINVAL if arguments are invalid
194  	 *
195  	 * \note The caller is responsible for freeing the result with regfree().
196  	 */
197  	int
198  	attrd_failure_regex(regex_t *regex, const char *rsc, const char *op,
199  	                    guint interval_ms)
200  	{
201  	    char *pattern = NULL;
202  	    int rc;
203  	
204  	    /* Create a pattern that matches desired attributes */
205  	
206  	    if (rsc == NULL) {
207  	        pattern = strdup(ATTRD_RE_CLEAR_ALL);
208  	    } else if (op == NULL) {
209  	        pattern = crm_strdup_printf(ATTRD_RE_CLEAR_ONE, rsc);
210  	    } else {
211  	        pattern = crm_strdup_printf(ATTRD_RE_CLEAR_OP, rsc, op, interval_ms);
212  	    }
213  	
214  	    /* Compile pattern into regular expression */
215  	    crm_trace("Clearing attributes matching %s", pattern);
216  	    rc = regcomp(regex, pattern, REG_EXTENDED|REG_NOSUB);
217  	    free(pattern);
218  	
219  	    return (rc == 0)? pcmk_ok : -EINVAL;
220  	}
221  	
222  	void
223  	attrd_free_attribute_value(gpointer data)
224  	{
225  	    attribute_value_t *v = data;
226  	
227  	    free(v->nodename);
228  	    free(v->current);
229  	    free(v->requested);
230  	    free(v);
231  	}
232  	
233  	void
234  	attrd_free_attribute(gpointer data)
235  	{
236  	    attribute_t *a = data;
237  	    if(a) {
238  	        free(a->id);
239  	        free(a->set_id);
240  	        free(a->set_type);
241  	        free(a->uuid);
242  	        free(a->user);
243  	
244  	        mainloop_timer_del(a->timer);
245  	        g_hash_table_destroy(a->values);
246  	
247  	        free(a);
248  	    }
249  	}
250  	
251  	/*!
252  	 * \internal
253  	 * \brief When a peer node leaves the cluster, stop tracking its protocol version.
254  	 *
255  	 * \param[in] host  The peer node's uname to be removed
256  	 */
257  	void
258  	attrd_remove_peer_protocol_ver(const char *host)
259  	{
260  	    if (peer_protocol_vers != NULL) {
261  	        g_hash_table_remove(peer_protocol_vers, host);
262  	    }
263  	}
264  	
265  	/*!
266  	 * \internal
267  	 * \brief When a peer node broadcasts a message with its protocol version, keep
268  	 *        track of that information.
269  	 *
270  	 * We keep track of each peer's protocol version so we know which peers to
271  	 * expect confirmation messages from when handling cluster-wide sync points.
272  	 * We additionally keep track of the lowest protocol version supported by all
273  	 * peers so we know when we can send IPC messages containing more than one
274  	 * request.
275  	 *
276  	 * \param[in] host  The peer node's uname to be tracked
277  	 * \param[in] value The peer node's protocol version
278  	 */
279  	void
280  	attrd_update_minimum_protocol_ver(const char *host, const char *value)
281  	{
282  	    int ver;
283  	
284  	    if (peer_protocol_vers == NULL) {
285  	        peer_protocol_vers = pcmk__strkey_table(free, NULL);
286  	    }
287  	
288  	    pcmk__scan_min_int(value, &ver, 0);
289  	
290  	    if (ver > 0) {
291  	        char *host_name = strdup(host);
292  	
293  	        /* Record the peer attrd's protocol version. */
294  	        CRM_ASSERT(host_name != NULL);
295  	        g_hash_table_insert(peer_protocol_vers, host_name, GINT_TO_POINTER(ver));
296  	
297  	        /* If the protocol version is a new minimum, record it as such. */
298  	        if (minimum_protocol_version == -1 || ver < minimum_protocol_version) {
299  	            minimum_protocol_version = ver;
300  	            crm_trace("Set minimum attrd protocol version to %d",
301  	                      minimum_protocol_version);
302  	        }
303  	    }
304  	}
305  	
306  	void
307  	attrd_copy_xml_attributes(xmlNode *src, xmlNode *dest)
308  	{
309  	    /* Copy attributes from the wrapper parent node into the child node.
310  	     * We can't just use copy_in_properties because we want to skip any
311  	     * attributes that are already set on the child.  For instance, if
312  	     * we were told to use a specific node, there will already be a node
313  	     * attribute on the child.  Copying the parent's node attribute over
314  	     * could result in the wrong value.
315  	     */
316  	    for (xmlAttrPtr a = pcmk__xe_first_attr(src); a != NULL; a = a->next) {
317  	        const char *p_name = (const char *) a->name;
318  	        const char *p_value = ((a == NULL) || (a->children == NULL)) ? NULL :
319  	                              (const char *) a->children->content;
320  	
321  	        if (crm_element_value(dest, p_name) == NULL) {
322  	            crm_xml_add(dest, p_name, p_value);
323  	        }
324  	    }
325  	}
326