view irccd/main.cpp @ 179:ef527409e638

Irccd: update JavaScript code
author David Demelier <markand@malikania.fr>
date Tue, 31 May 2016 21:03:01 +0200
parents bc291b131f6a
children 6635b9187d71
line wrap: on
line source

/*
 * main.cpp -- irccd main file
 *
 * Copyright (c) 2013-2016 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/sysconfig.hpp>

#if defined(HAVE_GETPID)
#  include <sys/types.h>
#  include <unistd.h>
#  include <cerrno>
#  include <cstring>
#  include <fstream>
#endif

#if defined(HAVE_DAEMON)
#  include <cstdlib>
#endif

#include <csignal>
#include <iostream>

#include <format.h>

#include <irccd/logger.hpp>
#include <irccd/options.hpp>
#include <irccd/path.hpp>
#include <irccd/service-plugin.hpp>
#include <irccd/service-rule.hpp>
#include <irccd/service-server.hpp>
#include <irccd/service-transport.hpp>
#include <irccd/system.hpp>
#include <irccd/config.hpp>
#include <irccd/irccd.hpp>

using namespace fmt::literals;

using namespace irccd;

namespace {

std::unique_ptr<Irccd> instance;

void usage()
{
	log::warning() << "usage: " << sys::programName() << " [options...]\n\n";
	log::warning() << "Available options:\n";
	log::warning() << "  -c, --config file       specify the configuration file\n";
	log::warning() << "  -f, --foreground        do not run as a daemon\n";
	log::warning() << "      --help              show this help\n";
	log::warning() << "  -p, --plugin name       load a specific plugin\n";
	log::warning() << "  -v, --verbose           be verbose\n";
	log::warning() << "      --version           show the version\n";
	std::exit(1);
}

void stop(int)
{
	instance->stop();
}

void init(int &argc, char **&argv)
{
	// Needed for some components.
	sys::setProgramName("irccd");
	path::setApplicationPath(argv[0]);

	// Default logging to console.
	log::setVerbose(false);
	log::setInterface(std::make_unique<log::Console>());

	// Register some signals.
	signal(SIGINT, stop);
	signal(SIGTERM, stop);

	-- argc;
	++ argv;
}

parser::Result parse(int &argc, char **&argv)
{
	// Parse command line options.
	parser::Result result;

	try {
		parser::Options options{
			{ "-c",			true	},
			{ "--config",		true	},
			{ "-f",			false	},
			{ "--foreground",	false	},
			{ "--help",		false	},
			{ "-p",			true	},
			{ "--plugin",		true	},
			{ "-v",			false	},
			{ "--verbose",		false	},
			{ "--version",		false	}
		};

		result = parser::read(argc, argv, options);

		for (const auto &pair : result) {
			if (pair.first == "--help")
				usage();
				// NOTREACHED
			if (pair.first == "--version") {
				std::cout << IRCCD_VERSION << std::endl;
				std::exit(1);
			}
			if (pair.first == "-v" || pair.first == "--verbose")
				log::setVerbose(true);
		}
	} catch (const std::exception &ex) {
		log::warning() << sys::programName() << ": " << ex.what() << std::endl;
		usage();
	}

	return result;
}

Config open(const parser::Result &result)
{
	auto it = result.find("-c");

	if (it != result.end() || (it = result.find("--config")) != result.end()) {
		try {
			return Config(it->second);
		} catch (const std::exception &ex) {
			throw std::runtime_error("{}: {}"_format(it->second, ex.what()));
		}
	}

	return Config::find();
}

void loadPid(const std::string &path)
{
	if (path.empty())
		return;

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

		if (!out)
			throw std::runtime_error("could not open pidfile {}: {}"_format(path, std::strerror(errno)));

		log::debug() << "irccd: pid written in " << path << std::endl;
		out << getpid() << std::endl;
#else
		throw std::runtime_error("pidfile option not supported on this platform");
#endif
	} catch (const std::exception &ex) {
		log::warning() << "irccd: " << ex.what() << std::endl;
	}
}

void loadGid(const std::string gid)
{
	try {
		if (!gid.empty())
#if defined(HAVE_SETGID)
			sys::setGid(gid);
#else
			throw std::runtime_error(" gid option not supported on this platform");
#endif
	} catch (const std::exception &ex) {
		log::warning() << "irccd: " << ex.what() << std::endl;
	}
}

void loadUid(const std::string &uid)
{
	try {
		if (!uid.empty())
#if defined(HAVE_SETUID)
			sys::setUid(uid);
#else
			throw std::runtime_error("uid option not supported on this platform");
#endif
	} catch (const std::exception &ex) {
		log::warning() << "irccd: " << ex.what() << std::endl;
	}
}

void loadForeground(bool foreground, const parser::Result &options)
{
	try {
#if defined(HAVE_DAEMON)
		if (options.count("-f") == 0 && options.count("--foreground") == 0 && !foreground)
			daemon(1, 0);
#else
		if (options.count("-f") > 0 || options.count("--foreground") > 0 || foreground)
			throw std::runtime_error("foreground option not supported on this platform");
#endif
	} catch (const std::exception &ex) {
		log::warning() << "irccd: " << ex.what() << std::endl;
	}
}

void load(const Config &config, const parser::Result &options)
{
	/*
	 * 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.
	config.loadLogs();
	config.loadFormats();

	// Show message here to use the formats.
	log::info() << "irccd: using " << config.path() << std::endl;

	// [general] section.
	loadPid(config.pidfile());
	loadGid(config.gid());
	loadUid(config.uid());
	loadForeground(config.isForeground(), options);

	// [transport]
	for (const auto &transport : config.loadTransports())
		instance->transportService().add(transport);

	// [server] section.
	for (const auto &server : config.loadServers())
		instance->serverService().add(server);

	// [rule] section.
	for (const auto &rule : config.loadRules())
		instance->ruleService().add(rule);

	// [plugin] section.
	config.loadPlugins(*instance);
}

} // !namespace

int main(int argc, char **argv)
{
	init(argc, argv);

	parser::Result options = parse(argc, argv);

	// Find configuration file.
	instance = std::make_unique<Irccd>();

	try {
		load(open(options), options);
	} catch (const std::exception &ex) {
		log::warning() << "irccd: " << ex.what() << std::endl;
		return 1;
	}

	instance->run();

	return 0;
}