root/daemons/attrd/pacemaker-attrd.c

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

DEFINITIONS

This source file includes following definitions.
  1. attrd_cib_destroy_cb
  2. attrd_erase_cb
  3. attrd_erase_attrs
  4. attrd_cib_connect
  5. attrd_cib_init
  6. ipc_already_running
  7. build_arg_context
  8. main

   1 /*
   2  * Copyright 2013-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 <sys/param.h>
  13 #include <stdio.h>
  14 #include <sys/types.h>
  15 #include <sys/stat.h>
  16 #include <unistd.h>
  17 
  18 #include <stdlib.h>
  19 #include <errno.h>
  20 #include <fcntl.h>
  21 
  22 #include <crm/crm.h>
  23 #include <crm/cib/internal.h>
  24 #include <crm/msg_xml.h>
  25 #include <crm/pengine/rules.h>
  26 #include <crm/common/cmdline_internal.h>
  27 #include <crm/common/iso8601.h>
  28 #include <crm/common/ipc.h>
  29 #include <crm/common/ipc_internal.h>
  30 #include <crm/common/output_internal.h>
  31 #include <crm/common/xml.h>
  32 #include <crm/cluster/internal.h>
  33 
  34 #include <crm/common/attrd_internal.h>
  35 #include "pacemaker-attrd.h"
  36 
  37 #define SUMMARY "daemon for managing Pacemaker node attributes"
  38 
  39 static pcmk__output_t *out = NULL;
  40 
  41 static pcmk__supported_format_t formats[] = {
  42     PCMK__SUPPORTED_FORMAT_NONE,
  43     PCMK__SUPPORTED_FORMAT_TEXT,
  44     PCMK__SUPPORTED_FORMAT_XML,
  45     { NULL, NULL, NULL }
  46 };
  47 
  48 lrmd_t *the_lrmd = NULL;
  49 crm_cluster_t *attrd_cluster = NULL;
  50 crm_trigger_t *attrd_config_read = NULL;
  51 crm_exit_t attrd_exit_status = CRM_EX_OK;
  52 
  53 static void
  54 attrd_cib_destroy_cb(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  55 {
  56     cib_t *conn = user_data;
  57 
  58     conn->cmds->signoff(conn);  /* Ensure IPC is cleaned up */
  59 
  60     if (attrd_shutting_down()) {
  61         crm_info("Connection disconnection complete");
  62 
  63     } else {
  64         /* eventually this should trigger a reconnect, not a shutdown */
  65         crm_crit("Lost connection to the CIB manager, shutting down");
  66         attrd_exit_status = CRM_EX_DISCONNECT;
  67         attrd_shutdown(0);
  68     }
  69 
  70     return;
  71 }
  72 
  73 static void
  74 attrd_erase_cb(xmlNode *msg, int call_id, int rc, xmlNode *output,
     /* [previous][next][first][last][top][bottom][index][help] */
  75                void *user_data)
  76 {
  77     do_crm_log_unlikely((rc? LOG_NOTICE : LOG_DEBUG),
  78                         "Cleared transient attributes: %s "
  79                         CRM_XS " xpath=%s rc=%d",
  80                         pcmk_strerror(rc), (char *) user_data, rc);
  81 }
  82 
  83 #define XPATH_TRANSIENT "//node_state[@uname='%s']/" XML_TAG_TRANSIENT_NODEATTRS
  84 
  85 /*!
  86  * \internal
  87  * \brief Wipe all transient attributes for this node from the CIB
  88  *
  89  * Clear any previous transient node attributes from the CIB. This is
  90  * normally done by the DC's controller when this node leaves the cluster, but
  91  * this handles the case where the node restarted so quickly that the
  92  * cluster layer didn't notice.
  93  *
  94  * \todo If pacemaker-attrd respawns after crashing (see PCMK_respawned),
  95  *       ideally we'd skip this and sync our attributes from the writer.
  96  *       However, currently we reject any values for us that the writer has, in
  97  *       attrd_peer_update().
  98  */
  99 static void
 100 attrd_erase_attrs(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 101 {
 102     int call_id;
 103     char *xpath = crm_strdup_printf(XPATH_TRANSIENT, attrd_cluster->uname);
 104 
 105     crm_info("Clearing transient attributes from CIB " CRM_XS " xpath=%s",
 106              xpath);
 107 
 108     call_id = the_cib->cmds->remove(the_cib, xpath, NULL,
 109                                     cib_quorum_override | cib_xpath);
 110     the_cib->cmds->register_callback_full(the_cib, call_id, 120, FALSE, xpath,
 111                                           "attrd_erase_cb", attrd_erase_cb,
 112                                           free);
 113 }
 114 
 115 static int
 116 attrd_cib_connect(int max_retry)
     /* [previous][next][first][last][top][bottom][index][help] */
 117 {
 118     static int attempts = 0;
 119 
 120     int rc = -ENOTCONN;
 121 
 122     the_cib = cib_new();
 123     if (the_cib == NULL) {
 124         return -ENOTCONN;
 125     }
 126 
 127     do {
 128         if(attempts > 0) {
 129             sleep(attempts);
 130         }
 131 
 132         attempts++;
 133         crm_debug("Connection attempt %d to the CIB manager", attempts);
 134         rc = the_cib->cmds->signon(the_cib, T_ATTRD, cib_command);
 135 
 136     } while(rc != pcmk_ok && attempts < max_retry);
 137 
 138     if (rc != pcmk_ok) {
 139         crm_err("Connection to the CIB manager failed: %s " CRM_XS " rc=%d",
 140                 pcmk_strerror(rc), rc);
 141         goto cleanup;
 142     }
 143 
 144     crm_debug("Connected to the CIB manager after %d attempts", attempts);
 145 
 146     rc = the_cib->cmds->set_connection_dnotify(the_cib, attrd_cib_destroy_cb);
 147     if (rc != pcmk_ok) {
 148         crm_err("Could not set disconnection callback");
 149         goto cleanup;
 150     }
 151 
 152     rc = the_cib->cmds->add_notify_callback(the_cib, T_CIB_REPLACE_NOTIFY, attrd_cib_replaced_cb);
 153     if(rc != pcmk_ok) {
 154         crm_err("Could not set CIB notification callback");
 155         goto cleanup;
 156     }
 157 
 158     rc = the_cib->cmds->add_notify_callback(the_cib, T_CIB_DIFF_NOTIFY, attrd_cib_updated_cb);
 159     if (rc != pcmk_ok) {
 160         crm_err("Could not set CIB notification callback (update)");
 161         goto cleanup;
 162     }
 163 
 164     return pcmk_ok;
 165 
 166   cleanup:
 167     cib__clean_up_connection(&the_cib);
 168     return -ENOTCONN;
 169 }
 170 
 171 /*!
 172  * \internal
 173  * \brief Prepare the CIB after cluster is connected
 174  */
 175 static void
 176 attrd_cib_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 177 {
 178     // We have no attribute values in memory, wipe the CIB to match
 179     attrd_erase_attrs();
 180 
 181     // Set a trigger for reading the CIB (for the alerts section)
 182     attrd_config_read = mainloop_add_trigger(G_PRIORITY_HIGH, attrd_read_options, NULL);
 183 
 184     // Always read the CIB at start-up
 185     mainloop_set_trigger(attrd_config_read);
 186 }
 187 
 188 static bool
 189 ipc_already_running(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 190 {
 191     pcmk_ipc_api_t *old_instance = NULL;
 192     int rc = pcmk_rc_ok;
 193 
 194     rc = pcmk_new_ipc_api(&old_instance, pcmk_ipc_attrd);
 195     if (rc != pcmk_rc_ok) {
 196         return false;
 197     }
 198 
 199     rc = pcmk_connect_ipc(old_instance, pcmk_ipc_dispatch_sync);
 200     if (rc != pcmk_rc_ok) {
 201         pcmk_free_ipc_api(old_instance);
 202         return false;
 203     }
 204 
 205     pcmk_disconnect_ipc(old_instance);
 206     pcmk_free_ipc_api(old_instance);
 207     return true;
 208 }
 209 
 210 static GOptionContext *
 211 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
     /* [previous][next][first][last][top][bottom][index][help] */
 212     return pcmk__build_arg_context(args, "text (default), xml", group, NULL);
 213 }
 214 
 215 int
 216 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 217 {
 218     int rc = pcmk_rc_ok;
 219 
 220     GError *error = NULL;
 221     bool initialized = false;
 222 
 223     GOptionGroup *output_group = NULL;
 224     pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
 225     gchar **processed_args = pcmk__cmdline_preproc(argv, NULL);
 226     GOptionContext *context = build_arg_context(args, &output_group);
 227 
 228     attrd_init_mainloop();
 229     crm_log_preinit(NULL, argc, argv);
 230     mainloop_add_signal(SIGTERM, attrd_shutdown);
 231 
 232     pcmk__register_formats(output_group, formats);
 233     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
 234         attrd_exit_status = CRM_EX_USAGE;
 235         goto done;
 236     }
 237 
 238     rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
 239     if ((rc != pcmk_rc_ok) || (out == NULL)) {
 240         attrd_exit_status = CRM_EX_ERROR;
 241         g_set_error(&error, PCMK__EXITC_ERROR, attrd_exit_status,
 242                     "Error creating output format %s: %s",
 243                     args->output_ty, pcmk_rc_str(rc));
 244         goto done;
 245     }
 246 
 247     if (args->version) {
 248         out->version(out, false);
 249         goto done;
 250     }
 251 
 252     initialized = true;
 253 
 254     crm_log_init(T_ATTRD, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
 255     crm_notice("Starting Pacemaker node attribute manager");
 256 
 257     if (ipc_already_running()) {
 258         crm_err("pacemaker-attrd is already active, aborting startup");
 259         crm_exit(CRM_EX_OK);
 260     }
 261 
 262     attributes = pcmk__strkey_table(NULL, attrd_free_attribute);
 263 
 264     /* Connect to the CIB before connecting to the cluster or listening for IPC.
 265      * This allows us to assume the CIB is connected whenever we process a
 266      * cluster or IPC message (which also avoids start-up race conditions).
 267      */
 268     if (attrd_cib_connect(30) != pcmk_ok) {
 269         attrd_exit_status = CRM_EX_FATAL;
 270         goto done;
 271     }
 272     crm_info("CIB connection active");
 273 
 274     if (attrd_cluster_connect() != pcmk_ok) {
 275         attrd_exit_status = CRM_EX_FATAL;
 276         goto done;
 277     }
 278     crm_info("Cluster connection active");
 279 
 280     // Initialization that requires the cluster to be connected
 281     attrd_election_init();
 282     attrd_cib_init();
 283 
 284     /* Set a private attribute for ourselves with the protocol version we
 285      * support. This lets all nodes determine the minimum supported version
 286      * across all nodes. It also ensures that the writer learns our node name,
 287      * so it can send our attributes to the CIB.
 288      */
 289     attrd_broadcast_protocol();
 290 
 291     attrd_init_ipc();
 292     crm_notice("Pacemaker node attribute manager successfully started and accepting connections");
 293     attrd_run_mainloop();
 294 
 295   done:
 296     if (initialized) {
 297         crm_info("Shutting down attribute manager");
 298 
 299         attrd_election_fini();
 300         attrd_ipc_fini();
 301         attrd_lrmd_disconnect();
 302         attrd_cib_disconnect();
 303         g_hash_table_destroy(attributes);
 304     }
 305 
 306     g_strfreev(processed_args);
 307     pcmk__free_arg_context(context);
 308 
 309     pcmk__output_and_clear_error(error, out);
 310 
 311     if (out != NULL) {
 312         out->finish(out, attrd_exit_status, true, NULL);
 313         pcmk__output_free(out);
 314     }
 315     crm_exit(attrd_exit_status);
 316 }

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