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 }
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): |
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