]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/inspircd.cpp
There is absolutely no need to cache connect timeout.
[user/henk/code/inspircd.git] / src / inspircd.cpp
index aaad2746ada3a7206134ebaad5a5e6371e5f1556..96b7792716942c6984e657ea4d2bb4f8a3c846b7 100644 (file)
@@ -11,8 +11,9 @@
  * ---------------------------------------------------
  */
 
-/* $Install: src/inspircd $(BINPATH) */
+/* w00t was here. ;p */
 
+/* $Install: src/inspircd $(BINPATH) */
 #include "inspircd.h"
 #include <signal.h>
 
        #ifndef RUSAGE_SELF
                #define RUSAGE_SELF 0
        #endif
+
+       /* CRT memory debugging */
+       #ifdef DEBUG
+       #define _CRTDBG_MAP_ALLOC
+       #include <stdlib.h>
+       #include <crtdbg.h>
+       #endif
 #endif
 
 #include <fstream>
@@ -69,6 +77,10 @@ const char* ExitCodes[] =
                "Couldn't load module on startup", /* 13 */
                "Could not create windows forked process", /* 14 */
                "Received SIGTERM", /* 15 */
+               "Bad command handler loaded", /* 16 */
+               "RegisterServiceCtrlHandler failed", /* 17 */
+               "UpdateSCMStatus failed", /* 18 */
+               "CreateEvent failed" /* 19 */
 };
 
 void InspIRCd::Cleanup()
@@ -87,7 +99,7 @@ void InspIRCd::Cleanup()
        /* Close all client sockets, or the new process inherits them */
        for (std::vector<User*>::const_iterator i = this->Users->local_users.begin(); i != this->Users->local_users.end(); i++)
        {
-               (*i)->SetWriteError("Server shutdown");
+               this->Users->QuitUser((*i), "Server shutdown");
                (*i)->CloseSocket();
        }
 
@@ -106,15 +118,109 @@ void InspIRCd::Cleanup()
                        this->Modules->Unload(k->c_str());
                }
        }
-
-       /* Close logging */
-       this->Logs->CloseLogs();
+       /* Remove core commands */
+       Parser->RemoveCommands("<core>");
 
        /* Cleanup Server Names */
        for(servernamelist::iterator itr = servernames.begin(); itr != servernames.end(); ++itr)
                delete (*itr);
 
+       /* Delete objects dynamically allocated in constructor
+        * (destructor would be more appropriate, but we're likely exiting)
+        */
+
+       // Must be deleted before modes as it decrements modelines
+       if (this->Users)
+       {
+               delete this->Users;
+               this->Users = 0;
+       }
+       
+       if (this->Modes)
+       {
+               delete this->Modes;
+               this->Modes = 0;
+       }
+
+       if (this->XLines)
+       {
+               delete this->XLines;
+               this->XLines = 0;
+       }
+
+       if (this->Parser)
+       {
+               delete this->Parser;
+               this->Parser = 0;
+
+       if (this->stats)
+       {
+               delete this->stats;
+               this->stats = 0;
+       }
+
+       if (this->Modules)
+       {
+               delete this->Modules;
+               this->Modules = 0;
+       }
 
+       if (this->BanCache)
+               delete this->BanCache;
+               this->BanCache = 0;
+       }
+
+       if (this->SNO)
+       {
+               delete this->SNO;
+               this->SNO = 0;
+       }
+
+       if (this->Config)
+       {
+               delete this->Config;
+               this->Config = 0;
+       }
+
+       if (this->Res)
+       {
+               delete this->Res;
+               this->Res = 0;
+       }
+
+       if (this->chanlist)
+       {
+               delete chanlist;
+               chanlist = 0;
+       }
+
+       if (this->PI)
+       {
+               delete this->PI;
+               this->PI = 0;
+       }
+       
+       if (this->Threads)
+       {
+               delete this->Threads;
+               this->Threads = 0;
+       }
+
+       /* Needs to be deleted after Res, DNS has a timer */
+       if (this->Timers)
+       {
+               delete this->Timers;
+               this->Timers = 0;
+       }
+
+       /* Close logging */
+       this->Logs->CloseLogs();
+
+       if (this->Logs)
+       {
+               delete this->Logs;
+               this->Logs = 0;
+       }
 }
 
 void InspIRCd::Restart(const std::string &reason)
@@ -124,8 +230,6 @@ void InspIRCd::Restart(const std::string &reason)
         */
        this->SendError(reason);
 
-       this->Cleanup();
-
        /* Figure out our filename (if theyve renamed it, we're boned) */
        std::string me;
 
@@ -137,7 +241,11 @@ void InspIRCd::Restart(const std::string &reason)
        me = Config->MyDir + "/inspircd";
 #endif
 
-       if (execv(me.c_str(), Config->argv) == -1)
+       char** argv = Config->argv;
+
+       this->Cleanup();
+
+       if (execv(me.c_str(), argv) == -1)
        {
                /* Will raise a SIGABRT if not trapped */
                throw CoreException(std::string("Failed to execv()! error: ") + strerror(errno));
@@ -249,7 +357,8 @@ bool InspIRCd::DaemonSeed()
 void InspIRCd::WritePID(const std::string &filename)
 {
        std::string fname = (filename.empty() ? "inspircd.pid" : filename);
-       if (*(fname.begin()) != '/')
+       std::replace(fname.begin(), fname.end(), '\\', '/');
+       if ((fname[0] != '/') && (!Config->StartsWithWindowsDriveLetter(filename)))
        {
                std::string::size_type pos;
                std::string confpath = this->ConfigFileName;
@@ -276,28 +385,66 @@ void InspIRCd::WritePID(const std::string &filename)
 InspIRCd::InspIRCd(int argc, char** argv)
        : GlobalCulls(this),
 
-        /* Functor initialisation. Note that the ordering here is very important. */
+        /* Functor initialisation. Note that the ordering here is very important. 
+         *
+         * THIS MUST MATCH ORDER OF DECLARATION OF THE HandleWhateverFunc classes
+         * within class InspIRCd.
+         */
         HandleProcessUser(this),
         HandleIsNick(this),
         HandleIsIdent(this),
         HandleFindDescriptor(this),
         HandleFloodQuitUser(this),
-
-        /* Functor pointer initialisation. Must match the order of the list above */
+        HandleIsChannel(this),
+        HandleIsSID(this),
+        HandleRehash(this),
+
+        /* Functor pointer initialisation. Must match the order of the list above
+         *
+         * THIS MUST MATCH THE ORDER OF DECLARATION OF THE FUNCTORS, e.g. the methods
+         * themselves within the class.
+         */
         ProcessUser(&HandleProcessUser),
+        IsChannel(&HandleIsChannel),
+        IsSID(&HandleIsSID),
+        Rehash(&HandleRehash),
         IsNick(&HandleIsNick),
         IsIdent(&HandleIsIdent),
         FindDescriptor(&HandleFindDescriptor),
         FloodQuitUser(&HandleFloodQuitUser)
 
 {
-
+#ifdef WIN32
+       // Strict, frequent checking of memory on debug builds
+       _CrtSetDbgFlag ( _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
+       
+       // Avoid erroneous frees on early exit
+       WindowsIPC = 0;
+#endif
        int found_ports = 0;
        FailedPortList pl;
        int do_version = 0, do_nofork = 0, do_debug = 0,
            do_nolog = 0, do_root = 0, do_testsuite = 0;    /* flag variables */
        char c = 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->chanlist = 0;
+       this->Config = 0;
+       this->SNO = 0;
+       this->BanCache = 0;
+       this->Modules = 0;
+       this->stats = 0;
+       this->Timers = 0;
+       this->Parser = 0;
+       this->XLines = 0;
+       this->Modes = 0;
+       this->Res = 0;
+
+
        memset(&server, 0, sizeof(server));
        memset(&client, 0, sizeof(client));
 
@@ -308,10 +455,13 @@ InspIRCd::InspIRCd(int argc, char** argv)
        SE = SEF->Create(this);
        delete SEF;
 
-
        ThreadEngineFactory* tef = new ThreadEngineFactory();
        this->Threads = tef->Create(this);
        delete tef;
+       this->Mutexes = new MutexFactory(this);
+
+       /* Default implementation does nothing */
+       this->PI = new ProtocolInterface(this);
 
        this->s_signal = 0;
        
@@ -324,8 +474,6 @@ InspIRCd::InspIRCd(int argc, char** argv)
        this->Users->uuidlist = new user_hash();
        this->chanlist = new chan_hash();
 
-       this->Res = NULL;
-
        this->Config = new ServerConfig(this);
        this->SNO = new SnomaskManager(this);
        this->BanCache = new BanCacheManager(this);
@@ -446,13 +594,6 @@ InspIRCd::InspIRCd(int argc, char** argv)
 
        this->Modes = new ModeParser(this);
 
-       /* set up fake client (uid is incorrect at this point,
-        * until after config is read. we set up the user again
-        * at that point 
-        */
-       this->FakeClient = new User(this);
-       this->FakeClient->SetFd(FD_MAGIC_NUMBER);
-
        if (!do_root)
                this->CheckRoot();
        else
@@ -499,10 +640,7 @@ InspIRCd::InspIRCd(int argc, char** argv)
         * For an explanation as to exactly how this works, and why it works this way, see GetUID().
         *   -- w00t
         */
-       if (*Config->sid)
-       {
-       }
-       else
+       if (!*Config->sid)
        {
                // Generate one
                size_t sid = 0;
@@ -516,20 +654,17 @@ InspIRCd::InspIRCd(int argc, char** argv)
                Config->sid[0] = (char)(sid / 100 + 48);
                Config->sid[1] = (char)(((sid / 10) % 10) + 48);
                Config->sid[2] = (char)(sid % 10 + 48);
+               Config->sid[3] = '\0';
        }
 
-       this->InitialiseUID();
-
        /* set up fake client again this time with the correct uid */
-       delete FakeClient;
-       this->FakeClient = new User(this);
+       this->FakeClient = new User(this, "#INVALID");
        this->FakeClient->SetFd(FD_MAGIC_NUMBER);
 
        // Get XLine to do it's thing.
        this->XLines->CheckELines();
        this->XLines->ApplyLines();
 
-
        CheckDie();
        int bounditems = BindPorts(true, found_ports, pl);
 
@@ -541,23 +676,20 @@ InspIRCd::InspIRCd(int argc, char** argv)
        this->BuildISupport();
        InitializeDisabledCommands(Config->DisabledCommands, this);
 
-       /*if ((Config->ports.size() == 0) && (found_ports > 0))
-       {
-               printf("\nERROR: I couldn't bind any ports! Are you sure you didn't start InspIRCd twice?\n");
-               Logs->Log("STARTUP", DEFAULT,"ERROR: I couldn't bind any ports! Something else is bound to those ports!");
-               Exit(EXIT_STATUS_BIND);
-       }*/
-
        if (Config->ports.size() != (unsigned int)found_ports)
        {
                printf("\nWARNING: Not all your client ports could be bound --\nstarting anyway with %d of %d client ports bound.\n\n", bounditems, found_ports);
                printf("The following port(s) failed to bind:\n");
+               printf("Hint: Try using a public IP instead of blank or *\n\n");
                int j = 1;
                for (FailedPortList::iterator i = pl.begin(); i != pl.end(); i++, j++)
                {
-                       printf("%d.\tIP: %s\tPort: %lu\n", j, i->first.empty() ? "<all>" : i->first.c_str(), (unsigned long)i->second);
+                       printf("%d.\tAddress: %s\tReason: %s\n", j, i->first.empty() ? "<all>" : i->first.c_str(), i->second.c_str());
                }
        }
+
+       printf("\nInspIRCd is now running as '%s'[%s] with %d max open sockets\n", Config->ServerName,Config->GetSID().c_str(), SE->GetMaxFds());
+       
 #ifndef WINDOWS
        if (!Config->nofork)
        {
@@ -574,7 +706,7 @@ InspIRCd::InspIRCd(int argc, char** argv)
                 * e.g. we are restarting, or being launched by cron. Dont kill parent, and dont
                 * close stdin/stdout
                 */
-               if (!do_nofork)
+               if ((!do_nofork) && (!do_testsuite))
                {
                        fclose(stdin);
                        fclose(stderr);
@@ -592,30 +724,15 @@ InspIRCd::InspIRCd(int argc, char** argv)
                WindowsForkKillOwner(this);
                FreeConsole();
        }
+       /* Set win32 service as running, if we are running as a service */
+       SetServiceRunning();
 #endif
 
-       printf("\nInspIRCd is now running as '%s'[%s]\n", Config->ServerName,Config->GetSID().c_str());
-       Logs->Log("STARTUP", DEFAULT, "Startup complete as '%s'[%s]", Config->ServerName,Config->GetSID().c_str());
+       Logs->Log("STARTUP", DEFAULT, "Startup complete as '%s'[%s], %d max open sockets", Config->ServerName,Config->GetSID().c_str(), SE->GetMaxFds());
 
        this->WritePID(Config->PID);
 }
 
-/* moved to a function, as UID generation can call this also */
-void InspIRCd::InitialiseUID()
-{
-       int i = 3;
-
-       current_uid[0] = Config->sid[0];
-       current_uid[1] = Config->sid[1];
-       current_uid[2] = Config->sid[2];
-
-       /* Initialise UID */
-       for(i = 3; i < UUID_LENGTH - 1; i++)
-               current_uid[i] = 'A';
-
-       current_uid[UUID_LENGTH] = '\0';
-}
-
 int InspIRCd::Run()
 {
        /* See if we're supposed to be running the test suite rather than entering the mainloop */
@@ -636,6 +753,35 @@ int InspIRCd::Run()
                static char window_title[100];
 #endif
 
+               /* Check if there is a config thread which has finished executing but has not yet been freed */
+               if (this->ConfigThread && this->ConfigThread->GetExitFlag())
+               {
+                       /* Rehash has completed */
+                       this->Logs->Log("CONFIG",DEBUG,"Detected ConfigThread exiting, tidying up...");
+
+                       /* IMPORTANT: This delete may hang if you fuck up your thread syncronization.
+                        * It will hang waiting for the ConfigThread to 'join' to avoid race conditons,
+                        * until the other thread is completed.
+                        */
+                       delete ConfigThread;
+                       ConfigThread = NULL;
+
+                       /* These are currently not known to be threadsafe, so they are executed outside
+                        * of the thread. It would be pretty simple to move them to the thread Run method
+                        * once they are known threadsafe with all the correct mutexes in place.
+                        *
+                        * XXX: The order of these is IMPORTANT, do not reorder them without testing
+                        * thoroughly!!!
+                        */
+                       this->XLines->CheckELines();
+                       this->XLines->ApplyLines();
+                       this->Res->Rehash();
+                       this->ResetMaxBans();
+                       InitializeDisabledCommands(Config->DisabledCommands, this);
+                       FOREACH_MOD_I(this, I_OnRehash, OnRehash(Config->RehashUser, Config->RehashParameter));
+                       this->BuildISupport();
+               }
+
                /* time() seems to be a pretty expensive syscall, so avoid calling it too much.
                 * Once per loop iteration is pleanty.
                 */
@@ -649,9 +795,14 @@ int InspIRCd::Run()
                 */
                if (TIME != OLDTIME)
                {
-                       if (TIME < OLDTIME)
+                       /* Allow a buffer of two seconds drift on this so that ntpdate etc dont harass admins */
+                       if (TIME < OLDTIME - 2)
                        {
-                               SNO->WriteToSnoMask('A', "\002EH?!\002 -- Time is flowing BACKWARDS in this dimension! Clock drifted backwards %d secs.",abs(OLDTIME-TIME));
+                               SNO->WriteToSnoMask('d', "\002EH?!\002 -- Time is flowing BACKWARDS in this dimension! Clock drifted backwards %lu secs.", (unsigned long)OLDTIME-TIME);
+                       }
+                       else if (TIME > OLDTIME + 2)
+                       {
+                               SNO->WriteToSnoMask('d', "\002EH?!\002 -- Time is jumping FORWARDS! Clock skipped %lu secs.", (unsigned long)TIME - OLDTIME);
                        }
 
                        if ((TIME % 3600) == 0)
@@ -723,15 +874,6 @@ void InspIRCd::BufferedSocketCull()
  * An ircd in five lines! bwahahaha. ahahahahaha. ahahah *cough*.
  */
 
-int main(int argc, char ** argv)
-{
-       SI = new InspIRCd(argc, argv);
-       mysig = &SI->s_signal;
-       SI->Run();
-       delete SI;
-       return 0;
-}
-
 /* this returns true when all modules are satisfied that the user should be allowed onto the irc server
  * (until this returns true, a user will block in the waiting state, waiting to connect up to the
  * registration timeout maximum seconds)
@@ -740,11 +882,9 @@ bool InspIRCd::AllModulesReportReady(User* user)
 {
        for (EventHandlerIter i = Modules->EventHandlers[I_OnCheckReady].begin(); i != Modules->EventHandlers[I_OnCheckReady].end(); ++i)
        {
-               int res = (*i)->OnCheckReady(user);
-               if (!res)
+               if (!(*i)->OnCheckReady(user))
                        return false;
        }
-
        return true;
 }
 
@@ -757,3 +897,18 @@ void InspIRCd::SetSignal(int signal)
 {
        *mysig = signal;
 }
+
+/* On posix systems, the flow of the program starts right here, with
+ * ENTRYPOINT being a #define that defines main(). On Windows, ENTRYPOINT
+ * defines smain() and the real main() is in the service code under
+ * win32service.cpp. This allows the service control manager to control
+ * the process where we are running as a windows service.
+ */
+ENTRYPOINT
+{
+       SI = new InspIRCd(argc, argv);
+       mysig = &SI->s_signal;
+       SI->Run();
+       delete SI;
+       return 0;
+}