]> git.netwichtig.de Git - user/henk/code/inspircd.git/commitdiff
In prep for remote includes, configuration reading is now two-pass.
authorbrain <brain@e03df62e-2008-0410-955e-edbf42e46eb7>
Sun, 11 Nov 2007 17:01:00 +0000 (17:01 +0000)
committerbrain <brain@e03df62e-2008-0410-955e-edbf42e46eb7>
Sun, 11 Nov 2007 17:01:00 +0000 (17:01 +0000)
Note that theres an important part missing from here, there can be a NON-BLOCKING delay between the start of pass 2 and the files being available for download.
At this point, ServerConfig::Read() should probably return an ENOTREADY or such at which point it gets monitored for ready state. The socket engine is ready at this point
so we can poll the socket engine for it. In the case of startup, the socket engine blocks in a private loop, its no good booting the ircd till we have a complete config!

git-svn-id: http://svn.inspircd.org/repository/trunk/inspircd@8565 e03df62e-2008-0410-955e-edbf42e46eb7

include/configreader.h
include/modules.h
src/configreader.cpp
src/inspircd.cpp
src/modules.cpp

index 617059cf44c5584a5b9bef9c157d393937838d72..394d7f0f677050b8492d9394c7b12ba3514a8c6b 100644 (file)
@@ -232,11 +232,11 @@ class CoreExport ServerConfig : public Extensible
         * configutation, appending errors to errorstream
         * and setting error if an error has occured.
         */
-       bool ParseLine(ConfigDataHash &target, std::string &line, long &linenumber, std::ostringstream &errorstream);
+       bool ParseLine(ConfigDataHash &target, std::string &line, long &linenumber, std::ostringstream &errorstream, int pass);
   
        /** Process an include directive
         */
-       bool DoInclude(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream);
+       bool DoInclude(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream, int pass);
 
        /** Check that there is only one of each configuration item
         */
@@ -643,12 +643,12 @@ class CoreExport ServerConfig : public Extensible
        /** Load 'filename' into 'target', with the new config parser everything is parsed into
         * tag/key/value at load-time rather than at read-value time.
         */
-       bool LoadConf(ConfigDataHash &target, const char* filename, std::ostringstream &errorstream);
+       bool LoadConf(ConfigDataHash &target, const char* filename, std::ostringstream &errorstream, int pass);
 
        /** Load 'filename' into 'target', with the new config parser everything is parsed into
         * tag/key/value at load-time rather than at read-value time.
         */
-       bool LoadConf(ConfigDataHash &target, const std::string &filename, std::ostringstream &errorstream);
+       bool LoadConf(ConfigDataHash &target, const std::string &filename, std::ostringstream &errorstream, int pass);
        
        /* Both these return true if the value existed or false otherwise */
        
index 5190563ef9dccef3f1ec57050c20abd01d4b0c3b..a3cac1486b891af17c9e8302334729bfe9500c01 100644 (file)
@@ -401,10 +401,12 @@ enum Implementation
        I_OnRawSocketAccept, I_OnRawSocketClose, I_OnRawSocketWrite, I_OnRawSocketRead, I_OnChangeLocalUserGECOS, I_OnUserRegister,
        I_OnOperCompare, I_OnChannelDelete, I_OnPostOper, I_OnSyncOtherMetaData, I_OnSetAway, I_OnCancelAway, I_OnUserList,
        I_OnPostCommand, I_OnPostJoin, I_OnWhoisLine, I_OnBuildExemptList, I_OnRawSocketConnect, I_OnGarbageCollect, I_OnBufferFlushed,
-       I_OnText,
+       I_OnText, I_OnReadConfig, I_OnDownloadFile,
        I_END
 };
 
+class ConfigReader;
+
 /** Base class for all InspIRCd modules
  *  This class is the base class for InspIRCd modules. All modules must inherit from this class,
  *  its methods will be called when irc server events occur. class inherited from module must be
@@ -434,6 +436,10 @@ class CoreExport Module : public Extensible
        {
        }
 
+       virtual void OnReadConfig(ServerConfig* config, ConfigReader* coreconf);
+
+       virtual int OnDownloadFile(const std::string &filename, std::stringstream &filedata);
+
        /** Returns the version number of a Module.
         * The method should return a Version object with its version information assigned via
         * Version::Version
index 115a22eabd032d41c9048bc8d742c3630d8565e2..d9afb99dd3ef63e9665e6dc4bd7fade55f80b9e6 100644 (file)
@@ -781,365 +781,395 @@ void ServerConfig::ReportConfigError(const std::string &errormessage, bool bail,
 
 void ServerConfig::Read(bool bail, User* user)
 {
-       static char debug[MAXBUF];      /* Temporary buffer for debugging value */
-       static char maxkeep[MAXBUF];    /* Temporary buffer for WhoWasMaxKeep value */
-       static char hidemodes[MAXBUF];  /* Modes to not allow listing from users below halfop */
-       static char exemptchanops[MAXBUF];      /* Exempt channel ops from these modes */
-       static char announceinvites[MAXBUF];    /* options:announceinvites setting */
-       int rem = 0, add = 0;           /* Number of modules added, number of modules removed */
-       std::ostringstream errstr;      /* String stream containing the error output */
-
-       /* These tags MUST occur and must ONLY occur once in the config file */
-       static char* Once[] = { "server", "admin", "files", "power", "options", NULL };
-
-       /* These tags can occur ONCE or not at all */
-       InitialConfig Values[] = {
-               {"options",     "softlimit",    MAXCLIENTS_S,           new ValueContainerUInt (&this->SoftLimit),              DT_INTEGER,  ValidateSoftLimit},
-               {"options",     "somaxconn",    SOMAXCONN_S,            new ValueContainerInt  (&this->MaxConn),                DT_INTEGER,  ValidateMaxConn},
-               {"options",     "moronbanner",  "Youre banned!",        new ValueContainerChar (this->MoronBanner),             DT_CHARPTR,  NoValidation},
-               {"server",      "name",         "",                     new ValueContainerChar (this->ServerName),              DT_HOSTNAME, ValidateServerName},
-               {"server",      "description",  "Configure Me",         new ValueContainerChar (this->ServerDesc),              DT_CHARPTR,  NoValidation},
-               {"server",      "network",      "Network",              new ValueContainerChar (this->Network),                 DT_NOSPACES, NoValidation},
-               {"server",      "id",           "0",                    new ValueContainerInt  (&this->sid),                    DT_NOSPACES, ValidateSID},
-               {"admin",       "name",         "",                     new ValueContainerChar (this->AdminName),               DT_CHARPTR,  NoValidation},
-               {"admin",       "email",        "Mis@configu.red",      new ValueContainerChar (this->AdminEmail),              DT_CHARPTR,  NoValidation},
-               {"admin",       "nick",         "Misconfigured",        new ValueContainerChar (this->AdminNick),               DT_CHARPTR,  NoValidation},
-               {"files",       "motd",         "",                     new ValueContainerChar (this->motd),                    DT_CHARPTR,  ValidateMotd},
-               {"files",       "rules",        "",                     new ValueContainerChar (this->rules),                   DT_CHARPTR,  ValidateRules},
-               {"power",       "diepass",      "",                     new ValueContainerChar (this->diepass),                 DT_CHARPTR,  ValidateNotEmpty},
-               {"power",       "pause",        "",                     new ValueContainerInt  (&this->DieDelay),               DT_INTEGER,  NoValidation},
-               {"power",       "restartpass",  "",                     new ValueContainerChar (this->restartpass),             DT_CHARPTR,  ValidateNotEmpty},
-               {"options",     "prefixquit",   "",                     new ValueContainerChar (this->PrefixQuit),              DT_CHARPTR,  NoValidation},
-               {"options",     "suffixquit",   "",                     new ValueContainerChar (this->SuffixQuit),              DT_CHARPTR,  NoValidation},
-               {"options",     "fixedquit",    "",                     new ValueContainerChar (this->FixedQuit),               DT_CHARPTR,  NoValidation},
-               {"options",     "loglevel",     "default",              new ValueContainerChar (debug),                         DT_CHARPTR,  ValidateLogLevel},
-               {"options",     "netbuffersize","10240",                new ValueContainerInt  (&this->NetBufferSize),          DT_INTEGER,  ValidateNetBufferSize},
-               {"options",     "maxwho",       "128",                  new ValueContainerInt  (&this->MaxWhoResults),          DT_INTEGER,  ValidateMaxWho},
-               {"options",     "allowhalfop",  "0",                    new ValueContainerBool (&this->AllowHalfop),            DT_BOOLEAN,  NoValidation},
-               {"dns",         "server",       "",                     new ValueContainerChar (this->DNSServer),               DT_IPADDRESS,DNSServerValidator},
-               {"dns",         "timeout",      "5",                    new ValueContainerInt  (&this->dns_timeout),            DT_INTEGER,  NoValidation},
-               {"options",     "moduledir",    MOD_PATH,               new ValueContainerChar (this->ModPath),                 DT_CHARPTR,  NoValidation},
-               {"disabled",    "commands",     "",                     new ValueContainerChar (this->DisabledCommands),        DT_CHARPTR,  NoValidation},
-               {"options",     "userstats",    "",                     new ValueContainerChar (this->UserStats),               DT_CHARPTR,  NoValidation},
-               {"options",     "customversion","",                     new ValueContainerChar (this->CustomVersion),           DT_CHARPTR,  NoValidation},
-               {"options",     "hidesplits",   "0",                    new ValueContainerBool (&this->HideSplits),             DT_BOOLEAN,  NoValidation},
-               {"options",     "hidebans",     "0",                    new ValueContainerBool (&this->HideBans),               DT_BOOLEAN,  NoValidation},
-               {"options",     "hidewhois",    "",                     new ValueContainerChar (this->HideWhoisServer),         DT_NOSPACES, NoValidation},
-               {"options",     "hidekills",    "",                     new ValueContainerChar (this->HideKillsServer),         DT_NOSPACES,  NoValidation},
-               {"options",     "operspywhois", "0",                    new ValueContainerBool (&this->OperSpyWhois),           DT_BOOLEAN,  NoValidation},
-               {"options",     "nouserdns",    "0",                    new ValueContainerBool (&this->NoUserDns),              DT_BOOLEAN,  NoValidation},
-               {"options",     "syntaxhints",  "0",                    new ValueContainerBool (&this->SyntaxHints),            DT_BOOLEAN,  NoValidation},
-               {"options",     "cyclehosts",   "0",                    new ValueContainerBool (&this->CycleHosts),             DT_BOOLEAN,  NoValidation},
-               {"options",     "ircumsgprefix","0",                    new ValueContainerBool (&this->UndernetMsgPrefix),      DT_BOOLEAN,  NoValidation},
-               {"options",     "announceinvites", "1",                 new ValueContainerChar (announceinvites),               DT_CHARPTR,  ValidateInvite},
-               {"options",     "hostintopic",  "1",                    new ValueContainerBool (&this->FullHostInTopic),        DT_BOOLEAN,  NoValidation},
-               {"options",     "hidemodes",    "",                     new ValueContainerChar (hidemodes),                     DT_CHARPTR,  ValidateModeLists},
-               {"options",     "exemptchanops","",                     new ValueContainerChar (exemptchanops),                 DT_CHARPTR,  ValidateExemptChanOps},
-               {"options",     "maxtargets",   "20",                   new ValueContainerUInt (&this->MaxTargets),             DT_INTEGER,  ValidateMaxTargets},
-               {"options",     "defaultmodes", "nt",                   new ValueContainerChar (this->DefaultModes),            DT_CHARPTR,  NoValidation},
-               {"pid",         "file",         "",                     new ValueContainerChar (this->PID),                     DT_CHARPTR,  NoValidation},
-               {"whowas",      "groupsize",    "10",                   new ValueContainerInt  (&this->WhoWasGroupSize),        DT_INTEGER,  NoValidation},
-               {"whowas",      "maxgroups",    "10240",                new ValueContainerInt  (&this->WhoWasMaxGroups),        DT_INTEGER,  NoValidation},
-               {"whowas",      "maxkeep",      "3600",                 new ValueContainerChar (maxkeep),                       DT_CHARPTR,  ValidateWhoWas},
-               {"die",         "value",        "",                     new ValueContainerChar (this->DieValue),                DT_CHARPTR,  NoValidation},
-               {"channels",    "users",        "20",                   new ValueContainerUInt (&this->MaxChans),               DT_INTEGER,  NoValidation},
-               {"channels",    "opers",        "60",                   new ValueContainerUInt (&this->OperMaxChans),           DT_INTEGER,  NoValidation},
-               {NULL,          NULL,           NULL,                   NULL,                                                   DT_NOTHING,  NoValidation}
-       };
-
-       /* These tags can occur multiple times, and therefore they have special code to read them
-        * which is different to the code for reading the singular tags listed above.
-        */
-       MultiConfig MultiValues[] = {
-
-               {"connect",
-                               {"allow",       "deny",         "password",     "timeout",      "pingfreq",     "flood",
-                               "threshold",    "sendq",        "recvq",        "localmax",     "globalmax",    "port",
-                               "name",         "parent",       "maxchans",     "limit",
-                               NULL},
-                               {"",            "",             "",             "",             "120",          "",
-                                "",            "",             "",             "3",            "3",            "0",
-                                "",            "",             "0",        "0",
-                                NULL},
-                               {DT_IPADDRESS|DT_ALLOW_WILD,
-                                               DT_IPADDRESS|DT_ALLOW_WILD,
-                                                               DT_CHARPTR,     DT_INTEGER,     DT_INTEGER,     DT_INTEGER,
-                                DT_INTEGER,    DT_INTEGER,     DT_INTEGER,     DT_INTEGER,     DT_INTEGER,     DT_INTEGER,
-                                DT_NOSPACES,   DT_NOSPACES,    DT_INTEGER,     DT_INTEGER},
-                               InitConnect, DoConnect, DoneConnect},
-
-               {"uline",
-                               {"server",      "silent",       NULL},
-                               {"",            "0",            NULL},
-                               {DT_HOSTNAME,   DT_BOOLEAN},
-                               InitULine,DoULine,DoneULine},
-
-               {"banlist",
-                               {"chan",        "limit",        NULL},
-                               {"",            "",             NULL},
-                               {DT_CHARPTR,    DT_INTEGER},
-                               InitMaxBans, DoMaxBans, DoneMaxBans},
-
-               {"module",
-                               {"name",        NULL},
-                               {"",            NULL},
-                               {DT_CHARPTR},
-                               InitModule, DoModule, DoneModule},
-
-               {"badip",
-                               {"reason",      "ipmask",       NULL},
-                               {"No reason",   "",             NULL},
-                               {DT_CHARPTR,    DT_IPADDRESS|DT_ALLOW_WILD},
-                               InitXLine, DoZLine, DoneConfItem},
-
-               {"badnick",
-                               {"reason",      "nick",         NULL},
-                               {"No reason",   "",             NULL},
-                               {DT_CHARPTR,    DT_CHARPTR},
-                               InitXLine, DoQLine, DoneConfItem},
-
-               {"badhost",
-                               {"reason",      "host",         NULL},
-                               {"No reason",   "",             NULL},
-                               {DT_CHARPTR,    DT_CHARPTR},
-                               InitXLine, DoKLine, DoneConfItem},
-
-               {"exception",
-                               {"reason",      "host",         NULL},
-                               {"No reason",   "",             NULL},
-                               {DT_CHARPTR,    DT_CHARPTR},
-                               InitXLine, DoELine, DoneELine},
-
-               {"type",
-                               {"name",        "classes",      NULL},
-                               {"",            "",             NULL},
-                               {DT_NOSPACES,   DT_CHARPTR},
-                               InitTypes, DoType, DoneClassesAndTypes},
-
-               {"class",
-                               {"name",        "commands",     NULL},
-                               {"",            "",             NULL},
-                               {DT_NOSPACES,   DT_CHARPTR},
-                               InitClasses, DoClass, DoneClassesAndTypes},
-
-               {NULL,
-                               {NULL},
-                               {NULL},
-                               {0},
-                               NULL, NULL, NULL}
-       };
-
-       include_stack.clear();
-
-       /* Load and parse the config file, if there are any errors then explode */
-
-       /* Make a copy here so if it fails then we can carry on running with an unaffected config */
-       ConfigDataHash newconfig;
+       int rem = 0, add = 0;           /* Number of modules added, number of modules removed */
 
-       if (this->LoadConf(newconfig, ServerInstance->ConfigFileName, errstr))
+       for (int pass = 0; pass < 2; pass++)
        {
-               /* If we succeeded, set the ircd config to the new one */
-               this->config_data = newconfig;
-       }
-       else
-       {
-               ReportConfigError(errstr.str(), bail, user);
-               return;
-       }
-
-       /* The stuff in here may throw CoreException, be sure we're in a position to catch it. */
-       try
-       {
-               /* Check we dont have more than one of singular tags, or any of them missing
-                */
-               for (int Index = 0; Once[Index]; Index++)
-                       if (!CheckOnce(Once[Index]))
-                               return;
-
-               /* Read the values of all the tags which occur once or not at all, and call their callbacks.
+       
+               ServerInstance->Log(DEBUG,"Start config pass %d", pass);
+
+               static char debug[MAXBUF];      /* Temporary buffer for debugging value */
+               static char maxkeep[MAXBUF];    /* Temporary buffer for WhoWasMaxKeep value */
+               static char hidemodes[MAXBUF];  /* Modes to not allow listing from users below halfop */
+               static char exemptchanops[MAXBUF];      /* Exempt channel ops from these modes */
+               static char announceinvites[MAXBUF];    /* options:announceinvites setting */
+               std::ostringstream errstr;      /* String stream containing the error output */
+
+               /* These tags MUST occur and must ONLY occur once in the config file */
+               static char* Once[] = { "server", "admin", "files", "power", "options", NULL };
+
+               /* These tags can occur ONCE or not at all */
+               InitialConfig Values[] = {
+                       {"options",     "softlimit",    MAXCLIENTS_S,           new ValueContainerUInt (&this->SoftLimit),              DT_INTEGER,  ValidateSoftLimit},
+                       {"options",     "somaxconn",    SOMAXCONN_S,            new ValueContainerInt  (&this->MaxConn),                DT_INTEGER,  ValidateMaxConn},
+                       {"options",     "moronbanner",  "Youre banned!",        new ValueContainerChar (this->MoronBanner),             DT_CHARPTR,  NoValidation},
+                       {"server",      "name",         "",                     new ValueContainerChar (this->ServerName),              DT_HOSTNAME, ValidateServerName},
+                       {"server",      "description",  "Configure Me",         new ValueContainerChar (this->ServerDesc),              DT_CHARPTR,  NoValidation},
+                       {"server",      "network",      "Network",              new ValueContainerChar (this->Network),                 DT_NOSPACES, NoValidation},
+                       {"server",      "id",           "0",                    new ValueContainerInt  (&this->sid),                    DT_NOSPACES, ValidateSID},
+                       {"admin",       "name",         "",                     new ValueContainerChar (this->AdminName),               DT_CHARPTR,  NoValidation},
+                       {"admin",       "email",        "Mis@configu.red",      new ValueContainerChar (this->AdminEmail),              DT_CHARPTR,  NoValidation},
+                       {"admin",       "nick",         "Misconfigured",        new ValueContainerChar (this->AdminNick),               DT_CHARPTR,  NoValidation},
+                       {"files",       "motd",         "",                     new ValueContainerChar (this->motd),                    DT_CHARPTR,  ValidateMotd},
+                       {"files",       "rules",        "",                     new ValueContainerChar (this->rules),                   DT_CHARPTR,  ValidateRules},
+                       {"power",       "diepass",      "",                     new ValueContainerChar (this->diepass),                 DT_CHARPTR,  ValidateNotEmpty},
+                       {"power",       "pause",        "",                     new ValueContainerInt  (&this->DieDelay),               DT_INTEGER,  NoValidation},
+                       {"power",       "restartpass",  "",                     new ValueContainerChar (this->restartpass),             DT_CHARPTR,  ValidateNotEmpty},
+                       {"options",     "prefixquit",   "",                     new ValueContainerChar (this->PrefixQuit),              DT_CHARPTR,  NoValidation},
+                       {"options",     "suffixquit",   "",                     new ValueContainerChar (this->SuffixQuit),              DT_CHARPTR,  NoValidation},
+                       {"options",     "fixedquit",    "",                     new ValueContainerChar (this->FixedQuit),               DT_CHARPTR,  NoValidation},
+                       {"options",     "loglevel",     "default",              new ValueContainerChar (debug),                         DT_CHARPTR,  ValidateLogLevel},
+                       {"options",     "netbuffersize","10240",                new ValueContainerInt  (&this->NetBufferSize),          DT_INTEGER,  ValidateNetBufferSize},
+                       {"options",     "maxwho",       "128",                  new ValueContainerInt  (&this->MaxWhoResults),          DT_INTEGER,  ValidateMaxWho},
+                       {"options",     "allowhalfop",  "0",                    new ValueContainerBool (&this->AllowHalfop),            DT_BOOLEAN,  NoValidation},
+                       {"dns",         "server",       "",                     new ValueContainerChar (this->DNSServer),               DT_IPADDRESS,DNSServerValidator},
+                       {"dns",         "timeout",      "5",                    new ValueContainerInt  (&this->dns_timeout),            DT_INTEGER,  NoValidation},
+                       {"options",     "moduledir",    MOD_PATH,               new ValueContainerChar (this->ModPath),                 DT_CHARPTR,  NoValidation},
+                       {"disabled",    "commands",     "",                     new ValueContainerChar (this->DisabledCommands),        DT_CHARPTR,  NoValidation},
+                       {"options",     "userstats",    "",                     new ValueContainerChar (this->UserStats),               DT_CHARPTR,  NoValidation},
+                       {"options",     "customversion","",                     new ValueContainerChar (this->CustomVersion),           DT_CHARPTR,  NoValidation},
+                       {"options",     "hidesplits",   "0",                    new ValueContainerBool (&this->HideSplits),             DT_BOOLEAN,  NoValidation},
+                       {"options",     "hidebans",     "0",                    new ValueContainerBool (&this->HideBans),               DT_BOOLEAN,  NoValidation},
+                       {"options",     "hidewhois",    "",                     new ValueContainerChar (this->HideWhoisServer),         DT_NOSPACES, NoValidation},
+                       {"options",     "hidekills",    "",                     new ValueContainerChar (this->HideKillsServer),         DT_NOSPACES,  NoValidation},
+                       {"options",     "operspywhois", "0",                    new ValueContainerBool (&this->OperSpyWhois),           DT_BOOLEAN,  NoValidation},
+                       {"options",     "nouserdns",    "0",                    new ValueContainerBool (&this->NoUserDns),              DT_BOOLEAN,  NoValidation},
+                       {"options",     "syntaxhints",  "0",                    new ValueContainerBool (&this->SyntaxHints),            DT_BOOLEAN,  NoValidation},
+                       {"options",     "cyclehosts",   "0",                    new ValueContainerBool (&this->CycleHosts),             DT_BOOLEAN,  NoValidation},
+                       {"options",     "ircumsgprefix","0",                    new ValueContainerBool (&this->UndernetMsgPrefix),      DT_BOOLEAN,  NoValidation},
+                       {"options",     "announceinvites", "1",                 new ValueContainerChar (announceinvites),               DT_CHARPTR,  ValidateInvite},
+                       {"options",     "hostintopic",  "1",                    new ValueContainerBool (&this->FullHostInTopic),        DT_BOOLEAN,  NoValidation},
+                       {"options",     "hidemodes",    "",                     new ValueContainerChar (hidemodes),                     DT_CHARPTR,  ValidateModeLists},
+                       {"options",     "exemptchanops","",                     new ValueContainerChar (exemptchanops),                 DT_CHARPTR,  ValidateExemptChanOps},
+                       {"options",     "maxtargets",   "20",                   new ValueContainerUInt (&this->MaxTargets),             DT_INTEGER,  ValidateMaxTargets},
+                       {"options",     "defaultmodes", "nt",                   new ValueContainerChar (this->DefaultModes),            DT_CHARPTR,  NoValidation},
+                       {"pid",         "file",         "",                     new ValueContainerChar (this->PID),                     DT_CHARPTR,  NoValidation},
+                       {"whowas",      "groupsize",    "10",                   new ValueContainerInt  (&this->WhoWasGroupSize),        DT_INTEGER,  NoValidation},
+                       {"whowas",      "maxgroups",    "10240",                new ValueContainerInt  (&this->WhoWasMaxGroups),        DT_INTEGER,  NoValidation},
+                       {"whowas",      "maxkeep",      "3600",                 new ValueContainerChar (maxkeep),                       DT_CHARPTR,  ValidateWhoWas},
+                       {"die",         "value",        "",                     new ValueContainerChar (this->DieValue),                DT_CHARPTR,  NoValidation},
+                       {"channels",    "users",        "20",                   new ValueContainerUInt (&this->MaxChans),               DT_INTEGER,  NoValidation},
+                       {"channels",    "opers",        "60",                   new ValueContainerUInt (&this->OperMaxChans),           DT_INTEGER,  NoValidation},
+                       {NULL,          NULL,           NULL,                   NULL,                                                   DT_NOTHING,  NoValidation}
+               };
+
+               /* These tags can occur multiple times, and therefore they have special code to read them
+                * which is different to the code for reading the singular tags listed above.
                 */
-               for (int Index = 0; Values[Index].tag; Index++)
+               MultiConfig MultiValues[] = {
+
+                       {"connect",
+                                       {"allow",       "deny",         "password",     "timeout",      "pingfreq",     "flood",
+                                       "threshold",    "sendq",        "recvq",        "localmax",     "globalmax",    "port",
+                                       "name",         "parent",       "maxchans",     "limit",
+                                       NULL},
+                                       {"",            "",             "",             "",             "120",          "",
+                                        "",            "",             "",             "3",            "3",            "0",
+                                        "",            "",             "0",        "0",
+                                        NULL},
+                                       {DT_IPADDRESS|DT_ALLOW_WILD,
+                                                       DT_IPADDRESS|DT_ALLOW_WILD,
+                                                                       DT_CHARPTR,     DT_INTEGER,     DT_INTEGER,     DT_INTEGER,
+                                       DT_INTEGER,     DT_INTEGER,     DT_INTEGER,     DT_INTEGER,     DT_INTEGER,     DT_INTEGER,
+                                       DT_NOSPACES,    DT_NOSPACES,    DT_INTEGER,     DT_INTEGER},
+                                       InitConnect, DoConnect, DoneConnect},
+
+                       {"uline",
+                                       {"server",      "silent",       NULL},
+                                       {"",            "0",            NULL},
+                                       {DT_HOSTNAME,   DT_BOOLEAN},
+                                       InitULine,DoULine,DoneULine},
+
+                       {"banlist",
+                                       {"chan",        "limit",        NULL},
+                                       {"",            "",             NULL},
+                                       {DT_CHARPTR,    DT_INTEGER},
+                                       InitMaxBans, DoMaxBans, DoneMaxBans},
+
+                       {"module",
+                                       {"name",        NULL},
+                                       {"",            NULL},
+                                       {DT_CHARPTR},
+                                       InitModule, DoModule, DoneModule},
+
+                       {"badip",
+                                       {"reason",      "ipmask",       NULL},
+                                       {"No reason",   "",             NULL},
+                                       {DT_CHARPTR,    DT_IPADDRESS|DT_ALLOW_WILD},
+                                       InitXLine, DoZLine, DoneConfItem},
+
+                       {"badnick",
+                                       {"reason",      "nick",         NULL},
+                                       {"No reason",   "",             NULL},
+                                       {DT_CHARPTR,    DT_CHARPTR},
+                                       InitXLine, DoQLine, DoneConfItem},
+       
+                       {"badhost",
+                                       {"reason",      "host",         NULL},
+                                       {"No reason",   "",             NULL},
+                                       {DT_CHARPTR,    DT_CHARPTR},
+                                       InitXLine, DoKLine, DoneConfItem},
+
+                       {"exception",
+                                       {"reason",      "host",         NULL},
+                                       {"No reason",   "",             NULL},
+                                       {DT_CHARPTR,    DT_CHARPTR},
+                                       InitXLine, DoELine, DoneELine},
+       
+                       {"type",
+                                       {"name",        "classes",      NULL},
+                                       {"",            "",             NULL},
+                                       {DT_NOSPACES,   DT_CHARPTR},
+                                       InitTypes, DoType, DoneClassesAndTypes},
+       
+                       {"class",
+                                       {"name",        "commands",     NULL},
+                                       {"",            "",             NULL},
+                                       {DT_NOSPACES,   DT_CHARPTR},
+                                       InitClasses, DoClass, DoneClassesAndTypes},
+       
+                       {NULL,
+                                       {NULL},
+                                       {NULL},
+                                       {0},
+                                       NULL, NULL, NULL}
+               };
+
+               include_stack.clear();
+
+               /* Load and parse the config file, if there are any errors then explode */
+
+               /* Make a copy here so if it fails then we can carry on running with an unaffected config */
+               ConfigDataHash newconfig;
+
+               if (this->LoadConf(newconfig, ServerInstance->ConfigFileName, errstr, pass))
                {
-                       char item[MAXBUF];
-                       int dt = Values[Index].datatype;
-                       bool allow_newlines = ((dt & DT_ALLOW_NEWLINE) > 0);
-                       bool allow_wild = ((dt & DT_ALLOW_WILD) > 0);
-                       dt &= ~DT_ALLOW_NEWLINE;
-                       dt &= ~DT_ALLOW_WILD;
-
-                       ConfValue(this->config_data, Values[Index].tag, Values[Index].value, Values[Index].default_value, 0, item, MAXBUF, allow_newlines);
-                       ValueItem vi(item);
-
-                       if (!Values[Index].validation_function(this, Values[Index].tag, Values[Index].value, vi))
-                               throw CoreException("One or more values in your configuration file failed to validate. Please see your ircd.log for more information.");
+                       /* If we succeeded, set the ircd config to the new one */
+                       this->config_data = newconfig;
+               }
+               else
+               {
+                       ReportConfigError(errstr.str(), bail, user);
+                       return;
+               }
 
-                       switch (Values[Index].datatype)
+               /* The stuff in here may throw CoreException, be sure we're in a position to catch it. */
+               try
+               {
+                       /* Read the values of all the tags which occur once or not at all, and call their callbacks.
+                        */
+                       for (int Index = 0; Values[Index].tag; Index++)
                        {
-                               case DT_NOSPACES:
-                               {
-                                       ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val;
-                                       this->ValidateNoSpaces(vi.GetString(), Values[Index].tag, Values[Index].value);
-                                       vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
-                               }
-                               break;
-                               case DT_HOSTNAME:
-                               {
-                                       ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val;
-                                       this->ValidateHostname(vi.GetString(), Values[Index].tag, Values[Index].value);
-                                       vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
-                               }
-                               break;
-                               case DT_IPADDRESS:
-                               {
-                                       ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val;
-                                       this->ValidateIP(vi.GetString(), Values[Index].tag, Values[Index].value, allow_wild);
-                                       vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
-                               }
-                               break;
-                               case DT_CHANNEL:
-                               {
-                                       ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val;
-                                       if (*(vi.GetString()) && !ServerInstance->IsChannel(vi.GetString()))
-                                               throw CoreException("The value of <"+std::string(Values[Index].tag)+":"+Values[Index].value+"> is not a valid channel name");
-                                       vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
-                               }
-                               break;
-                               case DT_CHARPTR:
+                               char item[MAXBUF];
+                               int dt = Values[Index].datatype;
+                               bool allow_newlines = ((dt & DT_ALLOW_NEWLINE) > 0);
+                               bool allow_wild = ((dt & DT_ALLOW_WILD) > 0);
+                               dt &= ~DT_ALLOW_NEWLINE;
+                               dt &= ~DT_ALLOW_WILD;
+
+                               ConfValue(this->config_data, Values[Index].tag, Values[Index].value, Values[Index].default_value, 0, item, MAXBUF, allow_newlines);
+                               ValueItem vi(item);
+
+                               if (!Values[Index].validation_function(this, Values[Index].tag, Values[Index].value, vi))
+                                       throw CoreException("One or more values in your configuration file failed to validate. Please see your ircd.log for more information.");
+       
+                               switch (Values[Index].datatype)
                                {
-                                       ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val;
-                                       /* Make sure we also copy the null terminator */
-                                       vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
-                               }
-                               break;
-                               case DT_INTEGER:
-                               {
-                                       int val = vi.GetInteger();
-                                       ValueContainerInt* vci = (ValueContainerInt*)Values[Index].val;
-                                       vci->Set(&val, sizeof(int));
-                               }
-                               break;
-                               case DT_BOOLEAN:
-                               {
-                                       bool val = vi.GetBool();
-                                       ValueContainerBool* vcb = (ValueContainerBool*)Values[Index].val;
-                                       vcb->Set(&val, sizeof(bool));
+                                       case DT_NOSPACES:
+                                       {
+                                               ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val;
+                                               this->ValidateNoSpaces(vi.GetString(), Values[Index].tag, Values[Index].value);
+                                               vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
+                                       }
+                                       break;
+                                       case DT_HOSTNAME:
+                                       {
+                                               ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val;
+                                               this->ValidateHostname(vi.GetString(), Values[Index].tag, Values[Index].value);
+                                               vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
+                                       }
+                                       break;
+                                       case DT_IPADDRESS:
+                                       {
+                                               ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val;
+                                               this->ValidateIP(vi.GetString(), Values[Index].tag, Values[Index].value, allow_wild);
+                                               vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
+                                       }
+                                       break;
+                                       case DT_CHANNEL:
+                                       {
+                                               ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val;
+                                               if (*(vi.GetString()) && !ServerInstance->IsChannel(vi.GetString()))
+                                                       throw CoreException("The value of <"+std::string(Values[Index].tag)+":"+Values[Index].value+"> is not a valid channel name");
+                                               vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
+                                       }
+                                       break;
+                                       case DT_CHARPTR:
+                                       {
+                                                       ValueContainerChar* vcc = (ValueContainerChar*)Values[Index].val;
+                                               /* Make sure we also copy the null terminator */
+                                               vcc->Set(vi.GetString(), strlen(vi.GetString()) + 1);
+                                       }
+                                       break;
+                                       case DT_INTEGER:
+                                       {
+                                               int val = vi.GetInteger();
+                                               ValueContainerInt* vci = (ValueContainerInt*)Values[Index].val;
+                                               vci->Set(&val, sizeof(int));
+                                       }
+                                       break;
+                                       case DT_BOOLEAN:
+                                       {
+                                               bool val = vi.GetBool();
+                                               ValueContainerBool* vcb = (ValueContainerBool*)Values[Index].val;
+                                               vcb->Set(&val, sizeof(bool));
+                                       }
+                                       break;
+                                       default:
+                                               /* You don't want to know what happens if someones bad code sends us here. */
+                                       break;
                                }
-                               break;
-                               default:
-                                       /* You don't want to know what happens if someones bad code sends us here. */
-                               break;
+       
+                               /* We're done with this now */
+                               delete Values[Index].val;
                        }
 
-                       /* We're done with this now */
-                       delete Values[Index].val;
-               }
-
-               /* Read the multiple-tag items (class tags, connect tags, etc)
-                * and call the callbacks associated with them. We have three
-                * callbacks for these, a 'start', 'item' and 'end' callback.
-                */
-               for (int Index = 0; MultiValues[Index].tag; Index++)
-               {
-                       MultiValues[Index].init_function(this, MultiValues[Index].tag);
-
-                       int number_of_tags = ConfValueEnum(this->config_data, MultiValues[Index].tag);
-
-                       for (int tagnum = 0; tagnum < number_of_tags; tagnum++)
+                       /* Read the multiple-tag items (class tags, connect tags, etc)
+                        * and call the callbacks associated with them. We have three
+                        * callbacks for these, a 'start', 'item' and 'end' callback.
+                        */
+                       for (int Index = 0; MultiValues[Index].tag; Index++)
                        {
-                               ValueList vl;
-                               for (int valuenum = 0; MultiValues[Index].items[valuenum]; valuenum++)
+                               MultiValues[Index].init_function(this, MultiValues[Index].tag);
+       
+                               int number_of_tags = ConfValueEnum(this->config_data, MultiValues[Index].tag);
+       
+                               for (int tagnum = 0; tagnum < number_of_tags; tagnum++)
                                {
-                                       int dt = MultiValues[Index].datatype[valuenum];
-                                       bool allow_newlines =  ((dt & DT_ALLOW_NEWLINE) > 0);
-                                       bool allow_wild = ((dt & DT_ALLOW_WILD) > 0);
-                                       dt &= ~DT_ALLOW_NEWLINE;
-                                       dt &= ~DT_ALLOW_WILD;
-
-                                       switch (dt)
+                                       ValueList vl;
+                                       for (int valuenum = 0; MultiValues[Index].items[valuenum]; valuenum++)
                                        {
-                                               case DT_NOSPACES:
-                                               {
-                                                       char item[MAXBUF];
-                                                       if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines))
-                                                               vl.push_back(ValueItem(item));
-                                                       else
-                                                               vl.push_back(ValueItem(""));
-                                                       this->ValidateNoSpaces(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum]);
-                                               }
-                                               break;
-                                               case DT_HOSTNAME:
-                                               {
-                                                       char item[MAXBUF];
-                                                       if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines))
-                                                               vl.push_back(ValueItem(item));
-                                                       else
-                                                               vl.push_back(ValueItem(""));
-                                                       this->ValidateHostname(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum]);
-                                               }
-                                               break;
-                                               case DT_IPADDRESS:
-                                               {
-                                                       char item[MAXBUF];
-                                                       if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines))
-                                                               vl.push_back(ValueItem(item));
-                                                       else
-                                                               vl.push_back(ValueItem(""));
-                                                       this->ValidateIP(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum], allow_wild);
-                                               }
-                                               break;
-                                               case DT_CHANNEL:
-                                               {
-                                                       char item[MAXBUF];
-                                                       if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines))
-                                                               vl.push_back(ValueItem(item));
-                                                       else
-                                                               vl.push_back(ValueItem(""));
-                                                       if (!ServerInstance->IsChannel(vl[vl.size()-1].GetString()))
-                                                               throw CoreException("The value of <"+std::string(MultiValues[Index].tag)+":"+MultiValues[Index].items[valuenum]+"> number "+ConvToStr(tagnum + 1)+" is not a valid channel name");
-                                               }
-                                               break;
-                                               case DT_CHARPTR:
-                                               {
-                                                       char item[MAXBUF];
-                                                       if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines))
-                                                               vl.push_back(ValueItem(item));
-                                                       else
-                                                               vl.push_back(ValueItem(""));
-                                               }
-                                               break;
-                                               case DT_INTEGER:
+                                               int dt = MultiValues[Index].datatype[valuenum];
+                                               bool allow_newlines =  ((dt & DT_ALLOW_NEWLINE) > 0);
+                                               bool allow_wild = ((dt & DT_ALLOW_WILD) > 0);
+                                               dt &= ~DT_ALLOW_NEWLINE;
+                                               dt &= ~DT_ALLOW_WILD;
+
+                                               switch (dt)
                                                {
-                                                       int item = 0;
-                                                       if (ConfValueInteger(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item))
+                                                       case DT_NOSPACES:
+                                                       {
+                                                               char item[MAXBUF];
+                                                               if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines))
+                                                                       vl.push_back(ValueItem(item));
+                                                               else
+                                                                       vl.push_back(ValueItem(""));
+                                                               this->ValidateNoSpaces(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum]);
+                                                       }
+                                                       break;
+                                                       case DT_HOSTNAME:
+                                                       {
+                                                               char item[MAXBUF];
+                                                               if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines))
+                                                                       vl.push_back(ValueItem(item));
+                                                               else
+                                                                       vl.push_back(ValueItem(""));
+                                                               this->ValidateHostname(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum]);
+                                                       }
+                                                       break;
+                                                       case DT_IPADDRESS:
+                                                       {
+                                                               char item[MAXBUF];
+                                                               if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines))
+                                                                       vl.push_back(ValueItem(item));
+                                                               else
+                                                                       vl.push_back(ValueItem(""));
+                                                               this->ValidateIP(vl[vl.size()-1].GetString(), MultiValues[Index].tag, MultiValues[Index].items[valuenum], allow_wild);
+                                                       }
+                                                       break;
+                                                       case DT_CHANNEL:
+                                                       {
+                                                               char item[MAXBUF];
+                                                               if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines))
+                                                                       vl.push_back(ValueItem(item));
+                                                               else
+                                                                       vl.push_back(ValueItem(""));
+                                                               if (!ServerInstance->IsChannel(vl[vl.size()-1].GetString()))
+                                                                       throw CoreException("The value of <"+std::string(MultiValues[Index].tag)+":"+MultiValues[Index].items[valuenum]+"> number "+ConvToStr(tagnum + 1)+" is not a valid channel name");
+                                                       }
+                                                       break;
+                                                       case DT_CHARPTR:
+                                                       {
+                                                               char item[MAXBUF];
+                                                               if (ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item, MAXBUF, allow_newlines))
+                                                                       vl.push_back(ValueItem(item));
+                                                               else
+                                                                       vl.push_back(ValueItem(""));
+                                                       }
+                                                       break;
+                                                       case DT_INTEGER:
+                                                       {
+                                                               int item = 0;
+                                                               if (ConfValueInteger(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum, item))
+                                                                       vl.push_back(ValueItem(item));
+                                                               else
+                                                                       vl.push_back(ValueItem(0));
+                                                       }
+                                                       break;
+                                                       case DT_BOOLEAN:
+                                                       {
+                                                               bool item = ConfValueBool(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum);
                                                                vl.push_back(ValueItem(item));
-                                                       else
-                                                               vl.push_back(ValueItem(0));
-                                               }
-                                               break;
-                                               case DT_BOOLEAN:
-                                               {
-                                                       bool item = ConfValueBool(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], MultiValues[Index].items_default[valuenum], tagnum);
-                                                       vl.push_back(ValueItem(item));
+                                                       }
+                                                       break;
+                                                       default:
+                                                               /* Someone was smoking craq if we got here, and we're all gonna die. */
+                                                       break;
                                                }
-                                               break;
-                                               default:
-                                                       /* Someone was smoking craq if we got here, and we're all gonna die. */
-                                               break;
                                        }
+
+                                       MultiValues[Index].validation_function(this, MultiValues[Index].tag, (char**)MultiValues[Index].items, vl, MultiValues[Index].datatype);
                                }
 
-                               MultiValues[Index].validation_function(this, MultiValues[Index].tag, (char**)MultiValues[Index].items, vl, MultiValues[Index].datatype);
+                               MultiValues[Index].finish_function(this, MultiValues[Index].tag);
                        }
+       
+               }
 
-                       MultiValues[Index].finish_function(this, MultiValues[Index].tag);
+               catch (CoreException &ce)
+               {
+                       ReportConfigError(ce.GetReason(), bail, user);
+                       return;
                }
 
-       }
+               /** XXX END PASS **/
+               ServerInstance->Log(DEBUG,"End config pass %d", pass);
+
+               if (pass == 0)
+               {
+                       /* FIRST PASS: Set up commands, load modules.
+                        * We cannot gaurantee that all config is correct
+                        * at this point
+                        */
+       
+                       /** Note: This is safe, the method checks for user == NULL */
+                       ServerInstance->Parser->SetupCommandTable(user);
+                       ServerInstance->Modules->LoadAll();
+               }
+               else
+               {
+                       /* SECOND PASS: Call modules to read configs, finalize
+                        * stuff. Check that we have at least the required number
+                        * of whichever items. This is no longer done first.
+                        */
+                       ConfigReader* n = new ConfigReader(ServerInstance);
+                       FOREACH_MOD(I_OnReadConfig,OnReadConfig(this, n));
+
+                       for (int Index = 0; Once[Index]; Index++)
+                               if (!CheckOnce(Once[Index]))
+                                       return;
+               }
 
-       catch (CoreException &ce)
-       {
-               ReportConfigError(ce.GetReason(), bail, user);
-               return;
        }
 
        // write once here, to try it out and make sure its ok
@@ -1165,54 +1195,51 @@ void ServerConfig::Read(bool bail, User* user)
                                user->WriteServ("NOTICE %s :*** %d.   IP: %s     Port: %lu", user->nick, j, i->first.empty() ? "<all>" : i->first.c_str(), (unsigned long)i->second);
                        }
                }
+       }
 
-               if (!removed_modules.empty())
+       if (!removed_modules.empty())
+       {
+               for (std::vector<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
                {
-                       for (std::vector<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
+                       if (ServerInstance->Modules->Unload(removing->c_str()))
                        {
-                               if (ServerInstance->Modules->Unload(removing->c_str()))
-                               {
-                                       ServerInstance->WriteOpers("*** REHASH UNLOADED MODULE: %s",removing->c_str());
-
-                                       if (user)
-                                               user->WriteServ("973 %s %s :Module %s successfully unloaded.",user->nick, removing->c_str(), removing->c_str());
-
-                                       rem++;
-                               }
-                               else
-                               {
-                                       if (user)
-                                               user->WriteServ("972 %s %s :%s",user->nick, removing->c_str(), ServerInstance->Modules->LastError().c_str());
-                               }
+                               ServerInstance->WriteOpers("*** REHASH UNLOADED MODULE: %s",removing->c_str());
+                               if (user)
+                                       user->WriteServ("973 %s %s :Module %s successfully unloaded.",user->nick, removing->c_str(), removing->c_str());
+                               rem++;
+                       }
+                       else
+                       {
+                               if (user)
+                                       user->WriteServ("972 %s %s :%s",user->nick, removing->c_str(), ServerInstance->Modules->LastError().c_str());
                        }
                }
+       }
 
-               if (!added_modules.empty())
+       if (!added_modules.empty())
+       {
+               for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
                {
-                       for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
+                       if (ServerInstance->Modules->Load(adding->c_str()))
                        {
-                               if (ServerInstance->Modules->Load(adding->c_str()))
-                               {
-                                       ServerInstance->WriteOpers("*** REHASH LOADED MODULE: %s",adding->c_str());
+                               ServerInstance->WriteOpers("*** REHASH LOADED MODULE: %s",adding->c_str());
+                               if (user)
+                                       user->WriteServ("975 %s %s :Module %s successfully loaded.",user->nick, adding->c_str(), adding->c_str());
 
-                                       if (user)
-                                               user->WriteServ("975 %s %s :Module %s successfully loaded.",user->nick, adding->c_str(), adding->c_str());
-
-                                       add++;
-                               }
-                               else
-                               {
-                                       if (user)
-                                               user->WriteServ("974 %s %s :%s",user->nick, adding->c_str(), ServerInstance->Modules->LastError().c_str());
-                               }
+                               add++;
+                       }
+                       else
+                       {
+                               if (user)
+                                       user->WriteServ("974 %s %s :%s",user->nick, adding->c_str(), ServerInstance->Modules->LastError().c_str());
                        }
                }
-
-               ServerInstance->Log(DEFAULT,"Successfully unloaded %lu of %lu modules and loaded %lu of %lu modules.",(unsigned long)rem,(unsigned long)removed_modules.size(),(unsigned long)add,(unsigned long)added_modules.size());
        }
 
+       ServerInstance->Log(DEFAULT,"Successfully unloaded %lu of %lu modules and loaded %lu of %lu modules in pass 2.",(unsigned long)rem,(unsigned long)removed_modules.size(),(unsigned long)add,(unsigned long)added_modules.size());
+
        /** Note: This is safe, the method checks for user == NULL */
-       ServerInstance->Parser->SetupCommandTable(user);
+       /*ServerInstance->Parser->SetupCommandTable(user);*/
 
        if (user)
                user->WriteServ("NOTICE %s :*** Successfully rehashed server.", user->nick);
@@ -1220,7 +1247,7 @@ void ServerConfig::Read(bool bail, User* user)
                ServerInstance->WriteOpers("*** Successfully rehashed server.");
 }
 
-bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::ostringstream &errorstream)
+bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::ostringstream &errorstream, int pass)
 {
        std::ifstream conf(filename);
        std::string line;
@@ -1237,11 +1264,22 @@ bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::o
        in_comment = false;
 
        /* Check if the file open failed first */
-       if (!conf)
+       if (!conf && (pass == 1))
        {
-               errorstream << "LoadConf: Couldn't open config file: " << filename << std::endl;
-               return false;
+               int MOD_RESULT = 0;
+               std::stringstream filedata;
+               /** XXX: THIS SHOULD BE NONBLOCKING WITH A CALLBACK, RETURN A VALUE LIKE
+                * LOADCONF_TRYAGAIN, LOADCONF_DONE_WITH_ERROR, LOADCONF_DONE_COMPLETE
+                */
+               FOREACH_RESULT(I_OnDownloadFile, OnDownloadFile(filename, filedata));
+               if (MOD_RESULT == 0)
+               {
+                       errorstream << "LoadConf: Couldn't open config file: " << filename << std::endl;
+                       return false;
+               }
        }
+       else if (!conf && (pass == 0))
+               return true;
 
        /* Fix the chmod of the file to restrict it to the current user and group */
        chmod(filename,0600);
@@ -1400,7 +1438,7 @@ bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::o
                                         * LoadConf() and load the included config into the same ConfigDataHash
                                         */
 
-                                       if (!this->ParseLine(target, line, linenumber, errorstream))
+                                       if (!this->ParseLine(target, line, linenumber, errorstream, pass))
                                                return false;
 
                                        line.clear();
@@ -1424,12 +1462,12 @@ bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::o
        return true;
 }
 
-bool ServerConfig::LoadConf(ConfigDataHash &target, const std::string &filename, std::ostringstream &errorstream)
+bool ServerConfig::LoadConf(ConfigDataHash &target, const std::string &filename, std::ostringstream &errorstream, int pass)
 {
-       return this->LoadConf(target, filename.c_str(), errorstream);
+       return this->LoadConf(target, filename.c_str(), errorstream, pass);
 }
 
-bool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long &linenumber, std::ostringstream &errorstream)
+bool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long &linenumber, std::ostringstream &errorstream, int pass)
 {
        std::string tagname;
        std::string current_key;
@@ -1528,7 +1566,7 @@ bool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long &li
 
                                                if ((tagname == "include") && (current_key == "file"))
                                                {
-                                                       if (!this->DoInclude(target, current_value, errorstream))
+                                                       if (!this->DoInclude(target, current_value, errorstream, pass))
                                                                return false;
                                                }
 
@@ -1553,7 +1591,7 @@ bool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long &li
        return true;
 }
 
-bool ServerConfig::DoInclude(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream)
+bool ServerConfig::DoInclude(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream, int pass)
 {
        std::string confpath;
        std::string newfile;
@@ -1579,7 +1617,7 @@ bool ServerConfig::DoInclude(ConfigDataHash &target, const std::string &file, st
                }
        }
 
-       return LoadConf(target, newfile, errorstream);
+       return LoadConf(target, newfile, errorstream, pass);
 }
 
 bool ServerConfig::ConfValue(ConfigDataHash &target, const char* tag, const char* var, int index, char* result, int length, bool allow_linefeeds)
index 871ed5ab2f1fbbf9bd7f7b54f4bf14870e39ac70..f21716e5bd5f06055dcbf5e1118547cfc63947ee 100644 (file)
@@ -426,11 +426,9 @@ InspIRCd::InspIRCd(int argc, char** argv)
        Config->forcedebug = do_debug;
        Config->writelog = !do_nolog;   
        Config->ClearStack();
-       Config->Read(true, NULL);
 
-       // Get XLine to do it's thing.
-       this->XLines->CheckELines();
-       this->XLines->ApplyLines();
+       this->Modes = new ModeParser(this);
+       this->AddServerName(Config->ServerName);
 
        /*
         * Initialise SID/UID.
@@ -489,16 +487,21 @@ InspIRCd::InspIRCd(int argc, char** argv)
 
        SE->RecoverFromFork();
 
-       this->Modes = new ModeParser(this);
-       this->AddServerName(Config->ServerName);
+       this->Res = new DNS(this);
+
+        Config->Read(true, NULL);
+
+        // Get XLine to do it's thing.
+        this->XLines->CheckELines();
+        this->XLines->ApplyLines();
+
+
        CheckDie();
        int bounditems = BindPorts(true, found_ports, pl);
 
        printf("\n");
 
-       this->Res = new DNS(this);
-
-       this->Modules->LoadAll();
+       /*this->Modules->LoadAll();*/
        
        /* Just in case no modules were loaded - fix for bug #101 */
        this->BuildISupport();
index 8f32e94acc541054fd1b19fb1713c64fcd6daa70..00265dc07199b606e7353ea1e4f47e43e4a8f51b 100644 (file)
@@ -106,6 +106,8 @@ std::string Event::GetEventID()
 
                Module::Module(InspIRCd* Me) : ServerInstance(Me) { }
                Module::~Module() { }
+void           Module::OnReadConfig(ServerConfig*, ConfigReader*) { }
+int            Module::OnDownloadFile(const std::string&, std::stringstream&) { return 0; }
 void           Module::OnUserConnect(User*) { }
 void           Module::OnUserQuit(User*, const std::string&, const std::string&) { }
 void           Module::OnUserDisconnect(User*) { }
@@ -825,7 +827,11 @@ ConfigReader::ConfigReader(InspIRCd* Instance, const std::string &filename) : Se
        this->data = new ConfigDataHash;
        this->privatehash = true;
        this->errorlog = new std::ostringstream(std::stringstream::in | std::stringstream::out);
-       this->readerror = ServerInstance->Config->LoadConf(*this->data, filename, *this->errorlog);
+       for (int pass = 0; pass < 2; pass++)
+       {
+               /*** XXX: Can return a 'not ready yet!' code! */
+               this->readerror = ServerInstance->Config->LoadConf(*this->data, filename, *this->errorlog, pass);
+       }
        if (!this->readerror)
                this->error = CONF_FILE_NOT_FOUND;
 }