root/daemons/attrd/pacemaker-attrd.c

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

DEFINITIONS

This source file includes following definitions.
  1. attrd_cpg_dispatch
  2. attrd_cpg_destroy
  3. attrd_cib_destroy_cb
  4. attrd_erase_cb
  5. attrd_erase_attrs
  6. attrd_cib_connect
  7. attrd_cib_init
  8. attrd_ipc_dispatch
  9. attrd_ipc_fini
  10. attrd_cluster_connect
  11. main

   1 /*
   2  * Copyright 2013-2020 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/iso8601.h>
  27 #include <crm/common/ipc.h>
  28 #include <crm/common/ipc_internal.h>
  29 #include <crm/common/xml.h>
  30 #include <crm/cluster/internal.h>
  31 
  32 #include <crm/common/attrd_internal.h>
  33 #include "pacemaker-attrd.h"
  34 
  35 lrmd_t *the_lrmd = NULL;
  36 crm_cluster_t *attrd_cluster = NULL;
  37 crm_trigger_t *attrd_config_read = NULL;
  38 static crm_exit_t attrd_exit_status = CRM_EX_OK;
  39 
  40 static void
  41 attrd_cpg_dispatch(cpg_handle_t handle,
     /* [previous][next][first][last][top][bottom][index][help] */
  42                  const struct cpg_name *groupName,
  43                  uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len)
  44 {
  45     uint32_t kind = 0;
  46     xmlNode *xml = NULL;
  47     const char *from = NULL;
  48     char *data = pcmk_message_common_cs(handle, nodeid, pid, msg, &kind, &from);
  49 
  50     if(data == NULL) {
  51         return;
  52     }
  53 
  54     if (kind == crm_class_cluster) {
  55         xml = string2xml(data);
  56     }
  57 
  58     if (xml == NULL) {
  59         crm_err("Bad message of class %d received from %s[%u]: '%.120s'", kind, from, nodeid, data);
  60     } else {
  61         crm_node_t *peer = crm_get_peer(nodeid, from);
  62 
  63         attrd_peer_message(peer, xml);
  64     }
  65 
  66     free_xml(xml);
  67     free(data);
  68 }
  69 
  70 static void
  71 attrd_cpg_destroy(gpointer unused)
     /* [previous][next][first][last][top][bottom][index][help] */
  72 {
  73     if (attrd_shutting_down()) {
  74         crm_info("Corosync disconnection complete");
  75 
  76     } else {
  77         crm_crit("Lost connection to cluster layer, shutting down");
  78         attrd_exit_status = CRM_EX_DISCONNECT;
  79         attrd_shutdown(0);
  80     }
  81 }
  82 
  83 static void
  84 attrd_cib_destroy_cb(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  85 {
  86     cib_t *conn = user_data;
  87 
  88     conn->cmds->signoff(conn);  /* Ensure IPC is cleaned up */
  89 
  90     if (attrd_shutting_down()) {
  91         crm_info("Connection disconnection complete");
  92 
  93     } else {
  94         /* eventually this should trigger a reconnect, not a shutdown */
  95         crm_crit("Lost connection to the CIB manager, shutting down");
  96         attrd_exit_status = CRM_EX_DISCONNECT;
  97         attrd_shutdown(0);
  98     }
  99 
 100     return;
 101 }
 102 
 103 static void
 104 attrd_erase_cb(xmlNode *msg, int call_id, int rc, xmlNode *output,
     /* [previous][next][first][last][top][bottom][index][help] */
 105                void *user_data)
 106 {
 107     do_crm_log_unlikely((rc? LOG_NOTICE : LOG_DEBUG),
 108                         "Cleared transient attributes: %s "
 109                         CRM_XS " xpath=%s rc=%d",
 110                         pcmk_strerror(rc), (char *) user_data, rc);
 111 }
 112 
 113 #define XPATH_TRANSIENT "//node_state[@uname='%s']/" XML_TAG_TRANSIENT_NODEATTRS
 114 
 115 /*!
 116  * \internal
 117  * \brief Wipe all transient attributes for this node from the CIB
 118  *
 119  * Clear any previous transient node attributes from the CIB. This is
 120  * normally done by the DC's controller when this node leaves the cluster, but
 121  * this handles the case where the node restarted so quickly that the
 122  * cluster layer didn't notice.
 123  *
 124  * \todo If pacemaker-attrd respawns after crashing (see PCMK_respawned),
 125  *       ideally we'd skip this and sync our attributes from the writer.
 126  *       However, currently we reject any values for us that the writer has, in
 127  *       attrd_peer_update().
 128  */
 129 static void
 130 attrd_erase_attrs(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 131 {
 132     int call_id;
 133     char *xpath = crm_strdup_printf(XPATH_TRANSIENT, attrd_cluster->uname);
 134 
 135     crm_info("Clearing transient attributes from CIB " CRM_XS " xpath=%s",
 136              xpath);
 137 
 138     call_id = the_cib->cmds->remove(the_cib, xpath, NULL,
 139                                     cib_quorum_override | cib_xpath);
 140     the_cib->cmds->register_callback_full(the_cib, call_id, 120, FALSE, xpath,
 141                                           "attrd_erase_cb", attrd_erase_cb,
 142                                           free);
 143 }
 144 
 145 static int
 146 attrd_cib_connect(int max_retry)
     /* [previous][next][first][last][top][bottom][index][help] */
 147 {
 148     static int attempts = 0;
 149 
 150     int rc = -ENOTCONN;
 151 
 152     the_cib = cib_new();
 153     if (the_cib == NULL) {
 154         return -ENOTCONN;
 155     }
 156 
 157     do {
 158         if(attempts > 0) {
 159             sleep(attempts);
 160         }
 161 
 162         attempts++;
 163         crm_debug("Connection attempt %d to the CIB manager", attempts);
 164         rc = the_cib->cmds->signon(the_cib, T_ATTRD, cib_command);
 165 
 166     } while(rc != pcmk_ok && attempts < max_retry);
 167 
 168     if (rc != pcmk_ok) {
 169         crm_err("Connection to the CIB manager failed: %s " CRM_XS " rc=%d",
 170                 pcmk_strerror(rc), rc);
 171         goto cleanup;
 172     }
 173 
 174     crm_debug("Connected to the CIB manager after %d attempts", attempts);
 175 
 176     rc = the_cib->cmds->set_connection_dnotify(the_cib, attrd_cib_destroy_cb);
 177     if (rc != pcmk_ok) {
 178         crm_err("Could not set disconnection callback");
 179         goto cleanup;
 180     }
 181 
 182     rc = the_cib->cmds->add_notify_callback(the_cib, T_CIB_REPLACE_NOTIFY, attrd_cib_replaced_cb);
 183     if(rc != pcmk_ok) {
 184         crm_err("Could not set CIB notification callback");
 185         goto cleanup;
 186     }
 187 
 188     rc = the_cib->cmds->add_notify_callback(the_cib, T_CIB_DIFF_NOTIFY, attrd_cib_updated_cb);
 189     if (rc != pcmk_ok) {
 190         crm_err("Could not set CIB notification callback (update)");
 191         goto cleanup;
 192     }
 193 
 194     return pcmk_ok;
 195 
 196   cleanup:
 197     the_cib->cmds->signoff(the_cib);
 198     cib_delete(the_cib);
 199     the_cib = NULL;
 200     return -ENOTCONN;
 201 }
 202 
 203 /*!
 204  * \internal
 205  * \brief Prepare the CIB after cluster is connected
 206  */
 207 static void
 208 attrd_cib_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 209 {
 210     // We have no attribute values in memory, wipe the CIB to match
 211     attrd_erase_attrs();
 212 
 213     // Set a trigger for reading the CIB (for the alerts section)
 214     attrd_config_read = mainloop_add_trigger(G_PRIORITY_HIGH, attrd_read_options, NULL);
 215 
 216     // Always read the CIB at start-up
 217     mainloop_set_trigger(attrd_config_read);
 218 }
 219 
 220 static qb_ipcs_service_t *ipcs = NULL;
 221 
 222 static int32_t
 223 attrd_ipc_dispatch(qb_ipcs_connection_t * c, void *data, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
 224 {
 225     uint32_t id = 0;
 226     uint32_t flags = 0;
 227     pcmk__client_t *client = pcmk__find_client(c);
 228     xmlNode *xml = NULL;
 229     const char *op;
 230 
 231     // Sanity-check, and parse XML from IPC data
 232     CRM_CHECK((c != NULL) && (client != NULL), return 0);
 233     if (data == NULL) {
 234         crm_debug("No IPC data from PID %d", pcmk__client_pid(c));
 235         return 0;
 236     }
 237     xml = pcmk__client_data2xml(client, data, &id, &flags);
 238     if (xml == NULL) {
 239         crm_debug("Unrecognizable IPC data from PID %d", pcmk__client_pid(c));
 240         return 0;
 241     }
 242 
 243 #if ENABLE_ACL
 244     CRM_ASSERT(client->user != NULL);
 245     pcmk__update_acl_user(xml, PCMK__XA_ATTR_USER, client->user);
 246 #endif
 247 
 248     op = crm_element_value(xml, PCMK__XA_TASK);
 249 
 250     if (client->name == NULL) {
 251         const char *value = crm_element_value(xml, F_ORIG);
 252         client->name = crm_strdup_printf("%s.%d", value?value:"unknown", client->pid);
 253     }
 254 
 255     if (pcmk__str_eq(op, PCMK__ATTRD_CMD_PEER_REMOVE, pcmk__str_casei)) {
 256         attrd_send_ack(client, id, flags);
 257         attrd_client_peer_remove(client->name, xml);
 258 
 259     } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_CLEAR_FAILURE, pcmk__str_casei)) {
 260         attrd_send_ack(client, id, flags);
 261         attrd_client_clear_failure(xml);
 262 
 263     } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE, pcmk__str_casei)) {
 264         attrd_send_ack(client, id, flags);
 265         attrd_client_update(xml);
 266 
 267     } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE_BOTH, pcmk__str_casei)) {
 268         attrd_send_ack(client, id, flags);
 269         attrd_client_update(xml);
 270 
 271     } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE_DELAY, pcmk__str_casei)) {
 272         attrd_send_ack(client, id, flags);
 273         attrd_client_update(xml);
 274 
 275     } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_REFRESH, pcmk__str_casei)) {
 276         attrd_send_ack(client, id, flags);
 277         attrd_client_refresh();
 278 
 279     } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_QUERY, pcmk__str_casei)) {
 280         /* queries will get reply, so no ack is necessary */
 281         attrd_client_query(client, id, flags, xml);
 282 
 283     } else {
 284         crm_info("Ignoring request from client %s with unknown operation %s",
 285                  client->name, op);
 286     }
 287 
 288     free_xml(xml);
 289     return 0;
 290 }
 291 
 292 void
 293 attrd_ipc_fini(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 294 {
 295     if (ipcs != NULL) {
 296         pcmk__drop_all_clients(ipcs);
 297         qb_ipcs_destroy(ipcs);
 298         ipcs = NULL;
 299     }
 300 }
 301 
 302 static int
 303 attrd_cluster_connect(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 304 {
 305     attrd_cluster = calloc(1, sizeof(crm_cluster_t));
 306 
 307     attrd_cluster->destroy = attrd_cpg_destroy;
 308     attrd_cluster->cpg.cpg_deliver_fn = attrd_cpg_dispatch;
 309     attrd_cluster->cpg.cpg_confchg_fn = pcmk_cpg_membership;
 310 
 311     crm_set_status_callback(&attrd_peer_change_cb);
 312 
 313     if (crm_cluster_connect(attrd_cluster) == FALSE) {
 314         crm_err("Cluster connection failed");
 315         return -ENOTCONN;
 316     }
 317     return pcmk_ok;
 318 }
 319 
 320 static pcmk__cli_option_t long_options[] = {
 321     // long option, argument type, storage, short option, description, flags
 322     {
 323         "help",     no_argument, NULL, '?',
 324         "\tThis text", pcmk__option_default
 325     },
 326     {
 327         "verbose",  no_argument, NULL, 'V',
 328         "\tIncrease debug output", pcmk__option_default
 329     },
 330     { 0, 0, 0, 0 }
 331 };
 332 
 333 int
 334 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 335 {
 336     int flag = 0;
 337     int index = 0;
 338     int argerr = 0;
 339     crm_ipc_t *old_instance = NULL;
 340 
 341     attrd_init_mainloop();
 342     crm_log_preinit(NULL, argc, argv);
 343     pcmk__set_cli_options(NULL, "[options]", long_options,
 344                           "daemon for managing Pacemaker node attributes");
 345 
 346     mainloop_add_signal(SIGTERM, attrd_shutdown);
 347 
 348      while (1) {
 349         flag = pcmk__next_cli_option(argc, argv, &index, NULL);
 350         if (flag == -1)
 351             break;
 352 
 353         switch (flag) {
 354             case 'V':
 355                 crm_bump_log_level(argc, argv);
 356                 break;
 357             case 'h':          /* Help message */
 358                 pcmk__cli_help(flag, CRM_EX_OK);
 359                 break;
 360             default:
 361                 ++argerr;
 362                 break;
 363         }
 364     }
 365 
 366     if (optind > argc) {
 367         ++argerr;
 368     }
 369 
 370     if (argerr) {
 371         pcmk__cli_help('?', CRM_EX_USAGE);
 372     }
 373 
 374     crm_log_init(T_ATTRD, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
 375     crm_notice("Starting Pacemaker node attribute manager");
 376 
 377     old_instance = crm_ipc_new(T_ATTRD, 0);
 378     if (crm_ipc_connect(old_instance)) {
 379         /* IPC end-point already up */
 380         crm_ipc_close(old_instance);
 381         crm_ipc_destroy(old_instance);
 382         crm_err("pacemaker-attrd is already active, aborting startup");
 383         crm_exit(CRM_EX_OK);
 384     } else {
 385         /* not up or not authentic, we'll proceed either way */
 386         crm_ipc_destroy(old_instance);
 387         old_instance = NULL;
 388     }
 389 
 390     attributes = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, free_attribute);
 391 
 392     /* Connect to the CIB before connecting to the cluster or listening for IPC.
 393      * This allows us to assume the CIB is connected whenever we process a
 394      * cluster or IPC message (which also avoids start-up race conditions).
 395      */
 396     if (attrd_cib_connect(10) != pcmk_ok) {
 397         attrd_exit_status = CRM_EX_FATAL;
 398         goto done;
 399     }
 400     crm_info("CIB connection active");
 401 
 402     if (attrd_cluster_connect() != pcmk_ok) {
 403         attrd_exit_status = CRM_EX_FATAL;
 404         goto done;
 405     }
 406     crm_info("Cluster connection active");
 407 
 408     // Initialization that requires the cluster to be connected
 409     attrd_election_init();
 410     attrd_cib_init();
 411 
 412     /* Set a private attribute for ourselves with the protocol version we
 413      * support. This lets all nodes determine the minimum supported version
 414      * across all nodes. It also ensures that the writer learns our node name,
 415      * so it can send our attributes to the CIB.
 416      */
 417     attrd_broadcast_protocol();
 418 
 419     attrd_init_ipc(&ipcs, attrd_ipc_dispatch);
 420     crm_notice("Pacemaker node attribute manager successfully started and accepting connections");
 421     attrd_run_mainloop();
 422 
 423   done:
 424     crm_info("Shutting down attribute manager");
 425 
 426     attrd_election_fini();
 427     attrd_ipc_fini();
 428     attrd_lrmd_disconnect();
 429     attrd_cib_disconnect();
 430     g_hash_table_destroy(attributes);
 431 
 432     crm_exit(attrd_exit_status);
 433 }

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