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 ¶m) 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 }