view libirccd/irccd/daemon/irccd.cpp @ 596:35832b7f4f9d

Irccd: move files to daemon folder, closes #731
author David Demelier <markand@malikania.fr>
date Wed, 06 Dec 2017 11:42:44 +0100
parents libirccd/irccd/irccd.cpp@1ad88e2e3086
children a3eeb5e9c482
line wrap: on
line source

/*
 * irccd.cpp -- main irccd class
 *
 * Copyright (c) 2013-2017 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 <irccd/logger.hpp>
#include <irccd/string_util.hpp>
#include <irccd/system.hpp>

#include "command_service.hpp"
#include "irccd.hpp"
#include "plugin_service.hpp"
#include "rule_service.hpp"
#include "server_service.hpp"
#include "transport_service.hpp"

namespace irccd {

namespace {

class log_filter : public log::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.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));
    }
};

void load_log_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 {
        log::set_logger(std::make_unique<log::file_logger>(std::move(normal), std::move(errors)));
    } catch (const std::exception& ex) {
        log::warning() << "logs: " << ex.what() << std::endl;
    }
}

void load_log_syslog()
{
#if defined(HAVE_SYSLOG)
    log::set_logger(std::make_unique<log::syslog_logger>());
#else
    log::warning() << "logs: syslog is not available on this platform" << std::endl;
#endif // !HAVE_SYSLOG
}

} // !namespace

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

    if (sc.empty())
        return;

    log::set_verbose(string_util::is_identifier(sc.get("verbose").value()));

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

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

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

    if (sc.empty())
        return;

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

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

    if (path.empty())
        return;

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

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

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

    if (gid.empty())
        return;

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

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

    if (uid.empty())
        return;

#if defined(HAVE_SETUID)
    try {
        sys::set_uid(uid);
    } catch (const std::exception& ex) {
        log::warning() << "irccd: failed to set uid: " << ex.what() << std::endl;
    }
#else
    log::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)
    , command_service_(std::make_unique<command_service>())
    , server_service_(std::make_unique<server_service>(*this))
    , tpt_service_(std::make_unique<transport_service>(*this))
    , rule_service_(std::make_unique<rule_service>())
    , plugin_service_(std::make_unique<plugin_service>(*this))
{
}

irccd::~irccd() = default;

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_)
        log::info() << "irccd: loading configuration from " << config_.path() << std::endl;
    else
        log::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 boost::system::error_category& irccd_category()
{
    static const class category : public boost::system::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;
}

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

} // !irccd