--------------------------------------- Licq Plugins Howto v0.2 Graham Roff --------------------------------------- This document is not nearly complete and is only intended to give a bried introduction to the main ideas behind the licq plugin system. Careful perusal of the licq and qt-gui plugin source code is a good place to find out more. 1.0 Introduction (IPC and Initialization) Writing a plugin for licq is not too complicated. There are two main ways for inter-system communication, signals and events. Signals are asynchronous and indicate some general change or occurance which the plugin might be interested in being aware of. Signals include when your status changes, when a new message arrives, and when you log on successfully. Events are synchronous to the extent that they are a response to something which the particular plugin started. For example the plugin might call icqSendUrl() to send a url, and will receive a notification event in response indicating the success of failure of this call. The arrival of a signal or event is indicated by activity on a pipe, specific to each plugin. Each character read from the pipe can be any of PLUGIN_SIGNAL, PLUGIN_EVENT, or PLUGIN_SHUTDOWN. A signal or event is read from the daemon simply by calling PopPluginSignal() or PopPluginEvent(). There are three main things each plugin must do: 1. Each plugin must include plugin.h and implement the functions prototyped within. These functions are described inside this header file. The main ones are: LP_Init(int, char **): This function is called to initialize the plugin, and is passed any command line arguments applicable. The function should return whether or not it was successful in initializing the plugin. LP_Main(CICQDaemon *): This function is called within its own thread and should actually run the plugin. It can exit by calling LP_Exit(int) or by returning an integer return code. 2. Each plugin must register with the licq daemon it gets passed in LP_Main(). This is done simply by calling CICQDaemon::Register(). This function returns the descriptor of the pipe to listen on for notification of signals and events. If a plugin exits without either calling CICQDaemon::Shutdown() or receiving a shutdown signal then it must call CICQDaemon::Unregister() to unregister itself. 3. Each plugin must exit properly when it receives a PLUGIN_SHUTDOWN character on the notification pipe. This simply involves calling LP_Exit() or returning from LP_Main(). 2.0 Multi-threading Issues Licq is a fully multi-threaded program and as such there are some important issues to be dealt with when writing plugins. First, any requests to get user information have been made thread safe (henceforth refered to as mt-safe) through a simple database type checkout system. Given a uin, a pointer to the relevant user is obtained by calling gUserManager.FetchUser(, ) where the is either LOCK_R or LOCK_W depending on whether one wants read or read/write access to the user (note that many threads may have a user locked for reading at the same time, but only one thread may have a user locked for writing, so avoid doing so if possible). The same idea applies to groups: gUserManager.FetchGroup(, ). Certain system calls should also be avoided or their mt-safe counterparts used. These include: strerror (on some systems this call is mt-safe, use strerror_r to be sure) gethostbyname (use gethostbyname_r, see socket.cpp for an example) and any other call that uses an internal static buffer.