summaryrefslogtreecommitdiffstats
path: root/daemons/attrd/attrd_utils.c
blob: 341ee1aed051db8722926d6a17a0bcfd1dc73174 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
/*
 * Copyright 2004-2023 the Pacemaker project contributors
 *
 * The version control history for this file may have further details.
 *
 * This source code is licensed under the GNU General Public License version 2
 * or later (GPLv2+) WITHOUT ANY WARRANTY.
 */

#include <crm_internal.h>

#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include <glib.h>
#include <regex.h>
#include <sys/types.h>

#include <crm/crm.h>
#include <crm/common/ipc_internal.h>
#include <crm/common/mainloop.h>
#include <crm/msg_xml.h>

#include "pacemaker-attrd.h"

cib_t *the_cib = NULL;

static bool requesting_shutdown = false;
static bool shutting_down = false;
static GMainLoop *mloop = NULL;

/* A hash table storing information on the protocol version of each peer attrd.
 * The key is the peer's uname, and the value is the protocol version number.
 */
GHashTable *peer_protocol_vers = NULL;

/*!
 * \internal
 * \brief  Set requesting_shutdown state
 */
void
attrd_set_requesting_shutdown(void)
{
    requesting_shutdown = true;
}

/*!
 * \internal
 * \brief  Clear requesting_shutdown state
 */
void
attrd_clear_requesting_shutdown(void)
{
    requesting_shutdown = false;
}

/*!
 * \internal
 * \brief Check whether local attribute manager is shutting down
 *
 * \param[in] if_requested  Also consider presence of "shutdown" attribute
 *
 * \return \c true if local attribute manager has begun shutdown sequence
 *         or (if \p if_requested is \c true) whether local node has a nonzero
 *         "shutdown" attribute set, otherwise \c false
 * \note Most callers should pass \c false for \p if_requested, because the
 *       attribute manager needs to continue performing while the controller is
 *       shutting down, and even needs to be eligible for election in case all
 *       nodes are shutting down.
 */
bool
attrd_shutting_down(bool if_requested)
{
    return shutting_down || (if_requested && requesting_shutdown);
}

/*!
 * \internal
 * \brief  Exit (using mainloop or not, as appropriate)
 *
 * \param[in] nsig  Ignored
 */
void
attrd_shutdown(int nsig)
{
    // Tell various functions not to do anthing
    shutting_down = true;

    // Don't respond to signals while shutting down
    mainloop_destroy_signal(SIGTERM);
    mainloop_destroy_signal(SIGCHLD);
    mainloop_destroy_signal(SIGPIPE);
    mainloop_destroy_signal(SIGUSR1);
    mainloop_destroy_signal(SIGUSR2);
    mainloop_destroy_signal(SIGTRAP);

    attrd_free_waitlist();
    attrd_free_confirmations();

    if (peer_protocol_vers != NULL) {
        g_hash_table_destroy(peer_protocol_vers);
        peer_protocol_vers = NULL;
    }

    if ((mloop == NULL) || !g_main_loop_is_running(mloop)) {
        /* If there's no main loop active, just exit. This should be possible
         * only if we get SIGTERM in brief windows at start-up and shutdown.
         */
        crm_exit(CRM_EX_OK);
    } else {
        g_main_loop_quit(mloop);
        g_main_loop_unref(mloop);
    }
}

/*!
 * \internal
 * \brief Create a main loop for attrd
 */
void
attrd_init_mainloop(void)
{
    mloop = g_main_loop_new(NULL, FALSE);
}

/*!
 * \internal
 * \brief Run attrd main loop
 */
void
attrd_run_mainloop(void)
{
    g_main_loop_run(mloop);
}

/* strlen("value") */
#define plus_plus_len (5)

/*!
 * \internal
 * \brief  Check whether an attribute value should be expanded
 *
 * \param[in] value  Attribute value to check
 *
 * \return true if value needs expansion, false otherwise
 */
bool
attrd_value_needs_expansion(const char *value)
{
    return ((strlen(value) >= (plus_plus_len + 2))
           && (value[plus_plus_len] == '+')
           && ((value[plus_plus_len + 1] == '+')
               || (value[plus_plus_len + 1] == '=')));
}

/*!
 * \internal
 * \brief Expand an increment expression into an integer
 *
 * \param[in] value      Attribute increment expression to expand
 * \param[in] old_value  Previous value of attribute
 *
 * \return Expanded value
 */
int
attrd_expand_value(const char *value, const char *old_value)
{
    int offset = 1;
    int int_value = char2score(old_value);

    if (value[plus_plus_len + 1] != '+') {
        const char *offset_s = value + (plus_plus_len + 2);

        offset = char2score(offset_s);
    }
    int_value += offset;

    if (int_value > INFINITY) {
        int_value = INFINITY;
    }
    return int_value;
}

/*!
 * \internal
 * \brief Create regular expression matching failure-related attributes
 *
 * \param[out] regex  Where to store created regular expression
 * \param[in]  rsc    Name of resource to clear (or NULL for all)
 * \param[in]  op     Operation to clear if rsc is specified (or NULL for all)
 * \param[in]  interval_ms  Interval of operation to clear if op is specified
 *
 * \return pcmk_ok on success, -EINVAL if arguments are invalid
 *
 * \note The caller is responsible for freeing the result with regfree().
 */
int
attrd_failure_regex(regex_t *regex, const char *rsc, const char *op,
                    guint interval_ms)
{
    char *pattern = NULL;
    int rc;

    /* Create a pattern that matches desired attributes */

    if (rsc == NULL) {
        pattern = strdup(ATTRD_RE_CLEAR_ALL);
    } else if (op == NULL) {
        pattern = crm_strdup_printf(ATTRD_RE_CLEAR_ONE, rsc);
    } else {
        pattern = crm_strdup_printf(ATTRD_RE_CLEAR_OP, rsc, op, interval_ms);
    }

    /* Compile pattern into regular expression */
    crm_trace("Clearing attributes matching %s", pattern);
    rc = regcomp(regex, pattern, REG_EXTENDED|REG_NOSUB);
    free(pattern);

    return (rc == 0)? pcmk_ok : -EINVAL;
}

void
attrd_free_attribute_value(gpointer data)
{
    attribute_value_t *v = data;

    free(v->nodename);
    free(v->current);
    free(v->requested);
    free(v);
}

void
attrd_free_attribute(gpointer data)
{
    attribute_t *a = data;
    if(a) {
        free(a->id);
        free(a->set_id);
        free(a->set_type);
        free(a->uuid);
        free(a->user);

        mainloop_timer_del(a->timer);
        g_hash_table_destroy(a->values);

        free(a);
    }
}

/*!
 * \internal
 * \brief When a peer node leaves the cluster, stop tracking its protocol version.
 *
 * \param[in] host  The peer node's uname to be removed
 */
void
attrd_remove_peer_protocol_ver(const char *host)
{
    if (peer_protocol_vers != NULL) {
        g_hash_table_remove(peer_protocol_vers, host);
    }
}

/*!
 * \internal
 * \brief When a peer node broadcasts a message with its protocol version, keep
 *        track of that information.
 *
 * We keep track of each peer's protocol version so we know which peers to
 * expect confirmation messages from when handling cluster-wide sync points.
 * We additionally keep track of the lowest protocol version supported by all
 * peers so we know when we can send IPC messages containing more than one
 * request.
 *
 * \param[in] host  The peer node's uname to be tracked
 * \param[in] value The peer node's protocol version
 */
void
attrd_update_minimum_protocol_ver(const char *host, const char *value)
{
    int ver;

    if (peer_protocol_vers == NULL) {
        peer_protocol_vers = pcmk__strkey_table(free, NULL);
    }

    pcmk__scan_min_int(value, &ver, 0);

    if (ver > 0) {
        char *host_name = strdup(host);

        /* Record the peer attrd's protocol version. */
        CRM_ASSERT(host_name != NULL);
        g_hash_table_insert(peer_protocol_vers, host_name, GINT_TO_POINTER(ver));

        /* If the protocol version is a new minimum, record it as such. */
        if (minimum_protocol_version == -1 || ver < minimum_protocol_version) {
            minimum_protocol_version = ver;
            crm_trace("Set minimum attrd protocol version to %d",
                      minimum_protocol_version);
        }
    }
}

void
attrd_copy_xml_attributes(xmlNode *src, xmlNode *dest)
{
    /* Copy attributes from the wrapper parent node into the child node.
     * We can't just use copy_in_properties because we want to skip any
     * attributes that are already set on the child.  For instance, if
     * we were told to use a specific node, there will already be a node
     * attribute on the child.  Copying the parent's node attribute over
     * could result in the wrong value.
     */
    for (xmlAttrPtr a = pcmk__xe_first_attr(src); a != NULL; a = a->next) {
        const char *p_name = (const char *) a->name;
        const char *p_value = ((a == NULL) || (a->children == NULL)) ? NULL :
                              (const char *) a->children->content;

        if (crm_element_value(dest, p_name) == NULL) {
            crm_xml_add(dest, p_name, p_value);
        }
    }
}