]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/coremods/core_reloadmodule.cpp
Fix a copy/paste error in the http path normalising code.
[user/henk/code/inspircd.git] / src / coremods / core_reloadmodule.cpp
index 7f0f15e7735169817acbf2823e73cf98f526a146..98bf2ab405b17f55665c2c0fc11230b65c530ae8 100644 (file)
@@ -1,8 +1,12 @@
 /*
  * InspIRCd -- Internet Relay Chat Daemon
  *
+ *   Copyright (C) 2017-2019 Sadie Powell <sadie@witchery.services>
+ *   Copyright (C) 2014-2016, 2018 Attila Molnar <attilamolnar@hush.com>
+ *   Copyright (C) 2012 Robby <robby@chatbelgie.be>
  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *   Copyright (C) 2007, 2010 Craig Edwards <brain@inspircd.org>
+ *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
  *
  * 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
 
 
 #include "inspircd.h"
+#include "listmode.h"
+#include "modules/reload.h"
+
+static Events::ModuleEventProvider* reloadevprov;
+static ClientProtocol::Serializer* dummyserializer;
+
+class DummySerializer : public ClientProtocol::Serializer
+{
+       bool Parse(LocalUser* user, const std::string& line, ClientProtocol::ParseOutput& parseoutput) CXX11_OVERRIDE
+       {
+               return false;
+       }
+
+       ClientProtocol::SerializedMessage Serialize(const ClientProtocol::Message& msg, const ClientProtocol::TagSelection& tagwl) const CXX11_OVERRIDE
+       {
+               return ClientProtocol::SerializedMessage();
+       }
+
+ public:
+       DummySerializer(Module* mod)
+               : ClientProtocol::Serializer(mod, "dummy")
+       {
+       }
+};
 
 class CommandReloadmodule : public Command
 {
+       Events::ModuleEventProvider evprov;
+       DummySerializer dummyser;
+
  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")
+               , dummyser(parent)
+       {
+               reloadevprov = &evprov;
+               dummyserializer = &dummyser;
+               flags_needed = 'o';
+               syntax = "<modulename>";
+       }
+
        /** Handle command.
         * @param parameters The parameters to the command
         * @param user The user issuing the command
         * @return A value from CmdResult to indicate command success or failure.
         */
-       CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
 };
 
-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;
+                       ClientProtocol::Serializer* serializer;
+               };
+
+               ProviderInfo(ModeHandler* mode)
+                       : itemname(mode->name)
+                       , mh(mode)
+               {
+               }
+
+               ProviderInfo(ExtensionItem* ei)
+                       : itemname(ei->name)
+                       , extitem(ei)
+               {
+               }
+
+               ProviderInfo(ClientProtocol::Serializer* ser)
+                       : itemname(ser->name)
+                       , serializer(ser)
+               {
+               }
+       };
+
+       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
+       struct UserData : public OwnedModesExts
+       {
+               static const size_t UNUSED_INDEX = (size_t)-1;
+               size_t serializerindex;
+
+               UserData(User* user, size_t serializeridx)
+                       : OwnedModesExts(user->uuid)
+                       , serializerindex(serializeridx)
+               {
+               }
+       };
+
+       /** 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 serializers provided by the module
+        */
+       std::vector<ProviderInfo> handledserializers;
+
+       /** 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);
+       size_t SaveSerializer(User* user);
+
+       /** Get the index of a ProviderInfo representing the serializer in the handledserializers list.
+        * If the serializer is not already in the list it is added.
+        * @param serializer Serializer to get an index to.
+        * @return Index of the ProviderInfo representing the serializer.
+        */
+       size_t GetSerializerIndex(ClientProtocol::Serializer* serializer);
+
+       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);
+
+       /** Link previously saved serializer names to currently available Serializers
+        */
+       void LinkSerializers();
+
+       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 previously saved serializer on a User.
+        * Quit the user if the serializer cannot be restored.
+        * @param serializerindex Saved serializer index to restore.
+        * @param user User whose serializer to restore. If not local then calling this method is a no-op.
+        * @return True if the serializer didn't need restoring or was restored successfully.
+        * False if the serializer should have been restored but the required serializer is unavailable and the user was quit.
+        */
+       bool RestoreSerializer(size_t serializerindex, User* user);
+
+       /** 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:
-       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);
+       /** 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);
+
+               // Save serializer name if applicable and get an index to it
+               size_t serializerindex = SaveSerializer(user);
+
+               // 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()) || (serializerindex != UserData::UNUSED_INDEX))
+               {
+                       userdatalist.push_back(UserData(user, serializerindex));
+                       userdatalist.back().swap(currdata);
+               }
+       }
+}
+
+size_t DataKeeper::GetSerializerIndex(ClientProtocol::Serializer* serializer)
+{
+       for (size_t i = 0; i < handledserializers.size(); i++)
+       {
+               if (handledserializers[i].serializer == serializer)
+                       return i;
+       }
+
+       handledserializers.push_back(ProviderInfo(serializer));
+       return handledserializers.size()-1;
+}
+
+size_t DataKeeper::SaveSerializer(User* user)
+{
+       LocalUser* const localuser = IS_LOCAL(user);
+       if ((!localuser) || (!localuser->serializer))
+               return UserData::UNUSED_INDEX;
+       if (localuser->serializer->creator != mod)
+               return UserData::UNUSED_INDEX;
+
+       const size_t serializerindex = GetSerializerIndex(localuser->serializer);
+       localuser->serializer = dummyserializer;
+       return serializerindex;
+}
+
+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->ToInternal(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;
+                       const PrefixMode* const pm = mh->IsPrefixMode();
+                       if ((pm) && (memb->HasMode(pm)))
+                               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::LinkSerializers()
+{
+       for (std::vector<ProviderInfo>::iterator i = handledserializers.begin(); i != handledserializers.end(); ++i)
+       {
+               ProviderInfo& item = *i;
+               item.serializer = ServerInstance->Modules.FindDataService<ClientProtocol::Serializer>(item.itemname);
+               VerifyServiceProvider(item.serializer, "Serializer");
+       }
+}
+
+void DataKeeper::Restore(Module* newmod)
+{
+       this->mod = newmod;
+
+       // Find the new extension items
+       LinkExtensions();
+       LinkModes(MODETYPE_USER);
+       LinkModes(MODETYPE_CHANNEL);
+       LinkSerializers();
+
+       // 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->FromInternal(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);
+       }
+}
+
+bool DataKeeper::RestoreSerializer(size_t serializerindex, User* user)
+{
+       if (serializerindex == UserData::UNUSED_INDEX)
+               return true;
+
+       // The following checks are redundant
+       LocalUser* const localuser = IS_LOCAL(user);
+       if (!localuser)
+               return true;
+       if (localuser->serializer != dummyserializer)
+               return true;
+
+       const ProviderInfo& provinfo = handledserializers[serializerindex];
+       if (!provinfo.serializer)
+       {
+               // Users cannot exist without a serializer
+               ServerInstance->Users.QuitUser(user, "Serializer lost in reload");
+               return false;
+       }
+
+       localuser->serializer = provinfo.serializer;
+       return true;
+}
+
+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;
+               }
+
+               // Attempt to restore serializer first, if it fails it's a fatal error and RestoreSerializer() quits them
+               if (!RestoreSerializer(userdata.serializerindex, user))
+                       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 ActionBase
+{
+       Module* const mod;
+       const std::string uuid;
+       const std::string passedname;
+
+ public:
+       ReloadAction(Module* m, const std::string& uid, const std::string& passedmodname)
+               : mod(m)
+               , uuid(uid)
+               , passedname(passedmodname)
+       {
+       }
+
+       void Call() CXX11_OVERRIDE
+       {
+               ReloadModule::DataKeeper datakeeper;
+               datakeeper.Save(mod);
+
+               std::string name = mod->ModuleSourceFile;
+               ServerInstance->Modules->DoSafeUnload(mod);
+               ServerInstance->GlobalCulls.Apply();
+               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, passedname, InspIRCd::Format("Module %ssuccessfully reloaded.", (result ? "" : "un")));
+
                ServerInstance->GlobalCulls.AddItem(this);
        }
 };
 
-CmdResult CommandReloadmodule::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandReloadmodule::Handle(User* user, const Params& parameters)
 {
        Module* m = ServerInstance->Modules->Find(parameters[0]);
        if (m == creator)
        {
-               user->WriteNumeric(RPL_LOADEDMODULE, "%s :You cannot reload core_reloadmodule.so (unload and load it)",
-                       parameters[0].c_str());
+               user->WriteNumeric(RPL_LOADEDMODULE, parameters[0], "You cannot reload core_reloadmodule (unload and load it)");
                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
        {
-               user->WriteNumeric(RPL_LOADEDMODULE, "%s :Could not find module by that name", parameters[0].c_str());
+               user->WriteNumeric(RPL_LOADEDMODULE, parameters[0], "Could not find module by that name");
                return CMD_FAILURE;
        }
 }
 
-COMMAND_INIT(CommandReloadmodule)
+class CoreModReloadmodule : public Module
+{
+ private:
+       CommandReloadmodule cmd;
+
+ public:
+       CoreModReloadmodule()
+               : cmd(this)
+       {
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the RELOADMODULE command", VF_CORE | VF_VENDOR);
+       }
+};
+
+MODULE_INIT(CoreModReloadmodule)