summaryrefslogtreecommitdiffstats
path: root/daemons/attrd/pacemaker-attrd.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemons/attrd/pacemaker-attrd.c')
-rw-r--r--daemons/attrd/pacemaker-attrd.c358
1 files changed, 358 insertions, 0 deletions
diff --git a/daemons/attrd/pacemaker-attrd.c b/daemons/attrd/pacemaker-attrd.c
new file mode 100644
index 0000000..037825b
--- /dev/null
+++ b/daemons/attrd/pacemaker-attrd.c
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2013-2023 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <crm/crm.h>
+#include <crm/cib/internal.h>
+#include <crm/msg_xml.h>
+#include <crm/pengine/rules.h>
+#include <crm/common/cmdline_internal.h>
+#include <crm/common/iso8601.h>
+#include <crm/common/ipc.h>
+#include <crm/common/ipc_internal.h>
+#include <crm/common/output_internal.h>
+#include <crm/common/xml.h>
+#include <crm/cluster/internal.h>
+
+#include <crm/common/attrd_internal.h>
+#include "pacemaker-attrd.h"
+
+#define SUMMARY "daemon for managing Pacemaker node attributes"
+
+gboolean stand_alone = FALSE;
+gchar **log_files = NULL;
+
+static GOptionEntry entries[] = {
+ { "stand-alone", 's', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &stand_alone,
+ "(Advanced use only) Run in stand-alone mode", NULL },
+
+ { "logfile", 'l', G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME_ARRAY,
+ &log_files, "Send logs to the additional named logfile", NULL },
+
+ { NULL }
+};
+
+static pcmk__output_t *out = NULL;
+
+static pcmk__supported_format_t formats[] = {
+ PCMK__SUPPORTED_FORMAT_NONE,
+ PCMK__SUPPORTED_FORMAT_TEXT,
+ PCMK__SUPPORTED_FORMAT_XML,
+ { NULL, NULL, NULL }
+};
+
+lrmd_t *the_lrmd = NULL;
+crm_cluster_t *attrd_cluster = NULL;
+crm_trigger_t *attrd_config_read = NULL;
+crm_exit_t attrd_exit_status = CRM_EX_OK;
+
+static void
+attrd_cib_destroy_cb(gpointer user_data)
+{
+ cib_t *conn = user_data;
+
+ conn->cmds->signoff(conn); /* Ensure IPC is cleaned up */
+
+ if (attrd_shutting_down()) {
+ crm_info("Connection disconnection complete");
+
+ } else {
+ /* eventually this should trigger a reconnect, not a shutdown */
+ crm_crit("Lost connection to the CIB manager, shutting down");
+ attrd_exit_status = CRM_EX_DISCONNECT;
+ attrd_shutdown(0);
+ }
+
+ return;
+}
+
+static void
+attrd_erase_cb(xmlNode *msg, int call_id, int rc, xmlNode *output,
+ void *user_data)
+{
+ do_crm_log_unlikely((rc? LOG_NOTICE : LOG_DEBUG),
+ "Cleared transient attributes: %s "
+ CRM_XS " xpath=%s rc=%d",
+ pcmk_strerror(rc), (char *) user_data, rc);
+}
+
+#define XPATH_TRANSIENT "//node_state[@uname='%s']/" XML_TAG_TRANSIENT_NODEATTRS
+
+/*!
+ * \internal
+ * \brief Wipe all transient attributes for this node from the CIB
+ *
+ * Clear any previous transient node attributes from the CIB. This is
+ * normally done by the DC's controller when this node leaves the cluster, but
+ * this handles the case where the node restarted so quickly that the
+ * cluster layer didn't notice.
+ *
+ * \todo If pacemaker-attrd respawns after crashing (see PCMK_respawned),
+ * ideally we'd skip this and sync our attributes from the writer.
+ * However, currently we reject any values for us that the writer has, in
+ * attrd_peer_update().
+ */
+static void
+attrd_erase_attrs(void)
+{
+ int call_id;
+ char *xpath = crm_strdup_printf(XPATH_TRANSIENT, attrd_cluster->uname);
+
+ crm_info("Clearing transient attributes from CIB " CRM_XS " xpath=%s",
+ xpath);
+
+ call_id = the_cib->cmds->remove(the_cib, xpath, NULL, cib_xpath);
+ the_cib->cmds->register_callback_full(the_cib, call_id, 120, FALSE, xpath,
+ "attrd_erase_cb", attrd_erase_cb,
+ free);
+}
+
+static int
+attrd_cib_connect(int max_retry)
+{
+ static int attempts = 0;
+
+ int rc = -ENOTCONN;
+
+ the_cib = cib_new();
+ if (the_cib == NULL) {
+ return -ENOTCONN;
+ }
+
+ do {
+ if(attempts > 0) {
+ sleep(attempts);
+ }
+
+ attempts++;
+ crm_debug("Connection attempt %d to the CIB manager", attempts);
+ rc = the_cib->cmds->signon(the_cib, T_ATTRD, cib_command);
+
+ } while(rc != pcmk_ok && attempts < max_retry);
+
+ if (rc != pcmk_ok) {
+ crm_err("Connection to the CIB manager failed: %s " CRM_XS " rc=%d",
+ pcmk_strerror(rc), rc);
+ goto cleanup;
+ }
+
+ crm_debug("Connected to the CIB manager after %d attempts", attempts);
+
+ rc = the_cib->cmds->set_connection_dnotify(the_cib, attrd_cib_destroy_cb);
+ if (rc != pcmk_ok) {
+ crm_err("Could not set disconnection callback");
+ goto cleanup;
+ }
+
+ rc = the_cib->cmds->add_notify_callback(the_cib, T_CIB_REPLACE_NOTIFY, attrd_cib_replaced_cb);
+ if(rc != pcmk_ok) {
+ crm_err("Could not set CIB notification callback");
+ goto cleanup;
+ }
+
+ rc = the_cib->cmds->add_notify_callback(the_cib, T_CIB_DIFF_NOTIFY, attrd_cib_updated_cb);
+ if (rc != pcmk_ok) {
+ crm_err("Could not set CIB notification callback (update)");
+ goto cleanup;
+ }
+
+ return pcmk_ok;
+
+ cleanup:
+ cib__clean_up_connection(&the_cib);
+ return -ENOTCONN;
+}
+
+/*!
+ * \internal
+ * \brief Prepare the CIB after cluster is connected
+ */
+static void
+attrd_cib_init(void)
+{
+ // We have no attribute values in memory, wipe the CIB to match
+ attrd_erase_attrs();
+
+ // Set a trigger for reading the CIB (for the alerts section)
+ attrd_config_read = mainloop_add_trigger(G_PRIORITY_HIGH, attrd_read_options, NULL);
+
+ // Always read the CIB at start-up
+ mainloop_set_trigger(attrd_config_read);
+}
+
+static bool
+ipc_already_running(void)
+{
+ pcmk_ipc_api_t *old_instance = NULL;
+ int rc = pcmk_rc_ok;
+
+ rc = pcmk_new_ipc_api(&old_instance, pcmk_ipc_attrd);
+ if (rc != pcmk_rc_ok) {
+ return false;
+ }
+
+ rc = pcmk_connect_ipc(old_instance, pcmk_ipc_dispatch_sync);
+ if (rc != pcmk_rc_ok) {
+ pcmk_free_ipc_api(old_instance);
+ return false;
+ }
+
+ pcmk_disconnect_ipc(old_instance);
+ pcmk_free_ipc_api(old_instance);
+ return true;
+}
+
+static GOptionContext *
+build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
+ GOptionContext *context = NULL;
+
+ context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
+ pcmk__add_main_args(context, entries);
+ return context;
+}
+
+int
+main(int argc, char **argv)
+{
+ int rc = pcmk_rc_ok;
+
+ GError *error = NULL;
+ bool initialized = false;
+
+ GOptionGroup *output_group = NULL;
+ pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
+ gchar **processed_args = pcmk__cmdline_preproc(argv, NULL);
+ GOptionContext *context = build_arg_context(args, &output_group);
+
+ attrd_init_mainloop();
+ crm_log_preinit(NULL, argc, argv);
+ mainloop_add_signal(SIGTERM, attrd_shutdown);
+
+ pcmk__register_formats(output_group, formats);
+ if (!g_option_context_parse_strv(context, &processed_args, &error)) {
+ attrd_exit_status = CRM_EX_USAGE;
+ goto done;
+ }
+
+ rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
+ if ((rc != pcmk_rc_ok) || (out == NULL)) {
+ attrd_exit_status = CRM_EX_ERROR;
+ g_set_error(&error, PCMK__EXITC_ERROR, attrd_exit_status,
+ "Error creating output format %s: %s",
+ args->output_ty, pcmk_rc_str(rc));
+ goto done;
+ }
+
+ if (args->version) {
+ out->version(out, false);
+ goto done;
+ }
+
+ // Open additional log files
+ pcmk__add_logfiles(log_files, out);
+
+ crm_log_init(T_ATTRD, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
+ crm_notice("Starting Pacemaker node attribute manager%s",
+ stand_alone ? " in standalone mode" : "");
+
+ if (ipc_already_running()) {
+ const char *msg = "pacemaker-attrd is already active, aborting startup";
+
+ attrd_exit_status = CRM_EX_OK;
+ g_set_error(&error, PCMK__EXITC_ERROR, attrd_exit_status, "%s", msg);
+ crm_err(msg);
+ goto done;
+ }
+
+ initialized = true;
+
+ attributes = pcmk__strkey_table(NULL, attrd_free_attribute);
+
+ /* Connect to the CIB before connecting to the cluster or listening for IPC.
+ * This allows us to assume the CIB is connected whenever we process a
+ * cluster or IPC message (which also avoids start-up race conditions).
+ */
+ if (!stand_alone) {
+ if (attrd_cib_connect(30) != pcmk_ok) {
+ attrd_exit_status = CRM_EX_FATAL;
+ g_set_error(&error, PCMK__EXITC_ERROR, attrd_exit_status,
+ "Could not connect to the CIB");
+ goto done;
+ }
+ crm_info("CIB connection active");
+ }
+
+ if (attrd_cluster_connect() != pcmk_ok) {
+ attrd_exit_status = CRM_EX_FATAL;
+ g_set_error(&error, PCMK__EXITC_ERROR, attrd_exit_status,
+ "Could not connect to the cluster");
+ goto done;
+ }
+ crm_info("Cluster connection active");
+
+ // Initialization that requires the cluster to be connected
+ attrd_election_init();
+
+ if (!stand_alone) {
+ attrd_cib_init();
+ }
+
+ /* Set a private attribute for ourselves with the protocol version we
+ * support. This lets all nodes determine the minimum supported version
+ * across all nodes. It also ensures that the writer learns our node name,
+ * so it can send our attributes to the CIB.
+ */
+ attrd_broadcast_protocol();
+
+ attrd_init_ipc();
+ crm_notice("Pacemaker node attribute manager successfully started and accepting connections");
+ attrd_run_mainloop();
+
+ done:
+ if (initialized) {
+ crm_info("Shutting down attribute manager");
+
+ attrd_election_fini();
+ attrd_ipc_fini();
+ attrd_lrmd_disconnect();
+
+ if (!stand_alone) {
+ attrd_cib_disconnect();
+ }
+
+ attrd_free_waitlist();
+ pcmk_cluster_free(attrd_cluster);
+ g_hash_table_destroy(attributes);
+ }
+
+ g_strfreev(processed_args);
+ pcmk__free_arg_context(context);
+
+ g_strfreev(log_files);
+
+ pcmk__output_and_clear_error(&error, out);
+
+ if (out != NULL) {
+ out->finish(out, attrd_exit_status, true, NULL);
+ pcmk__output_free(out);
+ }
+ pcmk__unregister_formats();
+ crm_exit(attrd_exit_status);
+}