void ServerConfig::Apply(ServerConfig* old, const std::string &useruid) { valid = true; if (old) { /* * These values can only be set on boot. Keep their old values. Do it before we send messages so we actually have a servername. */ this->CaseMapping = old->CaseMapping; this->ServerName = old->ServerName; this->sid = old->sid; this->cmdline = old->cmdline; } /* The stuff in here may throw CoreException, be sure we're in a position to catch it. */ try { // Ensure the user has actually edited ther config. ConfigTagList dietags = ConfTags("die"); if (dietags.first != dietags.second) { errstr << "Your configuration has not been edited correctly!" << std::endl; for (ConfigIter iter = dietags.first; iter != dietags.second; ++iter) { ConfigTag* tag = iter->second; const std::string reason = tag->getString("reason", "You left a <die> tag in your config", 1); errstr << reason << " (at " << tag->getTagLocation() << ")" << std::endl; } } Fill(); // Handle special items CrossCheckOperClassType(); CrossCheckConnectBlocks(old); } catch (CoreException &ce) { errstr << ce.GetReason() << std::endl; } // Check errors before dealing with failed binds, since continuing on failed bind is wanted in some circumstances. valid = errstr.str().empty(); // write once here, to try it out and make sure its ok if (valid) ServerInstance->WritePID(this->PID, !old); ConfigTagList binds = ConfTags("bind"); if (binds.first == binds.second) errstr << "Possible configuration error: you have not defined any <bind> blocks." << std::endl << "You will need to do this if you want clients to be able to connect!" << std::endl; if (old && valid) { // On first run, ports are bound later on FailedPortList pl; ServerInstance->BindPorts(pl); if (pl.size()) { errstr << "Not all your client ports could be bound." << std::endl << "The following port(s) failed to bind:" << std::endl; int j = 1; for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++) { errstr << j << ".\tAddress: " << i->first.str() << "\tReason: " << strerror(i->second) << std::endl; } } } User* user = useruid.empty() ? NULL : ServerInstance->FindNick(useruid); if (!valid) { ServerInstance->Logs.Log("CONFIG", LOG_DEFAULT, "There were errors in your configuration file:"); Classes.clear(); } while (errstr.good()) { std::string line; getline(errstr, line, '\n'); if (line.empty()) continue; // On startup, print out to console (still attached at this point) if (!old) std::cout << line << std::endl; // If a user is rehashing, tell them directly if (user) user->WriteRemoteNotice(InspIRCd::Format("*** %s", line.c_str())); // Also tell opers ServerInstance->SNO.WriteGlobalSno('a', line); } errstr.clear(); errstr.str(std::string()); /* No old configuration -> initial boot, nothing more to do here */ if (!old) { if (!valid) { ServerInstance->Exit(EXIT_STATUS_CONFIG); } return; } // If there were errors processing configuration, don't touch modules. if (!valid) return; ApplyModules(user); if (user) user->WriteRemoteNotice("*** Successfully rehashed server."); ServerInstance->SNO.WriteGlobalSno('a', "*** Successfully rehashed server."); }
void ServerConfig::Apply(ServerConfig* old, const std::string &useruid) { valid = true; if (old) { /* * These values can only be set on boot. Keep their old values. Do it before we send messages so we actually have a servername. */ this->ServerName = old->ServerName; this->sid = old->sid; this->cmdline = old->cmdline; } /* The stuff in here may throw CoreException, be sure we're in a position to catch it. */ try { for (int index = 0; index * sizeof(DeprecatedConfig) < sizeof(ChangedConfig); index++) { std::string value; ConfigTagList tags = ConfTags(ChangedConfig[index].tag); for(ConfigIter i = tags.first; i != tags.second; ++i) { if (i->second->readString(ChangedConfig[index].key, value, true) && (ChangedConfig[index].value.empty() || value == ChangedConfig[index].value)) { errstr << "Your configuration contains a deprecated value: <" << ChangedConfig[index].tag; if (ChangedConfig[index].value.empty()) { errstr << ':' << ChangedConfig[index].key; } else { errstr << ' ' << ChangedConfig[index].key << "=\"" << ChangedConfig[index].value << "\""; } errstr << "> - " << ChangedConfig[index].reason << " (at " << i->second->getTagLocation() << ")\n"; } } } Fill(); // Handle special items CrossCheckOperClassType(); CrossCheckConnectBlocks(old); } catch (CoreException &ce) { errstr << ce.GetReason(); } // Check errors before dealing with failed binds, since continuing on failed bind is wanted in some circumstances. valid = errstr.str().empty(); // write once here, to try it out and make sure its ok if (valid) ServerInstance->WritePID(this->PID); if (old) { // On first run, ports are bound later on FailedPortList pl; ServerInstance->BindPorts(pl); if (pl.size()) { errstr << "Not all your client ports could be bound.\nThe following port(s) failed to bind:\n"; int j = 1; for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++) { char buf[MAXBUF]; snprintf(buf, MAXBUF, "%d. Address: %s Reason: %s\n", j, i->first.empty() ? "<all>" : i->first.c_str(), i->second.c_str()); errstr << buf; } } } User* user = useruid.empty() ? NULL : ServerInstance->FindNick(useruid); if (!valid) ServerInstance->Logs->Log("CONFIG",LOG_DEFAULT, "There were errors in your configuration file:"); while (errstr.good()) { std::string line; getline(errstr, line, '\n'); if (line.empty()) continue; // On startup, print out to console (still attached at this point) if (!old) std::cout << line << std::endl; // If a user is rehashing, tell them directly if (user) user->SendText(":%s NOTICE %s :*** %s", ServerInstance->Config->ServerName.c_str(), user->nick.c_str(), line.c_str()); // Also tell opers ServerInstance->SNO->WriteGlobalSno('a', line); } errstr.clear(); errstr.str(std::string()); // Re-parse our MOTD and RULES files for colors -- Justasic for (ClassVector::const_iterator it = this->Classes.begin(), it_end = this->Classes.end(); it != it_end; ++it) { ConfigTag *tag = (*it)->config; // Make sure our connection class allows motd colors if(!tag->getBool("allowmotdcolors")) continue; ConfigFileCache::iterator file = this->Files.find(tag->getString("motd", "motd")); if (file != this->Files.end()) InspIRCd::ProcessColors(file->second); file = this->Files.find(tag->getString("rules", "rules")); if (file != this->Files.end()) InspIRCd::ProcessColors(file->second); } /* No old configuration -> initial boot, nothing more to do here */ if (!old) { if (!valid) { ServerInstance->Exit(EXIT_STATUS_CONFIG); } return; } // If there were errors processing configuration, don't touch modules. if (!valid) return; ApplyModules(user); if (user) user->SendText(":%s NOTICE %s :*** Successfully rehashed server.", ServerInstance->Config->ServerName.c_str(), user->nick.c_str()); ServerInstance->SNO->WriteGlobalSno('a', "*** Successfully rehashed server."); }
InspIRCd::InspIRCd(int argc, char** argv) : ConfigFileName(CONFIG_PATH "/inspircd.conf"), /* Functor pointer initialisation. * * THIS MUST MATCH THE ORDER OF DECLARATION OF THE FUNCTORS, e.g. the methods * themselves within the class. */ OperQuit("operquit", NULL), GenRandom(&HandleGenRandom), IsChannel(&HandleIsChannel), IsNick(&HandleIsNick), IsIdent(&HandleIsIdent), OnCheckExemption(&HandleOnCheckExemption) { ServerInstance = this; Extensions.Register(&OperQuit); FailedPortList pl; // Flag variables passed to getopt_long() later int do_version = 0, do_nofork = 0, do_debug = 0, do_nolog = 0, do_root = 0; // Initialize so that if we exit before proper initialization they're not deleted this->Logs = 0; this->Threads = 0; this->PI = 0; this->Users = 0; this->Config = 0; this->SNO = 0; this->BanCache = 0; this->Modules = 0; this->stats = 0; this->Parser = 0; this->XLines = 0; this->Modes = 0; this->ConfigThread = NULL; this->FakeClient = NULL; UpdateTime(); this->startup_time = TIME.tv_sec; // This must be created first, so other parts of Insp can use it while starting up this->Logs = new LogManager; SocketEngine::Init(); this->Threads = new ThreadEngine; /* Default implementation does nothing */ this->PI = new ProtocolInterface; // Create base manager classes early, so nothing breaks this->Users = new UserManager; this->Config = new ServerConfig; this->SNO = new SnomaskManager; this->BanCache = new BanCacheManager; this->Modules = new ModuleManager(); dynamic_reference_base::reset_all(); this->stats = new serverstats(); this->Parser = new CommandParser; this->XLines = new XLineManager; this->Config->cmdline.argv = argv; this->Config->cmdline.argc = argc; #ifdef _WIN32 srand(TIME.tv_nsec ^ TIME.tv_sec); // Initialize the console values g_hStdout = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO bufinf; if(GetConsoleScreenBufferInfo(g_hStdout, &bufinf)) { g_wOriginalColors = bufinf.wAttributes & 0x00FF; g_wBackgroundColor = bufinf.wAttributes & 0x00F0; } else { g_wOriginalColors = FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN; g_wBackgroundColor = 0; } #else srandom(TIME.tv_nsec ^ TIME.tv_sec); #endif struct option longopts[] = { { "nofork", no_argument, &do_nofork, 1 }, { "config", required_argument, NULL, 'c' }, { "debug", no_argument, &do_debug, 1 }, { "nolog", no_argument, &do_nolog, 1 }, { "runasroot", no_argument, &do_root, 1 }, { "version", no_argument, &do_version, 1 }, #ifdef INSPIRCD_ENABLE_TESTSUITE { "testsuite", no_argument, &do_testsuite, 1 }, #endif { 0, 0, 0, 0 } }; int c; int index; while ((c = getopt_long(argc, argv, ":c:", longopts, &index)) != -1) { switch (c) { case 'c': /* Config filename was set */ ConfigFileName = ServerInstance->Config->Paths.PrependConfig(optarg); break; case 0: /* getopt_long_only() set an int variable, just keep going */ break; case '?': /* Unknown parameter */ default: /* Fall through to handle other weird values too */ std::cout << "Unknown parameter '" << argv[optind-1] << "'" << std::endl; std::cout << "Usage: " << argv[0] << " [--nofork] [--nolog] [--debug] [--config <config>]" << std::endl << std::string(static_cast<int>(8+strlen(argv[0])), ' ') << "[--runasroot] [--version]" << std::endl; Exit(EXIT_STATUS_ARGV); break; } } #ifdef INSPIRCD_ENABLE_TESTSUITE if (do_testsuite) do_nofork = do_debug = true; #endif if (do_version) { std::cout << std::endl << VERSION << " " << REVISION << std::endl; Exit(EXIT_STATUS_NOERROR); } #ifdef _WIN32 // Set up winsock WSADATA wsadata; WSAStartup(MAKEWORD(2,2), &wsadata); #endif /* Set the finished argument values */ Config->cmdline.nofork = (do_nofork != 0); Config->cmdline.forcedebug = (do_debug != 0); Config->cmdline.writelog = !do_nolog; if (do_debug) { FileWriter* fw = new FileWriter(stdout); FileLogStream* fls = new FileLogStream(LOG_RAWIO, fw); Logs->AddLogTypes("*", fls, true); } if (!FileSystem::FileExists(ConfigFileName)) { #ifdef _WIN32 /* Windows can (and defaults to) hide file extensions, so let's play a bit nice for windows users. */ std::string txtconf = this->ConfigFileName; txtconf.append(".txt"); if (FileSystem::FileExists(txtconf)) { ConfigFileName = txtconf; } else #endif { std::cout << "ERROR: Cannot open config file: " << ConfigFileName << std::endl << "Exiting..." << std::endl; this->Logs->Log("STARTUP", LOG_DEFAULT, "Unable to open config file %s", ConfigFileName.c_str()); Exit(EXIT_STATUS_CONFIG); } } std::cout << con_green << "Inspire Internet Relay Chat Server" << con_reset << ", compiled on " __DATE__ " at " __TIME__ << std::endl; std::cout << con_green << "(C) InspIRCd Development Team." << con_reset << std::endl << std::endl; std::cout << "Developers:" << std::endl; std::cout << con_green << "\tBrain, FrostyCoolSlug, w00t, Om, Special, peavey" << std::endl; std::cout << "\taquanight, psychon, dz, danieldg, jackmcbarn" << std::endl; std::cout << "\tAttila" << con_reset << std::endl << std::endl; std::cout << "Others:\t\t\t" << con_green << "See /INFO Output" << con_reset << std::endl; this->Modes = new ModeParser; #ifndef _WIN32 if (!do_root) this->CheckRoot(); else { std::cout << "* WARNING * WARNING * WARNING * WARNING * WARNING *" << std::endl << "YOU ARE RUNNING INSPIRCD AS ROOT. THIS IS UNSUPPORTED" << std::endl << "AND IF YOU ARE HACKED, CRACKED, SPINDLED OR MUTILATED" << std::endl << "OR ANYTHING ELSE UNEXPECTED HAPPENS TO YOU OR YOUR" << std::endl << "SERVER, THEN IT IS YOUR OWN FAULT. IF YOU DID NOT MEAN" << std::endl << "TO START INSPIRCD AS ROOT, HIT CTRL+C NOW AND RESTART" << std::endl << "THE PROGRAM AS A NORMAL USER. YOU HAVE BEEN WARNED!" << std::endl << std::endl << "InspIRCd starting in 20 seconds, ctrl+c to abort..." << std::endl; sleep(20); } #endif this->SetSignals(); if (!Config->cmdline.nofork) { if (!this->DaemonSeed()) { std::cout << "ERROR: could not go into daemon mode. Shutting down." << std::endl; Logs->Log("STARTUP", LOG_DEFAULT, "ERROR: could not go into daemon mode. Shutting down."); Exit(EXIT_STATUS_FORK); } } SocketEngine::RecoverFromFork(); /* During startup we read the configuration now, not in * a seperate thread */ this->Config->Read(); this->Config->Apply(NULL, ""); Logs->OpenFileLogs(); ModeParser::InitBuiltinModes(); // If we don't have a SID, generate one based on the server name and the server description if (Config->sid.empty()) Config->sid = UIDGenerator::GenerateSID(Config->ServerName, Config->ServerDesc); // Initialize the UID generator with our sid this->UIDGen.init(Config->sid); // Create the server user for this server this->FakeClient = new FakeUser(Config->sid, Config->ServerName, Config->ServerDesc); // This is needed as all new XLines are marked pending until ApplyLines() is called this->XLines->ApplyLines(); int bounditems = BindPorts(pl); std::cout << std::endl; this->Modules->LoadAll(); // Build ISupport as ModuleManager::LoadAll() does not do it this->ISupport.Build(); Config->ApplyDisabledCommands(Config->DisabledCommands); if (!pl.empty()) { std::cout << std::endl << "WARNING: Not all your client ports could be bound -- " << std::endl << "starting anyway with " << bounditems << " of " << bounditems + (int)pl.size() << " client ports bound." << std::endl << std::endl; std::cout << "The following port(s) failed to bind:" << std::endl << std::endl; int j = 1; for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++) { std::cout << j << ".\tAddress: " << (i->first.empty() ? "<all>" : i->first) << " \tReason: " << i->second << std::endl; } std::cout << std::endl << "Hint: Try using a public IP instead of blank or *" << std::endl; } std::cout << "InspIRCd is now running as '" << Config->ServerName << "'[" << Config->GetSID() << "] with " << SocketEngine::GetMaxFds() << " max open sockets" << std::endl; #ifndef _WIN32 if (!Config->cmdline.nofork) { if (kill(getppid(), SIGTERM) == -1) { std::cout << "Error killing parent process: " << strerror(errno) << std::endl; Logs->Log("STARTUP", LOG_DEFAULT, "Error killing parent process: %s",strerror(errno)); } } /* Explicitly shut down stdio's stdin/stdout/stderr. * * The previous logic here was to only do this if stdio was connected to a controlling * terminal. However, we must do this always to avoid information leaks and other * problems related to stdio. * * The only exception is if we are in debug mode. * * -- nenolod */ if ((!do_nofork) && (!Config->cmdline.forcedebug)) { int fd = open("/dev/null", O_RDWR); fclose(stdin); fclose(stderr); fclose(stdout); if (dup2(fd, STDIN_FILENO) < 0) Logs->Log("STARTUP", LOG_DEFAULT, "Failed to dup /dev/null to stdin."); if (dup2(fd, STDOUT_FILENO) < 0) Logs->Log("STARTUP", LOG_DEFAULT, "Failed to dup /dev/null to stdout."); if (dup2(fd, STDERR_FILENO) < 0) Logs->Log("STARTUP", LOG_DEFAULT, "Failed to dup /dev/null to stderr."); close(fd); } else { Logs->Log("STARTUP", LOG_DEFAULT, "Keeping pseudo-tty open as we are running in the foreground."); } #else /* Set win32 service as running, if we are running as a service */ SetServiceRunning(); // Handle forking if(!do_nofork) { FreeConsole(); } QueryPerformanceFrequency(&stats->QPFrequency); #endif Logs->Log("STARTUP", LOG_DEFAULT, "Startup complete as '%s'[%s], %d max open sockets", Config->ServerName.c_str(),Config->GetSID().c_str(), SocketEngine::GetMaxFds()); #ifndef _WIN32 std::string SetUser = Config->ConfValue("security")->getString("runasuser"); std::string SetGroup = Config->ConfValue("security")->getString("runasgroup"); if (!SetGroup.empty()) { int ret; // setgroups ret = setgroups(0, NULL); if (ret == -1) { this->Logs->Log("STARTUP", LOG_DEFAULT, "setgroups() failed (wtf?): %s", strerror(errno)); this->QuickExit(0); } // setgid struct group *g; errno = 0; g = getgrnam(SetGroup.c_str()); if (!g) { this->Logs->Log("STARTUP", LOG_DEFAULT, "getgrnam(%s) failed (wrong group?): %s", SetGroup.c_str(), strerror(errno)); this->QuickExit(0); } ret = setgid(g->gr_gid); if (ret == -1) { this->Logs->Log("STARTUP", LOG_DEFAULT, "setgid() failed (wrong group?): %s", strerror(errno)); this->QuickExit(0); } } if (!SetUser.empty()) { // setuid struct passwd *u; errno = 0; u = getpwnam(SetUser.c_str()); if (!u) { this->Logs->Log("STARTUP", LOG_DEFAULT, "getpwnam(%s) failed (wrong user?): %s", SetUser.c_str(), strerror(errno)); this->QuickExit(0); } int ret = setuid(u->pw_uid); if (ret == -1) { this->Logs->Log("STARTUP", LOG_DEFAULT, "setuid() failed (wrong user?): %s", strerror(errno)); this->QuickExit(0); } } this->WritePID(Config->PID); #endif }