]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/coremods/core_reloadmodule.cpp
Use CommandBase::Params instead of std::vector<std::string>.
[user/henk/code/inspircd.git] / src / coremods / core_reloadmodule.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
5  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
6  *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
7  *
8  * This file is part of InspIRCd.  InspIRCd is free software: you can
9  * redistribute it and/or modify it under the terms of the GNU General Public
10  * License as published by the Free Software Foundation, version 2.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21
22 #include "inspircd.h"
23 #include "listmode.h"
24 #include "modules/reload.h"
25
26 static Events::ModuleEventProvider* reloadevprov;
27
28 class CommandReloadmodule : public Command
29 {
30         Events::ModuleEventProvider evprov;
31  public:
32         /** Constructor for reloadmodule.
33          */
34         CommandReloadmodule(Module* parent)
35                 : Command(parent, "RELOADMODULE", 1)
36                 , evprov(parent, "event/reloadmodule")
37         {
38                 reloadevprov = &evprov;
39                 flags_needed = 'o';
40                 syntax = "<modulename>";
41         }
42
43         /** Handle command.
44          * @param parameters The parameters to the command
45          * @param user The user issuing the command
46          * @return A value from CmdResult to indicate command success or failure.
47          */
48         CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
49 };
50
51 namespace ReloadModule
52 {
53
54 class DataKeeper
55 {
56         /** Data we save for each mode and extension provided by the module
57          */
58         struct ProviderInfo
59         {
60                 std::string itemname;
61                 union
62                 {
63                         ModeHandler* mh;
64                         ExtensionItem* extitem;
65                 };
66
67                 ProviderInfo(ModeHandler* mode)
68                         : itemname(mode->name)
69                         , mh(mode)
70                 {
71                 }
72
73                 ProviderInfo(ExtensionItem* ei)
74                         : itemname(ei->name)
75                         , extitem(ei)
76                 {
77                 }
78         };
79
80         struct InstanceData
81         {
82                 /** Position of the ModeHandler or ExtensionItem that the serialized data belongs to
83                  */
84                 size_t index;
85
86                 /** Serialized data
87                  */
88                 std::string serialized;
89
90                 InstanceData(size_t Index, const std::string& Serialized)
91                         : index(Index)
92                         , serialized(Serialized)
93                 {
94                 }
95         };
96
97         struct ModesExts
98         {
99                 /** Mode data for the object, one entry per mode set by the module being reloaded
100                  */
101                 std::vector<InstanceData> modelist;
102
103                 /** Extensions for the object, one entry per extension set by the module being reloaded
104                  */
105                 std::vector<InstanceData> extlist;
106
107                 bool empty() const { return ((modelist.empty()) && (extlist.empty())); }
108
109                 void swap(ModesExts& other)
110                 {
111                         modelist.swap(other.modelist);
112                         extlist.swap(other.extlist);
113                 }
114         };
115
116         struct OwnedModesExts : public ModesExts
117         {
118                 /** User uuid or channel name
119                  */
120                 std::string owner;
121
122                 OwnedModesExts(const std::string& Owner)
123                         : owner(Owner)
124                 {
125                 }
126         };
127
128         // Data saved for each channel
129         struct ChanData : public OwnedModesExts
130         {
131                 /** Type of data stored for each member who has any affected modes or extensions set
132                  */
133                 typedef OwnedModesExts MemberData;
134
135                 /** List of data (modes and extensions) about each member
136                  */
137                 std::vector<MemberData> memberdatalist;
138
139                 ChanData(Channel* chan)
140                         : OwnedModesExts(chan->name)
141                 {
142                 }
143         };
144
145         // Data saved for each user
146         typedef OwnedModesExts UserData;
147
148         /** Module being reloaded
149          */
150         Module* mod;
151
152         /** Stores all user and channel modes provided by the module
153          */
154         std::vector<ProviderInfo> handledmodes[2];
155
156         /** Stores all extensions provided by the module
157          */
158         std::vector<ProviderInfo> handledexts;
159
160         /** Stores all of the module data related to users
161          */
162         std::vector<UserData> userdatalist;
163
164         /** Stores all of the module data related to channels and memberships
165          */
166         std::vector<ChanData> chandatalist;
167
168         /** Data attached by modules
169          */
170         ReloadModule::CustomData moddata;
171
172         void SaveExtensions(Extensible* extensible, std::vector<InstanceData>& extdatalist);
173         void SaveMemberData(Channel* chan, std::vector<ChanData::MemberData>& memberdatalist);
174         static void SaveListModes(Channel* chan, ListModeBase* lm, size_t index, ModesExts& currdata);
175
176         void CreateModeList(ModeType modetype);
177         void DoSaveUsers();
178         void DoSaveChans();
179
180         /** Link previously saved extension names to currently available ExtensionItems
181          */
182         void LinkExtensions();
183
184         /** Link previously saved mode names to currently available ModeHandlers
185          * @param modetype Type of the modes to look for
186          */
187         void LinkModes(ModeType modetype);
188
189         void DoRestoreUsers();
190         void DoRestoreChans();
191         void DoRestoreModules();
192
193         /** Restore previously saved modes and extensions on an Extensible.
194          * The extensions are set directly on the extensible, the modes are added into the provided mode change list.
195          * @param data Data to unserialize from
196          * @param extensible Object to restore
197          * @param modetype MODETYPE_USER if the object being restored is a User, MODETYPE_CHANNEL otherwise
198          * (for Channels and Memberships).
199          * @param modechange Mode change to populate with the modes
200          */
201         void RestoreObj(const OwnedModesExts& data, Extensible* extensible, ModeType modetype, Modes::ChangeList& modechange);
202
203         /** Restore all previously saved extensions on an Extensible
204          * @param list List of extensions and their serialized data to restore
205          * @param extensible Target Extensible
206          */
207         void RestoreExtensions(const std::vector<InstanceData>& list, Extensible* extensible);
208
209         /** Restore all previously saved modes on a User, Channel or Membership
210          * @param list List of modes to restore
211          * @param modetype MODETYPE_USER if the object being restored is a User, MODETYPE_CHANNEL otherwise
212          * @param modechange Mode change to populate with the modes
213          */
214         void RestoreModes(const std::vector<InstanceData>& list, ModeType modetype, Modes::ChangeList& modechange);
215
216         /** Restore all modes and extensions of all members on a channel
217          * @param chan Channel whose members are being restored
218          * @param memberdata Data to restore
219          * @param modechange Mode change to populate with prefix modes
220          */
221         void RestoreMemberData(Channel* chan, const std::vector<ChanData::MemberData>& memberdatalist, Modes::ChangeList& modechange);
222
223         /** Verify that a service which had its data saved is available and owned by the module that owned it previously
224          * @param service Service descriptor
225          * @param type Human-readable type of the service for log messages
226          */
227         void VerifyServiceProvider(const ProviderInfo& service, const char* type);
228
229  public:
230         /** Save module state
231          * @param currmod Module whose data to save
232          */
233         void Save(Module* currmod);
234
235         /** Restore module state
236          * @param newmod Newly loaded instance of the module which had its data saved
237          */
238         void Restore(Module* newmod);
239
240         /** Handle reload failure
241          */
242         void Fail();
243 };
244
245 void DataKeeper::DoSaveUsers()
246 {
247         ModesExts currdata;
248
249         const user_hash& users = ServerInstance->Users->GetUsers();
250         for (user_hash::const_iterator i = users.begin(); i != users.end(); ++i)
251         {
252                 User* const user = i->second;
253
254                 // Serialize user modes
255                 for (size_t j = 0; j < handledmodes[MODETYPE_USER].size(); j++)
256                 {
257                         ModeHandler* mh = handledmodes[MODETYPE_USER][j].mh;
258                         if (user->IsModeSet(mh))
259                                 currdata.modelist.push_back(InstanceData(j, mh->GetUserParameter(user)));
260                 }
261
262                 // Serialize all extensions attached to the User
263                 SaveExtensions(user, currdata.extlist);
264
265                 // Add to list if the user has any modes or extensions set that we are interested in, otherwise we don't
266                 // have to do anything with this user when restoring
267                 if (!currdata.empty())
268                 {
269                         userdatalist.push_back(UserData(user->uuid));
270                         userdatalist.back().swap(currdata);
271                 }
272         }
273 }
274
275 void DataKeeper::SaveExtensions(Extensible* extensible, std::vector<InstanceData>& extdata)
276 {
277         const Extensible::ExtensibleStore& setexts = extensible->GetExtList();
278
279         // Position of the extension saved in the handledexts list
280         size_t index = 0;
281         for (std::vector<ProviderInfo>::const_iterator i = handledexts.begin(); i != handledexts.end(); ++i, index++)
282         {
283                 ExtensionItem* const item = i->extitem;
284                 Extensible::ExtensibleStore::const_iterator it = setexts.find(item);
285                 if (it == setexts.end())
286                         continue;
287
288                 std::string value = item->serialize(FORMAT_INTERNAL, extensible, it->second);
289                 // If the serialized value is empty the extension won't be saved and restored
290                 if (!value.empty())
291                         extdata.push_back(InstanceData(index, value));
292         }
293 }
294
295 void DataKeeper::SaveListModes(Channel* chan, ListModeBase* lm, size_t index, ModesExts& currdata)
296 {
297         const ListModeBase::ModeList* list = lm->GetList(chan);
298         if (!list)
299                 return;
300
301         for (ListModeBase::ModeList::const_iterator i = list->begin(); i != list->end(); ++i)
302         {
303                 const ListModeBase::ListItem& listitem = *i;
304                 currdata.modelist.push_back(InstanceData(index, listitem.mask));
305         }
306 }
307
308 void DataKeeper::DoSaveChans()
309 {
310         ModesExts currdata;
311         std::vector<OwnedModesExts> currmemberdata;
312
313         const chan_hash& chans = ServerInstance->GetChans();
314         for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
315         {
316                 Channel* const chan = i->second;
317
318                 // Serialize channel modes
319                 for (size_t j = 0; j < handledmodes[MODETYPE_CHANNEL].size(); j++)
320                 {
321                         ModeHandler* mh = handledmodes[MODETYPE_CHANNEL][j].mh;
322                         ListModeBase* lm = mh->IsListModeBase();
323                         if (lm)
324                                 SaveListModes(chan, lm, j, currdata);
325                         else if (chan->IsModeSet(mh))
326                                 currdata.modelist.push_back(InstanceData(j, chan->GetModeParameter(mh)));
327                 }
328
329                 // Serialize all extensions attached to the Channel
330                 SaveExtensions(chan, currdata.extlist);
331
332                 // Serialize all extensions attached to and all modes set on all members of the channel
333                 SaveMemberData(chan, currmemberdata);
334
335                 // Same logic as in DoSaveUsers() plus we consider the modes and extensions of all members
336                 if ((!currdata.empty()) || (!currmemberdata.empty()))
337                 {
338                         chandatalist.push_back(ChanData(chan));
339                         chandatalist.back().swap(currdata);
340                         chandatalist.back().memberdatalist.swap(currmemberdata);
341                 }
342         }
343 }
344
345 void DataKeeper::SaveMemberData(Channel* chan, std::vector<OwnedModesExts>& memberdatalist)
346 {
347         ModesExts currdata;
348         const Channel::MemberMap& users = chan->GetUsers();
349         for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
350         {
351                 Membership* const memb = i->second;
352
353                 for (size_t j = 0; j < handledmodes[MODETYPE_CHANNEL].size(); j++)
354                 {
355                         ModeHandler* mh = handledmodes[MODETYPE_CHANNEL][j].mh;
356                         const PrefixMode* const pm = mh->IsPrefixMode();
357                         if ((pm) && (memb->HasMode(pm)))
358                                 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
359                 }
360
361                 SaveExtensions(memb, currdata.extlist);
362
363                 // Same logic as in DoSaveUsers()
364                 if (!currdata.empty())
365                 {
366                         memberdatalist.push_back(OwnedModesExts(memb->user->uuid));
367                         memberdatalist.back().swap(currdata);
368                 }
369         }
370 }
371
372 void DataKeeper::RestoreMemberData(Channel* chan, const std::vector<ChanData::MemberData>& memberdatalist, Modes::ChangeList& modechange)
373 {
374         for (std::vector<ChanData::MemberData>::const_iterator i = memberdatalist.begin(); i != memberdatalist.end(); ++i)
375         {
376                 const ChanData::MemberData& md = *i;
377                 User* const user = ServerInstance->FindUUID(md.owner);
378                 if (!user)
379                 {
380                         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User %s is gone (while processing %s)", md.owner.c_str(), chan->name.c_str());
381                         continue;
382                 }
383
384                 Membership* const memb = chan->GetUser(user);
385                 if (!memb)
386                 {
387                         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Member %s is no longer on channel %s", md.owner.c_str(), chan->name.c_str());
388                         continue;
389                 }
390
391                 RestoreObj(md, memb, MODETYPE_CHANNEL, modechange);
392         }
393 }
394
395 void DataKeeper::CreateModeList(ModeType modetype)
396 {
397         const ModeParser::ModeHandlerMap& modes = ServerInstance->Modes->GetModes(modetype);
398         for (ModeParser::ModeHandlerMap::const_iterator i = modes.begin(); i != modes.end(); ++i)
399         {
400                 ModeHandler* mh = i->second;
401                 if (mh->creator == mod)
402                         handledmodes[modetype].push_back(ProviderInfo(mh));
403         }
404 }
405
406 void DataKeeper::Save(Module* currmod)
407 {
408         this->mod = currmod;
409
410         const ExtensionManager::ExtMap& allexts = ServerInstance->Extensions.GetExts();
411         for (ExtensionManager::ExtMap::const_iterator i = allexts.begin(); i != allexts.end(); ++i)
412         {
413                 ExtensionItem* ext = i->second;
414                 if (ext->creator == mod)
415                         handledexts.push_back(ProviderInfo(ext));
416         }
417
418         CreateModeList(MODETYPE_USER);
419         DoSaveUsers();
420
421         CreateModeList(MODETYPE_CHANNEL);
422         DoSaveChans();
423
424         FOREACH_MOD_CUSTOM(*reloadevprov, ReloadModule::EventListener, OnReloadModuleSave, (mod, this->moddata));
425
426         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());
427 }
428
429 void DataKeeper::VerifyServiceProvider(const ProviderInfo& service, const char* type)
430 {
431         const ServiceProvider* sp = service.extitem;
432         if (!sp)
433                 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "%s \"%s\" is no longer available", type, service.itemname.c_str());
434         else if (sp->creator != mod)
435                 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>"));
436 }
437
438 void DataKeeper::LinkModes(ModeType modetype)
439 {
440         std::vector<ProviderInfo>& list = handledmodes[modetype];
441         for (std::vector<ProviderInfo>::iterator i = list.begin(); i != list.end(); ++i)
442         {
443                 ProviderInfo& item = *i;
444                 item.mh = ServerInstance->Modes->FindMode(item.itemname, modetype);
445                 VerifyServiceProvider(item, (modetype == MODETYPE_USER ? "User mode" : "Channel mode"));
446         }
447 }
448
449 void DataKeeper::LinkExtensions()
450 {
451         for (std::vector<ProviderInfo>::iterator i = handledexts.begin(); i != handledexts.end(); ++i)
452         {
453                 ProviderInfo& item = *i;
454                 item.extitem = ServerInstance->Extensions.GetItem(item.itemname);
455                 VerifyServiceProvider(item.extitem, "Extension");
456         }
457 }
458
459 void DataKeeper::Restore(Module* newmod)
460 {
461         this->mod = newmod;
462
463         // Find the new extension items
464         LinkExtensions();
465         LinkModes(MODETYPE_USER);
466         LinkModes(MODETYPE_CHANNEL);
467
468         // Restore
469         DoRestoreUsers();
470         DoRestoreChans();
471         DoRestoreModules();
472
473         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restore finished");
474 }
475
476 void DataKeeper::Fail()
477 {
478         this->mod = NULL;
479
480         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restore failed, notifying modules");
481         DoRestoreModules();
482 }
483
484 void DataKeeper::RestoreObj(const OwnedModesExts& data, Extensible* extensible, ModeType modetype, Modes::ChangeList& modechange)
485 {
486         RestoreExtensions(data.extlist, extensible);
487         RestoreModes(data.modelist, modetype, modechange);
488 }
489
490 void DataKeeper::RestoreExtensions(const std::vector<InstanceData>& list, Extensible* extensible)
491 {
492         for (std::vector<InstanceData>::const_iterator i = list.begin(); i != list.end(); ++i)
493         {
494                 const InstanceData& id = *i;
495                 handledexts[id.index].extitem->unserialize(FORMAT_INTERNAL, extensible, id.serialized);
496         }
497 }
498
499 void DataKeeper::RestoreModes(const std::vector<InstanceData>& list, ModeType modetype, Modes::ChangeList& modechange)
500 {
501         for (std::vector<InstanceData>::const_iterator i = list.begin(); i != list.end(); ++i)
502         {
503                 const InstanceData& id = *i;
504                 modechange.push_add(handledmodes[modetype][id.index].mh, id.serialized);
505         }
506 }
507
508 void DataKeeper::DoRestoreUsers()
509 {
510         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restoring user data");
511         Modes::ChangeList modechange;
512
513         for (std::vector<UserData>::const_iterator i = userdatalist.begin(); i != userdatalist.end(); ++i)
514         {
515                 const UserData& userdata = *i;
516                 User* const user = ServerInstance->FindUUID(userdata.owner);
517                 if (!user)
518                 {
519                         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User %s is gone", userdata.owner.c_str());
520                         continue;
521                 }
522
523                 RestoreObj(userdata, user, MODETYPE_USER, modechange);
524                 ServerInstance->Modes.Process(ServerInstance->FakeClient, NULL, user, modechange, ModeParser::MODE_LOCALONLY);
525                 modechange.clear();
526         }
527 }
528
529 void DataKeeper::DoRestoreChans()
530 {
531         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restoring channel data");
532         Modes::ChangeList modechange;
533
534         for (std::vector<ChanData>::const_iterator i = chandatalist.begin(); i != chandatalist.end(); ++i)
535         {
536                 const ChanData& chandata = *i;
537                 Channel* const chan = ServerInstance->FindChan(chandata.owner);
538                 if (!chan)
539                 {
540                         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Channel %s not found", chandata.owner.c_str());
541                         continue;
542                 }
543
544                 RestoreObj(chandata, chan, MODETYPE_CHANNEL, modechange);
545                 // Process the mode change before applying any prefix modes
546                 ServerInstance->Modes.Process(ServerInstance->FakeClient, chan, NULL, modechange, ModeParser::MODE_LOCALONLY);
547                 modechange.clear();
548
549                 // Restore all member data
550                 RestoreMemberData(chan, chandata.memberdatalist, modechange);
551                 ServerInstance->Modes.Process(ServerInstance->FakeClient, chan, NULL, modechange, ModeParser::MODE_LOCALONLY);
552                 modechange.clear();
553         }
554 }
555
556 void DataKeeper::DoRestoreModules()
557 {
558         for (ReloadModule::CustomData::List::iterator i = moddata.list.begin(); i != moddata.list.end(); ++i)
559         {
560                 ReloadModule::CustomData::Data& data = *i;
561                 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Calling module data handler %p", (void*)data.handler);
562                 data.handler->OnReloadModuleRestore(mod, data.data);
563         }
564 }
565
566 } // namespace ReloadModule
567
568 class ReloadAction : public ActionBase
569 {
570         Module* const mod;
571         const std::string uuid;
572         const std::string passedname;
573
574  public:
575         ReloadAction(Module* m, const std::string& uid, const std::string& passedmodname)
576                 : mod(m)
577                 , uuid(uid)
578                 , passedname(passedmodname)
579         {
580         }
581
582         void Call() CXX11_OVERRIDE
583         {
584                 ReloadModule::DataKeeper datakeeper;
585                 datakeeper.Save(mod);
586
587                 DLLManager* dll = mod->ModuleDLLManager;
588                 std::string name = mod->ModuleSourceFile;
589                 ServerInstance->Modules->DoSafeUnload(mod);
590                 ServerInstance->GlobalCulls.Apply();
591                 delete dll;
592                 bool result = ServerInstance->Modules->Load(name);
593
594                 if (result)
595                 {
596                         Module* newmod = ServerInstance->Modules->Find(name);
597                         datakeeper.Restore(newmod);
598                 }
599                 else
600                         datakeeper.Fail();
601
602                 ServerInstance->SNO->WriteGlobalSno('a', "RELOAD MODULE: %s %ssuccessfully reloaded", passedname.c_str(), result ? "" : "un");
603                 User* user = ServerInstance->FindUUID(uuid);
604                 if (user)
605                         user->WriteNumeric(RPL_LOADEDMODULE, passedname, InspIRCd::Format("Module %ssuccessfully reloaded.", (result ? "" : "un")));
606
607                 ServerInstance->GlobalCulls.AddItem(this);
608         }
609 };
610
611 CmdResult CommandReloadmodule::Handle(User* user, const Params& parameters)
612 {
613         Module* m = ServerInstance->Modules->Find(parameters[0]);
614         if (m == creator)
615         {
616                 user->WriteNumeric(RPL_LOADEDMODULE, parameters[0], "You cannot reload core_reloadmodule (unload and load it)");
617                 return CMD_FAILURE;
618         }
619
620         if (creator->dying)
621                 return CMD_FAILURE;
622
623         if ((m) && (ServerInstance->Modules.CanUnload(m)))
624         {
625                 ServerInstance->AtomicActions.AddAction(new ReloadAction(m, user->uuid, parameters[0]));
626                 return CMD_SUCCESS;
627         }
628         else
629         {
630                 user->WriteNumeric(RPL_LOADEDMODULE, parameters[0], "Could not find module by that name");
631                 return CMD_FAILURE;
632         }
633 }
634
635 COMMAND_INIT(CommandReloadmodule)