diff --git a/exec/cfg.c b/exec/cfg.c index c300cc8f..991baf22 100644 --- a/exec/cfg.c +++ b/exec/cfg.c @@ -1,1373 +1,1396 @@ /* * Copyright (c) 2005-2006 MontaVista Software, Inc. * Copyright (c) 2006-2018 Red Hat, Inc. * * All rights reserved. * * Author: Steven Dake (sdake@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the MontaVista Software, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include <config.h> #include <sys/types.h> #include <sys/uio.h> #include <sys/socket.h> #include <sys/un.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <stddef.h> #include <limits.h> #include <errno.h> #include <string.h> #include <assert.h> #include <corosync/corotypes.h> #include <qb/qbipc_common.h> #include <corosync/cfg.h> #include <qb/qblist.h> #include <corosync/mar_gen.h> #include <corosync/totem/totemip.h> #include <corosync/totem/totem.h> #include <corosync/ipc_cfg.h> #include <corosync/logsys.h> #include <corosync/coroapi.h> #include <corosync/icmap.h> #include <corosync/corodefs.h> #include "totemconfig.h" #include "totemknet.h" #include "service.h" #include "main.h" LOGSYS_DECLARE_SUBSYS ("CFG"); enum cfg_message_req_types { MESSAGE_REQ_EXEC_CFG_RINGREENABLE = 0, MESSAGE_REQ_EXEC_CFG_KILLNODE = 1, MESSAGE_REQ_EXEC_CFG_SHUTDOWN = 2, MESSAGE_REQ_EXEC_CFG_RELOAD_CONFIG = 3, MESSAGE_REQ_EXEC_CFG_CRYPTO_RECONFIG = 4 }; #define DEFAULT_SHUTDOWN_TIMEOUT 5 static struct qb_list_head trackers_list; /* * Variables controlling a requested shutdown */ static corosync_timer_handle_t shutdown_timer; static struct cfg_info *shutdown_con; static uint32_t shutdown_flags; static int shutdown_yes; static int shutdown_no; static int shutdown_expected; struct cfg_info { struct qb_list_head list; void *conn; void *tracker_conn; enum {SHUTDOWN_REPLY_UNKNOWN, SHUTDOWN_REPLY_YES, SHUTDOWN_REPLY_NO} shutdown_reply; }; static void cfg_confchg_fn ( enum totem_configuration_type configuration_type, const unsigned int *member_list, size_t member_list_entries, const unsigned int *left_list, size_t left_list_entries, const unsigned int *joined_list, size_t joined_list_entries, const struct memb_ring_id *ring_id); static char *cfg_exec_init_fn (struct corosync_api_v1 *corosync_api_v1); static struct corosync_api_v1 *api; static int cfg_lib_init_fn (void *conn); static int cfg_lib_exit_fn (void *conn); static void message_handler_req_exec_cfg_ringreenable ( const void *message, unsigned int nodeid); static void message_handler_req_exec_cfg_killnode ( const void *message, unsigned int nodeid); static void message_handler_req_exec_cfg_shutdown ( const void *message, unsigned int nodeid); static void message_handler_req_exec_cfg_reload_config ( const void *message, unsigned int nodeid); static void message_handler_req_exec_cfg_reconfig_crypto ( const void *message, unsigned int nodeid); static void exec_cfg_killnode_endian_convert (void *msg); static void message_handler_req_lib_cfg_ringstatusget ( void *conn, const void *msg); static void message_handler_req_lib_cfg_nodestatusget ( void *conn, const void *msg); static void message_handler_req_lib_cfg_ringreenable ( void *conn, const void *msg); static void message_handler_req_lib_cfg_killnode ( void *conn, const void *msg); static void message_handler_req_lib_cfg_tryshutdown ( void *conn, const void *msg); static void message_handler_req_lib_cfg_replytoshutdown ( void *conn, const void *msg); static void message_handler_req_lib_cfg_get_node_addrs ( void *conn, const void *msg); static void message_handler_req_lib_cfg_local_get ( void *conn, const void *msg); static void message_handler_req_lib_cfg_reload_config ( void *conn, const void *msg); static void message_handler_req_lib_cfg_reopen_log_files ( void *conn, const void *msg); /* * Service Handler Definition */ static struct corosync_lib_handler cfg_lib_engine[] = { { /* 0 */ .lib_handler_fn = message_handler_req_lib_cfg_ringstatusget, .flow_control = CS_LIB_FLOW_CONTROL_REQUIRED }, { /* 1 */ .lib_handler_fn = message_handler_req_lib_cfg_ringreenable, .flow_control = CS_LIB_FLOW_CONTROL_REQUIRED }, { /* 2 */ .lib_handler_fn = message_handler_req_lib_cfg_killnode, .flow_control = CS_LIB_FLOW_CONTROL_REQUIRED }, { /* 3 */ .lib_handler_fn = message_handler_req_lib_cfg_tryshutdown, .flow_control = CS_LIB_FLOW_CONTROL_REQUIRED }, { /* 4 */ .lib_handler_fn = message_handler_req_lib_cfg_replytoshutdown, .flow_control = CS_LIB_FLOW_CONTROL_REQUIRED }, { /* 5 */ .lib_handler_fn = message_handler_req_lib_cfg_get_node_addrs, .flow_control = CS_LIB_FLOW_CONTROL_NOT_REQUIRED }, { /* 6 */ .lib_handler_fn = message_handler_req_lib_cfg_local_get, .flow_control = CS_LIB_FLOW_CONTROL_NOT_REQUIRED }, { /* 7 */ .lib_handler_fn = message_handler_req_lib_cfg_reload_config, .flow_control = CS_LIB_FLOW_CONTROL_REQUIRED }, { /* 8 */ .lib_handler_fn = message_handler_req_lib_cfg_reopen_log_files, .flow_control = CS_LIB_FLOW_CONTROL_NOT_REQUIRED }, { /* 9 */ .lib_handler_fn = message_handler_req_lib_cfg_nodestatusget, .flow_control = CS_LIB_FLOW_CONTROL_NOT_REQUIRED } }; static struct corosync_exec_handler cfg_exec_engine[] = { { /* 0 */ .exec_handler_fn = message_handler_req_exec_cfg_ringreenable, }, { /* 1 */ .exec_handler_fn = message_handler_req_exec_cfg_killnode, .exec_endian_convert_fn = exec_cfg_killnode_endian_convert }, { /* 2 */ .exec_handler_fn = message_handler_req_exec_cfg_shutdown, }, { /* 3 */ .exec_handler_fn = message_handler_req_exec_cfg_reload_config, }, { /* 4 */ .exec_handler_fn = message_handler_req_exec_cfg_reconfig_crypto, } }; /* * Exports the interface for the service */ struct corosync_service_engine cfg_service_engine = { .name = "corosync configuration service", .id = CFG_SERVICE, .priority = 1, .private_data_size = sizeof(struct cfg_info), .flow_control = CS_LIB_FLOW_CONTROL_NOT_REQUIRED, .allow_inquorate = CS_LIB_ALLOW_INQUORATE, .lib_init_fn = cfg_lib_init_fn, .lib_exit_fn = cfg_lib_exit_fn, .lib_engine = cfg_lib_engine, .lib_engine_count = sizeof (cfg_lib_engine) / sizeof (struct corosync_lib_handler), .exec_init_fn = cfg_exec_init_fn, .exec_engine = cfg_exec_engine, .exec_engine_count = sizeof (cfg_exec_engine) / sizeof (struct corosync_exec_handler), .confchg_fn = cfg_confchg_fn }; struct corosync_service_engine *cfg_get_service_engine_ver0 (void) { return (&cfg_service_engine); } struct req_exec_cfg_ringreenable { struct qb_ipc_request_header header __attribute__((aligned(8))); mar_message_source_t source __attribute__((aligned(8))); }; struct req_exec_cfg_reload_config { struct qb_ipc_request_header header __attribute__((aligned(8))); mar_message_source_t source __attribute__((aligned(8))); }; struct req_exec_cfg_crypto_reconfig { struct qb_ipc_request_header header __attribute__((aligned(8))); mar_uint32_t phase __attribute__((aligned(8))); }; struct req_exec_cfg_killnode { struct qb_ipc_request_header header __attribute__((aligned(8))); mar_uint32_t nodeid __attribute__((aligned(8))); mar_name_t reason __attribute__((aligned(8))); }; struct req_exec_cfg_shutdown { struct qb_ipc_request_header header __attribute__((aligned(8))); }; /* IMPL */ static char *cfg_exec_init_fn ( struct corosync_api_v1 *corosync_api_v1) { api = corosync_api_v1; qb_list_init(&trackers_list); return (NULL); } static void cfg_confchg_fn ( enum totem_configuration_type configuration_type, const unsigned int *member_list, size_t member_list_entries, const unsigned int *left_list, size_t left_list_entries, const unsigned int *joined_list, size_t joined_list_entries, const struct memb_ring_id *ring_id) { } /* * Tell other nodes we are shutting down */ static int send_shutdown(void) { struct req_exec_cfg_shutdown req_exec_cfg_shutdown; struct iovec iovec; ENTER(); req_exec_cfg_shutdown.header.size = sizeof (struct req_exec_cfg_shutdown); req_exec_cfg_shutdown.header.id = SERVICE_ID_MAKE (CFG_SERVICE, MESSAGE_REQ_EXEC_CFG_SHUTDOWN); iovec.iov_base = (char *)&req_exec_cfg_shutdown; iovec.iov_len = sizeof (struct req_exec_cfg_shutdown); assert (api->totem_mcast (&iovec, 1, TOTEM_SAFE) == 0); LEAVE(); return 0; } static void send_test_shutdown(void *only_conn, void *exclude_conn, int status) { struct res_lib_cfg_testshutdown res_lib_cfg_testshutdown; struct qb_list_head *iter; ENTER(); res_lib_cfg_testshutdown.header.size = sizeof(struct res_lib_cfg_testshutdown); res_lib_cfg_testshutdown.header.id = MESSAGE_RES_CFG_TESTSHUTDOWN; res_lib_cfg_testshutdown.header.error = status; res_lib_cfg_testshutdown.flags = shutdown_flags; if (only_conn) { TRACE1("sending testshutdown to only %p", only_conn); api->ipc_dispatch_send(only_conn, &res_lib_cfg_testshutdown, sizeof(res_lib_cfg_testshutdown)); } else { qb_list_for_each(iter, &trackers_list) { struct cfg_info *ci = qb_list_entry(iter, struct cfg_info, list); if (ci->conn != exclude_conn) { TRACE1("sending testshutdown to %p", ci->tracker_conn); api->ipc_dispatch_send(ci->tracker_conn, &res_lib_cfg_testshutdown, sizeof(res_lib_cfg_testshutdown)); } } } LEAVE(); } static void check_shutdown_status(void) { ENTER(); /* * Shutdown client might have gone away */ if (!shutdown_con) { LEAVE(); return; } /* * All replies safely gathered in ? */ if (shutdown_yes + shutdown_no >= shutdown_expected) { struct res_lib_cfg_tryshutdown res_lib_cfg_tryshutdown; api->timer_delete(shutdown_timer); if (shutdown_yes >= shutdown_expected || shutdown_flags == CFG_SHUTDOWN_FLAG_REGARDLESS) { TRACE1("shutdown confirmed"); res_lib_cfg_tryshutdown.header.size = sizeof(struct res_lib_cfg_tryshutdown); res_lib_cfg_tryshutdown.header.id = MESSAGE_RES_CFG_TRYSHUTDOWN; res_lib_cfg_tryshutdown.header.error = CS_OK; /* * Tell originator that shutdown was confirmed */ api->ipc_response_send(shutdown_con->conn, &res_lib_cfg_tryshutdown, sizeof(res_lib_cfg_tryshutdown)); shutdown_con = NULL; /* * Tell other nodes we are going down */ send_shutdown(); } else { TRACE1("shutdown cancelled"); res_lib_cfg_tryshutdown.header.size = sizeof(struct res_lib_cfg_tryshutdown); res_lib_cfg_tryshutdown.header.id = MESSAGE_RES_CFG_TRYSHUTDOWN; res_lib_cfg_tryshutdown.header.error = CS_ERR_BUSY; /* * Tell originator that shutdown was cancelled */ api->ipc_response_send(shutdown_con->conn, &res_lib_cfg_tryshutdown, sizeof(res_lib_cfg_tryshutdown)); shutdown_con = NULL; } log_printf(LOGSYS_LEVEL_DEBUG, "shutdown decision is: (yes count: %d, no count: %d) flags=%x", shutdown_yes, shutdown_no, shutdown_flags); } LEAVE(); } /* * Not all nodes responded to the shutdown (in time) */ static void shutdown_timer_fn(void *arg) { ENTER(); /* * Mark undecideds as "NO" */ shutdown_no = shutdown_expected; check_shutdown_status(); send_test_shutdown(NULL, NULL, CS_ERR_TIMEOUT); LEAVE(); } static void remove_ci_from_shutdown(struct cfg_info *ci) { ENTER(); /* * If the controlling shutdown process has quit, then cancel the * shutdown session */ if (ci == shutdown_con) { shutdown_con = NULL; api->timer_delete(shutdown_timer); } if (!qb_list_empty(&ci->list)) { qb_list_del(&ci->list); qb_list_init(&ci->list); /* * Remove our option */ if (shutdown_con) { if (ci->shutdown_reply == SHUTDOWN_REPLY_YES) shutdown_yes--; if (ci->shutdown_reply == SHUTDOWN_REPLY_NO) shutdown_no--; } /* * If we are leaving, then that's an implicit YES to shutdown */ ci->shutdown_reply = SHUTDOWN_REPLY_YES; shutdown_yes++; check_shutdown_status(); } LEAVE(); } int cfg_lib_exit_fn (void *conn) { struct cfg_info *ci = (struct cfg_info *)api->ipc_private_data_get (conn); ENTER(); remove_ci_from_shutdown(ci); LEAVE(); return (0); } static int cfg_lib_init_fn (void *conn) { struct cfg_info *ci = (struct cfg_info *)api->ipc_private_data_get (conn); ENTER(); qb_list_init(&ci->list); LEAVE(); return (0); } /* * Executive message handlers */ static void message_handler_req_exec_cfg_ringreenable ( const void *message, unsigned int nodeid) { ENTER(); LEAVE(); } static void exec_cfg_killnode_endian_convert (void *msg) { struct req_exec_cfg_killnode *req_exec_cfg_killnode = (struct req_exec_cfg_killnode *)msg; ENTER(); swab_mar_name_t(&req_exec_cfg_killnode->reason); LEAVE(); } static void message_handler_req_exec_cfg_killnode ( const void *message, unsigned int nodeid) { const struct req_exec_cfg_killnode *req_exec_cfg_killnode = message; cs_name_t reason; ENTER(); log_printf(LOGSYS_LEVEL_DEBUG, "request to kill node " CS_PRI_NODE_ID " (us=" CS_PRI_NODE_ID ")", req_exec_cfg_killnode->nodeid, api->totem_nodeid_get()); if (req_exec_cfg_killnode->nodeid == api->totem_nodeid_get()) { marshall_from_mar_name_t(&reason, &req_exec_cfg_killnode->reason); log_printf(LOGSYS_LEVEL_NOTICE, "Killed by node " CS_PRI_NODE_ID " : %s", nodeid, reason.value); corosync_fatal_error(COROSYNC_FATAL_ERROR_EXIT); } LEAVE(); } /* * Self shutdown */ static void message_handler_req_exec_cfg_shutdown ( const void *message, unsigned int nodeid) { ENTER(); log_printf(LOGSYS_LEVEL_NOTICE, "Node " CS_PRI_NODE_ID " was shut down by sysadmin", nodeid); if (nodeid == api->totem_nodeid_get()) { api->shutdown_request(); } LEAVE(); } /* strcmp replacement that can handle NULLs */ static int nullcheck_strcmp(const char* left, const char *right) { if (!left && right) return -1; if (left && !right) return 1; if (!left && !right) return 0; return strcmp(left, right); } /* * If a key has changed value in the new file, then warn the user and remove it from the temp_map */ static void delete_and_notify_if_changed(icmap_map_t temp_map, const char *key_name) { if (!(icmap_key_value_eq(temp_map, key_name, icmap_get_global_map(), key_name))) { if (icmap_delete_r(temp_map, key_name) == CS_OK) { log_printf(LOGSYS_LEVEL_NOTICE, "Modified entry '%s' in corosync.conf cannot be changed at run-time", key_name); } } } /* * Remove any keys from the new config file that in the new corosync.conf but that * cannot be changed at run time. A log message will be issued for each * entry that the user wants to change but they cannot. * * Add more here as needed. */ static void remove_ro_entries(icmap_map_t temp_map) { #ifndef HAVE_KNET_CRYPTO_RECONF delete_and_notify_if_changed(temp_map, "totem.secauth"); delete_and_notify_if_changed(temp_map, "totem.crypto_hash"); delete_and_notify_if_changed(temp_map, "totem.crypto_cipher"); delete_and_notify_if_changed(temp_map, "totem.keyfile"); delete_and_notify_if_changed(temp_map, "totem.key"); #endif delete_and_notify_if_changed(temp_map, "totem.version"); delete_and_notify_if_changed(temp_map, "totem.threads"); delete_and_notify_if_changed(temp_map, "totem.ip_version"); delete_and_notify_if_changed(temp_map, "totem.rrp_mode"); delete_and_notify_if_changed(temp_map, "totem.netmtu"); delete_and_notify_if_changed(temp_map, "totem.interface.ringnumber"); delete_and_notify_if_changed(temp_map, "totem.interface.bindnetaddr"); delete_and_notify_if_changed(temp_map, "totem.interface.mcastaddr"); delete_and_notify_if_changed(temp_map, "totem.interface.broadcast"); delete_and_notify_if_changed(temp_map, "totem.interface.mcastport"); delete_and_notify_if_changed(temp_map, "totem.interface.ttl"); delete_and_notify_if_changed(temp_map, "totem.transport"); delete_and_notify_if_changed(temp_map, "totem.cluster_name"); delete_and_notify_if_changed(temp_map, "quorum.provider"); delete_and_notify_if_changed(temp_map, "system.move_to_root_cgroup"); delete_and_notify_if_changed(temp_map, "system.sched_rr"); delete_and_notify_if_changed(temp_map, "system.priority"); delete_and_notify_if_changed(temp_map, "system.qb_ipc_type"); delete_and_notify_if_changed(temp_map, "system.state_dir"); } /* * Remove entries that exist in the global map, but not in the temp_map, this will * cause delete notifications to be sent to any listeners. * * NOTE: This routine depends entirely on the keys returned by the iterators * being in alpha-sorted order. */ static void remove_deleted_entries(icmap_map_t temp_map, const char *prefix) { icmap_iter_t old_iter; icmap_iter_t new_iter; const char *old_key, *new_key; int ret; old_iter = icmap_iter_init(prefix); new_iter = icmap_iter_init_r(temp_map, prefix); old_key = icmap_iter_next(old_iter, NULL, NULL); new_key = icmap_iter_next(new_iter, NULL, NULL); while (old_key || new_key) { ret = nullcheck_strcmp(old_key, new_key); if ((ret < 0 && old_key) || !new_key) { /* * new_key is greater, a line (or more) has been deleted * Continue until old is >= new */ do { /* Remove it from icmap & send notifications */ icmap_delete(old_key); old_key = icmap_iter_next(old_iter, NULL, NULL); ret = nullcheck_strcmp(old_key, new_key); } while (ret < 0 && old_key); } else if ((ret > 0 && new_key) || !old_key) { /* * old_key is greater, a line (or more) has been added * Continue until new is >= old * * we don't need to do anything special with this like tell * icmap. That will happen when we copy the values over */ do { new_key = icmap_iter_next(new_iter, NULL, NULL); ret = nullcheck_strcmp(old_key, new_key); } while (ret > 0 && new_key); } if (ret == 0) { new_key = icmap_iter_next(new_iter, NULL, NULL); old_key = icmap_iter_next(old_iter, NULL, NULL); } } icmap_iter_finalize(new_iter); icmap_iter_finalize(old_iter); } /* * Reload configuration file */ static void message_handler_req_exec_cfg_reload_config ( const void *message, unsigned int nodeid) { const struct req_exec_cfg_reload_config *req_exec_cfg_reload_config = message; struct res_lib_cfg_reload_config res_lib_cfg_reload_config; struct totem_config new_config; icmap_map_t temp_map; const char *error_string; int res = CS_OK; ENTER(); log_printf(LOGSYS_LEVEL_NOTICE, "Config reload requested by node " CS_PRI_NODE_ID, nodeid); icmap_set_uint8("config.totemconfig_reload_in_progress", 1); /* Make sure there is no rubbish in this that might be checked, even on error */ memset(&new_config, 0, sizeof(new_config)); /* * Set up a new hashtable as a staging area. */ if ((res = icmap_init_r(&temp_map)) != CS_OK) { log_printf(LOGSYS_LEVEL_ERROR, "Unable to create temporary icmap. config file reload cancelled\n"); goto reload_fini_nomap; } /* * Load new config into the temporary map */ res = coroparse_configparse(temp_map, &error_string); if (res == -1) { log_printf (LOGSYS_LEVEL_ERROR, "Unable to reload config file: %s", error_string); res = CS_ERR_INVALID_PARAM; goto reload_fini_nofree; } /* Signal start of the reload process */ icmap_set_uint8("config.reload_in_progress", 1); /* Detect deleted entries and remove them from the main icmap hashtable */ remove_deleted_entries(temp_map, "logging."); remove_deleted_entries(temp_map, "totem."); remove_deleted_entries(temp_map, "nodelist."); remove_deleted_entries(temp_map, "quorum."); remove_deleted_entries(temp_map, "uidgid.config."); remove_deleted_entries(temp_map, "nozzle."); /* Remove entries that cannot be changed */ remove_ro_entries(temp_map); /* Take a copy of the current setup so we can check what has changed */ memset(&new_config, 0, sizeof(new_config)); new_config.orig_interfaces = malloc (sizeof (struct totem_interface) * INTERFACE_MAX); assert(new_config.orig_interfaces != NULL); totempg_get_config(&new_config); new_config.crypto_changed = 0; new_config.interfaces = malloc (sizeof (struct totem_interface) * INTERFACE_MAX); assert(new_config.interfaces != NULL); memset(new_config.interfaces, 0, sizeof (struct totem_interface) * INTERFACE_MAX); /* For UDP[U] the configuration on link0 is static (apart from the nodelist) and only read at startup. So preserve it here */ if ( (new_config.transport_number == TOTEM_TRANSPORT_UDP) || (new_config.transport_number == TOTEM_TRANSPORT_UDPU)) { memcpy(&new_config.interfaces[0], &new_config.orig_interfaces[0], sizeof(struct totem_interface)); } /* Calculate new node and interface definitions */ if (totemconfig_configure_new_params(&new_config, temp_map, &error_string) == -1) { log_printf (LOGSYS_LEVEL_ERROR, "Cannot configure new interface definitions: %s\n", error_string); res = CS_ERR_INVALID_PARAM; goto reload_fini; } /* Read from temp_map into new_config */ totem_volatile_config_read(&new_config, temp_map, NULL); /* Get updated crypto parameters. Will set a flag in new_config if things have changed */ if (totem_reread_crypto_config(&new_config, temp_map, &error_string) == -1) { log_printf (LOGSYS_LEVEL_ERROR, "Crypto configuration is not valid: %s\n", error_string); res = CS_ERR_INVALID_PARAM; goto reload_fini; } /* Validate dynamic parameters */ if (totem_volatile_config_validate(&new_config, temp_map, &error_string) == -1) { log_printf (LOGSYS_LEVEL_ERROR, "Configuration is not valid: %s\n", error_string); res = CS_ERR_INVALID_PARAM; goto reload_fini; } /* Save this here so we can get at it for the later phases of crypto change */ if (new_config.crypto_changed) { #ifndef HAVE_KNET_CRYPTO_RECONF new_config.crypto_changed = 0; log_printf (LOGSYS_LEVEL_ERROR, "Crypto reconfiguration is not supported by the linked version of knet\n"); res = CS_ERR_INVALID_PARAM; goto reload_fini; #endif } /* * Copy new keys into live config. */ if ( (res = icmap_copy_map(icmap_get_global_map(), temp_map)) != CS_OK) { log_printf (LOGSYS_LEVEL_ERROR, "Error making new config live. cmap database may be inconsistent\n"); /* Return res from icmap */ goto reload_fini; } /* Copy into live system */ totempg_put_config(&new_config); totemconfig_commit_new_params(&new_config, temp_map); free(new_config.interfaces); reload_fini: /* All done - let clients know */ icmap_set_int32("config.reload_status", res); icmap_set_uint8("config.totemconfig_reload_in_progress", 0); icmap_set_uint8("config.reload_in_progress", 0); /* Finished with the temporary storage */ free(new_config.orig_interfaces); reload_fini_nofree: icmap_fini_r(temp_map); reload_fini_nomap: /* If crypto was changed, now it's loaded on all nodes we can enable it. * Each node sends its own PHASE message so we're not relying on the leader * node to survive the transition */ if (new_config.crypto_changed) { struct req_exec_cfg_crypto_reconfig req_exec_cfg_crypto_reconfig; struct iovec iovec; req_exec_cfg_crypto_reconfig.header.size = sizeof (struct req_exec_cfg_crypto_reconfig); req_exec_cfg_crypto_reconfig.header.id = SERVICE_ID_MAKE (CFG_SERVICE, MESSAGE_REQ_EXEC_CFG_CRYPTO_RECONFIG); req_exec_cfg_crypto_reconfig.phase = CRYPTO_RECONFIG_PHASE_ACTIVATE; iovec.iov_base = (char *)&req_exec_cfg_crypto_reconfig; iovec.iov_len = sizeof (struct req_exec_cfg_crypto_reconfig); assert (api->totem_mcast (&iovec, 1, TOTEM_SAFE) == 0); } /* All done, return result to the caller if it was on this system */ if (nodeid == api->totem_nodeid_get()) { res_lib_cfg_reload_config.header.size = sizeof(res_lib_cfg_reload_config); res_lib_cfg_reload_config.header.id = MESSAGE_RES_CFG_RELOAD_CONFIG; res_lib_cfg_reload_config.header.error = res; api->ipc_response_send(req_exec_cfg_reload_config->source.conn, &res_lib_cfg_reload_config, sizeof(res_lib_cfg_reload_config)); api->ipc_refcnt_dec(req_exec_cfg_reload_config->source.conn);; } LEAVE(); } /* Handle the phases of crypto reload * The first time we are called is after the new crypto config has been loaded * but not activated. * * 1 - activate the new crypto configuration * 2 - clear out the old configuration */ static void message_handler_req_exec_cfg_reconfig_crypto ( const void *message, unsigned int nodeid) { const struct req_exec_cfg_crypto_reconfig *req_exec_cfg_crypto_reconfig = message; /* Got our own reconfig message */ if (nodeid == api->totem_nodeid_get()) { log_printf (LOGSYS_LEVEL_DEBUG, "Crypto reconfiguration phase %d", req_exec_cfg_crypto_reconfig->phase); /* Do the deed */ totempg_crypto_reconfigure_phase(req_exec_cfg_crypto_reconfig->phase); /* Move to the next phase if not finished */ if (req_exec_cfg_crypto_reconfig->phase < CRYPTO_RECONFIG_PHASE_CLEANUP) { struct req_exec_cfg_crypto_reconfig req_exec_cfg_crypto_reconfig2; struct iovec iovec; req_exec_cfg_crypto_reconfig2.header.size = sizeof (struct req_exec_cfg_crypto_reconfig); req_exec_cfg_crypto_reconfig2.header.id = SERVICE_ID_MAKE (CFG_SERVICE, MESSAGE_REQ_EXEC_CFG_CRYPTO_RECONFIG); req_exec_cfg_crypto_reconfig2.phase = CRYPTO_RECONFIG_PHASE_CLEANUP; iovec.iov_base = (char *)&req_exec_cfg_crypto_reconfig2; iovec.iov_len = sizeof (struct req_exec_cfg_crypto_reconfig); assert (api->totem_mcast (&iovec, 1, TOTEM_SAFE) == 0); } } } /* * Library Interface Implementation */ static void message_handler_req_lib_cfg_ringstatusget ( void *conn, const void *msg) { struct res_lib_cfg_ringstatusget res_lib_cfg_ringstatusget; struct totem_ip_address interfaces[INTERFACE_MAX]; unsigned int iface_count; char **status; const char *totem_ip_string; char ifname[CFG_INTERFACE_NAME_MAX_LEN]; unsigned int iface_ids[INTERFACE_MAX]; unsigned int i; cs_error_t res = CS_OK; ENTER(); res_lib_cfg_ringstatusget.header.id = MESSAGE_RES_CFG_RINGSTATUSGET; res_lib_cfg_ringstatusget.header.size = sizeof (struct res_lib_cfg_ringstatusget); api->totem_ifaces_get ( api->totem_nodeid_get(), iface_ids, interfaces, INTERFACE_MAX, &status, &iface_count); assert(iface_count <= CFG_MAX_INTERFACES); res_lib_cfg_ringstatusget.interface_count = iface_count; for (i = 0; i < iface_count; i++) { totem_ip_string = (const char *)api->totem_ip_print (&interfaces[i]); if (!totem_ip_string) { totem_ip_string=""; } /* Allow for i/f number at the start */ if (strlen(totem_ip_string) >= CFG_INTERFACE_NAME_MAX_LEN-3) { log_printf(LOGSYS_LEVEL_ERROR, "String representation of interface %u is too long", i); res = CS_ERR_NAME_TOO_LONG; goto send_response; } snprintf(ifname, sizeof(ifname), "%d %s", iface_ids[i], totem_ip_string); if (strlen(status[i]) >= CFG_INTERFACE_STATUS_MAX_LEN) { log_printf(LOGSYS_LEVEL_ERROR, "Status string for interface %u is too long", i); res = CS_ERR_NAME_TOO_LONG; goto send_response; } strcpy ((char *)&res_lib_cfg_ringstatusget.interface_status[i], status[i]); strcpy ((char *)&res_lib_cfg_ringstatusget.interface_name[i], ifname); } send_response: res_lib_cfg_ringstatusget.header.error = res; api->ipc_response_send ( conn, &res_lib_cfg_ringstatusget, sizeof (struct res_lib_cfg_ringstatusget)); LEAVE(); } static void message_handler_req_lib_cfg_nodestatusget ( void *conn, const void *msg) { - struct res_lib_cfg_nodestatusget res_lib_cfg_nodestatusget; + struct res_lib_cfg_nodestatusget_version res_lib_cfg_nodestatusget_version; + struct res_lib_cfg_nodestatusget_v1 res_lib_cfg_nodestatusget_v1; + void *res_lib_cfg_nodestatusget_ptr = NULL; + size_t res_lib_cfg_nodestatusget_size; struct req_lib_cfg_nodestatusget *req_lib_cfg_nodestatusget = (struct req_lib_cfg_nodestatusget *)msg; struct totem_node_status node_status; - cs_error_t res = CS_OK; int i; ENTER(); + memset(&node_status, 0, sizeof(node_status)); + if (totempg_nodestatus_get(req_lib_cfg_nodestatusget->nodeid, &node_status) != 0) { + res_lib_cfg_nodestatusget_ptr = &res_lib_cfg_nodestatusget_version; + res_lib_cfg_nodestatusget_size = sizeof(res_lib_cfg_nodestatusget_version); + + res_lib_cfg_nodestatusget_version.header.error = CS_ERR_FAILED_OPERATION; + res_lib_cfg_nodestatusget_version.header.id = MESSAGE_RES_CFG_NODESTATUSGET; + res_lib_cfg_nodestatusget_version.header.size = res_lib_cfg_nodestatusget_size; + + goto ipc_response_send; + } + /* Currently only one structure version supported */ - if (req_lib_cfg_nodestatusget->version == TOTEM_NODE_STATUS_STRUCTURE_VERSION) - { - res_lib_cfg_nodestatusget.header.id = MESSAGE_RES_CFG_NODESTATUSGET; - res_lib_cfg_nodestatusget.header.size = sizeof (struct res_lib_cfg_nodestatusget); - - memset(&node_status, 0, sizeof(node_status)); - res = totempg_nodestatus_get(req_lib_cfg_nodestatusget->nodeid, - &node_status); - if (res == 0) { - res_lib_cfg_nodestatusget.node_status.nodeid = req_lib_cfg_nodestatusget->nodeid; - res_lib_cfg_nodestatusget.node_status.version = node_status.version; - res_lib_cfg_nodestatusget.node_status.reachable = node_status.reachable; - res_lib_cfg_nodestatusget.node_status.remote = node_status.remote; - res_lib_cfg_nodestatusget.node_status.external = node_status.external; - res_lib_cfg_nodestatusget.node_status.onwire_min = node_status.onwire_min; - res_lib_cfg_nodestatusget.node_status.onwire_max = node_status.onwire_max; - res_lib_cfg_nodestatusget.node_status.onwire_ver= node_status.onwire_ver; - - for (i=0; i < KNET_MAX_LINK; i++) { - res_lib_cfg_nodestatusget.node_status.link_status[i].enabled = node_status.link_status[i].enabled; - res_lib_cfg_nodestatusget.node_status.link_status[i].connected = node_status.link_status[i].connected; - res_lib_cfg_nodestatusget.node_status.link_status[i].dynconnected = node_status.link_status[i].dynconnected; - res_lib_cfg_nodestatusget.node_status.link_status[i].mtu = node_status.link_status[i].mtu; - memcpy(res_lib_cfg_nodestatusget.node_status.link_status[i].src_ipaddr, - node_status.link_status[i].src_ipaddr, CFG_MAX_HOST_LEN); - memcpy(res_lib_cfg_nodestatusget.node_status.link_status[i].dst_ipaddr, - node_status.link_status[i].dst_ipaddr, CFG_MAX_HOST_LEN); - } + switch (req_lib_cfg_nodestatusget->version) { + case CFG_NODE_STATUS_V1: + res_lib_cfg_nodestatusget_ptr = &res_lib_cfg_nodestatusget_v1; + res_lib_cfg_nodestatusget_size = sizeof(res_lib_cfg_nodestatusget_v1); + + res_lib_cfg_nodestatusget_v1.header.error = CS_OK; + res_lib_cfg_nodestatusget_v1.header.id = MESSAGE_RES_CFG_NODESTATUSGET; + res_lib_cfg_nodestatusget_v1.header.size = res_lib_cfg_nodestatusget_size; + + res_lib_cfg_nodestatusget_v1.node_status.version = CFG_NODE_STATUS_V1; + res_lib_cfg_nodestatusget_v1.node_status.nodeid = req_lib_cfg_nodestatusget->nodeid; + res_lib_cfg_nodestatusget_v1.node_status.reachable = node_status.reachable; + res_lib_cfg_nodestatusget_v1.node_status.remote = node_status.remote; + res_lib_cfg_nodestatusget_v1.node_status.external = node_status.external; + res_lib_cfg_nodestatusget_v1.node_status.onwire_min = node_status.onwire_min; + res_lib_cfg_nodestatusget_v1.node_status.onwire_max = node_status.onwire_max; + res_lib_cfg_nodestatusget_v1.node_status.onwire_ver = node_status.onwire_ver; + + for (i=0; i < KNET_MAX_LINK; i++) { + res_lib_cfg_nodestatusget_v1.node_status.link_status[i].enabled = node_status.link_status[i].enabled; + res_lib_cfg_nodestatusget_v1.node_status.link_status[i].connected = node_status.link_status[i].connected; + res_lib_cfg_nodestatusget_v1.node_status.link_status[i].dynconnected = node_status.link_status[i].dynconnected; + res_lib_cfg_nodestatusget_v1.node_status.link_status[i].mtu = node_status.link_status[i].mtu; + memcpy(res_lib_cfg_nodestatusget_v1.node_status.link_status[i].src_ipaddr, + node_status.link_status[i].src_ipaddr, CFG_MAX_HOST_LEN); + memcpy(res_lib_cfg_nodestatusget_v1.node_status.link_status[i].dst_ipaddr, + node_status.link_status[i].dst_ipaddr, CFG_MAX_HOST_LEN); } - } else { - res = CS_ERR_NOT_SUPPORTED; + break; + default: + /* + * Unsupported version requested + */ + res_lib_cfg_nodestatusget_ptr = &res_lib_cfg_nodestatusget_version; + res_lib_cfg_nodestatusget_size = sizeof(res_lib_cfg_nodestatusget_version); + + res_lib_cfg_nodestatusget_version.header.error = CS_ERR_NOT_SUPPORTED; + res_lib_cfg_nodestatusget_version.header.id = MESSAGE_RES_CFG_NODESTATUSGET; + res_lib_cfg_nodestatusget_version.header.size = res_lib_cfg_nodestatusget_size; + break; } - res_lib_cfg_nodestatusget.header.error = res; +ipc_response_send: api->ipc_response_send ( conn, - &res_lib_cfg_nodestatusget, - sizeof (struct res_lib_cfg_nodestatusget)); + res_lib_cfg_nodestatusget_ptr, + res_lib_cfg_nodestatusget_size); LEAVE(); } static void message_handler_req_lib_cfg_ringreenable ( void *conn, const void *msg) { struct res_lib_cfg_ringreenable res_lib_cfg_ringreenable; ENTER(); res_lib_cfg_ringreenable.header.id = MESSAGE_RES_CFG_RINGREENABLE; res_lib_cfg_ringreenable.header.size = sizeof (struct res_lib_cfg_ringreenable); res_lib_cfg_ringreenable.header.error = CS_ERR_NOT_SUPPORTED; api->ipc_response_send ( conn, &res_lib_cfg_ringreenable, sizeof (struct res_lib_cfg_ringreenable)); LEAVE(); } static void message_handler_req_lib_cfg_killnode ( void *conn, const void *msg) { const struct req_lib_cfg_killnode *req_lib_cfg_killnode = msg; struct res_lib_cfg_killnode res_lib_cfg_killnode; struct req_exec_cfg_killnode req_exec_cfg_killnode; struct iovec iovec; char key_name[ICMAP_KEYNAME_MAXLEN]; char tmp_key[ICMAP_KEYNAME_MAXLEN + 1]; icmap_map_t map; icmap_iter_t iter; const char *iter_key; uint32_t nodeid; char *status_str = NULL; int match_nodeid_flag = 0; cs_error_t error = CS_OK; ENTER(); map = icmap_get_global_map(); iter = icmap_iter_init_r(map, "runtime.members."); while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) { if (sscanf(iter_key, "runtime.members.%u.%s", &nodeid, key_name) != 2) { continue; } if (strcmp(key_name, "status") != 0) { continue; } if (nodeid != req_lib_cfg_killnode->nodeid) { continue; } match_nodeid_flag = 1; snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "runtime.members.%u.status", nodeid); if (icmap_get_string_r(map, tmp_key, &status_str) != CS_OK) { error = CS_ERR_LIBRARY; goto send_response; } if (strcmp(status_str, "joined") != 0) { error = CS_ERR_NOT_EXIST; goto send_response; } break; } if (!match_nodeid_flag) { error = CS_ERR_NOT_EXIST; goto send_response; } req_exec_cfg_killnode.header.size = sizeof (struct req_exec_cfg_killnode); req_exec_cfg_killnode.header.id = SERVICE_ID_MAKE (CFG_SERVICE, MESSAGE_REQ_EXEC_CFG_KILLNODE); req_exec_cfg_killnode.nodeid = req_lib_cfg_killnode->nodeid; marshall_to_mar_name_t(&req_exec_cfg_killnode.reason, &req_lib_cfg_killnode->reason); iovec.iov_base = (char *)&req_exec_cfg_killnode; iovec.iov_len = sizeof (struct req_exec_cfg_killnode); (void)api->totem_mcast (&iovec, 1, TOTEM_SAFE); send_response: res_lib_cfg_killnode.header.size = sizeof(struct res_lib_cfg_killnode); res_lib_cfg_killnode.header.id = MESSAGE_RES_CFG_KILLNODE; res_lib_cfg_killnode.header.error = error; api->ipc_response_send(conn, &res_lib_cfg_killnode, sizeof(res_lib_cfg_killnode)); free(status_str); icmap_iter_finalize(iter); LEAVE(); } static void message_handler_req_lib_cfg_tryshutdown ( void *conn, const void *msg) { struct cfg_info *ci = (struct cfg_info *)api->ipc_private_data_get (conn); const struct req_lib_cfg_tryshutdown *req_lib_cfg_tryshutdown = msg; struct qb_list_head *iter; ENTER(); if (req_lib_cfg_tryshutdown->flags == CFG_SHUTDOWN_FLAG_IMMEDIATE) { struct res_lib_cfg_tryshutdown res_lib_cfg_tryshutdown; /* * Tell other nodes */ send_shutdown(); res_lib_cfg_tryshutdown.header.size = sizeof(struct res_lib_cfg_tryshutdown); res_lib_cfg_tryshutdown.header.id = MESSAGE_RES_CFG_TRYSHUTDOWN; res_lib_cfg_tryshutdown.header.error = CS_OK; api->ipc_response_send(conn, &res_lib_cfg_tryshutdown, sizeof(res_lib_cfg_tryshutdown)); LEAVE(); return; } /* * Shutdown in progress, return an error */ if (shutdown_con) { struct res_lib_cfg_tryshutdown res_lib_cfg_tryshutdown; res_lib_cfg_tryshutdown.header.size = sizeof(struct res_lib_cfg_tryshutdown); res_lib_cfg_tryshutdown.header.id = MESSAGE_RES_CFG_TRYSHUTDOWN; res_lib_cfg_tryshutdown.header.error = CS_ERR_EXIST; api->ipc_response_send(conn, &res_lib_cfg_tryshutdown, sizeof(res_lib_cfg_tryshutdown)); LEAVE(); return; } ci->conn = conn; shutdown_con = (struct cfg_info *)api->ipc_private_data_get (conn); shutdown_flags = req_lib_cfg_tryshutdown->flags; shutdown_yes = 0; shutdown_no = 0; /* * Count the number of listeners */ shutdown_expected = 0; qb_list_for_each(iter, &trackers_list) { struct cfg_info *testci = qb_list_entry(iter, struct cfg_info, list); /* * It is assumed that we will allow shutdown */ if (testci != ci) { testci->shutdown_reply = SHUTDOWN_REPLY_UNKNOWN; shutdown_expected++; } } /* * If no-one is listening for events then we can just go down now */ if (shutdown_expected == 0) { struct res_lib_cfg_tryshutdown res_lib_cfg_tryshutdown; res_lib_cfg_tryshutdown.header.size = sizeof(struct res_lib_cfg_tryshutdown); res_lib_cfg_tryshutdown.header.id = MESSAGE_RES_CFG_TRYSHUTDOWN; res_lib_cfg_tryshutdown.header.error = CS_OK; /* * Tell originator that shutdown was confirmed */ api->ipc_response_send(conn, &res_lib_cfg_tryshutdown, sizeof(res_lib_cfg_tryshutdown)); send_shutdown(); LEAVE(); return; } else { unsigned int shutdown_timeout = DEFAULT_SHUTDOWN_TIMEOUT; /* * Look for a shutdown timeout in configuration map */ icmap_get_uint32("cfg.shutdown_timeout", &shutdown_timeout); /* * Start the timer. If we don't get a full set of replies before this goes * off we'll cancel the shutdown */ api->timer_add_duration((unsigned long long)shutdown_timeout*1000000000, NULL, shutdown_timer_fn, &shutdown_timer); /* * Tell the users we would like to shut down */ send_test_shutdown(NULL, conn, CS_OK); } /* * We don't sent a reply to the caller here. * We send it when we know if we can shut down or not */ LEAVE(); } static void message_handler_req_lib_cfg_replytoshutdown ( void *conn, const void *msg) { struct cfg_info *ci = (struct cfg_info *)api->ipc_private_data_get (conn); const struct req_lib_cfg_replytoshutdown *req_lib_cfg_replytoshutdown = msg; struct res_lib_cfg_replytoshutdown res_lib_cfg_replytoshutdown; int status = CS_OK; ENTER(); if (!shutdown_con) { status = CS_ERR_ACCESS; goto exit_fn; } if (req_lib_cfg_replytoshutdown->response) { shutdown_yes++; ci->shutdown_reply = SHUTDOWN_REPLY_YES; } else { shutdown_no++; ci->shutdown_reply = SHUTDOWN_REPLY_NO; } check_shutdown_status(); exit_fn: res_lib_cfg_replytoshutdown.header.error = status; res_lib_cfg_replytoshutdown.header.id = MESSAGE_RES_CFG_REPLYTOSHUTDOWN; res_lib_cfg_replytoshutdown.header.size = sizeof(res_lib_cfg_replytoshutdown); api->ipc_response_send(conn, &res_lib_cfg_replytoshutdown, sizeof(res_lib_cfg_replytoshutdown)); LEAVE(); } static void message_handler_req_lib_cfg_get_node_addrs (void *conn, const void *msg) { struct totem_ip_address node_ifs[INTERFACE_MAX]; unsigned int iface_ids[INTERFACE_MAX]; char buf[PIPE_BUF]; char **status; unsigned int num_interfaces = 0; struct sockaddr_storage *ss; int ret = CS_OK; int i; int live_addrs = 0; const struct req_lib_cfg_get_node_addrs *req_lib_cfg_get_node_addrs = msg; struct res_lib_cfg_get_node_addrs *res_lib_cfg_get_node_addrs = (struct res_lib_cfg_get_node_addrs *)buf; unsigned int nodeid = req_lib_cfg_get_node_addrs->nodeid; char *addr_buf; if (nodeid == 0) nodeid = api->totem_nodeid_get(); if (api->totem_ifaces_get(nodeid, iface_ids, node_ifs, INTERFACE_MAX, &status, &num_interfaces)) { ret = CS_ERR_EXIST; num_interfaces = 0; } res_lib_cfg_get_node_addrs->header.size = sizeof(struct res_lib_cfg_get_node_addrs) + (num_interfaces * TOTEMIP_ADDRLEN); res_lib_cfg_get_node_addrs->header.id = MESSAGE_RES_CFG_GET_NODE_ADDRS; res_lib_cfg_get_node_addrs->header.error = ret; if (num_interfaces) { res_lib_cfg_get_node_addrs->family = node_ifs[0].family; for (i = 0, addr_buf = (char *)res_lib_cfg_get_node_addrs->addrs; i < num_interfaces; i++) { ss = (struct sockaddr_storage *)&node_ifs[i].addr; if (ss->ss_family) { memcpy(addr_buf, node_ifs[i].addr, TOTEMIP_ADDRLEN); live_addrs++; addr_buf += TOTEMIP_ADDRLEN; } } res_lib_cfg_get_node_addrs->num_addrs = live_addrs; } else { res_lib_cfg_get_node_addrs->header.error = CS_ERR_NOT_EXIST; } api->ipc_response_send(conn, res_lib_cfg_get_node_addrs, res_lib_cfg_get_node_addrs->header.size); } static void message_handler_req_lib_cfg_local_get (void *conn, const void *msg) { struct res_lib_cfg_local_get res_lib_cfg_local_get; res_lib_cfg_local_get.header.size = sizeof(res_lib_cfg_local_get); res_lib_cfg_local_get.header.id = MESSAGE_RES_CFG_LOCAL_GET; res_lib_cfg_local_get.header.error = CS_OK; res_lib_cfg_local_get.local_nodeid = api->totem_nodeid_get (); api->ipc_response_send(conn, &res_lib_cfg_local_get, sizeof(res_lib_cfg_local_get)); } static void message_handler_req_lib_cfg_reload_config (void *conn, const void *msg) { struct req_exec_cfg_reload_config req_exec_cfg_reload_config; struct iovec iovec; ENTER(); req_exec_cfg_reload_config.header.size = sizeof (struct req_exec_cfg_reload_config); req_exec_cfg_reload_config.header.id = SERVICE_ID_MAKE (CFG_SERVICE, MESSAGE_REQ_EXEC_CFG_RELOAD_CONFIG); api->ipc_source_set (&req_exec_cfg_reload_config.source, conn); api->ipc_refcnt_inc(conn); iovec.iov_base = (char *)&req_exec_cfg_reload_config; iovec.iov_len = sizeof (struct req_exec_cfg_reload_config); assert (api->totem_mcast (&iovec, 1, TOTEM_SAFE) == 0); LEAVE(); } static void message_handler_req_lib_cfg_reopen_log_files (void *conn, const void *msg) { struct res_lib_cfg_reopen_log_files res_lib_cfg_reopen_log_files; cs_error_t res; ENTER(); log_printf(LOGSYS_LEVEL_DEBUG, "Reopening logging files\n"); res = logsys_reopen_log_files(); res_lib_cfg_reopen_log_files.header.size = sizeof(res_lib_cfg_reopen_log_files); res_lib_cfg_reopen_log_files.header.id = MESSAGE_RES_CFG_REOPEN_LOG_FILES; res_lib_cfg_reopen_log_files.header.error = res; api->ipc_response_send(conn, &res_lib_cfg_reopen_log_files, sizeof(res_lib_cfg_reopen_log_files)); LEAVE(); } diff --git a/include/corosync/cfg.h b/include/corosync/cfg.h index c9cd06d0..a7babc28 100644 --- a/include/corosync/cfg.h +++ b/include/corosync/cfg.h @@ -1,286 +1,291 @@ /* * Copyright (c) 2005 MontaVista Software, Inc. * Copyright (c) 2006-2013 Red Hat, Inc. * * All rights reserved. * * Author: Steven Dake (sdake@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the MontaVista Software, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef COROSYNC_CFG_H_DEFINED #define COROSYNC_CFG_H_DEFINED #include <netinet/in.h> #include <corosync/corotypes.h> typedef uint64_t corosync_cfg_handle_t; /** * Shutdown types. */ typedef enum { /** * REQUEST is the normal shutdown. * Other daemons will be consulted. */ COROSYNC_CFG_SHUTDOWN_FLAG_REQUEST = 0, /** * REGARDLESS will tell other daemons but ignore their opinions. */ COROSYNC_CFG_SHUTDOWN_FLAG_REGARDLESS = 1, /** * IMMEDIATE will shut down straight away * (but still tell other nodes). */ COROSYNC_CFG_SHUTDOWN_FLAG_IMMEDIATE = 2, } corosync_cfg_shutdown_flags_t; /** * @brief enum corosync_cfg_shutdown_reply_flags_t */ typedef enum { COROSYNC_CFG_SHUTDOWN_FLAG_NO = 0, COROSYNC_CFG_SHUTDOWN_FLAG_YES = 1, } corosync_cfg_shutdown_reply_flags_t; /** * @brief corosync_cfg_shutdown_callback_t callback */ typedef void (*corosync_cfg_shutdown_callback_t) ( corosync_cfg_handle_t cfg_handle, corosync_cfg_shutdown_flags_t flags); /** * @brief struct corosync_cfg_shutdown_callback_t */ typedef struct { corosync_cfg_shutdown_callback_t corosync_cfg_shutdown_callback; } corosync_cfg_callbacks_t; /** * A node address. This is a complete sockaddr_in[6] * * To explain: * If you cast cna_address to a 'struct sockaddr', the sa_family field * will be AF_INET or AF_INET6. Armed with that knowledge you can then * cast it to a sockaddr_in or sockaddr_in6 and pull out the address. * No other sockaddr fields are valid. * Also, you must ignore any part of the sockaddr beyond the length supplied */ typedef struct { int address_length; /**< @todo FIXME: set but never used */ char address[sizeof(struct sockaddr_in6)]; } corosync_cfg_node_address_t; /* * Interfaces */ #ifdef __cplusplus extern "C" { #endif /** * @brief corosync_cfg_initialize * @param cfg_handle * @param cfg_callbacks * @return */ cs_error_t corosync_cfg_initialize ( corosync_cfg_handle_t *cfg_handle, const corosync_cfg_callbacks_t *cfg_callbacks); /** * @brief corosync_cfg_fd_get * @param cfg_handle * @param selection_fd * @return */ cs_error_t corosync_cfg_fd_get ( corosync_cfg_handle_t cfg_handle, int32_t *selection_fd); /** * @brief corosync_cfg_dispatch * @param cfg_handle * @param dispatch_flags * @return */ cs_error_t corosync_cfg_dispatch ( corosync_cfg_handle_t cfg_handle, cs_dispatch_flags_t dispatch_flags); /** * @brief corosync_cfg_finalize * @param cfg_handle * @return */ cs_error_t corosync_cfg_finalize ( corosync_cfg_handle_t cfg_handle); /** * @brief corosync_cfg_ring_status_get * @param cfg_handle * @param interface_names * @param status * @param interface_count * @return */ cs_error_t corosync_cfg_ring_status_get ( corosync_cfg_handle_t cfg_handle, char ***interface_names, char ***status, unsigned int *interface_count); -#define CFG_NODE_STATUS_STRUCT_VERSION 1 +typedef enum { + CFG_NODE_STATUS_V1 = 1, +} corosync_cfg_node_status_version_t; + #define CFG_MAX_HOST_LEN 256 #define CFG_MAX_LINKS 8 -struct corosync_knet_link_status { + +struct corosync_knet_link_status_v1 { uint8_t enabled; /* link is configured and admin enabled for traffic */ uint8_t connected; /* link is connected for data (local view) */ uint8_t dynconnected; /* link has been activated by remote dynip */ unsigned int mtu; /* current detected MTU on this link */ char src_ipaddr[CFG_MAX_HOST_LEN]; char dst_ipaddr[CFG_MAX_HOST_LEN]; }; -struct corosync_knet_node_status { - uint32_t version; +struct corosync_cfg_node_status_v1 { + corosync_cfg_node_status_version_t version; unsigned int nodeid; uint8_t reachable; uint8_t remote; uint8_t external; uint8_t onwire_min; uint8_t onwire_max; uint8_t onwire_ver; - struct corosync_knet_link_status link_status[CFG_MAX_LINKS]; + struct corosync_knet_link_status_v1 link_status[CFG_MAX_LINKS]; }; /** * @brief corosync_cfg_node_status_get * @param cfg_handle * @param nodeid * @param node_status * @return */ cs_error_t corosync_cfg_node_status_get ( corosync_cfg_handle_t cfg_handle, unsigned int nodeid, - struct corosync_knet_node_status *node_status); + corosync_cfg_node_status_version_t version, + void *node_status); /** * @brief corosync_cfg_kill_node * @param cfg_handle * @param nodeid * @param reason * @return */ cs_error_t corosync_cfg_kill_node ( corosync_cfg_handle_t cfg_handle, unsigned int nodeid, const char *reason); /** * @brief corosync_cfg_try_shutdown * @param cfg_handle * @param flags * @return */ cs_error_t corosync_cfg_try_shutdown ( corosync_cfg_handle_t cfg_handle, corosync_cfg_shutdown_flags_t flags); /** * @brief corosync_cfg_replyto_shutdown * @param cfg_handle * @param flags * @return */ cs_error_t corosync_cfg_replyto_shutdown ( corosync_cfg_handle_t cfg_handle, corosync_cfg_shutdown_reply_flags_t flags); /** * @brief corosync_cfg_get_node_addrs * @param cfg_handle * @param nodeid * @param max_addrs * @param num_addrs * @param addrs * @return */ cs_error_t corosync_cfg_get_node_addrs ( corosync_cfg_handle_t cfg_handle, unsigned int nodeid, size_t max_addrs, int *num_addrs, corosync_cfg_node_address_t *addrs); /** * @brief corosync_cfg_local_get * @param handle * @param local_nodeid * @return */ cs_error_t corosync_cfg_local_get ( corosync_cfg_handle_t handle, unsigned int *local_nodeid); /** * @brief corosync_cfg_reload_config * @param handle * @return */ cs_error_t corosync_cfg_reload_config ( corosync_cfg_handle_t handle); /** * @brief Reopen logging files * @param handle CFG service handle * @return CS_OK on success, CS_ERR_NOT_SUPPORTED if reopening of logging files is not available, * otherwise one of common errors. */ cs_error_t corosync_cfg_reopen_log_files ( corosync_cfg_handle_t handle); #ifdef __cplusplus } #endif #endif /* COROSYNC_CFG_H_DEFINED */ diff --git a/include/corosync/ipc_cfg.h b/include/corosync/ipc_cfg.h index b4ac9fc5..65285a68 100644 --- a/include/corosync/ipc_cfg.h +++ b/include/corosync/ipc_cfg.h @@ -1,282 +1,287 @@ /* * Copyright (c) 2005 MontaVista Software, Inc. * Copyright (c) 2009-2013 Red Hat, Inc. * * All rights reserved. * * Author: Steven Dake (sdake@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the MontaVista Software, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef IPC_CFG_H_DEFINED #define IPC_CFG_H_DEFINED #include <netinet/in.h> #include <corosync/corotypes.h> #include <corosync/mar_gen.h> #define CFG_INTERFACE_NAME_MAX_LEN 128 #define CFG_INTERFACE_STATUS_MAX_LEN 512 /* * Too keep future ABI compatibility, this value * is intentionaly bigger then INTERFACE_MAX */ #define CFG_MAX_INTERFACES 16 /** * @brief The req_lib_cfg_types enum */ enum req_lib_cfg_types { MESSAGE_REQ_CFG_RINGSTATUSGET = 0, MESSAGE_REQ_CFG_RINGREENABLE = 1, MESSAGE_REQ_CFG_KILLNODE = 2, MESSAGE_REQ_CFG_TRYSHUTDOWN = 3, MESSAGE_REQ_CFG_REPLYTOSHUTDOWN = 4, MESSAGE_REQ_CFG_GET_NODE_ADDRS = 5, MESSAGE_REQ_CFG_LOCAL_GET = 6, MESSAGE_REQ_CFG_RELOAD_CONFIG = 7, MESSAGE_REQ_CFG_REOPEN_LOG_FILES = 8, MESSAGE_REQ_CFG_NODESTATUSGET = 9 }; /** * @brief The res_lib_cfg_types enum */ enum res_lib_cfg_types { MESSAGE_RES_CFG_RINGSTATUSGET = 0, MESSAGE_RES_CFG_RINGREENABLE = 1, MESSAGE_RES_CFG_STATETRACKSTART = 2, MESSAGE_RES_CFG_STATETRACKSTOP = 3, MESSAGE_RES_CFG_ADMINISTRATIVESTATESET = 4, MESSAGE_RES_CFG_ADMINISTRATIVESTATEGET = 5, MESSAGE_RES_CFG_SERVICELOAD = 6, MESSAGE_RES_CFG_SERVICEUNLOAD = 7, MESSAGE_RES_CFG_KILLNODE = 8, MESSAGE_RES_CFG_TRYSHUTDOWN = 9, MESSAGE_RES_CFG_TESTSHUTDOWN = 10, MESSAGE_RES_CFG_GET_NODE_ADDRS = 11, MESSAGE_RES_CFG_LOCAL_GET = 12, MESSAGE_RES_CFG_REPLYTOSHUTDOWN = 13, MESSAGE_RES_CFG_RELOAD_CONFIG = 14, MESSAGE_RES_CFG_REOPEN_LOG_FILES = 15, MESSAGE_RES_CFG_NODESTATUSGET = 16 }; /** * @brief The req_lib_cfg_ringstatusget struct */ struct req_lib_cfg_ringstatusget { struct qb_ipc_request_header header __attribute__((aligned(8))); }; /** * @brief The res_lib_cfg_ringstatusget struct */ struct res_lib_cfg_ringstatusget { struct qb_ipc_response_header header __attribute__((aligned(8))); mar_uint32_t interface_count __attribute__((aligned(8))); char interface_name[CFG_MAX_INTERFACES][CFG_INTERFACE_NAME_MAX_LEN] __attribute__((aligned(8))); char interface_status[CFG_MAX_INTERFACES][CFG_INTERFACE_STATUS_MAX_LEN] __attribute__((aligned(8))); }; /** * @brief The req_lib_cfg_nodestatusget struct */ struct req_lib_cfg_nodestatusget { struct qb_ipc_request_header header __attribute__((aligned(8))); unsigned int nodeid __attribute__((aligned(8))); mar_uint32_t version __attribute__((aligned(8))); }; +struct res_lib_cfg_nodestatusget_version { + struct qb_ipc_response_header header __attribute__((aligned(8))); + corosync_cfg_node_status_version_t version __attribute__((aligned(8))); +}; + /** * @brief The res_lib_cfg_nodestatusget struct */ -struct res_lib_cfg_nodestatusget { +struct res_lib_cfg_nodestatusget_v1 { struct qb_ipc_response_header header __attribute__((aligned(8))); - struct corosync_knet_node_status node_status __attribute__((aligned(8))); + struct corosync_cfg_node_status_v1 node_status __attribute__((aligned(8))); }; /** * @brief The req_lib_cfg_ringreenable struct */ struct req_lib_cfg_ringreenable { struct qb_ipc_request_header header __attribute__((aligned(8))); }; /** * @brief The res_lib_cfg_ringreenable struct */ struct res_lib_cfg_ringreenable { struct qb_ipc_response_header header __attribute__((aligned(8))); }; /** * @brief The req_lib_cfg_killnode struct */ struct req_lib_cfg_killnode { struct qb_ipc_request_header header __attribute__((aligned(8))); unsigned int nodeid __attribute__((aligned(8))); cs_name_t reason __attribute__((aligned(8))); }; /** * @brief The res_lib_cfg_killnode struct */ struct res_lib_cfg_killnode { struct qb_ipc_response_header header __attribute__((aligned(8))); }; /** * @brief The req_lib_cfg_tryshutdown struct */ struct req_lib_cfg_tryshutdown { struct qb_ipc_request_header header __attribute__((aligned(8))); unsigned int flags; }; /** * @brief The res_lib_cfg_tryshutdown struct */ struct res_lib_cfg_tryshutdown { struct qb_ipc_response_header header __attribute__((aligned(8))); }; /** * @brief The req_lib_cfg_replytoshutdown struct */ struct req_lib_cfg_replytoshutdown { struct qb_ipc_request_header header __attribute__((aligned(8))); unsigned int response; }; /** * @brief The res_lib_cfg_replytoshutdown struct */ struct res_lib_cfg_replytoshutdown { struct qb_ipc_response_header header __attribute__((aligned(8))); }; /** * @brief The res_lib_cfg_testshutdown struct */ struct res_lib_cfg_testshutdown { struct qb_ipc_response_header header __attribute__((aligned(8))); unsigned int flags; }; /** * @brief The req_lib_cfg_get_node_addrs struct */ struct req_lib_cfg_get_node_addrs { struct qb_ipc_request_header header __attribute__((aligned(8))); unsigned int nodeid; }; /** * @brief The res_lib_cfg_get_node_addrs struct */ struct res_lib_cfg_get_node_addrs { struct qb_ipc_response_header header __attribute__((aligned(8))); unsigned int family; unsigned int num_addrs; /* array of TOTEMIP_ADDRLEN items */ char addrs[]; }; /** * @brief The req_lib_cfg_local_get struct */ struct req_lib_cfg_local_get { struct qb_ipc_request_header header __attribute__((aligned(8))); }; /** * @brief The res_lib_cfg_local_get struct */ struct res_lib_cfg_local_get { struct qb_ipc_response_header header __attribute__((aligned(8))); mar_uint32_t local_nodeid __attribute__((aligned(8))); }; /** * @brief The req_lib_cfg_reload_config struct */ struct req_lib_cfg_reload_config { struct qb_ipc_request_header header __attribute__((aligned(8))); }; /** * @brief The res_lib_cfg_reload_config struct */ struct res_lib_cfg_reload_config { struct qb_ipc_response_header header __attribute__((aligned(8))); }; /** * @brief The req_lib_cfg_reopen_log_files struct */ struct req_lib_cfg_reopen_log_files { struct qb_ipc_request_header header __attribute__((aligned(8))); }; /** * @brief The res_lib_cfg_reopen_log_files struct */ struct res_lib_cfg_reopen_log_files { struct qb_ipc_response_header header __attribute__((aligned(8))); }; /** * @brief corosync_administrative_target_t enum */ typedef enum { AIS_AMF_ADMINISTRATIVETARGET_SERVICEUNIT = 0, AIS_AMF_ADMINISTRATIVETARGET_SERVICEGROUP = 1, AIS_AMF_ADMINISTRATIVETARGET_COMPONENTSERVICEINSTANCE = 2, AIS_AMF_ADMINISTRATIVETARGET_NODE = 3 } corosync_administrative_target_t; /** * @brief corosync_administrative_state_t enum */ typedef enum { AIS_AMF_ADMINISTRATIVESTATE_UNLOCKED = 0, AIS_AMF_ADMINISTRATIVESTATE_LOCKED = 1, AIS_AMF_ADMINISTRATIVESTATE_STOPPING = 2 } corosync_administrative_state_t; /** * @brief corosync_shutdown_flags_t enum */ typedef enum { CFG_SHUTDOWN_FLAG_REQUEST = 0, CFG_SHUTDOWN_FLAG_REGARDLESS = 1, CFG_SHUTDOWN_FLAG_IMMEDIATE = 2, } corosync_shutdown_flags_t; #endif /* IPC_CFG_H_DEFINED */ diff --git a/lib/cfg.c b/lib/cfg.c index 16ce6be5..4ce3582d 100644 --- a/lib/cfg.c +++ b/lib/cfg.c @@ -1,736 +1,766 @@ /* * Copyright (c) 2002-2005 MontaVista Software, Inc. * Copyright (c) 2006-2020 Red Hat, Inc. * * All rights reserved. * * Author: Steven Dake (sdake@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the MontaVista Software, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include <config.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <pthread.h> #include <limits.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/select.h> #include <sys/un.h> #include <sys/uio.h> #include <qb/qbipcc.h> #include <corosync/corotypes.h> #include <corosync/corodefs.h> #include <corosync/hdb.h> #include <corosync/cfg.h> #include <corosync/ipc_cfg.h> #include "util.h" /* * Data structure for instance data */ struct cfg_inst { qb_ipcc_connection_t *c; corosync_cfg_callbacks_t callbacks; cs_name_t comp_name; int comp_registered; int finalize; }; /* * All instances in one database */ static void cfg_inst_free (void *inst); DECLARE_HDB_DATABASE (cfg_hdb, cfg_inst_free); /* * Implementation */ cs_error_t corosync_cfg_initialize ( corosync_cfg_handle_t *cfg_handle, const corosync_cfg_callbacks_t *cfg_callbacks) { struct cfg_inst *cfg_inst; cs_error_t error = CS_OK; error = hdb_error_to_cs (hdb_handle_create (&cfg_hdb, sizeof (struct cfg_inst), cfg_handle)); if (error != CS_OK) { goto error_no_destroy; } error = hdb_error_to_cs (hdb_handle_get (&cfg_hdb, *cfg_handle, (void *)&cfg_inst)); if (error != CS_OK) { goto error_destroy; } cfg_inst->finalize = 0; cfg_inst->c = qb_ipcc_connect ("cfg", IPC_REQUEST_SIZE); if (cfg_inst->c == NULL) { error = qb_to_cs_error(-errno); goto error_put_destroy; } if (cfg_callbacks) { memcpy (&cfg_inst->callbacks, cfg_callbacks, sizeof (corosync_cfg_callbacks_t)); } (void)hdb_handle_put (&cfg_hdb, *cfg_handle); return (CS_OK); error_put_destroy: (void)hdb_handle_put (&cfg_hdb, *cfg_handle); error_destroy: (void)hdb_handle_destroy (&cfg_hdb, *cfg_handle); error_no_destroy: return (error); } cs_error_t corosync_cfg_fd_get ( corosync_cfg_handle_t cfg_handle, int32_t *selection_fd) { struct cfg_inst *cfg_inst; cs_error_t error; error = hdb_error_to_cs (hdb_handle_get (&cfg_hdb, cfg_handle, (void *)&cfg_inst)); if (error != CS_OK) { return (error); } error = qb_to_cs_error (qb_ipcc_fd_get (cfg_inst->c, selection_fd)); (void)hdb_handle_put (&cfg_hdb, cfg_handle); return (error); } cs_error_t corosync_cfg_dispatch ( corosync_cfg_handle_t cfg_handle, cs_dispatch_flags_t dispatch_flags) { int timeout = -1; cs_error_t error; int cont = 1; /* always continue do loop except when set to 0 */ struct cfg_inst *cfg_inst; struct res_lib_cfg_testshutdown *res_lib_cfg_testshutdown; corosync_cfg_callbacks_t callbacks; struct qb_ipc_response_header *dispatch_data; char dispatch_buf[IPC_DISPATCH_SIZE]; error = hdb_error_to_cs (hdb_handle_get (&cfg_hdb, cfg_handle, (void *)&cfg_inst)); if (error != CS_OK) { return (error); } /* * Timeout instantly for CS_DISPATCH_ONE_NONBLOCKING or CS_DISPATCH_ALL and * wait indefinately for CS_DISPATCH_ONE or CS_DISPATCH_BLOCKING */ if (dispatch_flags == CS_DISPATCH_ALL || dispatch_flags == CS_DISPATCH_ONE_NONBLOCKING) { timeout = 0; } dispatch_data = (struct qb_ipc_response_header *)dispatch_buf; do { error = qb_to_cs_error (qb_ipcc_event_recv ( cfg_inst->c, dispatch_buf, IPC_DISPATCH_SIZE, timeout)); if (error == CS_ERR_BAD_HANDLE) { error = CS_OK; goto error_put; } if (error == CS_ERR_TRY_AGAIN) { if (dispatch_flags == CS_DISPATCH_ONE_NONBLOCKING) { /* * Don't mask error */ goto error_put; } error = CS_OK; if (dispatch_flags == CS_DISPATCH_ALL) { break; /* exit do while cont is 1 loop */ } else { continue; /* next poll */ } } if (error != CS_OK) { goto error_put; } /* * Make copy of callbacks, message data, unlock instance, and call callback * A risk of this dispatch method is that the callback routines may * operate at the same time that cfgFinalize has been called in another thread. */ memcpy (&callbacks, &cfg_inst->callbacks, sizeof (corosync_cfg_callbacks_t)); /* * Dispatch incoming response */ switch (dispatch_data->id) { case MESSAGE_RES_CFG_TESTSHUTDOWN: if (callbacks.corosync_cfg_shutdown_callback == NULL) { break; } res_lib_cfg_testshutdown = (struct res_lib_cfg_testshutdown *)dispatch_data; callbacks.corosync_cfg_shutdown_callback(cfg_handle, res_lib_cfg_testshutdown->flags); break; default: error = CS_ERR_LIBRARY; goto error_nounlock; break; } if (cfg_inst->finalize) { /* * If the finalize has been called then get out of the dispatch. */ error = CS_ERR_BAD_HANDLE; goto error_put; } /* * Determine if more messages should be processed */ if (dispatch_flags == CS_DISPATCH_ONE || dispatch_flags == CS_DISPATCH_ONE_NONBLOCKING) { cont = 0; } } while (cont); error_put: (void)hdb_handle_put (&cfg_hdb, cfg_handle); error_nounlock: return (error); } static void cfg_inst_free (void *inst) { struct cfg_inst *cfg_inst = (struct cfg_inst *)inst; qb_ipcc_disconnect(cfg_inst->c); } cs_error_t corosync_cfg_finalize ( corosync_cfg_handle_t cfg_handle) { struct cfg_inst *cfg_inst; cs_error_t error; error = hdb_error_to_cs(hdb_handle_get (&cfg_hdb, cfg_handle, (void *)&cfg_inst)); if (error != CS_OK) { return (error); } /* * Another thread has already started finalizing */ if (cfg_inst->finalize) { (void)hdb_handle_put (&cfg_hdb, cfg_handle); return (CS_ERR_BAD_HANDLE); } cfg_inst->finalize = 1; (void)hdb_handle_destroy (&cfg_hdb, cfg_handle); (void)hdb_handle_put (&cfg_hdb, cfg_handle); return (error); } cs_error_t corosync_cfg_ring_status_get ( corosync_cfg_handle_t cfg_handle, char ***interface_names, char ***status, unsigned int *interface_count) { struct cfg_inst *cfg_inst; struct req_lib_cfg_ringstatusget req_lib_cfg_ringstatusget; struct res_lib_cfg_ringstatusget res_lib_cfg_ringstatusget; unsigned int i, j; cs_error_t error; struct iovec iov; error = hdb_error_to_cs(hdb_handle_get (&cfg_hdb, cfg_handle, (void *)&cfg_inst)); if (error != CS_OK) { return (error); } req_lib_cfg_ringstatusget.header.size = sizeof (struct req_lib_cfg_ringstatusget); req_lib_cfg_ringstatusget.header.id = MESSAGE_REQ_CFG_RINGSTATUSGET; iov.iov_base = (void *)&req_lib_cfg_ringstatusget, iov.iov_len = sizeof (struct req_lib_cfg_ringstatusget), error = qb_to_cs_error (qb_ipcc_sendv_recv(cfg_inst->c, &iov, 1, &res_lib_cfg_ringstatusget, sizeof (struct res_lib_cfg_ringstatusget), CS_IPC_TIMEOUT_MS)); if (error != CS_OK) { goto exit_handle_put; } *interface_count = res_lib_cfg_ringstatusget.interface_count; *interface_names = malloc (sizeof (char *) * *interface_count); if (*interface_names == NULL) { return (CS_ERR_NO_MEMORY); } memset (*interface_names, 0, sizeof (char *) * *interface_count); *status = malloc (sizeof (char *) * *interface_count); if (*status == NULL) { error = CS_ERR_NO_MEMORY; goto error_free_interface_names_array; } memset (*status, 0, sizeof (char *) * *interface_count); for (i = 0; i < res_lib_cfg_ringstatusget.interface_count; i++) { (*(interface_names))[i] = strdup (res_lib_cfg_ringstatusget.interface_name[i]); if ((*(interface_names))[i] == NULL) { error = CS_ERR_NO_MEMORY; goto error_free_interface_names; } } for (i = 0; i < res_lib_cfg_ringstatusget.interface_count; i++) { (*(status))[i] = strdup (res_lib_cfg_ringstatusget.interface_status[i]); if ((*(status))[i] == NULL) { error = CS_ERR_NO_MEMORY; goto error_free_status; } } goto exit_handle_put; error_free_status: for (j = 0; j < i; j++) { free ((*(status))[j]); } i = *interface_count; error_free_interface_names: for (j = 0; j < i; j++) { free ((*(interface_names))[j]); } free (*status); error_free_interface_names_array: free (*interface_names); exit_handle_put: (void)hdb_handle_put (&cfg_hdb, cfg_handle); return (error); } cs_error_t corosync_cfg_node_status_get ( corosync_cfg_handle_t cfg_handle, unsigned int nodeid, - struct corosync_knet_node_status *node_status) + corosync_cfg_node_status_version_t version, + void *node_status) { struct cfg_inst *cfg_inst; struct req_lib_cfg_nodestatusget req_lib_cfg_nodestatusget; - struct res_lib_cfg_nodestatusget res_lib_cfg_nodestatusget; cs_error_t error; struct iovec iov; + size_t cfg_node_status_size; + void *res_lib_cfg_nodestatuget_ptr; + struct res_lib_cfg_nodestatusget_v1 res_lib_cfg_nodestatusget_v1; + struct res_lib_cfg_nodestatusget_version *res_lib_cfg_nodestatusget_version; if (!node_status) { return (CS_ERR_INVALID_PARAM); } + switch (version) { + case CFG_NODE_STATUS_V1: + cfg_node_status_size = sizeof(struct res_lib_cfg_nodestatusget_v1); + res_lib_cfg_nodestatuget_ptr = &res_lib_cfg_nodestatusget_v1; + + break; + default: + return (CS_ERR_INVALID_PARAM); + break; + } + error = hdb_error_to_cs(hdb_handle_get (&cfg_hdb, cfg_handle, (void *)&cfg_inst)); if (error != CS_OK) { return (error); } req_lib_cfg_nodestatusget.header.size = sizeof (struct req_lib_cfg_nodestatusget); req_lib_cfg_nodestatusget.header.id = MESSAGE_REQ_CFG_NODESTATUSGET; req_lib_cfg_nodestatusget.nodeid = nodeid; - req_lib_cfg_nodestatusget.version = CFG_NODE_STATUS_STRUCT_VERSION; + req_lib_cfg_nodestatusget.version = version; iov.iov_base = (void *)&req_lib_cfg_nodestatusget, iov.iov_len = sizeof (struct req_lib_cfg_nodestatusget), error = qb_to_cs_error (qb_ipcc_sendv_recv(cfg_inst->c, &iov, 1, - &res_lib_cfg_nodestatusget, - sizeof (struct res_lib_cfg_nodestatusget), CS_IPC_TIMEOUT_MS)); + res_lib_cfg_nodestatuget_ptr, + cfg_node_status_size, CS_IPC_TIMEOUT_MS)); + if (error != CS_OK) { + goto error_put; + } - if (error == CS_OK) { - memcpy(node_status, &res_lib_cfg_nodestatusget.node_status, sizeof(struct corosync_knet_node_status)); + res_lib_cfg_nodestatusget_version = res_lib_cfg_nodestatuget_ptr; + error = res_lib_cfg_nodestatusget_version->header.error; + if (error != CS_OK) { + goto error_put; } - /* corosync sent us something we don't really understand. - - we might need to revisit this in the case of future structure versions */ - if (res_lib_cfg_nodestatusget.node_status.version != CFG_NODE_STATUS_STRUCT_VERSION) { + if (res_lib_cfg_nodestatusget_version->version != version) { + /* + * corosync sent us something we don't really understand. + */ error = CS_ERR_NOT_SUPPORTED; + goto error_put; } + switch (version) { + case CFG_NODE_STATUS_V1: + memcpy(node_status, &res_lib_cfg_nodestatusget_v1.node_status, + sizeof(struct corosync_cfg_node_status_v1)); + break; + } + +error_put: (void)hdb_handle_put (&cfg_hdb, cfg_handle); return (error); } cs_error_t corosync_cfg_kill_node ( corosync_cfg_handle_t cfg_handle, unsigned int nodeid, const char *reason) { struct cfg_inst *cfg_inst; struct req_lib_cfg_killnode req_lib_cfg_killnode; struct res_lib_cfg_killnode res_lib_cfg_killnode; cs_error_t error; struct iovec iov; if (strlen(reason) >= CS_MAX_NAME_LENGTH) return CS_ERR_NAME_TOO_LONG; error = hdb_error_to_cs (hdb_handle_get (&cfg_hdb, cfg_handle, (void *)&cfg_inst)); if (error != CS_OK) { return (error); } req_lib_cfg_killnode.header.id = MESSAGE_REQ_CFG_KILLNODE; req_lib_cfg_killnode.header.size = sizeof (struct req_lib_cfg_killnode); req_lib_cfg_killnode.nodeid = nodeid; strcpy((char *)req_lib_cfg_killnode.reason.value, reason); req_lib_cfg_killnode.reason.length = strlen(reason)+1; iov.iov_base = (void *)&req_lib_cfg_killnode; iov.iov_len = sizeof (struct req_lib_cfg_killnode); error = qb_to_cs_error (qb_ipcc_sendv_recv (cfg_inst->c, &iov, 1, &res_lib_cfg_killnode, sizeof (struct res_lib_cfg_killnode), CS_IPC_TIMEOUT_MS)); error = res_lib_cfg_killnode.header.error; (void)hdb_handle_put (&cfg_hdb, cfg_handle); return (error == CS_OK ? res_lib_cfg_killnode.header.error : error); } cs_error_t corosync_cfg_try_shutdown ( corosync_cfg_handle_t cfg_handle, corosync_cfg_shutdown_flags_t flags) { struct cfg_inst *cfg_inst; struct req_lib_cfg_tryshutdown req_lib_cfg_tryshutdown; struct res_lib_cfg_tryshutdown res_lib_cfg_tryshutdown; cs_error_t error; struct iovec iov; error = hdb_error_to_cs(hdb_handle_get (&cfg_hdb, cfg_handle, (void *)&cfg_inst)); if (error != CS_OK) { return (error); } req_lib_cfg_tryshutdown.header.id = MESSAGE_REQ_CFG_TRYSHUTDOWN; req_lib_cfg_tryshutdown.header.size = sizeof (struct req_lib_cfg_tryshutdown); req_lib_cfg_tryshutdown.flags = flags; iov.iov_base = (void *)&req_lib_cfg_tryshutdown; iov.iov_len = sizeof (req_lib_cfg_tryshutdown); error = qb_to_cs_error (qb_ipcc_sendv_recv (cfg_inst->c, &iov, 1, &res_lib_cfg_tryshutdown, sizeof (struct res_lib_cfg_tryshutdown), CS_IPC_TIMEOUT_MS)); (void)hdb_handle_put (&cfg_hdb, cfg_handle); return (error == CS_OK ? res_lib_cfg_tryshutdown.header.error : error); } cs_error_t corosync_cfg_replyto_shutdown ( corosync_cfg_handle_t cfg_handle, corosync_cfg_shutdown_reply_flags_t response) { struct cfg_inst *cfg_inst; struct req_lib_cfg_replytoshutdown req_lib_cfg_replytoshutdown; struct res_lib_cfg_replytoshutdown res_lib_cfg_replytoshutdown; struct iovec iov; cs_error_t error; error = hdb_error_to_cs(hdb_handle_get (&cfg_hdb, cfg_handle, (void *)&cfg_inst)); if (error != CS_OK) { return (error); } req_lib_cfg_replytoshutdown.header.id = MESSAGE_REQ_CFG_REPLYTOSHUTDOWN; req_lib_cfg_replytoshutdown.header.size = sizeof (struct req_lib_cfg_replytoshutdown); req_lib_cfg_replytoshutdown.response = response; iov.iov_base = (void *)&req_lib_cfg_replytoshutdown; iov.iov_len = sizeof (struct req_lib_cfg_replytoshutdown); error = qb_to_cs_error (qb_ipcc_sendv_recv (cfg_inst->c, &iov, 1, &res_lib_cfg_replytoshutdown, sizeof (struct res_lib_cfg_replytoshutdown), CS_IPC_TIMEOUT_MS)); return (error); } cs_error_t corosync_cfg_get_node_addrs ( corosync_cfg_handle_t cfg_handle, unsigned int nodeid, size_t max_addrs, int *num_addrs, corosync_cfg_node_address_t *addrs) { cs_error_t error; struct req_lib_cfg_get_node_addrs req_lib_cfg_get_node_addrs; struct res_lib_cfg_get_node_addrs *res_lib_cfg_get_node_addrs; struct cfg_inst *cfg_inst; int addrlen = 0; int i; struct iovec iov; const char *addr_buf; char response_buf[IPC_RESPONSE_SIZE]; char zeroes[sizeof(struct sockaddr_storage)]; error = hdb_error_to_cs(hdb_handle_get (&cfg_hdb, cfg_handle, (void *)&cfg_inst)); if (error != CS_OK) { return (error); } memset(zeroes, 0, sizeof(zeroes)); req_lib_cfg_get_node_addrs.header.size = sizeof (req_lib_cfg_get_node_addrs); req_lib_cfg_get_node_addrs.header.id = MESSAGE_REQ_CFG_GET_NODE_ADDRS; req_lib_cfg_get_node_addrs.nodeid = nodeid; iov.iov_base = (char *)&req_lib_cfg_get_node_addrs; iov.iov_len = sizeof (req_lib_cfg_get_node_addrs); error = qb_to_cs_error (qb_ipcc_sendv_recv ( cfg_inst->c, &iov, 1, response_buf, IPC_RESPONSE_SIZE, CS_IPC_TIMEOUT_MS)); res_lib_cfg_get_node_addrs = (struct res_lib_cfg_get_node_addrs *)response_buf; if (error != CS_OK) { goto error_put; } if (res_lib_cfg_get_node_addrs->family == AF_INET) addrlen = sizeof(struct sockaddr_in); if (res_lib_cfg_get_node_addrs->family == AF_INET6) addrlen = sizeof(struct sockaddr_in6); for (i = 0, addr_buf = (char *)res_lib_cfg_get_node_addrs->addrs; i < max_addrs && i<res_lib_cfg_get_node_addrs->num_addrs; i++, addr_buf += TOTEMIP_ADDRLEN) { struct sockaddr_in *in; struct sockaddr_in6 *in6; addrs[i].address_length = addrlen; if (res_lib_cfg_get_node_addrs->family == AF_INET) { in = (struct sockaddr_in *)addrs[i].address; if (memcmp(addr_buf, zeroes, addrlen) == 0) { in->sin_family = 0; } else { in->sin_family = AF_INET; } memcpy(&in->sin_addr, addr_buf, sizeof(struct in_addr)); } if (res_lib_cfg_get_node_addrs->family == AF_INET6) { in6 = (struct sockaddr_in6 *)addrs[i].address; if (memcmp(addr_buf, zeroes, addrlen) == 0) { in6->sin6_family = 0; } else { in6->sin6_family = AF_INET6; } memcpy(&in6->sin6_addr, addr_buf, sizeof(struct in6_addr)); } /* Mark it as unused */ } *num_addrs = res_lib_cfg_get_node_addrs->num_addrs; errno = error = res_lib_cfg_get_node_addrs->header.error; error_put: hdb_handle_put (&cfg_hdb, cfg_handle); return (error); } cs_error_t corosync_cfg_local_get ( corosync_cfg_handle_t handle, unsigned int *local_nodeid) { cs_error_t error; struct cfg_inst *cfg_inst; struct iovec iov; struct req_lib_cfg_local_get req_lib_cfg_local_get; struct res_lib_cfg_local_get res_lib_cfg_local_get; error = hdb_error_to_cs(hdb_handle_get (&cfg_hdb, handle, (void *)&cfg_inst)); if (error != CS_OK) { return (error); } req_lib_cfg_local_get.header.size = sizeof (struct qb_ipc_request_header); req_lib_cfg_local_get.header.id = MESSAGE_REQ_CFG_LOCAL_GET; iov.iov_base = (void *)&req_lib_cfg_local_get; iov.iov_len = sizeof (struct req_lib_cfg_local_get); error = qb_to_cs_error (qb_ipcc_sendv_recv ( cfg_inst->c, &iov, 1, &res_lib_cfg_local_get, sizeof (struct res_lib_cfg_local_get), CS_IPC_TIMEOUT_MS)); if (error != CS_OK) { goto error_exit; } error = res_lib_cfg_local_get.header.error; *local_nodeid = res_lib_cfg_local_get.local_nodeid; error_exit: (void)hdb_handle_put (&cfg_hdb, handle); return (error); } cs_error_t corosync_cfg_reload_config ( corosync_cfg_handle_t handle) { cs_error_t error; struct cfg_inst *cfg_inst; struct iovec iov; struct req_lib_cfg_reload_config req_lib_cfg_reload_config; struct res_lib_cfg_reload_config res_lib_cfg_reload_config; error = hdb_error_to_cs(hdb_handle_get (&cfg_hdb, handle, (void *)&cfg_inst)); if (error != CS_OK) { return (error); } req_lib_cfg_reload_config.header.size = sizeof (struct qb_ipc_request_header); req_lib_cfg_reload_config.header.id = MESSAGE_REQ_CFG_RELOAD_CONFIG; iov.iov_base = (void *)&req_lib_cfg_reload_config; iov.iov_len = sizeof (struct req_lib_cfg_reload_config); error = qb_to_cs_error (qb_ipcc_sendv_recv ( cfg_inst->c, &iov, 1, &res_lib_cfg_reload_config, sizeof (struct res_lib_cfg_reload_config), CS_IPC_TIMEOUT_MS)); if (error != CS_OK) { goto error_exit; } error = res_lib_cfg_reload_config.header.error; error_exit: (void)hdb_handle_put (&cfg_hdb, handle); return (error); } cs_error_t corosync_cfg_reopen_log_files ( corosync_cfg_handle_t handle) { cs_error_t error; struct cfg_inst *cfg_inst; struct iovec iov; struct req_lib_cfg_reopen_log_files req_lib_cfg_reopen_log_files; struct res_lib_cfg_reopen_log_files res_lib_cfg_reopen_log_files; error = hdb_error_to_cs(hdb_handle_get (&cfg_hdb, handle, (void *)&cfg_inst)); if (error != CS_OK) { return (error); } req_lib_cfg_reopen_log_files.header.size = sizeof (struct qb_ipc_request_header); req_lib_cfg_reopen_log_files.header.id = MESSAGE_REQ_CFG_REOPEN_LOG_FILES; iov.iov_base = (void *)&req_lib_cfg_reopen_log_files; iov.iov_len = sizeof (struct req_lib_cfg_reopen_log_files); error = qb_to_cs_error (qb_ipcc_sendv_recv ( cfg_inst->c, &iov, 1, &res_lib_cfg_reopen_log_files, sizeof (struct res_lib_cfg_reopen_log_files), CS_IPC_TIMEOUT_MS)); if (error != CS_OK) { goto error_exit; } error = res_lib_cfg_reopen_log_files.header.error; error_exit: (void)hdb_handle_put (&cfg_hdb, handle); return (error); } diff --git a/tools/corosync-cfgtool.c b/tools/corosync-cfgtool.c index c4f23f79..0de50bd7 100644 --- a/tools/corosync-cfgtool.c +++ b/tools/corosync-cfgtool.c @@ -1,542 +1,542 @@ /* * Copyright (c) 2006-2020 Red Hat, Inc. * * All rights reserved. * * Author: Steven Dake <sdake@redhat.com> * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the MontaVista Software, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include <config.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> #include <string.h> #include <pthread.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/select.h> #include <sys/un.h> #include <netinet/in.h> #include <arpa/inet.h> #include <limits.h> #include <corosync/corotypes.h> #include <corosync/totem/totem.h> #include <corosync/cfg.h> #include <corosync/cmap.h> #include "util.h" #define cs_repeat(result, max, code) \ do { \ int counter = 0; \ do { \ result = code; \ if (result == CS_ERR_TRY_AGAIN) { \ sleep(1); \ counter++; \ } else { \ break; \ } \ } while (counter < max); \ } while (0) enum user_action { ACTION_NOOP=0, ACTION_LINKSTATUS_GET, ACTION_NODESTATUS_GET, ACTION_RELOAD_CONFIG, ACTION_REOPEN_LOG_FILES, ACTION_SHUTDOW, ACTION_SHOWADDR, ACTION_KILL_NODE, }; static int node_compare(const void *aptr, const void *bptr) { uint32_t a,b; a = *(uint32_t *)aptr; b = *(uint32_t *)bptr; return a > b; } static int nodestatusget_do (enum user_action action, int brief) { cs_error_t result; corosync_cfg_handle_t handle; cmap_handle_t cmap_handle; char iter_key[CMAP_KEYNAME_MAXLEN]; cmap_iter_handle_t iter; unsigned int local_nodeid; unsigned int local_nodeid_index=0; unsigned int other_nodeid_index=0; unsigned int nodeid; int nodeid_match_guard; cmap_value_types_t type; size_t value_len; char *str; char *transport_str = NULL; uint32_t nodeid_list[KNET_MAX_HOST]; int s = 0; int rc = EXIT_SUCCESS; int transport_number = TOTEM_TRANSPORT_KNET; int i,j; - struct corosync_knet_node_status node_status; + struct corosync_cfg_node_status_v1 node_status; result = corosync_cfg_initialize (&handle, NULL); if (result != CS_OK) { fprintf (stderr, "Could not initialize corosync configuration API error %d\n", result); exit (EXIT_FAILURE); } result = cmap_initialize (&cmap_handle); if (result != CS_OK) { fprintf (stderr, "Could not initialize corosync cmap API error %d\n", result); exit (EXIT_FAILURE); } result = cmap_get_string(cmap_handle, "totem.transport", &str); if (result == CS_OK) { if (strcmp (str, "udpu") == 0) { transport_number = TOTEM_TRANSPORT_UDPU; } if (strcmp (str, "udp") == 0) { transport_number = TOTEM_TRANSPORT_UDP; } transport_str = str; } if (!transport_str) { transport_str = strdup("knet"); /* It's the default */ } result = corosync_cfg_local_get(handle, &local_nodeid); if (result != CS_OK) { fprintf (stderr, "Could not get the local node id, the error is: %d\n", result); free(transport_str); cmap_finalize(cmap_handle); corosync_cfg_finalize(handle); return EXIT_FAILURE; } /* Get a list of nodes. We do it this way rather than using votequorum as cfgtool * needs to be independent of quorum type */ result = cmap_iter_init(cmap_handle, "nodelist.node.", &iter); if (result != CS_OK) { fprintf (stderr, "Could not get nodelist from cmap. error %d\n", result); free(transport_str); cmap_finalize(cmap_handle); corosync_cfg_finalize(handle); exit (EXIT_FAILURE); } while ((cmap_iter_next(cmap_handle, iter, iter_key, &value_len, &type)) == CS_OK) { nodeid_match_guard = 0; if (sscanf(iter_key, "nodelist.node.%*u.nodeid%n", &nodeid_match_guard) != 0) { continue; } /* check for exact match */ if (nodeid_match_guard != strlen(iter_key)) { continue; } if (cmap_get_uint32(cmap_handle, iter_key, &nodeid) == CS_OK) { if (nodeid == local_nodeid) { local_nodeid_index = s; } else { /* Bit of an odd one this. but local node only uses one link (of course, to itself) so if we want to know which links are active across the cluster we need to look at another node (any other) node's link list */ other_nodeid_index = s; } nodeid_list[s++] = nodeid; } } /* It's nice to have these in nodeid order */ qsort(nodeid_list, s, sizeof(uint32_t), node_compare); cmap_finalize(cmap_handle); printf ("Local node ID " CS_PRI_NODE_ID ", transport %s\n", local_nodeid, transport_str); /* If node status requested then do print node-based info */ if (action == ACTION_NODESTATUS_GET) { for (i=0; i<s; i++) { - result = corosync_cfg_node_status_get(handle, nodeid_list[i], &node_status); + result = corosync_cfg_node_status_get(handle, nodeid_list[i], CFG_NODE_STATUS_V1, &node_status); if (result == CS_OK) { /* Only display node info if it is reachable (and not us) */ if (node_status.reachable && node_status.nodeid != local_nodeid) { printf("nodeid: %d", node_status.nodeid); printf(" reachable"); if (node_status.remote) { printf(" remote"); } if (node_status.external) { printf(" external"); } #ifdef HAVE_KNET_ONWIRE_VER if (transport_number == TOTEM_TRANSPORT_KNET) { printf(" onwire (min/max/cur): %d, %d, %d", node_status.onwire_min, node_status.onwire_max, node_status.onwire_ver); } #endif printf("\n"); for (j=0; j<CFG_MAX_LINKS; j++) { if (node_status.link_status[j].enabled) { printf(" LINK: %d", j); printf(" (%s%s%s)", node_status.link_status[j].src_ipaddr, transport_number==TOTEM_TRANSPORT_KNET?"->":"", node_status.link_status[j].dst_ipaddr); if (node_status.link_status[j].enabled) { printf(" enabled"); } if (node_status.link_status[j].connected) { printf(" connected"); } if (node_status.link_status[j].dynconnected) { printf(" dynconnected"); } printf(" mtu: %d\n", node_status.link_status[j].mtu); } } printf("\n"); } } } } /* Print in link order */ else { - struct corosync_knet_node_status node_info[s]; + struct corosync_cfg_node_status_v1 node_info[s]; memset(node_info, 0, sizeof(node_info)); for (i=0; i<s; i++) { - result = corosync_cfg_node_status_get(handle, nodeid_list[i], &node_info[i]); + result = corosync_cfg_node_status_get(handle, nodeid_list[i], CFG_NODE_STATUS_V1, &node_info[i]); if (result != CS_OK) { fprintf (stderr, "Could not get the node status for nodeid %d, the error is: %d\n", nodeid_list[i], result); } } for (i=0; i<CFG_MAX_LINKS; i++) { if (node_info[other_nodeid_index].link_status[i].enabled) { printf("LINK ID %d\n", i); printf("\taddr\t= %s\n", node_info[other_nodeid_index].link_status[i].src_ipaddr); if (brief) { printf("\tstatus\t= "); for (j=0; j<s; j++) { char status = (node_info[j].link_status[i].enabled | (node_info[j].link_status[i].connected << 1)) + '0'; if (status == '0') { status = 'n'; } printf("%c", status); } printf("\n"); } else { printf("\tstatus:\n"); for (j=0; j<s; j++) { printf("\t\tnodeid: %3d:\t", node_info[j].nodeid); if (j == local_nodeid_index) { printf("localhost"); } else { if (node_info[j].link_status[i].connected) { printf("connected"); } else { printf("disconnected"); } } printf("\n"); } } } } } free(transport_str); corosync_cfg_finalize(handle); return rc; } static int reload_config_do (void) { cs_error_t result; corosync_cfg_handle_t handle; int rc; rc = EXIT_SUCCESS; printf ("Reloading corosync.conf...\n"); result = corosync_cfg_initialize (&handle, NULL); if (result != CS_OK) { fprintf (stderr, "Could not initialize corosync configuration API error %s\n", cs_strerror(result)); exit (EXIT_FAILURE); } result = corosync_cfg_reload_config (handle); if (result != CS_OK) { fprintf (stderr, "Could not reload configuration. Error %s\n", cs_strerror(result)); rc = (int)result; } else { printf ("Done\n"); } (void)corosync_cfg_finalize (handle); return (rc); } static int reopen_log_files_do (void) { cs_error_t result; corosync_cfg_handle_t handle; int rc; rc = EXIT_SUCCESS; result = corosync_cfg_initialize (&handle, NULL); if (result != CS_OK) { fprintf (stderr, "Could not initialize corosync configuration API error %s\n", cs_strerror(result)); exit (EXIT_FAILURE); } result = corosync_cfg_reopen_log_files (handle); if (result != CS_OK) { fprintf (stderr, "Could not reopen corosync logging files. Error %s\n", cs_strerror(result)); rc = (int)result; } (void)corosync_cfg_finalize (handle); return (rc); } static void shutdown_do(void) { cs_error_t result; corosync_cfg_handle_t handle; corosync_cfg_callbacks_t callbacks; callbacks.corosync_cfg_shutdown_callback = NULL; result = corosync_cfg_initialize (&handle, &callbacks); if (result != CS_OK) { fprintf (stderr, "Could not initialize corosync configuration API error %d\n", result); exit (EXIT_FAILURE); } printf ("Shutting down corosync\n"); cs_repeat(result, 30, corosync_cfg_try_shutdown (handle, COROSYNC_CFG_SHUTDOWN_FLAG_REQUEST)); if (result != CS_OK) { fprintf (stderr, "Could not shutdown (error = %d)\n", result); } (void)corosync_cfg_finalize (handle); } static int showaddrs_do(unsigned int nodeid) { cs_error_t result; corosync_cfg_handle_t handle; int numaddrs; int i; int rc = EXIT_SUCCESS; corosync_cfg_node_address_t addrs[INTERFACE_MAX]; result = corosync_cfg_initialize (&handle, NULL); if (result != CS_OK) { fprintf (stderr, "Could not initialize corosync configuration API error %d\n", result); exit (EXIT_FAILURE); } if (corosync_cfg_get_node_addrs(handle, nodeid, INTERFACE_MAX, &numaddrs, addrs) == CS_OK) { for (i=0; i<numaddrs; i++) { char buf[INET6_ADDRSTRLEN]; struct sockaddr_storage *ss = (struct sockaddr_storage *)addrs[i].address; struct sockaddr_in *sin = (struct sockaddr_in *)addrs[i].address; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addrs[i].address; void *saddr; if (!ss->ss_family) { continue; } if (ss->ss_family == AF_INET6) { saddr = &sin6->sin6_addr; } else { saddr = &sin->sin_addr; } inet_ntop(ss->ss_family, saddr, buf, sizeof(buf)); if (i != 0) { printf(" "); } printf("%s", buf); } printf("\n"); } else { fprintf (stderr, "Could not get node address for nodeid %d\n", nodeid); rc = EXIT_FAILURE; } (void)corosync_cfg_finalize (handle); return rc; } static void killnode_do(unsigned int nodeid) { cs_error_t result; corosync_cfg_handle_t handle; printf ("Killing node " CS_PRI_NODE_ID "\n", nodeid); result = corosync_cfg_initialize (&handle, NULL); if (result != CS_OK) { fprintf (stderr, "Could not initialize corosync configuration API error %d\n", result); exit (EXIT_FAILURE); } result = corosync_cfg_kill_node (handle, nodeid, "Killed by corosync-cfgtool"); if (result != CS_OK) { fprintf (stderr, "Could not kill node (error = %s)\n", cs_strerror(result)); exit(EXIT_FAILURE); } (void)corosync_cfg_finalize (handle); } static void usage_do (void) { printf ("corosync-cfgtool [[-i <interface ip>] [-b] -s] [-R] [-L] [-k nodeid] [-a nodeid] [-h] [-H]\n\n"); printf ("A tool for displaying and configuring active parameters within corosync.\n"); printf ("options:\n"); printf ("\t-i\tFinds only information about the specified interface IP address or link id when used with -s..\n"); printf ("\t-s\tDisplays the status of the current links on this node.\n"); printf ("\t-n\tDisplays the status of the connected nodes and their links.\n"); printf ("\t-b\tDisplays the brief status of the current links on this node when used with -s.\n"); printf ("\t-R\tTell all instances of corosync in this cluster to reload corosync.conf.\n"); printf ("\t-L\tTell corosync to reopen all logging files.\n"); printf ("\t-k\tKill a node identified by node id.\n"); printf ("\t-a\tDisplay the IP address(es) of a node\n"); printf ("\t-h\tPrint basic usage.\n"); printf ("\t-H\tShutdown corosync cleanly on this node.\n"); } int main (int argc, char *argv[]) { const char *options = "i:snbrRLk:a:hH"; int opt; unsigned int nodeid = 0; char interface_name[128] = ""; int rc = EXIT_SUCCESS; enum user_action action = ACTION_NOOP; int brief = 0; long long int l; while ( (opt = getopt(argc, argv, options)) != -1 ) { switch (opt) { case 'i': strncpy(interface_name, optarg, sizeof(interface_name)); interface_name[sizeof(interface_name) - 1] = '\0'; break; case 's': action = ACTION_LINKSTATUS_GET; break; case 'n': action = ACTION_NODESTATUS_GET; break; case 'b': brief = 1; break; case 'R': action = ACTION_RELOAD_CONFIG; break; case 'L': action = ACTION_REOPEN_LOG_FILES; break; case 'k': if (util_strtonum(optarg, 1, UINT_MAX, &l) == -1) { fprintf(stderr, "The nodeid was not valid, try a positive number\n"); exit(EXIT_FAILURE); } nodeid = l; action = ACTION_KILL_NODE; break; case 'H': action = ACTION_SHUTDOW; break; case 'a': if (util_strtonum(optarg, 1, UINT_MAX, &l) == -1) { fprintf(stderr, "The nodeid was not valid, try a positive number\n"); exit(EXIT_FAILURE); } nodeid = l; action = ACTION_SHOWADDR; break; case '?': return (EXIT_FAILURE); break; case 'h': default: break; } } switch(action) { case ACTION_LINKSTATUS_GET: rc = nodestatusget_do(action, brief); break; case ACTION_NODESTATUS_GET: rc = nodestatusget_do(action, brief); break; case ACTION_RELOAD_CONFIG: rc = reload_config_do(); break; case ACTION_REOPEN_LOG_FILES: rc = reopen_log_files_do(); break; case ACTION_KILL_NODE: killnode_do(nodeid); break; case ACTION_SHUTDOW: shutdown_do(); break; case ACTION_SHOWADDR: rc = showaddrs_do(nodeid); break; case ACTION_NOOP: default: usage_do(); break; } return (rc); }