Skip to content
  • Aaron Jones's avatar
    modules/operserv/mod{inspect,load,reload,unload}: unify into new module · adb52695
    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.
    adb52695