view libirccd/irccd/daemon/irccd.cpp @ 690:849c4337c18e

Misc: headers reduction
author David Demelier <markand@malikania.fr>
date Tue, 17 Apr 2018 12:22:15 +0200
parents 168ea30142d9
children 91bc29e87399
line wrap: on
line source

/*
 * irccd.cpp -- main irccd class
 *
 * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <fstream>

#include <irccd/string_util.hpp>
#include <irccd/system.hpp>

#include "irccd.hpp"
#include "logger.hpp"

#include "service/plugin_service.hpp"
#include "service/rule_service.hpp"
#include "service/server_service.hpp"
#include "service/transport_service.hpp"

namespace irccd {

namespace {

class log_filter : public logger_filter {
private:
    std::string info_;
    std::string warning_;
    std::string debug_;

    std::string convert(const std::string& tmpl, std::string input) const
    {
        if (tmpl.empty())
            return input;

        string_util::subst params;

        params.flags &= ~(string_util::subst_flags::irc_attrs);
        params.flags |= string_util::subst_flags::shell_attrs;
        params.keywords.emplace("message", std::move(input));

        return string_util::format(tmpl, params);
    }

public:
    inline log_filter(std::string info, std::string warning, std::string debug) noexcept
        : info_(std::move(info))
        , warning_(std::move(warning))
        , debug_(std::move(debug))
    {
    }

    std::string pre_debug(std::string input) const override
    {
        return convert(debug_, std::move(input));
    }

    std::string pre_info(std::string input) const override
    {
        return convert(info_, std::move(input));
    }

    std::string pre_warning(std::string input) const override
    {
        return convert(warning_, std::move(input));
    }
};

} // !namespace

void irccd::load_logs_file(const ini::section& sc)
{
    /*
     * TODO: improve that with CMake options.
     */
#if defined(IRCCD_SYSTEM_WINDOWS)
    std::string normal = "log.txt";
    std::string errors = "errors.txt";
#else
    std::string normal = "/var/log/irccd/log.txt";
    std::string errors = "/var/log/irccd/errors.txt";
#endif

    ini::section::const_iterator it;

    if ((it = sc.find("path-logs")) != sc.end())
        normal = it->value();
    if ((it = sc.find("path-errors")) != sc.end())
        errors = it->value();

    try {
        logger_ = std::make_unique<file_logger>(std::move(normal), std::move(errors));
    } catch (const std::exception& ex) {
        logger_->warning() << "logs: " << ex.what() << std::endl;
    }
}

void irccd::load_logs_syslog()
{
#if defined(HAVE_SYSLOG)
    logger_ = std::make_unique<syslog_logger>();
#else
    logger_->warning() << "logs: syslog is not available on this platform" << std::endl;
#endif // !HAVE_SYSLOG
}

void irccd::load_logs()
{
    const auto sc = config_.get("logs");

    if (sc.empty())
        return;

    logger_->set_verbose(string_util::is_identifier(sc.get("verbose").value()));

    const auto type = sc.get("type").value();

    if (!type.empty()) {
        // Console is the default, no test case.
        if (type == "file")
            load_logs_file(sc);
        else if (type == "syslog")
            load_logs_syslog();
        else if (type != "console")
            logger_->warning() << "logs: invalid log type '" << type << std::endl;
    }
}

void irccd::load_formats()
{
    const auto sc = config_.get("format");

    if (sc.empty())
        return;

    logger_->set_filter(std::make_unique<log_filter>(
        sc.get("info").value(),
        sc.get("warning").value(),
        sc.get("debug").value()
    ));
}

void irccd::load_pid()
{
    const auto path = config_.get("general").get("pidfile").value();

    if (path.empty())
        return;

#if defined(HAVE_GETPID)
    std::ofstream out(path, std::ofstream::trunc);

    if (!out)
        logger_->warning() << "irccd: could not open" << path << ": " << std::strerror(errno) << std::endl;
    else {
        logger_->debug() << "irccd: pid written in " << path << std::endl;
        out << getpid() << std::endl;
    }
#else
    logger_->warning() << "irccd: pidfile not supported on this platform" << std::endl;
#endif
}

void irccd::load_gid()
{
    const auto gid = config_.get("general").get("gid").value();

    if (gid.empty())
        return;

#if defined(HAVE_SETGID)
    try {
        sys::set_gid(gid);
        logger_->info() << "irccd: setting gid to: " << gid << std::endl;
    } catch (const std::exception& ex) {
        logger_->warning() << "irccd: failed to set gid: " << ex.what() << std::endl;
    }
#else
    logger_->warning() << "irccd: gid option not supported" << std::endl;
#endif
}

void irccd::load_uid()
{
    const auto uid = config_.get("general").get("uid").value();

    if (uid.empty())
        return;

#if defined(HAVE_SETUID)
    try {
        sys::set_uid(uid);
        logger_->info() << "irccd: setting uid to: " << uid << std::endl;
    } catch (const std::exception& ex) {
        logger_->warning() << "irccd: failed to set uid: " << ex.what() << std::endl;
    }
#else
    logger_->warning() << "irccd: uid option not supported" << std::endl;
#endif
}

irccd::irccd(boost::asio::io_service& service, std::string config)
    : config_(std::move(config))
    , service_(service)
    , logger_(std::make_unique<console_logger>())
    , server_service_(std::make_unique<server_service>(*this))
    , tpt_service_(std::make_unique<transport_service>(*this))
    , rule_service_(std::make_unique<rule_service>(*this))
    , plugin_service_(std::make_unique<plugin_service>(*this))
{
}

irccd::~irccd() = default;

void irccd::set_log(std::unique_ptr<logger> logger) noexcept
{
    assert(logger);

    logger_ = std::move(logger);
}

void irccd::load() noexcept
{
    /*
     * Order matters, please be careful when changing this.
     *
     * 1. Open logs as early as possible to use the defined outputs on any
     *    loading errors.
     */

    // [logs] and [format] sections.
    load_logs();
    load_formats();

    if (!loaded_)
        logger_->info() << "irccd: loading configuration from " << config_.get_path() << std::endl;
    else
        logger_->info() << "irccd: reloading configuration" << std::endl;

    // [general] section.
    if (!loaded_) {
        load_pid();
        load_gid();
        load_uid();
    }

    if (!loaded_)
        tpt_service_->load(config_);

    server_service_->load(config_);
    plugin_service_->load(config_);
    rule_service_->load(config_);

    // Mark as loaded.
    loaded_ = true;
}

const std::error_category& irccd_category()
{
    static const class category : public std::error_category {
    public:
        const char* name() const noexcept override
        {
            return "irccd";
        }

        std::string message(int e) const override
        {
            switch (static_cast<irccd_error::error>(e)) {
            case irccd_error::error::not_irccd:
                return "daemon is not irccd instance";
            case irccd_error::error::incompatible_version:
                return "major version is incompatible";
            case irccd_error::error::auth_required:
                return "authentication is required";
            case irccd_error::error::invalid_auth:
                return "invalid authentication";
            case irccd_error::error::invalid_message:
                return "invalid message";
            case irccd_error::error::invalid_command:
                return "invalid command";
            case irccd_error::error::incomplete_message:
                return "command requires more arguments";
            default:
                return "no error";
            }
        }
    } category;

    return category;
}

std::error_code make_error_code(irccd_error::error e)
{
    return {static_cast<int>(e), irccd_category()};
}

} // !irccd