-
Aaron Jones authored
This commit replaces 4 old modules providing 1 command each, with 1 new module that provides those 4 commands. This allows all of the command handlers to share the same logic for dependency tracking. Thoroughly document the various utility functions to enable the code to be easily understood in future. The complete list of improvements to those commands is as follows: MODINSPECT - Support querying for multiple modules at once. - Print at least some details about a module even if it does not have a MAPI header (address, unloadability). - Don't print the module name twice (in the header, and in a name field immediately after it). - Print forward and reverse dependencies (including their load addresses and unloadability status) for each module being inspected, including a count of direct dependencies. To avoid modules being counted multiple times, this count is not recursive. For example, if foo depends on bar, and baz depends on both foo and bar, then inspecting bar would say it has 2 reverse dependencies, even though it would print baz twice. Modules are expected to properly express all of their dependencies, rather than relying on an implicit dependency chain. That is, baz should be made to explicitly depend on bar, rather than relying on its dependence on foo resulting in bar being available. That way, the count is accurate. - When printing whether a module is unloadable, reload-only, or permanent, take into account all of its reverse dependencies (and theirs (and theirs ...)). There is little utility in telling the user that this module is reloadable if a permanent module depends upon it; MODRELOAD would fail. Likewise for a permanent or reloadable module and MODUNLOAD. Distinguish between whether the result is because of the module's own declaration, or because of the declaration of one of its reverse dependencies. Also allow the result output to be coloured. MODLOAD - When loading a module fails, use fault_internalerror instead of fault_nosuch_target. It is not necessarily the case that the module does not exist; it could have also failed in modinit (e.g. trying to load "nickserv/set_language" when built with --disable-nls). The module_load() function will emit messages that distinguish between the two cases. - When rehashing after loading a module fails, use fault_internalerror instead of fault_nosuch_target. This is likely a copy/paste mistake. MODRELOAD - Support reloading multiple modules at once. The old module advertised that it could accept multiple arguments, but it would only process the first argument. This means we need to sort the list of modules by dependency order. For example, if we were asked to reload both saslserv/scram and crypto/pbkdf2v2 at the same time, the former depends on the latter, so the latter should be reloaded before it. That is, we take into account the dependencies that existed at the time the modules were unloaded, instead of relying on the modules to pull in their dependencies again. This is because some modules, like the example saslserv/scram given above, test whether the other module is loaded before depending upon it, as a matter of configuration validation. This also means we need to test if the module is already present before attempting to reload it, as it may have already been reloaded when parsing an earlier argument. - Now that the unified dependency tracking is present, and deciding whether a module can be reloaded takes into account all of its reverse dependencies, and the fact that this module is permanent, do away with the explicit checks for reloading operserv/main or this new module. They are not necessary. - When processing multiple module arguments, don't abort if one of them cannot be reloaded; just skip it. This allows as much of the reload command to be executed as possible. - When exiting due to failure to reload a reload-only module, also send a wallops to that effect. - When rehashing after loading a module fails, use fault_internalerror instead of fault_nosuch_target. This is likely a copy/paste mistake. MODUNLOAD - Take into account a module's reverse dependencies before trying to unload it. Otherwise, module_unload() itself could go on to unload a permanent or reload-only module, resulting in data loss, memory corruption, memory leaks, or any other untold havoc. When denying unload due to this, print the name of the reverse dependency responsible for preventing it. Furthermore, apply general style and tidiness fixups to all of the imported and re-written code; wrapping long strings, const-correctness, etc.