diff --git a/exec/logconfig.c b/exec/logconfig.c index b37622ec..350d8a94 100644 --- a/exec/logconfig.c +++ b/exec/logconfig.c @@ -1,753 +1,757 @@ /* * Copyright (c) 2002-2005 MontaVista Software, Inc. * Copyright (c) 2006-2011 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 "config.h" #include #include #ifdef LOGCONFIG_USE_ICMAP #include #define MAP_KEYNAME_MAXLEN ICMAP_KEYNAME_MAXLEN #define map_get_string(key_name, value) icmap_get_string(key_name, value) #else #include static cmap_handle_t cmap_handle; static const char *main_logfile; #define MAP_KEYNAME_MAXLEN CMAP_KEYNAME_MAXLEN #define map_get_string(key_name, value) cmap_get_string(cmap_handle, key_name, value) #endif #include "util.h" #include "logconfig.h" +#include "totemknet.h" static char error_string_response[512]; /** * insert_into_buffer * @target_buffer: a buffer where to write results * @bufferlen: tell us the size of the buffer to avoid overflows * @entry: entry that needs to be added to the buffer * @after: can either be NULL or set to a string. * if NULL, @entry is prependend to logsys_format_get buffer. * if set, @entry is added immediately after @after. * * Since the function is specific to logsys_format_get handling, it is implicit * that source is logsys_format_get(); * * In case of failure, target_buffer could be left dirty. So don't trust * any data leftover in it. * * Searching for "after" assumes that there is only entry of "after" * in the source. Afterall we control the string here and for logging format * it makes little to no sense to have duplicate format entries. * * Returns: 0 on success, -1 on failure **/ static int insert_into_buffer( char *target_buffer, size_t bufferlen, const char *entry, const char *after) { const char *current_format = NULL; current_format = logsys_format_get(); /* if the entry is already in the format we don't add it again */ if (strstr(current_format, entry) != NULL) { return -1; } /* if there is no "after", simply prepend the requested entry * otherwise go for beautiful string manipulation.... */ if (!after) { if (snprintf(target_buffer, bufferlen - 1, "%s%s", entry, current_format) >= bufferlen - 1) { return -1; } } else { const char *afterpos; size_t afterlen; size_t templen; /* check if after is contained in the format * and afterlen has a meaning or return an error */ afterpos = strstr(current_format, after); afterlen = strlen(after); if ((!afterpos) || (!afterlen)) { return -1; } templen = afterpos - current_format + afterlen; if (snprintf(target_buffer, templen + 1, "%s", current_format) >= bufferlen - 1) { return -1; } if (snprintf(target_buffer + templen, bufferlen - ( templen + 1 ), "%s%s", entry, current_format + templen) >= bufferlen - ( templen + 1 )) { return -1; } } return 0; } /* * format set is global specific option that * doesn't apply at system/subsystem level. */ static int corosync_main_config_format_set ( const char **error_string) { const char *error_reason; char new_format_buffer[PATH_MAX]; char *value = NULL; int err = 0; char timestamp_str_to_add[8]; if (map_get_string("logging.fileline", &value) == CS_OK) { if (strcmp (value, "on") == 0) { if (!insert_into_buffer(new_format_buffer, sizeof(new_format_buffer), " %f:%l", "g]")) { err = logsys_format_set(new_format_buffer); } else if (!insert_into_buffer(new_format_buffer, sizeof(new_format_buffer), "%f:%l", NULL)) { err = logsys_format_set(new_format_buffer); } } else if (strcmp (value, "off") == 0) { /* nothing to do here */ } else { error_reason = "unknown value for fileline"; free(value); goto parse_error; } free(value); } if (err) { error_reason = "not enough memory to set logging format buffer"; goto parse_error; } if (map_get_string("logging.function_name", &value) == CS_OK) { if (strcmp (value, "on") == 0) { if (!insert_into_buffer(new_format_buffer, sizeof(new_format_buffer), "%n:", "f:")) { err = logsys_format_set(new_format_buffer); } else if (!insert_into_buffer(new_format_buffer, sizeof(new_format_buffer), " %n", "g]")) { err = logsys_format_set(new_format_buffer); } } else if (strcmp (value, "off") == 0) { /* nothing to do here */ } else { error_reason = "unknown value for function_name"; free(value); goto parse_error; } free(value); } if (err) { error_reason = "not enough memory to set logging format buffer"; goto parse_error; } memset(timestamp_str_to_add, 0, sizeof(timestamp_str_to_add)); if (map_get_string("logging.timestamp", &value) == CS_OK) { if (strcmp (value, "on") == 0) { strcpy(timestamp_str_to_add, "%t"); #ifdef QB_FEATURE_LOG_HIRES_TIMESTAMPS } else if (strcmp (value, "hires") == 0) { strcpy(timestamp_str_to_add, "%T"); #endif } else if (strcmp (value, "off") == 0) { /* nothing to do here */ } else { error_reason = "unknown value for timestamp"; free(value); goto parse_error; } free(value); } else { /* * Display hires timestamp by default, otherwise standard timestamp */ #ifdef QB_FEATURE_LOG_HIRES_TIMESTAMPS strcpy(timestamp_str_to_add, "%T"); #else strcpy(timestamp_str_to_add, "%t"); #endif } if(strcmp(timestamp_str_to_add, "") != 0) { strcat(timestamp_str_to_add, " "); if (insert_into_buffer(new_format_buffer, sizeof(new_format_buffer), timestamp_str_to_add, NULL) == 0) { err = logsys_format_set(new_format_buffer); } } if (err) { error_reason = "not enough memory to set logging format buffer"; goto parse_error; } return (0); parse_error: *error_string = error_reason; return (-1); } /* * blackbox is another global specific option that * doesn't apply at system/subsystem level. */ static int corosync_main_config_blackbox_set ( const char **error_string) { const char *error_reason; char *value = NULL; if (map_get_string("logging.blackbox", &value) == CS_OK) { if (strcmp (value, "on") == 0) { (void)logsys_blackbox_set(QB_TRUE); } else if (strcmp (value, "off") == 0) { (void)logsys_blackbox_set(QB_FALSE); } else { error_reason = "unknown value for blackbox"; free(value); goto parse_error; } free(value); } else { (void)logsys_blackbox_set(QB_TRUE); } return (0); parse_error: *error_string = error_reason; return (-1); } static int corosync_main_config_log_destination_set ( const char *path, const char *key, const char *subsys, const char **error_string, unsigned int mode_mask, char deprecated, char default_value, const char *replacement) { static char formatted_error_reason[128]; char *value = NULL; unsigned int mode; char key_name[MAP_KEYNAME_MAXLEN]; snprintf(key_name, MAP_KEYNAME_MAXLEN, "%s.%s", path, key); if (map_get_string(key_name, &value) == CS_OK) { if (deprecated) { log_printf(LOGSYS_LEVEL_WARNING, "Warning: the %s config parameter has been obsoleted." " See corosync.conf man page %s directive.", key, replacement); } mode = logsys_config_mode_get (subsys); if (strcmp (value, "yes") == 0 || strcmp (value, "on") == 0) { mode |= mode_mask; if (logsys_config_mode_set(subsys, mode) < 0) { sprintf (formatted_error_reason, "unable to set mode %s", key); goto parse_error; } } else if (strcmp (value, "no") == 0 || strcmp (value, "off") == 0) { mode &= ~mode_mask; if (logsys_config_mode_set(subsys, mode) < 0) { sprintf (formatted_error_reason, "unable to unset mode %s", key); goto parse_error; } } else { sprintf (formatted_error_reason, "unknown value for %s", key); goto parse_error; } } /* Set to default if we are the top-level logger */ else if (!subsys && !deprecated) { mode = logsys_config_mode_get (subsys); if (default_value) { mode |= mode_mask; } else { mode &= ~mode_mask; } if (logsys_config_mode_set(subsys, mode) < 0) { sprintf (formatted_error_reason, "unable to change mode %s", key); goto parse_error; } } free(value); return (0); parse_error: *error_string = formatted_error_reason; free(value); return (-1); } static int corosync_main_config_set ( const char *path, const char *subsys, const char **error_string) { const char *error_reason = error_string_response; char *value = NULL; int mode; char key_name[MAP_KEYNAME_MAXLEN]; /* * this bit abuses the internal logsys exported API * to guarantee that all configured subsystems are * initialized too. * * using this approach avoids some headaches caused * by IPC and TOTEM that have a special logging * handling requirements */ if (subsys != NULL) { if (_logsys_subsys_create(subsys, NULL) < 0) { error_reason = "unable to create new logging subsystem"; goto parse_error; } } mode = logsys_config_mode_get(subsys); if (mode < 0) { error_reason = "unable to get mode"; goto parse_error; } if (corosync_main_config_log_destination_set (path, "to_stderr", subsys, &error_reason, LOGSYS_MODE_OUTPUT_STDERR, 0, 1, NULL) != 0) goto parse_error; if (corosync_main_config_log_destination_set (path, "to_syslog", subsys, &error_reason, LOGSYS_MODE_OUTPUT_SYSLOG, 0, 1, NULL) != 0) goto parse_error; snprintf(key_name, MAP_KEYNAME_MAXLEN, "%s.%s", path, "syslog_facility"); if (map_get_string(key_name, &value) == CS_OK) { int syslog_facility; syslog_facility = qb_log_facility2int(value); if (syslog_facility < 0) { error_reason = "unknown syslog facility specified"; goto parse_error; } if (logsys_config_syslog_facility_set(subsys, syslog_facility) < 0) { error_reason = "unable to set syslog facility"; goto parse_error; } free(value); } else { /* Set default here in case of a reload */ if (logsys_config_syslog_facility_set(subsys, qb_log_facility2int("daemon")) < 0) { error_reason = "unable to set syslog facility"; goto parse_error; } } snprintf(key_name, MAP_KEYNAME_MAXLEN, "%s.%s", path, "syslog_level"); if (map_get_string(key_name, &value) == CS_OK) { int syslog_priority; log_printf(LOGSYS_LEVEL_WARNING, "Warning: the syslog_level config parameter has been obsoleted." " See corosync.conf man page syslog_priority directive."); syslog_priority = logsys_priority_id_get(value); free(value); if (syslog_priority < 0) { error_reason = "unknown syslog level specified"; goto parse_error; } if (logsys_config_syslog_priority_set(subsys, syslog_priority) < 0) { error_reason = "unable to set syslog level"; goto parse_error; } } snprintf(key_name, MAP_KEYNAME_MAXLEN, "%s.%s", path, "syslog_priority"); if (map_get_string(key_name, &value) == CS_OK) { int syslog_priority; syslog_priority = logsys_priority_id_get(value); free(value); if (syslog_priority < 0) { error_reason = "unknown syslog priority specified"; goto parse_error; } if (logsys_config_syslog_priority_set(subsys, syslog_priority) < 0) { error_reason = "unable to set syslog priority"; goto parse_error; } } else if(strcmp(key_name, "logging.syslog_priority") == 0){ if (logsys_config_syslog_priority_set(subsys, logsys_priority_id_get("info")) < 0) { error_reason = "unable to set syslog level"; goto parse_error; } } #ifdef LOGCONFIG_USE_ICMAP snprintf(key_name, MAP_KEYNAME_MAXLEN, "%s.%s", path, "logfile"); if (map_get_string(key_name, &value) == CS_OK) { if (logsys_config_file_set (subsys, &error_reason, value) < 0) { goto parse_error; } free(value); } #else if (!subsys) { if (logsys_config_file_set (subsys, &error_reason, main_logfile) < 0) { goto parse_error; } } #endif if (corosync_main_config_log_destination_set (path, "to_file", subsys, &error_reason, LOGSYS_MODE_OUTPUT_FILE, 1, 0, "to_logfile") != 0) goto parse_error; if (corosync_main_config_log_destination_set (path, "to_logfile", subsys, &error_reason, LOGSYS_MODE_OUTPUT_FILE, 0, 0, NULL) != 0) goto parse_error; snprintf(key_name, MAP_KEYNAME_MAXLEN, "%s.%s", path, "logfile_priority"); if (map_get_string(key_name, &value) == CS_OK) { int logfile_priority; logfile_priority = logsys_priority_id_get(value); free(value); if (logfile_priority < 0) { error_reason = "unknown logfile priority specified"; goto parse_error; } if (logsys_config_logfile_priority_set(subsys, logfile_priority) < 0) { error_reason = "unable to set logfile priority"; goto parse_error; } } else if(strcmp(key_name,"logging.logfile_priority") == 0){ if (logsys_config_logfile_priority_set(subsys, logsys_priority_id_get("info")) < 0) { error_reason = "unable to set syslog level"; goto parse_error; } } snprintf(key_name, MAP_KEYNAME_MAXLEN, "%s.%s", path, "debug"); if (map_get_string(key_name, &value) == CS_OK) { if (strcmp (value, "trace") == 0) { if (logsys_config_debug_set (subsys, LOGSYS_DEBUG_TRACE) < 0) { error_reason = "unable to set debug trace"; free(value); goto parse_error; } } else if (strcmp (value, "on") == 0) { if (logsys_config_debug_set (subsys, LOGSYS_DEBUG_ON) < 0) { error_reason = "unable to set debug on"; free(value); goto parse_error; } } else if (strcmp (value, "off") == 0) { if (logsys_config_debug_set (subsys, LOGSYS_DEBUG_OFF) < 0) { error_reason = "unable to set debug off"; free(value); goto parse_error; } } else { error_reason = "unknown value for debug"; free(value); goto parse_error; } free(value); } else { if (logsys_config_debug_set (subsys, LOGSYS_DEBUG_OFF) < 0) { error_reason = "unable to set debug off"; goto parse_error; } } return (0); parse_error: *error_string = error_reason; return (-1); } static int corosync_main_config_read_logging ( const char **error_string) { const char *error_reason; #ifdef LOGCONFIG_USE_ICMAP icmap_iter_t iter; const char *key_name; #else cmap_iter_handle_t iter; char key_name[CMAP_KEYNAME_MAXLEN]; #endif char key_subsys[MAP_KEYNAME_MAXLEN]; char key_item[MAP_KEYNAME_MAXLEN]; int res; /* format set is supported only for toplevel */ if (corosync_main_config_format_set(&error_reason) < 0) { goto parse_error; } if (corosync_main_config_blackbox_set(&error_reason) < 0) { goto parse_error; } if (corosync_main_config_set ("logging", NULL, &error_reason) < 0) { goto parse_error; } /* * we will need 2 of these to compensate for new logging * config format */ #ifdef LOGCONFIG_USE_ICMAP iter = icmap_iter_init("logging.logger_subsys."); while ((key_name = icmap_iter_next(iter, NULL, NULL)) != NULL) { #else cmap_iter_init(cmap_handle, "logging.logger_subsys.", &iter); while ((cmap_iter_next(cmap_handle, iter, key_name, NULL, NULL)) == CS_OK) { #endif res = sscanf(key_name, "logging.logger_subsys.%[^.].%s", key_subsys, key_item); if (res != 2) { continue ; } if (strcmp(key_item, "subsys") != 0) { continue ; } if (snprintf(key_item, MAP_KEYNAME_MAXLEN, "logging.logger_subsys.%s", key_subsys) >= MAP_KEYNAME_MAXLEN) { /* * This should never happen */ error_reason = "Can't snprintf logger_subsys key_item"; goto parse_error; } if (corosync_main_config_set(key_item, key_subsys, &error_reason) < 0) { goto parse_error; } } #ifdef LOGCONFIG_USE_ICMAP icmap_iter_finalize(iter); #else cmap_iter_finalize(cmap_handle, iter); #endif logsys_config_apply(); + + /* Reconfigure knet logging */ + totemknet_configure_log_level(); return 0; parse_error: *error_string = error_reason; return (-1); } #ifdef LOGCONFIG_USE_ICMAP static void main_logging_notify( int32_t event, const char *key_name, struct icmap_notify_value new_val, struct icmap_notify_value old_val, void *user_data) #else static void main_logging_notify( cmap_handle_t cmap_handle_unused, cmap_handle_t cmap_track_handle_unused, int32_t event, const char *key_name, struct cmap_notify_value new_val, struct cmap_notify_value old_val, void *user_data) #endif { const char *error_string; static int reload_in_progress = 0; /* If a full reload happens then suspend updates for individual keys until * it's all completed */ if (strcmp(key_name, "config.reload_in_progress") == 0) { if (*(uint8_t *)new_val.data == 1) { reload_in_progress = 1; } else { reload_in_progress = 0; } } if (reload_in_progress) { log_printf(LOGSYS_LEVEL_DEBUG, "Ignoring key change, reload in progress. %s\n", key_name); return; } /* * Reload the logsys configuration */ if (logsys_format_set(NULL) == -1) { fprintf (stderr, "Unable to setup logging format.\n"); } corosync_main_config_read_logging(&error_string); } #ifdef LOGCONFIG_USE_ICMAP static void add_logsys_config_notification(void) { icmap_track_t icmap_track = NULL; icmap_track_add("logging.", ICMAP_TRACK_ADD | ICMAP_TRACK_DELETE | ICMAP_TRACK_MODIFY | ICMAP_TRACK_PREFIX, main_logging_notify, NULL, &icmap_track); icmap_track_add("config.reload_in_progress", ICMAP_TRACK_ADD | ICMAP_TRACK_MODIFY, main_logging_notify, NULL, &icmap_track); } #else static void add_logsys_config_notification(void) { cmap_track_handle_t cmap_track; cmap_track_add(cmap_handle, "logging.", CMAP_TRACK_ADD | CMAP_TRACK_DELETE | CMAP_TRACK_MODIFY | CMAP_TRACK_PREFIX, main_logging_notify, NULL, &cmap_track); cmap_track_add(cmap_handle, "config.reload_in_progress", CMAP_TRACK_ADD | CMAP_TRACK_MODIFY, main_logging_notify, NULL, &cmap_track); } #endif int corosync_log_config_read ( #ifndef LOGCONFIG_USE_ICMAP cmap_handle_t cmap_h, const char *default_logfile, #endif const char **error_string) { const char *error_reason = error_string_response; #ifndef LOGCONFIG_USE_ICMAP if (!cmap_h) { error_reason = "No cmap handle"; return (-1); } if (!default_logfile) { error_reason = "No default logfile"; return (-1); } cmap_handle = cmap_h; main_logfile = default_logfile; #endif if (corosync_main_config_read_logging(error_string) < 0) { error_reason = *error_string; goto parse_error; } add_logsys_config_notification(); 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); } diff --git a/exec/logsys.c b/exec/logsys.c index 88da45e1..30a4ee60 100644 --- a/exec/logsys.c +++ b/exec/logsys.c @@ -1,935 +1,952 @@ /* * Copyright (c) 2002-2004 MontaVista Software, Inc. * Copyright (c) 2006-2012 Red Hat, Inc. * * Author: Steven Dake (sdake@redhat.com) * Author: Lon Hohberger (lhh@redhat.com) * Author: Fabio M. Di Nitto (fdinitto@redhat.com) * * All rights reserved. * * 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 /* * syslog prioritynames, facility names to value mapping * Some C libraries build this in to their headers, but it is non-portable * so logsys supplies its own version. */ struct syslog_names { const char *c_name; int c_val; }; static struct syslog_names prioritynames[] = { { "alert", LOG_ALERT }, { "crit", LOG_CRIT }, { "debug", LOG_DEBUG }, { "emerg", LOG_EMERG }, { "err", LOG_ERR }, { "error", LOG_ERR }, { "info", LOG_INFO }, { "notice", LOG_NOTICE }, { "warning", LOG_WARNING }, { NULL, -1 } }; #define MAX_FILES_PER_SUBSYS 32 #ifdef HAVE_SMALL_MEMORY_FOOTPRINT #define IPC_LOGSYS_SIZE 8192*64 #else #define IPC_LOGSYS_SIZE 8192*1024 #endif /* * need unlogical order to preserve 64bit alignment */ struct logsys_logger { char subsys[LOGSYS_MAX_SUBSYS_NAMELEN]; /* subsystem name */ char *logfile; /* log to file */ unsigned int mode; /* subsystem mode */ unsigned int debug; /* debug on|off|trace */ int syslog_priority; /* priority */ int logfile_priority; /* priority to file */ int init_status; /* internal field to handle init queues for subsystems */ int32_t target_id; char *files[MAX_FILES_PER_SUBSYS]; int32_t file_idx; int32_t dirty; }; /* values for logsys_logger init_status */ #define LOGSYS_LOGGER_INIT_DONE 0 #define LOGSYS_LOGGER_NEEDS_INIT 1 static int logsys_system_needs_init = LOGSYS_LOGGER_NEEDS_INIT; static struct logsys_logger logsys_loggers[LOGSYS_MAX_SUBSYS_COUNT + 1]; static pthread_mutex_t logsys_config_mutex = PTHREAD_MUTEX_INITIALIZER; static int32_t _logsys_config_mode_set_unlocked(int32_t subsysid, uint32_t new_mode); static void _logsys_config_apply_per_file(int32_t s, const char *filename); static void _logsys_config_apply_per_subsys(int32_t s); static void _logsys_subsys_filename_add (int32_t s, const char *filename); static void logsys_file_format_get(char* file_format, int buf_len); static char *format_buffer=NULL; static int logsys_thread_started = 0; static int logsys_blackbox_enabled = 1; static int _logsys_config_subsys_get_unlocked (const char *subsys) { unsigned int i; if (!subsys) { return LOGSYS_MAX_SUBSYS_COUNT; } for (i = 0; i <= LOGSYS_MAX_SUBSYS_COUNT; i++) { if (strcmp (logsys_loggers[i].subsys, subsys) == 0) { return i; } } return (-1); } /* * we need a version that can work when somebody else is already * holding a config mutex lock or we will never get out of here */ static int logsys_config_file_set_unlocked ( int subsysid, const char **error_string, const char *file) { static char error_string_response[512]; int i; char file_format[128]; if (logsys_loggers[subsysid].target_id > 0) { int32_t f; for (f = 0; f < logsys_loggers[subsysid].file_idx; f++) { qb_log_filter_ctl(logsys_loggers[subsysid].target_id, QB_LOG_FILTER_REMOVE, QB_LOG_FILTER_FILE, logsys_loggers[subsysid].files[f], LOG_TRACE); } } logsys_loggers[subsysid].dirty = QB_TRUE; if (file == NULL) { return (0); } if (logsys_loggers[subsysid].target_id > 0 && logsys_loggers[subsysid].logfile != NULL && strcmp(file, logsys_loggers[subsysid].logfile) == 0) { return (0); } if (strlen(file) >= PATH_MAX) { snprintf (error_string_response, sizeof(error_string_response), "%s: logfile name exceed maximum system filename length", logsys_loggers[subsysid].subsys); *error_string = error_string_response; return (-1); } if (logsys_loggers[subsysid].logfile != NULL) { free(logsys_loggers[subsysid].logfile); logsys_loggers[subsysid].logfile = NULL; } logsys_loggers[subsysid].logfile = strdup(file); if (logsys_loggers[subsysid].logfile == NULL) { snprintf (error_string_response, sizeof(error_string_response), "Unable to allocate memory for logfile '%s'", file); *error_string = error_string_response; return (-1); } for (i = 0; i <= LOGSYS_MAX_SUBSYS_COUNT; i++) { if ((logsys_loggers[i].logfile != NULL) && (strcmp (logsys_loggers[i].logfile, file) == 0) && (i != subsysid)) { /* we have found another subsys with this config file * so add a filter */ logsys_loggers[subsysid].target_id = logsys_loggers[i].target_id; return (0); } } if (logsys_loggers[subsysid].target_id > 0) { int num_using_current = 0; for (i = 0; i <= LOGSYS_MAX_SUBSYS_COUNT; i++) { if (logsys_loggers[subsysid].target_id == logsys_loggers[i].target_id) { num_using_current++; } } if (num_using_current == 1) { /* no one else is using this close it */ qb_log_file_close(logsys_loggers[subsysid].target_id); } } logsys_loggers[subsysid].target_id = qb_log_file_open(file); if (logsys_loggers[subsysid].target_id < 0) { int err = -logsys_loggers[subsysid].target_id; char error_str[LOGSYS_MAX_PERROR_MSG_LEN]; const char *error_ptr; error_ptr = qb_strerror_r(err, error_str, sizeof(error_str)); free(logsys_loggers[subsysid].logfile); logsys_loggers[subsysid].logfile = NULL; snprintf (error_string_response, sizeof(error_string_response), "Can't open logfile '%s' for reason: %s (%d)", file, error_ptr, err); *error_string = error_string_response; return (-1); } logsys_file_format_get(file_format, 128); qb_log_format_set(logsys_loggers[subsysid].target_id, file_format); qb_log_ctl(logsys_loggers[subsysid].target_id, QB_LOG_CONF_ENABLED, (logsys_loggers[subsysid].mode & LOGSYS_MODE_OUTPUT_FILE)); if (logsys_thread_started) { qb_log_ctl(logsys_loggers[subsysid].target_id, QB_LOG_CONF_THREADED, QB_TRUE); } return (0); } static void logsys_subsys_init ( const char *subsys, int subsysid) { if (logsys_system_needs_init == LOGSYS_LOGGER_NEEDS_INIT) { logsys_loggers[subsysid].init_status = LOGSYS_LOGGER_NEEDS_INIT; } else { logsys_loggers[subsysid].mode = logsys_loggers[LOGSYS_MAX_SUBSYS_COUNT].mode; logsys_loggers[subsysid].debug = logsys_loggers[LOGSYS_MAX_SUBSYS_COUNT].debug; logsys_loggers[subsysid].syslog_priority = logsys_loggers[LOGSYS_MAX_SUBSYS_COUNT].syslog_priority; logsys_loggers[subsysid].logfile_priority = logsys_loggers[LOGSYS_MAX_SUBSYS_COUNT].logfile_priority; logsys_loggers[subsysid].init_status = LOGSYS_LOGGER_INIT_DONE; } strncpy (logsys_loggers[subsysid].subsys, subsys, sizeof (logsys_loggers[subsysid].subsys)); logsys_loggers[subsysid].subsys[ sizeof (logsys_loggers[subsysid].subsys) - 1] = '\0'; logsys_loggers[subsysid].file_idx = 0; } static const char *_logsys_tags_stringify(uint32_t tags) { if (tags == QB_LOG_TAG_LIBQB_MSG) { return "QB"; } else { return logsys_loggers[tags].subsys; } } void logsys_system_fini (void) { int i; int f; for (i = 0; i < LOGSYS_MAX_SUBSYS_COUNT; i++) { free(logsys_loggers[i].logfile); for (f = 0; f < logsys_loggers[i].file_idx; f++) { free(logsys_loggers[i].files[f]); } } qb_log_fini (); } /* * Internal API - exported */ int _logsys_system_setup( const char *mainsystem, unsigned int mode, int syslog_facility, int syslog_priority) { int i; int32_t fidx; char tempsubsys[LOGSYS_MAX_SUBSYS_NAMELEN]; if ((mainsystem == NULL) || (strlen(mainsystem) >= LOGSYS_MAX_SUBSYS_NAMELEN)) { return -1; } /* * Setup libqb as a subsys */ i = _logsys_subsys_create ("QB", "array.c,log.c,log_syslog.c,log_blackbox.c,log_format.c," "log_file.c,log_dcs.c,log_thread.c,ipc_shm.c,ipcs.c,ipc_us.c,loop.c," "loop_poll_epoll.c,loop_job.c,loop_poll_poll.c,loop_poll_kqueue.c," "loop_timerlist.c,loop_poll.c,ringbuffer.c,ringbuffer_helper.c,trie.c," "map.c,skiplist.c,rpl_sem.c,hdb.c,unix.c,hashtable.c,strlcpy.c,ipc_socket.c," "strchrnul.c,ipc_setup.c,strlcat.c"); if (i < 0) { return -1; } /* * name clash * _logsys_subsys_filename_add (i, "util.c"); */ /* * This file (logsys.c) is not exactly QB. We need tag for logsys.c if flightrecorder init * fails, and QB seems to be closest. */ _logsys_subsys_filename_add (i, "logsys.c"); i = LOGSYS_MAX_SUBSYS_COUNT; pthread_mutex_lock (&logsys_config_mutex); snprintf(logsys_loggers[i].subsys, LOGSYS_MAX_SUBSYS_NAMELEN, "%s", mainsystem); logsys_loggers[i].mode = mode; logsys_loggers[i].debug = LOGSYS_DEBUG_OFF; logsys_loggers[i].file_idx = 0; logsys_loggers[i].logfile_priority = syslog_priority; logsys_loggers[i].syslog_priority = syslog_priority; qb_log_init(mainsystem, syslog_facility, syslog_priority); if (logsys_loggers[i].mode & LOGSYS_MODE_OUTPUT_STDERR) { qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE); } else { qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_FALSE); } if (logsys_loggers[i].mode & LOGSYS_MODE_OUTPUT_SYSLOG) { qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_TRUE); } else { qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE); } qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_PRIORITY_BUMP, LOG_INFO - LOG_DEBUG); qb_log_filter_ctl(QB_LOG_BLACKBOX, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_TRACE); qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, IPC_LOGSYS_SIZE); qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_THREADED, QB_FALSE); /* * Blackbox is disabled at the init and enabled later based * on config (logging.blackbox) value. */ qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE); if (logsys_format_set(NULL) == -1) { pthread_mutex_unlock (&logsys_config_mutex); return -1; } qb_log_tags_stringify_fn_set(_logsys_tags_stringify); logsys_loggers[i].init_status = LOGSYS_LOGGER_INIT_DONE; logsys_system_needs_init = LOGSYS_LOGGER_INIT_DONE; for (i = 0; i < LOGSYS_MAX_SUBSYS_COUNT; i++) { if ((strcmp (logsys_loggers[i].subsys, "") != 0) && (logsys_loggers[i].init_status == LOGSYS_LOGGER_NEEDS_INIT)) { fidx = logsys_loggers[i].file_idx; strncpy (tempsubsys, logsys_loggers[i].subsys, sizeof (tempsubsys)); tempsubsys[sizeof (tempsubsys) - 1] = '\0'; logsys_subsys_init(tempsubsys, i); logsys_loggers[i].file_idx = fidx; _logsys_config_mode_set_unlocked(i, logsys_loggers[i].mode); _logsys_config_apply_per_subsys(i); } } pthread_mutex_unlock (&logsys_config_mutex); return (0); } static void _logsys_subsys_filename_add (int32_t s, const char *filename) { int i; if (filename == NULL) { return; } assert(logsys_loggers[s].file_idx < MAX_FILES_PER_SUBSYS); assert(logsys_loggers[s].file_idx >= 0); for (i = 0; i < logsys_loggers[s].file_idx; i++) { if (strcmp(logsys_loggers[s].files[i], filename) == 0) { return; } } logsys_loggers[s].files[logsys_loggers[s].file_idx++] = strdup(filename); if (logsys_system_needs_init == LOGSYS_LOGGER_INIT_DONE) { _logsys_config_apply_per_file(s, filename); } } int _logsys_subsys_create (const char *subsys, const char *filename) { int i; if ((subsys == NULL) || (strlen(subsys) >= LOGSYS_MAX_SUBSYS_NAMELEN)) { return -1; } pthread_mutex_lock (&logsys_config_mutex); i = _logsys_config_subsys_get_unlocked (subsys); if ((i > -1) && (i < LOGSYS_MAX_SUBSYS_COUNT)) { _logsys_subsys_filename_add(i, filename); pthread_mutex_unlock (&logsys_config_mutex); return i; } for (i = 0; i < LOGSYS_MAX_SUBSYS_COUNT; i++) { if (strcmp (logsys_loggers[i].subsys, "") == 0) { logsys_subsys_init(subsys, i); _logsys_subsys_filename_add(i, filename); break; } } if (i >= LOGSYS_MAX_SUBSYS_COUNT) { i = -1; } pthread_mutex_unlock (&logsys_config_mutex); return i; } int _logsys_config_subsys_get (const char *subsys) { unsigned int i; pthread_mutex_lock (&logsys_config_mutex); i = _logsys_config_subsys_get_unlocked (subsys); pthread_mutex_unlock (&logsys_config_mutex); return i; } static int32_t _logsys_config_mode_set_unlocked(int32_t subsysid, uint32_t new_mode) { if ( logsys_loggers[subsysid].mode == new_mode) { return 0; } if (logsys_loggers[subsysid].target_id > 0) { qb_log_ctl(logsys_loggers[subsysid].target_id, QB_LOG_CONF_ENABLED, (new_mode & LOGSYS_MODE_OUTPUT_FILE)); } if (subsysid == LOGSYS_MAX_SUBSYS_COUNT) { qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, (new_mode & LOGSYS_MODE_OUTPUT_STDERR)); qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, (new_mode & LOGSYS_MODE_OUTPUT_SYSLOG)); } logsys_loggers[subsysid].mode = new_mode; return 0; } int logsys_config_mode_set (const char *subsys, unsigned int mode) { int i; pthread_mutex_lock (&logsys_config_mutex); if (subsys != NULL) { i = _logsys_config_subsys_get_unlocked (subsys); if (i >= 0) { i = _logsys_config_mode_set_unlocked(i, mode); } } else { for (i = 0; i <= LOGSYS_MAX_SUBSYS_COUNT; i++) { _logsys_config_mode_set_unlocked(i, mode); } i = 0; } pthread_mutex_unlock (&logsys_config_mutex); return i; } unsigned int logsys_config_mode_get (const char *subsys) { int i; i = _logsys_config_subsys_get (subsys); if (i < 0) { return i; } return logsys_loggers[i].mode; } int logsys_config_file_set ( const char *subsys, const char **error_string, const char *file) { int i; int res; pthread_mutex_lock (&logsys_config_mutex); if (subsys != NULL) { i = _logsys_config_subsys_get_unlocked (subsys); if (i < 0) { res = i; } else { res = logsys_config_file_set_unlocked(i, error_string, file); } } else { for (i = 0; i <= LOGSYS_MAX_SUBSYS_COUNT; i++) { res = logsys_config_file_set_unlocked(i, error_string, file); if (res < 0) { break; } } } pthread_mutex_unlock (&logsys_config_mutex); return res; } static void logsys_file_format_get(char* file_format, int buf_len) { char *format_buffer_start; char *str_pos; file_format[0] = '\0'; format_buffer_start = format_buffer; if ((str_pos = strstr(format_buffer, "%t"))) { strcpy(file_format, "%t "); format_buffer_start = str_pos + 2; } if ((str_pos = strstr(format_buffer, "%T"))) { strcpy(file_format, "%T "); format_buffer_start = str_pos + 2; } strcat(file_format, "[%P] %H %N"); strncat(file_format, format_buffer_start, buf_len - strlen(file_format)); } int logsys_format_set (const char *format) { int i; int c; int w; int reminder; char syslog_format[128]; char file_format[128]; if (format_buffer) { free(format_buffer); format_buffer = NULL; } format_buffer = strdup(format ? format : "%7p [%6g] %b"); if (format_buffer == NULL) { return -1; } qb_log_format_set(QB_LOG_STDERR, format_buffer); logsys_file_format_get(file_format, 128); for (i = 0; i <= LOGSYS_MAX_SUBSYS_COUNT; i++) { if (logsys_loggers[i].target_id > 0) { qb_log_format_set(logsys_loggers[i].target_id, file_format); } } /* * This just goes through and remove %t, %T and %p from * the format string for syslog. */ w = 0; memset(syslog_format, '\0', sizeof(syslog_format)); for (c = 0; c < strlen(format_buffer); c++) { if (format_buffer[c] == '%') { reminder = c; for (c++; c < strlen(format_buffer); c++) { if (isdigit(format_buffer[c])) { continue; } if (format_buffer[c] == 't' || format_buffer[c] == 'p' || format_buffer[c] == 'T') { c++; } else { c = reminder; } break; } } syslog_format[w] = format_buffer[c]; w++; } qb_log_format_set(QB_LOG_SYSLOG, syslog_format); return 0; } char *logsys_format_get (void) { return format_buffer; } int logsys_config_syslog_facility_set ( const char *subsys, unsigned int facility) { return qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_FACILITY, facility); } int logsys_config_syslog_priority_set ( const char *subsys, unsigned int priority) { int i; pthread_mutex_lock (&logsys_config_mutex); if (subsys != NULL) { i = _logsys_config_subsys_get_unlocked (subsys); if (i >= 0) { logsys_loggers[i].syslog_priority = priority; logsys_loggers[i].dirty = QB_TRUE; i = 0; } } else { for (i = 0; i <= LOGSYS_MAX_SUBSYS_COUNT; i++) { logsys_loggers[i].syslog_priority = priority; logsys_loggers[i].dirty = QB_TRUE; } i = 0; } pthread_mutex_unlock (&logsys_config_mutex); return i; } int logsys_config_logfile_priority_set ( const char *subsys, unsigned int priority) { int i; pthread_mutex_lock (&logsys_config_mutex); if (subsys != NULL) { i = _logsys_config_subsys_get_unlocked (subsys); if (i >= 0) { logsys_loggers[i].logfile_priority = priority; logsys_loggers[i].dirty = QB_TRUE; i = 0; } } else { for (i = 0; i <= LOGSYS_MAX_SUBSYS_COUNT; i++) { logsys_loggers[i].logfile_priority = priority; logsys_loggers[i].dirty = QB_TRUE; } i = 0; } pthread_mutex_unlock (&logsys_config_mutex); return i; } static void _logsys_config_apply_per_file(int32_t s, const char *filename) { uint32_t syslog_priority = logsys_loggers[s].syslog_priority; uint32_t logfile_priority = logsys_loggers[s].logfile_priority; qb_log_filter_ctl(s, QB_LOG_TAG_SET, QB_LOG_FILTER_FILE, filename, LOG_TRACE); qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_REMOVE, QB_LOG_FILTER_FILE, filename, LOG_TRACE); qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_REMOVE, QB_LOG_FILTER_FILE, filename, LOG_TRACE); if (logsys_loggers[s].target_id > 0) { qb_log_filter_ctl(logsys_loggers[s].target_id, QB_LOG_FILTER_REMOVE, QB_LOG_FILTER_FILE, filename, LOG_TRACE); } if (logsys_loggers[s].debug != LOGSYS_DEBUG_OFF) { switch (logsys_loggers[s].debug) { case LOGSYS_DEBUG_ON: syslog_priority = LOG_DEBUG; logfile_priority = LOG_DEBUG; break; case LOGSYS_DEBUG_TRACE: syslog_priority = LOG_TRACE; logfile_priority = LOG_TRACE; break; default: assert(0); } } qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, filename, syslog_priority); qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, filename, logfile_priority); if (logsys_loggers[s].target_id > 0) { qb_log_filter_ctl(logsys_loggers[s].target_id, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, filename, logfile_priority); } } static void _logsys_config_apply_per_subsys(int32_t s) { int32_t f; for (f = 0; f < logsys_loggers[s].file_idx; f++) { _logsys_config_apply_per_file(s, logsys_loggers[s].files[f]); } if (logsys_loggers[s].target_id > 0) { qb_log_ctl(logsys_loggers[s].target_id, QB_LOG_CONF_ENABLED, (logsys_loggers[s].mode & LOGSYS_MODE_OUTPUT_FILE)); } logsys_loggers[s].dirty = QB_FALSE; } static void _logsys_config_apply_blackbox(void) { int blackbox_enable_res; blackbox_enable_res = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, logsys_blackbox_enabled); if (blackbox_enable_res < 0) { LOGSYS_PERROR (-blackbox_enable_res, LOGSYS_LEVEL_WARNING, "Unable to initialize log flight recorder. "\ "The most common cause of this error is " \ "not enough space on /dev/shm. Corosync will continue work, " \ "but blackbox will not be available"); } } void logsys_config_apply(void) { int32_t s; _logsys_config_apply_blackbox(); for (s = 0; s <= LOGSYS_MAX_SUBSYS_COUNT; s++) { if (strcmp(logsys_loggers[s].subsys, "") == 0) { continue; } _logsys_config_apply_per_subsys(s); } } +extern int logsys_config_debug_get ( + const char *subsys) +{ + int debug_level = logsys_loggers[0].debug; + int i; + + if (subsys != NULL) { + pthread_mutex_lock (&logsys_config_mutex); + i = _logsys_config_subsys_get_unlocked (subsys); + if (i >= 0) { + debug_level = logsys_loggers[i].debug; + } + pthread_mutex_unlock (&logsys_config_mutex); + } + return debug_level; +} + int logsys_config_debug_set ( const char *subsys, unsigned int debug) { int i; pthread_mutex_lock (&logsys_config_mutex); if (subsys != NULL) { i = _logsys_config_subsys_get_unlocked (subsys); if (i >= 0) { logsys_loggers[i].dirty = QB_TRUE; logsys_loggers[i].debug = debug; i = 0; } } else { for (i = 0; i <= LOGSYS_MAX_SUBSYS_COUNT; i++) { logsys_loggers[i].debug = debug; logsys_loggers[i].dirty = QB_TRUE; } i = 0; } pthread_mutex_unlock (&logsys_config_mutex); return i; } int logsys_priority_id_get (const char *name) { unsigned int i; for (i = 0; prioritynames[i].c_name != NULL; i++) { if (strcasecmp(name, prioritynames[i].c_name) == 0) { return (prioritynames[i].c_val); } } return (-1); } int logsys_thread_start (void) { int i; int err; err = qb_log_thread_start(); if (err != 0) { return (err); } qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_THREADED, QB_TRUE); for (i = 0; i <= LOGSYS_MAX_SUBSYS_COUNT; i++) { if (logsys_loggers[i].target_id > 0) { qb_log_ctl(logsys_loggers[i].target_id, QB_LOG_CONF_THREADED, QB_TRUE); } } logsys_thread_started = 1; return (0); } void logsys_blackbox_set(int enable) { pthread_mutex_lock (&logsys_config_mutex); logsys_blackbox_enabled = enable; pthread_mutex_unlock (&logsys_config_mutex); } /* * To set correct pid to qb blackbox filename after tty dettach (fork) we have to * close (this function) and (if needed) reopen blackbox (logsys_blackbox_postfork function). */ void logsys_blackbox_prefork(void) { (void)qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE); } void logsys_blackbox_postfork(void) { _logsys_config_apply_blackbox(); } cs_error_t logsys_reopen_log_files(void) { cs_error_t res; #ifdef HAVE_QB_LOG_FILE_REOPEN int i, j; int num_using_current; int32_t rc; res = CS_OK; pthread_mutex_lock (&logsys_config_mutex); for (i = 0; i <= LOGSYS_MAX_SUBSYS_COUNT; i++) { if (logsys_loggers[i].target_id <= 0 || logsys_loggers[i].logfile == NULL) { continue ; } num_using_current = 0; for (j = 0; j <= i; j++) { if (logsys_loggers[i].target_id == logsys_loggers[j].target_id) { num_using_current++; } } if (num_using_current == 1) { /* * First instance of target file. Reopen it. */ rc = qb_log_file_reopen(logsys_loggers[i].target_id, NULL); if (rc != 0) { LOGSYS_PERROR (-rc, LOGSYS_LEVEL_WARNING, "Unable to reopen log file %s", logsys_loggers[i].logfile); res = qb_to_cs_error(rc); } } } pthread_mutex_unlock (&logsys_config_mutex); #else res = CS_ERR_NOT_SUPPORTED; #endif return (res); } diff --git a/exec/totemknet.c b/exec/totemknet.c index 0782c018..6ad09b8e 100644 --- a/exec/totemknet.c +++ b/exec/totemknet.c @@ -1,2222 +1,2256 @@ /* * Copyright (c) 2016-2020 Red Hat, Inc. * * All rights reserved. * * Author: Christine Caulfield (ccaulfie@redhat.com) * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the MontaVista Software, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBNOZZLE #include #include #endif #include #include #include #include #include #include "totemknet.h" #include "main.h" #include "util.h" #include #include #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif #ifdef HAVE_LIBNOZZLE static int setup_nozzle(void *knet_context); #endif /* Should match that used by cfg */ #define CFG_INTERFACE_STATUS_MAX_LEN 512 struct totemknet_instance { struct crypto_instance *crypto_inst; qb_loop_t *poll_handle; knet_handle_t knet_handle; int link_mode; void *context; void (*totemknet_deliver_fn) ( void *context, const void *msg, unsigned int msg_len, const struct sockaddr_storage *system_from); void (*totemknet_iface_change_fn) ( void *context, const struct totem_ip_address *iface_address, unsigned int link_no); void (*totemknet_mtu_changed) ( void *context, int net_mtu); void (*totemknet_target_set_completed) (void *context); /* * Function and data used to log messages */ int totemknet_log_level_security; int totemknet_log_level_error; int totemknet_log_level_warning; int totemknet_log_level_notice; int totemknet_log_level_debug; int totemknet_subsys_id; int knet_subsys_id; void (*totemknet_log_printf) ( int level, int subsys, const char *function, const char *file, int line, const char *format, ...)__attribute__((format(printf, 6, 7))); void *knet_context; char iov_buffer[KNET_MAX_PACKET_SIZE]; char *link_status[INTERFACE_MAX]; struct totem_ip_address my_ids[INTERFACE_MAX]; uint16_t ip_port[INTERFACE_MAX]; int our_nodeid; int loopback_link; struct totem_config *totem_config; struct totem_ip_address token_target; qb_loop_timer_handle timer_netif_check_timeout; qb_loop_timer_handle timer_merge_detect_timeout; int send_merge_detect_message; unsigned int merge_detect_messages_sent_before_timeout; int logpipes[2]; int knet_fd; pthread_mutex_t log_mutex; #ifdef HAVE_LIBNOZZLE char *nozzle_name; char *nozzle_ipaddr; char *nozzle_prefix; char *nozzle_macaddr; nozzle_t nozzle_handle; #endif }; /* Awkward. But needed to get stats from knet */ struct totemknet_instance *global_instance; struct work_item { const void *msg; unsigned int msg_len; struct totemknet_instance *instance; }; int totemknet_member_list_rebind_ip ( void *knet_context); static int totemknet_configure_compression ( struct totemknet_instance *instance, struct totem_config *totem_config); static void totemknet_start_merge_detect_timeout( void *knet_context); static void totemknet_stop_merge_detect_timeout( void *knet_context); static void log_flush_messages ( void *knet_context); static void totemknet_instance_initialize (struct totemknet_instance *instance) { int res; memset (instance, 0, sizeof (struct totemknet_instance)); res = pthread_mutex_init(&instance->log_mutex, NULL); /* * There is not too much else what can be done. */ assert(res == 0); } #define knet_log_printf_lock(level, subsys, function, file, line, format, args...) \ do { \ (void)pthread_mutex_lock(&instance->log_mutex); \ instance->totemknet_log_printf ( \ level, subsys, function, file, line, \ (const char *)format, ##args); \ (void)pthread_mutex_unlock(&instance->log_mutex); \ } while (0); #define knet_log_printf(level, format, args...) \ do { \ knet_log_printf_lock ( \ level, instance->totemknet_subsys_id, \ __FUNCTION__, __FILE__, __LINE__, \ (const char *)format, ##args); \ } while (0); #define libknet_log_printf(level, format, args...) \ do { \ knet_log_printf_lock ( \ level, instance->knet_subsys_id, \ __FUNCTION__, "libknet.h", __LINE__, \ (const char *)format, ##args); \ } while (0); #define KNET_LOGSYS_PERROR(err_num, level, fmt, args...) \ do { \ char _error_str[LOGSYS_MAX_PERROR_MSG_LEN]; \ const char *_error_ptr = qb_strerror_r(err_num, _error_str, sizeof(_error_str)); \ instance->totemknet_log_printf ( \ level, instance->totemknet_subsys_id, \ __FUNCTION__, __FILE__, __LINE__, \ fmt ": %s (%d)", ##args, _error_ptr, err_num); \ } while(0) #ifdef HAVE_LIBNOZZLE static inline int is_ether_addr_multicast(const uint8_t *addr) { return (addr[0] & 0x01); } static inline int is_ether_addr_zero(const uint8_t *addr) { return (!addr[0] && !addr[1] && !addr[2] && !addr[3] && !addr[4] && !addr[5]); } static int ether_host_filter_fn(void *private_data, const unsigned char *outdata, ssize_t outdata_len, uint8_t tx_rx, knet_node_id_t this_host_id, knet_node_id_t src_host_id, int8_t *channel, knet_node_id_t *dst_host_ids, size_t *dst_host_ids_entries) { struct ether_header *eth_h = (struct ether_header *)outdata; uint8_t *dst_mac = (uint8_t *)eth_h->ether_dhost; uint16_t dst_host_id; if (is_ether_addr_zero(dst_mac)) return -1; if (is_ether_addr_multicast(dst_mac)) { return 1; } memmove(&dst_host_id, &dst_mac[4], 2); dst_host_ids[0] = ntohs(dst_host_id); *dst_host_ids_entries = 1; return 0; } #endif static int dst_host_filter_callback_fn(void *private_data, const unsigned char *outdata, ssize_t outdata_len, uint8_t tx_rx, knet_node_id_t this_host_id, knet_node_id_t src_host_id, int8_t *channel, knet_node_id_t *dst_host_ids, size_t *dst_host_ids_entries) { struct totem_message_header *header = (struct totem_message_header *)outdata; int res; #ifdef HAVE_LIBNOZZLE if (*channel != 0) { return ether_host_filter_fn(private_data, outdata, outdata_len, tx_rx, this_host_id, src_host_id, channel, dst_host_ids, dst_host_ids_entries); } #endif if (header->target_nodeid) { dst_host_ids[0] = header->target_nodeid; *dst_host_ids_entries = 1; res = 0; /* unicast message */ } else { *dst_host_ids_entries = 0; res = 1; /* multicast message */ } return res; } static void socket_error_callback_fn(void *private_data, int datafd, int8_t channel, uint8_t tx_rx, int error, int errorno) { struct totemknet_instance *instance = (struct totemknet_instance *)private_data; knet_log_printf (LOGSYS_LEVEL_DEBUG, "Knet socket ERROR notification called: txrx=%d, error=%d, errorno=%d", tx_rx, error, errorno); if ((error == -1 && errorno != EAGAIN) || (error == 0)) { knet_handle_remove_datafd(instance->knet_handle, datafd); } } static void host_change_callback_fn(void *private_data, knet_node_id_t host_id, uint8_t reachable, uint8_t remote, uint8_t external) { struct totemknet_instance *instance = (struct totemknet_instance *)private_data; // TODO: what? if anything. knet_log_printf (LOGSYS_LEVEL_DEBUG, "Knet host change callback. nodeid: " CS_PRI_NODE_ID " reachable: %d", host_id, reachable); } static void pmtu_change_callback_fn(void *private_data, unsigned int data_mtu) { struct totemknet_instance *instance = (struct totemknet_instance *)private_data; knet_log_printf (LOGSYS_LEVEL_DEBUG, "Knet pMTU change: %d", data_mtu); /* We don't need to tell corosync the actual knet MTU */ // instance->totemknet_mtu_changed(instance->context, data_mtu); } int totemknet_crypto_set ( void *knet_context, const char *cipher_type, const char *hash_type) { return (0); } static inline void ucast_sendmsg ( struct totemknet_instance *instance, struct totem_ip_address *system_to, const void *msg, unsigned int msg_len) { int res = 0; struct totem_message_header *header = (struct totem_message_header *)msg; struct msghdr msg_ucast; struct iovec iovec; header->target_nodeid = system_to->nodeid; iovec.iov_base = (void *)msg; iovec.iov_len = msg_len; /* * Build unicast message */ memset(&msg_ucast, 0, sizeof(msg_ucast)); msg_ucast.msg_iov = (void *)&iovec; msg_ucast.msg_iovlen = 1; #ifdef HAVE_MSGHDR_CONTROL msg_ucast.msg_control = 0; #endif #ifdef HAVE_MSGHDR_CONTROLLEN msg_ucast.msg_controllen = 0; #endif #ifdef HAVE_MSGHDR_FLAGS msg_ucast.msg_flags = 0; #endif #ifdef HAVE_MSGHDR_ACCRIGHTS msg_ucast.msg_accrights = NULL; #endif #ifdef HAVE_MSGHDR_ACCRIGHTSLEN msg_ucast.msg_accrightslen = 0; #endif /* * Transmit unicast message * An error here is recovered by totemsrp */ res = sendmsg (instance->knet_fd, &msg_ucast, MSG_NOSIGNAL); if (res < 0) { KNET_LOGSYS_PERROR (errno, instance->totemknet_log_level_debug, "sendmsg(ucast) failed (non-critical)"); } } static inline void mcast_sendmsg ( struct totemknet_instance *instance, const void *msg, unsigned int msg_len, int only_active) { int res; struct totem_message_header *header = (struct totem_message_header *)msg; struct msghdr msg_mcast; struct iovec iovec; iovec.iov_base = (void *)msg; iovec.iov_len = msg_len; header->target_nodeid = 0; /* * Build multicast message */ memset(&msg_mcast, 0, sizeof(msg_mcast)); msg_mcast.msg_iov = (void *)&iovec; msg_mcast.msg_iovlen = 1; #ifdef HAVE_MSGHDR_CONTROL msg_mcast.msg_control = 0; #endif #ifdef HAVE_MSGHDR_CONTROLLEN msg_mcast.msg_controllen = 0; #endif #ifdef HAVE_MSGHDR_FLAGS msg_mcast.msg_flags = 0; #endif #ifdef HAVE_MSGHDR_ACCRIGHTS msg_mcast.msg_accrights = NULL; #endif #ifdef HAVE_MSGHDR_ACCRIGHTSLEN msg_mcast.msg_accrightslen = 0; #endif // log_printf (LOGSYS_LEVEL_DEBUG, "totemknet: mcast_sendmsg. only_active=%d, len=%d", only_active, msg_len); res = sendmsg (instance->knet_fd, &msg_mcast, MSG_NOSIGNAL); if (res < msg_len) { knet_log_printf (LOGSYS_LEVEL_DEBUG, "totemknet: mcast_send sendmsg returned %d", res); } if (!only_active || instance->send_merge_detect_message) { /* * Current message was sent to all nodes */ instance->merge_detect_messages_sent_before_timeout++; instance->send_merge_detect_message = 0; } } static int node_compare(const void *aptr, const void *bptr) { uint16_t a,b; a = *(uint16_t *)aptr; b = *(uint16_t *)bptr; return a > b; } #ifndef OWN_INDEX_NONE #define OWN_INDEX_NONE -1 #endif int totemknet_nodestatus_get ( void *knet_context, unsigned int nodeid, struct totem_node_status *node_status) { int i; int res = 0; struct knet_link_status link_status; struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; struct knet_host_status knet_host_status; uint8_t link_list[KNET_MAX_LINK]; size_t num_links; if (!instance->knet_handle) { return CS_ERR_NOT_EXIST; /* Not using knet */ } if (!node_status) { return CS_ERR_INVALID_PARAM; } res = knet_host_get_status(instance->knet_handle, nodeid, &knet_host_status); if (res) { knet_log_printf (LOGSYS_LEVEL_WARNING, "knet_handle_get_host_status(%d) failed: %d", nodeid, res); return (-1); } node_status->nodeid = nodeid; node_status->reachable = knet_host_status.reachable; node_status->remote = knet_host_status.remote; node_status->external = knet_host_status.external; #ifdef HAVE_KNET_ONWIRE_VER res = knet_handle_get_onwire_ver(instance->knet_handle, nodeid, &node_status->onwire_min, &node_status->onwire_max, &node_status->onwire_ver); if (res) { knet_log_printf (LOGSYS_LEVEL_WARNING, "knet_handle_get_onwire_ver(%d) failed: %d", nodeid, res); return (-1); } #endif /* Get link info */ res = knet_link_get_link_list(instance->knet_handle, nodeid, link_list, &num_links); if (res) { knet_log_printf (LOGSYS_LEVEL_WARNING, "knet_link_get_link_list(%d) failed: %d", nodeid, res); return (-1); } /* node_status[] has been zeroed for us in totempg.c */ for (i=0; i < num_links; i++) { if (!instance->totem_config->interfaces[link_list[i]].configured) { continue; } res = knet_link_get_status(instance->knet_handle, nodeid, link_list[i], &link_status, sizeof(link_status)); if (res == 0) { node_status->link_status[link_list[i]].enabled = link_status.enabled; node_status->link_status[link_list[i]].connected = link_status.connected; node_status->link_status[link_list[i]].dynconnected = link_status.dynconnected; node_status->link_status[link_list[i]].mtu = link_status.mtu; memcpy(node_status->link_status[link_list[i]].src_ipaddr, link_status.src_ipaddr, KNET_MAX_HOST_LEN); memcpy(node_status->link_status[link_list[i]].dst_ipaddr, link_status.dst_ipaddr, KNET_MAX_HOST_LEN); } else { knet_log_printf (LOGSYS_LEVEL_WARNING, "knet_link_get_link_status(%d, %d) failed: %d", nodeid, link_list[i], res); } } return res; } int totemknet_ifaces_get (void *knet_context, char ***status, unsigned int *iface_count) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; struct knet_link_status link_status; knet_node_id_t host_list[KNET_MAX_HOST]; uint8_t link_list[KNET_MAX_LINK]; size_t num_hosts; size_t num_links; size_t link_idx; int i,j; char *ptr; int res = 0; /* * Don't do the whole 'link_info' bit if the caller just wants * a count of interfaces. */ if (status) { int own_idx = OWN_INDEX_NONE; res = knet_host_get_host_list(instance->knet_handle, host_list, &num_hosts); if (res) { return (-1); } qsort(host_list, num_hosts, sizeof(uint16_t), node_compare); for (j=0; jour_nodeid) { own_idx = j; break; } } for (i=0; ilink_status[i], 'd', CFG_INTERFACE_STATUS_MAX_LEN-1); if (own_idx != OWN_INDEX_NONE) { instance->link_status[i][own_idx] = 'n'; } instance->link_status[i][num_hosts] = '\0'; } /* This is all a bit "inside-out" because "status" is a set of strings per link * and knet orders things by host */ for (j=0; jknet_handle, host_list[j], link_list, &num_links); if (res) { return (-1); } link_idx = 0; for (i=0; i < num_links; i++) { /* * Skip over links that are unconfigured to corosync. This is basically * link0 if corosync isn't using it for comms, as we will still * have it set up for loopback. */ if (!instance->totem_config->interfaces[link_list[i]].configured) { continue; } ptr = instance->link_status[link_idx++]; res = knet_link_get_status(instance->knet_handle, host_list[j], link_list[i], &link_status, sizeof(link_status)); if (res == 0) { ptr[j] = '0' + (link_status.enabled | link_status.connected<<1 | link_status.dynconnected<<2); } else { knet_log_printf (LOGSYS_LEVEL_ERROR, "totemknet_ifaces_get: Cannot get link status: %s", strerror(errno)); ptr[j] = '?'; } } } *status = instance->link_status; } *iface_count = INTERFACE_MAX; return (res); } int totemknet_finalize ( void *knet_context) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int res = 0; int i,j; static knet_node_id_t nodes[KNET_MAX_HOST]; /* static to save stack */ uint8_t links[KNET_MAX_LINK]; size_t num_nodes; size_t num_links; knet_log_printf(LOG_DEBUG, "totemknet: finalize"); qb_loop_poll_del (instance->poll_handle, instance->logpipes[0]); qb_loop_poll_del (instance->poll_handle, instance->knet_fd); /* * Disable forwarding to make knet flush send queue. This ensures that the LEAVE message will be sent. */ res = knet_handle_setfwd(instance->knet_handle, 0); if (res) { knet_log_printf (LOGSYS_LEVEL_CRIT, "totemknet: knet_handle_setfwd failed: %s", strerror(errno)); } res = knet_host_get_host_list(instance->knet_handle, nodes, &num_nodes); if (res) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Cannot get knet node list for shutdown: %s", strerror(errno)); /* Crash out anyway */ goto finalise_error; } /* Tidily shut down all nodes & links. */ for (i=0; iknet_handle, nodes[i], links, &num_links); if (res) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Cannot get knet link list for node " CS_PRI_NODE_ID ": %s", nodes[i], strerror(errno)); goto finalise_error; } for (j=0; jknet_handle, nodes[i], links[j], 0); if (res) { knet_log_printf (LOGSYS_LEVEL_ERROR, "totemknet: knet_link_set_enable(node " CS_PRI_NODE_ID ", link %d) failed: %s", nodes[i], links[j], strerror(errno)); } res = knet_link_clear_config(instance->knet_handle, nodes[i], links[j]); if (res) { knet_log_printf (LOGSYS_LEVEL_ERROR, "totemknet: knet_link_clear_config(node " CS_PRI_NODE_ID ", link %d) failed: %s", nodes[i], links[j], strerror(errno)); } } res = knet_host_remove(instance->knet_handle, nodes[i]); if (res) { knet_log_printf (LOGSYS_LEVEL_ERROR, "totemknet: knet_host_remove(node " CS_PRI_NODE_ID ") failed: %s", nodes[i], strerror(errno)); } } finalise_error: res = knet_handle_free(instance->knet_handle); if (res) { knet_log_printf (LOGSYS_LEVEL_CRIT, "totemknet: knet_handle_free failed: %s", strerror(errno)); } totemknet_stop_merge_detect_timeout(instance); log_flush_messages(instance); /* * Error is deliberately ignored */ (void)pthread_mutex_destroy(&instance->log_mutex); return (res); } static int log_deliver_fn ( int fd, int revents, void *data) { struct totemknet_instance *instance = (struct totemknet_instance *)data; char buffer[sizeof(struct knet_log_msg)*4]; char *bufptr = buffer; int done = 0; int len; len = read(fd, buffer, sizeof(buffer)); while (done < len) { struct knet_log_msg *msg = (struct knet_log_msg *)bufptr; switch (msg->msglevel) { case KNET_LOG_ERR: libknet_log_printf (LOGSYS_LEVEL_ERROR, "%s: %s", knet_log_get_subsystem_name(msg->subsystem), msg->msg); break; case KNET_LOG_WARN: libknet_log_printf (LOGSYS_LEVEL_WARNING, "%s: %s", knet_log_get_subsystem_name(msg->subsystem), msg->msg); break; case KNET_LOG_INFO: libknet_log_printf (LOGSYS_LEVEL_INFO, "%s: %s", knet_log_get_subsystem_name(msg->subsystem), msg->msg); break; case KNET_LOG_DEBUG: libknet_log_printf (LOGSYS_LEVEL_DEBUG, "%s: %s", knet_log_get_subsystem_name(msg->subsystem), msg->msg); break; } bufptr += sizeof(struct knet_log_msg); done += sizeof(struct knet_log_msg); } return 0; } static int data_deliver_fn ( int fd, int revents, void *data) { struct totemknet_instance *instance = (struct totemknet_instance *)data; struct msghdr msg_hdr; struct iovec iov_recv; struct sockaddr_storage system_from; ssize_t msg_len; int truncated_packet; iov_recv.iov_base = instance->iov_buffer; iov_recv.iov_len = KNET_MAX_PACKET_SIZE; msg_hdr.msg_name = &system_from; msg_hdr.msg_namelen = sizeof (struct sockaddr_storage); msg_hdr.msg_iov = &iov_recv; msg_hdr.msg_iovlen = 1; #ifdef HAVE_MSGHDR_CONTROL msg_hdr.msg_control = 0; #endif #ifdef HAVE_MSGHDR_CONTROLLEN msg_hdr.msg_controllen = 0; #endif #ifdef HAVE_MSGHDR_FLAGS msg_hdr.msg_flags = 0; #endif #ifdef HAVE_MSGHDR_ACCRIGHTS msg_hdr.msg_accrights = NULL; #endif #ifdef HAVE_MSGHDR_ACCRIGHTSLEN msg_hdr.msg_accrightslen = 0; #endif msg_len = recvmsg (fd, &msg_hdr, MSG_NOSIGNAL | MSG_DONTWAIT); if (msg_len <= 0) { return (0); } truncated_packet = 0; #ifdef HAVE_MSGHDR_FLAGS if (msg_hdr.msg_flags & MSG_TRUNC) { truncated_packet = 1; } #else /* * We don't have MSGHDR_FLAGS, but we can (hopefully) safely make assumption that * if bytes_received == KNET_MAX_PACKET_SIZE then packet is truncated */ if (bytes_received == KNET_MAX_PACKET_SIZE) { truncated_packet = 1; } #endif if (truncated_packet) { knet_log_printf(instance->totemknet_log_level_error, "Received too big message. This may be because something bad is happening" "on the network (attack?), or you tried join more nodes than corosync is" "compiled with (%u) or bug in the code (bad estimation of " "the KNET_MAX_PACKET_SIZE). Dropping packet.", PROCESSOR_COUNT_MAX); return (0); } /* * Handle incoming message */ instance->totemknet_deliver_fn ( instance->context, instance->iov_buffer, msg_len, &system_from); return (0); } static void timer_function_netif_check_timeout ( void *data) { struct totemknet_instance *instance = (struct totemknet_instance *)data; int i; for (i=0; i < INTERFACE_MAX; i++) { if (!instance->totem_config->interfaces[i].configured) { continue; } instance->totemknet_iface_change_fn (instance->context, &instance->my_ids[i], i); } } static void knet_set_access_list_config(struct totemknet_instance *instance) { #ifdef HAVE_KNET_ACCESS_LIST uint32_t value; cs_error_t err; value = instance->totem_config->block_unlisted_ips; knet_log_printf (LOGSYS_LEVEL_DEBUG, "knet_enable access list: %d", value); err = knet_handle_enable_access_lists(instance->knet_handle, value); if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_WARNING, "knet_handle_enable_access_lists failed"); } #endif } +void totemknet_configure_log_level() +{ + int logsys_log_mode; + int knet_log_mode = KNET_LOG_INFO; + uint8_t s; + + if (!global_instance || !global_instance->knet_handle) { + return; + } + + /* Reconfigure logging level */ + logsys_log_mode = logsys_config_debug_get("KNET"); + + switch (logsys_log_mode) { + case LOGSYS_DEBUG_OFF: + knet_log_mode = KNET_LOG_INFO; + break; + case LOGSYS_DEBUG_ON: + knet_log_mode = KNET_LOG_DEBUG; + break; + case LOGSYS_DEBUG_TRACE: + knet_log_mode = KNET_LOG_DEBUG; + break; + } + log_printf (LOGSYS_LEVEL_DEBUG, "totemknet setting log level %s", knet_log_get_loglevel_name(knet_log_mode)); + for (s = 0; sknet_handle, s, knet_log_mode); + } +} + /* NOTE: this relies on the fact that totem_reload_notify() is called first */ static void totemknet_refresh_config( int32_t event, const char *key_name, struct icmap_notify_value new_val, struct icmap_notify_value old_val, void *user_data) { uint8_t reloading; uint32_t value; uint32_t link_no; size_t num_nodes; knet_node_id_t host_ids[KNET_MAX_HOST]; int i; int err; struct totemknet_instance *instance = (struct totemknet_instance *)user_data; ENTER(); /* * If a full reload is in progress then don't do anything until it's done and * can reconfigure it all atomically */ if (icmap_get_uint8("config.totemconfig_reload_in_progress", &reloading) == CS_OK && reloading) { return; } knet_set_access_list_config(instance); if (icmap_get_uint32("totem.knet_pmtud_interval", &value) == CS_OK) { instance->totem_config->knet_pmtud_interval = value; knet_log_printf (LOGSYS_LEVEL_DEBUG, "knet_pmtud_interval now %d", value); err = knet_handle_pmtud_setfreq(instance->knet_handle, instance->totem_config->knet_pmtud_interval); if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_WARNING, "knet_handle_pmtud_setfreq failed"); } } /* Configure link parameters for each node */ err = knet_host_get_host_list(instance->knet_handle, host_ids, &num_nodes); if (err != 0) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_host_get_host_list failed"); } for (i=0; iour_nodeid || !instance->totem_config->interfaces[link_no].configured) { continue; } err = knet_link_set_ping_timers(instance->knet_handle, host_ids[i], link_no, instance->totem_config->interfaces[link_no].knet_ping_interval, instance->totem_config->interfaces[link_no].knet_ping_timeout, instance->totem_config->interfaces[link_no].knet_ping_precision); if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_link_set_ping_timers for node " CS_PRI_NODE_ID " link %d failed", host_ids[i], link_no); } err = knet_link_set_pong_count(instance->knet_handle, host_ids[i], link_no, instance->totem_config->interfaces[link_no].knet_pong_count); if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_link_set_pong_count for node " CS_PRI_NODE_ID " link %d failed",host_ids[i], link_no); } err = knet_link_set_priority(instance->knet_handle, host_ids[i], link_no, instance->totem_config->interfaces[link_no].knet_link_priority); if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_link_set_priority for node " CS_PRI_NODE_ID " link %d failed", host_ids[i], link_no); } } } + /* Log levels get reconfigured from logconfig.c as that happens last in the reload */ LEAVE(); } static void totemknet_add_config_notifications(struct totemknet_instance *instance) { icmap_track_t icmap_track_totem = NULL; icmap_track_t icmap_track_reload = NULL; ENTER(); icmap_track_add("totem.", ICMAP_TRACK_ADD | ICMAP_TRACK_DELETE | ICMAP_TRACK_MODIFY | ICMAP_TRACK_PREFIX, totemknet_refresh_config, instance, &icmap_track_totem); icmap_track_add("config.totemconfig_reload_in_progress", ICMAP_TRACK_ADD | ICMAP_TRACK_MODIFY, totemknet_refresh_config, instance, &icmap_track_reload); LEAVE(); } static int totemknet_is_crypto_enabled(const struct totemknet_instance *instance) { return (!(strcmp(instance->totem_config->crypto_cipher_type, "none") == 0 && strcmp(instance->totem_config->crypto_hash_type, "none") == 0)); } static int totemknet_set_knet_crypto(struct totemknet_instance *instance) { struct knet_handle_crypto_cfg crypto_cfg; int res; /* These have already been validated */ memcpy(crypto_cfg.crypto_model, instance->totem_config->crypto_model, sizeof(crypto_cfg.crypto_model)); memcpy(crypto_cfg.crypto_cipher_type, instance->totem_config->crypto_cipher_type, sizeof(crypto_cfg.crypto_model)); memcpy(crypto_cfg.crypto_hash_type, instance->totem_config->crypto_hash_type, sizeof(crypto_cfg.crypto_model)); memcpy(crypto_cfg.private_key, instance->totem_config->private_key, instance->totem_config->private_key_len); crypto_cfg.private_key_len = instance->totem_config->private_key_len; #ifdef HAVE_KNET_CRYPTO_RECONF knet_log_printf(LOGSYS_LEVEL_DEBUG, "Configuring crypto %s/%s/%s on index %d", crypto_cfg.crypto_model, crypto_cfg.crypto_cipher_type, crypto_cfg.crypto_hash_type, instance->totem_config->crypto_index ); /* If crypto is being disabled we need to explicitly allow cleartext traffic in knet */ if (!totemknet_is_crypto_enabled(instance)) { res = knet_handle_crypto_rx_clear_traffic(instance->knet_handle, KNET_CRYPTO_RX_ALLOW_CLEAR_TRAFFIC); if (res) { knet_log_printf(LOGSYS_LEVEL_ERROR, "knet_handle_crypto_rx_clear_traffic(ALLOW) failed %s", strerror(errno)); } } /* use_config will be called later when all nodes are synced */ res = knet_handle_crypto_set_config(instance->knet_handle, &crypto_cfg, instance->totem_config->crypto_index); if (res == -1) { knet_log_printf(LOGSYS_LEVEL_ERROR, "knet_handle_crypto_set_config (index %d) failed: %s", instance->totem_config->crypto_index, strerror(errno)); goto exit_error; } if (res == -2) { knet_log_printf(LOGSYS_LEVEL_ERROR, "knet_handle_crypto_set_config (index %d) failed: -2", instance->totem_config->crypto_index); goto exit_error; } #else knet_log_printf(LOGSYS_LEVEL_DEBUG, "Configuring crypto %s/%s/%s", crypto_cfg.crypto_model, crypto_cfg.crypto_cipher_type, crypto_cfg.crypto_hash_type ); res = knet_handle_crypto(instance->knet_handle, &crypto_cfg); if (res == -1) { knet_log_printf(LOGSYS_LEVEL_ERROR, "knet_handle_crypto failed: %s", strerror(errno)); goto exit_error; } if (res == -2) { knet_log_printf(LOGSYS_LEVEL_ERROR, "knet_handle_crypto failed: -2"); goto exit_error; } #endif exit_error: return res; } /* * Create an instance */ int totemknet_initialize ( qb_loop_t *poll_handle, void **knet_context, struct totem_config *totem_config, totemsrp_stats_t *stats, void *context, void (*deliver_fn) ( void *context, const void *msg, unsigned int msg_len, const struct sockaddr_storage *system_from), void (*iface_change_fn) ( void *context, const struct totem_ip_address *iface_address, unsigned int link_no), void (*mtu_changed) ( void *context, int net_mtu), void (*target_set_completed) ( void *context)) { struct totemknet_instance *instance; char *tmp_str; int8_t channel=0; int allow_knet_handle_fallback=0; int res; int i; instance = malloc (sizeof (struct totemknet_instance)); if (instance == NULL) { return (-1); } totemknet_instance_initialize (instance); instance->totem_config = totem_config; /* * Configure logging */ instance->totemknet_log_level_security = 1; //totem_config->totem_logging_configuration.log_level_security; instance->totemknet_log_level_error = totem_config->totem_logging_configuration.log_level_error; instance->totemknet_log_level_warning = totem_config->totem_logging_configuration.log_level_warning; instance->totemknet_log_level_notice = totem_config->totem_logging_configuration.log_level_notice; instance->totemknet_log_level_debug = totem_config->totem_logging_configuration.log_level_debug; instance->totemknet_subsys_id = totem_config->totem_logging_configuration.log_subsys_id; instance->totemknet_log_printf = totem_config->totem_logging_configuration.log_printf; instance->knet_subsys_id = _logsys_subsys_create("KNET", "libknet.h"); /* * Initialize local variables for totemknet */ instance->our_nodeid = instance->totem_config->node_id; for (i=0; i< INTERFACE_MAX; i++) { totemip_copy(&instance->my_ids[i], &totem_config->interfaces[i].bindnet); instance->my_ids[i].nodeid = instance->our_nodeid; instance->ip_port[i] = totem_config->interfaces[i].ip_port; /* Needed for totemsrp */ totem_config->interfaces[i].boundto.nodeid = instance->our_nodeid; } instance->poll_handle = poll_handle; instance->context = context; instance->totemknet_deliver_fn = deliver_fn; instance->totemknet_iface_change_fn = iface_change_fn; instance->totemknet_mtu_changed = mtu_changed; instance->totemknet_target_set_completed = target_set_completed; instance->loopback_link = 0; res = pipe(instance->logpipes); if (res == -1) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_CRIT, "failed to create pipe for instance->logpipes"); goto exit_error; } if (fcntl(instance->logpipes[0], F_SETFL, O_NONBLOCK) == -1 || fcntl(instance->logpipes[1], F_SETFL, O_NONBLOCK) == -1) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_CRIT, "failed to set O_NONBLOCK flag for instance->logpipes"); goto exit_error; } if (icmap_get_string("system.allow_knet_handle_fallback", &tmp_str) == CS_OK) { if (strcmp(tmp_str, "yes") == 0) { allow_knet_handle_fallback = 1; } free(tmp_str); } #if defined(KNET_API_VER) && (KNET_API_VER == 2) instance->knet_handle = knet_handle_new(instance->totem_config->node_id, instance->logpipes[1], KNET_LOG_DEBUG, KNET_HANDLE_FLAG_PRIVILEGED); #else instance->knet_handle = knet_handle_new(instance->totem_config->node_id, instance->logpipes[1], KNET_LOG_DEBUG); #endif if (allow_knet_handle_fallback && !instance->knet_handle && errno == ENAMETOOLONG) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_WARNING, "knet_handle_new failed, trying unprivileged"); #if defined(KNET_API_VER) && (KNET_API_VER == 2) instance->knet_handle = knet_handle_new(instance->totem_config->node_id, instance->logpipes[1], KNET_LOG_DEBUG, 0); #else instance->knet_handle = knet_handle_new_ex(instance->totem_config->node_id, instance->logpipes[1], KNET_LOG_DEBUG, 0); #endif } if (!instance->knet_handle) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_CRIT, "knet_handle_new failed"); goto exit_error; } knet_set_access_list_config(instance); res = knet_handle_pmtud_setfreq(instance->knet_handle, instance->totem_config->knet_pmtud_interval); if (res) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_WARNING, "knet_handle_pmtud_setfreq failed"); } res = knet_handle_enable_filter(instance->knet_handle, instance, dst_host_filter_callback_fn); if (res) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_WARNING, "knet_handle_enable_filter failed"); } res = knet_handle_enable_sock_notify(instance->knet_handle, instance, socket_error_callback_fn); if (res) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_WARNING, "knet_handle_enable_sock_notify failed"); } res = knet_host_enable_status_change_notify(instance->knet_handle, instance, host_change_callback_fn); if (res) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_WARNING, "knet_host_enable_status_change_notify failed"); } res = knet_handle_enable_pmtud_notify(instance->knet_handle, instance, pmtu_change_callback_fn); if (res) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_WARNING, "knet_handle_enable_pmtud_notify failed"); } global_instance = instance; + /* Setup knet logging level */ + totemknet_configure_log_level(); + /* Get an fd into knet */ instance->knet_fd = 0; res = knet_handle_add_datafd(instance->knet_handle, &instance->knet_fd, &channel); if (res) { knet_log_printf(LOG_DEBUG, "knet_handle_add_datafd failed: %s", strerror(errno)); goto exit_error; } /* Enable crypto if requested */ #ifdef HAVE_KNET_CRYPTO_RECONF if (totemknet_is_crypto_enabled(instance)) { res = totemknet_set_knet_crypto(instance); if (res == 0) { res = knet_handle_crypto_use_config(instance->knet_handle, totem_config->crypto_index); if (res) { knet_log_printf(LOG_DEBUG, "knet_handle_crypto_use_config failed: %s", strerror(errno)); goto exit_error; } } else { knet_log_printf(LOG_DEBUG, "Failed to set up knet crypto"); goto exit_error; } res = knet_handle_crypto_rx_clear_traffic(instance->knet_handle, KNET_CRYPTO_RX_DISALLOW_CLEAR_TRAFFIC); if (res) { knet_log_printf(LOG_DEBUG, "knet_handle_crypto_rx_clear_traffic (DISALLOW) failed: %s", strerror(errno)); goto exit_error; } } else { res = knet_handle_crypto_rx_clear_traffic(instance->knet_handle, KNET_CRYPTO_RX_ALLOW_CLEAR_TRAFFIC); if (res) { knet_log_printf(LOG_DEBUG, "knet_handle_crypto_rx_clear_traffic (ALLOW) failed: %s", strerror(errno)); goto exit_error; } } #else if (totemknet_is_crypto_enabled(instance)) { res = totemknet_set_knet_crypto(instance); if (res) { knet_log_printf(LOG_DEBUG, "Failed to set up knet crypto"); goto exit_error; } } #endif /* Set up compression */ if (strcmp(totem_config->knet_compression_model, "none") != 0) { /* Not fatal, but will log */ (void)totemknet_configure_compression(instance, totem_config); } knet_handle_setfwd(instance->knet_handle, 1); instance->link_mode = KNET_LINK_POLICY_PASSIVE; if (strcmp(instance->totem_config->link_mode, "active")==0) { instance->link_mode = KNET_LINK_POLICY_ACTIVE; } if (strcmp(instance->totem_config->link_mode, "rr")==0) { instance->link_mode = KNET_LINK_POLICY_RR; } for (i=0; ilink_status[i] = malloc(CFG_INTERFACE_STATUS_MAX_LEN); if (!instance->link_status[i]) { goto exit_error; } } qb_loop_poll_add (instance->poll_handle, QB_LOOP_MED, instance->logpipes[0], POLLIN, instance, log_deliver_fn); qb_loop_poll_add (instance->poll_handle, QB_LOOP_HIGH, instance->knet_fd, POLLIN, instance, data_deliver_fn); /* * Upper layer isn't ready to receive message because it hasn't * initialized yet. Add short timer to check the interfaces. */ qb_loop_timer_add (instance->poll_handle, QB_LOOP_MED, 100*QB_TIME_NS_IN_MSEC, (void *)instance, timer_function_netif_check_timeout, &instance->timer_netif_check_timeout); totemknet_start_merge_detect_timeout(instance); /* Start listening for config changes */ totemknet_add_config_notifications(instance); /* Add stats keys to icmap */ stats_knet_add_handle(); knet_log_printf (LOGSYS_LEVEL_INFO, "totemknet initialized"); *knet_context = instance; return (0); exit_error: log_flush_messages(instance); free(instance); return (-1); } void *totemknet_buffer_alloc (void) { /* Need to have space for a message AND a struct mcast in case of encapsulated messages */ return malloc(KNET_MAX_PACKET_SIZE + 512); } void totemknet_buffer_release (void *ptr) { return free (ptr); } int totemknet_processor_count_set ( void *knet_context, int processor_count) { return (0); } int totemknet_recv_flush (void *knet_context) { return (0); } int totemknet_send_flush (void *knet_context) { return (0); } int totemknet_token_send ( void *knet_context, const void *msg, unsigned int msg_len) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int res = 0; ucast_sendmsg (instance, &instance->token_target, msg, msg_len); return (res); } int totemknet_mcast_flush_send ( void *knet_context, const void *msg, unsigned int msg_len) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int res = 0; mcast_sendmsg (instance, msg, msg_len, 0); return (res); } int totemknet_mcast_noflush_send ( void *knet_context, const void *msg, unsigned int msg_len) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int res = 0; mcast_sendmsg (instance, msg, msg_len, 1); return (res); } extern int totemknet_iface_check (void *knet_context) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int res = 0; knet_log_printf(LOG_DEBUG, "totemknet: iface_check"); return (res); } extern void totemknet_net_mtu_adjust (void *knet_context, struct totem_config *totem_config) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; knet_log_printf(LOG_DEBUG, "totemknet: Returning MTU of %d", totem_config->net_mtu); } int totemknet_token_target_set ( void *knet_context, unsigned int nodeid) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int res = 0; instance->token_target.nodeid = nodeid; instance->totemknet_target_set_completed (instance->context); return (res); } extern int totemknet_recv_mcast_empty ( void *knet_context) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; unsigned int res; struct sockaddr_storage system_from; struct msghdr msg_hdr; struct iovec iov_recv; struct pollfd ufd; int nfds; int msg_processed = 0; iov_recv.iov_base = instance->iov_buffer; iov_recv.iov_len = KNET_MAX_PACKET_SIZE; msg_hdr.msg_name = &system_from; msg_hdr.msg_namelen = sizeof (struct sockaddr_storage); msg_hdr.msg_iov = &iov_recv; msg_hdr.msg_iovlen = 1; #ifdef HAVE_MSGHDR_CONTROL msg_hdr.msg_control = 0; #endif #ifdef HAVE_MSGHDR_CONTROLLEN msg_hdr.msg_controllen = 0; #endif #ifdef HAVE_MSGHDR_FLAGS msg_hdr.msg_flags = 0; #endif #ifdef HAVE_MSGHDR_ACCRIGHTS msg_msg_hdr.msg_accrights = NULL; #endif #ifdef HAVE_MSGHDR_ACCRIGHTSLEN msg_msg_hdr.msg_accrightslen = 0; #endif do { ufd.fd = instance->knet_fd; ufd.events = POLLIN; nfds = poll (&ufd, 1, 0); if (nfds == 1 && ufd.revents & POLLIN) { res = recvmsg (instance->knet_fd, &msg_hdr, MSG_NOSIGNAL | MSG_DONTWAIT); if (res != -1) { msg_processed = 1; } else { msg_processed = -1; } } } while (nfds == 1); return (msg_processed); } int totemknet_iface_set (void *knet_context, const struct totem_ip_address *local_addr, unsigned short ip_port, unsigned int iface_no) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; totemip_copy(&instance->my_ids[iface_no], local_addr); knet_log_printf(LOG_INFO, "Configured link number %d: local addr: %s, port=%d", iface_no, totemip_print(local_addr), ip_port); instance->ip_port[iface_no] = ip_port; return 0; } int totemknet_member_add ( void *knet_context, const struct totem_ip_address *local, const struct totem_ip_address *member, int link_no) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int err; int port = instance->ip_port[link_no]; struct sockaddr_storage remote_ss; struct sockaddr_storage local_ss; int addrlen; int i; int host_found = 0; knet_node_id_t host_ids[KNET_MAX_HOST]; size_t num_host_ids; /* Only create 1 loopback link and use link 0 */ if (member->nodeid == instance->our_nodeid) { if (!instance->loopback_link) { link_no = 0; instance->loopback_link = 1; } else { /* Already done */ return 0; } } knet_log_printf (LOGSYS_LEVEL_DEBUG, "knet: member_add: " CS_PRI_NODE_ID " (%s), link=%d", member->nodeid, totemip_print(member), link_no); knet_log_printf (LOGSYS_LEVEL_DEBUG, "knet: local: " CS_PRI_NODE_ID " (%s)", local->nodeid, totemip_print(local)); /* Only add the host if it doesn't already exist in knet */ err = knet_host_get_host_list(instance->knet_handle, host_ids, &num_host_ids); if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_host_get_host_list"); return -1; } for (i=0; inodeid) { host_found = 1; } } if (!host_found) { err = knet_host_add(instance->knet_handle, member->nodeid); if (err != 0 && errno != EEXIST) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_host_add"); return -1; } } else { knet_log_printf (LOGSYS_LEVEL_DEBUG, "nodeid " CS_PRI_NODE_ID " already added", member->nodeid); } if (err == 0) { if (knet_host_set_policy(instance->knet_handle, member->nodeid, instance->link_mode)) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_set_policy failed"); return -1; } } memset(&local_ss, 0, sizeof(local_ss)); memset(&remote_ss, 0, sizeof(remote_ss)); /* Casts to remove const */ totemip_totemip_to_sockaddr_convert((struct totem_ip_address *)member, port, &remote_ss, &addrlen); totemip_totemip_to_sockaddr_convert((struct totem_ip_address *)local, port, &local_ss, &addrlen); if (member->nodeid == instance->our_nodeid) { knet_log_printf (LOGSYS_LEVEL_DEBUG, "knet: loopback link is %d\n", link_no); err = knet_link_set_config(instance->knet_handle, member->nodeid, link_no, KNET_TRANSPORT_LOOPBACK, &local_ss, &remote_ss, KNET_LINK_FLAG_TRAFFICHIPRIO); } else { err = knet_link_set_config(instance->knet_handle, member->nodeid, link_no, instance->totem_config->interfaces[link_no].knet_transport, &local_ss, &remote_ss, KNET_LINK_FLAG_TRAFFICHIPRIO); } if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_link_set_config failed"); return -1; } knet_log_printf (LOGSYS_LEVEL_DEBUG, "knet: member_add: Setting link prio to %d", instance->totem_config->interfaces[link_no].knet_link_priority); err = knet_link_set_priority(instance->knet_handle, member->nodeid, link_no, instance->totem_config->interfaces[link_no].knet_link_priority); if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_link_set_priority for nodeid " CS_PRI_NODE_ID ", link %d failed", member->nodeid, link_no); } /* ping timeouts maybe 0 here for a newly added interface so we leave this till later, it will get done in totemknet_refresh_config */ if (instance->totem_config->interfaces[link_no].knet_ping_interval != 0) { err = knet_link_set_ping_timers(instance->knet_handle, member->nodeid, link_no, instance->totem_config->interfaces[link_no].knet_ping_interval, instance->totem_config->interfaces[link_no].knet_ping_timeout, instance->totem_config->interfaces[link_no].knet_ping_precision); if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_link_set_ping_timers for nodeid " CS_PRI_NODE_ID ", link %d failed", member->nodeid, link_no); } err = knet_link_set_pong_count(instance->knet_handle, member->nodeid, link_no, instance->totem_config->interfaces[link_no].knet_pong_count); if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_link_set_pong_count for nodeid " CS_PRI_NODE_ID ", link %d failed", member->nodeid, link_no); } } err = knet_link_set_enable(instance->knet_handle, member->nodeid, link_no, 1); if (err) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_link_set_enable for nodeid " CS_PRI_NODE_ID ", link %d failed", member->nodeid, link_no); return -1; } /* register stats */ stats_knet_add_member(member->nodeid, link_no); return (0); } int totemknet_member_remove ( void *knet_context, const struct totem_ip_address *token_target, int link_no) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int res; uint8_t link_list[KNET_MAX_LINK]; size_t num_links; knet_log_printf (LOGSYS_LEVEL_DEBUG, "knet: member_remove: " CS_PRI_NODE_ID ", link=%d", token_target->nodeid, link_no); /* Don't remove the link with the loopback on it until we shut down */ if (token_target->nodeid == instance->our_nodeid) { return 0; } /* Tidy stats */ stats_knet_del_member(token_target->nodeid, link_no); /* Remove the link first */ res = knet_link_set_enable(instance->knet_handle, token_target->nodeid, link_no, 0); if (res != 0) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_link_set enable(off) for nodeid " CS_PRI_NODE_ID ", link %d failed", token_target->nodeid, link_no); return res; } res = knet_link_clear_config(instance->knet_handle, token_target->nodeid, link_no); if (res != 0) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_link_clear_config for nodeid " CS_PRI_NODE_ID ", link %d failed", token_target->nodeid, link_no); return res; } /* If this is the last link, then remove the node */ res = knet_link_get_link_list(instance->knet_handle, token_target->nodeid, link_list, &num_links); if (res) { return (0); /* not really failure */ } if (num_links == 0) { res = knet_host_remove(instance->knet_handle, token_target->nodeid); } return res; } int totemknet_member_list_rebind_ip ( void *knet_context) { return (0); } static int totemknet_configure_compression ( struct totemknet_instance *instance, struct totem_config *totem_config) { struct knet_handle_compress_cfg compress_cfg; int res = 0; assert(strlen(totem_config->knet_compression_model) < sizeof(compress_cfg.compress_model)); strcpy(compress_cfg.compress_model, totem_config->knet_compression_model); compress_cfg.compress_threshold = totem_config->knet_compression_threshold; compress_cfg.compress_level = totem_config->knet_compression_level; res = knet_handle_compress(instance->knet_handle, &compress_cfg); if (res) { KNET_LOGSYS_PERROR(errno, LOGSYS_LEVEL_ERROR, "knet_handle_compress failed"); } return res; } int totemknet_reconfigure ( void *knet_context, struct totem_config *totem_config) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int res = 0; (void)totemknet_configure_compression(instance, totem_config); #ifdef HAVE_LIBNOZZLE /* Set up nozzle device(s). Return code is ignored, because inability * configure nozzle is not fatal problem, errors are logged and * there is not much else we can do */ (void)setup_nozzle(instance); #endif if (totem_config->crypto_changed) { /* Flip crypto_index */ totem_config->crypto_index = 3-totem_config->crypto_index; res = totemknet_set_knet_crypto(instance); knet_log_printf(LOG_INFO, "kronosnet crypto reconfigured on index %d: %s/%s/%s", totem_config->crypto_index, totem_config->crypto_model, totem_config->crypto_cipher_type, totem_config->crypto_hash_type); } return (res); } int totemknet_crypto_reconfigure_phase ( void *knet_context, struct totem_config *totem_config, cfg_message_crypto_reconfig_phase_t phase) { #ifdef HAVE_KNET_CRYPTO_RECONF int res; int config_to_use; int config_to_clear; struct knet_handle_crypto_cfg crypto_cfg; struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; knet_log_printf(LOGSYS_LEVEL_DEBUG, "totemknet_crypto_reconfigure_phase %d, index=%d\n", phase, totem_config->crypto_index); switch (phase) { case CRYPTO_RECONFIG_PHASE_ACTIVATE: config_to_use = totem_config->crypto_index; if (!totemknet_is_crypto_enabled(instance)) { config_to_use = 0; /* we are clearing it */ } /* Enable the new config on this node */ res = knet_handle_crypto_use_config(instance->knet_handle, config_to_use); if (res == -1) { knet_log_printf(LOGSYS_LEVEL_ERROR, "knet_handle_crypto_use_config %d failed: %s", config_to_use, strerror(errno)); } break; case CRYPTO_RECONFIG_PHASE_CLEANUP: /* * All nodes should now have the new config. clear the old one out * OR disable crypto entirely if that's what the new config insists on. */ config_to_clear = 3-totem_config->crypto_index; knet_log_printf(LOGSYS_LEVEL_DEBUG, "Clearing old knet crypto config %d\n", config_to_clear); strcpy(crypto_cfg.crypto_model, "none"); strcpy(crypto_cfg.crypto_cipher_type, "none"); strcpy(crypto_cfg.crypto_hash_type, "none"); res = knet_handle_crypto_set_config(instance->knet_handle, &crypto_cfg, config_to_clear); if (res == -1) { knet_log_printf(LOGSYS_LEVEL_ERROR, "knet_handle_crypto_set_config to clear index %d failed: %s", config_to_clear, strerror(errno)); } if (res == -2) { knet_log_printf(LOGSYS_LEVEL_ERROR, "knet_handle_crypto_set_config to clear index %d failed: -2", config_to_clear); } /* If crypto is enabled then disable all cleartext reception */ if (totemknet_is_crypto_enabled(instance)) { res = knet_handle_crypto_rx_clear_traffic(instance->knet_handle, KNET_CRYPTO_RX_DISALLOW_CLEAR_TRAFFIC); if (res) { knet_log_printf(LOGSYS_LEVEL_ERROR, "knet_handle_crypto_rx_clear_traffic(DISALLOW) failed %s", strerror(errno)); } } } #endif return 0; } void totemknet_stats_clear ( void *knet_context) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; (void) knet_handle_clear_stats(instance->knet_handle, KNET_CLEARSTATS_HANDLE_AND_LINK); } /* For the stats module */ int totemknet_link_get_status ( knet_node_id_t node, uint8_t link_no, struct knet_link_status *status) { int res; int ret = CS_OK; /* We are probably not using knet */ if (!global_instance) { return CS_ERR_NOT_EXIST; } if (link_no >= INTERFACE_MAX) { return CS_ERR_NOT_EXIST; /* Invalid link number */ } res = knet_link_get_status(global_instance->knet_handle, node, link_no, status, sizeof(struct knet_link_status)); if (res) { switch (errno) { case EINVAL: ret = CS_ERR_INVALID_PARAM; break; case EBUSY: ret = CS_ERR_BUSY; break; case EDEADLK: ret = CS_ERR_TRY_AGAIN; break; default: ret = CS_ERR_LIBRARY; break; } } return (ret); } int totemknet_handle_get_stats ( struct knet_handle_stats *stats) { int res; /* We are probably not using knet */ if (!global_instance) { return CS_ERR_NOT_EXIST; } res = knet_handle_get_stats(global_instance->knet_handle, stats, sizeof(struct knet_handle_stats)); if (res != 0) { return (qb_to_cs_error(-errno)); } return CS_OK; } static void timer_function_merge_detect_timeout ( void *data) { struct totemknet_instance *instance = (struct totemknet_instance *)data; if (instance->merge_detect_messages_sent_before_timeout == 0) { instance->send_merge_detect_message = 1; } instance->merge_detect_messages_sent_before_timeout = 0; totemknet_start_merge_detect_timeout(instance); } static void totemknet_start_merge_detect_timeout( void *knet_context) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; qb_loop_timer_add(instance->poll_handle, QB_LOOP_MED, instance->totem_config->merge_timeout * 2 * QB_TIME_NS_IN_MSEC, (void *)instance, timer_function_merge_detect_timeout, &instance->timer_merge_detect_timeout); } static void totemknet_stop_merge_detect_timeout( void *knet_context) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; qb_loop_timer_del(instance->poll_handle, instance->timer_merge_detect_timeout); } static void log_flush_messages (void *knet_context) { struct pollfd pfd; struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int cont; cont = 1; while (cont) { pfd.fd = instance->logpipes[0]; pfd.events = POLLIN; pfd.revents = 0; if ((poll(&pfd, 1, 0) > 0) && (pfd.revents & POLLIN) && (log_deliver_fn(instance->logpipes[0], POLLIN, instance) == 0)) { cont = 1; } else { cont = 0; } } } #ifdef HAVE_LIBNOZZLE #define NOZZLE_NAME "nozzle.name" #define NOZZLE_IPADDR "nozzle.ipaddr" #define NOZZLE_PREFIX "nozzle.ipprefix" #define NOZZLE_MACADDR "nozzle.macaddr" #define NOZZLE_CHANNEL 1 static char *get_nozzle_script_dir(void *knet_context) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; char filename[PATH_MAX + FILENAME_MAX + 1]; static char updown_dirname[PATH_MAX + FILENAME_MAX + 1]; int res; const char *dirname_res; /* * Build script directory based on corosync.conf file location */ res = snprintf(filename, sizeof(filename), "%s", corosync_get_config_file()); if (res >= sizeof(filename)) { knet_log_printf (LOGSYS_LEVEL_DEBUG, "nozzle up/down path too long"); return NULL; } dirname_res = dirname(filename); res = snprintf(updown_dirname, sizeof(updown_dirname), "%s/%s", dirname_res, "updown.d"); if (res >= sizeof(updown_dirname)) { knet_log_printf (LOGSYS_LEVEL_DEBUG, "nozzle up/down path too long"); return NULL; } return updown_dirname; } /* * Deliberately doesn't return the status as caller doesn't care. * The result will be logged though */ static void run_nozzle_script(struct totemknet_instance *instance, int type, const char *typename) { int res; char *exec_string; res = nozzle_run_updown(instance->nozzle_handle, type, &exec_string); if (res == -1 && errno != ENOENT) { knet_log_printf (LOGSYS_LEVEL_INFO, "exec nozzle %s script failed: %s", typename, strerror(errno)); } else if (res == -2) { knet_log_printf (LOGSYS_LEVEL_INFO, "nozzle %s script failed", typename); knet_log_printf (LOGSYS_LEVEL_INFO, "%s", exec_string); } } /* * Reparse IP address to add in our node ID * IPv6 addresses must end in '::' * IPv4 addresses must just be valid * '/xx' lengths are optional for IPv6, mandatory for IPv4 * * Returns the modified IP address as a string to pass into libnozzle */ static int reparse_nozzle_ip_address(struct totemknet_instance *instance, const char *input_addr, const char *prefix, int nodeid, char *output_addr, size_t output_len) { char *coloncolon; int bits; int max_prefix = 64; uint32_t nodeid_mask; uint32_t addr_mask; uint32_t masked_nodeid; struct in_addr *addr; struct totem_ip_address totemip; coloncolon = strstr(input_addr, "::"); if (!coloncolon) { max_prefix = 30; } bits = atoi(prefix); if (bits < 8 || bits > max_prefix) { knet_log_printf(LOGSYS_LEVEL_ERROR, "nozzle IP address prefix must be >= 8 and <= %d (got %d)", max_prefix, bits); return -1; } /* IPv6 is easy */ if (coloncolon) { memcpy(output_addr, input_addr, coloncolon-input_addr); sprintf(output_addr + (coloncolon-input_addr), "::%x", nodeid); return 0; } /* For IPv4 we need to parse the address into binary, mask off the required bits, * add in the masked_nodeid and 'print' it out again */ nodeid_mask = UINT32_MAX & ((1<<(32 - bits)) - 1); addr_mask = UINT32_MAX ^ nodeid_mask; masked_nodeid = nodeid & nodeid_mask; if (totemip_parse(&totemip, input_addr, AF_INET)) { knet_log_printf(LOGSYS_LEVEL_ERROR, "Failed to parse IPv4 nozzle IP address"); return -1; } addr = (struct in_addr *)&totemip.addr; addr->s_addr &= htonl(addr_mask); addr->s_addr |= htonl(masked_nodeid); inet_ntop(AF_INET, addr, output_addr, output_len); return 0; } static int create_nozzle_device(void *knet_context, const char *name, const char *ipaddr, const char *prefix, const char *macaddr) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; char device_name[IFNAMSIZ+1]; size_t size = IFNAMSIZ; int8_t channel = NOZZLE_CHANNEL; nozzle_t nozzle_dev; int nozzle_fd; int res; char *updown_dir; char parsed_ipaddr[INET6_ADDRSTRLEN]; char mac[19]; memset(device_name, 0, size); memset(&mac, 0, sizeof(mac)); strncpy(device_name, name, size); updown_dir = get_nozzle_script_dir(knet_context); knet_log_printf (LOGSYS_LEVEL_INFO, "nozzle script dir is %s", updown_dir); nozzle_dev = nozzle_open(device_name, size, updown_dir); if (!nozzle_dev) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Unable to init nozzle device %s: %s", device_name, strerror(errno)); return -1; } instance->nozzle_handle = nozzle_dev; if (nozzle_set_mac(nozzle_dev, macaddr) < 0) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Unable to add set nozzle MAC to %s: %s", mac, strerror(errno)); goto out_clean; } if (reparse_nozzle_ip_address(instance, ipaddr, prefix, instance->our_nodeid, parsed_ipaddr, sizeof(parsed_ipaddr))) { /* Prints its own errors */ goto out_clean; } knet_log_printf (LOGSYS_LEVEL_INFO, "Local nozzle IP address is %s / %d", parsed_ipaddr, atoi(prefix)); if (nozzle_add_ip(nozzle_dev, parsed_ipaddr, prefix) < 0) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Unable to add set nozzle IP addr to %s/%s: %s", parsed_ipaddr, prefix, strerror(errno)); goto out_clean; } nozzle_fd = nozzle_get_fd(nozzle_dev); knet_log_printf (LOGSYS_LEVEL_INFO, "Opened '%s' on fd %d", device_name, nozzle_fd); res = knet_handle_add_datafd(instance->knet_handle, &nozzle_fd, &channel); if (res != 0) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Unable to add nozzle FD to knet: %s", strerror(errno)); goto out_clean; } run_nozzle_script(instance, NOZZLE_PREUP, "pre-up"); res = nozzle_set_up(nozzle_dev); if (res != 0) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Unable to set nozzle interface UP: %s", strerror(errno)); goto out_clean; } run_nozzle_script(instance, NOZZLE_UP, "up"); return 0; out_clean: nozzle_close(nozzle_dev); return -1; } static int remove_nozzle_device(void *knet_context) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; int res; int datafd; res = knet_handle_get_datafd(instance->knet_handle, NOZZLE_CHANNEL, &datafd); if (res != 0) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Can't find datafd for channel %d: %s", NOZZLE_CHANNEL, strerror(errno)); return -1; } res = knet_handle_remove_datafd(instance->knet_handle, datafd); if (res != 0) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Can't remove datafd for nozzle channel %d: %s", NOZZLE_CHANNEL, strerror(errno)); return -1; } run_nozzle_script(instance, NOZZLE_DOWN, "pre-down"); res = nozzle_set_down(instance->nozzle_handle); if (res != 0) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Can't set nozzle device down: %s", strerror(errno)); return -1; } run_nozzle_script(instance, NOZZLE_POSTDOWN, "post-down"); res = nozzle_close(instance->nozzle_handle); if (res != 0) { knet_log_printf (LOGSYS_LEVEL_ERROR, "Can't close nozzle device: %s", strerror(errno)); return -1; } knet_log_printf (LOGSYS_LEVEL_INFO, "Removed nozzle device"); return 0; } static void free_nozzle(struct totemknet_instance *instance) { free(instance->nozzle_name); free(instance->nozzle_ipaddr); free(instance->nozzle_prefix); free(instance->nozzle_macaddr); instance->nozzle_name = instance->nozzle_ipaddr = instance->nozzle_prefix = instance->nozzle_macaddr = NULL; } static int setup_nozzle(void *knet_context) { struct totemknet_instance *instance = (struct totemknet_instance *)knet_context; char *ipaddr_str = NULL; char *name_str = NULL; char *prefix_str = NULL; char *macaddr_str = NULL; char mac[32]; int name_res; int macaddr_res; int res = -1; /* * Return value ignored on purpose. icmap_get_string changes * ipaddr_str/prefix_str only on success. */ (void)icmap_get_string(NOZZLE_IPADDR, &ipaddr_str); (void)icmap_get_string(NOZZLE_PREFIX, &prefix_str); macaddr_res = icmap_get_string(NOZZLE_MACADDR, &macaddr_str); name_res = icmap_get_string(NOZZLE_NAME, &name_str); /* Is is being removed? */ if (name_res == CS_ERR_NOT_EXIST && instance->nozzle_handle) { remove_nozzle_device(instance); free_nozzle(instance); goto out_free; } if (!name_str) { /* no nozzle */ goto out_free; } if (!ipaddr_str) { knet_log_printf (LOGSYS_LEVEL_ERROR, "No IP address supplied for Nozzle device"); goto out_free; } if (!prefix_str) { knet_log_printf (LOGSYS_LEVEL_ERROR, "No prefix supplied for Nozzle IP address"); goto out_free; } if (macaddr_str && strlen(macaddr_str) != 17) { knet_log_printf (LOGSYS_LEVEL_ERROR, "macaddr for nozzle device is not in the correct format '%s'", macaddr_str); goto out_free; } if (!macaddr_str) { macaddr_str = (char*)"54:54:01:00:00:00"; } if (instance->nozzle_name && (strcmp(name_str, instance->nozzle_name) == 0) && (strcmp(ipaddr_str, instance->nozzle_ipaddr) == 0) && (strcmp(prefix_str, instance->nozzle_prefix) == 0) && (instance->nozzle_macaddr == NULL || strcmp(macaddr_str, instance->nozzle_macaddr) == 0)) { /* Nothing has changed */ knet_log_printf (LOGSYS_LEVEL_DEBUG, "Nozzle device info not changed"); goto out_free; } /* Add nodeid into MAC address */ memcpy(mac, macaddr_str, 12); snprintf(mac+12, sizeof(mac) - 13, "%02x:%02x", instance->our_nodeid >> 8, instance->our_nodeid & 0xFF); knet_log_printf (LOGSYS_LEVEL_INFO, "Local nozzle MAC address is %s", mac); if (name_res == CS_OK && name_str) { /* Reconfigure */ if (instance->nozzle_name) { remove_nozzle_device(instance); free_nozzle(instance); } res = create_nozzle_device(knet_context, name_str, ipaddr_str, prefix_str, mac); instance->nozzle_name = strdup(name_str); instance->nozzle_ipaddr = strdup(ipaddr_str); instance->nozzle_prefix = strdup(prefix_str); instance->nozzle_macaddr = strdup(macaddr_str); if (!instance->nozzle_name || !instance->nozzle_ipaddr || !instance->nozzle_prefix) { knet_log_printf (LOGSYS_LEVEL_ERROR, "strdup failed in nozzle allocation"); /* * This 'free' will cause a complete reconfigure of the device next time we reload * but will also let the the current device keep working until then. * remove_nozzle() only needs the, statically-allocated, nozzle_handle */ free_nozzle(instance); } } out_free: free(name_str); free(ipaddr_str); free(prefix_str); if (macaddr_res == CS_OK) { free(macaddr_str); } return res; } #endif // HAVE_LIBNOZZLE diff --git a/exec/totemknet.h b/exec/totemknet.h index 30068747..67c0ba6e 100644 --- a/exec/totemknet.h +++ b/exec/totemknet.h @@ -1,157 +1,159 @@ /* * Copyright (c) 2005 MontaVista Software, Inc. * Copyright (c) 2006-2011 Red Hat, Inc. * * All rights reserved. * * Author: Steven Dake (sdake@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the MontaVista Software, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef TOTEMKNET_H_DEFINED #define TOTEMKNET_H_DEFINED #include #include #include #include /** * Create an instance */ extern int totemknet_initialize ( qb_loop_t *poll_handle, void **knet_context, struct totem_config *totem_config, totemsrp_stats_t *stats, void *context, void (*deliver_fn) ( void *context, const void *msg, unsigned int msg_len, const struct sockaddr_storage *system_from), void (*iface_change_fn) ( void *context, const struct totem_ip_address *iface_address, unsigned int ring_no), void (*mtu_changed) ( void *context, int net_mtu), void (*target_set_completed) ( void *context)); extern void *totemknet_buffer_alloc (void); extern void totemknet_buffer_release (void *ptr); extern int totemknet_processor_count_set ( void *knet_context, int processor_count); extern int totemknet_token_send ( void *knet_context, const void *msg, unsigned int msg_len); extern int totemknet_mcast_flush_send ( void *knet_context, const void *msg, unsigned int msg_len); extern int totemknet_mcast_noflush_send ( void *knet_context, const void *msg, unsigned int msg_len); extern int totemknet_recv_flush (void *knet_context); extern int totemknet_send_flush (void *knet_context); extern int totemknet_iface_check (void *knet_context); extern int totemknet_finalize (void *knet_context); extern void totemknet_net_mtu_adjust (void *knet_context, struct totem_config *totem_config); extern int totemknet_nodestatus_get (void *knet_context, unsigned int nodeid, struct totem_node_status *node_status); extern int totemknet_ifaces_get (void *net_context, char ***status, unsigned int *iface_count); extern int totemknet_iface_set (void *net_context, const struct totem_ip_address *local_addr, unsigned short ip_port, unsigned int iface_no); extern int totemknet_token_target_set ( void *knet_context, unsigned int nodeid); extern int totemknet_crypto_set ( void *knet_context, const char *cipher_type, const char *hash_type); extern int totemknet_recv_mcast_empty ( void *knet_context); extern int totemknet_member_add ( void *knet_context, const struct totem_ip_address *local, const struct totem_ip_address *member, int ring_no); extern int totemknet_member_remove ( void *knet_context, const struct totem_ip_address *member, int ring_no); extern int totemknet_member_set_active ( void *knet_context, const struct totem_ip_address *member_ip, int active); extern int totemknet_reconfigure ( void *knet_context, struct totem_config *totem_config); extern int totemknet_crypto_reconfigure_phase ( void *knet_context, struct totem_config *totem_config, cfg_message_crypto_reconfig_phase_t phase); extern void totemknet_stats_clear ( void *knet_context); +extern void totemknet_configure_log_level (void); + #endif /* TOTEMKNET_H_DEFINED */ diff --git a/include/corosync/logsys.h b/include/corosync/logsys.h index 6f768242..325b24be 100644 --- a/include/corosync/logsys.h +++ b/include/corosync/logsys.h @@ -1,341 +1,350 @@ /* * Copyright (c) 2002-2004 MontaVista Software, Inc. * Copyright (c) 2006-2012 Red Hat, Inc. * * Author: Steven Dake (sdake@redhat.com) * Author: Lon Hohberger (lhh@redhat.com) * Author: Fabio M. Di Nitto (fdinitto@redhat.com) * * All rights reserved. * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the MontaVista Software, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef LOGSYS_H_DEFINED #define LOGSYS_H_DEFINED #include #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* * All of the LOGSYS_MODE's can be ORed together for combined behavior * * FORK and THREADED are ignored for SUBSYSTEMS */ #define LOGSYS_MODE_OUTPUT_FILE (1<<0) #define LOGSYS_MODE_OUTPUT_STDERR (1<<1) #define LOGSYS_MODE_OUTPUT_SYSLOG (1<<2) #define LOGSYS_MODE_FORK (1<<3) #define LOGSYS_MODE_THREADED (1<<4) /* * Log priorities, compliant with syslog and SA Forum Log spec. */ #define LOGSYS_LEVEL_EMERG LOG_EMERG #define LOGSYS_LEVEL_ALERT LOG_ALERT #define LOGSYS_LEVEL_CRIT LOG_CRIT #define LOGSYS_LEVEL_ERROR LOG_ERR #define LOGSYS_LEVEL_WARNING LOG_WARNING #define LOGSYS_LEVEL_NOTICE LOG_NOTICE #define LOGSYS_LEVEL_INFO LOG_INFO #define LOGSYS_LEVEL_DEBUG LOG_DEBUG #define LOGSYS_LEVEL_TRACE LOG_TRACE /* * logsys_logger bits * * SUBSYS_COUNT defines the maximum number of subsystems * SUBSYS_NAMELEN defines the maximum len of a subsystem name */ #define LOGSYS_MAX_SUBSYS_COUNT 32 #define LOGSYS_MAX_SUBSYS_NAMELEN 64 #define LOGSYS_MAX_PERROR_MSG_LEN 128 /* * Debug levels */ #define LOGSYS_DEBUG_OFF 0 #define LOGSYS_DEBUG_ON 1 #define LOGSYS_DEBUG_TRACE 2 #ifndef LOGSYS_UTILS_ONLY /** * @brief configuration bits that can only be done for the whole system * @param format * @return */ extern int logsys_format_set ( const char *format); /** * @brief logsys_format_get * @return */ extern char *logsys_format_get (void); /** * @brief per system/subsystem settings. * * NOTE: once a subsystem is created and configured, changing * the default does NOT affect the subsystems. * * Pass a NULL subsystem to change them all * * @param subsys * @param facility * @return */ extern int logsys_config_syslog_facility_set ( const char *subsys, unsigned int facility); /** * @brief logsys_config_syslog_priority_set * @param subsys * @param priority * @return */ extern int logsys_config_syslog_priority_set ( const char *subsys, unsigned int priority); /** * @brief logsys_config_mode_set * @param subsys * @param mode * @return */ extern int logsys_config_mode_set ( const char *subsys, unsigned int mode); /** * @brief logsys_config_mode_get * @param subsys * @return */ extern unsigned int logsys_config_mode_get ( const char *subsys); /** * @brief logsys_config_apply */ void logsys_config_apply(void); /** * @brief to close a logfile, just invoke this function with a NULL * file or if you want to change logfile, the old one will * be closed for you. * * @param subsys * @param error_string * @param file * @return */ extern int logsys_config_file_set ( const char *subsys, const char **error_string, const char *file); /** * @brief logsys_config_logfile_priority_set * @param subsys * @param priority * @return */ extern int logsys_config_logfile_priority_set ( const char *subsys, unsigned int priority); /** * @brief enabling debug, disable message priority filtering. * everything is sent everywhere. priority values * for file and syslog are not overwritten. * * @param subsys * @param value * @return */ extern int logsys_config_debug_set ( const char *subsys, unsigned int value); +/** + * @brief Return the debug flag for this subsys + * + * @param subsys + * @return LOGSYS_DEBUG_OFF | LOGSYS_DEBUG_ON | LOGSYS_DEBUG_TRACE + */ +extern int logsys_config_debug_get ( + const char *subsys); + /* * External API - helpers * * convert facility/priority to/from name/values */ /** * @brief logsys_priority_id_get * @param name * @return */ extern int logsys_priority_id_get ( const char *name); /** * @brief logsys_priority_name_get * @param priority * @return */ extern const char *logsys_priority_name_get ( unsigned int priority); /** * @brief _logsys_system_setup * @param mainsystem * @param mode * @param syslog_facility * @param syslog_priority * @return */ extern int _logsys_system_setup( const char *mainsystem, unsigned int mode, int syslog_facility, int syslog_priority); /** * @brief logsys_system_fini */ extern void logsys_system_fini (void); /** * @brief _logsys_config_subsys_get * @param subsys * @return */ extern int _logsys_config_subsys_get ( const char *subsys); /** * @brief _logsys_subsys_create * @param subsys * @param filename * @return */ extern int _logsys_subsys_create (const char *subsys, const char *filename); /** * @brief logsys_thread_start * @return */ extern int logsys_thread_start (void); extern void logsys_blackbox_set(int enable); extern void logsys_blackbox_prefork(void); extern void logsys_blackbox_postfork(void); extern cs_error_t logsys_reopen_log_files(void); /** * @brief logsys_subsys_id */ static int logsys_subsys_id __attribute__((unused)) = LOGSYS_MAX_SUBSYS_COUNT; /** * @brief The LOGSYS_DECLARE_SYSTEM macro * @param name * @param mode * @param syslog_facility * @param syslog_priority */ #define LOGSYS_DECLARE_SYSTEM(name,mode,syslog_facility,syslog_priority)\ QB_LOG_INIT_DATA(logsys_qb_init); \ __attribute__ ((constructor)) \ static void logsys_system_init (void) \ { \ if (_logsys_system_setup (name,mode,syslog_facility,syslog_priority) < 0) { \ fprintf (stderr, \ "Unable to setup logging system: %s.\n", name); \ exit (-1); \ } \ } /** * @brief The LOGSYS_DECLARE_SUBSYS macro * @param subsys */ #define LOGSYS_DECLARE_SUBSYS(subsys) \ __attribute__ ((constructor)) \ static void logsys_subsys_init (void) \ { \ logsys_subsys_id = \ _logsys_subsys_create ((subsys), __FILE__); \ if (logsys_subsys_id == -1) { \ fprintf (stderr, \ "Unable to create logging subsystem: %s.\n", subsys); \ exit (-1); \ } \ } /** * @brief The LOGSYS_PERROR macro * @param err_num * @param level * @param fmt * @param args */ #define LOGSYS_PERROR(err_num, level, fmt, args...) do { \ char _error_str[LOGSYS_MAX_PERROR_MSG_LEN]; \ const char *_error_ptr = qb_strerror_r(err_num, _error_str, sizeof(_error_str)); \ qb_log(level, fmt ": %s (%d)", ##args, _error_ptr, err_num); \ } while(0) #define log_printf(level, format, args...) qb_log(level, format, ##args) #define ENTER qb_enter #define LEAVE qb_leave #define TRACE1(format, args...) qb_log(LOG_TRACE, "TRACE1:" #format, ##args) #define TRACE2(format, args...) qb_log(LOG_TRACE, "TRACE2:" #format, ##args) #define TRACE3(format, args...) qb_log(LOG_TRACE, "TRACE3:" #format, ##args) #define TRACE4(format, args...) qb_log(LOG_TRACE, "TRACE4:" #format, ##args) #define TRACE5(format, args...) qb_log(LOG_TRACE, "TRACE5:" #format, ##args) #define TRACE6(format, args...) qb_log(LOG_TRACE, "TRACE6:" #format, ##args) #define TRACE7(format, args...) qb_log(LOG_TRACE, "TRACE7:" #format, ##args) #define TRACE8(format, args...) qb_log(LOG_TRACE, "TRACE8:" #format, ##args) #endif /* LOGSYS_UTILS_ONLY */ #ifdef __cplusplus } #endif #endif /* LOGSYS_H_DEFINED */ diff --git a/vqsim/vqmain.c b/vqsim/vqmain.c index e42ce9fd..8e5f0e3f 100644 --- a/vqsim/vqmain.c +++ b/vqsim/vqmain.c @@ -1,848 +1,854 @@ #include #include #include #include #include #include #include #include #include #ifdef HAVE_READLINE_READLINE_H #include #else #include /* isatty */ #endif #include "../exec/votequorum.h" #include "../exec/service.h" #include #include #include "icmap.h" #include "vqsim.h" /* Easier than including the config file with a ton of conflicting dependencies */ extern int coroparse_configparse (icmap_map_t config_map, const char **error_string); extern int corosync_log_config_read (const char **error_string); static int stdin_read_fn(int32_t fd, int32_t revents, void *data); +void totemknet_configure_log_level(void); /* 'Keep the compiler happy' time */ const char *corosync_get_config_file(void); /* One of these per partition */ struct vq_partition { TAILQ_HEAD(, vq_node) nodelist; struct memb_ring_id ring_id; int num; }; /* One of these per node */ struct vq_node { vq_object_t instance; unsigned int nodeid; int fd; struct vq_partition *partition; TAILQ_ENTRY(vq_node) entries; /* Last status */ int last_quorate; struct memb_ring_id last_ring_id; int last_view_list[MAX_NODES]; int last_view_list_entries; }; static struct vq_partition partitions[MAX_PARTITIONS]; static qb_loop_t *poll_loop; static int autofence; static int check_for_quorum; static FILE *output_file; static int sync_cmds = 1; static qb_loop_timer_handle kb_timer; static int waiting_for_sync = 0; static int is_tty; static int assert_on_timeout; static uint64_t command_timeout = 250000000L; static struct vq_node *find_by_pid(pid_t pid); static void send_partition_to_nodes(struct vq_partition *partition, int newring); static void start_kb_input_timeout(void *data); static void finish_wait_timeout(void *data); #ifndef HAVE_READLINE_READLINE_H #define INPUT_BUF_SIZE 1024 static char input_buf[INPUT_BUF_SIZE]; static size_t input_buf_term = 0; #endif /* 'Keep the compiler happy' time */ static char corosync_config_file[PATH_MAX + 1] = COROSYSCONFDIR "/corosync.conf"; const char *corosync_get_config_file(void) { return (corosync_config_file); } /* Tell all non-quorate nodes to quit */ static void force_fence(void) { int i; struct vq_node *vqn; for (i=0; iinstance); } } } /* Save quorum state from the incoming message */ static void save_quorum_state(struct vq_node *node, struct vqsim_quorum_msg *qmsg) { node->last_quorate = qmsg->quorate; memcpy(&node->last_ring_id, &qmsg->ring_id, sizeof(struct memb_ring_id)); memcpy(node->last_view_list, qmsg->view_list, sizeof(int) * qmsg->view_list_entries); node->last_view_list_entries = qmsg->view_list_entries; /* If at least one node is quorate and autofence is enabled, then fence everyone who is not quorate */ if (check_for_quorum && qmsg->quorate & autofence) { check_for_quorum = 0; force_fence(); } } /* Print current node state */ static void print_quorum_state(struct vq_node *node) { int i; if (node->last_quorate < 0) { fprintf(output_file, "%d:" CS_PRI_NODE_ID ": q=UNINITIALIZED\n", node->partition->num, node->nodeid); return; } fprintf(output_file, "%d:" CS_PRI_NODE_ID ": q=%d ring=[" CS_PRI_RING_ID "] ", node->partition->num, node->nodeid, node->last_quorate, node->last_ring_id.nodeid, (uint64_t)node->last_ring_id.seq); fprintf(output_file, "nodes=["); for (i = 0; i < node->last_view_list_entries; i++) { if (i) { fprintf(output_file, " "); } fprintf(output_file, CS_PRI_NODE_ID, node->last_view_list[i]); } fprintf(output_file, "]\n"); } static void propogate_vq_message(struct vq_node *vqn, const char *msg, int len) { struct vq_node *other_vqn; ssize_t write_res; /* Send it to everyone in that node's partition (including itself) */ TAILQ_FOREACH(other_vqn, &vqn->partition->nodelist, entries) { write_res = write(other_vqn->fd, msg, len); /* * Read counterpart is not ready for receiving non-complete message so * ensure all required information was send. */ assert(write_res == len); } } static void cmd_show_prompt_if_needed(void) { qb_loop_timer_del(poll_loop, kb_timer); if (is_tty) { printf("vqsim> "); fflush(stdout); } else { printf("#vqsim> "); fflush(stdout); } } void resume_kb_input(int show_status) { /* If running synchronously, we don't display the quorum messages as they come in. So run 'show' commamnd */ if (show_status && waiting_for_sync) { cmd_show_node_states(); } waiting_for_sync = 0; if (qb_loop_poll_add(poll_loop, QB_LOOP_MED, STDIN_FILENO, POLLIN | POLLERR, NULL, stdin_read_fn)) { if (errno != EEXIST) { perror("qb_loop_poll_add1 returned error"); } } /* Always shows the prompt here, cos we cleared waiting_for_sync */ cmd_show_prompt_if_needed(); } /* Return true (1) if all nodes in each partition have the same ring id, false(0) otherwise */ static int all_nodes_consistent(void) { int i; struct vq_node *vqn; struct memb_ring_id last_ring_id; for (i=0; ilast_ring_id.seq) { return 0; } last_ring_id.seq = vqn->last_ring_id.seq; } } return 1; } static int vq_parent_read_fn(int32_t fd, int32_t revents, void *data) { char msgbuf[8192]; int msglen; struct vqsim_msg_header *msg; struct vqsim_quorum_msg *qmsg; struct vq_node *vqn = data; if (revents == POLLIN) { msglen = read(fd, msgbuf, sizeof(msgbuf)); if (msglen < 0) { perror("read failed"); } else if (msglen < sizeof(*msg)) { fprintf(stderr, "Received message is too short\n"); } else { msg = (void*)msgbuf; switch (msg->type) { case VQMSG_QUORUM: qmsg = (void*)msgbuf; /* * Check length of message. * SOCK_SEQPACKET is used so this check is not strictly needed. */ if (msglen < sizeof(*qmsg) || qmsg->view_list_entries > MAX_NODES || msglen < sizeof(*qmsg) + sizeof(qmsg->view_list[0]) * qmsg->view_list_entries) { fprintf(stderr, "Received quorum message is too short or corrupted\n"); return (0); } save_quorum_state(vqn, qmsg); if (!sync_cmds) { print_quorum_state(vqn); } /* Have the partitions stabilised? */ if (sync_cmds && waiting_for_sync && all_nodes_consistent()) { qb_loop_timer_del(poll_loop, kb_timer); resume_kb_input(sync_cmds); } break; case VQMSG_EXEC: /* Message from votequorum, pass around the partition */ propogate_vq_message(vqn, msgbuf, msglen); break; case VQMSG_QUIT: case VQMSG_SYNC: case VQMSG_QDEVICE: case VQMSG_QUORUMQUIT: /* not used here */ break; } } } if (revents == POLLERR) { fprintf(stderr, "pollerr on " CS_PRI_NODE_ID "\n", vqn->nodeid); } return 0; } +/* Dummy routine to keep the linker happy */ +void totemknet_configure_log_level(void) +{ + +} static int read_corosync_conf(void) { int res; const char *error_string; int err = icmap_init(); if (!err) { fprintf(stderr, "icmap_init failed\n"); } /* Load corosync.conf */ logsys_format_set(NULL); res = coroparse_configparse(icmap_get_global_map(), &error_string); if (res == -1) { log_printf (LOGSYS_LEVEL_INFO, "Error loading corosync.conf %s", error_string); return -1; } else { res = corosync_log_config_read (&error_string); if (res < 0) { log_printf (LOGSYS_LEVEL_INFO, "error reading log config %s", error_string); syslog (LOGSYS_LEVEL_INFO, "error reading log config %s", error_string); } else { logsys_config_apply(); } } if (logsys_thread_start() != 0) { log_printf (LOGSYS_LEVEL_ERROR, "Can't initialize log thread"); return -1; } return 0; } static void remove_node(struct vq_node *node) { struct vq_partition *part; part = node->partition; /* Remove from partition list */ TAILQ_REMOVE(&part->nodelist, node, entries); free(node); /* Rebuild quorum */ send_partition_to_nodes(part, 1); } static int32_t sigchld_handler(int32_t sig, void *data) { pid_t pid; int status; struct vq_node *vqn; const char *exit_status=""; char text[132]; pid = wait(&status); if (WIFEXITED(status)) { vqn = find_by_pid(pid); if (vqn) { switch (WEXITSTATUS(status)) { case 0: exit_status = "(on request)"; break; case 1: exit_status = "(autofenced)"; break; default: sprintf(text, "(exit code %d)", WEXITSTATUS(status)); break; } printf("%d:" CS_PRI_NODE_ID ": Quit %s\n", vqn->partition->num, vqn->nodeid, exit_status); remove_node(vqn); } else { fprintf(stderr, "Unknown child %d exited with status %d\n", pid, WEXITSTATUS(status)); } } if (WIFSIGNALED(status)) { vqn = find_by_pid(pid); if (vqn) { printf("%d:" CS_PRI_NODE_ID " exited on signal %d%s\n", vqn->partition->num, vqn->nodeid, WTERMSIG(status), WCOREDUMP(status)?" (core dumped)":""); remove_node(vqn); } else { fprintf(stderr, "Unknown child %d exited with status %d%s\n", pid, WTERMSIG(status), WCOREDUMP(status)?" (core dumped)":""); } } return 0; } static void send_partition_to_nodes(struct vq_partition *partition, int newring) { struct vq_node *vqn; int nodelist[MAX_NODES]; int nodes = 0; int first = 1; if (newring) { /* Simulate corosync incrementing the seq by 4 for added authenticity */ partition->ring_id.seq += 4; } /* Build the node list */ TAILQ_FOREACH(vqn, &partition->nodelist, entries) { nodelist[nodes++] = vqn->nodeid; if (first) { partition->ring_id.nodeid = vqn->nodeid; first = 0; } } TAILQ_FOREACH(vqn, &partition->nodelist, entries) { vq_set_nodelist(vqn->instance, &partition->ring_id, nodelist, nodes); } } static void init_partitions(void) { int i; for (i=0; ilast_quorate = -1; /* mark "uninitialized" */ newvq->instance = vq_create_instance(poll_loop, nodeid); if (!newvq->instance) { fprintf(stderr, "ERR: could not create vq instance nodeid " CS_PRI_NODE_ID "\n", nodeid); free(newvq); return (pid_t) -1; } newvq->partition = &partitions[partno]; newvq->nodeid = nodeid; newvq->fd = vq_get_parent_fd(newvq->instance); TAILQ_INSERT_TAIL(&partitions[partno].nodelist, newvq, entries); if (qb_loop_poll_add(poll_loop, QB_LOOP_MED, newvq->fd, POLLIN | POLLERR, newvq, vq_parent_read_fn)) { perror("qb_loop_poll_add returned error"); return (pid_t) -1; } /* Send sync with all the nodes so far in it. */ send_partition_to_nodes(&partitions[partno], 1); return vq_get_pid(newvq->instance); } return (pid_t) -1; } static size_t create_nodes_from_config(void) { icmap_iter_t iter; char tmp_key[ICMAP_KEYNAME_MAXLEN]; uint32_t node_pos; uint32_t nodeid; const char *iter_key; int res; pid_t pid; size_t ret = 0; init_partitions(); 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.nodeid", node_pos); if (icmap_get_uint32(tmp_key, &nodeid) == CS_OK) { pid = create_node(nodeid, 0); if (pid == (pid_t) -1) { fprintf(stderr, "ERR: nodeid " CS_PRI_NODE_ID " could not be spawned\n", nodeid); exit(1); } ret++; } } icmap_iter_finalize(iter); return ret; } static struct vq_node *find_node(int nodeid) { int i; struct vq_node *vqn; for (i=0; inodeid == nodeid) { return vqn; } } } return NULL; } static struct vq_node *find_by_pid(pid_t pid) { int i; struct vq_node *vqn; for (i=0; iinstance) == pid) { return vqn; } } } return NULL; } /* Routines called from the parser */ /* * The parser calls this before running a command where * we might have to wait for a result to come back. */ void cmd_start_sync_command() { if (sync_cmds) { qb_loop_poll_del(poll_loop, STDIN_FILENO); qb_loop_timer_add(poll_loop, QB_LOOP_MED, command_timeout, NULL, finish_wait_timeout, &kb_timer); waiting_for_sync = 1; } } int cmd_start_new_node(int nodeid, int partition) { struct vq_node *node; node = find_node(nodeid); if (node) { fprintf(stderr, "ERR: nodeid " CS_PRI_NODE_ID " already exists in partition %d\n", nodeid, node->partition->num); return -1; } if (create_node(nodeid, partition) == -1) { return -1; } return 0; } void cmd_stop_all_nodes() { int i; struct vq_node *vqn; for (i=0; iinstance); } } } void cmd_show_node_states() { int i; struct vq_node *vqn; for (i=0; iinstance); /* Node will be removed when the child process exits */ return 0; } /* Move all nodes in 'nodelist' into partition 'partition' */ void cmd_move_nodes(int partition, int num_nodes, int *nodelist) { int i; struct vq_node *node; struct vq_node *vqn; int total_nodes = num_nodes; /* Work out the number of nodes affected */ TAILQ_FOREACH(vqn, &partitions[partition].nodelist, entries) { total_nodes++; } for (i=0; ipartition->nodelist, node, entries); /* Add it to the new partition */ TAILQ_INSERT_TAIL(&partitions[partition].nodelist, node, entries); node->partition = &partitions[partition]; } else { printf("ERR: node " CS_PRI_NODE_ID " does not exist\n", nodelist[i]); } } } /* Take all the nodes in part2 and join them to part1 */ void cmd_join_partitions(int part1, int part2) { struct vq_node *vqn; while (!TAILQ_EMPTY(&partitions[part2].nodelist)) { vqn = TAILQ_FIRST(&partitions[part2].nodelist); TAILQ_REMOVE(&vqn->partition->nodelist, vqn, entries); TAILQ_INSERT_TAIL(&partitions[part1].nodelist, vqn, entries); vqn->partition = &partitions[part1]; } } void cmd_set_autofence(int onoff) { autofence = onoff; fprintf(output_file, "#autofence: %s\n", onoff?"on":"off"); } void cmd_set_sync(int onoff) { autofence = onoff; fprintf(output_file, "#sync: %s\n", onoff?"on":"off"); sync_cmds = onoff; } void cmd_set_assert(int onoff) { assert_on_timeout = onoff; } void cmd_update_all_partitions(int newring) { int i; check_for_quorum = 1; for (i=0; iinstance, &node->partition->ring_id, onoff); } } /* If we get called then a command has timed-out */ static void finish_wait_timeout(void *data) { if (command_timeout) { fprintf(stderr, "ERR: Partition(s) not stable within timeout\n"); if (assert_on_timeout) { exit(2); } } resume_kb_input(sync_cmds); } void cmd_set_timeout(uint64_t seconds) { command_timeout = seconds * QB_TIME_NS_IN_MSEC; } /* ---------------------------------- */ #ifndef HAVE_READLINE_READLINE_H static void dummy_read_char(void); static void dummy_read_char() { int c, flush = 0; while (!flush) { c = getchar(); if (++input_buf_term >= INPUT_BUF_SIZE) { if (c != '\n' && c != EOF) fprintf(stderr, "User input overflows the limit: %zu\n", (size_t) INPUT_BUF_SIZE); input_buf[INPUT_BUF_SIZE - 1] = '\0'; flush = 1; } else if (c == '\n' || c == EOF) { input_buf[input_buf_term - 1] = '\0'; flush = 1; } else { input_buf[input_buf_term - 1] = c; } } parse_input_command((c == EOF) ? NULL : input_buf); input_buf_term = 0; } #endif static int stdin_read_fn(int32_t fd, int32_t revents, void *data) { #ifdef HAVE_READLINE_READLINE_H /* Send it to readline */ rl_callback_read_char(); #else dummy_read_char(); #endif return 0; } static void start_kb_input_timeout(void *data) { resume_kb_input(1); } static void usage(char *program) { printf("Usage:\n"); printf("\n"); printf("%s [-c ] [-o ]\n", program); printf("\n"); printf(" -c config file. defaults to /etc/corosync/corosync.conf\n"); printf(" -o output file. defaults to stdout\n"); printf(" -n no synchronization (on adding a node)\n"); printf(" -h display this help text\n"); printf("\n"); printf("%s always takes input from STDIN, but cannot use a file.\n", program); printf("If you want to script it then use\n cat | %s\n", program); printf("\n"); } int main(int argc, char **argv) { qb_loop_signal_handle sigchld_qb_handle; int ch; char *output_file_name = NULL; while ((ch = getopt (argc, argv, "c:o:nh")) != EOF) { switch (ch) { case 'c': if (strlen(optarg) >= sizeof(sizeof(corosync_config_file) - 1)) { fprintf(stderr, "Corosync config file path too long\n"); exit(1); } strncpy(corosync_config_file, optarg, sizeof(corosync_config_file) - 1); break; case 'o': output_file_name = optarg; break; case 'n': sync_cmds = 0; break; default: usage(argv[0]); exit(0); } } if (output_file_name) { output_file = fopen(output_file_name, "w"); if (!output_file) { fprintf(stderr, "Unable to open %s for output: %s\n", output_file_name, strerror(errno)); exit(3); } } else { output_file = stdout; } is_tty = isatty(STDIN_FILENO); qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FUNCTION, "*", LOG_DEBUG); qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE); qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FUNCTION, "*", LOG_DEBUG); poll_loop = qb_loop_create(); /* SIGCHLD handler to reap sub-processes and reconfigure the cluster */ qb_loop_signal_add(poll_loop, QB_LOOP_MED, SIGCHLD, NULL, sigchld_handler, &sigchld_qb_handle); #ifdef HAVE_READLINE_READLINE_H /* Readline will deal with completed lines when they arrive */ /* * For scripting add '#' to the start of the prompt so that * parsers can ignore input lines */ rl_already_prompted = 1; if (is_tty) { rl_callback_handler_install("vqsim> ", parse_input_command); } else { rl_callback_handler_install("#vqsim> ", parse_input_command); } #endif /* Create a full cluster of nodes from corosync.conf */ read_corosync_conf(); if (create_nodes_from_config() && sync_cmds) { /* Delay kb input handling by 1 second when we've just added the nodes from corosync.conf; expect that the delay will be cancelled substantially earlier once they all have reported their quorum info (the delay is in fact a failsafe input enabler here) */ qb_loop_timer_add(poll_loop, QB_LOOP_MED, 1000000000, NULL, start_kb_input_timeout, &kb_timer); waiting_for_sync = 1; } else { resume_kb_input(0); } qb_loop_run(poll_loop); return 0; }