anope

- supernets anope source code & configuration
git clone git://git.acid.vegas/anope.git
Log | Files | Refs | Archive | README

init.cpp (15629B)

      1 /* Initialization and related routines.
      2  *
      3  * (C) 2003-2022 Anope Team
      4  * Contact us at team@anope.org
      5  *
      6  * Please read COPYING and README for further details.
      7  *
      8  * Based on the original code of Epona by Lara.
      9  * Based on the original code of Services by Andy Church.
     10  */
     11 
     12 #include "services.h"
     13 #include "config.h"
     14 #include "users.h"
     15 #include "protocol.h"
     16 #include "bots.h"
     17 #include "xline.h"
     18 #include "socketengine.h"
     19 #include "servers.h"
     20 #include "language.h"
     21 
     22 #ifndef _WIN32
     23 #include <sys/wait.h>
     24 #include <sys/stat.h>
     25 
     26 #include <errno.h>
     27 #include <sys/types.h>
     28 #include <pwd.h>
     29 #include <grp.h>
     30 #endif
     31 
     32 Anope::string Anope::ConfigDir = "conf", Anope::DataDir = "data", Anope::ModuleDir = "lib", Anope::LocaleDir = "locale", Anope::LogDir = "logs";
     33 
     34 /* Vector of pairs of command line arguments and their params */
     35 static std::vector<std::pair<Anope::string, Anope::string> > CommandLineArguments;
     36 
     37 /** Called on startup to organize our starting arguments in a better way
     38  * and check for errors
     39  * @param ac number of args
     40  * @param av args
     41  */
     42 static void ParseCommandLineArguments(int ac, char **av)
     43 {
     44 	for (int i = 1; i < ac; ++i)
     45 	{
     46 		Anope::string option = av[i];
     47 		Anope::string param;
     48 		while (!option.empty() && option[0] == '-')
     49 			option.erase(option.begin());
     50 		size_t t = option.find('=');
     51 		if (t != Anope::string::npos)
     52 		{
     53 			param = option.substr(t + 1);
     54 			option.erase(t);
     55 		}
     56 
     57 		if (option.empty())
     58 			continue;
     59 
     60 		CommandLineArguments.push_back(std::make_pair(option, param));
     61 	}
     62 }
     63 
     64 /** Check if an argument was given on startup and its parameter
     65  * @param name The argument name
     66  * @param shortname A shorter name, eg --debug and -d
     67  * @param param A string to put the param, if any, of the argument
     68  * @return true if name/shortname was found, false if not
     69  */
     70 static bool GetCommandLineArgument(const Anope::string &name, char shortname, Anope::string &param)
     71 {
     72 	param.clear();
     73 
     74 	for (std::vector<std::pair<Anope::string, Anope::string> >::iterator it = CommandLineArguments.begin(), it_end = CommandLineArguments.end(); it != it_end; ++it)
     75 	{
     76 		if (it->first.equals_ci(name) || (it->first.length() == 1 && it->first[0] == shortname))
     77 		{
     78 			param = it->second;
     79 			return true;
     80 		}
     81 	}
     82 
     83 	return false;
     84 }
     85 
     86 /** Check if an argument was given on startup
     87  * @param name The argument name
     88  * @param shortname A shorter name, eg --debug and -d
     89  * @return true if name/shortname was found, false if not
     90  */
     91 static bool GetCommandLineArgument(const Anope::string &name, char shortname = 0)
     92 {
     93 	Anope::string Unused;
     94 	return GetCommandLineArgument(name, shortname, Unused);
     95 }
     96 
     97 bool Anope::AtTerm()
     98 {
     99 	return isatty(fileno(stdout)) && isatty(fileno(stdin)) && isatty(fileno(stderr));
    100 }
    101 
    102 static void setuidgid();
    103 
    104 void Anope::Fork()
    105 {
    106 #ifndef _WIN32
    107 	kill(getppid(), SIGUSR2);
    108 
    109 	if (!freopen("/dev/null", "r", stdin))
    110 		Log() << "Unable to redirect stdin to /dev/null: " << Anope::LastError();
    111 	if (!freopen("/dev/null", "w", stdout))
    112 		Log() << "Unable to redirect stdout to /dev/null: " << Anope::LastError();
    113 	if (!freopen("/dev/null", "w", stderr))
    114 		Log() << "Unable to redirect stderr to /dev/null: " << Anope::LastError();
    115 
    116 	setpgid(0, 0);
    117 
    118 	setuidgid();
    119 #else
    120 	FreeConsole();
    121 #endif
    122 }
    123 
    124 void Anope::HandleSignal()
    125 {
    126 	switch (Signal)
    127 	{
    128 		case SIGHUP:
    129 		{
    130 			Anope::SaveDatabases();
    131 
    132 			try
    133 			{
    134 				Configuration::Conf *new_config = new Configuration::Conf();
    135 				Configuration::Conf *old = Config;
    136 				Config = new_config;
    137 				Config->Post(old);
    138 				delete old;
    139 			}
    140 			catch (const ConfigException &ex)
    141 			{
    142 				Log() << "Error reloading configuration file: " << ex.GetReason();
    143 			}
    144 			break;
    145 		}
    146 		case SIGTERM:
    147 		case SIGINT:
    148 #ifndef _WIN32
    149 			Log() << "Received " << strsignal(Signal) << " signal (" << Signal << "), exiting.";
    150 			Anope::QuitReason = Anope::string("Services terminating via signal ") + strsignal(Signal) + " (" + stringify(Signal) + ")";
    151 #else
    152 			Log() << "Received signal " << Signal << ", exiting.";
    153 			Anope::QuitReason = Anope::string("Services terminating via signal ") + stringify(Signal);
    154 #endif
    155 			Anope::Quitting = true;
    156 			Anope::SaveDatabases();
    157 			break;
    158 	}
    159 
    160 	Signal = 0;
    161 }
    162 
    163 #ifndef _WIN32
    164 static void parent_signal_handler(int signal)
    165 {
    166 	if (signal == SIGUSR2)
    167 	{
    168 		Anope::Quitting = true;
    169 	}
    170 	else if (signal == SIGCHLD)
    171 	{
    172 		Anope::ReturnValue = -1;
    173 		Anope::Quitting = true;
    174 		int status = 0;
    175 		wait(&status);
    176 		if (WIFEXITED(status))
    177 			Anope::ReturnValue = WEXITSTATUS(status);
    178 	}
    179 }
    180 #endif
    181 
    182 static void SignalHandler(int sig)
    183 {
    184 	Anope::Signal = sig;
    185 }
    186 
    187 static void InitSignals()
    188 {
    189 	struct sigaction sa;
    190 
    191 	sa.sa_flags = 0;
    192 	sigemptyset(&sa.sa_mask);
    193 
    194 	sa.sa_handler = SignalHandler;
    195 
    196 	sigaction(SIGHUP, &sa, NULL);
    197 
    198 	sigaction(SIGTERM, &sa, NULL);
    199 	sigaction(SIGINT, &sa, NULL);
    200 
    201 	sa.sa_handler = SIG_IGN;
    202 
    203 #ifndef _WIN32
    204 	sigaction(SIGCHLD, &sa, NULL);
    205 #endif
    206 	sigaction(SIGPIPE, &sa, NULL);
    207 }
    208 
    209 /* Remove our PID file.  Done at exit. */
    210 
    211 static void remove_pidfile()
    212 {
    213 	remove(Config->GetBlock("serverinfo")->Get<const Anope::string>("pid").c_str());
    214 }
    215 
    216 /* Create our PID file and write the PID to it. */
    217 
    218 static void write_pidfile()
    219 {
    220 	FILE *pidfile = fopen(Config->GetBlock("serverinfo")->Get<const Anope::string>("pid").c_str(), "w");
    221 	if (pidfile)
    222 	{
    223 #ifdef _WIN32
    224 		fprintf(pidfile, "%d\n", static_cast<int>(GetCurrentProcessId()));
    225 #else
    226 		fprintf(pidfile, "%d\n", static_cast<int>(getpid()));
    227 #endif
    228 		fclose(pidfile);
    229 		atexit(remove_pidfile);
    230 	}
    231 	else
    232 		throw CoreException("Can not write to PID file " + Config->GetBlock("serverinfo")->Get<const Anope::string>("pid"));
    233 }
    234 
    235 static void setuidgid()
    236 {
    237 #ifndef _WIN32
    238 	Configuration::Block *options = Config->GetBlock("options");
    239 	uid_t uid = -1;
    240 	gid_t gid = -1;
    241 
    242 	if (!options->Get<const Anope::string>("user").empty())
    243 	{
    244 		errno = 0;
    245 		struct passwd *u = getpwnam(options->Get<const Anope::string>("user").c_str());
    246 		if (u == NULL)
    247 			Log() << "Unable to setuid to " << options->Get<const Anope::string>("user") << ": " << Anope::LastError();
    248 		else
    249 			uid = u->pw_uid;
    250 	}
    251 	if (!options->Get<const Anope::string>("group").empty())
    252 	{
    253 		errno = 0;
    254 		struct group *g = getgrnam(options->Get<const Anope::string>("group").c_str());
    255 		if (g == NULL)
    256 			Log() << "Unable to setgid to " << options->Get<const Anope::string>("group") << ": " << Anope::LastError();
    257 		else
    258 			gid = g->gr_gid;
    259 	}
    260 
    261 	for (unsigned i = 0; i < Config->LogInfos.size(); ++i)
    262 	{
    263 		LogInfo& li = Config->LogInfos[i];
    264 
    265 		for (unsigned j = 0; j < li.logfiles.size(); ++j)
    266 		{
    267 			LogFile* lf = li.logfiles[j];
    268 
    269 			errno = 0;
    270 			if (chown(lf->filename.c_str(), uid, gid) != 0)
    271 				Log() << "Unable to change the ownership of " << lf->filename << " to " << uid << "/" << gid << ": " << Anope::LastError();
    272 		}
    273 	}
    274 
    275 	if (static_cast<int>(gid) != -1)
    276 	{
    277 		if (setgid(gid) == -1)
    278 			Log() << "Unable to setgid to " << options->Get<const Anope::string>("group") << ": " << Anope::LastError();
    279 		else
    280 			Log() << "Successfully set group to " << options->Get<const Anope::string>("group");
    281 	}
    282 	if (static_cast<int>(uid) != -1)
    283 	{
    284 		if (setuid(uid) == -1)
    285 			Log() << "Unable to setuid to " << options->Get<const Anope::string>("user") << ": " << Anope::LastError();
    286 		else
    287 			Log() << "Successfully set user to " << options->Get<const Anope::string>("user");
    288 	}
    289 #endif
    290 }
    291 
    292 void Anope::Init(int ac, char **av)
    293 {
    294 	/* Set file creation mask and group ID. */
    295 #if defined(DEFUMASK) && HAVE_UMASK
    296 	umask(DEFUMASK);
    297 #endif
    298 
    299 	Serialize::RegisterTypes();
    300 
    301 	/* Parse command line arguments */
    302 	ParseCommandLineArguments(ac, av);
    303 
    304 	if (GetCommandLineArgument("version", 'v'))
    305 	{
    306 		Log(LOG_TERMINAL) << "Anope-" << Anope::Version() << " -- " << Anope::VersionBuildString();
    307 		throw CoreException();
    308 	}
    309 
    310 	if (GetCommandLineArgument("help", 'h'))
    311 	{
    312 		Log(LOG_TERMINAL) << "Anope-" << Anope::Version() << " -- " << Anope::VersionBuildString();
    313 		Log(LOG_TERMINAL) << "Anope IRC Services (https://www.anope.org/)";
    314 		Log(LOG_TERMINAL) << "Usage ./" << Anope::ServicesBin << " [options] ...";
    315 		Log(LOG_TERMINAL) << "-c, --config=filename.conf";
    316 		Log(LOG_TERMINAL) << "    --confdir=conf file directory";
    317 		Log(LOG_TERMINAL) << "    --dbdir=database directory";
    318 		Log(LOG_TERMINAL) << "-d, --debug[=level]";
    319 		Log(LOG_TERMINAL) << "-h, --help";
    320 		Log(LOG_TERMINAL) << "    --localedir=locale directory";
    321 		Log(LOG_TERMINAL) << "    --logdir=logs directory";
    322 		Log(LOG_TERMINAL) << "    --modulesdir=modules directory";
    323 		Log(LOG_TERMINAL) << "-e, --noexpire";
    324 		Log(LOG_TERMINAL) << "-n, --nofork";
    325 		Log(LOG_TERMINAL) << "    --nothird";
    326 		Log(LOG_TERMINAL) << "    --protocoldebug";
    327 		Log(LOG_TERMINAL) << "-r, --readonly";
    328 		Log(LOG_TERMINAL) << "-s, --support";
    329 		Log(LOG_TERMINAL) << "-v, --version";
    330 		Log(LOG_TERMINAL) << "";
    331 		Log(LOG_TERMINAL) << "Further support is available from https://www.anope.org/";
    332 		Log(LOG_TERMINAL) << "Or visit us on IRC at irc.anope.org #anope";
    333 		throw CoreException();
    334 	}
    335 
    336 	if (GetCommandLineArgument("nofork", 'n'))
    337 		Anope::NoFork = true;
    338 
    339 	if (GetCommandLineArgument("support", 's'))
    340 	{
    341 		Anope::NoFork = Anope::NoThird = true;
    342 		++Anope::Debug;
    343 	}
    344 
    345 	if (GetCommandLineArgument("readonly", 'r'))
    346 		Anope::ReadOnly = true;
    347 
    348 	if (GetCommandLineArgument("nothird"))
    349 		Anope::NoThird = true;
    350 
    351 	if (GetCommandLineArgument("noexpire", 'e'))
    352 		Anope::NoExpire = true;
    353 
    354 	if (GetCommandLineArgument("protocoldebug"))
    355 		Anope::ProtocolDebug = true;
    356 
    357 	Anope::string arg;
    358 	if (GetCommandLineArgument("debug", 'd', arg))
    359 	{
    360 		if (!arg.empty())
    361 		{
    362 			int level = arg.is_number_only() ? convertTo<int>(arg) : -1;
    363 			if (level > 0)
    364 				Anope::Debug = level;
    365 			else
    366 				throw CoreException("Invalid option given to --debug");
    367 		}
    368 		else
    369 			++Anope::Debug;
    370 	}
    371 
    372 	if (GetCommandLineArgument("config", 'c', arg))
    373 	{
    374 		if (arg.empty())
    375 			throw CoreException("The --config option requires a file name");
    376 		ServicesConf = Configuration::File(arg, false);
    377 	}
    378 
    379 	if (GetCommandLineArgument("confdir", 0, arg))
    380 	{
    381 		if (arg.empty())
    382 			throw CoreException("The --confdir option requires a path");
    383 		Anope::ConfigDir = arg;
    384 	}
    385 
    386 	if (GetCommandLineArgument("dbdir", 0, arg))
    387 	{
    388 		if (arg.empty())
    389 			throw CoreException("The --dbdir option requires a path");
    390 		Anope::DataDir = arg;
    391 	}
    392 
    393 	if (GetCommandLineArgument("localedir", 0, arg))
    394 	{
    395 		if (arg.empty())
    396 			throw CoreException("The --localedir option requires a path");
    397 		Anope::LocaleDir = arg;
    398 	}
    399 
    400 	if (GetCommandLineArgument("modulesdir", 0, arg))
    401 	{
    402 		if (arg.empty())
    403 			throw CoreException("The --modulesdir option requires a path");
    404 		Anope::ModuleDir = arg;
    405 	}
    406 
    407 	if (GetCommandLineArgument("logdir", 0, arg))
    408 	{
    409 		if (arg.empty())
    410 			throw CoreException("The --logdir option requires a path");
    411 		Anope::LogDir = arg;
    412 	}
    413 
    414 	/* Chdir to Services data directory. */
    415 	if (chdir(Anope::ServicesDir.c_str()) < 0)
    416 	{
    417 		throw CoreException("Unable to chdir to " + Anope::ServicesDir + ": " + Anope::LastError());
    418 	}
    419 
    420 	Log(LOG_TERMINAL) << "Anope " << Anope::Version() << ", " << Anope::VersionBuildString();
    421 
    422 #ifdef _WIN32
    423 	if (!SupportedWindowsVersion())
    424 		throw CoreException(GetWindowsVersion() + " is not a supported version of Windows");
    425 #else
    426 	/* If we're root, issue a warning now */
    427 	if (!getuid() && !getgid())
    428 	{
    429 		/* If we are configured to setuid later, don't issue a warning */
    430 		Configuration::Block *options = Config->GetBlock("options");
    431 		if (options->Get<const Anope::string>("user").empty())
    432 		{
    433 			std::cerr << "WARNING: You are currently running Anope as the root superuser. Anope does not" << std::endl;
    434 			std::cerr << "         require root privileges to run, and it is discouraged that you run Anope" << std::endl;
    435 			std::cerr << "         as the root superuser." << std::endl;
    436 			sleep(3);
    437 		}
    438 	}
    439 #endif
    440 
    441 #ifdef _WIN32
    442 	Log(LOG_TERMINAL) << "Using configuration file " << Anope::ConfigDir << "\\" << ServicesConf.GetName();
    443 #else
    444 	Log(LOG_TERMINAL) << "Using configuration file " << Anope::ConfigDir << "/" << ServicesConf.GetName();
    445 
    446 	/* Fork to background */
    447 	if (!Anope::NoFork)
    448 	{
    449 		/* Install these before fork() - it is possible for the child to
    450 		 * connect and kill() the parent before it is able to install the
    451 		 * handler.
    452 		 */
    453 		struct sigaction sa, old_sigusr2, old_sigchld;
    454 
    455 		sa.sa_flags = 0;
    456 		sigemptyset(&sa.sa_mask);
    457 		sa.sa_handler = parent_signal_handler;
    458 
    459 		sigaction(SIGUSR2, &sa, &old_sigusr2);
    460 		sigaction(SIGCHLD, &sa, &old_sigchld);
    461 
    462 		int i = fork();
    463 		if (i > 0)
    464 		{
    465 			sigset_t mask;
    466 
    467 			sigemptyset(&mask);
    468 			sigsuspend(&mask);
    469 
    470 			exit(Anope::ReturnValue);
    471 		}
    472 		else if (i == -1)
    473 		{
    474 			Log() << "Error, unable to fork: " << Anope::LastError();
    475 			Anope::NoFork = true;
    476 		}
    477 
    478 		/* Child doesn't need these */
    479 		sigaction(SIGUSR2, &old_sigusr2, NULL);
    480 		sigaction(SIGCHLD, &old_sigchld, NULL);
    481 	}
    482 
    483 #endif
    484 
    485 	/* Initialize the socket engine. Note that some engines can not survive a fork(), so this must be here. */
    486 	SocketEngine::Init();
    487 
    488 	/* Read configuration file; exit if there are problems. */
    489 	try
    490 	{
    491 		Config = new Configuration::Conf();
    492 	}
    493 	catch (const ConfigException &ex)
    494 	{
    495 		Log(LOG_TERMINAL) << ex.GetReason();
    496 		Log(LOG_TERMINAL) << "*** Support resources: Read through the services.conf self-contained";
    497 		Log(LOG_TERMINAL) << "*** documentation. Read the documentation files found in the 'docs'";
    498 		Log(LOG_TERMINAL) << "*** folder. Visit our portal located at https://www.anope.org/. Join";
    499 		Log(LOG_TERMINAL) << "*** our support channel on /server irc.anope.org channel #anope.";
    500 		throw CoreException("Configuration file failed to validate");
    501 	}
    502 
    503 	/* Create me */
    504 	Configuration::Block *block = Config->GetBlock("serverinfo");
    505 	Me = new Server(NULL, block->Get<const Anope::string>("name"), 0, block->Get<const Anope::string>("description"), block->Get<const Anope::string>("id"));
    506 	for (botinfo_map::const_iterator it = BotListByNick->begin(), it_end = BotListByNick->end(); it != it_end; ++it)
    507 	{
    508 		it->second->server = Me;
    509 		++Me->users;
    510 	}
    511 
    512 	/* Announce ourselves to the logfile. */
    513 	Log() << "Anope " << Anope::Version() << " starting up" << (Anope::Debug || Anope::ReadOnly ? " (options:" : "") << (Anope::Debug ? " debug" : "") << (Anope::ReadOnly ? " readonly" : "") << (Anope::Debug || Anope::ReadOnly ? ")" : "");
    514 
    515 	InitSignals();
    516 
    517 	/* Initialize multi-language support */
    518 	Language::InitLanguages();
    519 
    520 	/* Initialize random number generator */
    521 	block = Config->GetBlock("options");
    522 	srand(block->Get<unsigned>("seed") ^ time(NULL));
    523 
    524 	/* load modules */
    525 	Log() << "Loading modules...";
    526 	for (int i = 0; i < Config->CountBlock("module"); ++i)
    527 		ModuleManager::LoadModule(Config->GetBlock("module", i)->Get<const Anope::string>("name"), NULL);
    528 
    529 #ifndef _WIN32
    530 	/* We won't background later, so we should setuid now */
    531 	if (Anope::NoFork)
    532 		setuidgid();
    533 #endif
    534 
    535 	Module *protocol = ModuleManager::FindFirstOf(PROTOCOL);
    536 	if (protocol == NULL)
    537 		throw CoreException("You must load a protocol module!");
    538 
    539 	/* Write our PID to the PID file. */
    540 	write_pidfile();
    541 
    542 	Log() << "Using IRCd protocol " << protocol->name;
    543 
    544 	/* Auto assign sid if applicable */
    545 	if (IRCD->RequiresID)
    546 	{
    547 		Anope::string sid = IRCD->SID_Retrieve();
    548 		if (Me->GetSID() == Me->GetName())
    549 			Me->SetSID(sid);
    550 		for (botinfo_map::iterator it = BotListByNick->begin(), it_end = BotListByNick->end(); it != it_end; ++it)
    551 			it->second->GenerateUID();
    552 	}
    553 
    554 	/* Load up databases */
    555 	Log() << "Loading databases...";
    556 	EventReturn MOD_RESULT;
    557 	FOREACH_RESULT(OnLoadDatabase, MOD_RESULT, ());
    558 	static_cast<void>(MOD_RESULT);
    559 	Log() << "Databases loaded";
    560 
    561 	FOREACH_MOD(OnPostInit, ());
    562 
    563 	for (channel_map::const_iterator it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end; ++it)
    564 		it->second->Sync();
    565 
    566 	Serialize::CheckTypes();
    567 }