# IRC Services Technical Reference Manual

## 4. The module system

Previous section: Communication (socket) handling | Table of Contents | Next section: IRC server interface

### 4-1. The module concept

The module system was introduced in Services 5.0, partly to allow greater flexibility in configuring Services, partly to simplify the process of extending Services, and partly to help clarify the boundaries between distinct sets of functionality. Rather than merge every conceivable function into a single monolithic program, Services was divided into a comparatively small core and a number of modules which each implemented distinct sets of features. These modules could then be developed separately from the core and from each other, without concern for the effects of changes in other parts of the program.

Aside from providing a cleaner program structure, modules make it simpler to implement different variations on a particular feature. A good example of this is the protocol modules (discussed in section 5); rather than a complex set of `#if` and `#else` directives to handle differences between IRC server types, a separate protocol module can be written for each type of IRC server; this results in a certain amount of code duplication for similar protocols, but makes the code for each protocol much more maintainable, and allows the code for a single protocol to be changed easily without worrying that it will break processing for other protocols.

Modules also make it easier to add new features to Services, whether as part of the Services package or as third-party additions. By connecting to the Services core and standard modules through a set of well-defined callbacks, no changes need to be made to the main source code when the module is updated, and vice versa. The compilation system (see section 10-3-2) also makes it easy for users to add modules on their own, by simply "dropping in" the source code for the new module; Services will automatically find the module and compile it.

Below, section 4-2 discusses the basic structure of a module, along with other background issues; section 4-3 discusses how modules are managed, including the details of module loading, configuration, and unloading; section 4-4 discusses how to make symbols available to other modules and use symbols from other modules; and section 4-5 covers callbacks.

Back to top

### 4-2. Structure of a module

From the viewpoint of the Services core, a module is a single unit of functionality with a known interface. The only direct accesses made to module data are to a limited number of functions and variables (as described in section 4-2-1 below); all other module interactions occur through callbacks, which it is the module's responsibility to install as necessary.

A typical module, therefore, will use its initialization routine (one of the directly-accessed functions) to install callback functions, enabling it to respond to events such as messages from the IRC server or connections on a socket. After performing any other necessary setup actions, the initialization routine returns control to the Services core, which will then call the module's callback functions as appropriate. The bulk of a module is thus usually contained in these callback functions or their helper routines.

Back to top

#### 4-2-1. Required and optional symbols

Modules are required to define two variables, used by the module loader: `int32 module_version`, initialized to the value of the macro `MODULE_VERSION_CODE`, and `Module **_this_module_ptr`, a pointer to a variable which will receive the module's handle (see section 4-3 for information on module handles). However, both of these variables are normally defined by `modules.h` when compiling the module's main file (the source file with the same base name as the module object file), so ordinarily there is no need to be concerned about these variables.

Modules may optionally define two functions, which the module loader will call when the module is loaded and unloaded, respectively:

`int init_module()`
  Performs any initialization operations necessary for the module, returning nonzero on success, zero on failure. This function is called during the module loading process (see section 4-3-1); if it returns failure, the module load is aborted.
`int exit_module(int shutdown)`
  Performs any cleanup operations necessary for the module. If `shutdown` is zero, then the function is being called for an unload operation during normal program processing; if `exit_module()` returns zero, then the unload is aborted (a nonzero return value indicates permission to unload the module). If `shutdown` is nonzero, then the function is being called because the program is shutting down; in this case, the function must perform all necessary shutdown operations (returning zero to cancel the unload is not permitted). Implementation note: The `shutdown` parameter was included primarily for the protocol modules, due to some shortcuts taken on the assumption that only one protocol module would be used during any given execution.

Modules may also optionally define `ConfigDirective module_config[]` (this must be an array, not a pointer), containing configuration information for the module which will be processed by the module loader. The use of this array is described in section 4-3-2.

Back to top

#### 4-2-2. Utility macros and functions

The header file `modules.h`, which must be included by all module source files, includes a few macros for convenience in writing modules:

`THIS_MODULE`
  Evaluates to the module handle for this module. This can be used, for example, if a module wants to pass its handle explicitly to another module, but it is more commonly used by other macros in `modules.h` to automatically pass the module's handle to various module-related routines (see below).
`MODULE_NAME`
  Evaluates to a string containing the module's name; the value should be treated as constant but not literal. (Internally, it is simply the result of `get_module_name(THIS_MODULE)`.)
`MODULE_VERSION_CODE`
  Evaluates to a numeric code indicating the Services version number. The version number consists of four parts: major version number, minor version number, release status (alpha, beta/prerelease, or final), and release number. These are passed to the macro to produce a number indicating the version, where greater values indicate later versions. For example:
  MODULE_VERSION_CODE(5,0,MODULE_VERSION_FINAL,4) < MODULE_VERSION_CODE(5,1,MODULE_VERSION_ALPHA,3) < MODULE_VERSION_CODE(5,1,MODULE_VERSION_BETA,2) < MODULE_VERSION_CODE(5,1,MODULE_VERSION_FINAL,1)
  because version 5.0 is earlier than version 5.1, alpha releases are earlier than beta releases, and beta releases are earlier than final releases. (These values correspond to the versions 5.0.4, 5.1a3, 5.1pre2, and 5.1.1 respectively.) While this value is primarily used to set the `module_version` required variable, as described above, it can also be used in conditional compilation when compatibility with multiple versions of Services is desired.

In addition, many of the routines described in this section take a `module` or `caller` parameter, so that the module subsystem knows which module is making the call; however, macros are used to hide this parameter from the module source code, both to improve readability of the code and to avoid potential errors from passing the wrong parameter. These functions are:

- `add_callback()` (and `add_callback_pri()`)
- `call_callback()` (and variations)
- `module_log()` (and variations; declared in `log.h`)
- `register_callback()`
- `remove_callback()`
- `unregister_callback()`
- `unuse_module()`
- `use_module()`

Back to top

#### 4-2-3. Dynamic versus static modules

It is possible for modules to be compiled either dynamically, so that the module is loaded into memory only when used, or statically, building all modules into the main executable file. The decision of which to use is made by the user via the `configure` script, and cannot be controlled by individual modules. In general, modules will behave exactly the same whether compiled dynamically or statically; however, it is important to be aware of particular behaviors with each method, to avoid problems that cause the module to fail when using one method or the other.

Dynamic linking involves compiling each module into an independent loadable object file, which is then loaded into memory as needed using the system's `dlopen()` family of functions. (If these functions are not available, dynamic modules cannot be used; the `configure` script will detect this and select static modules automatically.) Any direct references to symbols not defined in the module, such as core Services routines, will be marked as unresolved references in the object file. As a result, it is possible to compile a module that references a nonexistent symbol—for example, accidentally typing `strmcp` instead of `strcmp`—and the error will not be detected until an attempt is made to load the module.

Static linking, on the other hand, results in every module being linked together with the core code to produce a single executable file. This means that every symbol in the Services core—and in other modules—will be available for use. Static linking can detect references to nonexistent symbols at compile time, unlike dynamic linking, but will not generate an error if one module references a symbol from another. For example, if a protocol module erroneously references a symbol from one of the pseudoclients directly (rather than using the symbol lookup method described in section 4-4-2), the protocol module will fail to load when using dynamic linking unless the appropriate pseudoclient module is loaded first. (Since the current design of Services requires that a protocol module be loaded first, this would result in Services not being able to run at all.)

When using modules composed of two or more source files that share internal-use symbols, static linking can cause spurious errors at link time. This is because, even though the symbols are only used internally, they are still included in the final object file for the module; thus, if two separate modules happen to use the same name for a function or variable, the final link will report a "duplicate symbol" error. This is an actual problem with the NickServ and ChanServ modules, both of which include various internal utility routines in a separate source file, and as a result some ChanServ routines have to be renamed (see the relevant part of section 7-4-1-1).

Additionally, when using static linking, module-global variables will always retain their values between a module unload and a subsequent module load; when using dynamic linking, it is system-dependent whether the values will be retained or reset to their initial values. Thus it is important that a module's cleanup routine not only free all resources used by the module, but also reset all variables to appropriate values for the next call to the initialization routine.

For these reasons, it is best to test modules using both dynamic and static linking, to ensure that unintended errors are not introduced.

Back to top

### 4-3. Module management

The module subsystem uses the routines described in this section to manage modules, performing loading, configuration and unloading as well as checking whether a module is loaded or locking modules into memory.

Most module subsystem routines take or return a module handle, a value of type `Module *`, to identify a particular module. The `Module` type is declared as simply `struct Module_` in `modules.h`, rendering the type opaque to callers. Internally, `struct Module_` is used to hold information about each loaded module, and has the following fields:

`Module *next, *prev`
  Used to maintain the linked list of loaded modules.
`char *name`
  The module's name, set to the pathname used to load the module (for example, "`nickserv/main`").
`ConfigDirective *modconfig`
  The value of the symbol `module_config` in this module, or `NULL` if the `module_config` symbol does not exist.
`Module ***this_module_pptr`
  The value of the symbol `_this_module_ptr` in this module. (This is the address of the `_this_module_ptr` variable, which itself is a pointer to a variable of type `Module *`, so the type of this field is `Module ***`.)
`const int32 *module_version_ptr`
  The value of the symbol `module_version` in this module (again, the address of the variable, not its value).
`void *dllhandle`
  The handle returned by the system `dlopen()` function. For static linking, this instead points to an internal structure with the module data.
`CallbackList *callbacks`
`int callbacks_count`
  A variable-length array containing the callbacks registered by this module.
`const Module **users`
`int users_count`
  A variable-length array containing handles of modules which have called `use_module()` for this module.

Loaded modules are kept as a doubly-linked list, with the list head in the file-scope variable `modulelist`. This list always contains at least one entry: a dummy entry for the Services core, defined in the variable `coremodule`.

The module management routines are:

`Module *load_module(const char *modulename)`
  Loads the given module and returns a pointer to its module handle, or `NULL` on failure. Described in section 4-3-1.
`int unload_module(Module *module)`
  Unloads the given module. Returns nonzero if the module was successfully unloaded, zero if not. Described in section 4-3-3.
`Module *find_module(const char *modulename)`
  Returns a handle for the module whose name is given by `modulename`. If no such module has been loaded, returns `NULL`. Described in section 4-3-4.
`void use_module(Module *module)`
  Marks the given module as "in use", preventing it from being unloaded. Described in section 4-3-5.
`void unuse_module(Module *module)`
  Releases the lock on a module set by `use_module()`. Described in section 4-3-5.
`void modules_init()`
  Initializes the module subsystem. Described in section 4-3-6.
`void modules_cleanup()`
  Shuts down the module subsystem. Described in section 4-3-6.

Back to top

#### 4-3-1. Module loading

A module can be loaded by calling the `load_module()` routine. This routine takes a single parameter, a string giving the name of the module to load (such as "nickserv/main"), and returns the handle for the newly-loaded module or `NULL` on failure. The routine:

- Calls `internal_load_module()` to load the module into memory. This helper routine takes care of performing the actual module load (or lookup, when modules are linked statically) and filling in the `Module` structure; see below for details.
- Links the new `Module` structure into the loaded module list.
- Calls the core's `configure()` routine to configure module settings; see section 4-3-2 for details.
- Calls `internal_init_module()` to perform module initialization. This returns nonzero (success) if the module's initialization function, `module_init()`, returns success (or does not exist).
- Calls the "`load module`" callback, a module callback (see section 4-5) used to signal that a new module has been loaded.
- Finally, performs a check to ensure that a valid protocol module is loaded. This is a kludge to ensure that a protocol module is loaded before any other modules, since some modules alter their initialization behavior depending on the protocol in use. See also section 5-1.

The actual low-level work of loading the module and initializing the `Module` structure is handled by the `internal_load_module()` helper routine mentioned above. This routine:

- Ensures that the name given (which, when using dynamic linking, becomes part of the file pathname) does not contain `../`", to avoid attempts by malicious code to access files outside the Services data directory.
- Checks whether the module has already been loaded, returning an error if so.
- Calls `my_dlopen()` (see below) to actually load the module into memory.
- Allocates a new `Module` structure, filling in the system module handle and module name.
- Looks up the `_this_module_ptr` symbol in the module, returning an error if the symbol is not found, and stores the symbol's value in the `this_module_pptr` field of the `Module` structure. The variable pointed to by `_this_module_ptr` is then set to the module handle, allowing the `THIS_MODULE` macro to work.

  To perform the lookup, `internal_load_module()` first calls `check_module_symbol()` to get the symbol value. Then, if dynamic linking is in use, the returned value is checked the `this_module_pptr` field of each module in the loaded module list to see whether the same address has been used for a different module. This latter check is required because of a problem (on some systems) with the system function `dlsym()`, used for looking up symbols when dynamic linking is in use: if the requested symbol is not present in the requested module but is present in another module, `dlsym()` may return the symbol from the other module (see section 4-4-2 for details).

  Note that the implementation used with static linking does not have this problem, rendering the check unnecessary. In fact, attempting to perform the check with static linking can actually cause problems on some systems. This is due to constant merging performed by linkers, which results in distinct modules legitimately having the same value for such a symbol. This has been observed with the `module_version` symbol (see below), so the check is disabled when static linking is in use.

- Looks up the `module_version` symbol in the module, returning an error if the symbol is not found or the 32-bit value that it points to does not match the value of the `MODULE_VERSION_CODE` macro, and stores the symbol value (variable address) in the `module_version_ptr` field of the `Module` structure.
- Looks up the `module_config` symbol in the module, setting the `modconfig` field of the `Module` structure to the symbol's value (or `NULL` if the symbol is not found).
- Returns the new `Module` structure.

The `my_dlopen()` function referred to above is one of four wrappers for the system dynamic linking functions: `dlopen()`, `dlclose()`, `dlsym()`, and `dlerror()`. When using dynamic linking, the wrapper functions essentially just call their system counterparts, although `my_dlopen()` generates the actual pathname from the module name (prepending the module directory pathname and appending the shared object extension "`.so`"), and `my_dlsym()` includes workarounds for systems that prepend symbol names with underscores and that do not handle `dlsym(NULL,...)`. When static linking is in use, the wrapper functions instead search through the pregenerated module and symbol lists for the requested module or symbol (`dlclose()` does nothing, and `dlerror()` uses a static error message variable to hold the value to return). See section 10-3-2) for details on the generation of these lists.

Note that `load_module()` (and its counterpart, `unload_module()`) are only intended to be called by the Services core to load modules specified by `LoadModule` directives in `ircservices.conf`. Modules should never attempt to load other modules on their own, since the user may have intentionally chosen not to load certain modules; instead, an error should be generated if a required module is not available.

Back to top

#### 4-3-2. Module configuration

Configuration of modules is performed through the same routine, `configure()`, that handles the Services core configuration; however, a different file is used, `modules.conf` rather than `ircservices.conf`, and each module's configuration directives must be enclosed by the `Module` and `EndModule` meta-directives. (Configuration files and the `configure()` routine are described in detail in section 2-3-2.)

Each module can define configuration directives to be parsed by the configuration file reader by creating an array of `ConfigDirective` entries named `module_config`. `load_module()` will check for this symbol and, if found, pass it to `configure()`, as described above; thus, the variables corresponding to the configuration directives will have already been set when the module's `init_module()` function is called. The directives will likewise be reprocessed automatically during reconfiguration; however, it may be necessary for the module to detect changes in configuration variables (such as the nickname of a pseudoclient). For this purpose, the module can hook into the "`reconfigure`" callback, which is called once before reconfiguration takes place and once after (see appendix C for details on the callback).

All configuration variables will be reset to their initial values when the module is unloaded, so the module's `exit_module()` routine does not need to take any particular actions with respect to such variables.

Back to top

#### 4-3-3. Module unloading

Loaded modules are unloaded from memory with the `unload_module()` routine. This routine, which is implemented in a separate `internal_unload_module()` routine to allow passing a `shutdown` parameter to the module's cleanup function, performs the reverse of `load_module()`'s operations, returning nonzero if the module is successfully unloaded, zero if not. The routine:

- Checks whether the module is in use (locked) by another module (see section 4-3-5), returning failure if so.
- Calls the module's `exit_module()` function (if it exists) with a parameter of zero, indicating that the unload request is explicit rather than part of the program shutdown process. The unload fails if the function returns zero (failure).
- Removes the module from the global module list, ensuring that other modules cannot access it during the unload procedure (particularly when the "`unload module`" callback is called).
- Checks that the module has not left any other modules in a locked state, by calling `use_module()` without a corresponding `unuse_module()` (see section 4-3-5). If it has, a warning message is logged and the offending locks are removed.
- Checks that the module has not left any callback functions hooked into other modules' (or the core's) callbacks. If it has, a warning message is logged and the offending functions are removed.
- Checks that the module has not left any callbacks of its own registered. If it has, a warning message is logged and the offending callbacks are removed.
- Calls the "`unload module`" callback, passing the handle for the module that is being unloaded.
- Calls `deconfigure()` for the module's configuration directives (if any), restoring configuration variables to their original values.
- Frees resources used by the `Module` structure, and calls `my_dlclose()` to unload the module at the system level.

Aside from `unload_module()`, modules are also unloaded during the shutdown process (see section 4-3-6).

Back to top

#### 4-3-4. Searching for a module

If one module needs to access another, it can do so with the `find_module()` routine. This routine takes a module name as parameter, searches through the loaded module list for a module whose name exactly matches the given name (case-sensitively), and returns the handle for that module (`NULL` if no matching module is found).

`find_module()` is used most commonly when one module wants to use functionality provided by another; for example, the module implementing the ChanServ pseudoclient (`chanserv/main`) uses it to retrieve the handle for the NickServ module (`nickserv/main`). Note, however, that in cases where the module being searched for is not required, it may not be present at initialization time; in this case, it is better to hook into the core's "`load module`" and "`unload module`" callbacks, and only use `find_module()` to check whether the desired module has already been loaded when `init_module()` is called. For example, the MemoServ module, `memoserv/main`, takes this approach with respect to the `chanserv/main` module when deciding whether to enable channel memos.

Back to top

#### 4-3-5. Locking a module in memory

If one module requires another in order to function properly, the "user" module can request that the "used" module not be unloaded. This is accomplished by calling the `use_module()` routine, passing the "used" module as the `module` parameter; this causes the specified module to be locked in memory. When the module is no longer needed (for example, when the "user" module's `exit_module()` routine is being called for unloading), `unuse_module()` unlocks the module, allowing it to be unloaded normally.

One problem that can arise when locking resources is the case of a circular lock dependency: if module A has locked module B, but module B has also locked module A, then neither of them can be unloaded until one or the other module releases its lock; if the modules only release their locks at cleanup time, there would be no way to unload them. In order to prevent situations like this, `use_module()` checks for such circular dependencies and issues a warning if one is detected (in the form of a "BUG" message written to the log file). Naturally, it is also not permitted for a module to attempt to lock itself.

Note that both of these routines are internally named with a preceding underscore (`_use_module()`, `_unuse_module()`) and take a second parameter, `const Module *caller`, which is used to ensure that modules do not leave stale locks around when they are unloaded (as described in section 4-3-3). However, these are hidden by macros which automatically pass `THIS_MODULE` as the `caller` parameter.

Back to top

#### 4-3-6. Global initialization and cleanup

Before calling any module-related routines, the module subsystem must be initialized by calling `modules_init()`, which obtains the system handle for the program itself (when using dynamic linking) and sets up the three module subsystem callbacks: "`load module`", "`reconfigure`", and "`unload module`".

Likewise, the `modules_cleanup()` routine must be called when the program shuts down. Aside from removing the callbacks mentioned above, `modules_cleanup()` checks that other parts of the Services core have removed any callbacks they registered, and calls the internal routine `unload_all_modules()` to walk the loaded module list and unload each module. In this case, each module's `exit_module()` function is called with a nonzero `shutdown` value, indicating that the program is shutting down and the module must clean up after itself. (The module will be unloaded regardless of the return value of `exit_module()`.)

Back to top

### 4-4. External symbols

While some modules only require Services core functions to operate, most interact with other modules. This necessitates a way of accessing functions and data provided by outside modules.

Back to top

#### 4-4-1. Exporting symbols

A module can designate certain symbols (functions or variables) to be exported, so that other modules can access them as described in the next section. This is done using one of three macros, depending on what is being exported:

- `EXPORT_FUNC(symbol)` (for functions)
- `EXPORT_VAR(type,symbol)` (for ordinary variables)
- `EXPORT_ARRAY(eltype,symbol)` (for arrays; `eltype` is the type of an element of the array)

These macros do not expand to anything themselves, but are used by the compilation process (see section 10-3-2) to generate lists of exported symbols when using static linking. Even without these macros, the symbols will still (if not declared `static`) be recorded in the object file, but since there is no way to access the program's symbols when using static linking—and in fact, the symbols may have been stripped from the executable file by the user—the macros must be used in order for `get_module_symbol()` (see section 4-4-2 below) to be able to retrieve the symbol's value.

Note that the symbols used by the module subsystem (`module_version` and so on, as described in section 4-2-1) are automatically made available to the module subsystem, and need not be explicitly exported. This is done by the `Makefile` controlling module compilation, as described in section 10-3-2.

Back to top

#### 4-4-2. Importing symbols

There are two methods of accessing symbols located in other modules: by calling `get_module_symbol()` or `check_module_symbol()`, or by referencing the symbol directly. Each has its benefits and disadvantages.

`get_module_symbol()` and `check_module_symbol()` allow a caller to retrieve the value of a given symbol in a specified (or any) module. The functions are declared as follows (note that `get_module_symbol()` is declared internally as `_get_module_symbol()` and takes a hidden `caller` parameter, but these are hidden by a macro in `modules.h`):

void *get_module_symbol(Module *module, const char *symname) int check_module_symbol(Module *module, const char *symname, void **resultptr, const char **errorptr)

Both of these functions retrieve the value of the symbol `symname` in the module `module`; if `module` is `NULL`, the symbol is searched for in all loaded modules (if defined in more than one module, then an arbitrary module is used for the lookup). If the symbol is not defined in `module` but is defined in another module, then the result of the lookup is defined to be either `NULL` or the value of the symbol in the module in which it is defined. This undesirable behavior unfortunately cannot be avoided due to identical behavior by (at least) the glibc library used on Linux. When using static linking, the functions always return `NULL` for this case.

The difference between their two functions is how they deal with the case of a nonexistent symbol. `get_module_symbol()` is intended for use when the symbol is assumed to exist; it returns the symbol's value directly if it exist, and logs a warning and returns `NULL` otherwise. The danger of using this for symbols that may legitimately not exist (other than generating spurious log messages) is that it is possible for a symbol to have the value zero (`NULL`), which would be indistinguishable from a symbol lookup failure. `check_module_symbol()`, on the other hands, takes two extra parameters, a pointer to a variable in which the result is to be stored on success and a pointer to a variable in which the relevant error message is to be stored on failure (either or both of which may be `NULL`), and returns nonzero on success, zero on failure. On success, the variable pointed to by `resultptr` is set to the value of the pointer, and the variable pointed to by `errorptr` is left unchanged; on failure, the variable pointed to by `errorptr` is set to a human-readable error string, and the variable pointed to by `resultptr` is left unchanged.

Note that the "value of the symbol" here is the value in linker terms; in other words, when using these functions to access a variable, the value of the symbol is the address of the variable, not its value. To access the value of the `s_NickServ` string variable in an unspecified module, for example, one would write code like:

char **p_s_NickServ = get_module_symbol(NULL, "s_NickServ"); if (p_s_NickServ) { notice(*p_s_NickServ, user->nick, "message"); } else { /* Unable to resolve s_NickServ */ }

It is also important to keep in mind that the value of such variables may change, or the module containing the variable may be unloaded, at any time. Accesses should therefore always be done by looking up the symbol, ensuring that the symbol exists, and referencing it through the returned pointer. Since this can incur significant overhead, however, another option (when the module name is known) is to use the "`load module`" and "`unload module`" callbacks to watch for the module to be loaded or unloaded; a single `get_module_symbol()` or `check_module_symbol()` will then suffice, and the returned variable pointer can be saved locally until the module is later unloaded. Many of the modules distributed with Services take this approach.

The other method of accessing symbols in other modules is to simply reference the symbol as if it was an ordinary external variable or function. Doing this avoids the overhead of looking up the symbol every time it is to be used, or monitoring the relevant module for loads and unloads. However, it also requires the module containing the symbol to be loaded before the module that uses it; if the modules are not loaded in the correct order, `load_module()` will report an "unresolved symbol" error. For example, if module A exports a symbol `symA` which module B accesses directly, attempting to load module B before module A will cause an "unresolved symbol" error for `symA`, without giving module B a chance to manually resolve the symbol. As a general rule, therefore, it is recommended to avoid direct references to symbols from other modules, unless the other module can be reasonably expected to be loaded first (for example, many of the pseudoclients' sub-modules access symbols from their respective core modules in this fashion).

Back to top

### 4-5. Callbacks

Callbacks are a method of allowing modules to signal other modules when certain events occur. A module first establishes a callback by registering it, giving it a name unique among all callbacks for that module (two callbacks in different modules can share the same name). Other modules can use that name to refer to the callback, and add functions to be called when the callback's event occurs (also referred to as hooking into the callback). The module which added the callback can then instruct the module subsystem to call each of those functions in turn when the requisite event occurs.

The Services core also sets up a number of callbacks which can be hooked into by modules as necessary. A full list of callbacks available in the Services core and standard modules can be found in Appendix C.

Back to top

#### 4-5-1. Registering callbacks

A module that wishes to register a new callback can do so with the `register_callback()` routine (internally `_register_callback()`):

int register_callback([Module *module,] const char *name)

This routine checks to ensure that the given module has not already registered a callback of the same name (by calling `find_callback()`, an internal helper function that looks for a callback by name in a given module), then adds the callback to the module's callback table with an empty callback function list. The return value is the ID value assigned to the callback (a value used when calling or removing the callback, both for efficiency and to eliminate the possibility of mistyping the callback name string when referring to it internally); a return value of -1 signals an error. Note that the module's `THIS_MODULE` macro (which evaluates to `NULL` when called by the core) is automatically passed to the function by the `register_callback()` macro in `modules.h`, like the `caller` parameter to `use_module()` and `unuse_module()`.

When the callback is no longer needed, it can be unregistered by calling `unregister_callback()` (internally `_unregister_callback()`):

int unregister_callback([Module *module,] int id)

The routine returns 1 if the callback was found and freed, 0 if the ID value given was invalid.

Back to top

#### 4-5-2. Hooking into callbacks

Once a callback has been registered, other modules can add functions to or remove functions from the callback with the `add_callback()`, `add_callback_pri()`, and `remove_callback()` functions (internally `_add_callback_pri()` and `_remove_callback`—`add_callback()` is defined in terms of `add_callback_pri()`):

int add_callback(Module *module, const char *name, callback_t callback, [const Module *caller]) int add_callback_pri(Module *module, const char *name, callback_t callback, int priority, [const Module *caller]) int remove_callback(Module *module, const char *name, callback_t callback, [const Module *caller])

The type of a callback function is defined as `callback_t`:

typedef int (*callback_t)()

The number and type of parameters passed to the function depends on the particular callback, and thus the `callback_t` type is not a true prototype. The `int` return value is used both to return a result from the callback function itself and to control execution of the list of callback functions, with a nonzero value terminating execution, as described in section 4-5-3 below.

`add_callback()` and `add_callback_pri()` add the given function to the selected callback, returning nonzero on success, zero on error. In the case of `add_callback_pri()`, the callback function can be given a numeric priority; callback functions with higher priority values are called before those with lower values, and callback functions with the same priority value are called in the same order they were added. `add_callback()` is equivalent to `add_callback_pri()` with a priority of zero, and in fact is implemented as a macro that does exactly that. Implementation note: Priorities were added to allow the `operserv/sessions` module to have its "`user check`" callback function, which increments the user count for a session if the user passes the session check, called last. This is arguably a bad design decision, as it would be better (if less efficient) to move the session count incrementing to the "`user create`" callback, and the current method cannot stop another module from adding a callback function at an even lower priority and causing the session count to "leak" if the lower-priority function rejects the user.

`remove_callback()` removes the given function from the selected callback, returning nonzero if the function was found and successfully removed, else zero. `remove_callback()` deliberately does not generate a log warning when used with a callback function that has not been added, in order to simplify modules' cleanup routines (so that they can call `remove_callback()` for all callbacks that may have potentially been added without having to keep track of which ones were in fact added).

Back to top

#### 4-5-3. Calling callbacks

A module can instruct the module subsystem to call all callback functions for a particular callback with the `call_callback()` set of functions (actually macros, with the implementing function internally named `_call_callback_5()`):

int call_callback([Module *module,] int id) int call_callback_1([Module *module,] int id, void *arg1) int call_callback_2([Module *module,] int id, void *arg1, void *arg2) int call_callback_3([Module *module,] int id, void *arg1, void *arg2, void *arg3) int call_callback_4([Module *module,] int id, void *arg1, void *arg2, void *arg3, void *arg4) int call_callback_5([Module *module,] int id, void *arg1, void *arg2, void *arg3, void *arg4, void *arg5)

As with `register_module()` and `unregister_module()`, the module handle of the calling module is automatically passed in the hidden `module` parameter.

These functions all call the selected callback (specified by callback ID value, as returned from `register_callback()`), and differ only in the number of parameters passed to the callback. The parameters `arg1` through `arg5` are passed to the callback functions unchanged; the parameters are declared as the (more or less) generic type `void *`, but may be of any type, as the `call_callback()` macros take care of converting the parameters to `void *` to avoid type conversion warnings (but see section 11-1 for problems with this method of passing parameters). It is the callback function's responsibility to ensure that it is declared with the correct parameter list.

After checking the validity of the module and callback ID parameters, `call_callback()` calls each callback function that has been added to the callback, in priority and time order (see section 4-5-2 about calling order). If any callback function returns a nonzero value, `call_callback()` stops immediately and returns that value, skipping the rest of the callback functions; if all callback functions return zero (or if there are no callback functions for the given callback), zero is returned. (-1 is returned if the callback ID is invalid.) Thus, callbacks should be designed so that a return value of zero from a callback function means "continue processing" and a nonzero value means "stop processing or take some other action".

Back to top

### 4-6. Adding a module to Services

Adding a new module to Services consists generally of the following steps:

- Create a subdirectory for the module under the `modules` directory.
- Write the module itself.
- Create a Makefile for the module (see section 10-3-2).

For submodules of pre-existing modules, like new NickServ or ChanServ features, the module can naturally go in the appropriate pre-existing directory instead, with the corresponding Makefile modified to compile the new module.

For modules intended to be distributed separately from Services itself, it is sufficient to distribute the subdirectory containing the module source code and Makefile; by copying that directory into the `modules` directory of a fresh Services distribution, the module Makefile will automatically detect it and compile the module without any further intervention on the part of the user. (See also section 3-10 of th user's manual.)

In addition to this section, sections 2-2 (concerning utility functions) and 2-4 (concerning logging), along with Appendix C (containing a list of core and standard module callbacks), provide useful information for module development. Additionally, the following sections of this manual can be helpful in writing different types of modules:

- Protocol modules: sections 5 and 2-5, and particularly section 5-6-14, which discusses the Unreal protocol module in detail.
- Database modules: sections 6 and 2-9-2.
- Pseudoclient modules: sections 7, 2-6, 2-8, 2-10, and 6-2.

Back to top

Previous section: Communication (socket) handling | Table of Contents | Next section: IRC server interface