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