diff --git a/exec/cfg.c b/exec/cfg.c index 1c4e0d57..ee6bbd41 100644 --- a/exec/cfg.c +++ b/exec/cfg.c @@ -1,1473 +1,1474 @@ /* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 }; /* in milliseconds */ #define DEFAULT_SHUTDOWN_TIMEOUT 5000 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_trackstart ( void *conn, const void *msg); static void message_handler_req_lib_cfg_trackstop ( 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 }, { /* 10 */ .lib_handler_fn = message_handler_req_lib_cfg_trackstart, .flow_control = CS_LIB_FLOW_CONTROL_REQUIRED }, { /* 11 */ .lib_handler_fn = message_handler_req_lib_cfg_trackstop, .flow_control = CS_LIB_FLOW_CONTROL_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.allow_knet_handle_fallback"); + delete_and_notify_if_changed(temp_map, "system.allow_knet_asymmetric_routes"); 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); // Clear this out in case it all goes well icmap_delete("config.reload_error_message"); 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); 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.interfaces); 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_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; 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 */ 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); } 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; } ipc_response_send: api->ipc_response_send ( conn, res_lib_cfg_nodestatusget_ptr, res_lib_cfg_nodestatusget_size); LEAVE(); } static void message_handler_req_lib_cfg_trackstart ( void *conn, const void *msg) { struct cfg_info *ci = (struct cfg_info *)api->ipc_private_data_get (conn); struct res_lib_cfg_trackstart res_lib_cfg_trackstart; ENTER(); /* * We only do shutdown tracking at the moment */ if (qb_list_empty(&ci->list)) { qb_list_add(&ci->list, &trackers_list); ci->tracker_conn = conn; if (shutdown_con) { /* * Shutdown already in progress, ask the newcomer's opinion */ ci->shutdown_reply = SHUTDOWN_REPLY_UNKNOWN; shutdown_expected++; send_test_shutdown(conn, NULL, CS_OK); } } res_lib_cfg_trackstart.header.size = sizeof(struct res_lib_cfg_trackstart); res_lib_cfg_trackstart.header.id = MESSAGE_RES_CFG_STATETRACKSTART; res_lib_cfg_trackstart.header.error = CS_OK; api->ipc_response_send(conn, &res_lib_cfg_trackstart, sizeof(res_lib_cfg_trackstart)); LEAVE(); } static void message_handler_req_lib_cfg_trackstop ( void *conn, const void *msg) { struct cfg_info *ci = (struct cfg_info *)api->ipc_private_data_get (conn); struct res_lib_cfg_trackstop res_lib_cfg_trackstop; ENTER(); remove_ci_from_shutdown(ci); res_lib_cfg_trackstop.header.size = sizeof(struct res_lib_cfg_trackstop); res_lib_cfg_trackstop.header.id = MESSAGE_RES_CFG_STATETRACKSTOP; res_lib_cfg_trackstop.header.error = CS_OK; api->ipc_response_send(conn, &res_lib_cfg_trackstop, sizeof(res_lib_cfg_trackstop)); 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*QB_TIME_NS_IN_MSEC, 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/exec/coroparse.c b/exec/coroparse.c index b017aca9..7eec2bc5 100644 --- a/exec/coroparse.c +++ b/exec/coroparse.c @@ -1,1703 +1,1711 @@ /* * Copyright (c) 2006-2022 Red Hat, Inc. * * All rights reserved. * * Author: Patrick Caulfield (pcaulfie@redhat.com) * Jan Friesse (jfriesse@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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LOGSYS_UTILS_ONLY 1 #include #include #include "main.h" #include "util.h" enum parser_cb_type { PARSER_CB_START, PARSER_CB_END, PARSER_CB_SECTION_START, PARSER_CB_SECTION_END, PARSER_CB_ITEM, }; enum main_cp_cb_data_state { MAIN_CP_CB_DATA_STATE_NORMAL, MAIN_CP_CB_DATA_STATE_TOTEM, MAIN_CP_CB_DATA_STATE_INTERFACE, MAIN_CP_CB_DATA_STATE_LOGGER_SUBSYS, MAIN_CP_CB_DATA_STATE_UIDGID, MAIN_CP_CB_DATA_STATE_LOGGING_DAEMON, MAIN_CP_CB_DATA_STATE_MEMBER, MAIN_CP_CB_DATA_STATE_QUORUM, MAIN_CP_CB_DATA_STATE_QDEVICE, MAIN_CP_CB_DATA_STATE_NODELIST, MAIN_CP_CB_DATA_STATE_NODELIST_NODE, MAIN_CP_CB_DATA_STATE_PLOAD, MAIN_CP_CB_DATA_STATE_SYSTEM, MAIN_CP_CB_DATA_STATE_RESOURCES, MAIN_CP_CB_DATA_STATE_RESOURCES_SYSTEM, MAIN_CP_CB_DATA_STATE_RESOURCES_PROCESS, MAIN_CP_CB_DATA_STATE_RESOURCES_SYSTEM_MEMUSED, MAIN_CP_CB_DATA_STATE_RESOURCES_PROCESS_MEMUSED }; typedef int (*parser_cb_f)(const char *path, char *key, char *value, enum main_cp_cb_data_state *state, enum parser_cb_type type, const char **error_string, icmap_map_t config_map, void *user_data); struct key_value_list_item { char *key; char *value; struct qb_list_head list; }; struct main_cp_cb_data { int linknumber; char *bindnetaddr; char *mcastaddr; char *broadcast; int mcastport; int ttl; int knet_link_priority; int knet_ping_interval; int knet_ping_timeout; int knet_ping_precision; int knet_pong_count; int knet_pmtud_interval; unsigned int knet_mtu; char *knet_transport; struct qb_list_head logger_subsys_items_head; char *subsys; char *logging_daemon_name; struct qb_list_head member_items_head; int node_number; }; static int read_config_file_into_icmap( const char **error_string, icmap_map_t config_map); static char error_string_response[512]; static int uid_determine (const char *req_user) { int pw_uid = 0; struct passwd passwd; struct passwd* pwdptr = &passwd; struct passwd* temp_pwd_pt; char *pwdbuffer; int pwdlinelen, rc; long int id; char *ep; id = strtol(req_user, &ep, 10); if (*req_user != '\0' && *ep == '\0' && id >= 0 && id <= UINT_MAX) { return (id); } pwdlinelen = sysconf (_SC_GETPW_R_SIZE_MAX); if (pwdlinelen == -1) { pwdlinelen = 256; } pwdbuffer = malloc (pwdlinelen); while ((rc = getpwnam_r (req_user, pwdptr, pwdbuffer, pwdlinelen, &temp_pwd_pt)) == ERANGE) { char *n; pwdlinelen *= 2; if (pwdlinelen <= 32678) { n = realloc (pwdbuffer, pwdlinelen); if (n != NULL) { pwdbuffer = n; continue; } } } if (rc != 0) { free (pwdbuffer); sprintf (error_string_response, "getpwnam_r(): %s", strerror(rc)); return (-1); } if (temp_pwd_pt == NULL) { free (pwdbuffer); sprintf (error_string_response, "The '%s' user is not found in /etc/passwd, please read the documentation.", req_user); return (-1); } pw_uid = passwd.pw_uid; free (pwdbuffer); return pw_uid; } static int gid_determine (const char *req_group) { int corosync_gid = 0; struct group group; struct group * grpptr = &group; struct group * temp_grp_pt; char *grpbuffer; int grplinelen, rc; long int id; char *ep; id = strtol(req_group, &ep, 10); if (*req_group != '\0' && *ep == '\0' && id >= 0 && id <= UINT_MAX) { return (id); } grplinelen = sysconf (_SC_GETGR_R_SIZE_MAX); if (grplinelen == -1) { grplinelen = 256; } grpbuffer = malloc (grplinelen); while ((rc = getgrnam_r (req_group, grpptr, grpbuffer, grplinelen, &temp_grp_pt)) == ERANGE) { char *n; grplinelen *= 2; if (grplinelen <= 32678) { n = realloc (grpbuffer, grplinelen); if (n != NULL) { grpbuffer = n; continue; } } } if (rc != 0) { free (grpbuffer); sprintf (error_string_response, "getgrnam_r(): %s", strerror(rc)); return (-1); } if (temp_grp_pt == NULL) { free (grpbuffer); sprintf (error_string_response, "The '%s' group is not found in /etc/group, please read the documentation.", req_group); return (-1); } corosync_gid = group.gr_gid; free (grpbuffer); return corosync_gid; } static char *strchr_rs (const char *haystack, int byte) { const char *end_address = strchr (haystack, byte); if (end_address) { end_address += 1; /* skip past { or = */ while (*end_address == ' ' || *end_address == '\t' || (unsigned char)*end_address == 0xA0) end_address++; } return ((char *) end_address); } int coroparse_configparse (icmap_map_t config_map, const char **error_string) { if (read_config_file_into_icmap(error_string, config_map)) { return -1; } return 0; } static char *remove_whitespace(char *string, int remove_colon_and_brace) { char *start; char *end; start = string; while (*start == ' ' || *start == '\t' || (unsigned char)*start == 0xA0) start++; end = start+(strlen(start))-1; while ((*end == ' ' || *end == '\t' || (unsigned char)*end == 0xA0 || (remove_colon_and_brace && (*end == ':' || *end == '{'))) && end > start) end--; if (*end != '\0') *(end + 1) = '\0'; return start; } static int parse_section(FILE *fp, const char *fname, int *line_no, char *path, const char **error_string, int depth, enum main_cp_cb_data_state state, parser_cb_f parser_cb, icmap_map_t config_map, void *user_data) { char line[512]; int i; char *loc; int ignore_line; char new_keyname[ICMAP_KEYNAME_MAXLEN]; static char formated_err[384]; const char *tmp_error_string; if (strcmp(path, "") == 0) { parser_cb("", NULL, NULL, &state, PARSER_CB_START, error_string, config_map, user_data); } tmp_error_string = NULL; while (fgets (line, sizeof (line), fp)) { (*line_no)++; if (strlen(line) > 0) { /* * Check if complete line was read. Use feof to handle files * without ending \n at the end of the file */ if ((line[strlen(line) - 1] != '\n') && !feof(fp)) { tmp_error_string = "Line too long"; goto parse_error; } if (line[strlen(line) - 1] == '\n') line[strlen(line) - 1] = '\0'; if (strlen (line) > 0 && line[strlen(line) - 1] == '\r') line[strlen(line) - 1] = '\0'; } /* * Clear out white space and tabs */ for (i = strlen (line) - 1; i > -1; i--) { if (line[i] == '\t' || line[i] == ' ' || (unsigned char)line[i] == 0xA0) { line[i] = '\0'; } else { break; } } ignore_line = 1; for (i = 0; i < strlen (line); i++) { if (line[i] != '\t' && line[i] != ' ' && (unsigned char)line[i] != 0xA0) { if (line[i] != '#') ignore_line = 0; break; } } /* * Clear out comments and empty lines */ if (ignore_line) { continue; } /* New section ? */ if ((loc = strchr_rs (line, '{'))) { char *section; char *after_section; enum main_cp_cb_data_state newstate; *(loc-1) = '\0'; section = remove_whitespace(line, 1); after_section = remove_whitespace(loc, 0); if (strcmp(section, "") == 0) { tmp_error_string = "Missing section name before opening bracket '{'"; goto parse_error; } if (strcmp(after_section, "") != 0) { tmp_error_string = "Extra characters after opening bracket '{'"; goto parse_error; } if (strlen(path) + strlen(section) + 1 >= ICMAP_KEYNAME_MAXLEN) { tmp_error_string = "Start of section makes total cmap path too long"; goto parse_error; } strcpy(new_keyname, path); if (strcmp(path, "") != 0) { strcat(new_keyname, "."); } strcat(new_keyname, section); /* Only use the new state for items further down the stack */ newstate = state; if (!parser_cb(new_keyname, NULL, NULL, &newstate, PARSER_CB_SECTION_START, &tmp_error_string, config_map, user_data)) { goto parse_error; } if (parse_section(fp, fname, line_no, new_keyname, error_string, depth + 1, newstate, parser_cb, config_map, user_data)) return -1; continue ; } /* New key/value */ if ((loc = strchr_rs (line, ':'))) { char *key; char *value; *(loc-1) = '\0'; key = remove_whitespace(line, 1); value = remove_whitespace(loc, 0); if (strlen(path) + strlen(key) + 1 >= ICMAP_KEYNAME_MAXLEN) { tmp_error_string = "New key makes total cmap path too long"; goto parse_error; } strcpy(new_keyname, path); if (strcmp(path, "") != 0) { strcat(new_keyname, "."); } strcat(new_keyname, key); if (!parser_cb(new_keyname, key, value, &state, PARSER_CB_ITEM, &tmp_error_string, config_map, user_data)) { goto parse_error; } continue ; } if (strchr_rs (line, '}')) { char *trimmed_line; trimmed_line = remove_whitespace(line, 0); if (strcmp(trimmed_line, "}") != 0) { tmp_error_string = "Extra characters before or after closing bracket '}'"; goto parse_error; } if (depth == 0) { tmp_error_string = "Unexpected closing brace"; goto parse_error; } if (!parser_cb(path, NULL, NULL, &state, PARSER_CB_SECTION_END, &tmp_error_string, config_map, user_data)) { goto parse_error; } return 0; } /* * Line is not opening section, ending section or value -> error */ tmp_error_string = "Line is not opening or closing section or key value"; goto parse_error; } if (strcmp(path, "") != 0) { tmp_error_string = "Missing closing brace"; goto parse_error; } if (strcmp(path, "") == 0) { parser_cb("", NULL, NULL, &state, PARSER_CB_END, error_string, config_map, user_data); } return 0; parse_error: if (snprintf(formated_err, sizeof(formated_err), "parser error: %s:%u: %s", fname, *line_no, tmp_error_string) >= sizeof(formated_err)) { *error_string = "Can't format parser error message"; } else { *error_string = formated_err; } return -1; } static int safe_atoq_range(icmap_value_types_t value_type, long long int *min_val, long long int *max_val) { switch (value_type) { case ICMAP_VALUETYPE_INT8: *min_val = INT8_MIN; *max_val = INT8_MAX; break; case ICMAP_VALUETYPE_UINT8: *min_val = 0; *max_val = UINT8_MAX; break; case ICMAP_VALUETYPE_INT16: *min_val = INT16_MIN; *max_val = INT16_MAX; break; case ICMAP_VALUETYPE_UINT16: *min_val = 0; *max_val = UINT16_MAX; break; case ICMAP_VALUETYPE_INT32: *min_val = INT32_MIN; *max_val = INT32_MAX; break; case ICMAP_VALUETYPE_UINT32: *min_val = 0; *max_val = UINT32_MAX; break; default: return (-1); } return (0); } /* * Convert string str to long long int res. Type of result is target_type and currently only * ICMAP_VALUETYPE_[U]INT[8|16|32] is supported. * Return 0 on success, -1 on failure. */ static int safe_atoq(const char *str, long long int *res, icmap_value_types_t target_type) { long long int val; long long int min_val, max_val; char *endptr; errno = 0; val = strtoll(str, &endptr, 10); if (errno == ERANGE) { return (-1); } if (endptr == str) { return (-1); } if (*endptr != '\0') { return (-1); } if (safe_atoq_range(target_type, &min_val, &max_val) != 0) { return (-1); } if (val < min_val || val > max_val) { return (-1); } *res = val; return (0); } static int str_to_ull(const char *str, unsigned long long int *res) { unsigned long long int val; char *endptr; errno = 0; val = strtoull(str, &endptr, 10); if (errno == ERANGE) { return (-1); } if (endptr == str) { return (-1); } if (*endptr != '\0') { return (-1); } *res = val; return (0); } static int handle_crypto_model(const char *val, const char **error_string) { if (util_is_valid_knet_crypto_model(val, NULL, 0, "Invalid crypto model. Should be ", error_string) == 1) { return (0); } else { return (-1); } } static int handle_compress_model(const char *val, const char **error_string) { if (util_is_valid_knet_compress_model(val, NULL, 0, "Invalid compression model. Should be ", error_string) == 1) { return (0); } else { return (-1); } } static int main_config_parser_cb(const char *path, char *key, char *value, enum main_cp_cb_data_state *state, enum parser_cb_type type, const char **error_string, icmap_map_t config_map, void *user_data) { int ii; long long int val; long long int min_val, max_val; icmap_value_types_t val_type = ICMAP_VALUETYPE_BINARY; unsigned long long int ull; int add_as_string; char key_name[ICMAP_KEYNAME_MAXLEN + 1]; static char formated_err[256]; struct main_cp_cb_data *data = (struct main_cp_cb_data *)user_data; struct key_value_list_item *kv_item; struct qb_list_head *iter, *tmp_iter; int uid, gid; cs_error_t cs_err; cs_err = CS_OK; /* * Formally this check is not needed because length is checked by parse_section */ if (strlen(path) >= sizeof(key_name)) { if (snprintf(formated_err, sizeof(formated_err), "Can't store path \"%s\" into key_name", path) >= sizeof(formated_err)) { *error_string = "Can't format path into key_name error message"; } else { *error_string = formated_err; } return (0); } /* * Key_name is used in atoi_error/icmap_set_error, but many of icmap_set* * are using path, so initialize key_name to valid value */ strncpy(key_name, path, sizeof(key_name) - 1); switch (type) { case PARSER_CB_START: memset(data, 0, sizeof(struct main_cp_cb_data)); *state = MAIN_CP_CB_DATA_STATE_NORMAL; break; case PARSER_CB_END: break; case PARSER_CB_ITEM: add_as_string = 1; switch (*state) { case MAIN_CP_CB_DATA_STATE_NORMAL: break; case MAIN_CP_CB_DATA_STATE_PLOAD: if ((strcmp(path, "pload.count") == 0) || (strcmp(path, "pload.size") == 0)) { val_type = ICMAP_VALUETYPE_UINT32; if (safe_atoq(value, &val, val_type) != 0) { goto atoi_error; } if ((cs_err = icmap_set_uint32_r(config_map, path, val)) != CS_OK) { goto icmap_set_error; } add_as_string = 0; } break; case MAIN_CP_CB_DATA_STATE_QUORUM: if ((strcmp(path, "quorum.expected_votes") == 0) || (strcmp(path, "quorum.votes") == 0) || (strcmp(path, "quorum.last_man_standing_window") == 0) || (strcmp(path, "quorum.leaving_timeout") == 0)) { val_type = ICMAP_VALUETYPE_UINT32; if (safe_atoq(value, &val, val_type) != 0) { goto atoi_error; } if ((cs_err = icmap_set_uint32_r(config_map, path, val)) != CS_OK) { goto icmap_set_error; } add_as_string = 0; } if ((strcmp(path, "quorum.two_node") == 0) || (strcmp(path, "quorum.expected_votes_tracking") == 0) || (strcmp(path, "quorum.allow_downscale") == 0) || (strcmp(path, "quorum.wait_for_all") == 0) || (strcmp(path, "quorum.auto_tie_breaker") == 0) || (strcmp(path, "quorum.last_man_standing") == 0)) { val_type = ICMAP_VALUETYPE_UINT8; if (safe_atoq(value, &val, val_type) != 0) { goto atoi_error; } if ((cs_err = icmap_set_uint8_r(config_map, path, val)) != CS_OK) { goto icmap_set_error; } add_as_string = 0; } break; case MAIN_CP_CB_DATA_STATE_QDEVICE: if ((strcmp(path, "quorum.device.timeout") == 0) || (strcmp(path, "quorum.device.sync_timeout") == 0) || (strcmp(path, "quorum.device.votes") == 0)) { val_type = ICMAP_VALUETYPE_UINT32; if (safe_atoq(value, &val, val_type) != 0) { goto atoi_error; } if ((cs_err = icmap_set_uint32_r(config_map, path, val)) != CS_OK) { goto icmap_set_error; } add_as_string = 0; } if ((strcmp(path, "quorum.device.master_wins") == 0)) { val_type = ICMAP_VALUETYPE_UINT8; if (safe_atoq(value, &val, val_type) != 0) { goto atoi_error; } if ((cs_err = icmap_set_uint8_r(config_map, path, val)) != CS_OK) { goto icmap_set_error; } add_as_string = 0; } break; case MAIN_CP_CB_DATA_STATE_TOTEM: if ((strcmp(path, "totem.version") == 0) || (strcmp(path, "totem.nodeid") == 0) || (strcmp(path, "totem.threads") == 0) || (strcmp(path, "totem.token") == 0) || (strcmp(path, "totem.token_coefficient") == 0) || (strcmp(path, "totem.token_retransmit") == 0) || (strcmp(path, "totem.token_warning") == 0) || (strcmp(path, "totem.hold") == 0) || (strcmp(path, "totem.token_retransmits_before_loss_const") == 0) || (strcmp(path, "totem.join") == 0) || (strcmp(path, "totem.send_join") == 0) || (strcmp(path, "totem.consensus") == 0) || (strcmp(path, "totem.merge") == 0) || (strcmp(path, "totem.downcheck") == 0) || (strcmp(path, "totem.fail_recv_const") == 0) || (strcmp(path, "totem.seqno_unchanged_const") == 0) || (strcmp(path, "totem.rrp_token_expired_timeout") == 0) || (strcmp(path, "totem.rrp_problem_count_timeout") == 0) || (strcmp(path, "totem.rrp_problem_count_threshold") == 0) || (strcmp(path, "totem.rrp_problem_count_mcast_threshold") == 0) || (strcmp(path, "totem.rrp_autorecovery_check_timeout") == 0) || (strcmp(path, "totem.heartbeat_failures_allowed") == 0) || (strcmp(path, "totem.max_network_delay") == 0) || (strcmp(path, "totem.window_size") == 0) || (strcmp(path, "totem.max_messages") == 0) || (strcmp(path, "totem.miss_count_const") == 0) || (strcmp(path, "totem.knet_pmtud_interval") == 0) || (strcmp(path, "totem.knet_mtu") == 0) || (strcmp(path, "totem.knet_compression_threshold") == 0) || (strcmp(path, "totem.netmtu") == 0)) { val_type = ICMAP_VALUETYPE_UINT32; if (safe_atoq(value, &val, val_type) != 0) { goto atoi_error; } if ((cs_err = icmap_set_uint32_r(config_map,path, val)) != CS_OK) { goto icmap_set_error; } add_as_string = 0; } if (strcmp(path, "totem.knet_compression_level") == 0) { val_type = ICMAP_VALUETYPE_INT32; if (safe_atoq(value, &val, val_type) != 0) { goto atoi_error; } if ((cs_err = icmap_set_int32_r(config_map, path, val)) != CS_OK) { goto icmap_set_error; } add_as_string = 0; } if (strcmp(path, "totem.config_version") == 0) { if (str_to_ull(value, &ull) != 0) { goto atoi_error; } if ((cs_err = icmap_set_uint64_r(config_map, path, ull)) != CS_OK) { goto icmap_set_error; } add_as_string = 0; } if (strcmp(path, "totem.ip_version") == 0) { if ((strcmp(value, "ipv4") != 0) && (strcmp(value, "ipv6") != 0) && (strcmp(value, "ipv6-4") != 0) && (strcmp(value, "ipv4-6") != 0)) { *error_string = "Invalid ip_version type"; return (0); } } if (strcmp(path, "totem.crypto_model") == 0) { if (handle_crypto_model(value, error_string) != 0) { return (0); } } if (strcmp(path, "totem.crypto_cipher") == 0) { if ((strcmp(value, "none") != 0) && (strcmp(value, "aes256") != 0) && (strcmp(value, "aes192") != 0) && (strcmp(value, "aes128") != 0)) { *error_string = "Invalid cipher type. " "Should be none, aes256, aes192 or aes128"; return (0); } } if (strcmp(path, "totem.crypto_hash") == 0) { if ((strcmp(value, "none") != 0) && (strcmp(value, "md5") != 0) && (strcmp(value, "sha1") != 0) && (strcmp(value, "sha256") != 0) && (strcmp(value, "sha384") != 0) && (strcmp(value, "sha512") != 0)) { *error_string = "Invalid hash type. " "Should be none, md5, sha1, sha256, sha384 or sha512"; return (0); } } if (strcmp(path, "totem.knet_compression_model") == 0) { if (handle_compress_model(value, error_string) != 0) { return (0); } } break; case MAIN_CP_CB_DATA_STATE_SYSTEM: if (strcmp(path, "system.qb_ipc_type") == 0) { if ((strcmp(value, "native") != 0) && (strcmp(value, "shm") != 0) && (strcmp(value, "socket") != 0)) { *error_string = "Invalid system.qb_ipc_type"; return (0); } } if (strcmp(path, "system.sched_rr") == 0) { if ((strcmp(value, "yes") != 0) && (strcmp(value, "no") != 0)) { *error_string = "Invalid system.sched_rr value"; return (0); } } if (strcmp(path, "system.move_to_root_cgroup") == 0) { if ((strcmp(value, "yes") != 0) && (strcmp(value, "no") != 0) && (strcmp(value, "auto") != 0)) { *error_string = "Invalid system.move_to_root_cgroup"; return (0); } } if (strcmp(path, "system.allow_knet_handle_fallback") == 0) { if ((strcmp(value, "yes") != 0) && (strcmp(value, "no") != 0)) { *error_string = "Invalid system.allow_knet_handle_fallback"; return (0); } } + if (strcmp(path, "system.allow_knet_asymmetric_routes") == 0) { + if ((strcmp(value, "yes") != 0) && + (strcmp(value, "no") != 0)) { + *error_string = "Invalid system.allow_knet_asymmetric_routes"; + + return (0); + } + } break; case MAIN_CP_CB_DATA_STATE_INTERFACE: if (strcmp(path, "totem.interface.linknumber") == 0) { val_type = ICMAP_VALUETYPE_UINT8; if (safe_atoq(value, &val, val_type) != 0) { goto atoi_error; } data->linknumber = val; add_as_string = 0; } if (strcmp(path, "totem.interface.bindnetaddr") == 0) { data->bindnetaddr = strdup(value); add_as_string = 0; } if (strcmp(path, "totem.interface.mcastaddr") == 0) { data->mcastaddr = strdup(value); add_as_string = 0; } if (strcmp(path, "totem.interface.broadcast") == 0) { data->broadcast = strdup(value); add_as_string = 0; } if (strcmp(path, "totem.interface.mcastport") == 0) { val_type = ICMAP_VALUETYPE_UINT16; if (safe_atoq(value, &val, val_type) != 0) { goto atoi_error; } data->mcastport = val; add_as_string = 0; } if (strcmp(path, "totem.interface.ttl") == 0) { val_type = ICMAP_VALUETYPE_UINT8; if (safe_atoq(value, &val, val_type) != 0) { goto atoi_error; } data->ttl = val; add_as_string = 0; } if (strcmp(path, "totem.interface.knet_link_priority") == 0) { val_type = ICMAP_VALUETYPE_UINT8; if (safe_atoq(value, &val, val_type) != 0) { goto atoi_error; } data->knet_link_priority = val; add_as_string = 0; } if (strcmp(path, "totem.interface.knet_ping_interval") == 0) { val_type = ICMAP_VALUETYPE_UINT32; if (safe_atoq(value, &val, val_type) != 0) { goto atoi_error; } data->knet_ping_interval = val; add_as_string = 0; } if (strcmp(path, "totem.interface.knet_ping_timeout") == 0) { val_type = ICMAP_VALUETYPE_UINT32; if (safe_atoq(value, &val, val_type) != 0) { goto atoi_error; } data->knet_ping_timeout = val; add_as_string = 0; } if (strcmp(path, "totem.interface.knet_ping_precision") == 0) { val_type = ICMAP_VALUETYPE_UINT32; if (safe_atoq(value, &val, val_type) != 0) { goto atoi_error; } data->knet_ping_precision = val; add_as_string = 0; } if (strcmp(path, "totem.interface.knet_pong_count") == 0) { val_type = ICMAP_VALUETYPE_UINT32; if (safe_atoq(value, &val, val_type) != 0) { goto atoi_error; } data->knet_pong_count = val; add_as_string = 0; } if (strcmp(path, "totem.interface.knet_transport") == 0) { val_type = ICMAP_VALUETYPE_STRING; data->knet_transport = strdup(value); add_as_string = 0; } break; case MAIN_CP_CB_DATA_STATE_LOGGER_SUBSYS: if (strcmp(key, "subsys") == 0) { data->subsys = strdup(value); if (data->subsys == NULL) { *error_string = "Can't alloc memory"; return (0); } } else { kv_item = malloc(sizeof(*kv_item)); if (kv_item == NULL) { *error_string = "Can't alloc memory"; return (0); } memset(kv_item, 0, sizeof(*kv_item)); kv_item->key = strdup(key); kv_item->value = strdup(value); if (kv_item->key == NULL || kv_item->value == NULL) { free(kv_item->key); free(kv_item->value); free(kv_item); *error_string = "Can't alloc memory"; return (0); } qb_list_init(&kv_item->list); qb_list_add(&kv_item->list, &data->logger_subsys_items_head); } add_as_string = 0; break; case MAIN_CP_CB_DATA_STATE_LOGGING_DAEMON: if (strcmp(key, "subsys") == 0) { data->subsys = strdup(value); if (data->subsys == NULL) { *error_string = "Can't alloc memory"; return (0); } } else if (strcmp(key, "name") == 0) { data->logging_daemon_name = strdup(value); if (data->logging_daemon_name == NULL) { *error_string = "Can't alloc memory"; return (0); } } else { kv_item = malloc(sizeof(*kv_item)); if (kv_item == NULL) { *error_string = "Can't alloc memory"; return (0); } memset(kv_item, 0, sizeof(*kv_item)); kv_item->key = strdup(key); kv_item->value = strdup(value); if (kv_item->key == NULL || kv_item->value == NULL) { free(kv_item->key); free(kv_item->value); free(kv_item); *error_string = "Can't alloc memory"; return (0); } qb_list_init(&kv_item->list); qb_list_add(&kv_item->list, &data->logger_subsys_items_head); } add_as_string = 0; break; case MAIN_CP_CB_DATA_STATE_UIDGID: if (strcmp(key, "uid") == 0) { uid = uid_determine(value); if (uid == -1) { *error_string = error_string_response; return (0); } snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "uidgid.config.uid.%u", uid); if ((cs_err = icmap_set_uint8_r(config_map, key_name, 1)) != CS_OK) { goto icmap_set_error; } add_as_string = 0; } else if (strcmp(key, "gid") == 0) { gid = gid_determine(value); if (gid == -1) { *error_string = error_string_response; return (0); } snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "uidgid.config.gid.%u", gid); if ((cs_err = icmap_set_uint8_r(config_map, key_name, 1)) != CS_OK) { goto icmap_set_error; } add_as_string = 0; } else { *error_string = "uidgid: Only uid and gid are allowed items"; return (0); } break; case MAIN_CP_CB_DATA_STATE_MEMBER: if (strcmp(key, "memberaddr") != 0) { *error_string = "Only memberaddr is allowed in member section"; return (0); } kv_item = malloc(sizeof(*kv_item)); if (kv_item == NULL) { *error_string = "Can't alloc memory"; return (0); } memset(kv_item, 0, sizeof(*kv_item)); kv_item->key = strdup(key); kv_item->value = strdup(value); if (kv_item->key == NULL || kv_item->value == NULL) { free(kv_item->key); free(kv_item->value); free(kv_item); *error_string = "Can't alloc memory"; return (0); } qb_list_init(&kv_item->list); qb_list_add(&kv_item->list, &data->member_items_head); add_as_string = 0; break; case MAIN_CP_CB_DATA_STATE_NODELIST: break; case MAIN_CP_CB_DATA_STATE_NODELIST_NODE: snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.%s", data->node_number, key); if ((strcmp(key, "nodeid") == 0) || (strcmp(key, "quorum_votes") == 0)) { val_type = ICMAP_VALUETYPE_UINT32; if (safe_atoq(value, &val, val_type) != 0) { goto atoi_error; } if ((cs_err = icmap_set_uint32_r(config_map, key_name, val)) != CS_OK) { goto icmap_set_error; } add_as_string = 0; } if (add_as_string) { if ((cs_err = icmap_set_string_r(config_map, key_name, value)) != CS_OK) { goto icmap_set_error; }; add_as_string = 0; } break; case MAIN_CP_CB_DATA_STATE_RESOURCES: if (strcmp(key, "watchdog_timeout") == 0) { val_type = ICMAP_VALUETYPE_UINT32; if (safe_atoq(value, &val, val_type) != 0) { goto atoi_error; } if ((cs_err = icmap_set_uint32_r(config_map,path, val)) != CS_OK) { goto icmap_set_error; } add_as_string = 0; } break; case MAIN_CP_CB_DATA_STATE_RESOURCES_SYSTEM: case MAIN_CP_CB_DATA_STATE_RESOURCES_SYSTEM_MEMUSED: if (strcmp(key, "poll_period") == 0) { if (str_to_ull(value, &ull) != 0) { goto atoi_error; } if ((cs_err = icmap_set_uint64_r(config_map,path, ull)) != CS_OK) { goto icmap_set_error; } add_as_string = 0; } break; case MAIN_CP_CB_DATA_STATE_RESOURCES_PROCESS: case MAIN_CP_CB_DATA_STATE_RESOURCES_PROCESS_MEMUSED: if (strcmp(key, "poll_period") == 0) { if (str_to_ull(value, &ull) != 0) { goto atoi_error; } if ((cs_err = icmap_set_uint64_r(config_map,path, ull)) != CS_OK) { goto icmap_set_error; } add_as_string = 0; } break; } if (add_as_string) { if ((cs_err = icmap_set_string_r(config_map, path, value)) != CS_OK) { goto icmap_set_error; } } break; case PARSER_CB_SECTION_START: if (strcmp(path, "totem.interface") == 0) { *state = MAIN_CP_CB_DATA_STATE_INTERFACE; data->linknumber = 0; data->mcastport = -1; data->ttl = -1; data->knet_link_priority = -1; data->knet_ping_interval = -1; data->knet_ping_timeout = -1; data->knet_ping_precision = -1; data->knet_pong_count = -1; data->knet_transport = NULL; qb_list_init(&data->member_items_head); }; if (strcmp(path, "totem") == 0) { *state = MAIN_CP_CB_DATA_STATE_TOTEM; }; if (strcmp(path, "system") == 0) { *state = MAIN_CP_CB_DATA_STATE_SYSTEM; } if (strcmp(path, "logging.logger_subsys") == 0) { *state = MAIN_CP_CB_DATA_STATE_LOGGER_SUBSYS; qb_list_init(&data->logger_subsys_items_head); data->subsys = NULL; } if (strcmp(path, "logging.logging_daemon") == 0) { *state = MAIN_CP_CB_DATA_STATE_LOGGING_DAEMON; qb_list_init(&data->logger_subsys_items_head); data->subsys = NULL; data->logging_daemon_name = NULL; } if (strcmp(path, "uidgid") == 0) { *state = MAIN_CP_CB_DATA_STATE_UIDGID; } if (strcmp(path, "totem.interface.member") == 0) { *state = MAIN_CP_CB_DATA_STATE_MEMBER; } if (strcmp(path, "quorum") == 0) { *state = MAIN_CP_CB_DATA_STATE_QUORUM; } if (strcmp(path, "quorum.device") == 0) { *state = MAIN_CP_CB_DATA_STATE_QDEVICE; } if (strcmp(path, "nodelist") == 0) { *state = MAIN_CP_CB_DATA_STATE_NODELIST; data->node_number = 0; } if (strcmp(path, "nodelist.node") == 0) { *state = MAIN_CP_CB_DATA_STATE_NODELIST_NODE; } if (strcmp(path, "resources") == 0) { *state = MAIN_CP_CB_DATA_STATE_RESOURCES; } if (strcmp(path, "resources.system") == 0) { *state = MAIN_CP_CB_DATA_STATE_RESOURCES_SYSTEM; } if (strcmp(path, "resources.system.memory_used") == 0) { *state = MAIN_CP_CB_DATA_STATE_RESOURCES_SYSTEM_MEMUSED; } if (strcmp(path, "resources.process") == 0) { *state = MAIN_CP_CB_DATA_STATE_RESOURCES_PROCESS; } if (strcmp(path, "resources.process.memory_used") == 0) { *state = MAIN_CP_CB_DATA_STATE_RESOURCES_PROCESS_MEMUSED; } break; case PARSER_CB_SECTION_END: switch (*state) { case MAIN_CP_CB_DATA_STATE_INTERFACE: /* * Create new interface section */ if (data->bindnetaddr != NULL) { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.bindnetaddr", data->linknumber); cs_err = icmap_set_string_r(config_map, key_name, data->bindnetaddr); free(data->bindnetaddr); data->bindnetaddr = NULL; if (cs_err != CS_OK) { goto icmap_set_error; } } if (data->mcastaddr != NULL) { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.mcastaddr", data->linknumber); cs_err = icmap_set_string_r(config_map, key_name, data->mcastaddr); free(data->mcastaddr); data->mcastaddr = NULL; if (cs_err != CS_OK) { goto icmap_set_error; } } if (data->broadcast != NULL) { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.broadcast", data->linknumber); cs_err = icmap_set_string_r(config_map, key_name, data->broadcast); free(data->broadcast); data->broadcast = NULL; if (cs_err != CS_OK) { goto icmap_set_error; } } if (data->mcastport > -1) { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.mcastport", data->linknumber); if ((cs_err = icmap_set_uint16_r(config_map, key_name, data->mcastport)) != CS_OK) { goto icmap_set_error; } } if (data->ttl > -1) { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.ttl", data->linknumber); if ((cs_err = icmap_set_uint8_r(config_map, key_name, data->ttl)) != CS_OK) { goto icmap_set_error; } } if (data->knet_link_priority > -1) { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.knet_link_priority", data->linknumber); if ((cs_err = icmap_set_uint8_r(config_map, key_name, data->knet_link_priority)) != CS_OK) { goto icmap_set_error; } } if (data->knet_ping_interval > -1) { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.knet_ping_interval", data->linknumber); if ((cs_err = icmap_set_uint32_r(config_map, key_name, data->knet_ping_interval)) != CS_OK) { goto icmap_set_error; } } if (data->knet_ping_timeout > -1) { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.knet_ping_timeout", data->linknumber); if ((cs_err = icmap_set_uint32_r(config_map, key_name, data->knet_ping_timeout)) != CS_OK) { goto icmap_set_error; } } if (data->knet_ping_precision > -1) { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.knet_ping_precision", data->linknumber); if ((cs_err = icmap_set_uint32_r(config_map, key_name, data->knet_ping_precision)) != CS_OK) { goto icmap_set_error; } } if (data->knet_pong_count > -1) { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.knet_pong_count", data->linknumber); if ((cs_err = icmap_set_uint32_r(config_map, key_name, data->knet_pong_count)) != CS_OK) { goto icmap_set_error; } } if (data->knet_transport) { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.knet_transport", data->linknumber); cs_err = icmap_set_string_r(config_map, key_name, data->knet_transport); free(data->knet_transport); if (cs_err != CS_OK) { goto icmap_set_error; } } ii = 0; qb_list_for_each_safe(iter, tmp_iter, &(data->member_items_head)) { kv_item = qb_list_entry(iter, struct key_value_list_item, list); snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.member.%u", data->linknumber, ii); cs_err = icmap_set_string_r(config_map, key_name, kv_item->value); free(kv_item->value); free(kv_item->key); free(kv_item); ii++; if (cs_err != CS_OK) { goto icmap_set_error; } } break; case MAIN_CP_CB_DATA_STATE_LOGGER_SUBSYS: if (data->subsys == NULL) { *error_string = "No subsys key in logger_subsys directive"; return (0); } qb_list_for_each_safe(iter, tmp_iter, &(data->logger_subsys_items_head)) { kv_item = qb_list_entry(iter, struct key_value_list_item, list); snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "logging.logger_subsys.%s.%s", data->subsys, kv_item->key); cs_err = icmap_set_string_r(config_map, key_name, kv_item->value); free(kv_item->value); free(kv_item->key); free(kv_item); if (cs_err != CS_OK) { goto icmap_set_error; } } snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "logging.logger_subsys.%s.subsys", data->subsys); cs_err = icmap_set_string_r(config_map, key_name, data->subsys); free(data->subsys); if (cs_err != CS_OK) { goto icmap_set_error; } break; case MAIN_CP_CB_DATA_STATE_LOGGING_DAEMON: if (data->logging_daemon_name == NULL) { *error_string = "No name key in logging_daemon directive"; return (0); } qb_list_for_each_safe(iter, tmp_iter, &(data->logger_subsys_items_head)) { kv_item = qb_list_entry(iter, struct key_value_list_item, list); if (data->subsys == NULL) { if (strcmp(data->logging_daemon_name, "corosync") == 0) { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "logging.%s", kv_item->key); } else { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "logging.logging_daemon.%s.%s", data->logging_daemon_name, kv_item->key); } } else { if (strcmp(data->logging_daemon_name, "corosync") == 0) { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "logging.logger_subsys.%s.%s", data->subsys, kv_item->key); } else { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "logging.logging_daemon.%s.%s.%s", data->logging_daemon_name, data->subsys, kv_item->key); } } cs_err = icmap_set_string_r(config_map, key_name, kv_item->value); free(kv_item->value); free(kv_item->key); free(kv_item); if (cs_err != CS_OK) { goto icmap_set_error; } } if (data->subsys == NULL) { if (strcmp(data->logging_daemon_name, "corosync") != 0) { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "logging.logging_daemon.%s.name", data->logging_daemon_name); cs_err = icmap_set_string_r(config_map, key_name, data->logging_daemon_name); } } else { if (strcmp(data->logging_daemon_name, "corosync") == 0) { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "logging.logger_subsys.%s.subsys", data->subsys); cs_err = icmap_set_string_r(config_map, key_name, data->subsys); } else { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "logging.logging_daemon.%s.%s.subsys", data->logging_daemon_name, data->subsys); cs_err = icmap_set_string_r(config_map, key_name, data->subsys); if (cs_err != CS_OK) { free(data->subsys); free(data->logging_daemon_name); goto icmap_set_error; } snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "logging.logging_daemon.%s.%s.name", data->logging_daemon_name, data->subsys); cs_err = icmap_set_string_r(config_map, key_name, data->logging_daemon_name); } } free(data->subsys); free(data->logging_daemon_name); if (cs_err != CS_OK) { goto icmap_set_error; } break; case MAIN_CP_CB_DATA_STATE_NODELIST_NODE: data->node_number++; break; case MAIN_CP_CB_DATA_STATE_NORMAL: case MAIN_CP_CB_DATA_STATE_PLOAD: case MAIN_CP_CB_DATA_STATE_UIDGID: case MAIN_CP_CB_DATA_STATE_MEMBER: case MAIN_CP_CB_DATA_STATE_QUORUM: case MAIN_CP_CB_DATA_STATE_QDEVICE: case MAIN_CP_CB_DATA_STATE_NODELIST: case MAIN_CP_CB_DATA_STATE_TOTEM: case MAIN_CP_CB_DATA_STATE_SYSTEM: break; case MAIN_CP_CB_DATA_STATE_RESOURCES: *state = MAIN_CP_CB_DATA_STATE_NORMAL; break; case MAIN_CP_CB_DATA_STATE_RESOURCES_SYSTEM: *state = MAIN_CP_CB_DATA_STATE_RESOURCES; break; case MAIN_CP_CB_DATA_STATE_RESOURCES_SYSTEM_MEMUSED: *state = MAIN_CP_CB_DATA_STATE_RESOURCES_SYSTEM; break; case MAIN_CP_CB_DATA_STATE_RESOURCES_PROCESS: *state = MAIN_CP_CB_DATA_STATE_RESOURCES; break; case MAIN_CP_CB_DATA_STATE_RESOURCES_PROCESS_MEMUSED: *state = MAIN_CP_CB_DATA_STATE_RESOURCES_PROCESS; break; } break; } return (1); atoi_error: min_val = max_val = 0; /* * This is really assert, because developer ether doesn't set val_type correctly or * we've got here after some nasty memory overwrite */ assert(safe_atoq_range(val_type, &min_val, &max_val) == 0); if (snprintf(formated_err, sizeof(formated_err), "Value of key \"%s\" is expected to be integer in range (%lld..%lld), but \"%s\" was given", key_name, min_val, max_val, value) >= sizeof(formated_err)) { *error_string = "Can't format parser error message"; } else { *error_string = formated_err; } return (0); icmap_set_error: if (snprintf(formated_err, sizeof(formated_err), "Can't store key \"%s\" into icmap, returned error is %s", key_name, cs_strerror(cs_err)) >= sizeof(formated_err)) { *error_string = "Can't format parser error message"; } else { *error_string = formated_err; } return (0); } static int uidgid_config_parser_cb(const char *path, char *key, char *value, enum main_cp_cb_data_state *state, enum parser_cb_type type, const char **error_string, icmap_map_t config_map, void *user_data) { char key_name[ICMAP_KEYNAME_MAXLEN]; int uid, gid; static char formated_err[256]; cs_error_t cs_err; switch (type) { case PARSER_CB_START: break; case PARSER_CB_END: break; case PARSER_CB_ITEM: if (strcmp(path, "uidgid.uid") == 0) { uid = uid_determine(value); if (uid == -1) { *error_string = error_string_response; return (0); } snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "uidgid.config.uid.%u", uid); if ((cs_err = icmap_set_uint8_r(config_map, key_name, 1)) != CS_OK) { goto icmap_set_error; } } else if (strcmp(path, "uidgid.gid") == 0) { gid = gid_determine(value); if (gid == -1) { *error_string = error_string_response; return (0); } snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "uidgid.config.gid.%u", gid); if ((cs_err = icmap_set_uint8_r(config_map, key_name, 1)) != CS_OK) { goto icmap_set_error; } } else { *error_string = "uidgid: Only uid and gid are allowed items"; return (0); } break; case PARSER_CB_SECTION_START: if (strcmp(path, "uidgid") != 0) { *error_string = "uidgid: Can't add subsection different than uidgid"; return (0); }; break; case PARSER_CB_SECTION_END: break; } return (1); icmap_set_error: if (snprintf(formated_err, sizeof(formated_err), "Can't store key \"%s\" into icmap, returned error is %s", key_name, cs_strerror(cs_err)) >= sizeof(formated_err)) { *error_string = "Can't format parser error message"; } else { *error_string = formated_err; } return (0); } static int read_uidgid_files_into_icmap( const char **error_string, icmap_map_t config_map) { FILE *fp; char *dirname_res; DIR *dp; struct dirent *dirent; char filename[PATH_MAX + FILENAME_MAX + 1]; char uidgid_dirname[PATH_MAX + FILENAME_MAX + 1]; int res = 0; struct stat stat_buf; enum main_cp_cb_data_state state = MAIN_CP_CB_DATA_STATE_NORMAL; char key_name[ICMAP_KEYNAME_MAXLEN]; int line_no; /* * Build uidgid directory based on corosync.conf file location */ res = snprintf(filename, sizeof(filename), "%s", corosync_get_config_file()); if (res >= sizeof(filename)) { *error_string = "uidgid.d path too long"; return (-1); } dirname_res = dirname(filename); res = snprintf(uidgid_dirname, sizeof(uidgid_dirname), "%s/%s", dirname_res, "uidgid.d"); if (res >= sizeof(uidgid_dirname)) { *error_string = "uidgid.d path too long"; return (-1); } dp = opendir (uidgid_dirname); if (dp == NULL) return 0; for (dirent = readdir(dp); dirent != NULL; dirent = readdir(dp)) { res = snprintf(filename, sizeof (filename), "%s/%s", uidgid_dirname, dirent->d_name); if (res >= sizeof(filename)) { res = -1; *error_string = "uidgid.d dirname path too long"; goto error_exit; } res = stat (filename, &stat_buf); if (res == 0 && S_ISREG(stat_buf.st_mode)) { fp = fopen (filename, "r"); if (fp == NULL) continue; key_name[0] = 0; line_no = 0; res = parse_section(fp, filename, &line_no, key_name, error_string, 0, state, uidgid_config_parser_cb, config_map, NULL); fclose (fp); if (res != 0) { goto error_exit; } } } error_exit: closedir(dp); return res; } /* Read config file and load into icmap */ static int read_config_file_into_icmap( const char **error_string, icmap_map_t config_map) { FILE *fp; const char *filename; char *error_reason = error_string_response; int res; char key_name[ICMAP_KEYNAME_MAXLEN]; struct main_cp_cb_data data; enum main_cp_cb_data_state state = MAIN_CP_CB_DATA_STATE_NORMAL; int line_no; filename = corosync_get_config_file(); fp = fopen (filename, "r"); if (fp == NULL) { char error_str[100]; const char *error_ptr = qb_strerror_r(errno, error_str, sizeof(error_str)); snprintf (error_reason, sizeof(error_string_response), "Can't read file %s: %s", filename, error_ptr); *error_string = error_reason; return -1; } key_name[0] = 0; line_no = 0; res = parse_section(fp, filename, &line_no, key_name, error_string, 0, state, main_config_parser_cb, config_map, &data); fclose(fp); if (res == 0) { res = read_uidgid_files_into_icmap(error_string, config_map); } if (res == 0) { snprintf (error_reason, sizeof(error_string_response), "Successfully read main configuration file '%s'.", filename); *error_string = error_reason; } return res; } diff --git a/exec/totemknet.c b/exec/totemknet.c index 1093f02d..4db0f371 100644 --- a/exec/totemknet.c +++ b/exec/totemknet.c @@ -1,2366 +1,2374 @@ /* * Copyright (c) 2016-2022 Red Hat, Inc. * * All rights reserved. * * Author: Christine Caulfield (ccaulfie@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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBNOZZLE #include #include #endif #include #include #include #include #include #include "totemknet.h" #include "main.h" #include "util.h" #include #include #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif #ifdef HAVE_LIBNOZZLE static int setup_nozzle(void *knet_context); #endif /* Should match that used by cfg */ #define CFG_INTERFACE_STATUS_MAX_LEN 512 struct totemknet_instance { struct crypto_instance *crypto_inst; struct knet_handle_crypto_cfg last_good_crypto_cfg; qb_loop_t *poll_handle; knet_handle_t knet_handle; int link_mode; void *context; int (*totemknet_deliver_fn) ( void *context, const void *msg, unsigned int msg_len, const struct sockaddr_storage *system_from); int (*totemknet_iface_change_fn) ( void *context, const struct totem_ip_address *iface_address, unsigned int link_no); void (*totemknet_mtu_changed) ( void *context, int net_mtu); void (*totemknet_target_set_completed) (void *context); /* * Function and data used to log messages */ int totemknet_log_level_security; int totemknet_log_level_error; int totemknet_log_level_warning; int totemknet_log_level_notice; int totemknet_log_level_debug; int totemknet_subsys_id; int knet_subsys_id; void (*totemknet_log_printf) ( int level, int subsys, const char *function, const char *file, int line, const char *format, ...)__attribute__((format(printf, 6, 7))); void *knet_context; char iov_buffer[KNET_MAX_PACKET_SIZE]; char *link_status[INTERFACE_MAX]; struct totem_ip_address my_ids[INTERFACE_MAX]; uint16_t ip_port[INTERFACE_MAX]; int our_nodeid; int loopback_link; struct totem_config *totem_config; struct totem_ip_address token_target; qb_loop_timer_handle timer_netif_check_timeout; qb_loop_timer_handle timer_merge_detect_timeout; int send_merge_detect_message; unsigned int merge_detect_messages_sent_before_timeout; int logpipes[2]; int knet_fd; pthread_mutex_t log_mutex; #ifdef HAVE_LIBNOZZLE char *nozzle_name; char *nozzle_ipaddr; char *nozzle_prefix; char *nozzle_macaddr; nozzle_t nozzle_handle; #endif }; /* Awkward. But needed to get stats from knet */ struct totemknet_instance *global_instance; struct work_item { const void *msg; unsigned int msg_len; struct totemknet_instance *instance; }; int totemknet_member_list_rebind_ip ( void *knet_context); static int totemknet_configure_compression ( struct totemknet_instance *instance, struct totem_config *totem_config); static void totemknet_start_merge_detect_timeout( void *knet_context); static void totemknet_stop_merge_detect_timeout( void *knet_context); static void log_flush_messages ( void *knet_context); static void totemknet_instance_initialize (struct totemknet_instance *instance) { int res; memset (instance, 0, sizeof (struct totemknet_instance)); res = pthread_mutex_init(&instance->log_mutex, NULL); /* * There is not too much else what can be done. */ assert(res == 0); } #define knet_log_printf_lock(level, subsys, function, file, line, format, args...) \ do { \ (void)pthread_mutex_lock(&instance->log_mutex); \ instance->totemknet_log_printf ( \ level, subsys, function, file, line, \ (const char *)format, ##args); \ (void)pthread_mutex_unlock(&instance->log_mutex); \ } while (0); #define knet_log_printf(level, format, args...) \ do { \ knet_log_printf_lock ( \ level, instance->totemknet_subsys_id, \ __FUNCTION__, __FILE__, __LINE__, \ (const char *)format, ##args); \ } while (0); #define libknet_log_printf(level, format, args...) \ do { \ knet_log_printf_lock ( \ level, instance->knet_subsys_id, \ __FUNCTION__, "libknet.h", __LINE__, \ (const char *)format, ##args); \ } while (0); #define KNET_LOGSYS_PERROR(err_num, level, fmt, args...) \ do { \ char _error_str[LOGSYS_MAX_PERROR_MSG_LEN]; \ const char *_error_ptr = qb_strerror_r(err_num, _error_str, sizeof(_error_str)); \ instance->totemknet_log_printf ( \ level, instance->totemknet_subsys_id, \ __FUNCTION__, __FILE__, __LINE__, \ fmt ": %s (%d)", ##args, _error_ptr, err_num); \ } while(0) #ifdef HAVE_LIBNOZZLE static inline int is_ether_addr_multicast(const uint8_t *addr) { return (addr[0] & 0x01); } static inline int is_ether_addr_zero(const uint8_t *addr) { return (!addr[0] && !addr[1] && !addr[2] && !addr[3] && !addr[4] && !addr[5]); } static int ether_host_filter_fn(void *private_data, const unsigned char *outdata, ssize_t outdata_len, uint8_t tx_rx, knet_node_id_t this_host_id, knet_node_id_t src_host_id, int8_t *channel, knet_node_id_t *dst_host_ids, size_t *dst_host_ids_entries) { struct ether_header *eth_h = (struct ether_header *)outdata; uint8_t *dst_mac = (uint8_t *)eth_h->ether_dhost; uint16_t dst_host_id; if (is_ether_addr_zero(dst_mac)) return -1; if (is_ether_addr_multicast(dst_mac)) { return 1; } memmove(&dst_host_id, &dst_mac[4], 2); dst_host_ids[0] = ntohs(dst_host_id); *dst_host_ids_entries = 1; return 0; } #endif static int dst_host_filter_callback_fn(void *private_data, const unsigned char *outdata, ssize_t outdata_len, uint8_t tx_rx, knet_node_id_t this_host_id, knet_node_id_t src_host_id, int8_t *channel, knet_node_id_t *dst_host_ids, size_t *dst_host_ids_entries) { struct totem_message_header *header = (struct totem_message_header *)outdata; int res; #ifdef HAVE_LIBNOZZLE if (*channel != 0) { return ether_host_filter_fn(private_data, outdata, outdata_len, tx_rx, this_host_id, src_host_id, channel, dst_host_ids, dst_host_ids_entries); } #endif if (header->target_nodeid) { dst_host_ids[0] = header->target_nodeid; *dst_host_ids_entries = 1; res = 0; /* unicast message */ } else { *dst_host_ids_entries = 0; res = 1; /* multicast message */ } return res; } static void socket_error_callback_fn(void *private_data, int datafd, int8_t channel, uint8_t tx_rx, int error, int errorno) { struct totemknet_instance *instance = (struct totemknet_instance *)private_data; knet_log_printf (LOGSYS_LEVEL_DEBUG, "Knet socket ERROR notification called: txrx=%d, error=%d, errorno=%d", tx_rx, error, errorno); if ((error == -1 && errorno != EAGAIN) || (error == 0)) { knet_handle_remove_datafd(instance->knet_handle, datafd); } } static void host_change_callback_fn(void *private_data, knet_node_id_t host_id, uint8_t reachable, uint8_t remote, uint8_t external) { struct totemknet_instance *instance = (struct totemknet_instance *)private_data; // TODO: what? if anything. knet_log_printf (LOGSYS_LEVEL_DEBUG, "Knet host change callback. nodeid: " CS_PRI_NODE_ID " reachable: %d", host_id, reachable); } static void pmtu_change_callback_fn(void *private_data, unsigned int data_mtu) { struct totemknet_instance *instance = (struct totemknet_instance *)private_data; knet_log_printf (LOGSYS_LEVEL_DEBUG, "Knet pMTU change: %d", data_mtu); /* We don't need to tell corosync the actual knet MTU */ // instance->totemknet_mtu_changed(instance->context, data_mtu); } int totemknet_crypto_set ( void *knet_context, const char *cipher_type, const char *hash_type) { return (0); } static inline void ucast_sendmsg ( struct totemknet_instance *instance, struct totem_ip_address *system_to, const void *msg, unsigned int msg_len) { int res = 0; struct totem_message_header *header = (struct totem_message_header *)msg; struct msghdr msg_ucast; struct iovec iovec; header->target_nodeid = system_to->nodeid; iovec.iov_base = (void *)msg; iovec.iov_len = msg_len; /* * Build unicast message */ memset(&msg_ucast, 0, sizeof(msg_ucast)); msg_ucast.msg_iov = (void *)&iovec; msg_ucast.msg_iovlen = 1; #ifdef HAVE_MSGHDR_CONTROL msg_ucast.msg_control = 0; #endif #ifdef HAVE_MSGHDR_CONTROLLEN msg_ucast.msg_controllen = 0; #endif #ifdef HAVE_MSGHDR_FLAGS msg_ucast.msg_flags = 0; #endif #ifdef HAVE_MSGHDR_ACCRIGHTS msg_ucast.msg_accrights = NULL; #endif #ifdef HAVE_MSGHDR_ACCRIGHTSLEN msg_ucast.msg_accrightslen = 0; #endif /* * Transmit unicast message * An error here is recovered by totemsrp */ res = sendmsg (instance->knet_fd, &msg_ucast, MSG_NOSIGNAL); if (res < 0) { KNET_LOGSYS_PERROR (errno, instance->totemknet_log_level_debug, "sendmsg(ucast) failed (non-critical)"); } } static inline void mcast_sendmsg ( struct totemknet_instance *instance, const void *msg, unsigned int msg_len, int only_active) { int res; struct totem_message_header *header = (struct totem_message_header *)msg; struct msghdr msg_mcast; struct iovec iovec; iovec.iov_base = (void *)msg; iovec.iov_len = msg_len; header->target_nodeid = 0; /* * Build multicast message */ memset(&msg_mcast, 0, sizeof(msg_mcast)); msg_mcast.msg_iov = (void *)&iovec; msg_mcast.msg_iovlen = 1; #ifdef HAVE_MSGHDR_CONTROL msg_mcast.msg_control = 0; #endif #ifdef HAVE_MSGHDR_CONTROLLEN msg_mcast.msg_controllen = 0; #endif #ifdef HAVE_MSGHDR_FLAGS msg_mcast.msg_flags = 0; #endif #ifdef HAVE_MSGHDR_ACCRIGHTS msg_mcast.msg_accrights = NULL; #endif #ifdef HAVE_MSGHDR_ACCRIGHTSLEN msg_mcast.msg_accrightslen = 0; #endif // log_printf (LOGSYS_LEVEL_DEBUG, "totemknet: mcast_sendmsg. only_active=%d, len=%d", only_active, msg_len); res = sendmsg (instance->knet_fd, &msg_mcast, MSG_NOSIGNAL); if (res < msg_len) { knet_log_printf (LOGSYS_LEVEL_DEBUG, "totemknet: mcast_send sendmsg returned %d", res); } if (!only_active || instance->send_merge_detect_message) { /* * Current message was sent to all nodes */ instance->merge_detect_messages_sent_before_timeout++; instance->send_merge_detect_message = 0; } } static int node_compare(const void *aptr, const void *bptr) { uint16_t a,b; a = *(uint16_t *)aptr; b = *(uint16_t *)bptr; return a > b; } #ifndef OWN_INDEX_NONE #define OWN_INDEX_NONE -1 #endif int totemknet_nodestatus_get ( void *knet_context, unsigned int nodeid, struct totem_node_status *node_status) { int i; int res = 0; struct knet_link_status link_status; struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; struct knet_host_status knet_host_status; uint8_t link_list[KNET_MAX_LINK]; size_t num_links; if (!instance->knet_handle) { return CS_ERR_NOT_EXIST; /* Not using knet */ } if (!node_status) { return CS_ERR_INVALID_PARAM; } res = knet_host_get_status(instance->knet_handle, nodeid, &knet_host_status); if (res) { knet_log_printf (LOGSYS_LEVEL_WARNING, "knet_handle_get_host_status(%d) failed: %d", nodeid, res); return (-1); } node_status->nodeid = nodeid; node_status->reachable = knet_host_status.reachable; node_status->remote = knet_host_status.remote; node_status->external = knet_host_status.external; #ifdef HAVE_KNET_ONWIRE_VER res = knet_handle_get_onwire_ver(instance->knet_handle, nodeid, &node_status->onwire_min, &node_status->onwire_max, &node_status->onwire_ver); if (res) { knet_log_printf (LOGSYS_LEVEL_WARNING, "knet_handle_get_onwire_ver(%d) failed: %d", nodeid, res); return (-1); } #endif /* Get link info */ res = knet_link_get_link_list(instance->knet_handle, nodeid, link_list, &num_links); if (res) { knet_log_printf (LOGSYS_LEVEL_WARNING, "knet_link_get_link_list(%d) failed: %d", nodeid, res); return (-1); } /* node_status[] has been zeroed for us in totempg.c */ for (i=0; i < num_links; i++) { if (!instance->totem_config->interfaces[link_list[i]].configured) { continue; } res = knet_link_get_status(instance->knet_handle, nodeid, link_list[i], &link_status, sizeof(link_status)); if (res == 0) { node_status->link_status[link_list[i]].enabled = link_status.enabled; node_status->link_status[link_list[i]].connected = link_status.connected; node_status->link_status[link_list[i]].dynconnected = link_status.dynconnected; node_status->link_status[link_list[i]].mtu = link_status.mtu; memcpy(node_status->link_status[link_list[i]].src_ipaddr, link_status.src_ipaddr, KNET_MAX_HOST_LEN); memcpy(node_status->link_status[link_list[i]].dst_ipaddr, link_status.dst_ipaddr, KNET_MAX_HOST_LEN); } else { knet_log_printf (LOGSYS_LEVEL_WARNING, "knet_link_get_link_status(%d, %d) failed: %d", nodeid, link_list[i], res); } } return res; } int totemknet_ifaces_get (void *knet_context, char ***status, unsigned int *iface_count) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; struct knet_link_status link_status; knet_node_id_t host_list[KNET_MAX_HOST]; uint8_t link_list[KNET_MAX_LINK]; size_t num_hosts; size_t num_links; size_t link_idx; int i,j; char *ptr; int res = 0; /* * Don't do the whole 'link_info' bit if the caller just wants * a count of interfaces. */ if (status) { int own_idx = OWN_INDEX_NONE; res = knet_host_get_host_list(instance->knet_handle, host_list, &num_hosts); if (res) { return (-1); } qsort(host_list, num_hosts, sizeof(uint16_t), node_compare); for (j=0; jour_nodeid) { own_idx = j; break; } } for (i=0; ilink_status[i], 'd', CFG_INTERFACE_STATUS_MAX_LEN-1); if (own_idx != OWN_INDEX_NONE) { instance->link_status[i][own_idx] = 'n'; } instance->link_status[i][num_hosts] = '\0'; } /* This is all a bit "inside-out" because "status" is a set of strings per link * and knet orders things by host */ for (j=0; jknet_handle, host_list[j], link_list, &num_links); if (res) { return (-1); } link_idx = 0; for (i=0; i < num_links; i++) { /* * Skip over links that are unconfigured to corosync. This is basically * link0 if corosync isn't using it for comms, as we will still * have it set up for loopback. */ if (!instance->totem_config->interfaces[link_list[i]].configured) { continue; } ptr = instance->link_status[link_idx++]; res = knet_link_get_status(instance->knet_handle, host_list[j], link_list[i], &link_status, sizeof(link_status)); if (res == 0) { ptr[j] = '0' + (link_status.enabled | link_status.connected<<1 | link_status.dynconnected<<2); } else { knet_log_printf (LOGSYS_LEVEL_ERROR, "totemknet_ifaces_get: Cannot get link status: %s", strerror(errno)); ptr[j] = '?'; } } } *status = instance->link_status; } *iface_count = INTERFACE_MAX; return (res); } int totemknet_finalize ( void *knet_context) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int res = 0; int i,j; static knet_node_id_t nodes[KNET_MAX_HOST]; /* static to save stack */ uint8_t links[KNET_MAX_LINK]; size_t num_nodes; size_t num_links; knet_log_printf(LOG_DEBUG, "totemknet: finalize"); qb_loop_poll_del (instance->poll_handle, instance->logpipes[0]); qb_loop_poll_del (instance->poll_handle, instance->knet_fd); /* * Disable forwarding to make knet flush send queue. This ensures that the LEAVE message will be sent. */ res = knet_handle_setfwd(instance->knet_handle, 0); if (res) { knet_log_printf (LOGSYS_LEVEL_CRIT, "totemknet: knet_handle_setfwd failed: %s", strerror(errno)); } res = knet_host_get_host_list(instance->knet_handle, nodes, &num_nodes); if (res) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Cannot get knet node list for shutdown: %s", strerror(errno)); /* Crash out anyway */ goto finalise_error; } /* Tidily shut down all nodes & links. */ for (i=0; iknet_handle, nodes[i], links, &num_links); if (res) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Cannot get knet link list for node " CS_PRI_NODE_ID ": %s", nodes[i], strerror(errno)); goto finalise_error; } for (j=0; jknet_handle, nodes[i], links[j], 0); if (res) { knet_log_printf (LOGSYS_LEVEL_ERROR, "totemknet: knet_link_set_enable(node " CS_PRI_NODE_ID ", link %d) failed: %s", nodes[i], links[j], strerror(errno)); } res = knet_link_clear_config(instance->knet_handle, nodes[i], links[j]); if (res) { knet_log_printf (LOGSYS_LEVEL_ERROR, "totemknet: knet_link_clear_config(node " CS_PRI_NODE_ID ", link %d) failed: %s", nodes[i], links[j], strerror(errno)); } } res = knet_host_remove(instance->knet_handle, nodes[i]); if (res) { knet_log_printf (LOGSYS_LEVEL_ERROR, "totemknet: knet_host_remove(node " CS_PRI_NODE_ID ") failed: %s", nodes[i], strerror(errno)); } } finalise_error: res = knet_handle_free(instance->knet_handle); if (res) { knet_log_printf (LOGSYS_LEVEL_CRIT, "totemknet: knet_handle_free failed: %s", strerror(errno)); } totemknet_stop_merge_detect_timeout(instance); log_flush_messages(instance); /* * Error is deliberately ignored */ (void)pthread_mutex_destroy(&instance->log_mutex); return (res); } static int log_deliver_fn ( int fd, int revents, void *data) { struct totemknet_instance *instance = (struct totemknet_instance *)data; char buffer[sizeof(struct knet_log_msg)*4]; char *bufptr = buffer; int done = 0; int len; len = read(fd, buffer, sizeof(buffer)); while (done < len) { struct knet_log_msg *msg = (struct knet_log_msg *)bufptr; switch (msg->msglevel) { case KNET_LOG_ERR: libknet_log_printf (LOGSYS_LEVEL_ERROR, "%s: %s", knet_log_get_subsystem_name(msg->subsystem), msg->msg); break; case KNET_LOG_WARN: libknet_log_printf (LOGSYS_LEVEL_WARNING, "%s: %s", knet_log_get_subsystem_name(msg->subsystem), msg->msg); break; case KNET_LOG_INFO: libknet_log_printf (LOGSYS_LEVEL_INFO, "%s: %s", knet_log_get_subsystem_name(msg->subsystem), msg->msg); break; case KNET_LOG_DEBUG: libknet_log_printf (LOGSYS_LEVEL_DEBUG, "%s: %s", knet_log_get_subsystem_name(msg->subsystem), msg->msg); break; #ifdef KNET_LOG_TRACE case KNET_LOG_TRACE: libknet_log_printf (LOGSYS_LEVEL_TRACE, "%s: %s", knet_log_get_subsystem_name(msg->subsystem), msg->msg); break; #endif } bufptr += sizeof(struct knet_log_msg); done += sizeof(struct knet_log_msg); } return 0; } static int data_deliver_fn ( int fd, int revents, void *data) { struct totemknet_instance *instance = (struct totemknet_instance *)data; struct msghdr msg_hdr; struct iovec iov_recv; struct sockaddr_storage system_from; ssize_t msg_len; char *data_ptr = instance->iov_buffer; int truncated_packet; iov_recv.iov_base = instance->iov_buffer; iov_recv.iov_len = KNET_MAX_PACKET_SIZE; msg_hdr.msg_name = &system_from; msg_hdr.msg_namelen = sizeof (struct sockaddr_storage); msg_hdr.msg_iov = &iov_recv; msg_hdr.msg_iovlen = 1; #ifdef HAVE_MSGHDR_CONTROL msg_hdr.msg_control = 0; #endif #ifdef HAVE_MSGHDR_CONTROLLEN msg_hdr.msg_controllen = 0; #endif #ifdef HAVE_MSGHDR_FLAGS msg_hdr.msg_flags = 0; #endif #ifdef HAVE_MSGHDR_ACCRIGHTS msg_hdr.msg_accrights = NULL; #endif #ifdef HAVE_MSGHDR_ACCRIGHTSLEN msg_hdr.msg_accrightslen = 0; #endif msg_len = recvmsg (fd, &msg_hdr, MSG_NOSIGNAL | MSG_DONTWAIT); if (msg_len <= 0) { return (0); } truncated_packet = 0; /* * If it's from the knet fd then it will have the optional knet header on it */ #ifdef KNET_DATAFD_FLAG_RX_RETURN_INFO if (fd == instance->knet_fd) { struct knet_datafd_header *datafd_header = (struct knet_datafd_header *)data_ptr; /* knet_log_printf (LOGSYS_LEVEL_DEBUG, "Packet from knet_fd nodeid: %d\n", datafd_header->src_nodeid); */ /* Advance past the ACTUAL header size, not the size we think it might be */ data_ptr += datafd_header->size; msg_len -= datafd_header->size; } #endif #ifdef HAVE_MSGHDR_FLAGS if (msg_hdr.msg_flags & MSG_TRUNC) { truncated_packet = 1; } #else /* * We don't have MSGHDR_FLAGS, but we can (hopefully) safely make assumption that * if bytes_received == KNET_MAX_PACKET_SIZE then packet is truncated */ if (bytes_received == KNET_MAX_PACKET_SIZE) { truncated_packet = 1; } #endif if (truncated_packet) { knet_log_printf(instance->totemknet_log_level_error, "Received too big message. This may be because something bad is happening" "on the network (attack?), or you tried join more nodes than corosync is" "compiled with (%u) or bug in the code (bad estimation of " "the KNET_MAX_PACKET_SIZE). Dropping packet.", PROCESSOR_COUNT_MAX); return (0); } /* * Handle incoming message */ instance->totemknet_deliver_fn ( instance->context, data_ptr, msg_len, &system_from); return (0); } static void timer_function_netif_check_timeout ( void *data) { struct totemknet_instance *instance = (struct totemknet_instance *)data; int i; int res = 0; for (i=0; i < INTERFACE_MAX; i++) { if (!instance->totem_config->interfaces[i].configured) { continue; } res = instance->totemknet_iface_change_fn (instance->context, &instance->my_ids[i], i); } if (res != 0) { /* This is only called at startup, so we can quit here. Refresh takes a different path */ corosync_exit_error(COROSYNC_DONE_MAINCONFIGREAD); } } static void knet_set_access_list_config(struct totemknet_instance *instance) { #ifdef HAVE_KNET_ACCESS_LIST uint32_t value; cs_error_t err; value = instance->totem_config->block_unlisted_ips; knet_log_printf (LOGSYS_LEVEL_DEBUG, "knet_enable access list: %d", value); err = knet_handle_enable_access_lists(instance->knet_handle, value); if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_WARNING, "knet_handle_enable_access_lists failed"); } #endif } void totemknet_configure_log_level() { int logsys_log_mode; int knet_log_mode = KNET_LOG_INFO; uint8_t s; int err; if (!global_instance || !global_instance->knet_handle) { return; } /* Reconfigure logging level */ logsys_log_mode = logsys_config_debug_get("KNET"); switch (logsys_log_mode) { case LOGSYS_DEBUG_OFF: knet_log_mode = KNET_LOG_INFO; break; case LOGSYS_DEBUG_ON: knet_log_mode = KNET_LOG_DEBUG; break; case LOGSYS_DEBUG_TRACE: #ifdef KNET_LOG_TRACE knet_log_mode = KNET_LOG_TRACE; #else knet_log_mode = KNET_LOG_DEBUG; #endif break; } log_printf (LOGSYS_LEVEL_DEBUG, "totemknet setting log level %s", knet_log_get_loglevel_name(knet_log_mode)); err = 0; for (s = 0; sknet_handle, s, knet_log_mode); } /* If one fails, they all fail. no point in issuing KNET_MAX_SUBSYSTEMS errors */ if (err) { log_printf (LOGSYS_LEVEL_ERROR, "totemknet failed to set log level: %s", strerror(errno)); } } /* NOTE: this relies on the fact that totem_reload_notify() is called first */ static void totemknet_refresh_config( int32_t event, const char *key_name, struct icmap_notify_value new_val, struct icmap_notify_value old_val, void *user_data) { uint8_t reloading; int after_reload; uint32_t link_no; size_t num_nodes; knet_node_id_t host_ids[KNET_MAX_HOST]; int i; int err; struct totemknet_instance *instance = (struct totemknet_instance *)user_data; ENTER(); /* * If a full reload is in progress then don't do anything until it's done and * can reconfigure it all atomically */ if (icmap_get_uint8("config.totemconfig_reload_in_progress", &reloading) == CS_OK && reloading) { return; } after_reload = (strcmp(key_name, "config.totemconfig_reload_in_progress") == 0); knet_set_access_list_config(instance); if (strcmp(key_name, "totem.knet_pmtud_interval") == 0 || after_reload) { knet_log_printf (LOGSYS_LEVEL_DEBUG, "knet_pmtud_interval now %u", instance->totem_config->knet_pmtud_interval); err = knet_handle_pmtud_setfreq(instance->knet_handle, instance->totem_config->knet_pmtud_interval); if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_WARNING, "knet_handle_pmtud_setfreq failed"); } } if (strcmp(key_name, "totem.knet_mtu") == 0 || after_reload) { knet_log_printf (LOGSYS_LEVEL_DEBUG, "knet_mtu now %u", instance->totem_config->knet_mtu); err = knet_handle_pmtud_set(instance->knet_handle, instance->totem_config->knet_mtu); if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_WARNING, "knet_handle_pmtud failed"); } } /* Configure link parameters for each node */ err = knet_host_get_host_list(instance->knet_handle, host_ids, &num_nodes); if (err != 0) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_host_get_host_list failed"); } for (i=0; iour_nodeid || !instance->totem_config->interfaces[link_no].configured) { continue; } err = knet_link_set_ping_timers(instance->knet_handle, host_ids[i], link_no, instance->totem_config->interfaces[link_no].knet_ping_interval, instance->totem_config->interfaces[link_no].knet_ping_timeout, instance->totem_config->interfaces[link_no].knet_ping_precision); if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_link_set_ping_timers for node " CS_PRI_NODE_ID " link %d failed", host_ids[i], link_no); linkerr = err; } err = knet_link_set_pong_count(instance->knet_handle, host_ids[i], link_no, instance->totem_config->interfaces[link_no].knet_pong_count); if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_link_set_pong_count for node " CS_PRI_NODE_ID " link %d failed",host_ids[i], link_no); linkerr = err; } err = knet_link_set_priority(instance->knet_handle, host_ids[i], link_no, instance->totem_config->interfaces[link_no].knet_link_priority); if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_link_set_priority for node " CS_PRI_NODE_ID " link %d failed", host_ids[i], link_no); linkerr = err; } } if (linkerr) { icmap_set_string("config.reload_error_message", "Failed to set knet ping timers(2)"); } } /* Log levels get reconfigured from logconfig.c as that happens last in the reload */ LEAVE(); } static void totemknet_add_config_notifications(struct totemknet_instance *instance) { icmap_track_t icmap_track_totem = NULL; icmap_track_t icmap_track_reload = NULL; ENTER(); icmap_track_add("totem.", ICMAP_TRACK_ADD | ICMAP_TRACK_DELETE | ICMAP_TRACK_MODIFY | ICMAP_TRACK_PREFIX, totemknet_refresh_config, instance, &icmap_track_totem); icmap_track_add("config.totemconfig_reload_in_progress", ICMAP_TRACK_ADD | ICMAP_TRACK_MODIFY, totemknet_refresh_config, instance, &icmap_track_reload); LEAVE(); } static int totemknet_is_crypto_enabled(const struct totemknet_instance *instance) { return (!(strcmp(instance->totem_config->crypto_cipher_type, "none") == 0 && strcmp(instance->totem_config->crypto_hash_type, "none") == 0)); } static int totemknet_set_knet_crypto(struct totemknet_instance *instance) { struct knet_handle_crypto_cfg crypto_cfg; int res; /* These have already been validated */ memcpy(crypto_cfg.crypto_model, instance->totem_config->crypto_model, sizeof(crypto_cfg.crypto_model)); memcpy(crypto_cfg.crypto_cipher_type, instance->totem_config->crypto_cipher_type, sizeof(crypto_cfg.crypto_model)); memcpy(crypto_cfg.crypto_hash_type, instance->totem_config->crypto_hash_type, sizeof(crypto_cfg.crypto_model)); memcpy(crypto_cfg.private_key, instance->totem_config->private_key, instance->totem_config->private_key_len); crypto_cfg.private_key_len = instance->totem_config->private_key_len; #ifdef HAVE_KNET_CRYPTO_RECONF knet_log_printf(LOGSYS_LEVEL_DEBUG, "Configuring crypto %s/%s/%s on index %d", crypto_cfg.crypto_model, crypto_cfg.crypto_cipher_type, crypto_cfg.crypto_hash_type, instance->totem_config->crypto_index ); /* If crypto is being disabled we need to explicitly allow cleartext traffic in knet */ if (!totemknet_is_crypto_enabled(instance)) { res = knet_handle_crypto_rx_clear_traffic(instance->knet_handle, KNET_CRYPTO_RX_ALLOW_CLEAR_TRAFFIC); if (res) { knet_log_printf(LOGSYS_LEVEL_ERROR, "knet_handle_crypto_rx_clear_traffic(ALLOW) failed %s", strerror(errno)); } } /* use_config will be called later when all nodes are synced */ res = knet_handle_crypto_set_config(instance->knet_handle, &crypto_cfg, instance->totem_config->crypto_index); if (res == 0) { /* Keep a copy in case it fails in future */ memcpy(&instance->last_good_crypto_cfg, &crypto_cfg, sizeof(crypto_cfg)); } if (res == -1) { knet_log_printf(LOGSYS_LEVEL_ERROR, "knet_handle_crypto_set_config (index %d) failed: %s", instance->totem_config->crypto_index, strerror(errno)); goto exit_error; } if (res == -2) { knet_log_printf(LOGSYS_LEVEL_ERROR, "knet_handle_crypto_set_config (index %d) failed: -2", instance->totem_config->crypto_index); goto exit_error; } #else knet_log_printf(LOGSYS_LEVEL_DEBUG, "Configuring crypto %s/%s/%s", crypto_cfg.crypto_model, crypto_cfg.crypto_cipher_type, crypto_cfg.crypto_hash_type ); res = knet_handle_crypto(instance->knet_handle, &crypto_cfg); if (res == -1) { knet_log_printf(LOGSYS_LEVEL_ERROR, "knet_handle_crypto failed: %s", strerror(errno)); goto exit_error; } if (res == -2) { knet_log_printf(LOGSYS_LEVEL_ERROR, "knet_handle_crypto failed: -2"); goto exit_error; } #endif exit_error: #ifdef HAVE_KNET_CRYPTO_RECONF if (res) { icmap_set_string("config.reload_error_message", "Failed to set crypto parameters"); /* Restore the old values in cmap & totem_config */ icmap_set_string("totem.crypto_cipher", instance->last_good_crypto_cfg.crypto_cipher_type); icmap_set_string("totem.crypto_hash", instance->last_good_crypto_cfg.crypto_hash_type); icmap_set_string("totem.crypto_model", instance->last_good_crypto_cfg.crypto_model); memcpy(instance->totem_config->crypto_hash_type, instance->last_good_crypto_cfg.crypto_hash_type, sizeof(instance->last_good_crypto_cfg.crypto_hash_type)); memcpy(instance->totem_config->crypto_cipher_type, instance->last_good_crypto_cfg.crypto_cipher_type, sizeof(instance->last_good_crypto_cfg.crypto_cipher_type)); memcpy(instance->totem_config->crypto_model, instance->last_good_crypto_cfg.crypto_model, sizeof(instance->last_good_crypto_cfg.crypto_model)); } #endif return res; } /* * Create an instance */ int totemknet_initialize ( qb_loop_t *poll_handle, void **knet_context, struct totem_config *totem_config, totemsrp_stats_t *stats, void *context, int (*deliver_fn) ( void *context, const void *msg, unsigned int msg_len, const struct sockaddr_storage *system_from), int (*iface_change_fn) ( void *context, const struct totem_ip_address *iface_address, unsigned int link_no), void (*mtu_changed) ( void *context, int net_mtu), void (*target_set_completed) ( void *context)) { struct totemknet_instance *instance; char *tmp_str; int8_t channel=0; int allow_knet_handle_fallback=0; + uint64_t flags = 0; int res; int i; instance = malloc (sizeof (struct totemknet_instance)); if (instance == NULL) { return (-1); } totemknet_instance_initialize (instance); instance->totem_config = totem_config; /* * Configure logging */ instance->totemknet_log_level_security = 1; //totem_config->totem_logging_configuration.log_level_security; instance->totemknet_log_level_error = totem_config->totem_logging_configuration.log_level_error; instance->totemknet_log_level_warning = totem_config->totem_logging_configuration.log_level_warning; instance->totemknet_log_level_notice = totem_config->totem_logging_configuration.log_level_notice; instance->totemknet_log_level_debug = totem_config->totem_logging_configuration.log_level_debug; instance->totemknet_subsys_id = totem_config->totem_logging_configuration.log_subsys_id; instance->totemknet_log_printf = totem_config->totem_logging_configuration.log_printf; instance->knet_subsys_id = _logsys_subsys_create("KNET", "libknet.h"); /* * Initialize local variables for totemknet */ instance->our_nodeid = instance->totem_config->node_id; for (i=0; i< INTERFACE_MAX; i++) { totemip_copy(&instance->my_ids[i], &totem_config->interfaces[i].bindnet); instance->my_ids[i].nodeid = instance->our_nodeid; instance->ip_port[i] = totem_config->interfaces[i].ip_port; /* Needed for totemsrp */ totem_config->interfaces[i].boundto.nodeid = instance->our_nodeid; } instance->poll_handle = poll_handle; instance->context = context; instance->totemknet_deliver_fn = deliver_fn; instance->totemknet_iface_change_fn = iface_change_fn; instance->totemknet_mtu_changed = mtu_changed; instance->totemknet_target_set_completed = target_set_completed; instance->loopback_link = 0; res = pipe(instance->logpipes); if (res == -1) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_CRIT, "failed to create pipe for instance->logpipes"); goto exit_error; } if (fcntl(instance->logpipes[0], F_SETFL, O_NONBLOCK) == -1 || fcntl(instance->logpipes[1], F_SETFL, O_NONBLOCK) == -1) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_CRIT, "failed to set O_NONBLOCK flag for instance->logpipes"); goto exit_error; } if (icmap_get_string("system.allow_knet_handle_fallback", &tmp_str) == CS_OK) { if (strcmp(tmp_str, "yes") == 0) { allow_knet_handle_fallback = 1; } free(tmp_str); } + if (icmap_get_string("system.allow_knet_asymmetric_routes", &tmp_str) == CS_OK) { + if (strcmp(tmp_str, "yes") == 0) { + flags |= KNET_HANDLE_FLAG_ALLOWIFACEMISMATCH; + } + free(tmp_str); + } + #if defined(KNET_API_VER) && (KNET_API_VER == 2) - instance->knet_handle = knet_handle_new(instance->totem_config->node_id, instance->logpipes[1], KNET_LOG_DEBUG, KNET_HANDLE_FLAG_PRIVILEGED); + instance->knet_handle = knet_handle_new(instance->totem_config->node_id, instance->logpipes[1], KNET_LOG_DEBUG, flags | KNET_HANDLE_FLAG_PRIVILEGED); #else - instance->knet_handle = knet_handle_new(instance->totem_config->node_id, instance->logpipes[1], KNET_LOG_DEBUG); + instance->knet_handle = knet_handle_new_ex(instance->totem_config->node_id, instance->logpipes[1], KNET_LOG_DEBUG, flags); #endif if (allow_knet_handle_fallback && !instance->knet_handle && errno == ENAMETOOLONG) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_WARNING, "knet_handle_new failed, trying unprivileged"); #if defined(KNET_API_VER) && (KNET_API_VER == 2) - instance->knet_handle = knet_handle_new(instance->totem_config->node_id, instance->logpipes[1], KNET_LOG_DEBUG, 0); + instance->knet_handle = knet_handle_new(instance->totem_config->node_id, instance->logpipes[1], KNET_LOG_DEBUG, flags); #else - instance->knet_handle = knet_handle_new_ex(instance->totem_config->node_id, instance->logpipes[1], KNET_LOG_DEBUG, 0); + instance->knet_handle = knet_handle_new_ex(instance->totem_config->node_id, instance->logpipes[1], KNET_LOG_DEBUG, flags); #endif } if (!instance->knet_handle) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_CRIT, "knet_handle_new failed"); goto exit_error; } knet_set_access_list_config(instance); res = knet_handle_pmtud_setfreq(instance->knet_handle, instance->totem_config->knet_pmtud_interval); if (res) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_WARNING, "knet_handle_pmtud_setfreq failed"); } res = knet_handle_pmtud_set(instance->knet_handle, instance->totem_config->knet_mtu); if (res) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_WARNING, "knet_handle_pmtud_set failed"); } res = knet_handle_enable_filter(instance->knet_handle, instance, dst_host_filter_callback_fn); if (res) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_WARNING, "knet_handle_enable_filter failed"); } res = knet_handle_enable_sock_notify(instance->knet_handle, instance, socket_error_callback_fn); if (res) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_WARNING, "knet_handle_enable_sock_notify failed"); } res = knet_host_enable_status_change_notify(instance->knet_handle, instance, host_change_callback_fn); if (res) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_WARNING, "knet_host_enable_status_change_notify failed"); } res = knet_handle_enable_pmtud_notify(instance->knet_handle, instance, pmtu_change_callback_fn); if (res) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_WARNING, "knet_handle_enable_pmtud_notify failed"); } global_instance = instance; /* Setup knet logging level */ totemknet_configure_log_level(); /* Get an fd into knet */ instance->knet_fd = 0; #ifdef KNET_DATAFD_FLAG_RX_RETURN_INFO res = knet_handle_add_datafd(instance->knet_handle, &instance->knet_fd, &channel, KNET_DATAFD_FLAG_RX_RETURN_INFO); #else res = knet_handle_add_datafd(instance->knet_handle, &instance->knet_fd, &channel); #endif if (res) { knet_log_printf(LOG_DEBUG, "knet_handle_add_datafd failed: %s", strerror(errno)); goto exit_error; } /* Enable crypto if requested */ #ifdef HAVE_KNET_CRYPTO_RECONF if (totemknet_is_crypto_enabled(instance)) { res = totemknet_set_knet_crypto(instance); if (res == 0) { res = knet_handle_crypto_use_config(instance->knet_handle, totem_config->crypto_index); if (res) { knet_log_printf(LOG_DEBUG, "knet_handle_crypto_use_config failed: %s", strerror(errno)); goto exit_error; } } else { knet_log_printf(LOG_DEBUG, "Failed to set up knet crypto"); goto exit_error; } res = knet_handle_crypto_rx_clear_traffic(instance->knet_handle, KNET_CRYPTO_RX_DISALLOW_CLEAR_TRAFFIC); if (res) { knet_log_printf(LOG_DEBUG, "knet_handle_crypto_rx_clear_traffic (DISALLOW) failed: %s", strerror(errno)); goto exit_error; } } else { res = knet_handle_crypto_rx_clear_traffic(instance->knet_handle, KNET_CRYPTO_RX_ALLOW_CLEAR_TRAFFIC); if (res) { knet_log_printf(LOG_DEBUG, "knet_handle_crypto_rx_clear_traffic (ALLOW) failed: %s", strerror(errno)); goto exit_error; } } #else if (totemknet_is_crypto_enabled(instance)) { res = totemknet_set_knet_crypto(instance); if (res) { knet_log_printf(LOG_DEBUG, "Failed to set up knet crypto"); goto exit_error; } } #endif /* Set up compression */ if (strcmp(totem_config->knet_compression_model, "none") != 0) { /* Not fatal, but will log */ (void)totemknet_configure_compression(instance, totem_config); } knet_handle_setfwd(instance->knet_handle, 1); instance->link_mode = KNET_LINK_POLICY_PASSIVE; if (strcmp(instance->totem_config->link_mode, "active")==0) { instance->link_mode = KNET_LINK_POLICY_ACTIVE; } if (strcmp(instance->totem_config->link_mode, "rr")==0) { instance->link_mode = KNET_LINK_POLICY_RR; } for (i=0; ilink_status[i] = malloc(CFG_INTERFACE_STATUS_MAX_LEN); if (!instance->link_status[i]) { goto exit_error; } } qb_loop_poll_add (instance->poll_handle, QB_LOOP_MED, instance->logpipes[0], POLLIN, instance, log_deliver_fn); qb_loop_poll_add (instance->poll_handle, QB_LOOP_HIGH, instance->knet_fd, POLLIN, instance, data_deliver_fn); /* * Upper layer isn't ready to receive message because it hasn't * initialized yet. Add short timer to check the interfaces. */ qb_loop_timer_add (instance->poll_handle, QB_LOOP_MED, 100*QB_TIME_NS_IN_MSEC, (void *)instance, timer_function_netif_check_timeout, &instance->timer_netif_check_timeout); totemknet_start_merge_detect_timeout(instance); /* Start listening for config changes */ totemknet_add_config_notifications(instance); /* Add stats keys to icmap */ stats_knet_add_handle(); knet_log_printf (LOGSYS_LEVEL_INFO, "totemknet initialized"); *knet_context = instance; return (0); exit_error: log_flush_messages(instance); free(instance); return (-1); } void *totemknet_buffer_alloc (void) { /* Need to have space for a message AND a struct mcast in case of encapsulated messages */ return malloc(KNET_MAX_PACKET_SIZE + 512); } void totemknet_buffer_release (void *ptr) { return free (ptr); } int totemknet_processor_count_set ( void *knet_context, int processor_count) { return (0); } int totemknet_recv_flush (void *knet_context) { return (0); } int totemknet_send_flush (void *knet_context) { return (0); } int totemknet_token_send ( void *knet_context, const void *msg, unsigned int msg_len) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int res = 0; ucast_sendmsg (instance, &instance->token_target, msg, msg_len); return (res); } int totemknet_mcast_flush_send ( void *knet_context, const void *msg, unsigned int msg_len) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int res = 0; mcast_sendmsg (instance, msg, msg_len, 0); return (res); } int totemknet_mcast_noflush_send ( void *knet_context, const void *msg, unsigned int msg_len) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int res = 0; mcast_sendmsg (instance, msg, msg_len, 1); return (res); } extern int totemknet_iface_check (void *knet_context) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int res = 0; knet_log_printf(LOG_DEBUG, "totemknet: iface_check"); return (res); } extern void totemknet_net_mtu_adjust (void *knet_context, struct totem_config *totem_config) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; knet_log_printf(LOG_DEBUG, "totemknet: Returning MTU of %d", totem_config->net_mtu); } int totemknet_token_target_set ( void *knet_context, unsigned int nodeid) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int res = 0; instance->token_target.nodeid = nodeid; instance->totemknet_target_set_completed (instance->context); return (res); } extern int totemknet_recv_mcast_empty ( void *knet_context) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; unsigned int res; struct sockaddr_storage system_from; struct msghdr msg_hdr; struct iovec iov_recv; struct pollfd ufd; int nfds; int msg_processed = 0; iov_recv.iov_base = instance->iov_buffer; iov_recv.iov_len = KNET_MAX_PACKET_SIZE; msg_hdr.msg_name = &system_from; msg_hdr.msg_namelen = sizeof (struct sockaddr_storage); msg_hdr.msg_iov = &iov_recv; msg_hdr.msg_iovlen = 1; #ifdef HAVE_MSGHDR_CONTROL msg_hdr.msg_control = 0; #endif #ifdef HAVE_MSGHDR_CONTROLLEN msg_hdr.msg_controllen = 0; #endif #ifdef HAVE_MSGHDR_FLAGS msg_hdr.msg_flags = 0; #endif #ifdef HAVE_MSGHDR_ACCRIGHTS msg_msg_hdr.msg_accrights = NULL; #endif #ifdef HAVE_MSGHDR_ACCRIGHTSLEN msg_msg_hdr.msg_accrightslen = 0; #endif do { ufd.fd = instance->knet_fd; ufd.events = POLLIN; nfds = poll (&ufd, 1, 0); if (nfds == 1 && ufd.revents & POLLIN) { res = recvmsg (instance->knet_fd, &msg_hdr, MSG_NOSIGNAL | MSG_DONTWAIT); if (res != -1) { msg_processed = 1; } else { msg_processed = -1; } } } while (nfds == 1); return (msg_processed); } int totemknet_iface_set (void *knet_context, const struct totem_ip_address *local_addr, unsigned short ip_port, unsigned int iface_no) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; totemip_copy(&instance->my_ids[iface_no], local_addr); knet_log_printf(LOG_INFO, "Configured link number %d: local addr: %s, port=%d", iface_no, totemip_print(local_addr), ip_port); instance->ip_port[iface_no] = ip_port; return 0; } int totemknet_member_add ( void *knet_context, const struct totem_ip_address *local, const struct totem_ip_address *member, int link_no) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int err; int port = instance->ip_port[link_no]; struct sockaddr_storage remote_ss; struct sockaddr_storage local_ss; int addrlen; int i; int host_found = 0; knet_node_id_t host_ids[KNET_MAX_HOST]; size_t num_host_ids; /* Only create 1 loopback link and use link 0 */ if (member->nodeid == instance->our_nodeid) { if (!instance->loopback_link) { link_no = 0; instance->loopback_link = 1; } else { /* Already done */ return 0; } } knet_log_printf (LOGSYS_LEVEL_DEBUG, "knet: member_add: " CS_PRI_NODE_ID " (%s), link=%d", member->nodeid, totemip_print(member), link_no); knet_log_printf (LOGSYS_LEVEL_DEBUG, "knet: local: " CS_PRI_NODE_ID " (%s)", local->nodeid, totemip_print(local)); /* Only add the host if it doesn't already exist in knet */ err = knet_host_get_host_list(instance->knet_handle, host_ids, &num_host_ids); if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_host_get_host_list"); return -1; } for (i=0; inodeid) { host_found = 1; } } if (!host_found) { err = knet_host_add(instance->knet_handle, member->nodeid); if (err != 0 && errno != EEXIST) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_host_add"); return -1; } } else { knet_log_printf (LOGSYS_LEVEL_DEBUG, "nodeid " CS_PRI_NODE_ID " already added", member->nodeid); } if (err == 0) { if (knet_host_set_policy(instance->knet_handle, member->nodeid, instance->link_mode)) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_set_policy failed"); return -1; } } memset(&local_ss, 0, sizeof(local_ss)); memset(&remote_ss, 0, sizeof(remote_ss)); /* Casts to remove const */ totemip_totemip_to_sockaddr_convert((struct totem_ip_address *)member, port, &remote_ss, &addrlen); totemip_totemip_to_sockaddr_convert((struct totem_ip_address *)local, port, &local_ss, &addrlen); if (member->nodeid == instance->our_nodeid) { knet_log_printf (LOGSYS_LEVEL_DEBUG, "knet: loopback link is %d\n", link_no); err = knet_link_set_config(instance->knet_handle, member->nodeid, link_no, KNET_TRANSPORT_LOOPBACK, &local_ss, &remote_ss, KNET_LINK_FLAG_TRAFFICHIPRIO); } else { err = knet_link_set_config(instance->knet_handle, member->nodeid, link_no, instance->totem_config->interfaces[link_no].knet_transport, &local_ss, &remote_ss, KNET_LINK_FLAG_TRAFFICHIPRIO); } if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_link_set_config failed"); return -1; } knet_log_printf (LOGSYS_LEVEL_DEBUG, "knet: member_add: Setting link prio to %d", instance->totem_config->interfaces[link_no].knet_link_priority); err = knet_link_set_priority(instance->knet_handle, member->nodeid, link_no, instance->totem_config->interfaces[link_no].knet_link_priority); if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_link_set_priority for nodeid " CS_PRI_NODE_ID ", link %d failed", member->nodeid, link_no); } /* * Ping timeouts may be 0 here for a newly added interface (on a reload), * so we leave this till later, it will get done in totemknet_refresh_config. * For the initial startup, we are all preset and ready to go from here. */ if (instance->totem_config->interfaces[link_no].knet_ping_interval != 0) { err = knet_link_set_ping_timers(instance->knet_handle, member->nodeid, link_no, instance->totem_config->interfaces[link_no].knet_ping_interval, instance->totem_config->interfaces[link_no].knet_ping_timeout, instance->totem_config->interfaces[link_no].knet_ping_precision); if (err) { /* Flush logs before reporting this error so that the knet message prints before ours */ int saved_errno = errno; log_flush_messages(instance); errno = saved_errno; KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_link_set_ping_timers for nodeid " CS_PRI_NODE_ID ", link %d failed", member->nodeid, link_no); icmap_set_string("config.reload_error_message", "Failed to set knet ping timers"); return -1; } err = knet_link_set_pong_count(instance->knet_handle, member->nodeid, link_no, instance->totem_config->interfaces[link_no].knet_pong_count); if (err) { /* Flush logs before reporting this error so that the knet message prints before ours */ int saved_errno = errno; log_flush_messages(instance); errno = saved_errno; KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_link_set_pong_count for nodeid " CS_PRI_NODE_ID ", link %d failed", member->nodeid, link_no); icmap_set_string("config.reload_error_message", "Failed to set knet pong count"); return -1; } } err = knet_link_set_enable(instance->knet_handle, member->nodeid, link_no, 1); if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_link_set_enable for nodeid " CS_PRI_NODE_ID ", link %d failed", member->nodeid, link_no); return -1; } /* register stats */ stats_knet_add_member(member->nodeid, link_no); return (0); } int totemknet_member_remove ( void *knet_context, const struct totem_ip_address *token_target, int link_no) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int res; uint8_t link_list[KNET_MAX_LINK]; size_t num_links; knet_log_printf (LOGSYS_LEVEL_DEBUG, "knet: member_remove: " CS_PRI_NODE_ID ", link=%d", token_target->nodeid, link_no); /* Don't remove the link with the loopback on it until we shut down */ if (token_target->nodeid == instance->our_nodeid) { return 0; } /* Tidy stats */ stats_knet_del_member(token_target->nodeid, link_no); /* Remove the link first */ res = knet_link_set_enable(instance->knet_handle, token_target->nodeid, link_no, 0); if (res != 0) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_link_set enable(off) for nodeid " CS_PRI_NODE_ID ", link %d failed", token_target->nodeid, link_no); return res; } res = knet_link_clear_config(instance->knet_handle, token_target->nodeid, link_no); if (res != 0) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_link_clear_config for nodeid " CS_PRI_NODE_ID ", link %d failed", token_target->nodeid, link_no); return res; } /* If this is the last link, then remove the node */ res = knet_link_get_link_list(instance->knet_handle, token_target->nodeid, link_list, &num_links); if (res) { return (0); /* not really failure */ } if (num_links == 0) { res = knet_host_remove(instance->knet_handle, token_target->nodeid); } return res; } int totemknet_member_list_rebind_ip ( void *knet_context) { return (0); } static int totemknet_configure_compression ( struct totemknet_instance *instance, struct totem_config *totem_config) { struct knet_handle_compress_cfg compress_cfg; int res = 0; assert(strlen(totem_config->knet_compression_model) < sizeof(compress_cfg.compress_model)); strcpy(compress_cfg.compress_model, totem_config->knet_compression_model); compress_cfg.compress_threshold = totem_config->knet_compression_threshold; compress_cfg.compress_level = totem_config->knet_compression_level; res = knet_handle_compress(instance->knet_handle, &compress_cfg); if (res) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_handle_compress failed"); } return res; } int totemknet_reconfigure ( void *knet_context, struct totem_config *totem_config) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int res = 0; (void)totemknet_configure_compression(instance, totem_config); #ifdef HAVE_LIBNOZZLE /* Set up nozzle device(s). Return code is ignored, because inability * configure nozzle is not fatal problem, errors are logged and * there is not much else we can do */ (void)setup_nozzle(instance); #endif if (totem_config->crypto_changed) { /* Flip crypto_index */ totem_config->crypto_index = 3-totem_config->crypto_index; res = totemknet_set_knet_crypto(instance); if (res == 0) { knet_log_printf(LOG_INFO, "kronosnet crypto reconfigured on index %d: %s/%s/%s", totem_config->crypto_index, totem_config->crypto_model, totem_config->crypto_cipher_type, totem_config->crypto_hash_type); } else { icmap_set_string("config.reload_error_message", "Failed to set knet crypto"); } } return (res); } int totemknet_crypto_reconfigure_phase ( void *knet_context, struct totem_config *totem_config, cfg_message_crypto_reconfig_phase_t phase) { #ifdef HAVE_KNET_CRYPTO_RECONF int res; int config_to_use; int config_to_clear; struct knet_handle_crypto_cfg crypto_cfg; struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; knet_log_printf(LOGSYS_LEVEL_DEBUG, "totemknet_crypto_reconfigure_phase %d, index=%d\n", phase, totem_config->crypto_index); switch (phase) { case CRYPTO_RECONFIG_PHASE_ACTIVATE: config_to_use = totem_config->crypto_index; if (!totemknet_is_crypto_enabled(instance)) { config_to_use = 0; /* we are clearing it */ } /* Enable the new config on this node */ res = knet_handle_crypto_use_config(instance->knet_handle, config_to_use); if (res == -1) { knet_log_printf(LOGSYS_LEVEL_ERROR, "knet_handle_crypto_use_config %d failed: %s", config_to_use, strerror(errno)); } break; case CRYPTO_RECONFIG_PHASE_CLEANUP: /* * All nodes should now have the new config. clear the old one out * OR disable crypto entirely if that's what the new config insists on. */ config_to_clear = 3-totem_config->crypto_index; knet_log_printf(LOGSYS_LEVEL_DEBUG, "Clearing old knet crypto config %d\n", config_to_clear); strcpy(crypto_cfg.crypto_model, "none"); strcpy(crypto_cfg.crypto_cipher_type, "none"); strcpy(crypto_cfg.crypto_hash_type, "none"); res = knet_handle_crypto_set_config(instance->knet_handle, &crypto_cfg, config_to_clear); if (res == -1) { knet_log_printf(LOGSYS_LEVEL_ERROR, "knet_handle_crypto_set_config to clear index %d failed: %s", config_to_clear, strerror(errno)); } if (res == -2) { knet_log_printf(LOGSYS_LEVEL_ERROR, "knet_handle_crypto_set_config to clear index %d failed: -2", config_to_clear); } /* If crypto is enabled then disable all cleartext reception */ if (totemknet_is_crypto_enabled(instance)) { res = knet_handle_crypto_rx_clear_traffic(instance->knet_handle, KNET_CRYPTO_RX_DISALLOW_CLEAR_TRAFFIC); if (res) { knet_log_printf(LOGSYS_LEVEL_ERROR, "knet_handle_crypto_rx_clear_traffic(DISALLOW) failed %s", strerror(errno)); } } } #endif return 0; } void totemknet_stats_clear ( void *knet_context) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; (void) knet_handle_clear_stats(instance->knet_handle, KNET_CLEARSTATS_HANDLE_AND_LINK); } /* For the stats module */ int totemknet_link_get_status ( knet_node_id_t node, uint8_t link_no, struct knet_link_status *status) { int res; int ret = CS_OK; /* We are probably not using knet */ if (!global_instance) { return CS_ERR_NOT_EXIST; } if (link_no >= INTERFACE_MAX) { return CS_ERR_NOT_EXIST; /* Invalid link number */ } res = knet_link_get_status(global_instance->knet_handle, node, link_no, status, sizeof(struct knet_link_status)); if (res) { switch (errno) { case EINVAL: ret = CS_ERR_INVALID_PARAM; break; case EBUSY: ret = CS_ERR_BUSY; break; case EDEADLK: ret = CS_ERR_TRY_AGAIN; break; default: ret = CS_ERR_LIBRARY; break; } } return (ret); } int totemknet_handle_get_stats ( struct knet_handle_stats *stats) { int res; /* We are probably not using knet */ if (!global_instance) { return CS_ERR_NOT_EXIST; } res = knet_handle_get_stats(global_instance->knet_handle, stats, sizeof(struct knet_handle_stats)); if (res != 0) { return (qb_to_cs_error(-errno)); } return CS_OK; } static void timer_function_merge_detect_timeout ( void *data) { struct totemknet_instance *instance = (struct totemknet_instance *)data; if (instance->merge_detect_messages_sent_before_timeout == 0) { instance->send_merge_detect_message = 1; } instance->merge_detect_messages_sent_before_timeout = 0; totemknet_start_merge_detect_timeout(instance); } static void totemknet_start_merge_detect_timeout( void *knet_context) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; qb_loop_timer_add(instance->poll_handle, QB_LOOP_MED, instance->totem_config->merge_timeout * 2 * QB_TIME_NS_IN_MSEC, (void *)instance, timer_function_merge_detect_timeout, &instance->timer_merge_detect_timeout); } static void totemknet_stop_merge_detect_timeout( void *knet_context) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; qb_loop_timer_del(instance->poll_handle, instance->timer_merge_detect_timeout); } static void log_flush_messages (void *knet_context) { struct pollfd pfd; struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int cont; cont = 1; while (cont) { pfd.fd = instance->logpipes[0]; pfd.events = POLLIN; pfd.revents = 0; if ((poll(&pfd, 1, 0) > 0) && (pfd.revents & POLLIN) && (log_deliver_fn(instance->logpipes[0], POLLIN, instance) == 0)) { cont = 1; } else { cont = 0; } } } #ifdef HAVE_LIBNOZZLE #define NOZZLE_NAME "nozzle.name" #define NOZZLE_IPADDR "nozzle.ipaddr" #define NOZZLE_PREFIX "nozzle.ipprefix" #define NOZZLE_MACADDR "nozzle.macaddr" #define NOZZLE_CHANNEL 1 static char *get_nozzle_script_dir(void *knet_context) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; char filename[PATH_MAX + FILENAME_MAX + 1]; static char updown_dirname[PATH_MAX + FILENAME_MAX + 1]; int res; const char *dirname_res; /* * Build script directory based on corosync.conf file location */ res = snprintf(filename, sizeof(filename), "%s", corosync_get_config_file()); if (res >= sizeof(filename)) { knet_log_printf (LOGSYS_LEVEL_DEBUG, "nozzle up/down path too long"); return NULL; } dirname_res = dirname(filename); res = snprintf(updown_dirname, sizeof(updown_dirname), "%s/%s", dirname_res, "updown.d"); if (res >= sizeof(updown_dirname)) { knet_log_printf (LOGSYS_LEVEL_DEBUG, "nozzle up/down path too long"); return NULL; } return updown_dirname; } /* * Deliberately doesn't return the status as caller doesn't care. * The result will be logged though */ static void run_nozzle_script(struct totemknet_instance *instance, int type, const char *typename) { int res; char *exec_string; res = nozzle_run_updown(instance->nozzle_handle, type, &exec_string); if (res == -1 && errno != ENOENT) { knet_log_printf (LOGSYS_LEVEL_INFO, "exec nozzle %s script failed: %s", typename, strerror(errno)); } else if (res == -2) { knet_log_printf (LOGSYS_LEVEL_INFO, "nozzle %s script failed", typename); knet_log_printf (LOGSYS_LEVEL_INFO, "%s", exec_string); } } /* * Reparse IP address to add in our node ID * IPv6 addresses must end in '::' * IPv4 addresses must just be valid * '/xx' lengths are optional for IPv6, mandatory for IPv4 * * Returns the modified IP address as a string to pass into libnozzle */ static int reparse_nozzle_ip_address(struct totemknet_instance *instance, const char *input_addr, const char *prefix, int nodeid, char *output_addr, size_t output_len) { char *coloncolon; int bits; int max_prefix = 64; uint32_t nodeid_mask; uint32_t addr_mask; uint32_t masked_nodeid; struct in_addr *addr; struct totem_ip_address totemip; coloncolon = strstr(input_addr, "::"); if (!coloncolon) { max_prefix = 30; } bits = atoi(prefix); if (bits < 8 || bits > max_prefix) { knet_log_printf(LOGSYS_LEVEL_ERROR, "nozzle IP address prefix must be >= 8 and <= %d (got %d)", max_prefix, bits); return -1; } /* IPv6 is easy */ if (coloncolon) { memcpy(output_addr, input_addr, coloncolon-input_addr); sprintf(output_addr + (coloncolon-input_addr), "::%x", nodeid); return 0; } /* For IPv4 we need to parse the address into binary, mask off the required bits, * add in the masked_nodeid and 'print' it out again */ nodeid_mask = UINT32_MAX & ((1<<(32 - bits)) - 1); addr_mask = UINT32_MAX ^ nodeid_mask; masked_nodeid = nodeid & nodeid_mask; if (totemip_parse(&totemip, input_addr, AF_INET)) { knet_log_printf(LOGSYS_LEVEL_ERROR, "Failed to parse IPv4 nozzle IP address"); return -1; } addr = (struct in_addr *)&totemip.addr; addr->s_addr &= htonl(addr_mask); addr->s_addr |= htonl(masked_nodeid); inet_ntop(AF_INET, addr, output_addr, output_len); return 0; } static int create_nozzle_device(void *knet_context, const char *name, const char *ipaddr, const char *prefix, const char *macaddr) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; char device_name[IFNAMSIZ+1]; size_t size = IFNAMSIZ; int8_t channel = NOZZLE_CHANNEL; nozzle_t nozzle_dev; int nozzle_fd; int res; char *updown_dir; char parsed_ipaddr[INET6_ADDRSTRLEN]; char mac[19]; memset(device_name, 0, size); memset(&mac, 0, sizeof(mac)); strncpy(device_name, name, size); updown_dir = get_nozzle_script_dir(knet_context); knet_log_printf (LOGSYS_LEVEL_INFO, "nozzle script dir is %s", updown_dir); nozzle_dev = nozzle_open(device_name, size, updown_dir); if (!nozzle_dev) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Unable to init nozzle device %s: %s", device_name, strerror(errno)); return -1; } instance->nozzle_handle = nozzle_dev; if (nozzle_set_mac(nozzle_dev, macaddr) < 0) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Unable to add set nozzle MAC to %s: %s", mac, strerror(errno)); goto out_clean; } if (reparse_nozzle_ip_address(instance, ipaddr, prefix, instance->our_nodeid, parsed_ipaddr, sizeof(parsed_ipaddr))) { /* Prints its own errors */ goto out_clean; } knet_log_printf (LOGSYS_LEVEL_INFO, "Local nozzle IP address is %s / %d", parsed_ipaddr, atoi(prefix)); if (nozzle_add_ip(nozzle_dev, parsed_ipaddr, prefix) < 0) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Unable to add set nozzle IP addr to %s/%s: %s", parsed_ipaddr, prefix, strerror(errno)); goto out_clean; } nozzle_fd = nozzle_get_fd(nozzle_dev); knet_log_printf (LOGSYS_LEVEL_INFO, "Opened '%s' on fd %d", device_name, nozzle_fd); #ifdef KNET_DATAFD_FLAG_RX_RETURN_INFO res = knet_handle_add_datafd(instance->knet_handle, &nozzle_fd, &channel, 0); #else res = knet_handle_add_datafd(instance->knet_handle, &nozzle_fd, &channel); #endif if (res != 0) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Unable to add nozzle FD to knet: %s", strerror(errno)); goto out_clean; } run_nozzle_script(instance, NOZZLE_PREUP, "pre-up"); res = nozzle_set_up(nozzle_dev); if (res != 0) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Unable to set nozzle interface UP: %s", strerror(errno)); goto out_clean; } run_nozzle_script(instance, NOZZLE_UP, "up"); return 0; out_clean: nozzle_close(nozzle_dev); return -1; } static int remove_nozzle_device(void *knet_context) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int res; int datafd; res = knet_handle_get_datafd(instance->knet_handle, NOZZLE_CHANNEL, &datafd); if (res != 0) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Can't find datafd for channel %d: %s", NOZZLE_CHANNEL, strerror(errno)); return -1; } res = knet_handle_remove_datafd(instance->knet_handle, datafd); if (res != 0) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Can't remove datafd for nozzle channel %d: %s", NOZZLE_CHANNEL, strerror(errno)); return -1; } run_nozzle_script(instance, NOZZLE_DOWN, "pre-down"); res = nozzle_set_down(instance->nozzle_handle); if (res != 0) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Can't set nozzle device down: %s", strerror(errno)); return -1; } run_nozzle_script(instance, NOZZLE_POSTDOWN, "post-down"); res = nozzle_close(instance->nozzle_handle); if (res != 0) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Can't close nozzle device: %s", strerror(errno)); return -1; } knet_log_printf (LOGSYS_LEVEL_INFO, "Removed nozzle device"); return 0; } static void free_nozzle(struct totemknet_instance *instance) { free(instance->nozzle_name); free(instance->nozzle_ipaddr); free(instance->nozzle_prefix); free(instance->nozzle_macaddr); instance->nozzle_name = instance->nozzle_ipaddr = instance->nozzle_prefix = instance->nozzle_macaddr = NULL; } static int setup_nozzle(void *knet_context) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; char *ipaddr_str = NULL; char *name_str = NULL; char *prefix_str = NULL; char *macaddr_str = NULL; char mac[32]; int name_res; int macaddr_res; int res = -1; /* * Return value ignored on purpose. icmap_get_string changes * ipaddr_str/prefix_str only on success. */ (void)icmap_get_string(NOZZLE_IPADDR, &ipaddr_str); (void)icmap_get_string(NOZZLE_PREFIX, &prefix_str); macaddr_res = icmap_get_string(NOZZLE_MACADDR, &macaddr_str); name_res = icmap_get_string(NOZZLE_NAME, &name_str); /* Is is being removed? */ if (name_res == CS_ERR_NOT_EXIST && instance->nozzle_handle) { remove_nozzle_device(instance); free_nozzle(instance); goto out_free; } if (!name_str) { /* no nozzle */ goto out_free; } if (!ipaddr_str) { knet_log_printf (LOGSYS_LEVEL_ERROR, "No IP address supplied for Nozzle device"); goto out_free; } if (!prefix_str) { knet_log_printf (LOGSYS_LEVEL_ERROR, "No prefix supplied for Nozzle IP address"); goto out_free; } if (macaddr_str && strlen(macaddr_str) != 17) { knet_log_printf (LOGSYS_LEVEL_ERROR, "macaddr for nozzle device is not in the correct format '%s'", macaddr_str); goto out_free; } if (!macaddr_str) { macaddr_str = (char*)"54:54:01:00:00:00"; } if (instance->nozzle_name && (strcmp(name_str, instance->nozzle_name) == 0) && (strcmp(ipaddr_str, instance->nozzle_ipaddr) == 0) && (strcmp(prefix_str, instance->nozzle_prefix) == 0) && (instance->nozzle_macaddr == NULL || strcmp(macaddr_str, instance->nozzle_macaddr) == 0)) { /* Nothing has changed */ knet_log_printf (LOGSYS_LEVEL_DEBUG, "Nozzle device info not changed"); goto out_free; } /* Add nodeid into MAC address */ memcpy(mac, macaddr_str, 12); snprintf(mac+12, sizeof(mac) - 13, "%02x:%02x", instance->our_nodeid >> 8, instance->our_nodeid & 0xFF); knet_log_printf (LOGSYS_LEVEL_INFO, "Local nozzle MAC address is %s", mac); if (name_res == CS_OK && name_str) { /* Reconfigure */ if (instance->nozzle_name) { remove_nozzle_device(instance); free_nozzle(instance); } res = create_nozzle_device(knet_context, name_str, ipaddr_str, prefix_str, mac); instance->nozzle_name = strdup(name_str); instance->nozzle_ipaddr = strdup(ipaddr_str); instance->nozzle_prefix = strdup(prefix_str); instance->nozzle_macaddr = strdup(macaddr_str); if (!instance->nozzle_name || !instance->nozzle_ipaddr || !instance->nozzle_prefix) { knet_log_printf (LOGSYS_LEVEL_ERROR, "strdup failed in nozzle allocation"); /* * This 'free' will cause a complete reconfigure of the device next time we reload * but will also let the the current device keep working until then. * remove_nozzle() only needs the, statically-allocated, nozzle_handle */ free_nozzle(instance); } } out_free: free(name_str); free(ipaddr_str); free(prefix_str); if (macaddr_res == CS_OK) { free(macaddr_str); } return res; } #endif // HAVE_LIBNOZZLE