]> git.netwichtig.de Git - user/henk/code/inspircd.git/commitdiff
Merge branch 'master+serviceinit'
authorAttila Molnar <attilamolnar@hush.com>
Wed, 2 Dec 2015 12:30:24 +0000 (13:30 +0100)
committerAttila Molnar <attilamolnar@hush.com>
Wed, 2 Dec 2015 12:30:24 +0000 (13:30 +0100)
include/commands/cmd_whowas.h
include/extensible.h
include/modules.h
include/modules/reload.h [new file with mode: 0644]
src/base.cpp
src/coremods/core_reloadmodule.cpp
src/coremods/core_whowas.cpp
src/modules.cpp
src/modules/m_globalload.cpp
src/modules/m_services_account.cpp

index cd2101e9fad7aa15f7947313c7a7796b8679ec26..aaea31864f38bc2ec53cb46811fb14406f5b47ea 100644 (file)
@@ -172,6 +172,16 @@ namespace WhoWas
                /** Shrink all data structures to honor the current settings
                 */
                void Prune();
+
+               /** Remove a nick (and all entries belonging to it) from the database
+                * @param it Iterator to the nick to purge
+                */
+               void PurgeNick(whowas_users::iterator it);
+
+               /** Remove a nick (and all entries belonging to it) from the database
+                * @param nick Nick to purge
+                */
+               void PurgeNick(WhoWas::Nick* nick);
        };
 }
 
index 3453f3e15259195547978609282fc83262f031b4..1a51fdaafe54d62fa5dbb59d9921519bd8d5124c 100644 (file)
@@ -125,11 +125,20 @@ class CoreExport Extensible : public classbase
 
 class CoreExport ExtensionManager
 {
-       std::map<std::string, reference<ExtensionItem> > types;
  public:
+       typedef std::map<std::string, reference<ExtensionItem> > ExtMap;
+
        bool Register(ExtensionItem* item);
        void BeginUnregister(Module* module, std::vector<reference<ExtensionItem> >& list);
        ExtensionItem* GetItem(const std::string& name);
+
+       /** Get all registered extensions keyed by their names
+        * @return Const map of ExtensionItem pointers keyed by their names
+        */
+       const ExtMap& GetExts() const { return types; }
+
+ private:
+       ExtMap types;
 };
 
 /** Base class for items that are NOT synchronized between servers */
@@ -196,6 +205,7 @@ class CoreExport LocalStringExt : public SimpleExtItem<std::string>
        LocalStringExt(const std::string& key, ExtensibleType exttype, Module* owner);
        virtual ~LocalStringExt();
        std::string serialize(SerializeFormat format, const Extensible* container, void* item) const;
+       void unserialize(SerializeFormat format, Extensible* container, const std::string& value);
 };
 
 class CoreExport LocalIntExt : public LocalExtItem
@@ -204,6 +214,7 @@ class CoreExport LocalIntExt : public LocalExtItem
        LocalIntExt(const std::string& key, ExtensibleType exttype, Module* owner);
        virtual ~LocalIntExt();
        std::string serialize(SerializeFormat format, const Extensible* container, void* item) const;
+       void unserialize(SerializeFormat format, Extensible* container, const std::string& value);
        intptr_t get(const Extensible* container) const;
        intptr_t set(Extensible* container, intptr_t value);
        void unset(Extensible* container) { set(container, 0); }
index 378aec1eeb023febfe1336054d144efae095da88..93e5c05a0fd56f9f1c65da2877bab4239f440d08 100644 (file)
@@ -1035,9 +1035,6 @@ class CoreExport ModuleManager : public fakederef<ModuleManager>
                PRIO_STATE_LAST
        } prioritizationState;
 
-       /** Internal unload module hook */
-       bool CanUnload(Module*);
-
        /** Loads all core modules (cmd_*)
         */
        void LoadCoreModules(std::map<std::string, ServiceList>& servicemap);
@@ -1165,18 +1162,19 @@ class CoreExport ModuleManager : public fakederef<ModuleManager>
         */
        bool Unload(Module* module);
 
-       /** Run an asynchronous reload of the given module. When the reload is
-        * complete, the callback will be run with true if the reload succeeded
-        * and false if it did not.
-        */
-       void Reload(Module* module, HandlerBase1<void, bool>* callback);
-
        /** Called by the InspIRCd constructor to load all modules from the config file.
         */
        void LoadAll();
        void UnloadAll();
        void DoSafeUnload(Module*);
 
+       /** Check if a module can be unloaded and if yes, prepare it for unload
+        * @param mod Module to be unloaded
+        * @return True if the module is unloadable, false otherwise.
+        * If true the module must be unloaded in the current main loop iteration.
+        */
+       bool CanUnload(Module* mod);
+
        /** Find a module by name, and return a Module* to it.
         * This is preferred over iterating the module lists yourself.
         * @param name The module name to look up
diff --git a/include/modules/reload.h b/include/modules/reload.h
new file mode 100644 (file)
index 0000000..dcdbc95
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+#include "event.h"
+
+namespace ReloadModule
+{
+       class EventListener;
+       class DataKeeper;
+
+       /** Container for data saved by modules before another module is reloaded.
+        */
+       class CustomData
+       {
+               struct Data
+               {
+                       EventListener* handler;
+                       void* data;
+                       Data(EventListener* Handler, void* moddata) : handler(Handler), data(moddata) { }
+               };
+               typedef std::vector<Data> List;
+               List list;
+
+        public:
+               /** Add data to the saved state of a module.
+                * The provided handler's OnReloadModuleRestore() method will be called when the reload is done with the pointer
+                * provided.
+                * @param handler Handler for restoring the data
+                * @param data Pointer to the data, will be passed back to the provided handler's OnReloadModuleRestore() after the
+                * reload finishes
+                */
+               void add(EventListener* handler, void* data)
+               {
+                       list.push_back(Data(handler, data));
+               }
+
+               friend class DataKeeper;
+       };
+
+       class EventListener : public Events::ModuleEventListener
+       {
+        public:
+               EventListener(Module* mod)
+                       : ModuleEventListener(mod, "event/reloadmodule")
+               {
+               }
+
+               /** Called whenever a module is about to be reloaded. Use this event to save data related to the module that you want
+                * to be restored after the reload.
+                * @param mod Module to be reloaded
+                * @param cd CustomData instance that can store your data once.
+                */
+               virtual void OnReloadModuleSave(Module* mod, CustomData& cd) = 0;
+
+               /** Restore data after a reload. Only called if data was added in OnReloadModuleSave().
+                * @param mod Reloaded module, if NULL the reload failed and the module no longer exists
+                * @param data Pointer that was passed to CustomData::add() in OnReloadModuleSave() at the time when the module's state
+                * was saved
+                */
+               virtual void OnReloadModuleRestore(Module* mod, void* data) = 0;
+       };
+}
index 4111451f8893a2b11b29b1ddbeaad5f356498c00..0ff3fbe4c303256865110bd050492abdeb7c6de2 100644 (file)
@@ -157,10 +157,10 @@ bool ExtensionManager::Register(ExtensionItem* item)
 
 void ExtensionManager::BeginUnregister(Module* module, std::vector<reference<ExtensionItem> >& list)
 {
-       std::map<std::string, reference<ExtensionItem> >::iterator i = types.begin();
+       ExtMap::iterator i = types.begin();
        while (i != types.end())
        {
-               std::map<std::string, reference<ExtensionItem> >::iterator me = i++;
+               ExtMap::iterator me = i++;
                ExtensionItem* item = me->second;
                if (item->creator == module)
                {
@@ -172,7 +172,7 @@ void ExtensionManager::BeginUnregister(Module* module, std::vector<reference<Ext
 
 ExtensionItem* ExtensionManager::GetItem(const std::string& name)
 {
-       std::map<std::string, reference<ExtensionItem> >::iterator i = types.find(name);
+       ExtMap::iterator i = types.find(name);
        if (i == types.end())
                return NULL;
        return i->second;
@@ -248,11 +248,17 @@ LocalStringExt::~LocalStringExt()
 
 std::string LocalStringExt::serialize(SerializeFormat format, const Extensible* container, void* item) const
 {
-       if (item && format == FORMAT_USER)
+       if ((item) && (format != FORMAT_NETWORK))
                return *static_cast<std::string*>(item);
        return "";
 }
 
+void LocalStringExt::unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+{
+       if (format != FORMAT_NETWORK)
+               set(container, value);
+}
+
 LocalIntExt::LocalIntExt(const std::string& Key, ExtensibleType exttype, Module* mod)
        : LocalExtItem(Key, exttype, mod)
 {
@@ -264,11 +270,17 @@ LocalIntExt::~LocalIntExt()
 
 std::string LocalIntExt::serialize(SerializeFormat format, const Extensible* container, void* item) const
 {
-       if (format != FORMAT_USER)
+       if (format == FORMAT_NETWORK)
                return "";
        return ConvToStr(reinterpret_cast<intptr_t>(item));
 }
 
+void LocalIntExt::unserialize(SerializeFormat format, Extensible* container, const std::string& value)
+{
+       if (format != FORMAT_NETWORK)
+               set(container, ConvToInt(value));
+}
+
 intptr_t LocalIntExt::get(const Extensible* container) const
 {
        return reinterpret_cast<intptr_t>(get_raw(container));
index 7f0f15e7735169817acbf2823e73cf98f526a146..0d01d9e856519a44c7fb9bd5c7fab2cfc33545a9 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * InspIRCd -- Internet Relay Chat Daemon
  *
+ *   Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
  *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
  *
 
 
 #include "inspircd.h"
+#include "listmode.h"
+#include "modules/reload.h"
+
+static Events::ModuleEventProvider* reloadevprov;
 
 class CommandReloadmodule : public Command
 {
+       Events::ModuleEventProvider evprov;
  public:
        /** Constructor for reloadmodule.
         */
-       CommandReloadmodule ( Module* parent) : Command( parent, "RELOADMODULE",1) { flags_needed = 'o'; syntax = "<modulename>"; }
+       CommandReloadmodule(Module* parent)
+               : Command(parent, "RELOADMODULE", 1)
+               , evprov(parent, "event/reloadmodule")
+       {
+               reloadevprov = &evprov;
+               flags_needed = 'o';
+               syntax = "<modulename>";
+       }
+
        /** Handle command.
         * @param parameters The parameters to the command
         * @param user The user issuing the command
@@ -34,21 +48,561 @@ class CommandReloadmodule : public Command
        CmdResult Handle(const std::vector<std::string>& parameters, User *user);
 };
 
-class ReloadModuleWorker : public HandlerBase1<void, bool>
+namespace ReloadModule
+{
+
+class DataKeeper
+{
+       /** Data we save for each mode and extension provided by the module
+        */
+       struct ProviderInfo
+       {
+               std::string itemname;
+               union
+               {
+                       ModeHandler* mh;
+                       ExtensionItem* extitem;
+               };
+
+               ProviderInfo(ModeHandler* mode)
+                       : itemname(mode->name)
+                       , mh(mode)
+               {
+               }
+
+               ProviderInfo(ExtensionItem* ei)
+                       : itemname(ei->name)
+                       , extitem(ei)
+               {
+               }
+       };
+
+       struct InstanceData
+       {
+               /** Position of the ModeHandler or ExtensionItem that the serialized data belongs to
+                */
+               size_t index;
+
+               /** Serialized data
+                */
+               std::string serialized;
+
+               InstanceData(size_t Index, const std::string& Serialized)
+                       : index(Index)
+                       , serialized(Serialized)
+               {
+               }
+       };
+
+       struct ModesExts
+       {
+               /** Mode data for the object, one entry per mode set by the module being reloaded
+                */
+               std::vector<InstanceData> modelist;
+
+               /** Extensions for the object, one entry per extension set by the module being reloaded
+                */
+               std::vector<InstanceData> extlist;
+
+               bool empty() const { return ((modelist.empty()) && (extlist.empty())); }
+
+               void swap(ModesExts& other)
+               {
+                       modelist.swap(other.modelist);
+                       extlist.swap(other.extlist);
+               }
+       };
+
+       struct OwnedModesExts : public ModesExts
+       {
+               /** User uuid or channel name
+                */
+               std::string owner;
+
+               OwnedModesExts(const std::string& Owner)
+                       : owner(Owner)
+               {
+               }
+       };
+
+       // Data saved for each channel
+       struct ChanData : public OwnedModesExts
+       {
+               /** Type of data stored for each member who has any affected modes or extensions set
+                */
+               typedef OwnedModesExts MemberData;
+
+               /** List of data (modes and extensions) about each member
+                */
+               std::vector<MemberData> memberdatalist;
+
+               ChanData(Channel* chan)
+                       : OwnedModesExts(chan->name)
+               {
+               }
+       };
+
+       // Data saved for each user
+       typedef OwnedModesExts UserData;
+
+       /** Module being reloaded
+        */
+       Module* mod;
+
+       /** Stores all user and channel modes provided by the module
+        */
+       std::vector<ProviderInfo> handledmodes[2];
+
+       /** Stores all extensions provided by the module
+        */
+       std::vector<ProviderInfo> handledexts;
+
+       /** Stores all of the module data related to users
+        */
+       std::vector<UserData> userdatalist;
+
+       /** Stores all of the module data related to channels and memberships
+        */
+       std::vector<ChanData> chandatalist;
+
+       /** Data attached by modules
+        */
+       ReloadModule::CustomData moddata;
+
+       void SaveExtensions(Extensible* extensible, std::vector<InstanceData>& extdatalist);
+       void SaveMemberData(Channel* chan, std::vector<ChanData::MemberData>& memberdatalist);
+       static void SaveListModes(Channel* chan, ListModeBase* lm, size_t index, ModesExts& currdata);
+
+       void CreateModeList(ModeType modetype);
+       void DoSaveUsers();
+       void DoSaveChans();
+
+       /** Link previously saved extension names to currently available ExtensionItems
+        */
+       void LinkExtensions();
+
+       /** Link previously saved mode names to currently available ModeHandlers
+        * @param modetype Type of the modes to look for
+        */
+       void LinkModes(ModeType modetype);
+
+       void DoRestoreUsers();
+       void DoRestoreChans();
+       void DoRestoreModules();
+
+       /** Restore previously saved modes and extensions on an Extensible.
+        * The extensions are set directly on the extensible, the modes are added into the provided mode change list.
+        * @param data Data to unserialize from
+        * @param extensible Object to restore
+        * @param modetype MODETYPE_USER if the object being restored is a User, MODETYPE_CHANNEL otherwise
+        * (for Channels and Memberships).
+        * @param modechange Mode change to populate with the modes
+        */
+       void RestoreObj(const OwnedModesExts& data, Extensible* extensible, ModeType modetype, Modes::ChangeList& modechange);
+
+       /** Restore all previously saved extensions on an Extensible
+        * @param list List of extensions and their serialized data to restore
+        * @param extensible Target Extensible
+        */
+       void RestoreExtensions(const std::vector<InstanceData>& list, Extensible* extensible);
+
+       /** Restore all previously saved modes on a User, Channel or Membership
+        * @param list List of modes to restore
+        * @param modetype MODETYPE_USER if the object being restored is a User, MODETYPE_CHANNEL otherwise
+        * @param modechange Mode change to populate with the modes
+        */
+       void RestoreModes(const std::vector<InstanceData>& list, ModeType modetype, Modes::ChangeList& modechange);
+
+       /** Restore all modes and extensions of all members on a channel
+        * @param chan Channel whose members are being restored
+        * @param memberdata Data to restore
+        * @param modechange Mode change to populate with prefix modes
+        */
+       void RestoreMemberData(Channel* chan, const std::vector<ChanData::MemberData>& memberdatalist, Modes::ChangeList& modechange);
+
+       /** Verify that a service which had its data saved is available and owned by the module that owned it previously
+        * @param service Service descriptor
+        * @param type Human-readable type of the service for log messages
+        */
+       void VerifyServiceProvider(const ProviderInfo& service, const char* type);
+
+ public:
+       /** Save module state
+        * @param currmod Module whose data to save
+        */
+       void Save(Module* currmod);
+
+       /** Restore module state
+        * @param newmod Newly loaded instance of the module which had its data saved
+        */
+       void Restore(Module* newmod);
+
+       /** Handle reload failure
+        */
+       void Fail();
+};
+
+void DataKeeper::DoSaveUsers()
+{
+       ModesExts currdata;
+
+       const user_hash& users = ServerInstance->Users->GetUsers();
+       for (user_hash::const_iterator i = users.begin(); i != users.end(); ++i)
+       {
+               User* const user = i->second;
+
+               // Serialize user modes
+               for (size_t j = 0; j < handledmodes[MODETYPE_USER].size(); j++)
+               {
+                       ModeHandler* mh = handledmodes[MODETYPE_USER][j].mh;
+                       if (user->IsModeSet(mh))
+                               currdata.modelist.push_back(InstanceData(j, mh->GetUserParameter(user)));
+               }
+
+               // Serialize all extensions attached to the User
+               SaveExtensions(user, currdata.extlist);
+
+               // Add to list if the user has any modes or extensions set that we are interested in, otherwise we don't
+               // have to do anything with this user when restoring
+               if (!currdata.empty())
+               {
+                       userdatalist.push_back(UserData(user->uuid));
+                       userdatalist.back().swap(currdata);
+               }
+       }
+}
+
+void DataKeeper::SaveExtensions(Extensible* extensible, std::vector<InstanceData>& extdata)
+{
+       const Extensible::ExtensibleStore& setexts = extensible->GetExtList();
+
+       // Position of the extension saved in the handledexts list
+       size_t index = 0;
+       for (std::vector<ProviderInfo>::const_iterator i = handledexts.begin(); i != handledexts.end(); ++i, index++)
+       {
+               ExtensionItem* const item = i->extitem;
+               Extensible::ExtensibleStore::const_iterator it = setexts.find(item);
+               if (it == setexts.end())
+                       continue;
+
+               std::string value = item->serialize(FORMAT_INTERNAL, extensible, it->second);
+               // If the serialized value is empty the extension won't be saved and restored
+               if (!value.empty())
+                       extdata.push_back(InstanceData(index, value));
+       }
+}
+
+void DataKeeper::SaveListModes(Channel* chan, ListModeBase* lm, size_t index, ModesExts& currdata)
+{
+       const ListModeBase::ModeList* list = lm->GetList(chan);
+       if (!list)
+               return;
+
+       for (ListModeBase::ModeList::const_iterator i = list->begin(); i != list->end(); ++i)
+       {
+               const ListModeBase::ListItem& listitem = *i;
+               currdata.modelist.push_back(InstanceData(index, listitem.mask));
+       }
+}
+
+void DataKeeper::DoSaveChans()
+{
+       ModesExts currdata;
+       std::vector<OwnedModesExts> currmemberdata;
+
+       const chan_hash& chans = ServerInstance->GetChans();
+       for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
+       {
+               Channel* const chan = i->second;
+
+               // Serialize channel modes
+               for (size_t j = 0; j < handledmodes[MODETYPE_CHANNEL].size(); j++)
+               {
+                       ModeHandler* mh = handledmodes[MODETYPE_CHANNEL][j].mh;
+                       ListModeBase* lm = mh->IsListModeBase();
+                       if (lm)
+                               SaveListModes(chan, lm, j, currdata);
+                       else if (chan->IsModeSet(mh))
+                               currdata.modelist.push_back(InstanceData(j, chan->GetModeParameter(mh)));
+               }
+
+               // Serialize all extensions attached to the Channel
+               SaveExtensions(chan, currdata.extlist);
+
+               // Serialize all extensions attached to and all modes set on all members of the channel
+               SaveMemberData(chan, currmemberdata);
+
+               // Same logic as in DoSaveUsers() plus we consider the modes and extensions of all members
+               if ((!currdata.empty()) || (!currmemberdata.empty()))
+               {
+                       chandatalist.push_back(ChanData(chan));
+                       chandatalist.back().swap(currdata);
+                       chandatalist.back().memberdatalist.swap(currmemberdata);
+               }
+       }
+}
+
+void DataKeeper::SaveMemberData(Channel* chan, std::vector<OwnedModesExts>& memberdatalist)
 {
+       ModesExts currdata;
+       const Channel::MemberMap& users = chan->GetUsers();
+       for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
+       {
+               Membership* const memb = i->second;
+
+               for (size_t j = 0; j < handledmodes[MODETYPE_CHANNEL].size(); j++)
+               {
+                       ModeHandler* mh = handledmodes[MODETYPE_CHANNEL][j].mh;
+                       if ((mh->IsPrefixMode()) && (memb->hasMode(mh->GetModeChar())))
+                               currdata.modelist.push_back(InstanceData(j, memb->user->uuid)); // Need to pass the user's uuid to the mode parser to set the mode later
+               }
+
+               SaveExtensions(memb, currdata.extlist);
+
+               // Same logic as in DoSaveUsers()
+               if (!currdata.empty())
+               {
+                       memberdatalist.push_back(OwnedModesExts(memb->user->uuid));
+                       memberdatalist.back().swap(currdata);
+               }
+       }
+}
+
+void DataKeeper::RestoreMemberData(Channel* chan, const std::vector<ChanData::MemberData>& memberdatalist, Modes::ChangeList& modechange)
+{
+       for (std::vector<ChanData::MemberData>::const_iterator i = memberdatalist.begin(); i != memberdatalist.end(); ++i)
+       {
+               const ChanData::MemberData& md = *i;
+               User* const user = ServerInstance->FindUUID(md.owner);
+               if (!user)
+               {
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User %s is gone (while processing %s)", md.owner.c_str(), chan->name.c_str());
+                       continue;
+               }
+
+               Membership* const memb = chan->GetUser(user);
+               if (!memb)
+               {
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Member %s is no longer on channel %s", md.owner.c_str(), chan->name.c_str());
+                       continue;
+               }
+
+               RestoreObj(md, memb, MODETYPE_CHANNEL, modechange);
+       }
+}
+
+void DataKeeper::CreateModeList(ModeType modetype)
+{
+       const ModeParser::ModeHandlerMap& modes = ServerInstance->Modes->GetModes(modetype);
+       for (ModeParser::ModeHandlerMap::const_iterator i = modes.begin(); i != modes.end(); ++i)
+       {
+               ModeHandler* mh = i->second;
+               if (mh->creator == mod)
+                       handledmodes[modetype].push_back(ProviderInfo(mh));
+       }
+}
+
+void DataKeeper::Save(Module* currmod)
+{
+       this->mod = currmod;
+
+       const ExtensionManager::ExtMap& allexts = ServerInstance->Extensions.GetExts();
+       for (ExtensionManager::ExtMap::const_iterator i = allexts.begin(); i != allexts.end(); ++i)
+       {
+               ExtensionItem* ext = i->second;
+               if (ext->creator == mod)
+                       handledexts.push_back(ProviderInfo(ext));
+       }
+
+       CreateModeList(MODETYPE_USER);
+       DoSaveUsers();
+
+       CreateModeList(MODETYPE_CHANNEL);
+       DoSaveChans();
+
+       FOREACH_MOD_CUSTOM(*reloadevprov, ReloadModule::EventListener, OnReloadModuleSave, (mod, this->moddata));
+
+       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Saved data about %lu users %lu chans %lu modules", (unsigned long)userdatalist.size(), (unsigned long)chandatalist.size(), (unsigned long)moddata.list.size());
+}
+
+void DataKeeper::VerifyServiceProvider(const ProviderInfo& service, const char* type)
+{
+       const ServiceProvider* sp = service.extitem;
+       if (!sp)
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "%s \"%s\" is no longer available", type, service.itemname.c_str());
+       else if (sp->creator != mod)
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "%s \"%s\" is now handled by %s", type, service.itemname.c_str(), (sp->creator ? sp->creator->ModuleSourceFile.c_str() : "<core>"));
+}
+
+void DataKeeper::LinkModes(ModeType modetype)
+{
+       std::vector<ProviderInfo>& list = handledmodes[modetype];
+       for (std::vector<ProviderInfo>::iterator i = list.begin(); i != list.end(); ++i)
+       {
+               ProviderInfo& item = *i;
+               item.mh = ServerInstance->Modes->FindMode(item.itemname, modetype);
+               VerifyServiceProvider(item, (modetype == MODETYPE_USER ? "User mode" : "Channel mode"));
+       }
+}
+
+void DataKeeper::LinkExtensions()
+{
+       for (std::vector<ProviderInfo>::iterator i = handledexts.begin(); i != handledexts.end(); ++i)
+       {
+               ProviderInfo& item = *i;
+               item.extitem = ServerInstance->Extensions.GetItem(item.itemname);
+               VerifyServiceProvider(item.extitem, "Extension");
+       }
+}
+
+void DataKeeper::Restore(Module* newmod)
+{
+       this->mod = newmod;
+
+       // Find the new extension items
+       LinkExtensions();
+       LinkModes(MODETYPE_USER);
+       LinkModes(MODETYPE_CHANNEL);
+
+       // Restore
+       DoRestoreUsers();
+       DoRestoreChans();
+       DoRestoreModules();
+
+       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restore finished");
+}
+
+void DataKeeper::Fail()
+{
+       this->mod = NULL;
+
+       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restore failed, notifying modules");
+       DoRestoreModules();
+}
+
+void DataKeeper::RestoreObj(const OwnedModesExts& data, Extensible* extensible, ModeType modetype, Modes::ChangeList& modechange)
+{
+       RestoreExtensions(data.extlist, extensible);
+       RestoreModes(data.modelist, modetype, modechange);
+}
+
+void DataKeeper::RestoreExtensions(const std::vector<InstanceData>& list, Extensible* extensible)
+{
+       for (std::vector<InstanceData>::const_iterator i = list.begin(); i != list.end(); ++i)
+       {
+               const InstanceData& id = *i;
+               handledexts[id.index].extitem->unserialize(FORMAT_INTERNAL, extensible, id.serialized);
+       }
+}
+
+void DataKeeper::RestoreModes(const std::vector<InstanceData>& list, ModeType modetype, Modes::ChangeList& modechange)
+{
+       for (std::vector<InstanceData>::const_iterator i = list.begin(); i != list.end(); ++i)
+       {
+               const InstanceData& id = *i;
+               modechange.push_add(handledmodes[modetype][id.index].mh, id.serialized);
+       }
+}
+
+void DataKeeper::DoRestoreUsers()
+{
+       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restoring user data");
+       Modes::ChangeList modechange;
+
+       for (std::vector<UserData>::const_iterator i = userdatalist.begin(); i != userdatalist.end(); ++i)
+       {
+               const UserData& userdata = *i;
+               User* const user = ServerInstance->FindUUID(userdata.owner);
+               if (!user)
+               {
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User %s is gone", userdata.owner.c_str());
+                       continue;
+               }
+
+               RestoreObj(userdata, user, MODETYPE_USER, modechange);
+               ServerInstance->Modes.Process(ServerInstance->FakeClient, NULL, user, modechange, ModeParser::MODE_LOCALONLY);
+               modechange.clear();
+       }
+}
+
+void DataKeeper::DoRestoreChans()
+{
+       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restoring channel data");
+       Modes::ChangeList modechange;
+
+       for (std::vector<ChanData>::const_iterator i = chandatalist.begin(); i != chandatalist.end(); ++i)
+       {
+               const ChanData& chandata = *i;
+               Channel* const chan = ServerInstance->FindChan(chandata.owner);
+               if (!chan)
+               {
+                       ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Channel %s not found", chandata.owner.c_str());
+                       continue;
+               }
+
+               RestoreObj(chandata, chan, MODETYPE_CHANNEL, modechange);
+               // Process the mode change before applying any prefix modes
+               ServerInstance->Modes.Process(ServerInstance->FakeClient, chan, NULL, modechange, ModeParser::MODE_LOCALONLY);
+               modechange.clear();
+
+               // Restore all member data
+               RestoreMemberData(chan, chandata.memberdatalist, modechange);
+               ServerInstance->Modes.Process(ServerInstance->FakeClient, chan, NULL, modechange, ModeParser::MODE_LOCALONLY);
+               modechange.clear();
+       }
+}
+
+void DataKeeper::DoRestoreModules()
+{
+       for (ReloadModule::CustomData::List::iterator i = moddata.list.begin(); i != moddata.list.end(); ++i)
+       {
+               ReloadModule::CustomData::Data& data = *i;
+               ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Calling module data handler %p", (void*)data.handler);
+               data.handler->OnReloadModuleRestore(mod, data.data);
+       }
+}
+
+} // namespace ReloadModule
+
+class ReloadAction : public HandlerBase0<void>
+{
+       Module* const mod;
+       const std::string uuid;
+       const std::string passedname;
+
  public:
-       const std::string name;
-       const std::string uid;
-       ReloadModuleWorker(const std::string& uuid, const std::string& modn)
-               : name(modn), uid(uuid) {}
-       void Call(bool result)
-       {
-               ServerInstance->SNO->WriteGlobalSno('a', "RELOAD MODULE: %s %ssuccessfully reloaded",
-                       name.c_str(), result ? "" : "un");
-               User* user = ServerInstance->FindNick(uid);
+       ReloadAction(Module* m, const std::string& uid, const std::string& passedmodname)
+               : mod(m)
+               , uuid(uid)
+               , passedname(passedmodname)
+       {
+       }
+
+       void Call()
+       {
+               ReloadModule::DataKeeper datakeeper;
+               datakeeper.Save(mod);
+
+               DLLManager* dll = mod->ModuleDLLManager;
+               std::string name = mod->ModuleSourceFile;
+               ServerInstance->Modules->DoSafeUnload(mod);
+               ServerInstance->GlobalCulls.Apply();
+               delete dll;
+               bool result = ServerInstance->Modules->Load(name);
+
+               if (result)
+               {
+                       Module* newmod = ServerInstance->Modules->Find(name);
+                       datakeeper.Restore(newmod);
+               }
+               else
+                       datakeeper.Fail();
+
+               ServerInstance->SNO->WriteGlobalSno('a', "RELOAD MODULE: %s %ssuccessfully reloaded", passedname.c_str(), result ? "" : "un");
+               User* user = ServerInstance->FindUUID(uuid);
                if (user)
-                       user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module %ssuccessfully reloaded.",
-                               name.c_str(), result ? "" : "un");
+                       user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module %ssuccessfully reloaded.", passedname.c_str(), result ? "" : "un");
+
                ServerInstance->GlobalCulls.AddItem(this);
        }
 };
@@ -63,9 +617,12 @@ CmdResult CommandReloadmodule::Handle (const std::vector<std::string>& parameter
                return CMD_FAILURE;
        }
 
-       if (m)
+       if (creator->dying)
+               return CMD_FAILURE;
+
+       if ((m) && (ServerInstance->Modules.CanUnload(m)))
        {
-               ServerInstance->Modules->Reload(m, (creator->dying ? NULL : new ReloadModuleWorker(user->uuid, parameters[0])));
+               ServerInstance->AtomicActions.AddAction(new ReloadAction(m, user->uuid, parameters[0]));
                return CMD_SUCCESS;
        }
        else
index d73fdf491bae833100a7ab53315a3fffe3cfbcb3..79ce94cc9f8f8b3603816c2ec43d9976b698d1dc 100644 (file)
@@ -47,23 +47,18 @@ CmdResult CommandWhowas::Handle (const std::vector<std::string>& parameters, Use
        else
        {
                const WhoWas::Nick::List& list = nick->entries;
-               if (!list.empty())
+               for (WhoWas::Nick::List::const_iterator i = list.begin(); i != list.end(); ++i)
                {
-                       for (WhoWas::Nick::List::const_iterator i = list.begin(); i != list.end(); ++i)
-                       {
-                               WhoWas::Entry* u = *i;
+                       WhoWas::Entry* u = *i;
 
-                               user->WriteNumeric(RPL_WHOWASUSER, "%s %s %s * :%s", parameters[0].c_str(),
-                                       u->ident.c_str(),u->dhost.c_str(),u->gecos.c_str());
+                       user->WriteNumeric(RPL_WHOWASUSER, "%s %s %s * :%s", parameters[0].c_str(), u->ident.c_str(),u->dhost.c_str(),u->gecos.c_str());
 
-                               if (user->HasPrivPermission("users/auspex"))
-                                       user->WriteNumeric(RPL_WHOWASIP, "%s :was connecting from *@%s",
-                                               parameters[0].c_str(), u->host.c_str());
+                       if (user->HasPrivPermission("users/auspex"))
+                               user->WriteNumeric(RPL_WHOWASIP, "%s :was connecting from *@%s", parameters[0].c_str(), u->host.c_str());
 
-                               std::string signon = InspIRCd::TimeString(u->signon);
-                               bool hide_server = (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"));
-                               user->WriteNumeric(RPL_WHOISSERVER, "%s %s :%s", parameters[0].c_str(), (hide_server ? ServerInstance->Config->HideWhoisServer.c_str() : u->server.c_str()), signon.c_str());
-                       }
+                       std::string signon = InspIRCd::TimeString(u->signon);
+                       bool hide_server = (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"));
+                       user->WriteNumeric(RPL_WHOISSERVER, "%s %s :%s", parameters[0].c_str(), (hide_server ? ServerInstance->Config->HideWhoisServer.c_str() : u->server.c_str()), signon.c_str());
                }
        }
 
@@ -81,11 +76,7 @@ const WhoWas::Nick* WhoWas::Manager::FindNick(const std::string& nickname) const
        whowas_users::const_iterator it = whowas.find(nickname);
        if (it == whowas.end())
                return NULL;
-
-       const Nick* nick = it->second;
-       if (nick->entries.empty())
-               return NULL;
-       return nick;
+       return it->second;
 }
 
 WhoWas::Manager::Stats WhoWas::Manager::GetStats() const
@@ -124,10 +115,7 @@ void WhoWas::Manager::Add(User* user)
                if (whowas.size() > this->MaxGroups)
                {
                        // Too many nicks, remove the nick which was inserted the longest time ago from both the map and the fifo
-                       nick = whowas_fifo.front();
-                       whowas_fifo.pop_front();
-                       whowas.erase(nick->nick);
-                       delete nick;
+                       PurgeNick(whowas_fifo.front());
                }
        }
        else
@@ -155,24 +143,13 @@ void WhoWas::Manager::Prune()
        {
                WhoWas::Nick* nick = whowas_fifo.front();
                if ((whowas_fifo.size() > this->MaxGroups) || (nick->addtime < min))
-               {
-                       /* hopefully redundant integrity check, but added while debugging r6216 */
-                       if (!whowas.erase(nick->nick))
-                       {
-                               /* this should never happen, if it does maps are corrupt */
-                               ServerInstance->Logs->Log("WHOWAS", LOG_DEFAULT, "BUG: Whowas maps got corrupted! (1)");
-                               return;
-                       }
-
-                       whowas_fifo.pop_front();
-                       delete nick;
-               }
+                       PurgeNick(nick);
                else
                        break;
        }
 
        /* Then cut the whowas sets to new size (groupsize) */
-       for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
+       for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); )
        {
                WhoWas::Nick::List& list = i->second->entries;
                while (list.size() > this->GroupSize)
@@ -180,6 +157,11 @@ void WhoWas::Manager::Prune()
                        delete list.front();
                        list.pop_front();
                }
+
+               if (list.empty())
+                       PurgeNick(i++);
+               else
+                       ++i;
        }
 }
 
@@ -187,7 +169,7 @@ void WhoWas::Manager::Prune()
 void WhoWas::Manager::Maintain()
 {
        time_t min = ServerInstance->Time() - this->MaxKeep;
-       for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
+       for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); )
        {
                WhoWas::Nick::List& list = i->second->entries;
                while (!list.empty() && list.front()->signon < min)
@@ -195,6 +177,11 @@ void WhoWas::Manager::Maintain()
                        delete list.front();
                        list.pop_front();
                }
+
+               if (list.empty())
+                       PurgeNick(i++);
+               else
+                       ++i;
        }
 }
 
@@ -223,6 +210,25 @@ void WhoWas::Manager::UpdateConfig(unsigned int NewGroupSize, unsigned int NewMa
        Prune();
 }
 
+void WhoWas::Manager::PurgeNick(whowas_users::iterator it)
+{
+       WhoWas::Nick* nick = it->second;
+       whowas_fifo.erase(nick);
+       whowas.erase(it);
+       delete nick;
+}
+
+void WhoWas::Manager::PurgeNick(WhoWas::Nick* nick)
+{
+       whowas_users::iterator it = whowas.find(nick->nick);
+       if (it == whowas.end())
+       {
+               ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in whowas database, please report");
+               return;
+       }
+       PurgeNick(it);
+}
+
 WhoWas::Entry::Entry(User* user)
        : host(user->host)
        , dhost(user->dhost)
index ca13dad738d7352c13025c3f68a6676482f8a29e..d77221c39f847b975f7c0af38c1c47ee635d3782 100644 (file)
@@ -451,26 +451,6 @@ namespace
                        ServerInstance->GlobalCulls.AddItem(this);
                }
        };
-
-       struct ReloadAction : public HandlerBase0<void>
-       {
-               Module* const mod;
-               HandlerBase1<void, bool>* const callback;
-               ReloadAction(Module* m, HandlerBase1<void, bool>* c)
-                       : mod(m), callback(c) {}
-               void Call()
-               {
-                       DLLManager* dll = mod->ModuleDLLManager;
-                       std::string name = mod->ModuleSourceFile;
-                       ServerInstance->Modules->DoSafeUnload(mod);
-                       ServerInstance->GlobalCulls.Apply();
-                       delete dll;
-                       bool rv = ServerInstance->Modules->Load(name);
-                       if (callback)
-                               callback->Call(rv);
-                       ServerInstance->GlobalCulls.AddItem(this);
-               }
-       };
 }
 
 bool ModuleManager::Unload(Module* mod)
@@ -481,14 +461,6 @@ bool ModuleManager::Unload(Module* mod)
        return true;
 }
 
-void ModuleManager::Reload(Module* mod, HandlerBase1<void, bool>* callback)
-{
-       if (CanUnload(mod))
-               ServerInstance->AtomicActions.AddAction(new ReloadAction(mod, callback));
-       else if (callback)
-               callback->Call(false);
-}
-
 void ModuleManager::LoadAll()
 {
        std::map<std::string, ServiceList> servicemap;
index a3f3242f060745ad7e6e3180fee84384ee0956d3..294187fa5c81bea51b6b8d775c0c72fa374861ac 100644 (file)
@@ -116,25 +116,6 @@ class CommandGunloadmodule : public Command
        }
 };
 
-class GReloadModuleWorker : public HandlerBase1<void, bool>
-{
- public:
-       const std::string nick;
-       const std::string name;
-       const std::string uid;
-       GReloadModuleWorker(const std::string& usernick, const std::string& uuid, const std::string& modn)
-               : nick(usernick), name(modn), uid(uuid) {}
-       void Call(bool result)
-       {
-               ServerInstance->SNO->WriteToSnoMask('a', "MODULE '%s' GLOBALLY RELOADED BY '%s'%s", name.c_str(), nick.c_str(), result ? "" : " (failed here)");
-               User* user = ServerInstance->FindNick(uid);
-               if (user)
-                       user->WriteNumeric(RPL_LOADEDMODULE, "%s :Module %ssuccessfully reloaded.",
-                               name.c_str(), result ? "" : "un");
-               ServerInstance->GlobalCulls.AddItem(this);
-       }
-};
-
 /** Handle /GRELOADMODULE
  */
 class CommandGreloadmodule : public Command
@@ -154,10 +135,8 @@ class CommandGreloadmodule : public Command
                        Module* m = ServerInstance->Modules->Find(parameters[0]);
                        if (m)
                        {
-                               GReloadModuleWorker* worker = NULL;
-                               if ((m != creator) && (!creator->dying))
-                                       worker = new GReloadModuleWorker(user->nick, user->uuid, parameters[0]);
-                               ServerInstance->Modules->Reload(m, worker);
+                               ServerInstance->SNO->WriteToSnoMask('a', "MODULE '%s' GLOBALLY RELOADED BY '%s'", parameters[0].c_str(), user->nick.c_str());
+                               ServerInstance->Parser.CallHandler("RELOADMODULE", parameters, user);
                        }
                        else
                        {
index a11b05ca32852bcb0ae0c3bc6b7059310882b020..48ab511f08c8a557cccbd1af4892ea425c488e26 100644 (file)
@@ -118,6 +118,11 @@ class AccountExtItemImpl : public AccountExtItem
                User* user = static_cast<User*>(container);
 
                StringExtItem::unserialize(format, container, value);
+
+               // If we are being reloaded then don't send the numeric or run the event
+               if (format == FORMAT_INTERNAL)
+                       return;
+
                if (!value.empty())
                {
                        // Logged in