+ // Collects performance statistics for the STATS command.
+ void CollectStats()
+ {
+#ifndef _WIN32
+ static rusage ru;
+ if (getrusage(RUSAGE_SELF, &ru) == -1)
+ return; // Should never happen.
+
+ ServerInstance->stats.LastSampled.tv_sec = ServerInstance->Time();
+ ServerInstance->stats.LastSampled.tv_nsec = ServerInstance->Time_ns();
+ ServerInstance->stats.LastCPU = ru.ru_utime;
+#else
+ if (!QueryPerformanceCounter(&ServerInstance->stats.LastSampled))
+ return; // Should never happen.
+
+ FILETIME CreationTime;
+ FILETIME ExitTime;
+ FILETIME KernelTime;
+ FILETIME UserTime;
+ GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime);
+
+ ServerInstance->stats.LastCPU.dwHighDateTime = KernelTime.dwHighDateTime + UserTime.dwHighDateTime;
+ ServerInstance->stats.LastCPU.dwLowDateTime = KernelTime.dwLowDateTime + UserTime.dwLowDateTime;
+#endif
+ }
+
+ // Checks whether the server clock has skipped too much and warn about it if it has.
+ void CheckTimeSkip(time_t oldtime, time_t newtime)
+ {
+ if (!ServerInstance->Config->TimeSkipWarn)
+ return;
+
+ time_t timediff = newtime - oldtime;
+
+ if (timediff > ServerInstance->Config->TimeSkipWarn)
+ ServerInstance->SNO->WriteToSnoMask('a', "\002Performance warning!\002 Server clock jumped forwards by %lu seconds!", timediff);
+
+ else if (timediff < -ServerInstance->Config->TimeSkipWarn)
+ ServerInstance->SNO->WriteToSnoMask('a', "\002Performance warning!\002 Server clock jumped backwards by %lu seconds!", labs(timediff));
+ }
+
+ // Drops to the unprivileged user/group specified in <security:runas{user,group}>.
+ void DropRoot()
+ {
+#ifndef _WIN32
+ ConfigTag* security = ServerInstance->Config->ConfValue("security");
+
+ const std::string SetGroup = security->getString("runasgroup");
+ if (!SetGroup.empty())
+ {
+ errno = 0;
+ if (setgroups(0, NULL) == -1)
+ {
+ ServerInstance->Logs->Log("STARTUP", LOG_DEFAULT, "setgroups() failed (wtf?): %s", strerror(errno));
+ exit(EXIT_STATUS_CONFIG);
+ }
+
+ struct group* g = getgrnam(SetGroup.c_str());
+ if (!g)
+ {
+ ServerInstance->Logs->Log("STARTUP", LOG_DEFAULT, "getgrnam(%s) failed (wrong group?): %s", SetGroup.c_str(), strerror(errno));
+ exit(EXIT_STATUS_CONFIG);
+ }
+
+ if (setgid(g->gr_gid) == -1)
+ {
+ ServerInstance->Logs->Log("STARTUP", LOG_DEFAULT, "setgid(%d) failed (wrong group?): %s", g->gr_gid, strerror(errno));
+ exit(EXIT_STATUS_CONFIG);
+ }
+ }
+
+ const std::string SetUser = security->getString("runasuser");
+ if (!SetUser.empty())
+ {
+ errno = 0;
+ struct passwd* u = getpwnam(SetUser.c_str());
+ if (!u)
+ {
+ ServerInstance->Logs->Log("STARTUP", LOG_DEFAULT, "getpwnam(%s) failed (wrong user?): %s", SetUser.c_str(), strerror(errno));
+ exit(EXIT_STATUS_CONFIG);
+ }
+
+ if (setuid(u->pw_uid) == -1)
+ {
+ ServerInstance->Logs->Log("STARTUP", LOG_DEFAULT, "setuid(%d) failed (wrong user?): %s", u->pw_uid, strerror(errno));
+ exit(EXIT_STATUS_CONFIG);
+ }
+ }
+#endif
+ }
+
+ // Expands a path relative to the current working directory.
+ std::string ExpandPath(const char* path)
+ {
+#ifdef _WIN32
+ TCHAR configPath[MAX_PATH + 1];
+ if (GetFullPathName(path, MAX_PATH, configPath, NULL) > 0)
+ return configPath;
+#else
+ char configPath[PATH_MAX + 1];
+ if (realpath(path, configPath))
+ return configPath;
+#endif
+ return path;
+ }
+
+ // Locates a config file on the file system.
+ bool FindConfigFile(std::string& path)
+ {
+ if (FileSystem::FileExists(path))
+ return true;
+
+#ifdef _WIN32
+ // Windows hides file extensions by default so try appending .txt to the path
+ // to help users who have that feature enabled and can't create .conf files.
+ const std::string txtpath = path + ".txt";
+ if (FileSystem::FileExists(txtpath))
+ {
+ path.assign(txtpath);
+ return true;
+ }
+#endif
+ return false;
+ }
+
+ // Attempts to fork into the background.
+ void ForkIntoBackground()
+ {
+#ifndef _WIN32
+ // We use VoidSignalHandler whilst forking to avoid breaking daemon scripts
+ // if the parent process exits with SIGTERM (15) instead of EXIT_STATUS_NOERROR (0).
+ signal(SIGTERM, VoidSignalHandler);
+
+ errno = 0;
+ int childpid = fork();
+ if (childpid < 0)
+ {
+ ServerInstance->Logs->Log("STARTUP", LOG_DEFAULT, "fork() failed: %s", strerror(errno));
+ std::cout << con_red << "Error:" << con_reset << " unable to fork into background: " << strerror(errno);
+ ServerInstance->Exit(EXIT_STATUS_FORK);
+ }
+ else if (childpid > 0)
+ {
+ // Wait until the child process kills the parent so that the shell prompt
+ // doesnt display over the output. Sending a kill with a signal of 0 just
+ // checks that the child pid is still running. If it is not then an error
+ // happened and the parent should exit.
+ while (kill(childpid, 0) != -1)
+ sleep(1);
+ exit(EXIT_STATUS_NOERROR);
+ }
+ else
+ {
+ setsid();
+ signal(SIGTERM, InspIRCd::SetSignal);
+ SocketEngine::RecoverFromFork();
+ }
+#endif
+ }
+
+ // Increase the size of a core dump file to improve debugging problems.
+ void IncreaseCoreDumpSize()
+ {
+#ifndef _WIN32
+ errno = 0;
+ rlimit rl;
+ if (getrlimit(RLIMIT_CORE, &rl) == -1)
+ {
+ ServerInstance->Logs->Log("STARTUP", LOG_DEFAULT, "Unable to increase core dump size: getrlimit(RLIMIT_CORE) failed: %s", strerror(errno));
+ return;
+ }
+
+ rl.rlim_cur = rl.rlim_max;
+ if (setrlimit(RLIMIT_CORE, &rl) == -1)
+ ServerInstance->Logs->Log("STARTUP", LOG_DEFAULT, "Unable to increase core dump size: setrlimit(RLIMIT_CORE) failed: %s", strerror(errno));
+#endif
+ }
+
+ // Parses the command line options.
+ void ParseOptions()
+ {
+ int do_debug = 0, do_nofork = 0, do_nolog = 0;
+ int do_nopid = 0, do_runasroot = 0, do_version = 0;
+ struct option longopts[] =
+ {
+ { "config", required_argument, NULL, 'c' },
+ { "debug", no_argument, &do_debug, 1 },
+ { "nofork", no_argument, &do_nofork, 1 },
+ { "nolog", no_argument, &do_nolog, 1 },
+ { "nopid", no_argument, &do_nopid, 1 },
+ { "runasroot", no_argument, &do_runasroot, 1 },
+ { "version", no_argument, &do_version, 1 },
+ { 0, 0, 0, 0 }
+ };
+
+ char** argv = ServerInstance->Config->cmdline.argv;
+ int ret;
+ while ((ret = getopt_long(ServerInstance->Config->cmdline.argc, argv, ":c:", longopts, NULL)) != -1)
+ {
+ switch (ret)
+ {
+ case 0:
+ // A long option was specified.
+ break;
+
+ case 'c':
+ // The -c option was specified.
+ ServerInstance->ConfigFileName = ExpandPath(optarg);
+ break;
+
+ default:
+ // An unknown option was specified.
+ std::cout << con_red << "Error:" << con_reset << " unknown option '" << argv[optind - 1] << "'." << std::endl
+ << con_bright << "Usage: " << con_reset << argv[0] << " [--config <file>] [--debug] [--nofork] [--nolog]" << std::endl
+ << std::string(strlen(argv[0]) + 8, ' ') << "[--nopid] [--runasroot] [--version]" << std::endl;
+ ServerInstance->Exit(EXIT_STATUS_ARGV);
+ break;
+ }
+ }
+
+ if (do_version)
+ {
+ std::cout << std::endl << INSPIRCD_VERSION << std::endl;
+ ServerInstance->Exit(EXIT_STATUS_NOERROR);
+ }
+
+ // Store the relevant parsed arguments
+ ServerInstance->Config->cmdline.forcedebug = !!do_debug;
+ ServerInstance->Config->cmdline.nofork = !!do_nofork;
+ ServerInstance->Config->cmdline.runasroot = !!do_runasroot;
+ ServerInstance->Config->cmdline.writelog = !do_nolog;
+ ServerInstance->Config->cmdline.writepid = !do_nopid;
+ }
+ // Seeds the random number generator if applicable.
+ void SeedRng(timespec ts)
+ {
+#if defined _WIN32
+ srand(ts.tv_nsec ^ ts.tv_sec);
+#elif !defined HAS_ARC4RANDOM_BUF
+ srandom(ts.tv_nsec ^ ts.tv_sec);
+#endif
+ }
+
+ // Sets handlers for various process signals.
+ void SetSignals()
+ {
+#ifndef _WIN32
+ signal(SIGALRM, SIG_IGN);
+ signal(SIGCHLD, SIG_IGN);
+ signal(SIGHUP, InspIRCd::SetSignal);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGUSR1, SIG_IGN);
+ signal(SIGUSR2, SIG_IGN);
+ signal(SIGXFSZ, SIG_IGN);
+#endif
+ signal(SIGTERM, InspIRCd::SetSignal);
+ }
+
+ void TryBindPorts()
+ {
+ FailedPortList pl;
+ ServerInstance->BindPorts(pl);
+
+ if (!pl.empty())
+ {
+ std::cout << con_red << "Warning!" << con_reset << " Some of your listener" << (pl.size() == 1 ? "s" : "") << " failed to bind:" << std::endl
+ << std::endl;
+
+ for (FailedPortList::const_iterator iter = pl.begin(); iter != pl.end(); ++iter)
+ {
+ const FailedPort& fp = *iter;
+ std::cout << " " << con_bright << fp.sa.str() << con_reset << ": " << strerror(fp.error) << '.' << std::endl
+ << " " << "Created from <bind> tag at " << fp.tag->getTagLocation() << std::endl
+ << std::endl;
+ }
+
+ std::cout << con_bright << "Hints:" << con_reset << std::endl
+ << "- For TCP/IP listeners try using a public IP address in <bind:address> instead" << std::endl
+ << " of * of leaving it blank." << std::endl
+ << "- For UNIX socket listeners try enabling <bind:rewrite> to replace old sockets." << std::endl;
+ }
+ }
+
+ // Required for returning the proper value of EXIT_SUCCESS for the parent process.
+ void VoidSignalHandler(int)
+ {
+ exit(EXIT_STATUS_NOERROR);
+ }