diff --git a/daemons/based/pacemaker-based.c b/daemons/based/pacemaker-based.c index 7b2d049c41..1521245fe8 100644 --- a/daemons/based/pacemaker-based.c +++ b/daemons/based/pacemaker-based.c @@ -1,377 +1,415 @@ /* * Copyright 2004-2022 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 #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include +#include #include +#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; GMainLoop *mainloop = NULL; -const char *cib_root = NULL; +gchar *cib_root = NULL; char *cib_our_uname = 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; static void cib_init(void); void cib_shutdown(int nsig); static bool startCib(const char *filename); extern int write_cib_contents(gpointer p); void cib_cleanup(void); +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; } -static pcmk__cli_option_t long_options[] = { - // long option, argument type, storage, short option, description, flags - { - "help", no_argument, 0, '?', - "\tThis text", pcmk__option_default - }, - { - "verbose", no_argument, NULL, 'V', - "\tIncrease debug output", pcmk__option_default - }, - { - "stand-alone", no_argument, NULL, 's', - "\tAdvanced use only", pcmk__option_default - }, - { - "disk-writes", no_argument, NULL, 'w', - "\tAdvanced use only", pcmk__option_default - }, - { - "cib-root", required_argument, NULL, 'r', - "\tAdvanced use only", pcmk__option_default - }, - { 0, 0, 0, 0 } +/*! + * \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 GOptionContext * +build_arg_context(pcmk__common_args_t *args) +{ + GOptionContext *context = NULL; + + context = pcmk__build_arg_context(args, NULL, NULL, "[metadata]"); + pcmk__add_main_args(context, entries); + return context; +} + int main(int argc, char **argv) { - int flag; - int rc = 0; - int index = 0; - int argerr = 0; - struct passwd *pwentry = NULL; + int rc = pcmk_rc_ok; crm_ipc_t *old_instance = NULL; - crm_log_preinit(NULL, argc, argv); - pcmk__set_cli_options(NULL, "[options]", long_options, - "daemon for managing the configuration " - "of a Pacemaker cluster"); - - mainloop_add_signal(SIGTERM, cib_shutdown); - mainloop_add_signal(SIGPIPE, cib_enable_writes); + GError *error = NULL; - cib_writer = mainloop_add_trigger(G_PRIORITY_LOW, write_cib_contents, 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); - while (1) { - flag = pcmk__next_cli_option(argc, argv, &index, NULL); - if (flag == -1) - break; + crm_log_preinit(NULL, argc, argv); - switch (flag) { - case 'V': - crm_bump_log_level(argc, argv); - break; - case 's': - stand_alone = TRUE; - preserve_status = TRUE; - cib_writes_enabled = FALSE; - - pwentry = getpwnam(CRM_DAEMON_USER); - CRM_CHECK(pwentry != NULL, - crm_perror(LOG_ERR, "Invalid uid (%s) specified", CRM_DAEMON_USER); - return CRM_EX_FATAL); - - rc = setgid(pwentry->pw_gid); - if (rc < 0) { - crm_perror(LOG_ERR, "Could not set group to %d", pwentry->pw_gid); - return CRM_EX_FATAL; - } + if (!g_option_context_parse_strv(context, &processed_args, &error)) { + exit_code = CRM_EX_USAGE; + goto done; + } - rc = initgroups(CRM_DAEMON_USER, pwentry->pw_gid); - if (rc < 0) { - crm_perror(LOG_ERR, "Could not setup groups for user %d", pwentry->pw_uid); - return CRM_EX_FATAL; - } + if (args->version) { + g_strfreev(processed_args); + pcmk__free_arg_context(context); - rc = setuid(pwentry->pw_uid); - if (rc < 0) { - crm_perror(LOG_ERR, "Could not set user to %d", pwentry->pw_uid); - return CRM_EX_FATAL; - } - break; - case '?': /* Help message */ - pcmk__cli_help(flag, CRM_EX_OK); - break; - case 'w': - cib_writes_enabled = TRUE; - break; - case 'r': - cib_root = optarg; - break; - case 'm': - cib_metadata(); - return CRM_EX_OK; - default: - ++argerr; - break; - } - } - if (argc - optind == 1 && pcmk__str_eq("metadata", argv[optind], pcmk__str_casei)) { - cib_metadata(); - return CRM_EX_OK; + /* FIXME: When pacemaker-based is converted to use formatted output, + * this can go. + */ + pcmk__cli_help('v', CRM_EX_OK); } - if (optind > argc) { - ++argerr; - } + mainloop_add_signal(SIGTERM, cib_shutdown); + mainloop_add_signal(SIGPIPE, cib_enable_writes); - if (argerr) { - pcmk__cli_help('?', CRM_EX_USAGE); + 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 printed an error message with crm_err. */ - return CRM_EX_FATAL; + /* 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"); - crm_exit(CRM_EX_OK); + 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 = CRM_CONFIG_DIR; + 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) == FALSE) { + if (!pcmk__daemon_can_write(cib_root, NULL)) { + exit_code = CRM_EX_FATAL; crm_err("Terminating due to bad permissions on %s", cib_root); - fprintf(stderr, "ERROR: Bad permissions on %s (see logs for details)\n", - cib_root); - fflush(stderr); - return CRM_EX_FATAL; + 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); - crm_exit(CRM_EX_OK); + +done: + g_free(cib_root); + + g_strfreev(processed_args); + pcmk__free_arg_context(context); + + pcmk__output_and_clear_error(error, NULL); + crm_exit(exit_code); } void cib_cleanup(void) { crm_peer_destroy(); if (local_notify_queue) { g_hash_table_destroy(local_notify_queue); } pcmk__client_cleanup(); g_hash_table_destroy(config_hash); free(cib_our_uname); } #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) { if (is_corosync_cluster()) { #if SUPPORT_COROSYNC 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 } 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 == FALSE) { + if (!stand_alone) { if (is_corosync_cluster()) { crm_set_status_callback(&cib_peer_update_callback); } if (crm_cluster_connect(&crm_cluster) == FALSE) { crm_crit("Cannot sign in to the cluster... terminating"); crm_exit(CRM_EX_FATAL); } cib_our_uname = crm_cluster.uname; } else { cib_our_uname = strdup("localhost"); } 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; } diff --git a/daemons/based/pacemaker-based.h b/daemons/based/pacemaker-based.h index b2683938a7..4f7fc2d5b7 100644 --- a/daemons/based/pacemaker-based.h +++ b/daemons/based/pacemaker-based.h @@ -1,157 +1,157 @@ /* * Copyright 2004-2022 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #ifndef PACEMAKER_BASED__H # define PACEMAKER_BASED__H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_GNUTLS_GNUTLS_H # include #endif // CIB-specific client flags enum cib_client_flags { // Notifications cib_notify_pre = (UINT64_C(1) << 0), cib_notify_post = (UINT64_C(1) << 1), cib_notify_replace = (UINT64_C(1) << 2), cib_notify_confirm = (UINT64_C(1) << 3), cib_notify_diff = (UINT64_C(1) << 4), // Whether client is another cluster daemon cib_is_daemon = (UINT64_C(1) << 12), }; typedef struct cib_operation_s { const char *operation; gboolean modifies_cib; gboolean needs_privileges; gboolean needs_quorum; int (*prepare) (xmlNode *, xmlNode **, const char **); int (*cleanup) (int, xmlNode **, xmlNode **); int (*fn) (const char *, int, const char *, xmlNode *, xmlNode *, xmlNode *, xmlNode **, xmlNode **); } cib_operation_t; extern bool based_is_primary; extern GHashTable *peer_hash; extern GHashTable *config_hash; extern xmlNode *the_cib; extern crm_trigger_t *cib_writer; extern gboolean cib_writes_enabled; extern GMainLoop *mainloop; extern crm_cluster_t crm_cluster; extern GHashTable *local_notify_queue; extern gboolean legacy_mode; extern gboolean stand_alone; extern gboolean cib_shutdown_flag; -extern const char *cib_root; +extern gchar *cib_root; extern char *cib_our_uname; extern int cib_status; extern FILE *msg_cib_strm; extern struct qb_ipcs_service_handlers ipc_ro_callbacks; extern struct qb_ipcs_service_handlers ipc_rw_callbacks; extern qb_ipcs_service_t *ipcs_ro; extern qb_ipcs_service_t *ipcs_rw; extern qb_ipcs_service_t *ipcs_shm; void cib_peer_callback(xmlNode *msg, void *private_data); void cib_common_callback_worker(uint32_t id, uint32_t flags, xmlNode *op_request, pcmk__client_t *cib_client, gboolean privileged); void cib_shutdown(int nsig); void initiate_exit(void); void terminate_cib(const char *caller, int fast); gboolean cib_legacy_mode(void); gboolean uninitializeCib(void); xmlNode *readCibXml(char *buffer); xmlNode *readCibXmlFile(const char *dir, const char *file, gboolean discard_status); int activateCibXml(xmlNode *doc, gboolean to_disk, const char *op); xmlNode *createCibRequest(gboolean isLocal, const char *operation, const char *section, const char *verbose, xmlNode *data); int cib_process_shutdown_req(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); int cib_process_default(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); int cib_process_ping(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); int cib_process_readwrite(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); int cib_process_replace_svr(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); int cib_server_process_diff(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); int cib_process_sync(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); int cib_process_sync_one(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); int cib_process_delete_absolute(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); int cib_process_upgrade_server(const char *op, int options, const char *section, xmlNode *req, xmlNode *input, xmlNode *existing_cib, xmlNode **result_cib, xmlNode **answer); void send_sync_request(const char *host); xmlNode *cib_msg_copy(xmlNode *msg, gboolean with_data); xmlNode *cib_construct_reply(xmlNode *request, xmlNode *output, int rc); int cib_get_operation_id(const char *op, int *operation); cib_op_t *cib_op_func(int call_type); gboolean cib_op_modifies(int call_type); int cib_op_prepare(int call_type, xmlNode *request, xmlNode **input, const char **section); int cib_op_cleanup(int call_type, int options, xmlNode **input, xmlNode **output); int cib_op_can_run(int call_type, int call_options, gboolean privileged, gboolean global_update); void cib_diff_notify(int options, const char *client, const char *call_id, const char *op, xmlNode *update, int result, xmlNode *old_cib); void cib_replace_notify(const char *origin, xmlNode *update, int result, xmlNode *diff, uint32_t change_section); static inline const char * cib_config_lookup(const char *opt) { return g_hash_table_lookup(config_hash, opt); } #endif // PACEMAKER_BASED__H