summaryrefslogtreecommitdiffstats
path: root/daemons/based/pacemaker-based.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemons/based/pacemaker-based.c')
-rw-r--r--daemons/based/pacemaker-based.c442
1 files changed, 442 insertions, 0 deletions
diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c
new file mode 100644
index 0000000..129997e
--- /dev/null
+++ b/daemons/based/pacemaker-based.c
@@ -0,0 +1,442 @@
+/*
+ * Copyright 2004-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 <stdio.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <grp.h>
+#include <bzlib.h>
+#include <sys/types.h>
+
+#include <libxml/parser.h>
+
+#include <crm/crm.h>
+#include <crm/cib/internal.h>
+#include <crm/msg_xml.h>
+#include <crm/cluster/internal.h>
+#include <crm/common/cmdline_internal.h>
+#include <crm/common/mainloop.h>
+#include <crm/common/output_internal.h>
+#include <crm/common/xml.h>
+
+#include <pacemaker-based.h>
+
+#define SUMMARY "daemon for managing the configuration of a Pacemaker cluster"
+
+extern int init_remote_listener(int port, gboolean encrypted);
+gboolean cib_shutdown_flag = FALSE;
+int cib_status = pcmk_ok;
+
+crm_cluster_t *crm_cluster = NULL;
+
+GMainLoop *mainloop = NULL;
+gchar *cib_root = NULL;
+static gboolean preserve_status = FALSE;
+
+gboolean cib_writes_enabled = TRUE;
+
+int remote_fd = 0;
+int remote_tls_fd = 0;
+
+GHashTable *config_hash = NULL;
+GHashTable *local_notify_queue = NULL;
+
+pcmk__output_t *logger_out = NULL;
+
+static void cib_init(void);
+void cib_shutdown(int nsig);
+static bool startCib(const char *filename);
+extern int write_cib_contents(gpointer p);
+
+static crm_exit_t exit_code = CRM_EX_OK;
+
+static void
+cib_enable_writes(int nsig)
+{
+ crm_info("(Re)enabling disk writes");
+ cib_writes_enabled = TRUE;
+}
+
+/*!
+ * \internal
+ * \brief Set up options, users, and groups for stand-alone mode
+ *
+ * \param[out] error GLib error object
+ *
+ * \return Standard Pacemaker return code
+ */
+static int
+setup_stand_alone(GError **error)
+{
+ int rc = 0;
+ struct passwd *pwentry = NULL;
+
+ preserve_status = TRUE;
+ cib_writes_enabled = FALSE;
+
+ errno = 0;
+ pwentry = getpwnam(CRM_DAEMON_USER);
+ if (pwentry == NULL) {
+ exit_code = CRM_EX_FATAL;
+ if (errno != 0) {
+ g_set_error(error, PCMK__EXITC_ERROR, exit_code,
+ "Error getting password DB entry for %s: %s",
+ CRM_DAEMON_USER, strerror(errno));
+ return errno;
+ }
+ g_set_error(error, PCMK__EXITC_ERROR, exit_code,
+ "Password DB entry for '%s' not found", CRM_DAEMON_USER);
+ return ENXIO;
+ }
+
+ rc = setgid(pwentry->pw_gid);
+ if (rc < 0) {
+ exit_code = CRM_EX_FATAL;
+ g_set_error(error, PCMK__EXITC_ERROR, exit_code,
+ "Could not set group to %d: %s",
+ pwentry->pw_gid, strerror(errno));
+ return errno;
+ }
+
+ rc = initgroups(CRM_DAEMON_USER, pwentry->pw_gid);
+ if (rc < 0) {
+ exit_code = CRM_EX_FATAL;
+ g_set_error(error, PCMK__EXITC_ERROR, exit_code,
+ "Could not setup groups for user %d: %s",
+ pwentry->pw_uid, strerror(errno));
+ return errno;
+ }
+
+ rc = setuid(pwentry->pw_uid);
+ if (rc < 0) {
+ exit_code = CRM_EX_FATAL;
+ g_set_error(error, PCMK__EXITC_ERROR, exit_code,
+ "Could not set user to %d: %s",
+ pwentry->pw_uid, strerror(errno));
+ return errno;
+ }
+ return pcmk_rc_ok;
+}
+
+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 },
+
+ { "disk-writes", 'w', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
+ &cib_writes_enabled,
+ "(Advanced use only) Enable disk writes (enabled by default unless in "
+ "stand-alone mode)", NULL },
+
+ { "cib-root", 'r', G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &cib_root,
+ "(Advanced use only) Directory where the CIB XML file should be located "
+ "(default: " CRM_CONFIG_DIR ")", NULL },
+
+ { NULL }
+};
+
+static pcmk__supported_format_t formats[] = {
+ PCMK__SUPPORTED_FORMAT_NONE,
+ PCMK__SUPPORTED_FORMAT_TEXT,
+ PCMK__SUPPORTED_FORMAT_XML,
+ { NULL, NULL, NULL }
+};
+
+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,
+ "[metadata]");
+ pcmk__add_main_args(context, entries);
+ return context;
+}
+
+int
+main(int argc, char **argv)
+{
+ int rc = pcmk_rc_ok;
+ crm_ipc_t *old_instance = NULL;
+
+ pcmk__output_t *out = NULL;
+
+ GError *error = NULL;
+
+ GOptionGroup *output_group = NULL;
+ pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
+ gchar **processed_args = pcmk__cmdline_preproc(argv, "r");
+ GOptionContext *context = build_arg_context(args, &output_group);
+
+ crm_log_preinit(NULL, argc, argv);
+
+ pcmk__register_formats(output_group, formats);
+ if (!g_option_context_parse_strv(context, &processed_args, &error)) {
+ exit_code = CRM_EX_USAGE;
+ goto done;
+ }
+
+ rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
+ if (rc != pcmk_rc_ok) {
+ exit_code = CRM_EX_ERROR;
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "Error creating output format %s: %s",
+ args->output_ty, pcmk_rc_str(rc));
+ goto done;
+ }
+
+ if (args->version) {
+ out->version(out, false);
+ goto done;
+ }
+
+ rc = pcmk__log_output_new(&logger_out);
+ if (rc != pcmk_rc_ok) {
+ exit_code = CRM_EX_ERROR;
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "Error creating output format log: %s", pcmk_rc_str(rc));
+ goto done;
+ }
+ pcmk__output_set_log_level(logger_out, LOG_TRACE);
+
+ mainloop_add_signal(SIGTERM, cib_shutdown);
+ mainloop_add_signal(SIGPIPE, cib_enable_writes);
+
+ cib_writer = mainloop_add_trigger(G_PRIORITY_LOW, write_cib_contents, NULL);
+
+ if ((g_strv_length(processed_args) >= 2)
+ && pcmk__str_eq(processed_args[1], "metadata", pcmk__str_none)) {
+ cib_metadata();
+ goto done;
+ }
+
+ pcmk__cli_init_logging("pacemaker-based", args->verbosity);
+ crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
+ crm_notice("Starting Pacemaker CIB manager");
+
+ old_instance = crm_ipc_new(PCMK__SERVER_BASED_RO, 0);
+ if (old_instance == NULL) {
+ /* crm_ipc_new() will have already logged an error message with
+ * crm_err()
+ */
+ exit_code = CRM_EX_FATAL;
+ goto done;
+ }
+
+ if (crm_ipc_connect(old_instance)) {
+ /* IPC end-point already up */
+ crm_ipc_close(old_instance);
+ crm_ipc_destroy(old_instance);
+ crm_err("pacemaker-based is already active, aborting startup");
+ goto done;
+ } else {
+ /* not up or not authentic, we'll proceed either way */
+ crm_ipc_destroy(old_instance);
+ old_instance = NULL;
+ }
+
+ if (stand_alone) {
+ rc = setup_stand_alone(&error);
+ if (rc != pcmk_rc_ok) {
+ goto done;
+ }
+ }
+
+ if (cib_root == NULL) {
+ cib_root = g_strdup(CRM_CONFIG_DIR);
+ } else {
+ crm_notice("Using custom config location: %s", cib_root);
+ }
+
+ if (!pcmk__daemon_can_write(cib_root, NULL)) {
+ exit_code = CRM_EX_FATAL;
+ crm_err("Terminating due to bad permissions on %s", cib_root);
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "Bad permissions on %s (see logs for details)", cib_root);
+ goto done;
+ }
+
+ crm_peer_init();
+
+ // Read initial CIB, connect to cluster, and start IPC servers
+ cib_init();
+
+ // Run the main loop
+ mainloop = g_main_loop_new(NULL, FALSE);
+ crm_notice("Pacemaker CIB manager successfully started and accepting connections");
+ g_main_loop_run(mainloop);
+
+ /* If main loop returned, clean up and exit. We disconnect in case
+ * terminate_cib() was called with fast=-1.
+ */
+ crm_cluster_disconnect(crm_cluster);
+ pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm);
+
+done:
+ g_strfreev(processed_args);
+ pcmk__free_arg_context(context);
+
+ crm_peer_destroy();
+
+ if (local_notify_queue != NULL) {
+ g_hash_table_destroy(local_notify_queue);
+ }
+
+ if (config_hash != NULL) {
+ g_hash_table_destroy(config_hash);
+ }
+ pcmk__client_cleanup();
+ pcmk_cluster_free(crm_cluster);
+ g_free(cib_root);
+
+ pcmk__output_and_clear_error(&error, out);
+
+ if (out != NULL) {
+ out->finish(out, exit_code, true, NULL);
+ pcmk__output_free(out);
+ }
+ pcmk__unregister_formats();
+ crm_exit(exit_code);
+}
+
+#if SUPPORT_COROSYNC
+static void
+cib_cs_dispatch(cpg_handle_t handle,
+ const struct cpg_name *groupName,
+ uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len)
+{
+ uint32_t kind = 0;
+ xmlNode *xml = NULL;
+ const char *from = NULL;
+ char *data = pcmk_message_common_cs(handle, nodeid, pid, msg, &kind, &from);
+
+ if(data == NULL) {
+ return;
+ }
+ if (kind == crm_class_cluster) {
+ xml = string2xml(data);
+ if (xml == NULL) {
+ crm_err("Invalid XML: '%.120s'", data);
+ free(data);
+ return;
+ }
+ crm_xml_add(xml, F_ORIG, from);
+ /* crm_xml_add_int(xml, F_SEQ, wrapper->id); */
+ cib_peer_callback(xml, NULL);
+ }
+
+ free_xml(xml);
+ free(data);
+}
+
+static void
+cib_cs_destroy(gpointer user_data)
+{
+ if (cib_shutdown_flag) {
+ crm_info("Corosync disconnection complete");
+ } else {
+ crm_crit("Lost connection to cluster layer, shutting down");
+ terminate_cib(__func__, CRM_EX_DISCONNECT);
+ }
+}
+#endif
+
+static void
+cib_peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *data)
+{
+ switch (type) {
+ case crm_status_processes:
+ if (cib_legacy_mode()
+ && !pcmk_is_set(node->processes, crm_get_cluster_proc())) {
+
+ uint32_t old = data? *(const uint32_t *)data : 0;
+
+ if ((node->processes ^ old) & crm_proc_cpg) {
+ crm_info("Attempting to disable legacy mode after %s left the cluster",
+ node->uname);
+ legacy_mode = FALSE;
+ }
+ }
+ break;
+
+ case crm_status_uname:
+ case crm_status_nstate:
+ if (cib_shutdown_flag && (crm_active_peers() < 2)
+ && (pcmk__ipc_client_count() == 0)) {
+
+ crm_info("No more peers");
+ terminate_cib(__func__, -1);
+ }
+ break;
+ }
+}
+
+static void
+cib_init(void)
+{
+ crm_cluster = pcmk_cluster_new();
+
+#if SUPPORT_COROSYNC
+ if (is_corosync_cluster()) {
+ crm_cluster->destroy = cib_cs_destroy;
+ crm_cluster->cpg.cpg_deliver_fn = cib_cs_dispatch;
+ crm_cluster->cpg.cpg_confchg_fn = pcmk_cpg_membership;
+ }
+#endif // SUPPORT_COROSYNC
+
+ config_hash = pcmk__strkey_table(free, free);
+
+ if (startCib("cib.xml") == FALSE) {
+ crm_crit("Cannot start CIB... terminating");
+ crm_exit(CRM_EX_NOINPUT);
+ }
+
+ if (!stand_alone) {
+ crm_set_status_callback(&cib_peer_update_callback);
+
+ if (!crm_cluster_connect(crm_cluster)) {
+ crm_crit("Cannot sign in to the cluster... terminating");
+ crm_exit(CRM_EX_FATAL);
+ }
+ }
+
+ pcmk__serve_based_ipc(&ipcs_ro, &ipcs_rw, &ipcs_shm, &ipc_ro_callbacks,
+ &ipc_rw_callbacks);
+
+ if (stand_alone) {
+ based_is_primary = true;
+ }
+}
+
+static bool
+startCib(const char *filename)
+{
+ gboolean active = FALSE;
+ xmlNode *cib = readCibXmlFile(cib_root, filename, !preserve_status);
+
+ if (activateCibXml(cib, TRUE, "start") == 0) {
+ int port = 0;
+
+ active = TRUE;
+
+ cib_read_config(config_hash, cib);
+
+ pcmk__scan_port(crm_element_value(cib, "remote-tls-port"), &port);
+ if (port >= 0) {
+ remote_tls_fd = init_remote_listener(port, TRUE);
+ }
+
+ pcmk__scan_port(crm_element_value(cib, "remote-clear-port"), &port);
+ if (port >= 0) {
+ remote_fd = init_remote_listener(port, FALSE);
+ }
+ }
+ return active;
+}