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_ipc_accept
  9. attrd_ipc_closed
  10. attrd_ipc_destroy
  11. attrd_init_ipc
  12. attrd_cib_disconnect
  13. attrd_cib_replaced_cb
  14. attrd_value_needs_expansion
  15. attrd_expand_value
  16. attrd_failure_regex

   1 /*
   2  * Copyright 2004-2022 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 
  23 #include "pacemaker-attrd.h"
  24 
  25 cib_t *the_cib = NULL;
  26 
  27 static bool requesting_shutdown = FALSE;
  28 static bool shutting_down = FALSE;
  29 static GMainLoop *mloop = NULL;
  30 
  31 /*!
  32  * \internal
  33  * \brief  Set requesting_shutdown state
  34  */
  35 void
  36 attrd_set_requesting_shutdown()
     /* [previous][next][first][last][top][bottom][index][help] */
  37 {
  38     requesting_shutdown = TRUE;
  39 }
  40 
  41 /*!
  42  * \internal
  43  * \brief  Clear requesting_shutdown state
  44  */
  45 void
  46 attrd_clear_requesting_shutdown()
     /* [previous][next][first][last][top][bottom][index][help] */
  47 {
  48     requesting_shutdown = FALSE;
  49 }
  50 
  51 /*!
  52  * \internal
  53  * \brief Check whether we're currently requesting shutdown
  54  *
  55  * \return TRUE if requesting shutdown, FALSE otherwise
  56  */
  57 gboolean
  58 attrd_requesting_shutdown()
     /* [previous][next][first][last][top][bottom][index][help] */
  59 {
  60     return requesting_shutdown;
  61 }
  62 
  63 /*!
  64  * \internal
  65  * \brief Check whether we're currently shutting down
  66  *
  67  * \return TRUE if shutting down, FALSE otherwise
  68  */
  69 gboolean
  70 attrd_shutting_down()
     /* [previous][next][first][last][top][bottom][index][help] */
  71 {
  72     return shutting_down;
  73 }
  74 
  75 /*!
  76  * \internal
  77  * \brief  Exit (using mainloop or not, as appropriate)
  78  *
  79  * \param[in] nsig  Ignored
  80  */
  81 void
  82 attrd_shutdown(int nsig)
     /* [previous][next][first][last][top][bottom][index][help] */
  83 {
  84     // Tell various functions not to do anthing
  85     shutting_down = TRUE;
  86 
  87     // Don't respond to signals while shutting down
  88     mainloop_destroy_signal(SIGTERM);
  89     mainloop_destroy_signal(SIGCHLD);
  90     mainloop_destroy_signal(SIGPIPE);
  91     mainloop_destroy_signal(SIGUSR1);
  92     mainloop_destroy_signal(SIGUSR2);
  93     mainloop_destroy_signal(SIGTRAP);
  94 
  95     if ((mloop == NULL) || !g_main_loop_is_running(mloop)) {
  96         /* If there's no main loop active, just exit. This should be possible
  97          * only if we get SIGTERM in brief windows at start-up and shutdown.
  98          */
  99         crm_exit(CRM_EX_OK);
 100     } else {
 101         g_main_loop_quit(mloop);
 102         g_main_loop_unref(mloop);
 103     }
 104 }
 105 
 106 /*!
 107  * \internal
 108  * \brief Create a main loop for attrd
 109  */
 110 void
 111 attrd_init_mainloop()
     /* [previous][next][first][last][top][bottom][index][help] */
 112 {
 113     mloop = g_main_loop_new(NULL, FALSE);
 114 }
 115 
 116 /*!
 117  * \internal
 118  * \brief Run attrd main loop
 119  */
 120 void
 121 attrd_run_mainloop()
     /* [previous][next][first][last][top][bottom][index][help] */
 122 {
 123     g_main_loop_run(mloop);
 124 }
 125 
 126 /*!
 127  * \internal
 128  * \brief Accept a new client IPC connection
 129  *
 130  * \param[in] c    New connection
 131  * \param[in] uid  Client user id
 132  * \param[in] gid  Client group id
 133  *
 134  * \return pcmk_ok on success, -errno otherwise
 135  */
 136 static int32_t
 137 attrd_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 138 {
 139     crm_trace("New client connection %p", c);
 140     if (shutting_down) {
 141         crm_info("Ignoring new connection from pid %d during shutdown",
 142                  pcmk__client_pid(c));
 143         return -EPERM;
 144     }
 145 
 146     if (pcmk__new_client(c, uid, gid) == NULL) {
 147         return -EIO;
 148     }
 149     return pcmk_ok;
 150 }
 151 
 152 /*!
 153  * \internal
 154  * \brief Destroy a client IPC connection
 155  *
 156  * \param[in] c  Connection to destroy
 157  *
 158  * \return FALSE (i.e. do not re-run this callback)
 159  */
 160 static int32_t
 161 attrd_ipc_closed(qb_ipcs_connection_t *c)
     /* [previous][next][first][last][top][bottom][index][help] */
 162 {
 163     pcmk__client_t *client = pcmk__find_client(c);
 164 
 165     if (client == NULL) {
 166         crm_trace("Ignoring request to clean up unknown connection %p", c);
 167     } else {
 168         crm_trace("Cleaning up closed client connection %p", c);
 169         pcmk__free_client(client);
 170     }
 171     return FALSE;
 172 }
 173 
 174 /*!
 175  * \internal
 176  * \brief Destroy a client IPC connection
 177  *
 178  * \param[in] c  Connection to destroy
 179  *
 180  * \note We handle a destroyed connection the same as a closed one,
 181  *       but we need a separate handler because the return type is different.
 182  */
 183 static void
 184 attrd_ipc_destroy(qb_ipcs_connection_t *c)
     /* [previous][next][first][last][top][bottom][index][help] */
 185 {
 186     crm_trace("Destroying client connection %p", c);
 187     attrd_ipc_closed(c);
 188 }
 189 
 190 /*!
 191  * \internal
 192  * \brief Set up attrd IPC communication
 193  *
 194  * \param[out] ipcs         Will be set to newly allocated server connection
 195  * \param[in]  dispatch_fn  Handler for new messages on connection
 196  */
 197 void
 198 attrd_init_ipc(qb_ipcs_service_t **ipcs, qb_ipcs_msg_process_fn dispatch_fn)
     /* [previous][next][first][last][top][bottom][index][help] */
 199 {
 200 
 201     static struct qb_ipcs_service_handlers ipc_callbacks = {
 202         .connection_accept = attrd_ipc_accept,
 203         .connection_created = NULL,
 204         .msg_process = NULL,
 205         .connection_closed = attrd_ipc_closed,
 206         .connection_destroyed = attrd_ipc_destroy
 207     };
 208 
 209     ipc_callbacks.msg_process = dispatch_fn;
 210     pcmk__serve_attrd_ipc(ipcs, &ipc_callbacks);
 211 }
 212 
 213 void
 214 attrd_cib_disconnect()
     /* [previous][next][first][last][top][bottom][index][help] */
 215 {
 216     CRM_CHECK(the_cib != NULL, return);
 217     the_cib->cmds->del_notify_callback(the_cib, T_CIB_REPLACE_NOTIFY, attrd_cib_replaced_cb);
 218     the_cib->cmds->del_notify_callback(the_cib, T_CIB_DIFF_NOTIFY, attrd_cib_updated_cb);
 219     cib__clean_up_connection(&the_cib);
 220 }
 221 
 222 void
 223 attrd_cib_replaced_cb(const char *event, xmlNode * msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 224 {
 225     int change_section = cib_change_section_nodes | cib_change_section_status | cib_change_section_alerts;
 226 
 227     if (attrd_requesting_shutdown() || attrd_shutting_down()) {
 228         return;
 229     }
 230 
 231     crm_element_value_int(msg, F_CIB_CHANGE_SECTION, &change_section);
 232 
 233     if (attrd_election_won()) {
 234         if (change_section & (cib_change_section_nodes | cib_change_section_status)) {
 235             crm_notice("Updating all attributes after %s event", event);
 236             write_attributes(TRUE, FALSE);
 237         }
 238     }
 239 
 240     if (change_section & cib_change_section_alerts) {
 241         // Check for changes in alerts
 242         mainloop_set_trigger(attrd_config_read);
 243     }
 244 }
 245 
 246 /* strlen("value") */
 247 #define plus_plus_len (5)
 248 
 249 /*!
 250  * \internal
 251  * \brief  Check whether an attribute value should be expanded
 252  *
 253  * \param[in] value  Attribute value to check
 254  *
 255  * \return TRUE if value needs expansion, FALSE otherwise
 256  */
 257 gboolean
 258 attrd_value_needs_expansion(const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 259 {
 260     return ((strlen(value) >= (plus_plus_len + 2))
 261            && (value[plus_plus_len] == '+')
 262            && ((value[plus_plus_len + 1] == '+')
 263                || (value[plus_plus_len + 1] == '=')));
 264 }
 265 
 266 /*!
 267  * \internal
 268  * \brief Expand an increment expression into an integer
 269  *
 270  * \param[in] value      Attribute increment expression to expand
 271  * \param[in] old_value  Previous value of attribute
 272  *
 273  * \return Expanded value
 274  */
 275 int
 276 attrd_expand_value(const char *value, const char *old_value)
     /* [previous][next][first][last][top][bottom][index][help] */
 277 {
 278     int offset = 1;
 279     int int_value = char2score(old_value);
 280 
 281     if (value[plus_plus_len + 1] != '+') {
 282         const char *offset_s = value + (plus_plus_len + 2);
 283 
 284         offset = char2score(offset_s);
 285     }
 286     int_value += offset;
 287 
 288     if (int_value > INFINITY) {
 289         int_value = INFINITY;
 290     }
 291     return int_value;
 292 }
 293 
 294 /*!
 295  * \internal
 296  * \brief Create regular expression matching failure-related attributes
 297  *
 298  * \param[out] regex  Where to store created regular expression
 299  * \param[in]  rsc    Name of resource to clear (or NULL for all)
 300  * \param[in]  op     Operation to clear if rsc is specified (or NULL for all)
 301  * \param[in]  interval_ms  Interval of operation to clear if op is specified
 302  *
 303  * \return pcmk_ok on success, -EINVAL if arguments are invalid
 304  *
 305  * \note The caller is responsible for freeing the result with regfree().
 306  */
 307 int
 308 attrd_failure_regex(regex_t *regex, const char *rsc, const char *op,
     /* [previous][next][first][last][top][bottom][index][help] */
 309                     guint interval_ms)
 310 {
 311     char *pattern = NULL;
 312     int rc;
 313 
 314     /* Create a pattern that matches desired attributes */
 315 
 316     if (rsc == NULL) {
 317         pattern = strdup(ATTRD_RE_CLEAR_ALL);
 318     } else if (op == NULL) {
 319         pattern = crm_strdup_printf(ATTRD_RE_CLEAR_ONE, rsc);
 320     } else {
 321         pattern = crm_strdup_printf(ATTRD_RE_CLEAR_OP, rsc, op, interval_ms);
 322     }
 323 
 324     /* Compile pattern into regular expression */
 325     crm_trace("Clearing attributes matching %s", pattern);
 326     rc = regcomp(regex, pattern, REG_EXTENDED|REG_NOSUB);
 327     free(pattern);
 328 
 329     return (rc == 0)? pcmk_ok : -EINVAL;
 330 }

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