root/daemons/attrd/attrd_utils.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. attrd_set_requesting_shutdown
  2. attrd_clear_requesting_shutdown
  3. attrd_requesting_shutdown
  4. attrd_shutting_down
  5. attrd_shutdown
  6. attrd_init_mainloop
  7. attrd_run_mainloop
  8. attrd_cib_disconnect
  9. attrd_cib_replaced_cb
  10. attrd_value_needs_expansion
  11. attrd_expand_value
  12. attrd_failure_regex
  13. attrd_free_attribute_value
  14. attrd_free_attribute
  15. attrd_remove_peer_protocol_ver
  16. attrd_update_minimum_protocol_ver
  17. attrd_copy_xml_attributes

   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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  53 {
  54     requesting_shutdown = false;
  55 }
  56 
  57 /*!
  58  * \internal
  59  * \brief Check whether we're currently requesting shutdown
  60  *
  61  * \return true if requesting shutdown, false otherwise
  62  */
  63 bool
  64 attrd_requesting_shutdown(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  65 {
  66     return requesting_shutdown;
  67 }
  68 
  69 /*!
  70  * \internal
  71  * \brief Check whether we're currently shutting down
  72  *
  73  * \return true if shutting down, false otherwise
  74  */
  75 bool
  76 attrd_shutting_down(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  77 {
  78     return shutting_down;
  79 }
  80 
  81 /*!
  82  * \internal
  83  * \brief  Exit (using mainloop or not, as appropriate)
  84  *
  85  * \param[in] nsig  Ignored
  86  */
  87 void
  88 attrd_shutdown(int nsig)
     /* [previous][next][first][last][top][bottom][index][help] */
  89 {
  90     // Tell various functions not to do anthing
  91     shutting_down = true;
  92 
  93     // Don't respond to signals while shutting down
  94     mainloop_destroy_signal(SIGTERM);
  95     mainloop_destroy_signal(SIGCHLD);
  96     mainloop_destroy_signal(SIGPIPE);
  97     mainloop_destroy_signal(SIGUSR1);
  98     mainloop_destroy_signal(SIGUSR2);
  99     mainloop_destroy_signal(SIGTRAP);
 100 
 101     attrd_free_waitlist();
 102     attrd_free_confirmations();
 103 
 104     if (peer_protocol_vers != NULL) {
 105         g_hash_table_destroy(peer_protocol_vers);
 106         peer_protocol_vers = NULL;
 107     }
 108 
 109     if ((mloop == NULL) || !g_main_loop_is_running(mloop)) {
 110         /* If there's no main loop active, just exit. This should be possible
 111          * only if we get SIGTERM in brief windows at start-up and shutdown.
 112          */
 113         crm_exit(CRM_EX_OK);
 114     } else {
 115         g_main_loop_quit(mloop);
 116         g_main_loop_unref(mloop);
 117     }
 118 }
 119 
 120 /*!
 121  * \internal
 122  * \brief Create a main loop for attrd
 123  */
 124 void
 125 attrd_init_mainloop(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 126 {
 127     mloop = g_main_loop_new(NULL, FALSE);
 128 }
 129 
 130 /*!
 131  * \internal
 132  * \brief Run attrd main loop
 133  */
 134 void
 135 attrd_run_mainloop(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 136 {
 137     g_main_loop_run(mloop);
 138 }
 139 
 140 void
 141 attrd_cib_disconnect(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 142 {
 143     CRM_CHECK(the_cib != NULL, return);
 144     the_cib->cmds->del_notify_callback(the_cib, T_CIB_REPLACE_NOTIFY, attrd_cib_replaced_cb);
 145     the_cib->cmds->del_notify_callback(the_cib, T_CIB_DIFF_NOTIFY, attrd_cib_updated_cb);
 146     cib__clean_up_connection(&the_cib);
 147 }
 148 
 149 void
 150 attrd_cib_replaced_cb(const char *event, xmlNode * msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 151 {
 152     int change_section = cib_change_section_nodes | cib_change_section_status | cib_change_section_alerts;
 153 
 154     if (attrd_requesting_shutdown() || attrd_shutting_down()) {
 155         return;
 156     }
 157 
 158     crm_element_value_int(msg, F_CIB_CHANGE_SECTION, &change_section);
 159 
 160     if (attrd_election_won()) {
 161         if (change_section & (cib_change_section_nodes | cib_change_section_status)) {
 162             crm_notice("Updating all attributes after %s event", event);
 163             attrd_write_attributes(true, false);
 164         }
 165     }
 166 
 167     if (change_section & cib_change_section_alerts) {
 168         // Check for changes in alerts
 169         mainloop_set_trigger(attrd_config_read);
 170     }
 171 }
 172 
 173 /* strlen("value") */
 174 #define plus_plus_len (5)
 175 
 176 /*!
 177  * \internal
 178  * \brief  Check whether an attribute value should be expanded
 179  *
 180  * \param[in] value  Attribute value to check
 181  *
 182  * \return true if value needs expansion, false otherwise
 183  */
 184 bool
 185 attrd_value_needs_expansion(const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 186 {
 187     return ((strlen(value) >= (plus_plus_len + 2))
 188            && (value[plus_plus_len] == '+')
 189            && ((value[plus_plus_len + 1] == '+')
 190                || (value[plus_plus_len + 1] == '=')));
 191 }
 192 
 193 /*!
 194  * \internal
 195  * \brief Expand an increment expression into an integer
 196  *
 197  * \param[in] value      Attribute increment expression to expand
 198  * \param[in] old_value  Previous value of attribute
 199  *
 200  * \return Expanded value
 201  */
 202 int
 203 attrd_expand_value(const char *value, const char *old_value)
     /* [previous][next][first][last][top][bottom][index][help] */
 204 {
 205     int offset = 1;
 206     int int_value = char2score(old_value);
 207 
 208     if (value[plus_plus_len + 1] != '+') {
 209         const char *offset_s = value + (plus_plus_len + 2);
 210 
 211         offset = char2score(offset_s);
 212     }
 213     int_value += offset;
 214 
 215     if (int_value > INFINITY) {
 216         int_value = INFINITY;
 217     }
 218     return int_value;
 219 }
 220 
 221 /*!
 222  * \internal
 223  * \brief Create regular expression matching failure-related attributes
 224  *
 225  * \param[out] regex  Where to store created regular expression
 226  * \param[in]  rsc    Name of resource to clear (or NULL for all)
 227  * \param[in]  op     Operation to clear if rsc is specified (or NULL for all)
 228  * \param[in]  interval_ms  Interval of operation to clear if op is specified
 229  *
 230  * \return pcmk_ok on success, -EINVAL if arguments are invalid
 231  *
 232  * \note The caller is responsible for freeing the result with regfree().
 233  */
 234 int
 235 attrd_failure_regex(regex_t *regex, const char *rsc, const char *op,
     /* [previous][next][first][last][top][bottom][index][help] */
 236                     guint interval_ms)
 237 {
 238     char *pattern = NULL;
 239     int rc;
 240 
 241     /* Create a pattern that matches desired attributes */
 242 
 243     if (rsc == NULL) {
 244         pattern = strdup(ATTRD_RE_CLEAR_ALL);
 245     } else if (op == NULL) {
 246         pattern = crm_strdup_printf(ATTRD_RE_CLEAR_ONE, rsc);
 247     } else {
 248         pattern = crm_strdup_printf(ATTRD_RE_CLEAR_OP, rsc, op, interval_ms);
 249     }
 250 
 251     /* Compile pattern into regular expression */
 252     crm_trace("Clearing attributes matching %s", pattern);
 253     rc = regcomp(regex, pattern, REG_EXTENDED|REG_NOSUB);
 254     free(pattern);
 255 
 256     return (rc == 0)? pcmk_ok : -EINVAL;
 257 }
 258 
 259 void
 260 attrd_free_attribute_value(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 261 {
 262     attribute_value_t *v = data;
 263 
 264     free(v->nodename);
 265     free(v->current);
 266     free(v->requested);
 267     free(v);
 268 }
 269 
 270 void
 271 attrd_free_attribute(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 272 {
 273     attribute_t *a = data;
 274     if(a) {
 275         free(a->id);
 276         free(a->set_id);
 277         free(a->set_type);
 278         free(a->uuid);
 279         free(a->user);
 280 
 281         mainloop_timer_del(a->timer);
 282         g_hash_table_destroy(a->values);
 283 
 284         free(a);
 285     }
 286 }
 287 
 288 /*!
 289  * \internal
 290  * \brief When a peer node leaves the cluster, stop tracking its protocol version.
 291  *
 292  * \param[in] host  The peer node's uname to be removed
 293  */
 294 void
 295 attrd_remove_peer_protocol_ver(const char *host)
     /* [previous][next][first][last][top][bottom][index][help] */
 296 {
 297     if (peer_protocol_vers != NULL) {
 298         g_hash_table_remove(peer_protocol_vers, host);
 299     }
 300 }
 301 
 302 /*!
 303  * \internal
 304  * \brief When a peer node broadcasts a message with its protocol version, keep
 305  *        track of that information.
 306  *
 307  * We keep track of each peer's protocol version so we know which peers to
 308  * expect confirmation messages from when handling cluster-wide sync points.
 309  * We additionally keep track of the lowest protocol version supported by all
 310  * peers so we know when we can send IPC messages containing more than one
 311  * request.
 312  *
 313  * \param[in] host  The peer node's uname to be tracked
 314  * \param[in] value The peer node's protocol version
 315  */
 316 void
 317 attrd_update_minimum_protocol_ver(const char *host, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 318 {
 319     int ver;
 320 
 321     if (peer_protocol_vers == NULL) {
 322         peer_protocol_vers = pcmk__strkey_table(free, NULL);
 323     }
 324 
 325     pcmk__scan_min_int(value, &ver, 0);
 326 
 327     if (ver > 0) {
 328         char *host_name = strdup(host);
 329 
 330         /* Record the peer attrd's protocol version. */
 331         CRM_ASSERT(host_name != NULL);
 332         g_hash_table_insert(peer_protocol_vers, host_name, GINT_TO_POINTER(ver));
 333 
 334         /* If the protocol version is a new minimum, record it as such. */
 335         if (minimum_protocol_version == -1 || ver < minimum_protocol_version) {
 336             minimum_protocol_version = ver;
 337             crm_trace("Set minimum attrd protocol version to %d",
 338                       minimum_protocol_version);
 339         }
 340     }
 341 }
 342 
 343 void
 344 attrd_copy_xml_attributes(xmlNode *src, xmlNode *dest)
     /* [previous][next][first][last][top][bottom][index][help] */
 345 {
 346     /* Copy attributes from the wrapper parent node into the child node.
 347      * We can't just use copy_in_properties because we want to skip any
 348      * attributes that are already set on the child.  For instance, if
 349      * we were told to use a specific node, there will already be a node
 350      * attribute on the child.  Copying the parent's node attribute over
 351      * could result in the wrong value.
 352      */
 353     for (xmlAttrPtr a = pcmk__xe_first_attr(src); a != NULL; a = a->next) {
 354         const char *p_name = (const char *) a->name;
 355         const char *p_value = ((a == NULL) || (a->children == NULL)) ? NULL :
 356                               (const char *) a->children->content;
 357 
 358         if (crm_element_value(dest, p_name) == NULL) {
 359             crm_xml_add(dest, p_name, p_value);
 360         }
 361     }
 362 }

/* [previous][next][first][last][top][bottom][index][help] */