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

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