From 4c1b7f761c0fd7a75bca750dc508cd0fd297ea55 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Thu, 14 Jun 2018 10:43:53 +0300 Subject: [PATCH] MXS-421 Add maxscale::event concept MaxScale now defines events for which the syslog facility and level can explicitly be defined by the administrator. Currently there is only one such event, namelt AUTHENTICATION_FAILURE. In a subsequent commit, config.cc will be modified so that event-related configuration parameters are passed to event::configure() and in another subsequent commit the authenticators will be modifed to use this mechanism. In practice a line like: MXS_WARNING("%s: login attempt for user '%s'@[%s]:%s, " "authentication failed.", dcb->service->name, client_data->user, dcb->remote, dcb->path); will be changed to MXS_LOG_EVENT(event::AUTHENTICATION_FAILURE, "%s: login attempt for user '%s'@[%s]:%s, " "authentication failed.", dcb->service->name, client_data->user, dcb->remote, dcb->path); --- include/maxscale/event.hh | 174 ++++++++++++ server/core/CMakeLists.txt | 1 + server/core/event.cc | 479 ++++++++++++++++++++++++++++++++ server/core/internal/event.hh | 46 +++ server/core/test/CMakeLists.txt | 3 + server/core/test/test_event.cc | 471 +++++++++++++++++++++++++++++++ 6 files changed, 1174 insertions(+) create mode 100644 include/maxscale/event.hh create mode 100644 server/core/event.cc create mode 100644 server/core/internal/event.hh create mode 100644 server/core/test/test_event.cc diff --git a/include/maxscale/event.hh b/include/maxscale/event.hh new file mode 100644 index 000000000..253a2a9e6 --- /dev/null +++ b/include/maxscale/event.hh @@ -0,0 +1,174 @@ +#pragma once +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl11. + * + * Change Date: 2020-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include +#include +#include + +namespace maxscale +{ + +/** + * @brief Convert a syslog level into a string + * + * @param level One of LOG_WARNING, LOG_ERR, ... + * + * @return The corresponding string; "LOG_WARNING", "LOG_ERR", ... + */ +const char* log_level_to_string(int32_t level); + +/** + * @brief Convert a string to a syslog level + * + * @param pLevel [out] A syslog level. + * @param zValue A syslog value as string; "LOG_WARNING", "LOG_ERR", ... + * + * @return True, if the string corresponds to a syslog level, false otherwise. + */ +bool log_level_from_string(int32_t* pLevel, const char* zValue); + +/** + * @brief Convert a syslog facility into a string + * + * @param level One of LOG_USER, LOG_LOCAL0, ... + * + * @return The corresponding string; "LOG_USER", "LOG_LOCAL0", ... + */ +const char* log_facility_to_string(int32_t facility); + +/** + * @brief Convert a string to a syslog facility + * + * @param pFacility [out] A syslog facility. + * @param zValue A syslog facility as string; "LOG_USER", "LOG_LOCAL0", ... + * + * @return True, if the string corresponds to a syslog facility, false otherwise. + */ +bool log_facility_from_string(int32_t* pFacility, const char* zValue); + +namespace event +{ + +enum id_t +{ + AUTHENTICATION_FAILURE /**< Authentication failure */ +}; + +enum +{ + DEFAULT_FACILITY = LOG_USER, + DEFAULT_LEVEL = LOG_WARNING +}; + +/** + * @brief Convert an event id to a string. + * + * The string corresponding to an event id is the symbolic constant + * converted to all lower-case. + * + * @param id An event id. + * + * @return The corresponding string. + */ +const char* to_string(id_t id); + +/** + * @brief Convert a string to an event id + * + * @param pId [out] An event id. + * @param zValue An event id a string; "authentication_failure", ... + * + * @return True, if the string could be converted, false otherwise. + */ +bool from_string(id_t* pId, const char* zValue); +bool from_string(id_t* pId, const std::string& value) +{ + return from_string(pId, value.c_str()); +} + +/** + * @brief Set the syslog facility of an event. + * + * @param id The id of the event. + * @param facility One of LOG_USER, LOG_LOCAL0, ... + * + * @note If @c facility contains other bits than facility bits, they + * will silently be ignored. + */ +void set_log_facility(id_t id, int32_t facility); + +/** + * @brief Get the syslog facility of an event. + * + * @param id An event id. + * + * @return The facility of the event. + */ +int32_t get_log_facility(id_t id); + +/** + * @brief Set the syslog level of an event. + * + * @param id The id of the event. + * @param level One of LOG_WARNING, LOG_ERR, ... + * + * @note If @c level contains other bits than level bits, they + * will silently be ignored. + */ +void set_log_level(id_t id, int32_t level); + +/** + * @brief Get the syslog level of an event. + * + * @param id An event id. + * + * @return The level of the event. + */ +int32_t get_log_level(id_t id); + + +/** + * @brief Log an event. + * + * Usually this function should not be used, but the macro + * @c MXS_LOG_EVENT should be used in its stead. + * + * @param event_id The id of the event. + * @param modname The module where the event is logged. + * @param file The file where the event is logged. + * @param line The line where the event is logged. + * @param function The function where the event is logged. + * @param format Printf formatting string. + * @param ... Formatting string specific additional arguments. + * + */ +void log(id_t event_id, + const char* modname, + const char* file, int line, const char* function, + const char* format, ...) mxs_attribute((format(printf, 6, 7))); + +} + +} + +/** + * @brief Log an event. + * + * @param event_id The id of the event. + * @param format Printf formatting string. + * @param ... Formatting string specific additional arguments. + * + */ +#define MXS_LOG_EVENT(event_id, format, ...)\ + maxscale::event::log(event_id, MXS_MODULE_NAME, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__) diff --git a/server/core/CMakeLists.txt b/server/core/CMakeLists.txt index 6f71c1053..cdfa8c06d 100644 --- a/server/core/CMakeLists.txt +++ b/server/core/CMakeLists.txt @@ -10,6 +10,7 @@ add_library(maxscale-common SHARED config_runtime.cc dcb.cc encryption.cc + event.cc externcmd.cc filter.cc hashtable.cc diff --git a/server/core/event.cc b/server/core/event.cc new file mode 100644 index 000000000..656b2bfdb --- /dev/null +++ b/server/core/event.cc @@ -0,0 +1,479 @@ +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl11. + * + * Change Date: 2020-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include "internal/event.hh" +#include +#include +#include +#include + +using namespace std; + +namespace +{ + +using namespace maxscale; + +const char CN_UNKNOWN[] = "Unknown"; + +const char CN_FACILITY[] = "facility"; +const char CN_LEVEL[] = "level"; + +const char CN_AUTHENTICATION_FAILURE[] = "authentication_failure"; + +const char EVENT_PREFIX[] = "event."; + + +struct NAME_AND_VALUE +{ + const char* zName; + int32_t value; +}; + +int name_and_value_compare(const void* pLeft, const void* pRight) +{ + const NAME_AND_VALUE* pL = static_cast(pLeft); + const NAME_AND_VALUE* pR = static_cast(pRight); + + return strcmp(pL->zName, pR->zName); +} + + +// Keep these in alphabetical order. +const NAME_AND_VALUE levels[] = +{ + { + "LOG_ALERT", + LOG_ALERT, + }, + { + "LOG_CRIT", + LOG_CRIT, + }, + { + "LOG_DEBUG", + LOG_DEBUG, + }, + { + "LOG_EMER", + LOG_EMERG, + }, + { + "LOG_ERR", + LOG_ERR, + }, + { + "LOG_INFO", + LOG_INFO, + }, + { + "LOG_NOTICE", + LOG_NOTICE, + }, + { + "LOG_WARNING", + LOG_WARNING, + }, +}; + +const int N_LEVELS = sizeof(levels) / sizeof(levels[0]); + + +// Keep these in alphabetical order. +const NAME_AND_VALUE facilities[] = +{ + { + "LOG_AUTH", + LOG_AUTH + }, + { + "LOG_AUTHPRIV", + LOG_AUTHPRIV + }, + { + "LOG_CRON", + LOG_CRON + }, + { + "LOG_DAEMON", + LOG_DAEMON + }, + { + "LOG_FTP", + LOG_FTP + }, + { + "LOG_KERN", + LOG_KERN + }, + { + "LOG_LOCAL0", + LOG_LOCAL0 + }, + { + "LOG_LOCAL1", + LOG_LOCAL1 + }, + { + "LOG_LOCAL2", + LOG_LOCAL2 + }, + { + "LOG_LOCAL3", + LOG_LOCAL3 + }, + { + "LOG_LOCAL4", + LOG_LOCAL4 + }, + { + "LOG_LOCAL5", + LOG_LOCAL5 + }, + { + "LOG_LOCAL6", + LOG_LOCAL6 + }, + { + "LOG_LOCAL7", + LOG_LOCAL0 + }, + { + "LOG_LPR", + LOG_LPR + }, + { + "LOG_MAIL", + LOG_MAIL + }, + { + "LOG_NEWS", + LOG_NEWS + }, + { + "LOG_SYSLOG", + LOG_SYSLOG + }, + { + "LOG_USER", + LOG_USER + }, + { + "LOG_UUCP", + LOG_UUCP + }, +}; + +const int N_FACILITIES = sizeof(facilities) / sizeof(facilities[0]); + + +struct EVENT +{ + const char* zName; + event::id_t id; + int32_t facility; + int32_t level; +}; + +// Keep these in alphabetical order. +EVENT events[] = +{ + { + CN_AUTHENTICATION_FAILURE, + event::AUTHENTICATION_FAILURE, + event::DEFAULT_FACILITY, + event::DEFAULT_LEVEL + } +}; + +const int N_EVENTS = sizeof(events) / sizeof(events[0]); + +int event_compare(const void* pLeft, const void* pRight) +{ + const EVENT* pL = static_cast(pLeft); + const EVENT* pR = static_cast(pRight); + + return strcmp(pL->zName, pR->zName); +} + + +struct +{ + EVENT* events; + const NAME_AND_VALUE* levels; + const NAME_AND_VALUE* facilities; +} this_unit = +{ + events, + levels, + facilities +}; + +event::result_t configure_facility(event::id_t id, const char* zValue) +{ + event::result_t rv = event::INVALID; + + int32_t facility; + if (log_facility_from_string(&facility, zValue)) + { + event::set_log_facility(id, facility); + rv = event::ACCEPTED; + } + else + { + MXS_ERROR("%s is not a valid facility.", zValue); + } + + return rv; +} + +event::result_t configure_level(event::id_t id, const char* zValue) +{ + event::result_t rv = event::INVALID; + + int32_t level; + if (log_level_from_string(&level, zValue)) + { + event::set_log_level(id, level); + rv = event::ACCEPTED; + } + else + { + MXS_ERROR("%s is not a valid level.", zValue); + } + + return rv; +} + +} + +namespace maxscale +{ + +const char* log_level_to_string(int32_t level) +{ + auto begin = this_unit.levels; + auto end = begin + N_LEVELS; + + auto i = find_if(begin, end, [level](const NAME_AND_VALUE& item) -> bool + { + return item.value == level; + }); + + return i == end ? CN_UNKNOWN : i->zName; +} + +bool log_level_from_string(int32_t* pLevel, const char* zValue) +{ + NAME_AND_VALUE key = { zValue }; + void* pResult = bsearch(&key, this_unit.levels, N_LEVELS, sizeof(NAME_AND_VALUE), + name_and_value_compare); + + if (pResult) + { + const NAME_AND_VALUE* pItem = static_cast(pResult); + + *pLevel = pItem->value; + } + + return pResult != nullptr; +} + +const char* log_facility_to_string(int32_t facility) +{ + auto begin = this_unit.facilities; + auto end = begin + N_FACILITIES; + + auto i = find_if(begin, end, [facility](const NAME_AND_VALUE& item) -> bool + { + return item.value == facility; + }); + + return i == end ? CN_UNKNOWN : i->zName; +} + +bool log_facility_from_string(int32_t* pFacility, const char* zValue) +{ + NAME_AND_VALUE key = { zValue }; + void* pResult = bsearch(&key, this_unit.facilities, N_FACILITIES, sizeof(NAME_AND_VALUE), + name_and_value_compare); + + if (pResult) + { + const NAME_AND_VALUE* pItem = static_cast(pResult); + + *pFacility = pItem->value; + } + + return pResult != nullptr; +} + + +namespace event +{ + +const char* to_string(id_t id) +{ + auto begin = this_unit.events; + auto end = begin + N_EVENTS; + + auto i = find_if(begin, end, [id](const EVENT& item) -> bool + { + return item.id == id; + }); + + return i == end ? CN_UNKNOWN : i->zName; +} + +bool from_string(id_t* pId, const char* zValue) +{ + EVENT key = { zValue }; + void* pResult = bsearch(&key, this_unit.events, N_EVENTS, sizeof(EVENT), + event_compare); + + if (pResult) + { + const EVENT* pItem = static_cast(pResult); + + *pId = pItem->id; + } + + return pResult != nullptr; +} + +void set_log_facility(id_t id, int32_t facility) +{ + bool rv = false; + ss_dassert((id >= 0) && (id < N_EVENTS)); + + // We silently strip away other than the relevant bits. + facility = facility & LOG_FACMASK; + + EVENT& event = this_unit.events[id]; + + atomic_store_int32(&event.facility, facility); +} + +int32_t get_log_facility(id_t id) +{ + ss_dassert((id >= 0) && (id < N_EVENTS)); + + const EVENT& event = this_unit.events[id]; + + return atomic_load_int32(&event.facility); +} + +void set_log_level(id_t id, int32_t level) +{ + ss_dassert((id >= 0) && (id < N_EVENTS)); + + // We silently strip away other than the relevant bits. + level = level & LOG_PRIMASK; + + EVENT& event = this_unit.events[id]; + + atomic_store_int32(&event.level, level); +} + +int32_t get_log_level(id_t id) +{ + ss_dassert((id >= 0) && (id < N_EVENTS)); + + const EVENT& event = this_unit.events[id]; + + return atomic_load_int32(&event.level); +} + +result_t configure(const char* zName, const char* zValue) +{ + result_t rv = IGNORED; + + if (strncmp(zName, EVENT_PREFIX, sizeof(EVENT_PREFIX) - 1) == 0) + { + rv = INVALID; + + string name(zName + sizeof(EVENT_PREFIX) - 1); // Character following '.' + + auto i = name.find_first_of('.'); + + if (i != string::npos) + { + string event = name.substr(0, i); + string property = name.substr(i + 1); + + id_t id; + if (from_string(&id, event.c_str())) + { + ss_dassert((id >= 0) && (id < N_EVENTS)); + + if (property == CN_FACILITY) + { + rv = configure_facility(id, zValue); + } + else if (property == CN_LEVEL) + { + rv = configure_level(id, zValue); + } + else + { + MXS_ERROR("%s is neither %s nor %s.", property.c_str(), CN_FACILITY, CN_LEVEL); + } + } + else + { + MXS_ERROR("%s does not refer to a known event.", zValue); + } + } + else + { + MXS_ERROR("%s is not a valid event configuration.", zName); + } + } + + return rv; +} + +void log(id_t event_id, + const char* modname, + const char* file, int line, const char* function, + const char* format, ...) +{ + va_list valist; + + ss_dassert((event_id >= 0) && (event_id < N_EVENTS)); + + const EVENT& event = this_unit.events[event_id]; + + int priority = atomic_load_int32(&event.facility) | atomic_load_int32(&event.level); + + va_start(valist, format); + int len = vsnprintf(NULL, 0, format, valist); + va_end(valist); + + if (len > BUFSIZ) + { + len = BUFSIZ; + } + + char message[len + 1]; + + va_start(valist, format); + vsnprintf(message, len + 1, format, valist); + va_end(valist); + + mxs_log_message(priority, modname, file, line, function, "%s", message); +} + +} // event + +} // maxscale diff --git a/server/core/internal/event.hh b/server/core/internal/event.hh new file mode 100644 index 000000000..228153f73 --- /dev/null +++ b/server/core/internal/event.hh @@ -0,0 +1,46 @@ +#pragma once +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl11. + * + * Change Date: 2020-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include +#include + +namespace maxscale +{ + +namespace event +{ + +enum result_t +{ + IGNORED, /**< The configuration was ignored, it does not affect events. */ + INVALID, /**< The configuration was invalid. */ + ACCEPTED /**< The configuration was accepted. */ +}; + +/** + * @brief Configure an event + * + * @param zName A MaxScale event configuration item name, + * such as "event.authentication_failure.facility" + * @param zValue The value it should be set to, e.g. "LOG_ERROR". + * + * @return IGNORED, if @c zName does not start with "event.", + * INVALID, if @c zName or @c zValue is invalid, and + * ACCEPTED, if @c zName and @c zValue are valid. + */ +result_t configure(const char* zName, const char* zValue); + +} + +} diff --git a/server/core/test/CMakeLists.txt b/server/core/test/CMakeLists.txt index 30ce55d58..245634599 100644 --- a/server/core/test/CMakeLists.txt +++ b/server/core/test/CMakeLists.txt @@ -4,6 +4,7 @@ add_executable(test_atomic test_atomic.cc) add_executable(test_buffer test_buffer.cc) add_executable(test_config test_config.cc) add_executable(test_dcb test_dcb.cc) +add_executable(test_event test_event.cc) add_executable(test_filter test_filter.cc) add_executable(test_hash test_hash.cc) add_executable(test_hint test_hint.cc) @@ -35,6 +36,7 @@ target_link_libraries(test_atomic maxscale-common) target_link_libraries(test_buffer maxscale-common) target_link_libraries(test_config maxscale-common) target_link_libraries(test_dcb maxscale-common) +target_link_libraries(test_event maxscale-common) target_link_libraries(test_filter maxscale-common) target_link_libraries(test_hash maxscale-common) target_link_libraries(test_hint maxscale-common) @@ -65,6 +67,7 @@ add_test(test_atomic test_atomic) add_test(test_buffer test_buffer) add_test(test_config test_config) add_test(test_dcb test_dcb) +add_test(test_event test_event) add_test(test_filter test_filter) add_test(test_hash test_hash) add_test(test_hint test_hint) diff --git a/server/core/test/test_event.cc b/server/core/test/test_event.cc new file mode 100644 index 000000000..9c216f9c9 --- /dev/null +++ b/server/core/test/test_event.cc @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl11. + * + * Change Date: 2020-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include "../internal/event.hh" +#include +#include +#include +#include +#include + +using namespace maxscale; +using namespace std; + +namespace +{ + +struct NAME_AND_VALUE +{ + const char* zName; + int32_t value; +}; + +const NAME_AND_VALUE levels[] = +{ + { + "LOG_ALERT", + LOG_ALERT, + }, + { + "LOG_CRIT", + LOG_CRIT, + }, + { + "LOG_DEBUG", + LOG_DEBUG, + }, + { + "LOG_EMER", + LOG_EMERG, + }, + { + "LOG_ERR", + LOG_ERR, + }, + { + "LOG_INFO", + LOG_INFO, + }, + { + "LOG_NOTICE", + LOG_NOTICE, + }, + { + "LOG_WARNING", + LOG_WARNING, + }, + { + "BLAH", + -1 + } +}; + +const int N_LEVELS = sizeof(levels) / sizeof(levels[0]); + +// Keep these in alphabetical order. +const NAME_AND_VALUE facilities[] = +{ + { + "LOG_AUTH", + LOG_AUTH + }, + { + "LOG_AUTHPRIV", + LOG_AUTHPRIV + }, + { + "LOG_CRON", + LOG_CRON + }, + { + "LOG_DAEMON", + LOG_DAEMON + }, + { + "LOG_FTP", + LOG_FTP + }, + { + "LOG_KERN", + LOG_KERN + }, + { + "LOG_LOCAL0", + LOG_LOCAL0 + }, + { + "LOG_LOCAL1", + LOG_LOCAL1 + }, + { + "LOG_LOCAL2", + LOG_LOCAL2 + }, + { + "LOG_LOCAL3", + LOG_LOCAL3 + }, + { + "LOG_LOCAL4", + LOG_LOCAL4 + }, + { + "LOG_LOCAL5", + LOG_LOCAL5 + }, + { + "LOG_LOCAL6", + LOG_LOCAL6 + }, + { + "LOG_LOCAL7", + LOG_LOCAL0 + }, + { + "LOG_LPR", + LOG_LPR + }, + { + "LOG_MAIL", + LOG_MAIL + }, + { + "LOG_NEWS", + LOG_NEWS + }, + { + "LOG_SYSLOG", + LOG_SYSLOG + }, + { + "LOG_USER", + LOG_USER + }, + { + "LOG_UUCP", + LOG_UUCP + }, + { + "BLAH", + -1 + } +}; + +const int N_FACILITIES = sizeof(facilities) / sizeof(facilities[0]); + + +template +int test_names_and_values(const NAME_AND_VALUE* begin, + const NAME_AND_VALUE* end, + bool (*from_string)(T*, const char* zName), + const char* (*to_string)(T), + const char* zProperty) +{ + int errors = 0; + + for_each(begin, end, [&errors, zProperty, from_string, to_string](const NAME_AND_VALUE& item) + { + T value; + bool rv = from_string(&value, item.zName); + + if (item.value != -1) + { + if (rv) + { + if (value != item.value) + { + ++errors; + cerr << "error: Wrong " << zProperty << " was returned for " + << item.zName << ", " << item.value << " was expected, but " + << value << " was returned." << endl; + } + } + else + { + ++errors; + cerr << "error: " << item.zName << " was not recognized as a syslog " + << zProperty << "." << endl; + } + + const char* zName = to_string(static_cast(item.value)); + + if (strcmp(zName, item.zName) != 0) + { + ++errors; + cerr << "error: Code " << item.value << " was converted to " << zName + << " although " << item.zName << " was expected." << endl; + } + } + else + { + if (rv) + { + ++errors; + cerr << "error: " << item.zName << " was incorrectly recognized " + << "as a syslog " << zProperty << " salthough it should not " + << "have been." << endl; + } + + const char* zName = to_string(static_cast(item.value)); + + if (strcmp(zName, "Unknown") != 0) + { + cerr << "error: Invalid code " << item.value << " was not converted " + << "to Unknown as expected but to " << zName << "." << endl; + } + } + }); + + return errors; +} + +int test_levels() +{ + const NAME_AND_VALUE* begin = levels; + const NAME_AND_VALUE* end = begin + N_LEVELS; + + return test_names_and_values(begin, end, &log_level_from_string, &log_level_to_string, "level"); +} + +int test_facilities() +{ + const NAME_AND_VALUE* begin = facilities; + const NAME_AND_VALUE* end = facilities + N_LEVELS; + + return test_names_and_values(begin, end, &log_facility_from_string, &log_facility_to_string, "facility"); +} + + +const NAME_AND_VALUE events[] = +{ + { + "authentication_failure", + event::AUTHENTICATION_FAILURE + } +}; + +const int N_EVENTS = sizeof(events) / sizeof(events[0]); + +int test_event_basics() +{ + int errors = 0; + + const NAME_AND_VALUE* begin = events; + const NAME_AND_VALUE* end = events + N_EVENTS; + + errors += test_names_and_values(begin, end, &event::from_string, &event::to_string, "event"); + + for_each(begin, end, [&errors](const NAME_AND_VALUE& item) + { + event::id_t id = static_cast(item.value); + + int32_t facility = event::get_log_facility(id); + + if (facility != event::DEFAULT_FACILITY) + { + ++errors; + cerr << "error: Default facility for " << event::to_string(id) << " was " + << log_facility_to_string(facility) << " and not " + << log_facility_to_string(event::DEFAULT_FACILITY) + << "." << endl; + } + + event::set_log_facility(id, LOG_LOCAL0); + + facility = event::get_log_facility(id); + + if (facility != LOG_LOCAL0) + { + ++errors; + cerr << "error: Set facility LOG_LOCAL0 as not stored, but was " + << log_facility_to_string(facility) << "." << endl; + } + + int32_t level = event::get_log_level(id); + + if (level != event::DEFAULT_LEVEL) + { + ++errors; + cerr << "error: Default level for " << event::to_string(id) << " was " + << log_level_to_string(level) << " and not " + << log_level_to_string(event::DEFAULT_LEVEL) + << "." << endl; + } + + event::set_log_level(id, LOG_ALERT); + + level = event::get_log_level(id); + + if (level != LOG_ALERT) + { + ++errors; + cerr << "error: Set level LOG_ALERT as not stored, but was " + << log_level_to_string(level) << "." << endl; + } + }); + + return errors; +} + + +enum class What +{ + FACILITY, + LEVEL, + IRRELEVANT, +}; + +struct CONFIGURATION +{ + const char* zParameter; + const char* zValue; + event::id_t id; + event::result_t result; + What what; + int32_t value; +}; + +const CONFIGURATION configurations[] = +{ + { + "event.authentication_failure.facility", + "LOG_LOCAL0", + event::AUTHENTICATION_FAILURE, + event::ACCEPTED, + What::FACILITY, + LOG_LOCAL0 + }, + { + "event.authentication_failure.level", + "LOG_ALERT", + event::AUTHENTICATION_FAILURE, + event::ACCEPTED, + What::LEVEL, + LOG_ALERT + }, + { + "event.authentication_failure.facility", + "LOG_BLAH", + event::AUTHENTICATION_FAILURE, + event::INVALID, + What::FACILITY, + -1 + }, + { + "event.authentication_failure.level", + "LOG_BLAH", + event::AUTHENTICATION_FAILURE, + event::INVALID, + What::LEVEL, + -1 + }, + { + "event.blah.facility", + "LOG_LOCAL0", + static_cast(-1), + event::INVALID, + What::FACILITY, + LOG_LOCAL0 + }, + { + "blah", + "LOG_LOCAL0", + static_cast(-1), + event::IGNORED, + What::IRRELEVANT, + LOG_LOCAL0 + } +}; + +const size_t N_CONFIGURATIONS = sizeof(configurations) / sizeof(configurations[0]); + +int test_event_configuration() +{ + int errors = 0; + + const CONFIGURATION* begin = configurations; + const CONFIGURATION* end = begin + N_CONFIGURATIONS; + + for_each(begin, end, [&errors](const CONFIGURATION& c) + { + event::result_t result = event::configure(c.zParameter, c.zValue); + + if (result == c.result) + { + if (result == event::ACCEPTED) + { + if (c.what == What::FACILITY) + { + int32_t facility = event::get_log_facility(c.id); + + if (facility != c.value) + { + ++errors; + cerr << "error: Configuration \"" + << c.zParameter << "=" << c.zValue + << " did not affect the facility in the expected way." + << endl; + } + } + else + { + ss_dassert(c.what == What::LEVEL); + + int32_t level = event::get_log_level(c.id); + + if (level != c.value) + { + ++errors; + cerr << "error: Configuration \"" + << c.zParameter << "=" << c.zValue + << " did not affect the level in the expected way." + << endl; + } + } + } + } + else + { + ++errors; + cerr << "error: Configuration \"" + << c.zParameter << "=" << c.zValue + << " did not produce the expected result." + << endl; + } + }); + + return errors; +} + +int test_events() +{ + int errors = 0; + + errors += test_event_basics(); + errors += test_event_configuration(); + + return errors; +} + +} + +int main() +{ + int errors = 0; + + errors += test_levels(); + errors += test_facilities(); + errors += test_events(); + + return errors; +}