597 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			597 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 * 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: 2024-02-10
 | 
						|
 *
 | 
						|
 * 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 <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <syslog.h>
 | 
						|
#include <time.h>
 | 
						|
#include <algorithm>
 | 
						|
#include <fstream>
 | 
						|
#include <iostream>
 | 
						|
#include <sstream>
 | 
						|
#include <maxbase/assert.h>
 | 
						|
 | 
						|
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<class T>
 | 
						|
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<T>(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<T>(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<event::id_t>(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<event::id_t>(-1),
 | 
						|
        event::INVALID,
 | 
						|
        What::FACILITY,
 | 
						|
        LOG_LOCAL0
 | 
						|
    },
 | 
						|
    {
 | 
						|
        "blah",
 | 
						|
        "LOG_LOCAL0",
 | 
						|
        static_cast<event::id_t>(-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
 | 
						|
                         {
 | 
						|
                             mxb_assert(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;
 | 
						|
}
 | 
						|
 | 
						|
string get_auth_log()
 | 
						|
{
 | 
						|
    string name;
 | 
						|
 | 
						|
    const char DEBIAN_AUTH_LOG[] = "/var/log/auth.log";
 | 
						|
    const char REDHAT_AUTH_LOG[] = "/var/log/secure";
 | 
						|
 | 
						|
    if (access(DEBIAN_AUTH_LOG, F_OK) == 0)
 | 
						|
    {
 | 
						|
        cout << "notice: " << DEBIAN_AUTH_LOG << " exists, assuming a Debian system." << endl;
 | 
						|
        name = DEBIAN_AUTH_LOG;
 | 
						|
    }
 | 
						|
    else if (access(REDHAT_AUTH_LOG, F_OK) == 0)
 | 
						|
    {
 | 
						|
        cout << "notice: " << REDHAT_AUTH_LOG << " exists, assuming a RedHat system." << endl;
 | 
						|
        name = REDHAT_AUTH_LOG;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        cout << "warning: Neither " << DEBIAN_AUTH_LOG << ", nor " << REDHAT_AUTH_LOG << " exists." << endl;
 | 
						|
    }
 | 
						|
 | 
						|
    return name;
 | 
						|
}
 | 
						|
 | 
						|
int test_logging()
 | 
						|
{
 | 
						|
    int errors = 0;
 | 
						|
 | 
						|
    event::set_log_facility(event::AUTHENTICATION_FAILURE, LOG_AUTH);
 | 
						|
    event::set_log_level(event::AUTHENTICATION_FAILURE, LOG_ERR);
 | 
						|
 | 
						|
    stringstream ss;
 | 
						|
    ss << "test_event_";
 | 
						|
    ss << getpid();
 | 
						|
    ss << "_";
 | 
						|
 | 
						|
    for (int i = 0; i < 2; ++i)
 | 
						|
    {
 | 
						|
        ss << random();
 | 
						|
    }
 | 
						|
 | 
						|
    string id = ss.str();
 | 
						|
 | 
						|
    MXS_LOG_EVENT(event::AUTHENTICATION_FAILURE, "%s", id.c_str());
 | 
						|
 | 
						|
    string name = get_auth_log();
 | 
						|
 | 
						|
    if (name.empty())
 | 
						|
    {
 | 
						|
        cout << "warning: Don't know where to look for authentication errors. Ignoring test." << endl;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        if (access(name.c_str(), R_OK) != 0)
 | 
						|
        {
 | 
						|
            cout << "warning: Cannot read " << name << ", ignoring test." << endl;
 | 
						|
            name.clear();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (!name.empty())
 | 
						|
    {
 | 
						|
        // We have no control over how quickly syslog messages are flushed
 | 
						|
        // to the file. So, we try a few times before giving up.
 | 
						|
 | 
						|
        bool found = false;
 | 
						|
        int attempts = 0;
 | 
						|
        const int MAX_ATTEMPTS = 10;
 | 
						|
 | 
						|
        do
 | 
						|
        {
 | 
						|
            ++attempts;
 | 
						|
 | 
						|
            sleep(1);
 | 
						|
 | 
						|
            ifstream in(name);
 | 
						|
 | 
						|
            if (in)
 | 
						|
            {
 | 
						|
                string line;
 | 
						|
                while (std::getline(in, line))
 | 
						|
                {
 | 
						|
                    if (line.find(id) != string::npos)
 | 
						|
                    {
 | 
						|
                        found = true;
 | 
						|
                        cout << "notice: Found '" << id << "' in line '" << line << "'." << endl;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                cerr << "error: Could not open '" << name << "'." << endl;
 | 
						|
                attempts = MAX_ATTEMPTS;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        while (!found && (attempts < MAX_ATTEMPTS));
 | 
						|
 | 
						|
        errors = found ? 0 : 1;
 | 
						|
    }
 | 
						|
 | 
						|
    return errors;
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
int main()
 | 
						|
{
 | 
						|
    int errors = 0;
 | 
						|
 | 
						|
    srandom(time(NULL));
 | 
						|
 | 
						|
    if (mxs_log_init("TEST_EVENT", ".", MXS_LOG_TARGET_DEFAULT))
 | 
						|
    {
 | 
						|
        mxs_log_set_syslog_enabled(true);
 | 
						|
 | 
						|
        errors += test_levels();
 | 
						|
        errors += test_facilities();
 | 
						|
        errors += test_events();
 | 
						|
        errors += test_logging();
 | 
						|
 | 
						|
        mxs_log_finish();
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        ++errors;
 | 
						|
        cerr << "error: Could not initialize log manager." << endl;
 | 
						|
    }
 | 
						|
 | 
						|
    return errors;
 | 
						|
}
 |