#include <signal.h>
#ifndef _WIN32
- #include <dirent.h>
#include <unistd.h>
#include <sys/resource.h>
#include <dlfcn.h>
#include <getopt.h>
-
- /* Some systems don't define RUSAGE_SELF. This should fix them. */
- #ifndef RUSAGE_SELF
- #define RUSAGE_SELF 0
- #endif
-
#include <pwd.h> // setuid
#include <grp.h> // setgid
#else
#include <iostream>
#include "xline.h"
#include "exitcodes.h"
-#include "testsuite.h"
InspIRCd* ServerInstance = NULL;
"Received SIGTERM" // 10
};
-#ifdef INSPIRCD_ENABLE_TESTSUITE
-/** True if we have been told to run the testsuite from the commandline,
- * rather than entering the mainloop.
- */
-static int do_testsuite = 0;
-#endif
-
template<typename T> static void DeleteZero(T*&n)
{
T* t = n;
}
ports.clear();
- /* Close all client sockets, or the new process inherits them */
+ // Tell modules that we're shutting down.
+ const std::string quitmsg = "Server shutting down";
+ FOREACH_MOD(OnShutdown, (quitmsg));
+
+ // Disconnect all local users
const UserManager::LocalList& list = Users.GetLocalUsers();
- for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
- Users->QuitUser(*i, "Server shutdown");
+ while (!list.empty())
+ ServerInstance->Users.QuitUser(list.front(), quitmsg);
GlobalCulls.Apply();
Modules->UnloadAll();
{
#ifndef _WIN32
signal(SIGALRM, SIG_IGN);
+ signal(SIGCHLD, SIG_IGN);
signal(SIGHUP, InspIRCd::SetSignal);
signal(SIGPIPE, SIG_IGN);
- signal(SIGCHLD, SIG_IGN);
- /* We want E2BIG not a signal! */
+ signal(SIGUSR1, SIG_IGN);
+ signal(SIGUSR2, SIG_IGN);
signal(SIGXFSZ, SIG_IGN);
#endif
signal(SIGTERM, InspIRCd::SetSignal);
}
-void InspIRCd::QuickExit(int status)
-{
- exit(status);
-}
-
// Required for returning the proper value of EXIT_SUCCESS for the parent process
static void VoidSignalHandler(int signalreceived)
{
- exit(0);
+ exit(EXIT_STATUS_NOERROR);
}
bool InspIRCd::DaemonSeed()
std::cout << "InspIRCd Process ID: " << con_green << GetCurrentProcessId() << con_reset << std::endl;
return true;
#else
- // Do not use QuickExit here: It will exit with status SIGTERM which would break e.g. daemon scripts
+ // Do not use exit() here: It will exit with status SIGTERM which would break e.g. daemon scripts
signal(SIGTERM, VoidSignalHandler);
int childpid = fork();
*/
while (kill(childpid, 0) != -1)
sleep(1);
- exit(0);
+ exit(EXIT_STATUS_NOERROR);
}
setsid ();
std::cout << "InspIRCd Process ID: " << con_green << getpid() << con_reset << std::endl;
#endif
}
-void InspIRCd::WritePID(const std::string &filename)
+void InspIRCd::WritePID(const std::string& filename, bool exitonfail)
{
#ifndef _WIN32
- std::string fname(filename);
- if (fname.empty())
- fname = ServerInstance->Config->Paths.PrependData("inspircd.pid");
+ if (!ServerInstance->Config->cmdline.writepid)
+ {
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "--nopid specified on command line; PID file not written.");
+ return;
+ }
+
+ std::string fname = ServerInstance->Config->Paths.PrependData(filename.empty() ? "inspircd.pid" : filename);
std::ofstream outfile(fname.c_str());
if (outfile.is_open())
{
}
else
{
- std::cout << "Failed to write PID-file '" << fname << "', exiting." << std::endl;
- this->Logs->Log("STARTUP", LOG_DEFAULT, "Failed to write PID-file '%s', exiting.",fname.c_str());
- Exit(EXIT_STATUS_PID);
+ if (exitonfail)
+ std::cout << "Failed to write PID-file '" << fname << "', exiting." << std::endl;
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "Failed to write PID-file '%s'%s", fname.c_str(), (exitonfail ? ", exiting." : ""));
+ if (exitonfail)
+ Exit(EXIT_STATUS_PID);
}
#endif
}
* THIS MUST MATCH THE ORDER OF DECLARATION OF THE FUNCTORS, e.g. the methods
* themselves within the class.
*/
- OperQuit("operquit", ExtensionItem::EXT_USER, NULL),
- GenRandom(&HandleGenRandom),
- IsChannel(&HandleIsChannel),
- IsNick(&HandleIsNick),
- IsIdent(&HandleIsIdent),
- OnCheckExemption(&HandleOnCheckExemption)
+ GenRandom(&DefaultGenRandom),
+ IsChannel(&DefaultIsChannel),
+ IsNick(&DefaultIsNick),
+ IsIdent(&DefaultIsIdent)
{
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;
+ do_nolog = 0, do_nopid = 0, do_root = 0;
// Initialize so that if we exit before proper initialization they're not deleted
this->Config = 0;
srandom(TIME.tv_nsec ^ TIME.tv_sec);
#endif
+ {
+ ServiceProvider* provs[] =
+ {
+ &rfcevents.numeric, &rfcevents.join, &rfcevents.part, &rfcevents.kick, &rfcevents.quit, &rfcevents.nick,
+ &rfcevents.mode, &rfcevents.topic, &rfcevents.privmsg, &rfcevents.invite, &rfcevents.ping, &rfcevents.pong,
+ &rfcevents.error
+ };
+ Modules.AddServices(provs, sizeof(provs)/sizeof(provs[0]));
+ }
+
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 },
+ { "nopid", no_argument, &do_nopid, 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 }
};
{
case 'c':
/* Config filename was set */
- ConfigFileName = ServerInstance->Config->Paths.PrependConfig(optarg);
+ ConfigFileName = optarg;
+#ifdef _WIN32
+ TCHAR configPath[MAX_PATH + 1];
+ if (GetFullPathName(optarg, MAX_PATH, configPath, NULL) > 0)
+ ConfigFileName = configPath;
+#else
+ char configPath[PATH_MAX + 1];
+ if (realpath(optarg, configPath))
+ ConfigFileName = configPath;
+#endif
break;
case 0:
/* getopt_long_only() set an int variable, just keep going */
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;
+ std::cout << "Usage: " << argv[0] << " [--nofork] [--nolog] [--nopid] [--debug] [--config <config>]" << std::endl <<
+ std::string(static_cast<size_t>(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 << INSPIRCD_VERSION << " " << INSPIRCD_REVISION << std::endl;
+ std::cout << std::endl << INSPIRCD_VERSION << std::endl;
Exit(EXIT_STATUS_NOERROR);
}
Config->cmdline.nofork = (do_nofork != 0);
Config->cmdline.forcedebug = (do_debug != 0);
Config->cmdline.writelog = !do_nolog;
+ Config->cmdline.writepid = !do_nopid;
if (do_debug)
{
- FileWriter* fw = new FileWriter(stdout);
+ FileWriter* fw = new FileWriter(stdout, 1);
FileLogStream* fls = new FileLogStream(LOG_RAWIO, fw);
Logs->AddLogTypes("*", fls, true);
}
}
}
- 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;
+ std::cout << con_green << "InspIRCd - Internet Relay Chat Daemon" << con_reset << std::endl;
+ std::cout << "For contributors & authors: " << con_green << "See /INFO Output" << con_reset << std::endl;
#ifndef _WIN32
if (!do_root)
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())
// Build ISupport as ModuleManager::LoadAll() does not do it
this->ISupport.Build();
- Config->ApplyDisabledCommands(Config->DisabledCommands);
if (!pl.empty())
{
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 << j << ".\tAddress: " << i->first.str() << " \tReason: " << strerror(i->second) << std::endl;
}
std::cout << std::endl << "Hint: Try using a public IP instead of blank or *" << std::endl;
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());
+ Logs->Log("STARTUP", LOG_DEFAULT, "Startup complete as '%s'[%s], %lu 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");
+ ConfigTag* security = Config->ConfValue("security");
+
+ const std::string SetGroup = security->getString("runasgroup");
if (!SetGroup.empty())
{
- int ret;
-
- // setgroups
- ret = setgroups(0, NULL);
-
- if (ret == -1)
+ errno = 0;
+ if (setgroups(0, NULL) == -1)
{
this->Logs->Log("STARTUP", LOG_DEFAULT, "setgroups() failed (wtf?): %s", strerror(errno));
- this->QuickExit(0);
+ exit(EXIT_STATUS_CONFIG);
}
- // setgid
- struct group *g;
-
- errno = 0;
- g = getgrnam(SetGroup.c_str());
-
+ struct group* 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);
+ exit(EXIT_STATUS_CONFIG);
}
- ret = setgid(g->gr_gid);
-
- if (ret == -1)
+ if (setgid(g->gr_gid) == -1)
{
- this->Logs->Log("STARTUP", LOG_DEFAULT, "setgid() failed (wrong group?): %s", strerror(errno));
- this->QuickExit(0);
+ this->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())
{
- // setuid
- struct passwd *u;
-
errno = 0;
- u = getpwnam(SetUser.c_str());
-
+ struct passwd* 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);
+ exit(EXIT_STATUS_CONFIG);
}
- int ret = setuid(u->pw_uid);
-
- if (ret == -1)
+ if (setuid(u->pw_uid) == -1)
{
- this->Logs->Log("STARTUP", LOG_DEFAULT, "setuid() failed (wrong user?): %s", strerror(errno));
- this->QuickExit(0);
+ this->Logs->Log("STARTUP", LOG_DEFAULT, "setuid(%d) failed (wrong user?): %s", u->pw_uid, strerror(errno));
+ exit(EXIT_STATUS_CONFIG);
}
}
void InspIRCd::Run()
{
-#ifdef INSPIRCD_ENABLE_TESTSUITE
- /* See if we're supposed to be running the test suite rather than entering the mainloop */
- if (do_testsuite)
- {
- TestSuite* ts = new TestSuite;
- delete ts;
- return;
- }
-#endif
-
UpdateTime();
time_t OLDTIME = TIME.tv_sec;
}
#endif
- /* Allow a buffer of two seconds drift on this so that ntpdate etc dont harass admins */
- if (TIME.tv_sec < OLDTIME - 2)
- {
- SNO->WriteToSnoMask('d', "\002EH?!\002 -- Time is flowing BACKWARDS in this dimension! Clock drifted backwards %lu secs.", (unsigned long)OLDTIME-TIME.tv_sec);
- }
- else if (TIME.tv_sec > OLDTIME + 2)
+ if (Config->TimeSkipWarn)
{
- SNO->WriteToSnoMask('d', "\002EH?!\002 -- Time is jumping FORWARDS! Clock skipped %lu secs.", (unsigned long)TIME.tv_sec - OLDTIME);
+ time_t timediff = TIME.tv_sec - OLDTIME;
+
+ if (timediff > Config->TimeSkipWarn)
+ SNO->WriteToSnoMask('a', "\002Performance warning!\002 Server clock jumped forwards by %lu seconds!", timediff);
+
+ else if (timediff < -Config->TimeSkipWarn)
+ SNO->WriteToSnoMask('a', "\002Performance warning!\002 Server clock jumped backwards by %lu seconds!", labs(timediff));
}
OLDTIME = TIME.tv_sec;
if ((TIME.tv_sec % 3600) == 0)
{
- Users->GarbageCollect();
FOREACH_MOD(OnGarbageCollect, ());
+
+ // HACK: ELines are not expired properly at the moment but it can't be fixed as
+ // the 2.0 XLine system is a spaghetti nightmare. Instead we skip over expired
+ // ELines in XLineManager::CheckELines() and expire them here instead.
+ XLines->GetAll("E");
}
Timers.TickTimers(TIME.tv_sec);