]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/coremods/core_reloadmodule.cpp
Send ERR_CANTUNLOADMODULE when unloading a module on reload fails.
[user/henk/code/inspircd.git] / src / coremods / core_reloadmodule.cpp
index 9101442216465c477df8dbfe5911ff47729e1215..34eaeae186ee04f378dbda557967c52583157dfc 100644 (file)
@@ -1,9 +1,12 @@
 /*
  * InspIRCd -- Internet Relay Chat Daemon
  *
- *   Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *   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 "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)
                , evprov(parent, "event/reloadmodule")
+               , dummyser(parent)
        {
                reloadevprov = &evprov;
+               dummyserializer = &dummyser;
                flags_needed = 'o';
                syntax = "<modulename>";
        }
@@ -45,7 +72,7 @@ class CommandReloadmodule : public 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) CXX11_OVERRIDE;
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
 };
 
 namespace ReloadModule
@@ -62,6 +89,7 @@ class DataKeeper
                {
                        ModeHandler* mh;
                        ExtensionItem* extitem;
+                       ClientProtocol::Serializer* serializer;
                };
 
                ProviderInfo(ModeHandler* mode)
@@ -75,6 +103,12 @@ class DataKeeper
                        , extitem(ei)
                {
                }
+
+               ProviderInfo(ClientProtocol::Serializer* ser)
+                       : itemname(ser->name)
+                       , serializer(ser)
+               {
+               }
        };
 
        struct InstanceData
@@ -143,7 +177,17 @@ class DataKeeper
        };
 
        // Data saved for each user
-       typedef OwnedModesExts UserData;
+       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
         */
@@ -157,6 +201,10 @@ class DataKeeper
         */
        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;
@@ -172,6 +220,14 @@ class DataKeeper
        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();
@@ -186,6 +242,10 @@ class DataKeeper
         */
        void LinkModes(ModeType modetype);
 
+       /** Link previously saved serializer names to currently available Serializers
+        */
+       void LinkSerializers();
+
        void DoRestoreUsers();
        void DoRestoreChans();
        void DoRestoreModules();
@@ -213,6 +273,15 @@ class DataKeeper
         */
        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
@@ -262,16 +331,44 @@ void DataKeeper::DoSaveUsers()
                // 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())
+               if ((!currdata.empty()) || (serializerindex != UserData::UNUSED_INDEX))
                {
-                       userdatalist.push_back(UserData(user->uuid));
+                       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();
@@ -285,7 +382,7 @@ void DataKeeper::SaveExtensions(Extensible* extensible, std::vector<InstanceData
                if (it == setexts.end())
                        continue;
 
-               std::string value = item->serialize(FORMAT_INTERNAL, extensible, it->second);
+               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));
@@ -456,6 +553,16 @@ void DataKeeper::LinkExtensions()
        }
 }
 
+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;
@@ -464,6 +571,7 @@ void DataKeeper::Restore(Module* newmod)
        LinkExtensions();
        LinkModes(MODETYPE_USER);
        LinkModes(MODETYPE_CHANNEL);
+       LinkSerializers();
 
        // Restore
        DoRestoreUsers();
@@ -492,7 +600,7 @@ void DataKeeper::RestoreExtensions(const std::vector<InstanceData>& list, Extens
        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);
+               handledexts[id.index].extitem->FromInternal(extensible, id.serialized);
        }
 }
 
@@ -505,6 +613,30 @@ void DataKeeper::RestoreModes(const std::vector<InstanceData>& list, ModeType mo
        }
 }
 
+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");
@@ -520,6 +652,10 @@ void DataKeeper::DoRestoreUsers()
                        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();
@@ -584,11 +720,9 @@ class ReloadAction : public ActionBase
                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)
@@ -602,18 +736,21 @@ class ReloadAction : public ActionBase
                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, passedname, InspIRCd::Format("Module %ssuccessfully reloaded.", (result ? "" : "un")));
+               {
+                       int numeric = result ? RPL_LOADEDMODULE : ERR_CANTUNLOADMODULE;
+                       user->WriteNumeric(numeric, 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, parameters[0], "You cannot reload core_reloadmodule (unload and load it)");
+               user->WriteNumeric(ERR_CANTUNLOADMODULE, parameters[0], "You cannot reload core_reloadmodule (unload and load it)");
                return CMD_FAILURE;
        }
 
@@ -627,9 +764,26 @@ CmdResult CommandReloadmodule::Handle (const std::vector<std::string>& parameter
        }
        else
        {
-               user->WriteNumeric(RPL_LOADEDMODULE, parameters[0], "Could not find module by that name");
+               user->WriteNumeric(ERR_CANTUNLOADMODULE, 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)