diff --git a/exec/cmap.c b/exec/cmap.c index d222740a..6da48759 100644 --- a/exec/cmap.c +++ b/exec/cmap.c @@ -1,1029 +1,1029 @@ /* * Copyright (c) 2011-2012 Red Hat, Inc. * * All rights reserved. * * Author: 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 Red Hat, 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 "service.h" LOGSYS_DECLARE_SUBSYS ("CMAP"); #define MAX_REQ_EXEC_CMAP_MCAST_ITEMS 32 #define ICMAP_VALUETYPE_NOT_EXIST 0 struct cmap_conn_info { struct hdb_handle_database iter_db; struct hdb_handle_database track_db; }; typedef uint64_t cmap_iter_handle_t; typedef uint64_t cmap_track_handle_t; struct cmap_track_user_data { void *conn; cmap_track_handle_t track_handle; uint64_t track_inst_handle; }; enum cmap_message_req_types { MESSAGE_REQ_EXEC_CMAP_MCAST = 0, }; enum cmap_mcast_reason { CMAP_MCAST_REASON_SYNC = 0, CMAP_MCAST_REASON_NEW_CONFIG_VERSION = 1, }; static struct corosync_api_v1 *api; static char *cmap_exec_init_fn (struct corosync_api_v1 *corosync_api); static int cmap_exec_exit_fn(void); static int cmap_lib_init_fn (void *conn); static int cmap_lib_exit_fn (void *conn); static void message_handler_req_lib_cmap_set(void *conn, const void *message); static void message_handler_req_lib_cmap_delete(void *conn, const void *message); static void message_handler_req_lib_cmap_get(void *conn, const void *message); static void message_handler_req_lib_cmap_adjust_int(void *conn, const void *message); static void message_handler_req_lib_cmap_iter_init(void *conn, const void *message); static void message_handler_req_lib_cmap_iter_next(void *conn, const void *message); static void message_handler_req_lib_cmap_iter_finalize(void *conn, const void *message); static void message_handler_req_lib_cmap_track_add(void *conn, const void *message); static void message_handler_req_lib_cmap_track_delete(void *conn, const void *message); static void cmap_notify_fn(int32_t event, const char *key_name, struct icmap_notify_value new_val, struct icmap_notify_value old_val, void *user_data); static void message_handler_req_exec_cmap_mcast( const void *message, unsigned int nodeid); static void exec_cmap_mcast_endian_convert(void *message); /* * Reson is subtype of message. argc is number of items in argv array. Argv is array * of strings (key names) which will be send to wire. There can be maximum * MAX_REQ_EXEC_CMAP_MCAST_ITEMS items (for more items, CS_ERR_TOO_MANY_GROUPS * error is returned). If key is not found, item has type ICMAP_VALUETYPE_NOT_EXIST * and length zero. */ static cs_error_t cmap_mcast_send(enum cmap_mcast_reason reason, int argc, char *argv[]); static void cmap_sync_init ( const unsigned int *trans_list, size_t trans_list_entries, const unsigned int *member_list, size_t member_list_entries, const struct memb_ring_id *ring_id); static int cmap_sync_process (void); static void cmap_sync_activate (void); static void cmap_sync_abort (void); static void cmap_config_version_track_cb( int32_t event, const char *key_name, struct icmap_notify_value new_value, struct icmap_notify_value old_value, void *user_data); /* * Library Handler Definition */ static struct corosync_lib_handler cmap_lib_engine[] = { { /* 0 */ .lib_handler_fn = message_handler_req_lib_cmap_set, .flow_control = CS_LIB_FLOW_CONTROL_NOT_REQUIRED }, { /* 1 */ .lib_handler_fn = message_handler_req_lib_cmap_delete, .flow_control = CS_LIB_FLOW_CONTROL_NOT_REQUIRED }, { /* 2 */ .lib_handler_fn = message_handler_req_lib_cmap_get, .flow_control = CS_LIB_FLOW_CONTROL_NOT_REQUIRED }, { /* 3 */ .lib_handler_fn = message_handler_req_lib_cmap_adjust_int, .flow_control = CS_LIB_FLOW_CONTROL_NOT_REQUIRED }, { /* 4 */ .lib_handler_fn = message_handler_req_lib_cmap_iter_init, .flow_control = CS_LIB_FLOW_CONTROL_NOT_REQUIRED }, { /* 5 */ .lib_handler_fn = message_handler_req_lib_cmap_iter_next, .flow_control = CS_LIB_FLOW_CONTROL_NOT_REQUIRED }, { /* 6 */ .lib_handler_fn = message_handler_req_lib_cmap_iter_finalize, .flow_control = CS_LIB_FLOW_CONTROL_NOT_REQUIRED }, { /* 7 */ .lib_handler_fn = message_handler_req_lib_cmap_track_add, .flow_control = CS_LIB_FLOW_CONTROL_NOT_REQUIRED }, { /* 8 */ .lib_handler_fn = message_handler_req_lib_cmap_track_delete, .flow_control = CS_LIB_FLOW_CONTROL_NOT_REQUIRED }, }; static struct corosync_exec_handler cmap_exec_engine[] = { { /* 0 - MESSAGE_REQ_EXEC_CMAP_MCAST */ .exec_handler_fn = message_handler_req_exec_cmap_mcast, .exec_endian_convert_fn = exec_cmap_mcast_endian_convert }, }; struct corosync_service_engine cmap_service_engine = { .name = "corosync configuration map access", .id = CMAP_SERVICE, .priority = 1, .private_data_size = sizeof(struct cmap_conn_info), .flow_control = CS_LIB_FLOW_CONTROL_NOT_REQUIRED, .allow_inquorate = CS_LIB_ALLOW_INQUORATE, .lib_init_fn = cmap_lib_init_fn, .lib_exit_fn = cmap_lib_exit_fn, .lib_engine = cmap_lib_engine, .lib_engine_count = sizeof (cmap_lib_engine) / sizeof (struct corosync_lib_handler), .exec_init_fn = cmap_exec_init_fn, .exec_exit_fn = cmap_exec_exit_fn, .exec_engine = cmap_exec_engine, .exec_engine_count = sizeof (cmap_exec_engine) / sizeof (struct corosync_exec_handler), .sync_init = cmap_sync_init, .sync_process = cmap_sync_process, .sync_activate = cmap_sync_activate, .sync_abort = cmap_sync_abort }; struct corosync_service_engine *cmap_get_service_engine_ver0 (void) { return (&cmap_service_engine); } struct req_exec_cmap_mcast_item { mar_name_t key_name __attribute__((aligned(8))); mar_uint8_t value_type __attribute__((aligned(8))); mar_size_t value_len __attribute__((aligned(8))); uint8_t value[] __attribute__((aligned(8))); }; struct req_exec_cmap_mcast { struct qb_ipc_request_header header __attribute__((aligned(8))); mar_uint8_t reason __attribute__((aligned(8))); mar_uint8_t no_items __attribute__((aligned(8))); mar_uint8_t reserved1 __attribute__((aligned(8))); mar_uint8_t reserver2 __attribute__((aligned(8))); /* * Following are array of req_exec_cmap_mcast_item alligned to 8 bytes */ }; static size_t cmap_sync_trans_list_entries = 0; static size_t cmap_sync_member_list_entries = 0; static uint64_t cmap_highest_config_version_received = 0; static uint64_t cmap_my_config_version = 0; static int cmap_first_sync = 1; static icmap_track_t cmap_config_version_track; static void cmap_config_version_track_cb( int32_t event, const char *key_name, struct icmap_notify_value new_value, struct icmap_notify_value old_value, void *user_data) { const char *key = "totem.config_version"; cs_error_t ret; ENTER(); if (icmap_get_uint64("totem.config_version", &cmap_my_config_version) != CS_OK) { cmap_my_config_version = 0; } ret = cmap_mcast_send(CMAP_MCAST_REASON_NEW_CONFIG_VERSION, 1, (char **)&key); if (ret != CS_OK) { log_printf(LOGSYS_LEVEL_ERROR, "Can't inform other nodes about new config version"); } LEAVE(); } static int cmap_exec_exit_fn(void) { if (icmap_track_delete(cmap_config_version_track) != CS_OK) { log_printf(LOGSYS_LEVEL_ERROR, "Can't delete config_version icmap tracker"); } return 0; } static char *cmap_exec_init_fn ( struct corosync_api_v1 *corosync_api) { cs_error_t ret; api = corosync_api; ret = icmap_track_add("totem.config_version", ICMAP_TRACK_ADD | ICMAP_TRACK_DELETE | ICMAP_TRACK_MODIFY, cmap_config_version_track_cb, NULL, &cmap_config_version_track); if (ret != CS_OK) { return ((char *)"Can't add config_version icmap tracker"); } return (NULL); } static int cmap_lib_init_fn (void *conn) { struct cmap_conn_info *conn_info = (struct cmap_conn_info *)api->ipc_private_data_get (conn); log_printf(LOGSYS_LEVEL_DEBUG, "lib_init_fn: conn=%p", conn); api->ipc_refcnt_inc(conn); memset(conn_info, 0, sizeof(*conn_info)); hdb_create(&conn_info->iter_db); hdb_create(&conn_info->track_db); return (0); } static int cmap_lib_exit_fn (void *conn) { struct cmap_conn_info *conn_info = (struct cmap_conn_info *)api->ipc_private_data_get (conn); hdb_handle_t iter_handle = 0; icmap_iter_t *iter; hdb_handle_t track_handle = 0; icmap_track_t *track; log_printf(LOGSYS_LEVEL_DEBUG, "exit_fn for conn=%p", conn); hdb_iterator_reset(&conn_info->iter_db); while (hdb_iterator_next(&conn_info->iter_db, (void*)&iter, &iter_handle) == 0) { icmap_iter_finalize(*iter); (void)hdb_handle_put (&conn_info->iter_db, iter_handle); } hdb_destroy(&conn_info->iter_db); hdb_iterator_reset(&conn_info->track_db); while (hdb_iterator_next(&conn_info->track_db, (void*)&track, &track_handle) == 0) { free(icmap_track_get_user_data(*track)); icmap_track_delete(*track); (void)hdb_handle_put (&conn_info->track_db, track_handle); } hdb_destroy(&conn_info->track_db); api->ipc_refcnt_dec(conn); return (0); } static void cmap_sync_init ( const unsigned int *trans_list, size_t trans_list_entries, const unsigned int *member_list, size_t member_list_entries, const struct memb_ring_id *ring_id) { cmap_sync_trans_list_entries = trans_list_entries; cmap_sync_member_list_entries = member_list_entries; cmap_highest_config_version_received = 0; if (icmap_get_uint64("totem.config_version", &cmap_my_config_version) != CS_OK) { cmap_my_config_version = 0; } } static int cmap_sync_process (void) { const char *key = "totem.config_version"; cs_error_t ret; ret = cmap_mcast_send(CMAP_MCAST_REASON_SYNC, 1, (char **)&key); return (ret == CS_OK ? 0 : -1); } static void cmap_sync_activate (void) { if (cmap_sync_trans_list_entries == 0) { log_printf(LOGSYS_LEVEL_DEBUG, "Single node sync -> no action"); return ; } if (cmap_first_sync == 1) { cmap_first_sync = 0; } else { log_printf(LOGSYS_LEVEL_DEBUG, "Not first sync -> no action"); return ; } if (cmap_my_config_version == 0) { log_printf(LOGSYS_LEVEL_DEBUG, "My config version is 0 -> no action"); return ; } if (cmap_highest_config_version_received == 0) { log_printf(LOGSYS_LEVEL_DEBUG, "Other nodes version is 0 -> no action"); return ; } if (cmap_highest_config_version_received != cmap_my_config_version) { log_printf(LOGSYS_LEVEL_ERROR, - "Received config version (%"PRIu64") is different then my config version (%"PRIu64")! Exiting", + "Received config version (%"PRIu64") is different than my config version (%"PRIu64")! Exiting", cmap_highest_config_version_received, cmap_my_config_version); api->shutdown_request(); return ; } } static void cmap_sync_abort (void) { } static void message_handler_req_lib_cmap_set(void *conn, const void *message) { const struct req_lib_cmap_set *req_lib_cmap_set = message; struct res_lib_cmap_set res_lib_cmap_set; cs_error_t ret; if (icmap_is_key_ro((char *)req_lib_cmap_set->key_name.value)) { ret = CS_ERR_ACCESS; } else { ret = icmap_set((char *)req_lib_cmap_set->key_name.value, &req_lib_cmap_set->value, req_lib_cmap_set->value_len, req_lib_cmap_set->type); } memset(&res_lib_cmap_set, 0, sizeof(res_lib_cmap_set)); res_lib_cmap_set.header.size = sizeof(res_lib_cmap_set); res_lib_cmap_set.header.id = MESSAGE_RES_CMAP_SET; res_lib_cmap_set.header.error = ret; api->ipc_response_send(conn, &res_lib_cmap_set, sizeof(res_lib_cmap_set)); } static void message_handler_req_lib_cmap_delete(void *conn, const void *message) { const struct req_lib_cmap_set *req_lib_cmap_set = message; struct res_lib_cmap_delete res_lib_cmap_delete; cs_error_t ret; if (icmap_is_key_ro((char *)req_lib_cmap_set->key_name.value)) { ret = CS_ERR_ACCESS; } else { ret = icmap_delete((char *)req_lib_cmap_set->key_name.value); } memset(&res_lib_cmap_delete, 0, sizeof(res_lib_cmap_delete)); res_lib_cmap_delete.header.size = sizeof(res_lib_cmap_delete); res_lib_cmap_delete.header.id = MESSAGE_RES_CMAP_DELETE; res_lib_cmap_delete.header.error = ret; api->ipc_response_send(conn, &res_lib_cmap_delete, sizeof(res_lib_cmap_delete)); } static void message_handler_req_lib_cmap_get(void *conn, const void *message) { const struct req_lib_cmap_get *req_lib_cmap_get = message; struct res_lib_cmap_get *res_lib_cmap_get; struct res_lib_cmap_get error_res_lib_cmap_get; cs_error_t ret; size_t value_len; size_t res_lib_cmap_get_size; icmap_value_types_t type; void *value; value_len = req_lib_cmap_get->value_len; res_lib_cmap_get_size = sizeof(*res_lib_cmap_get) + value_len; res_lib_cmap_get = malloc(res_lib_cmap_get_size); if (res_lib_cmap_get == NULL) { ret = CS_ERR_NO_MEMORY; goto error_exit; } memset(res_lib_cmap_get, 0, res_lib_cmap_get_size); if (value_len > 0) { value = res_lib_cmap_get->value; } else { value = NULL; } ret = icmap_get((char *)req_lib_cmap_get->key_name.value, value, &value_len, &type); if (ret != CS_OK) { free(res_lib_cmap_get); goto error_exit; } res_lib_cmap_get->header.size = res_lib_cmap_get_size; res_lib_cmap_get->header.id = MESSAGE_RES_CMAP_GET; res_lib_cmap_get->header.error = ret; res_lib_cmap_get->type = type; res_lib_cmap_get->value_len = value_len; api->ipc_response_send(conn, res_lib_cmap_get, res_lib_cmap_get_size); free(res_lib_cmap_get); return ; error_exit: memset(&error_res_lib_cmap_get, 0, sizeof(error_res_lib_cmap_get)); error_res_lib_cmap_get.header.size = sizeof(error_res_lib_cmap_get); error_res_lib_cmap_get.header.id = MESSAGE_RES_CMAP_GET; error_res_lib_cmap_get.header.error = ret; api->ipc_response_send(conn, &error_res_lib_cmap_get, sizeof(error_res_lib_cmap_get)); } static void message_handler_req_lib_cmap_adjust_int(void *conn, const void *message) { const struct req_lib_cmap_adjust_int *req_lib_cmap_adjust_int = message; struct res_lib_cmap_adjust_int res_lib_cmap_adjust_int; cs_error_t ret; if (icmap_is_key_ro((char *)req_lib_cmap_adjust_int->key_name.value)) { ret = CS_ERR_ACCESS; } else { ret = icmap_adjust_int((char *)req_lib_cmap_adjust_int->key_name.value, req_lib_cmap_adjust_int->step); } memset(&res_lib_cmap_adjust_int, 0, sizeof(res_lib_cmap_adjust_int)); res_lib_cmap_adjust_int.header.size = sizeof(res_lib_cmap_adjust_int); res_lib_cmap_adjust_int.header.id = MESSAGE_RES_CMAP_ADJUST_INT; res_lib_cmap_adjust_int.header.error = ret; api->ipc_response_send(conn, &res_lib_cmap_adjust_int, sizeof(res_lib_cmap_adjust_int)); } static void message_handler_req_lib_cmap_iter_init(void *conn, const void *message) { const struct req_lib_cmap_iter_init *req_lib_cmap_iter_init = message; struct res_lib_cmap_iter_init res_lib_cmap_iter_init; cs_error_t ret; icmap_iter_t iter; icmap_iter_t *hdb_iter; cmap_iter_handle_t handle = 0ULL; const char *prefix; struct cmap_conn_info *conn_info = (struct cmap_conn_info *)api->ipc_private_data_get (conn); if (req_lib_cmap_iter_init->prefix.length > 0) { prefix = (char *)req_lib_cmap_iter_init->prefix.value; } else { prefix = NULL; } iter = icmap_iter_init(prefix); if (iter == NULL) { ret = CS_ERR_NO_SECTIONS; goto reply_send; } ret = hdb_error_to_cs(hdb_handle_create(&conn_info->iter_db, sizeof(iter), &handle)); if (ret != CS_OK) { goto reply_send; } ret = hdb_error_to_cs(hdb_handle_get(&conn_info->iter_db, handle, (void *)&hdb_iter)); if (ret != CS_OK) { goto reply_send; } *hdb_iter = iter; (void)hdb_handle_put (&conn_info->iter_db, handle); reply_send: memset(&res_lib_cmap_iter_init, 0, sizeof(res_lib_cmap_iter_init)); res_lib_cmap_iter_init.header.size = sizeof(res_lib_cmap_iter_init); res_lib_cmap_iter_init.header.id = MESSAGE_RES_CMAP_ITER_INIT; res_lib_cmap_iter_init.header.error = ret; res_lib_cmap_iter_init.iter_handle = handle; api->ipc_response_send(conn, &res_lib_cmap_iter_init, sizeof(res_lib_cmap_iter_init)); } static void message_handler_req_lib_cmap_iter_next(void *conn, const void *message) { const struct req_lib_cmap_iter_next *req_lib_cmap_iter_next = message; struct res_lib_cmap_iter_next res_lib_cmap_iter_next; cs_error_t ret; icmap_iter_t *iter; size_t value_len = 0; icmap_value_types_t type = 0; const char *res = NULL; struct cmap_conn_info *conn_info = (struct cmap_conn_info *)api->ipc_private_data_get (conn); ret = hdb_error_to_cs(hdb_handle_get(&conn_info->iter_db, req_lib_cmap_iter_next->iter_handle, (void *)&iter)); if (ret != CS_OK) { goto reply_send; } res = icmap_iter_next(*iter, &value_len, &type); if (res == NULL) { ret = CS_ERR_NO_SECTIONS; } (void)hdb_handle_put (&conn_info->iter_db, req_lib_cmap_iter_next->iter_handle); reply_send: memset(&res_lib_cmap_iter_next, 0, sizeof(res_lib_cmap_iter_next)); res_lib_cmap_iter_next.header.size = sizeof(res_lib_cmap_iter_next); res_lib_cmap_iter_next.header.id = MESSAGE_RES_CMAP_ITER_NEXT; res_lib_cmap_iter_next.header.error = ret; if (res != NULL) { res_lib_cmap_iter_next.value_len = value_len; res_lib_cmap_iter_next.type = type; memcpy(res_lib_cmap_iter_next.key_name.value, res, strlen(res)); res_lib_cmap_iter_next.key_name.length = strlen(res); } api->ipc_response_send(conn, &res_lib_cmap_iter_next, sizeof(res_lib_cmap_iter_next)); } static void message_handler_req_lib_cmap_iter_finalize(void *conn, const void *message) { const struct req_lib_cmap_iter_finalize *req_lib_cmap_iter_finalize = message; struct res_lib_cmap_iter_finalize res_lib_cmap_iter_finalize; cs_error_t ret; icmap_iter_t *iter; struct cmap_conn_info *conn_info = (struct cmap_conn_info *)api->ipc_private_data_get (conn); ret = hdb_error_to_cs(hdb_handle_get(&conn_info->iter_db, req_lib_cmap_iter_finalize->iter_handle, (void *)&iter)); if (ret != CS_OK) { goto reply_send; } icmap_iter_finalize(*iter); (void)hdb_handle_destroy(&conn_info->iter_db, req_lib_cmap_iter_finalize->iter_handle); (void)hdb_handle_put (&conn_info->iter_db, req_lib_cmap_iter_finalize->iter_handle); reply_send: memset(&res_lib_cmap_iter_finalize, 0, sizeof(res_lib_cmap_iter_finalize)); res_lib_cmap_iter_finalize.header.size = sizeof(res_lib_cmap_iter_finalize); res_lib_cmap_iter_finalize.header.id = MESSAGE_RES_CMAP_ITER_FINALIZE; res_lib_cmap_iter_finalize.header.error = ret; api->ipc_response_send(conn, &res_lib_cmap_iter_finalize, sizeof(res_lib_cmap_iter_finalize)); } static void cmap_notify_fn(int32_t event, const char *key_name, struct icmap_notify_value new_val, struct icmap_notify_value old_val, void *user_data) { struct cmap_track_user_data *cmap_track_user_data = (struct cmap_track_user_data *)user_data; struct res_lib_cmap_notify_callback res_lib_cmap_notify_callback; struct iovec iov[3]; memset(&res_lib_cmap_notify_callback, 0, sizeof(res_lib_cmap_notify_callback)); res_lib_cmap_notify_callback.header.size = sizeof(res_lib_cmap_notify_callback) + new_val.len + old_val.len; res_lib_cmap_notify_callback.header.id = MESSAGE_RES_CMAP_NOTIFY_CALLBACK; res_lib_cmap_notify_callback.header.error = CS_OK; res_lib_cmap_notify_callback.new_value_type = new_val.type; res_lib_cmap_notify_callback.old_value_type = old_val.type; res_lib_cmap_notify_callback.new_value_len = new_val.len; res_lib_cmap_notify_callback.old_value_len = old_val.len; res_lib_cmap_notify_callback.event = event; res_lib_cmap_notify_callback.key_name.length = strlen(key_name); res_lib_cmap_notify_callback.track_inst_handle = cmap_track_user_data->track_inst_handle; memcpy(res_lib_cmap_notify_callback.key_name.value, key_name, strlen(key_name)); iov[0].iov_base = (char *)&res_lib_cmap_notify_callback; iov[0].iov_len = sizeof(res_lib_cmap_notify_callback); iov[1].iov_base = (char *)new_val.data; iov[1].iov_len = new_val.len; iov[2].iov_base = (char *)old_val.data; iov[2].iov_len = old_val.len; api->ipc_dispatch_iov_send(cmap_track_user_data->conn, iov, 3); } static void message_handler_req_lib_cmap_track_add(void *conn, const void *message) { const struct req_lib_cmap_track_add *req_lib_cmap_track_add = message; struct res_lib_cmap_track_add res_lib_cmap_track_add; cs_error_t ret; cmap_track_handle_t handle = 0; icmap_track_t track = NULL; icmap_track_t *hdb_track; struct cmap_track_user_data *cmap_track_user_data; const char *key_name; struct cmap_conn_info *conn_info = (struct cmap_conn_info *)api->ipc_private_data_get (conn); cmap_track_user_data = malloc(sizeof(*cmap_track_user_data)); if (cmap_track_user_data == NULL) { ret = CS_ERR_NO_MEMORY; goto reply_send; } memset(cmap_track_user_data, 0, sizeof(*cmap_track_user_data)); if (req_lib_cmap_track_add->key_name.length > 0) { key_name = (char *)req_lib_cmap_track_add->key_name.value; } else { key_name = NULL; } ret = icmap_track_add(key_name, req_lib_cmap_track_add->track_type, cmap_notify_fn, cmap_track_user_data, &track); if (ret != CS_OK) { free(cmap_track_user_data); goto reply_send; } ret = hdb_error_to_cs(hdb_handle_create(&conn_info->track_db, sizeof(track), &handle)); if (ret != CS_OK) { free(cmap_track_user_data); goto reply_send; } ret = hdb_error_to_cs(hdb_handle_get(&conn_info->track_db, handle, (void *)&hdb_track)); if (ret != CS_OK) { free(cmap_track_user_data); goto reply_send; } *hdb_track = track; cmap_track_user_data->conn = conn; cmap_track_user_data->track_handle = handle; cmap_track_user_data->track_inst_handle = req_lib_cmap_track_add->track_inst_handle; (void)hdb_handle_put (&conn_info->track_db, handle); reply_send: memset(&res_lib_cmap_track_add, 0, sizeof(res_lib_cmap_track_add)); res_lib_cmap_track_add.header.size = sizeof(res_lib_cmap_track_add); res_lib_cmap_track_add.header.id = MESSAGE_RES_CMAP_TRACK_ADD; res_lib_cmap_track_add.header.error = ret; res_lib_cmap_track_add.track_handle = handle; api->ipc_response_send(conn, &res_lib_cmap_track_add, sizeof(res_lib_cmap_track_add)); } static void message_handler_req_lib_cmap_track_delete(void *conn, const void *message) { const struct req_lib_cmap_track_delete *req_lib_cmap_track_delete = message; struct res_lib_cmap_track_delete res_lib_cmap_track_delete; cs_error_t ret; icmap_track_t *track; struct cmap_conn_info *conn_info = (struct cmap_conn_info *)api->ipc_private_data_get (conn); uint64_t track_inst_handle = 0; ret = hdb_error_to_cs(hdb_handle_get(&conn_info->track_db, req_lib_cmap_track_delete->track_handle, (void *)&track)); if (ret != CS_OK) { goto reply_send; } track_inst_handle = ((struct cmap_track_user_data *)icmap_track_get_user_data(*track))->track_inst_handle; free(icmap_track_get_user_data(*track)); ret = icmap_track_delete(*track); (void)hdb_handle_put (&conn_info->track_db, req_lib_cmap_track_delete->track_handle); (void)hdb_handle_destroy(&conn_info->track_db, req_lib_cmap_track_delete->track_handle); reply_send: memset(&res_lib_cmap_track_delete, 0, sizeof(res_lib_cmap_track_delete)); res_lib_cmap_track_delete.header.size = sizeof(res_lib_cmap_track_delete); res_lib_cmap_track_delete.header.id = MESSAGE_RES_CMAP_TRACK_DELETE; res_lib_cmap_track_delete.header.error = ret; res_lib_cmap_track_delete.track_inst_handle = track_inst_handle; api->ipc_response_send(conn, &res_lib_cmap_track_delete, sizeof(res_lib_cmap_track_delete)); } static cs_error_t cmap_mcast_send(enum cmap_mcast_reason reason, int argc, char *argv[]) { int i; size_t value_len; icmap_value_types_t value_type; cs_error_t err; size_t item_len; size_t msg_len = 0; struct req_exec_cmap_mcast req_exec_cmap_mcast; struct req_exec_cmap_mcast_item *item = NULL; struct iovec req_exec_cmap_iovec[MAX_REQ_EXEC_CMAP_MCAST_ITEMS + 1]; ENTER(); if (argc > MAX_REQ_EXEC_CMAP_MCAST_ITEMS) { return (CS_ERR_TOO_MANY_GROUPS); } memset(req_exec_cmap_iovec, 0, sizeof(req_exec_cmap_iovec)); for (i = 0; i < argc; i++) { err = icmap_get(argv[i], NULL, &value_len, &value_type); if (err != CS_OK && err != CS_ERR_NOT_EXIST) { goto free_mem; } if (err == CS_ERR_NOT_EXIST) { value_type = ICMAP_VALUETYPE_NOT_EXIST; value_len = 0; } item_len = MAR_ALIGN_UP(sizeof(*item) + value_len, 8); item = malloc(item_len); if (item == NULL) { goto free_mem; } memset(item, 0, sizeof(item_len)); item->value_type = value_type; item->value_len = value_len; item->key_name.length = strlen(argv[i]); strcpy((char *)item->key_name.value, argv[i]); if (value_type != ICMAP_VALUETYPE_NOT_EXIST) { err = icmap_get(argv[i], item->value, &value_len, &value_type); if (err != CS_OK) { goto free_mem; } } req_exec_cmap_iovec[i + 1].iov_base = item; req_exec_cmap_iovec[i + 1].iov_len = item_len; msg_len += item_len; qb_log(LOG_TRACE, "Item %u - type %u, len %zu", i, item->value_type, item->value_len); item = NULL; } memset(&req_exec_cmap_mcast, 0, sizeof(req_exec_cmap_mcast)); req_exec_cmap_mcast.header.size = sizeof(req_exec_cmap_mcast) + msg_len; req_exec_cmap_mcast.reason = reason; req_exec_cmap_mcast.no_items = argc; req_exec_cmap_iovec[0].iov_base = &req_exec_cmap_mcast; req_exec_cmap_iovec[0].iov_len = sizeof(req_exec_cmap_mcast); qb_log(LOG_TRACE, "Sending %u items (%u iovec) for reason %u", argc, argc + 1, reason); err = (api->totem_mcast(req_exec_cmap_iovec, argc + 1, TOTEM_AGREED) == 0 ? CS_OK : CS_ERR_MESSAGE_ERROR); free_mem: for (i = 0; i < argc; i++) { free(req_exec_cmap_iovec[i + 1].iov_base); } free(item); LEAVE(); return (err); } static struct req_exec_cmap_mcast_item *cmap_mcast_item_find( const void *message, char *key) { const struct req_exec_cmap_mcast *req_exec_cmap_mcast = message; int i; const char *p; struct req_exec_cmap_mcast_item *item; mar_uint16_t key_name_len; p = (const char *)message + sizeof(*req_exec_cmap_mcast); for (i = 0; i < req_exec_cmap_mcast->no_items; i++) { item = (struct req_exec_cmap_mcast_item *)p; key_name_len = item->key_name.length; if (strlen(key) == key_name_len && strcmp((char *)item->key_name.value, key) == 0) { return (item); } p += MAR_ALIGN_UP(sizeof(*item) + item->value_len, 8); } return (NULL); } static void message_handler_req_exec_cmap_mcast_reason_sync_nv( enum cmap_mcast_reason reason, const void *message, unsigned int nodeid) { char member_config_version[ICMAP_KEYNAME_MAXLEN]; uint64_t config_version = 0; struct req_exec_cmap_mcast_item *item; mar_size_t value_len; ENTER(); item = cmap_mcast_item_find(message, (char *)"totem.config_version"); if (item != NULL) { value_len = item->value_len; if (item->value_type == ICMAP_VALUETYPE_NOT_EXIST) { config_version = 0; } if (item->value_type == ICMAP_VALUETYPE_UINT64) { memcpy(&config_version, item->value, value_len); } } qb_log(LOG_TRACE, "Received config version %"PRIu64" from node %x", config_version, nodeid); if (nodeid != api->totem_nodeid_get() && config_version > cmap_highest_config_version_received) { cmap_highest_config_version_received = config_version; } snprintf(member_config_version, ICMAP_KEYNAME_MAXLEN, "runtime.totem.pg.mrp.srp.members.%u.config_version", nodeid); icmap_set_uint64(member_config_version, config_version); LEAVE(); } static void message_handler_req_exec_cmap_mcast( const void *message, unsigned int nodeid) { const struct req_exec_cmap_mcast *req_exec_cmap_mcast = message; ENTER(); switch (req_exec_cmap_mcast->reason) { case CMAP_MCAST_REASON_SYNC: message_handler_req_exec_cmap_mcast_reason_sync_nv(req_exec_cmap_mcast->reason, message, nodeid); break; case CMAP_MCAST_REASON_NEW_CONFIG_VERSION: message_handler_req_exec_cmap_mcast_reason_sync_nv(req_exec_cmap_mcast->reason, message, nodeid); break; default: qb_log(LOG_TRACE, "Received mcast with unknown reason %u", req_exec_cmap_mcast->reason); }; LEAVE(); } static void exec_cmap_mcast_endian_convert(void *message) { struct req_exec_cmap_mcast *req_exec_cmap_mcast = message; const char *p; int i; struct req_exec_cmap_mcast_item *item; uint16_t u16; uint32_t u32; uint64_t u64; float flt; double dbl; swab_coroipc_request_header_t(&req_exec_cmap_mcast->header); p = (const char *)message + sizeof(*req_exec_cmap_mcast); for (i = 0; i < req_exec_cmap_mcast->no_items; i++) { item = (struct req_exec_cmap_mcast_item *)p; swab_mar_uint16_t(&item->key_name.length); swab_mar_size_t(&item->value_len); switch (item->value_type) { case ICMAP_VALUETYPE_INT16: case ICMAP_VALUETYPE_UINT16: memcpy(&u16, item->value, sizeof(u16)); u16 = swab16(u16); memcpy(item->value, &u16, sizeof(u16)); break; case ICMAP_VALUETYPE_INT32: case ICMAP_VALUETYPE_UINT32: memcpy(&u32, item->value, sizeof(u32)); u32 = swab32(u32); memcpy(item->value, &u32, sizeof(u32)); break; case ICMAP_VALUETYPE_INT64: case ICMAP_VALUETYPE_UINT64: memcpy(&u64, item->value, sizeof(u64)); u64 = swab64(u64); memcpy(item->value, &u64, sizeof(u64)); break; case ICMAP_VALUETYPE_FLOAT: memcpy(&flt, item->value, sizeof(flt)); swabflt(&flt); memcpy(item->value, &flt, sizeof(flt)); break; case ICMAP_VALUETYPE_DOUBLE: memcpy(&dbl, item->value, sizeof(dbl)); swabdbl(&dbl); memcpy(item->value, &dbl, sizeof(dbl)); break; } p += MAR_ALIGN_UP(sizeof(*item) + item->value_len, 8); } } diff --git a/exec/coroparse.c b/exec/coroparse.c index bf465396..422b5864 100644 --- a/exec/coroparse.c +++ b/exec/coroparse.c @@ -1,1287 +1,1287 @@ /* * Copyright (c) 2006-2013 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 #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, }; typedef int (*parser_cb_f)(const char *path, char *key, char *value, enum parser_cb_type type, const char **error_string, icmap_map_t config_map, void *user_data); 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_QB }; struct key_value_list_item { char *key; char *value; struct list_head list; }; struct main_cp_cb_data { enum main_cp_cb_data_state state; int ringnumber; char *bindnetaddr; char *mcastaddr; char *broadcast; int mcastport; int ttl; struct list_head logger_subsys_items_head; char *subsys; char *logging_daemon_name; struct list_head member_items_head; int node_number; int ring0_addr_added; }; 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 (*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 (*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') 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') start++; end = start+(strlen(start))-1; while ((*end == ' ' || *end == '\t' || (remove_colon_and_brace && (*end == ':' || *end == '{'))) && end > start) end--; if (end != start) *(end+1) = '\0'; return start; } static int parse_section(FILE *fp, char *path, const char **error_string, int depth, 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]; if (strcmp(path, "") == 0) { parser_cb("", NULL, NULL, PARSER_CB_START, error_string, config_map, user_data); } while (fgets (line, sizeof (line), fp)) { if (strlen(line) > 0) { 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] == ' ') { line[i] = '\0'; } else { break; } } ignore_line = 1; for (i = 0; i < strlen (line); i++) { if (line[i] != '\t' && line[i] != ' ') { 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 = remove_whitespace(line, 1); loc--; *loc = '\0'; if (strlen(path) + strlen(section) + 1 >= ICMAP_KEYNAME_MAXLEN) { *error_string = "parser error: Start of section makes total cmap path too long"; return -1; } strcpy(new_keyname, path); if (strcmp(path, "") != 0) { strcat(new_keyname, "."); } strcat(new_keyname, section); if (!parser_cb(new_keyname, NULL, NULL, PARSER_CB_SECTION_START, error_string, config_map, user_data)) { return -1; } if (parse_section(fp, new_keyname, error_string, depth + 1, 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) { *error_string = "parser error: New key makes total cmap path too long"; return -1; } strcpy(new_keyname, path); if (strcmp(path, "") != 0) { strcat(new_keyname, "."); } strcat(new_keyname, key); if (!parser_cb(new_keyname, key, value, PARSER_CB_ITEM, error_string, config_map, user_data)) { return -1; } continue ; } if (strchr_rs (line, '}')) { if (depth == 0) { *error_string = "parser error: Unexpected closing brace"; return -1; } if (!parser_cb(path, NULL, NULL, PARSER_CB_SECTION_END, error_string, config_map, user_data)) { return -1; } return 0; } } if (strcmp(path, "") != 0) { *error_string = "parser error: Missing closing brace"; return -1; } if (strcmp(path, "") == 0) { parser_cb("", NULL, NULL, PARSER_CB_END, error_string, config_map, user_data); } return 0; } 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 main_config_parser_cb(const char *path, char *key, char *value, 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]; 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 list_head *iter, *iter_next; int uid, gid; switch (type) { case PARSER_CB_START: memset(data, 0, sizeof(struct main_cp_cb_data)); data->state = MAIN_CP_CB_DATA_STATE_NORMAL; break; case PARSER_CB_END: break; case PARSER_CB_ITEM: add_as_string = 1; switch (data->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; } icmap_set_uint32_r(config_map, path, val); 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; } icmap_set_uint32_r(config_map, path, val); 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; } icmap_set_uint8_r(config_map, path, val); add_as_string = 0; } break; case MAIN_CP_CB_DATA_STATE_QDEVICE: if ((strcmp(path, "quorum.device.timeout") == 0) || (strcmp(path, "quorum.device.votes") == 0)) { val_type = ICMAP_VALUETYPE_UINT32; if (safe_atoq(value, &val, val_type) != 0) { goto atoi_error; } icmap_set_uint32_r(config_map, path, val); 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; } icmap_set_uint8_r(config_map, path, val); 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.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.netmtu") == 0)) { val_type = ICMAP_VALUETYPE_UINT32; if (safe_atoq(value, &val, val_type) != 0) { goto atoi_error; } icmap_set_uint32_r(config_map,path, val); add_as_string = 0; } if (strcmp(path, "totem.config_version") == 0) { if (str_to_ull(value, &ull) != 0) { goto atoi_error; } icmap_set_uint64_r(config_map, path, ull); add_as_string = 0; } if (strcmp(path, "totem.ip_version") == 0) { if ((strcmp(value, "ipv4") != 0) && (strcmp(value, "ipv6") != 0)) { *error_string = "Invalid ip_version type"; return (0); } } if (strcmp(path, "totem.crypto_type") == 0) { if ((strcmp(value, "nss") != 0) && (strcmp(value, "aes256") != 0) && (strcmp(value, "aes192") != 0) && (strcmp(value, "aes128") != 0) && (strcmp(value, "3des") != 0)) { *error_string = "Invalid crypto type"; 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) && (strcmp(value, "3des") != 0)) { *error_string = "Invalid cipher type"; 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"; return (0); } } break; case MAIN_CP_CB_DATA_STATE_QB: if (strcmp(path, "qb.ipc_type") == 0) { if ((strcmp(value, "native") != 0) && (strcmp(value, "shm") != 0) && (strcmp(value, "socket") != 0)) { *error_string = "Invalid qb ipc_type"; return (0); } } break; case MAIN_CP_CB_DATA_STATE_INTERFACE: if (strcmp(path, "totem.interface.ringnumber") == 0) { val_type = ICMAP_VALUETYPE_UINT8; if (safe_atoq(value, &val, val_type) != 0) { goto atoi_error; } data->ringnumber = 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; } 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); *error_string = "Can't alloc memory"; return (0); } list_init(&kv_item->list); 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); *error_string = "Can't alloc memory"; return (0); } list_init(&kv_item->list); 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.uid.%u", uid); icmap_set_uint8_r(config_map, key_name, 1); 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.gid.%u", gid); icmap_set_uint8_r(config_map, key_name, 1); 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); *error_string = "Can't alloc memory"; return (0); } list_init(&kv_item->list); 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; } icmap_set_uint32_r(config_map, key_name, val); add_as_string = 0; } if (strcmp(key, "ring0_addr") == 0) { data->ring0_addr_added = 1; } if (add_as_string) { icmap_set_string_r(config_map, key_name, value); add_as_string = 0; } break; } if (add_as_string) { icmap_set_string_r(config_map, path, value); } break; case PARSER_CB_SECTION_START: if (strcmp(path, "totem.interface") == 0) { data->state = MAIN_CP_CB_DATA_STATE_INTERFACE; data->ringnumber = 0; data->mcastport = -1; data->ttl = -1; list_init(&data->member_items_head); }; if (strcmp(path, "totem") == 0) { data->state = MAIN_CP_CB_DATA_STATE_TOTEM; }; if (strcmp(path, "qb") == 0) { data->state = MAIN_CP_CB_DATA_STATE_QB; } if (strcmp(path, "logging.logger_subsys") == 0) { data->state = MAIN_CP_CB_DATA_STATE_LOGGER_SUBSYS; list_init(&data->logger_subsys_items_head); data->subsys = NULL; } if (strcmp(path, "logging.logging_daemon") == 0) { data->state = MAIN_CP_CB_DATA_STATE_LOGGING_DAEMON; list_init(&data->logger_subsys_items_head); data->subsys = NULL; data->logging_daemon_name = NULL; } if (strcmp(path, "uidgid") == 0) { data->state = MAIN_CP_CB_DATA_STATE_UIDGID; } if (strcmp(path, "totem.interface.member") == 0) { data->state = MAIN_CP_CB_DATA_STATE_MEMBER; } if (strcmp(path, "quorum") == 0) { data->state = MAIN_CP_CB_DATA_STATE_QUORUM; } if (strcmp(path, "quorum.device") == 0) { data->state = MAIN_CP_CB_DATA_STATE_QDEVICE; } if (strcmp(path, "nodelist") == 0) { data->state = MAIN_CP_CB_DATA_STATE_NODELIST; data->node_number = 0; } if (strcmp(path, "nodelist.node") == 0) { data->state = MAIN_CP_CB_DATA_STATE_NODELIST_NODE; data->ring0_addr_added = 0; } break; case PARSER_CB_SECTION_END: switch (data->state) { case MAIN_CP_CB_DATA_STATE_NORMAL: break; case MAIN_CP_CB_DATA_STATE_PLOAD: data->state = MAIN_CP_CB_DATA_STATE_NORMAL; break; 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->ringnumber); icmap_set_string_r(config_map, key_name, data->bindnetaddr); free(data->bindnetaddr); data->bindnetaddr = NULL; } if (data->mcastaddr != NULL) { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.mcastaddr", data->ringnumber); icmap_set_string_r(config_map, key_name, data->mcastaddr); free(data->mcastaddr); data->mcastaddr = NULL; } if (data->broadcast != NULL) { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.broadcast", data->ringnumber); icmap_set_string_r(config_map, key_name, data->broadcast); free(data->broadcast); data->broadcast = NULL; } if (data->mcastport > -1) { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.mcastport", data->ringnumber); icmap_set_uint16_r(config_map, key_name, data->mcastport); } if (data->ttl > -1) { snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.ttl", data->ringnumber); icmap_set_uint8_r(config_map, key_name, data->ttl); } ii = 0; for (iter = data->member_items_head.next; iter != &data->member_items_head; iter = iter_next) { kv_item = list_entry(iter, struct key_value_list_item, list); snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.member.%u", data->ringnumber, ii); icmap_set_string_r(config_map, key_name, kv_item->value); iter_next = iter->next; free(kv_item->value); free(kv_item->key); free(kv_item); ii++; } data->state = MAIN_CP_CB_DATA_STATE_TOTEM; break; case MAIN_CP_CB_DATA_STATE_TOTEM: data->state = MAIN_CP_CB_DATA_STATE_NORMAL; break; case MAIN_CP_CB_DATA_STATE_QB: data->state = MAIN_CP_CB_DATA_STATE_NORMAL; break; case MAIN_CP_CB_DATA_STATE_LOGGER_SUBSYS: if (data->subsys == NULL) { *error_string = "No subsys key in logger_subsys directive"; return (0); } for (iter = data->logger_subsys_items_head.next; iter != &data->logger_subsys_items_head; iter = iter_next) { kv_item = 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); icmap_set_string_r(config_map, key_name, kv_item->value); iter_next = iter->next; free(kv_item->value); free(kv_item->key); free(kv_item); } snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "logging.logger_subsys.%s.subsys", data->subsys); icmap_set_string_r(config_map, key_name, data->subsys); free(data->subsys); data->state = MAIN_CP_CB_DATA_STATE_NORMAL; 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); } for (iter = data->logger_subsys_items_head.next; iter != &data->logger_subsys_items_head; iter = iter_next) { kv_item = 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); } } icmap_set_string_r(config_map, key_name, kv_item->value); iter_next = iter->next; free(kv_item->value); free(kv_item->key); free(kv_item); } 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); 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); 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); icmap_set_string_r(config_map, key_name, data->subsys); snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "logging.logging_daemon.%s.%s.name", data->logging_daemon_name, data->subsys); icmap_set_string_r(config_map, key_name, data->logging_daemon_name); } } free(data->subsys); free(data->logging_daemon_name); data->state = MAIN_CP_CB_DATA_STATE_NORMAL; break; case MAIN_CP_CB_DATA_STATE_UIDGID: data->state = MAIN_CP_CB_DATA_STATE_UIDGID; break; case MAIN_CP_CB_DATA_STATE_MEMBER: data->state = MAIN_CP_CB_DATA_STATE_INTERFACE; break; case MAIN_CP_CB_DATA_STATE_QUORUM: data->state = MAIN_CP_CB_DATA_STATE_NORMAL; break; case MAIN_CP_CB_DATA_STATE_QDEVICE: data->state = MAIN_CP_CB_DATA_STATE_QUORUM; break; case MAIN_CP_CB_DATA_STATE_NODELIST: data->state = MAIN_CP_CB_DATA_STATE_NORMAL; break; case MAIN_CP_CB_DATA_STATE_NODELIST_NODE: if (!data->ring0_addr_added) { *error_string = "No ring0_addr specified for node"; return (0); } data->node_number++; data->state = MAIN_CP_CB_DATA_STATE_NODELIST; 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); snprintf(formated_err, sizeof(formated_err), "Value of key \"%s\" is expected to be integer in range (%lld..%lld), but \"%s\" was given", key, min_val, max_val, value); *error_string = formated_err; return (0); } static int uidgid_config_parser_cb(const char *path, char *key, char *value, 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; 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.uid.%u", uid); icmap_set_uint8_r(config_map, key_name, 1); } 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.gid.%u", gid); icmap_set_uint8_r(config_map, key_name, 1); } 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 then uidgid"; + *error_string = "uidgid: Can't add subsection different than uidgid"; return (0); }; break; case PARSER_CB_SECTION_END: break; } return (1); } static int read_uidgid_files_into_icmap( const char **error_string, icmap_map_t config_map) { FILE *fp; const char *dirname; DIR *dp; struct dirent *dirent; struct dirent *entry; char filename[PATH_MAX + FILENAME_MAX + 1]; int res = 0; size_t len; int return_code; struct stat stat_buf; char key_name[ICMAP_KEYNAME_MAXLEN]; dirname = COROSYSCONFDIR "/uidgid.d"; dp = opendir (dirname); if (dp == NULL) return 0; len = offsetof(struct dirent, d_name) + FILENAME_MAX + 1; entry = malloc(len); if (entry == NULL) { res = 0; goto error_exit; } for (return_code = readdir_r(dp, entry, &dirent); dirent != NULL && return_code == 0; return_code = readdir_r(dp, entry, &dirent)) { snprintf(filename, sizeof (filename), "%s/%s", dirname, dirent->d_name); 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; res = parse_section(fp, key_name, error_string, 0, uidgid_config_parser_cb, config_map, NULL); fclose (fp); if (res != 0) { goto error_exit; } } } error_exit: free (entry); 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; filename = getenv ("COROSYNC_MAIN_CONFIG_FILE"); if (!filename) filename = COROSYSCONFDIR "/corosync.conf"; 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 reason = (%s)", filename, error_ptr); *error_string = error_reason; return -1; } key_name[0] = 0; res = parse_section(fp, key_name, error_string, 0, 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/totemconfig.c b/exec/totemconfig.c index 1d2e4c19..58bb5efa 100644 --- a/exec/totemconfig.c +++ b/exec/totemconfig.c @@ -1,1353 +1,1353 @@ /* * Copyright (c) 2002-2005 MontaVista Software, Inc. * Copyright (c) 2006-2013 Red Hat, Inc. * * All rights reserved. * * Author: Steven Dake (sdake@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 "util.h" #include "totemconfig.h" #define TOKEN_RETRANSMITS_BEFORE_LOSS_CONST 4 #define TOKEN_TIMEOUT 1000 #define TOKEN_COEFFICIENT 650 #define JOIN_TIMEOUT 50 #define MERGE_TIMEOUT 200 #define DOWNCHECK_TIMEOUT 1000 #define FAIL_TO_RECV_CONST 2500 #define SEQNO_UNCHANGED_CONST 30 #define MINIMUM_TIMEOUT (int)(1000/HZ)*3 #define MAX_NETWORK_DELAY 50 #define WINDOW_SIZE 50 #define MAX_MESSAGES 17 #define MISS_COUNT_CONST 5 #define RRP_PROBLEM_COUNT_TIMEOUT 2000 #define RRP_PROBLEM_COUNT_THRESHOLD_DEFAULT 10 #define RRP_PROBLEM_COUNT_THRESHOLD_MIN 2 #define RRP_AUTORECOVERY_CHECK_TIMEOUT 1000 #define DEFAULT_PORT 5405 static char error_string_response[512]; static void add_totem_config_notification(struct totem_config *totem_config); /* All the volatile parameters are uint32s, luckily */ static uint32_t *totem_get_param_by_name(struct totem_config *totem_config, const char *param_name) { if (strcmp(param_name, "totem.token") == 0) return &totem_config->token_timeout; if (strcmp(param_name, "totem.token_retransmit") == 0) return &totem_config->token_retransmit_timeout; if (strcmp(param_name, "totem.hold") == 0) return &totem_config->token_hold_timeout; if (strcmp(param_name, "totem.token_retransmits_before_loss_const") == 0) return &totem_config->token_retransmits_before_loss_const; if (strcmp(param_name, "totem.join") == 0) return &totem_config->join_timeout; if (strcmp(param_name, "totem.send_join") == 0) return &totem_config->send_join_timeout; if (strcmp(param_name, "totem.consensus") == 0) return &totem_config->consensus_timeout; if (strcmp(param_name, "totem.merge") == 0) return &totem_config->merge_timeout; if (strcmp(param_name, "totem.downcheck") == 0) return &totem_config->downcheck_timeout; if (strcmp(param_name, "totem.fail_recv_const") == 0) return &totem_config->fail_to_recv_const; if (strcmp(param_name, "totem.seqno_unchanged_const") == 0) return &totem_config->seqno_unchanged_const; if (strcmp(param_name, "totem.rrp_token_expired_timeout") == 0) return &totem_config->rrp_token_expired_timeout; if (strcmp(param_name, "totem.rrp_problem_count_timeout") == 0) return &totem_config->rrp_problem_count_timeout; if (strcmp(param_name, "totem.rrp_problem_count_threshold") == 0) return &totem_config->rrp_problem_count_threshold; if (strcmp(param_name, "totem.rrp_problem_count_mcast_threshold") == 0) return &totem_config->rrp_problem_count_mcast_threshold; if (strcmp(param_name, "totem.rrp_autorecovery_check_timeout") == 0) return &totem_config->rrp_autorecovery_check_timeout; if (strcmp(param_name, "totem.heartbeat_failures_allowed") == 0) return &totem_config->heartbeat_failures_allowed; if (strcmp(param_name, "totem.max_network_delay") == 0) return &totem_config->max_network_delay; if (strcmp(param_name, "totem.window_size") == 0) return &totem_config->window_size; if (strcmp(param_name, "totem.max_messages") == 0) return &totem_config->max_messages; if (strcmp(param_name, "totem.miss_count_const") == 0) return &totem_config->miss_count_const; return NULL; } /* * Read key_name from icmap. If key is not found or key_name == delete_key or if allow_zero is false * and readed value is zero, default value is used and stored into totem_config. */ static void totem_volatile_config_set_value (struct totem_config *totem_config, const char *key_name, const char *deleted_key, unsigned int default_value, int allow_zero_value) { if (icmap_get_uint32(key_name, totem_get_param_by_name(totem_config, key_name)) != CS_OK || (deleted_key != NULL && strcmp(deleted_key, key_name) == 0) || (!allow_zero_value && *totem_get_param_by_name(totem_config, key_name) == 0)) { *totem_get_param_by_name(totem_config, key_name) = default_value; } } /* * Read and validate config values from cmap and store them into totem_config. If key doesn't exists, * default value is stored. deleted_key is name of key beeing processed by delete operation * from cmap. It is considered as non existing even if it can be read. Can be NULL. */ static void totem_volatile_config_read (struct totem_config *totem_config, const char *deleted_key) { uint32_t u32; totem_volatile_config_set_value(totem_config, "totem.token_retransmits_before_loss_const", deleted_key, TOKEN_RETRANSMITS_BEFORE_LOSS_CONST, 0); totem_volatile_config_set_value(totem_config, "totem.token", deleted_key, TOKEN_TIMEOUT, 0); if (totem_config->interface_count > 0 && totem_config->interfaces[0].member_count > 2) { u32 = TOKEN_COEFFICIENT; icmap_get_uint32("totem.token_coefficient", &u32); totem_config->token_timeout += (totem_config->interfaces[0].member_count - 2) * u32; } totem_volatile_config_set_value(totem_config, "totem.max_network_delay", deleted_key, MAX_NETWORK_DELAY, 0); totem_volatile_config_set_value(totem_config, "totem.window_size", deleted_key, WINDOW_SIZE, 0); totem_volatile_config_set_value(totem_config, "totem.max_messages", deleted_key, MAX_MESSAGES, 0); totem_volatile_config_set_value(totem_config, "totem.miss_count_const", deleted_key, MISS_COUNT_CONST, 0); totem_volatile_config_set_value(totem_config, "totem.token_retransmit", deleted_key, (int)(totem_config->token_timeout / (totem_config->token_retransmits_before_loss_const + 0.2)), 0); totem_volatile_config_set_value(totem_config, "totem.hold", deleted_key, (int)(totem_config->token_retransmit_timeout * 0.8 - (1000/HZ)), 0); totem_volatile_config_set_value(totem_config, "totem.join", deleted_key, JOIN_TIMEOUT, 0); totem_volatile_config_set_value(totem_config, "totem.consensus", deleted_key, (int)(float)(1.2 * totem_config->token_timeout), 0); totem_volatile_config_set_value(totem_config, "totem.merge", deleted_key, MERGE_TIMEOUT, 0); totem_volatile_config_set_value(totem_config, "totem.downcheck", deleted_key, DOWNCHECK_TIMEOUT, 0); totem_volatile_config_set_value(totem_config, "totem.fail_recv_const", deleted_key, FAIL_TO_RECV_CONST, 0); totem_volatile_config_set_value(totem_config, "totem.seqno_unchanged_const", deleted_key, SEQNO_UNCHANGED_CONST, 0); totem_volatile_config_set_value(totem_config, "totem.send_join", deleted_key, 0, 1); totem_volatile_config_set_value(totem_config, "totem.rrp_problem_count_timeout", deleted_key, RRP_PROBLEM_COUNT_TIMEOUT, 0); totem_volatile_config_set_value(totem_config, "totem.rrp_problem_count_threshold", deleted_key, RRP_PROBLEM_COUNT_THRESHOLD_DEFAULT, 0); totem_volatile_config_set_value(totem_config, "totem.rrp_problem_count_mcast_threshold", deleted_key, totem_config->rrp_problem_count_threshold * 10, 0); totem_volatile_config_set_value(totem_config, "totem.rrp_token_expired_timeout", deleted_key, totem_config->token_retransmit_timeout, 0); totem_volatile_config_set_value(totem_config, "totem.rrp_autorecovery_check_timeout", deleted_key, RRP_AUTORECOVERY_CHECK_TIMEOUT, 0); totem_volatile_config_set_value(totem_config, "totem.heartbeat_failures_allowed", deleted_key, 0, 1); } static int totem_volatile_config_validate ( struct totem_config *totem_config, const char **error_string) { static char local_error_reason[512]; const char *error_reason = local_error_reason; if (totem_config->max_network_delay < MINIMUM_TIMEOUT) { snprintf (local_error_reason, sizeof(local_error_reason), - "The max_network_delay parameter (%d ms) may not be less then (%d ms).", + "The max_network_delay parameter (%d ms) may not be less than (%d ms).", totem_config->max_network_delay, MINIMUM_TIMEOUT); goto parse_error; } if (totem_config->token_timeout < MINIMUM_TIMEOUT) { snprintf (local_error_reason, sizeof(local_error_reason), - "The token timeout parameter (%d ms) may not be less then (%d ms).", + "The token timeout parameter (%d ms) may not be less than (%d ms).", totem_config->token_timeout, MINIMUM_TIMEOUT); goto parse_error; } if (totem_config->token_retransmit_timeout < MINIMUM_TIMEOUT) { snprintf (local_error_reason, sizeof(local_error_reason), - "The token retransmit timeout parameter (%d ms) may not be less then (%d ms).", + "The token retransmit timeout parameter (%d ms) may not be less than (%d ms).", totem_config->token_retransmit_timeout, MINIMUM_TIMEOUT); goto parse_error; } if (totem_config->token_hold_timeout < MINIMUM_TIMEOUT) { snprintf (local_error_reason, sizeof(local_error_reason), - "The token hold timeout parameter (%d ms) may not be less then (%d ms).", + "The token hold timeout parameter (%d ms) may not be less than (%d ms).", totem_config->token_hold_timeout, MINIMUM_TIMEOUT); goto parse_error; } if (totem_config->join_timeout < MINIMUM_TIMEOUT) { snprintf (local_error_reason, sizeof(local_error_reason), - "The join timeout parameter (%d ms) may not be less then (%d ms).", + "The join timeout parameter (%d ms) may not be less than (%d ms).", totem_config->join_timeout, MINIMUM_TIMEOUT); goto parse_error; } if (totem_config->consensus_timeout < MINIMUM_TIMEOUT) { snprintf (local_error_reason, sizeof(local_error_reason), - "The consensus timeout parameter (%d ms) may not be less then (%d ms).", + "The consensus timeout parameter (%d ms) may not be less than (%d ms).", totem_config->consensus_timeout, MINIMUM_TIMEOUT); goto parse_error; } if (totem_config->merge_timeout < MINIMUM_TIMEOUT) { snprintf (local_error_reason, sizeof(local_error_reason), - "The merge timeout parameter (%d ms) may not be less then (%d ms).", + "The merge timeout parameter (%d ms) may not be less than (%d ms).", totem_config->merge_timeout, MINIMUM_TIMEOUT); goto parse_error; } if (totem_config->downcheck_timeout < MINIMUM_TIMEOUT) { snprintf (local_error_reason, sizeof(local_error_reason), - "The downcheck timeout parameter (%d ms) may not be less then (%d ms).", + "The downcheck timeout parameter (%d ms) may not be less than (%d ms).", totem_config->downcheck_timeout, MINIMUM_TIMEOUT); goto parse_error; } if (totem_config->rrp_problem_count_timeout < MINIMUM_TIMEOUT) { snprintf (local_error_reason, sizeof(local_error_reason), - "The RRP problem count timeout parameter (%d ms) may not be less then (%d ms).", + "The RRP problem count timeout parameter (%d ms) may not be less than (%d ms).", totem_config->rrp_problem_count_timeout, MINIMUM_TIMEOUT); goto parse_error; } if (totem_config->rrp_problem_count_threshold < RRP_PROBLEM_COUNT_THRESHOLD_MIN) { snprintf (local_error_reason, sizeof(local_error_reason), - "The RRP problem count threshold (%d problem count) may not be less then (%d problem count).", + "The RRP problem count threshold (%d problem count) may not be less than (%d problem count).", totem_config->rrp_problem_count_threshold, RRP_PROBLEM_COUNT_THRESHOLD_MIN); goto parse_error; } if (totem_config->rrp_problem_count_mcast_threshold < RRP_PROBLEM_COUNT_THRESHOLD_MIN) { snprintf (local_error_reason, sizeof(local_error_reason), - "The RRP multicast problem count threshold (%d problem count) may not be less then (%d problem count).", + "The RRP multicast problem count threshold (%d problem count) may not be less than (%d problem count).", totem_config->rrp_problem_count_mcast_threshold, RRP_PROBLEM_COUNT_THRESHOLD_MIN); goto parse_error; } if (totem_config->rrp_token_expired_timeout < MINIMUM_TIMEOUT) { snprintf (local_error_reason, sizeof(local_error_reason), - "The RRP token expired timeout parameter (%d ms) may not be less then (%d ms).", + "The RRP token expired timeout parameter (%d ms) may not be less than (%d ms).", totem_config->rrp_token_expired_timeout, MINIMUM_TIMEOUT); goto parse_error; } return 0; parse_error: snprintf (error_string_response, sizeof(error_string_response), "parse error in config: %s\n", error_reason); *error_string = error_string_response; return (-1); } static int totem_get_crypto(struct totem_config *totem_config) { char *str; const char *tmp_cipher; const char *tmp_hash; tmp_hash = "sha1"; tmp_cipher = "aes256"; if (icmap_get_string("totem.secauth", &str) == CS_OK) { if (strcmp (str, "off") == 0) { tmp_hash = "none"; tmp_cipher = "none"; } free(str); } if (icmap_get_string("totem.crypto_cipher", &str) == CS_OK) { if (strcmp(str, "none") == 0) { tmp_cipher = "none"; } if (strcmp(str, "aes256") == 0) { tmp_cipher = "aes256"; } if (strcmp(str, "aes192") == 0) { tmp_cipher = "aes192"; } if (strcmp(str, "aes128") == 0) { tmp_cipher = "aes128"; } if (strcmp(str, "3des") == 0) { tmp_cipher = "3des"; } free(str); } if (icmap_get_string("totem.crypto_hash", &str) == CS_OK) { if (strcmp(str, "none") == 0) { tmp_hash = "none"; } if (strcmp(str, "md5") == 0) { tmp_hash = "md5"; } if (strcmp(str, "sha1") == 0) { tmp_hash = "sha1"; } if (strcmp(str, "sha256") == 0) { tmp_hash = "sha256"; } if (strcmp(str, "sha384") == 0) { tmp_hash = "sha384"; } if (strcmp(str, "sha512") == 0) { tmp_hash = "sha512"; } free(str); } if ((strcmp(tmp_cipher, "none") != 0) && (strcmp(tmp_hash, "none") == 0)) { return -1; } free(totem_config->crypto_cipher_type); free(totem_config->crypto_hash_type); totem_config->crypto_cipher_type = strdup(tmp_cipher); totem_config->crypto_hash_type = strdup(tmp_hash); return 0; } static int totem_config_get_ip_version(void) { int res; char *str; res = AF_INET; if (icmap_get_string("totem.ip_version", &str) == CS_OK) { if (strcmp(str, "ipv4") == 0) { res = AF_INET; } if (strcmp(str, "ipv6") == 0) { res = AF_INET6; } free(str); } return (res); } static uint16_t generate_cluster_id (const char *cluster_name) { int i; int value = 0; for (i = 0; i < strlen(cluster_name); i++) { value <<= 1; value += cluster_name[i]; } return (value & 0xFFFF); } static int get_cluster_mcast_addr ( const char *cluster_name, const struct totem_ip_address *bindnet, unsigned int ringnumber, int ip_version, struct totem_ip_address *res) { uint16_t clusterid; char addr[INET6_ADDRSTRLEN + 1]; int err; if (cluster_name == NULL) { return (-1); } clusterid = generate_cluster_id(cluster_name) + ringnumber; memset (res, 0, sizeof(*res)); switch (bindnet->family) { case AF_INET: snprintf(addr, sizeof(addr), "239.192.%d.%d", clusterid >> 8, clusterid % 0xFF); break; case AF_INET6: snprintf(addr, sizeof(addr), "ff15::%x", clusterid); break; default: /* * Unknown family */ return (-1); } err = totemip_parse (res, addr, ip_version); return (err); } static int find_local_node_in_nodelist(struct totem_config *totem_config) { icmap_iter_t iter; const char *iter_key; int res = 0; unsigned int node_pos; int local_node_pos = -1; struct totem_ip_address bind_addr; int interface_up, interface_num; char tmp_key[ICMAP_KEYNAME_MAXLEN]; char *node_addr_str; struct totem_ip_address node_addr; res = totemip_iface_check(&totem_config->interfaces[0].bindnet, &bind_addr, &interface_up, &interface_num, totem_config->clear_node_high_bit); if (res == -1) { return (-1); } iter = icmap_iter_init("nodelist.node."); while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) { res = sscanf(iter_key, "nodelist.node.%u.%s", &node_pos, tmp_key); if (res != 2) { continue; } if (strcmp(tmp_key, "ring0_addr") != 0) { continue; } snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.ring0_addr", node_pos); if (icmap_get_string(tmp_key, &node_addr_str) != CS_OK) { continue; } res = totemip_parse (&node_addr, node_addr_str, totem_config->ip_version); free(node_addr_str); if (res == -1) { continue ; } if (totemip_equal(&bind_addr, &node_addr)) { local_node_pos = node_pos; } } icmap_iter_finalize(iter); return (local_node_pos); } static void put_nodelist_members_to_config(struct totem_config *totem_config) { icmap_iter_t iter, iter2; const char *iter_key, *iter_key2; int res = 0; unsigned int node_pos; char tmp_key[ICMAP_KEYNAME_MAXLEN]; char tmp_key2[ICMAP_KEYNAME_MAXLEN]; char *node_addr_str; int member_count; unsigned int ringnumber = 0; int i, j; /* Clear out nodelist so we can put the new one in if needed */ for (i = 0; i < totem_config->interface_count; i++) { for (j = 0; j < PROCESSOR_COUNT_MAX; j++) { memset(&totem_config->interfaces[i].member_list[j], 0, sizeof(struct totem_ip_address)); } totem_config->interfaces[i].member_count = 0; } iter = icmap_iter_init("nodelist.node."); while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) { res = sscanf(iter_key, "nodelist.node.%u.%s", &node_pos, tmp_key); if (res != 2) { continue; } if (strcmp(tmp_key, "ring0_addr") != 0) { continue; } snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.", node_pos); iter2 = icmap_iter_init(tmp_key); while ((iter_key2 = icmap_iter_next(iter2, NULL, NULL)) != NULL) { res = sscanf(iter_key2, "nodelist.node.%u.ring%u%s", &node_pos, &ringnumber, tmp_key2); if (res != 3 || strcmp(tmp_key2, "_addr") != 0) { continue; } if (icmap_get_string(iter_key2, &node_addr_str) != CS_OK) { continue; } member_count = totem_config->interfaces[ringnumber].member_count; res = totemip_parse(&totem_config->interfaces[ringnumber].member_list[member_count], node_addr_str, totem_config->ip_version); if (res != -1) { totem_config->interfaces[ringnumber].member_count++; } free(node_addr_str); } icmap_iter_finalize(iter2); } icmap_iter_finalize(iter); } /* * Tries to find node (node_pos) in config nodelist which address matches any * local interface. Address can be stored in ring0_addr or if ipaddr_key_prefix is not NULL * key with prefix ipaddr_key is used (there can be multiuple of them) * This function differs * from find_local_node_in_nodelist because it doesn't need bindnetaddr, * but doesn't work when bind addr is network address (so IP must be exact * match). * * Returns 1 on success (address was found, node_pos is then correctly set) or 0 on failure. */ int totem_config_find_local_addr_in_nodelist(const char *ipaddr_key_prefix, unsigned int *node_pos) { struct list_head addrs; struct totem_ip_if_address *if_addr; icmap_iter_t iter, iter2; const char *iter_key, *iter_key2; struct list_head *list; const char *ipaddr_key; int ip_version; struct totem_ip_address node_addr; char *node_addr_str; int node_found = 0; int res = 0; char tmp_key[ICMAP_KEYNAME_MAXLEN]; if (totemip_getifaddrs(&addrs) == -1) { return 0; } ip_version = totem_config_get_ip_version(); iter = icmap_iter_init("nodelist.node."); while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) { res = sscanf(iter_key, "nodelist.node.%u.%s", node_pos, tmp_key); if (res != 2) { continue; } if (strcmp(tmp_key, "ring0_addr") != 0) { continue; } if (icmap_get_string(iter_key, &node_addr_str) != CS_OK) { continue ; } free(node_addr_str); /* * ring0_addr found -> let's iterate thru ipaddr_key_prefix */ snprintf(tmp_key, sizeof(tmp_key), "nodelist.node.%u.%s", *node_pos, (ipaddr_key_prefix != NULL ? ipaddr_key_prefix : "ring0_addr")); iter2 = icmap_iter_init(tmp_key); while ((iter_key2 = icmap_iter_next(iter2, NULL, NULL)) != NULL) { /* * ring0_addr must be exact match, not prefix */ ipaddr_key = (ipaddr_key_prefix != NULL ? iter_key2 : tmp_key); if (icmap_get_string(ipaddr_key, &node_addr_str) != CS_OK) { continue ; } if (totemip_parse(&node_addr, node_addr_str, ip_version) == -1) { free(node_addr_str); continue ; } free(node_addr_str); /* * Try to match ip with if_addrs */ node_found = 0; for (list = addrs.next; list != &addrs; list = list->next) { if_addr = list_entry(list, struct totem_ip_if_address, list); if (totemip_equal(&node_addr, &if_addr->ip_addr)) { node_found = 1; break; } } if (node_found) { break ; } } icmap_iter_finalize(iter2); if (node_found) { break ; } } icmap_iter_finalize(iter); totemip_freeifaddrs(&addrs); return (node_found); } static void config_convert_nodelist_to_interface(struct totem_config *totem_config) { int res = 0; unsigned int node_pos; char tmp_key[ICMAP_KEYNAME_MAXLEN]; char tmp_key2[ICMAP_KEYNAME_MAXLEN]; char *node_addr_str; unsigned int ringnumber = 0; icmap_iter_t iter; const char *iter_key; if (totem_config_find_local_addr_in_nodelist(NULL, &node_pos)) { /* * We found node, so create interface section */ snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.", node_pos); iter = icmap_iter_init(tmp_key); while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) { res = sscanf(iter_key, "nodelist.node.%u.ring%u%s", &node_pos, &ringnumber, tmp_key2); if (res != 3 || strcmp(tmp_key2, "_addr") != 0) { continue ; } if (icmap_get_string(iter_key, &node_addr_str) != CS_OK) { continue; } snprintf(tmp_key2, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.bindnetaddr", ringnumber); icmap_set_string(tmp_key2, node_addr_str); free(node_addr_str); } icmap_iter_finalize(iter); } } extern int totem_config_read ( struct totem_config *totem_config, const char **error_string, uint64_t *warnings) { int res = 0; char *str; unsigned int ringnumber = 0; int member_count = 0; icmap_iter_t iter, member_iter; const char *iter_key; const char *member_iter_key; char ringnumber_key[ICMAP_KEYNAME_MAXLEN]; char tmp_key[ICMAP_KEYNAME_MAXLEN]; uint8_t u8; uint16_t u16; char *cluster_name = NULL; int i; int local_node_pos; int nodeid_set; *warnings = 0; memset (totem_config, 0, sizeof (struct totem_config)); totem_config->interfaces = malloc (sizeof (struct totem_interface) * INTERFACE_MAX); if (totem_config->interfaces == 0) { *error_string = "Out of memory trying to allocate ethernet interface storage area"; return -1; } memset (totem_config->interfaces, 0, sizeof (struct totem_interface) * INTERFACE_MAX); strcpy (totem_config->rrp_mode, "none"); icmap_get_uint32("totem.version", (uint32_t *)&totem_config->version); if (totem_get_crypto(totem_config) != 0) { *error_string = "crypto_cipher requires crypto_hash with value other than none"; return -1; } if (icmap_get_string("totem.rrp_mode", &str) == CS_OK) { if (strlen(str) >= TOTEM_RRP_MODE_BYTES) { *error_string = "totem.rrp_mode is too long"; free(str); return -1; } strcpy (totem_config->rrp_mode, str); free(str); } icmap_get_uint32("totem.nodeid", &totem_config->node_id); totem_config->clear_node_high_bit = 0; if (icmap_get_string("totem.clear_node_high_bit", &str) == CS_OK) { if (strcmp (str, "yes") == 0) { totem_config->clear_node_high_bit = 1; } free(str); } icmap_get_uint32("totem.threads", &totem_config->threads); icmap_get_uint32("totem.netmtu", &totem_config->net_mtu); if (icmap_get_string("totem.cluster_name", &cluster_name) != CS_OK) { cluster_name = NULL; } totem_config->ip_version = totem_config_get_ip_version(); if (icmap_get_string("totem.interface.0.bindnetaddr", &str) != CS_OK) { /* * We were not able to find ring 0 bindnet addr. Try to use nodelist informations */ config_convert_nodelist_to_interface(totem_config); } else { free(str); } iter = icmap_iter_init("totem.interface."); while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) { res = sscanf(iter_key, "totem.interface.%[^.].%s", ringnumber_key, tmp_key); if (res != 2) { continue; } if (strcmp(tmp_key, "bindnetaddr") != 0) { continue; } member_count = 0; ringnumber = atoi(ringnumber_key); if (ringnumber >= INTERFACE_MAX) { free(cluster_name); snprintf (error_string_response, sizeof(error_string_response), - "parse error in config: interface ring number %u is bigger then allowed maximum %u\n", + "parse error in config: interface ring number %u is bigger than allowed maximum %u\n", ringnumber, INTERFACE_MAX - 1); *error_string = error_string_response; return -1; } /* * Get the bind net address */ if (icmap_get_string(iter_key, &str) == CS_OK) { res = totemip_parse (&totem_config->interfaces[ringnumber].bindnet, str, totem_config->interfaces[ringnumber].mcast_addr.family); free(str); } /* * Get interface multicast address */ snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.mcastaddr", ringnumber); if (icmap_get_string(tmp_key, &str) == CS_OK) { res = totemip_parse (&totem_config->interfaces[ringnumber].mcast_addr, str, totem_config->ip_version); free(str); } else { /* * User not specified address -> autogenerate one from cluster_name key * (if available) */ res = get_cluster_mcast_addr (cluster_name, &totem_config->interfaces[ringnumber].bindnet, ringnumber, totem_config->ip_version, &totem_config->interfaces[ringnumber].mcast_addr); } totem_config->broadcast_use = 0; snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.broadcast", ringnumber); if (icmap_get_string(tmp_key, &str) == CS_OK) { if (strcmp (str, "yes") == 0) { totem_config->broadcast_use = 1; totemip_parse ( &totem_config->interfaces[ringnumber].mcast_addr, "255.255.255.255", totem_config->ip_version); } free(str); } /* * Get mcast port */ snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.mcastport", ringnumber); if (icmap_get_uint16(tmp_key, &totem_config->interfaces[ringnumber].ip_port) != CS_OK) { if (totem_config->broadcast_use) { totem_config->interfaces[ringnumber].ip_port = DEFAULT_PORT + (2 * ringnumber); } else { totem_config->interfaces[ringnumber].ip_port = DEFAULT_PORT; } } /* * Get the TTL */ totem_config->interfaces[ringnumber].ttl = 1; snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.ttl", ringnumber); if (icmap_get_uint8(tmp_key, &u8) == CS_OK) { totem_config->interfaces[ringnumber].ttl = u8; } snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.member.", ringnumber); member_iter = icmap_iter_init(tmp_key); while ((member_iter_key = icmap_iter_next(member_iter, NULL, NULL)) != NULL) { if (member_count == 0) { if (icmap_get_string("nodelist.node.0.ring0_addr", &str) == CS_OK) { free(str); *warnings |= TOTEM_CONFIG_WARNING_MEMBERS_IGNORED; break; } else { *warnings |= TOTEM_CONFIG_WARNING_MEMBERS_DEPRECATED; } } if (icmap_get_string(member_iter_key, &str) == CS_OK) { res = totemip_parse (&totem_config->interfaces[ringnumber].member_list[member_count++], str, totem_config->ip_version); } } icmap_iter_finalize(member_iter); totem_config->interfaces[ringnumber].member_count = member_count; totem_config->interface_count++; } icmap_iter_finalize(iter); /* * Store automatically generated items back to icmap */ for (i = 0; i < totem_config->interface_count; i++) { snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.mcastaddr", i); if (icmap_get_string(tmp_key, &str) == CS_OK) { free(str); } else { str = (char *)totemip_print(&totem_config->interfaces[i].mcast_addr); icmap_set_string(tmp_key, str); } snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.mcastport", i); if (icmap_get_uint16(tmp_key, &u16) != CS_OK) { icmap_set_uint16(tmp_key, totem_config->interfaces[i].ip_port); } } totem_config->transport_number = TOTEM_TRANSPORT_UDP; if (icmap_get_string("totem.transport", &str) == CS_OK) { if (strcmp (str, "udpu") == 0) { totem_config->transport_number = TOTEM_TRANSPORT_UDPU; } if (strcmp (str, "iba") == 0) { totem_config->transport_number = TOTEM_TRANSPORT_RDMA; } free(str); } free(cluster_name); /* * Check existence of nodelist */ if (icmap_get_string("nodelist.node.0.ring0_addr", &str) == CS_OK) { free(str); /* * find local node */ local_node_pos = find_local_node_in_nodelist(totem_config); if (local_node_pos != -1) { icmap_set_uint32("nodelist.local_node_pos", local_node_pos); snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.nodeid", local_node_pos); nodeid_set = (totem_config->node_id != 0); if (icmap_get_uint32(tmp_key, &totem_config->node_id) == CS_OK && nodeid_set) { *warnings |= TOTEM_CONFIG_WARNING_TOTEM_NODEID_IGNORED; } /* * Make localnode ring0_addr read only, so we can be sure that local * node never changes. If rebinding to other IP would be in future * supported, this must be changed and handled properly! */ snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.ring0_addr", local_node_pos); icmap_set_ro_access(tmp_key, 0, 1); icmap_set_ro_access("nodelist.local_node_pos", 0, 1); } put_nodelist_members_to_config(totem_config); } /* * Get things that might change in the future (and can depend on totem_config->interfaces); */ totem_volatile_config_read(totem_config, NULL); icmap_set_uint8("config.totemconfig_reload_in_progress", 0); add_totem_config_notification(totem_config); return 0; } int totem_config_validate ( struct totem_config *totem_config, const char **error_string) { static char local_error_reason[512]; char parse_error[512]; const char *error_reason = local_error_reason; int i; unsigned int interface_max = INTERFACE_MAX; if (totem_config->interface_count == 0) { error_reason = "No interfaces defined"; goto parse_error; } for (i = 0; i < totem_config->interface_count; i++) { /* * Some error checking of parsed data to make sure its valid */ struct totem_ip_address null_addr; memset (&null_addr, 0, sizeof (struct totem_ip_address)); if ((totem_config->transport_number == 0) && memcmp (&totem_config->interfaces[i].mcast_addr, &null_addr, sizeof (struct totem_ip_address)) == 0) { error_reason = "No multicast address specified"; goto parse_error; } if (totem_config->interfaces[i].ip_port == 0) { error_reason = "No multicast port specified"; goto parse_error; } if (totem_config->interfaces[i].ttl > 255) { error_reason = "Invalid TTL (should be 0..255)"; goto parse_error; } if (totem_config->transport_number != TOTEM_TRANSPORT_UDP && totem_config->interfaces[i].ttl != 1) { error_reason = "Can only set ttl on multicast transport types"; goto parse_error; } if (totem_config->interfaces[i].mcast_addr.family == AF_INET6 && totem_config->node_id == 0) { error_reason = "An IPV6 network requires that a node ID be specified."; goto parse_error; } if (totem_config->broadcast_use == 0 && totem_config->transport_number == 0) { if (totem_config->interfaces[i].mcast_addr.family != totem_config->interfaces[i].bindnet.family) { error_reason = "Multicast address family does not match bind address family"; goto parse_error; } if (totem_config->interfaces[i].mcast_addr.family != totem_config->interfaces[i].bindnet.family) { error_reason = "Not all bind address belong to the same IP family"; goto parse_error; } if (totemip_is_mcast (&totem_config->interfaces[i].mcast_addr) != 0) { error_reason = "mcastaddr is not a correct multicast address."; goto parse_error; } } } if (totem_config->version != 2) { error_reason = "This totem parser can only parse version 2 configurations."; goto parse_error; } if (totem_volatile_config_validate(totem_config, error_string) == -1) { return (-1); } /* * RRP values validation */ if (strcmp (totem_config->rrp_mode, "none") && strcmp (totem_config->rrp_mode, "active") && strcmp (totem_config->rrp_mode, "passive")) { snprintf (local_error_reason, sizeof(local_error_reason), "The RRP mode \"%s\" specified is invalid. It must be none, active, or passive.\n", totem_config->rrp_mode); goto parse_error; } if (strcmp (totem_config->rrp_mode, "none") == 0) { interface_max = 1; } if (interface_max < totem_config->interface_count) { snprintf (parse_error, sizeof(parse_error), "%d is too many configured interfaces for the rrp_mode setting %s.", totem_config->interface_count, totem_config->rrp_mode); error_reason = parse_error; goto parse_error; } if (totem_config->net_mtu == 0) { totem_config->net_mtu = 1500; } return 0; parse_error: snprintf (error_string_response, sizeof(error_string_response), "parse error in config: %s\n", error_reason); *error_string = error_string_response; return (-1); } static int read_keyfile ( const char *key_location, struct totem_config *totem_config, const char **error_string) { int fd; int res; ssize_t expected_key_len = sizeof (totem_config->private_key); int saved_errno; char error_str[100]; const char *error_ptr; fd = open (key_location, O_RDONLY); if (fd == -1) { error_ptr = qb_strerror_r(errno, error_str, sizeof(error_str)); snprintf (error_string_response, sizeof(error_string_response), "Could not open %s: %s\n", key_location, error_ptr); goto parse_error; } res = read (fd, totem_config->private_key, expected_key_len); saved_errno = errno; close (fd); if (res == -1) { error_ptr = qb_strerror_r (saved_errno, error_str, sizeof(error_str)); snprintf (error_string_response, sizeof(error_string_response), "Could not read %s: %s\n", key_location, error_ptr); goto parse_error; } totem_config->private_key_len = expected_key_len; if (res != expected_key_len) { snprintf (error_string_response, sizeof(error_string_response), "Could only read %d bits of 1024 bits from %s.\n", res * 8, key_location); goto parse_error; } return 0; parse_error: *error_string = error_string_response; return (-1); } int totem_config_keyread ( struct totem_config *totem_config, const char **error_string) { int got_key = 0; char *key_location = NULL; int res; size_t key_len; memset (totem_config->private_key, 0, 128); totem_config->private_key_len = 128; if (strcmp(totem_config->crypto_cipher_type, "none") == 0 && strcmp(totem_config->crypto_hash_type, "none") == 0) { return (0); } /* cmap may store the location of the key file */ if (icmap_get_string("totem.keyfile", &key_location) == CS_OK) { res = read_keyfile(key_location, totem_config, error_string); free(key_location); if (res) { goto key_error; } got_key = 1; } else { /* Or the key itself may be in the cmap */ if (icmap_get("totem.key", NULL, &key_len, NULL) == CS_OK) { if (key_len > sizeof (totem_config->private_key)) { sprintf(error_string_response, "key is too long"); goto key_error; } if (icmap_get("totem.key", totem_config->private_key, &key_len, NULL) == CS_OK) { totem_config->private_key_len = key_len; got_key = 1; } else { sprintf(error_string_response, "can't store private key"); goto key_error; } } } /* In desperation we read the default filename */ if (!got_key) { const char *filename = getenv("COROSYNC_TOTEM_AUTHKEY_FILE"); if (!filename) filename = COROSYSCONFDIR "/authkey"; res = read_keyfile(filename, totem_config, error_string); if (res) goto key_error; } return (0); key_error: *error_string = error_string_response; return (-1); } static void debug_dump_totem_config(const struct totem_config *totem_config) { log_printf(LOGSYS_LEVEL_DEBUG, "Token Timeout (%d ms) retransmit timeout (%d ms)", totem_config->token_timeout, totem_config->token_retransmit_timeout); log_printf(LOGSYS_LEVEL_DEBUG, "token hold (%d ms) retransmits before loss (%d retrans)", totem_config->token_hold_timeout, totem_config->token_retransmits_before_loss_const); log_printf(LOGSYS_LEVEL_DEBUG, "join (%d ms) send_join (%d ms) consensus (%d ms) merge (%d ms)", totem_config->join_timeout, totem_config->send_join_timeout, totem_config->consensus_timeout, totem_config->merge_timeout); log_printf(LOGSYS_LEVEL_DEBUG, "downcheck (%d ms) fail to recv const (%d msgs)", totem_config->downcheck_timeout, totem_config->fail_to_recv_const); log_printf(LOGSYS_LEVEL_DEBUG, "seqno unchanged const (%d rotations) Maximum network MTU %d", totem_config->seqno_unchanged_const, totem_config->net_mtu); log_printf(LOGSYS_LEVEL_DEBUG, "window size per rotation (%d messages) maximum messages per rotation (%d messages)", totem_config->window_size, totem_config->max_messages); log_printf(LOGSYS_LEVEL_DEBUG, "missed count const (%d messages)", totem_config->miss_count_const); log_printf(LOGSYS_LEVEL_DEBUG, "RRP token expired timeout (%d ms)", totem_config->rrp_token_expired_timeout); log_printf(LOGSYS_LEVEL_DEBUG, "RRP token problem counter (%d ms)", totem_config->rrp_problem_count_timeout); log_printf(LOGSYS_LEVEL_DEBUG, "RRP threshold (%d problem count)", totem_config->rrp_problem_count_threshold); log_printf(LOGSYS_LEVEL_DEBUG, "RRP multicast threshold (%d problem count)", totem_config->rrp_problem_count_mcast_threshold); log_printf(LOGSYS_LEVEL_DEBUG, "RRP automatic recovery check timeout (%d ms)", totem_config->rrp_autorecovery_check_timeout); log_printf(LOGSYS_LEVEL_DEBUG, "RRP mode set to %s.", totem_config->rrp_mode); log_printf(LOGSYS_LEVEL_DEBUG, "heartbeat_failures_allowed (%d)", totem_config->heartbeat_failures_allowed); log_printf(LOGSYS_LEVEL_DEBUG, "max_network_delay (%d ms)", totem_config->max_network_delay); } static void totem_change_notify( int32_t event, const char *key_name, struct icmap_notify_value new_val, struct icmap_notify_value old_val, void *user_data) { struct totem_config *totem_config = (struct totem_config *)user_data; uint32_t *param; uint8_t reloading; const char *deleted_key = NULL; const char *error_string; /* * 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.reload_in_progress", &reloading) == CS_OK && reloading) return; param = totem_get_param_by_name((struct totem_config *)user_data, key_name); /* * Process change only if changed key is found in totem_config (-> param is not NULL) * or for special key token_coefficient. token_coefficient key is not stored in * totem_config, but it is used for computation of token timeout. */ if (!param && strcmp(key_name, "totem.token_coefficient") != 0) return; /* - * Values other then UINT32 are not supported, or needed (yet) + * Values other than UINT32 are not supported, or needed (yet) */ switch (event) { case ICMAP_TRACK_DELETE: deleted_key = key_name; break; case ICMAP_TRACK_ADD: case ICMAP_TRACK_MODIFY: deleted_key = NULL; break; default: break; } totem_volatile_config_read (totem_config, deleted_key); log_printf(LOGSYS_LEVEL_DEBUG, "Totem related config key changed. Dumping actual totem config."); debug_dump_totem_config(totem_config); if (totem_volatile_config_validate(totem_config, &error_string) == -1) { log_printf (LOGSYS_LEVEL_ERROR, "%s", error_string); /* * TODO: Consider corosync exit and/or load defaults for volatile * values. For now, log error seems to be enough */ } } static void totem_reload_notify( int32_t event, const char *key_name, struct icmap_notify_value new_val, struct icmap_notify_value old_val, void *user_data) { struct totem_config *totem_config = (struct totem_config *)user_data; uint32_t local_node_pos; const char *error_string; /* Reload has completed */ if (*(uint8_t *)new_val.data == 0) { put_nodelist_members_to_config (totem_config); totem_volatile_config_read (totem_config, NULL); log_printf(LOGSYS_LEVEL_DEBUG, "Configuration reloaded. Dumping actual totem config."); debug_dump_totem_config(totem_config); if (totem_volatile_config_validate(totem_config, &error_string) == -1) { log_printf (LOGSYS_LEVEL_ERROR, "%s", error_string); /* * TODO: Consider corosync exit and/or load defaults for volatile * values. For now, log error seems to be enough */ } /* Reinstate the local_node_pos */ local_node_pos = find_local_node_in_nodelist(totem_config); if (local_node_pos != -1) { icmap_set_uint32("nodelist.local_node_pos", local_node_pos); } icmap_set_uint8("config.totemconfig_reload_in_progress", 0); } else { icmap_set_uint8("config.totemconfig_reload_in_progress", 1); } } static void add_totem_config_notification(struct totem_config *totem_config) { icmap_track_t icmap_track; icmap_track_add("totem.", ICMAP_TRACK_ADD | ICMAP_TRACK_DELETE | ICMAP_TRACK_MODIFY | ICMAP_TRACK_PREFIX, totem_change_notify, totem_config, &icmap_track); icmap_track_add("config.reload_in_progress", ICMAP_TRACK_ADD | ICMAP_TRACK_MODIFY, totem_reload_notify, totem_config, &icmap_track); }