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