1.2/Writing Modules For InspIRCd
From the makers of InspIRCd.
| 1.1 Documentation | 1.2 Documentation | 2.0 Documentation | 2.1 Documentation |
Contents |
A 30-Minute Module Tutorial
This tutorial will assume you have at least intermediate knowledge of C++. For example, before attempting to write InspIRCd modules you should have at least a basic understanding of the following, or some of the basic concepts will be difficult to grasp:
- What a inheritance is in relation to C++ classes
- What a constructor and destructor are in C++ and how they work
- What polymorphism is in relation to C++ classes
- The C++ standard template library (STL)
There are probably more things to consider, but so long as you're aware of these basics, you will be fine. If you are writing a module which you wish to make available to others, you should probably check it against our STL FAQ, which acts as a checklist to weed out un-scalable code.
This tutorial is intended for InspIRCd 1.2.x. These instructions WILL NOT work with InspIRCd 1.1 or below due to major API changes.
Structure of an InspIRCd module
The Module Entrypoint
An InspIRCd module at its most basic is a single class within a shared object or dynamic link library (.so) file. When a module is loaded the core will import the symbol 'init_module' from your module, and attempt to call it. Upon being called, the function should return a pointer to an object inherited from class Module. A macro called MODULE_INIT() defines this entrypoint to your module for you, and does a lot of initial work (read: saves you typing) on your behalf. So, to sum this up, our module currently looks like this (although it will not compile yet!):
#include "inspircd.h" MODULE_INIT(MyModule)
The Module Class
The more astute amongst you will already know what is going to be added next: We are going to add the definition of the MyModule class to the module file. All modules in InspIRCd inherit from a base class Module that in itself inherits from classbase. Once we have added our definition, our code will look like the one below. It is advisable that every module's class name be named differently.
#include "inspircd.h"
class MyModule : public Module
{
MyModule(InspIRCd* Me) : Module(Me)
{
}
virtual ~MyModule()
{
}
virtual Version GetVersion()
{
return Version("$Id$", 0, API_VERSION);
}
};
MODULE_INIT(MyModule)
Now, we have what can be classed a working module. InspIRCd can load this module, and unload this module. A few lines within this code require further clarification:
virtual Version GetVersion()
{
return Version("$Id$", 0, API_VERSION);
}
This section of code informs the core what version number our module is. This will be displayed in the output of /MODULES. The string is a version number (usually an SVN or CVS ID but you may place any text you want here) and the next number is actually a set of flags. You must use the | operator to logically OR the flags together into a bitmask. Available flags are:
| Flag name | Meaning |
|---|---|
| VF_STATIC | The module is static, cannot be unloaded by /UNLOADMODULE or a rehash. In 1.2 series InspIRCd you should avoid using this unless absolutely neccessary, as setting this flag hinders hot-patching. |
| VF_VENDOR | This flag indicates that the module is part of the original distribution. You should never set this flag in a third-party module, although nothing prevents you doing so. |
| VF_SERVICEPROVIDER | This module provides a service to other modules which are usually accessible via the Event and Request subsystem. |
| VF_COMMON | This module needs to be common on all servers to link - You should set this if your module implements a mode letter. |
The final parameter must always be set to API_VERSION. This is to allow for binary versioning, and if the value does not match that defined in the core, your module will not load. It is not often that this value is changed by the InspIRCd development team, however when it is this indicates a breaking change which would prevent the module from being functional in any useful manner.
From examples to working code
Making the module do something
Most of you may have realised that the example module we have just created can't actually DO anything yet! In that case, what we have is useless until we add some code to it so that it can handle events from the core. Your next question therefore is most likely 'So how do we do this?'
Here's how. You must add methods to your Module class, of which a complete list may be found on the developer and module documentation page.
For purpose of this tutorial, we will just add a handler which logs all joins and parts. Note that to do this, we will have to make use of another class, which represents the server itself. This class, surprisingly enough, is called InspIRCd. When your module is created, a pointer to our server (via the InspIRCd class) is passed to our module:
#include "inspircd.h"
class MyModule : public Module
{
public:
MyModule(InspIRCd* Me) : Module(Me)
{
// Note: Calling the parent constructor copies the 'Me'
// pointer into Module::ServerInstance for us.
Implementation eventlist[] = { I_OnUserJoin, I_OnUserPart };
ServerInstance->Modules->Attach(eventlist, this, 2);
}
virtual ~MyModule()
{
}
virtual Version GetVersion()
{
return Version("$Id$", 0, API_VERSION);
}
virtual void OnUserJoin(User* user, Channel* channel, bool &silent)
{
// method called when a user joins a channel
ServerInstance->Logs->Log("mymodule", DEBUG, "User " + user->nick + " joined " + channel->name);
}
virtual void OnUserPart(User* user, Channel* channel, const std::string &partmessage, bool &silent)
{
// method called when a user parts a channel
ServerInstance->Logs->Log("mymodule", DEBUG, "User " + user->nick + " parted " + channel->name);
}
};
MODULE_INIT(MyModule)
Please note that when you should not create objects of type 'InspIRCd' yourself! This would infer that you are creating a server, which is why one is already passed to you from the core - a server already exists, and your module is getting a pointer to it. If you were to instantiate a class of type 'InspIRCd', you would actually create a whole new instance of the irc server within the process!
Structures and classes of note
There are several classes of note within this module which are useful to be aware of when developing InspIRCd modules. Amongst others, you will see the classes User and Channel (defined in include/users.h and include/channels.h respectively) which define users and channels in the ircd (also defined is a captain obvious class which makes obvious statements -- just kidding!). It is recommended you read through these headers for more details, as there is a lot of information contained within them which is useful for prospective module authors! You should also read include/modules.h for information on what types of event triggers are available to a module, what they do, what parameters they take, and what they should return (if anything).
Attaching event triggers to a module
The Attach() method of the ModuleManager class is used to attach triggers to events within your module. By doing this, you inform the core that your module wishes to receive notification of these events. This is used by the core to speed up calling multiple modules, and make the module system more efficient. You may attach and detatch (with the Detatch() method) events at any time, however it is normal to do this in the constructor.
For each method you wish to activate, you should pass its enumeration value to Attach() as shown above. For example:
Implementation eventlist[] = { I_OnUserJoin, I_OnUserPart };
ServerInstance->Modules->Attach(eventlist, this, 2);
This would enable both the OnUserJoin and OnUserPart events, enabling the methods OnUserJoin and OnUserPart to be triggered. If you do not enable a method, or you use Detach() on it, it will not be called. The third parameter is optional, and indicates the number of events in the eventlist array you wish to attach or detach. If you do not specify this parameter, you should pass one event ID only, for example:
ServerInstance->Modules->Attach(I_OnUserJoin, this);
The Special Comments
InspIRCd supports 'special comments'. These are comments which are parsed by ./configure to determine dependencies, include paths and other information from your module's C++ source code. This prevents the need for you to distribute your own configure scripts and/or makefiles with your modules, and makes things simpler for the user.
These special comments must be used exactly in the format shown, all on one line, and spacing must be preserved at all times.
ModDesc
/* $ModDesc: Description here */
Used to describe what your module does.
CompileFlags
/* $CompileFlags: <gcc flags> */
Flags to be added to the gcc build line in the makefile, e.g. include paths. This is ignored on windows builds.
LinkerFlags
/* $LinkerFlags: <ld flags> */
Flags to be added to the gcc link line in the makefile, e.g. library paths. This is ignored on windows builds, where you should use the Microsoft Visual C++ #pragma directive to include a library instead:
#ifdef WINDOWS #pragma comment(lib, "libgnutls-13.lib") #endif
ModDep
/* $ModDep: <files> */
Lists one or more files (seperated by spaces) which your module depends upon to be up to date before it can build. For example if you place foobar.h here, any changes to foobar.h also cause your module to be rebuilt. It is usual to only list headers here, not .cpp files. This is ignored on windows builds.
How to build your module
- The first time you build the module, re-run configure with the -modupdate switch:
brain@brainwave ~/inspircd $ ./configure -modupdate Detecting modules ........................................................................................................ Ok, 153 modules. Updating files... Writing Makefiles Writing src/modules/Makefile Executing program for module m_mysql.cpp ... mysql_config --include Executing program for module m_mysql.cpp ... mysql_config --libs_r Adding extra library path to m_mysql.cpp ... /usr/lib64/mysql Adding extra library path to m_mysql.cpp ... /usr/lib64 Executing program for module m_pgsql.cpp ... pg_config --includedir Evaluating perl code for module m_pgsql.cpp ... (Created and executed /tmp/fileFOrFhk) Executing program for module m_pgsql.cpp ... pg_config --libdir Executing program for module m_regex_pcre.cpp ... pcre-config --cflags Executing program for module m_regex_pcre.cpp ... pcre-config --libs Locating include directory for package tre for module m_regex_tre.cpp... -DVERSION_TRE="0.7.5" (cached) Locating library directory for package tre for module m_regex_tre.cpp... -Wl,-O1 -ltre (cached) Executing program for module m_ssl_gnutls.cpp ... libgnutls-config --cflags Executing program for module m_ssl_gnutls.cpp ... libgnutls-config --libs Checking version of package openssl is >= 0.9.7... Yes (version 0.9.8h) Locating include directory for package openssl for module m_ssl_openssl.cpp... -DVERSION_OPENSSL="0.9.8h" (cached) Locating library directory for package openssl for module m_ssl_openssl.cpp... -lssl -lcrypto -ldl (cached) Composing Makefile rules for directory m_spanningtree... (53 files found) Scanning src for core files .................................. done! Scanning src/commands for core files ..................................................... done! Scanning src/modes for core files ................ done! Scanning src/socketengines for core files ...... done! Scanning src/modules for core files .......................................................... done! Writing Makefile ... Writing inspircd ... Writing cache file for future ./configures ... Complete.
- Re-run make from the top of the InspIRCd source tree.
- You may now either load your module into a running InspIRCd with /LOADMODULE or /REHASH, or start up your IRC server with the module configured in its Configuration file.
Where to find more help
You may find more help either by checking our Programmer Documentation, or by asking the devleopers directly on the IRC channel. If you choose to ask directly remember that we are only human (yes, really...) and that we can't be awake 24 hours a day, 7 days a week and that we do have lives, and jobs to go to.
There is also a Forum where you may post and expect a slower, but more in-depth reply to most questions.

















