The goal of this HOWTO is to provide comprehensive documentation of the plugin API provided by the Olympus client as well as to help plugin designers create consistent plugins that take full advantage of the Olympus system.
It is recommended that all plugin developers:
There is a template plugin that can be used as a reference as well as a starting point included in the Olympus source distribution. This template can be found in:
olympus/docs/templates/plugins/
It helps to understand the larger framework of a system when writing
components for it. In this section we look at the design of Olympus in "broad
strokes". Further information can be found in the docs/
directory of the Olympus source distribution.
Olympus is a client/server administration system. The server executes the requests of the client. The client collects information from the server as to the state of the machine and from the user as to what they want to do. These two pieces of software comunicate with each other via a network protocol called netmessage.
This division between client and server is extremely important for three reasons:
The client itself takes care of all the details of the network, encryption and host management. It also launches the individual plugins as needed.
The server maintains a database of the locations of various executables and configuration files, Olympus user accounts and permissions and records of administration activity. In future it will also contain information as directed on other machines running the Olympus server sotware. Paths (and detalis on using) executables and configuration files are keyed in the database according to function. In concert with the Remote File Interface and CommandXML, plugins can use this as a mechanism for executing commands and modifying config files in a platform independant way without losing much (if anything) in the way of system specifics.
The plugins interact with the client and server via a set of four APIs in the client, each of which is covered in detail in the following sections. These APIs cover interaction with the client (plugin superclass), server requests (netmessage), configuration files (remote file interface), and command execution (commandXML).
The plugin
superclass is inherited by all plugins (surprise!).
It allows the plugin to be loaded by client at run-time as well as providing an
API to various client resources such as the network and the plugin registry.
The first step in making a plugin is creating a subclass of
plugin
and including plugin.h
. All subclasses of
plugin have as their class name
mod_<SOMETHING>
mod_admin
, a mod_log
, and a
mod_sysinit
.
The shared library that the plugin is compiled into is required to be named
mod_<SOMETHING>.so
mod_admin
, a mod_log
, and a
mod_sysinit
. Each shared library should contain exactly one plugin
subclass and its support classes. (Note: On Win32, the plugin libraries will
end in .dll rather than .so.)
Within the header file, the plugin needs to register
itself and implement all required virtual methods
of the plugin
superclass.
To be loadable, a plugin must implement one external function to provide the hooks the client requires. This is as simple as including the following in the main source file of the plugins (preferable mod_<SOMETHING>):
extern "C"
{
plugin* launchPlugin() { return new mod_<SOMETHING>; }
}
All plugin subclasses must reimplement eight virtual methods. These methods can be broken out into two different categories: identifcation methods and functional methods.
The identification methods allows the client to peer inside the class and relay vital information to other components and the user. These methods include:
virtual const char *name()
virtual const char *group()
virtual const char *desc()
virtual const char *credits()
virtual const char *version()
const char *
s.
The functional methods are a bit more involved and critical to proper
operation. The first of these methods is:
exec(hostObj *_host, unsigned int _moduleID, moduleList *_modItem)
exec(...)
, only _moduleID
is of long term
importance to the plugin as it is passed to every
netmessage created. The other two items are needed by the client for
various bookkeeping and API reasons. In fact, the first line of the
exec(...)
method should be:
init(_host, _moduleID, _modItem);
When a netmessage is received from the host
for an instance of a plugin, it is passed to the plugin through:
virtual void recv(netmessage* message, int objectID);
olympus/src/common/include/commid.h
#include
in plugin.h. It is possible to tell exactly
what type of netmessage was received. Also note that
once a netmessage is passed to the plugin, the plugin "owns" it and is
responsible for its deletion.
The getFocus()
method is called whenever the plugin needs to
step to the front and grab the GUI focus. This is a simple yet important thing
to take care of so that the plugin is easily findable when open.
In addition to the five informative and three funcational methods, there are three other virtual methods that may be overloaded if desired. These optional methods are:
virtual void connected();
virtual void disconnected();
virtual void messageSent(uint16 messageID, uint16 statusCode, int
percentDone);
messageSent(...)
is called
whenver a netmessage is sent. Included in its parameters is the ID of the
message sent, the statusCode returned from the netmessage and a report on how
much of the message has been sent.
The plugin
superclass also contains an API for plugins to
access the network, get host information, store persistent information and
launch other plugins. (Future embelishments will include easy access to a help
browser system and more robust inter-plugin communication). Each of these
methods along with a description of their purpose is found below:
int clearPendingMessages()
Clears all netmessages sitting in the outgoing network queue.int queueMessage(netmessage*)
Adds a netmessage to the outgoing network message queue. The ID
number given to this specific message is returned. This ID can be
stored for later recognition of replies to this specific message. Once
thie method is called, the netmessage sent should not be modified or
deleted by the plugin.transport* rawSocket()
transport
object associated with the
host to pass to a netmessage constructor. QString hostCommonName()
Returns the name assigned to the host by the user (not necessarily the
host's domain name or IP address). Useful for windows titles, dialog
box text, etc.bool isConnected()
Returns true
if there is an active network connection
to this host, false
otherwise.int launchPlugin(const char *mod_name)
Attempts to launch plugin mod_name
. If unsuccessful,
returns 0. If successful, returns the moduleID of the plugin.bool pluginExists(const char *mod_name)
If the plugin with class name mod_name
exists,
returns true
. Otherwise, returns false
plugin* pluginRunning(int moduleID)
Checks if a plugin is running under moduleID
. If there is
such a plugin returns a pointer to it. Otherwise returns NULL
vector* regGet(const char* key)
Returns all the values stored under key
. If none, a valid but empty
vector is returned. The vector belongs to the plugin, including deletion.
vector* regGet(const char* plugin, const char* key)
Returns all the values stored under key
for plugin
plugin
. If none, a valid but empty vector is returned. The
vector belongs to the plugin, including deletion.void regInsert(const char* key, const char* value)
Inserts value
under key
. Use this to add a value
even if there is already a value assigned to key
.
void regInsert(const char* key, vector* value)
Inserts each valye in value
under key
. Use this
to add a value even if there is already a value assigned to
key
.
void regRemove(const char* key, const char* value = NULL)
If a value is given, removes that value
for that
key
. If no value is given, removes all values for
key
void regSet(const char* key, const char* value)
Sets key
to value
in the registry.
void regSet(const char* key, vector* value)
Sets key
to each value in value
in the registry.
void regUpdate(const char* key, const char* value, const char* newValue)
Removes the value assocatied with key
to
newValue
QPixmap largeIcon(QString*)
Returns the iconName
pixmap from the
largeIcon
collection.
int makeTempFile(QString& newFileName)
If a temporary file is needed, use this method. It (securely) creates a
temporary file and puts it in the proper place in the file system (user
defined). It places the name of the new file in newFileName
(removing any pre-existing data in it) and returns a file handle. Once
created, the plugin must close the filehandle and delete the file. QPixmap smallIcon(QString* iconName)
Returns the iconName
pixmap from the
smallIcon
collection.
All communications between the Olympus client and server are done via
netmessages. All netmessages are subclasses of the
netmessage
superclass. This superclass takes care of
serializing and deserializing objects, sending them across the network and
keeping record of how things are going. Each subclass of netmessage does one
specific (although usually fairly broad) thing such as execute programs, get
listings of files, fetch files, etc.
Each netmessage takes a transport
object which takes care of the
low level network activity and can be retreived via
plugin::rawSocket()
, the moduleID of the plugin which acts as a
return address on the message and an object ID. Each netmessage has
a unique object ID as defined in:
olympus/src/common/include/commid.h
When created, each netmessage is stamped with a message identification number. This gives each netmessage a four part fingerprint that is used to route the message: host, object ID, module ID, and message ID.
To send a netmessage to the server a plugin needs only to call
uint32 plugin::queueMessage(netmessage*)
which takes care of all network
details from there, including:
ququMessage(...)
returns the ID number that has been assigned to that
message. Each ID is guarenteed to be unique (even in the case of a roll-over due to
exhausting all four billion plus available IDs). All replies to this netmessage will
have this same messageID. Therefore, to track which reply belongs to which message
sent all a plugin has to do is keep track of these messageIDs.
Replies are routed to the plugin by means of the pure virtual
plugin::recv(netessage *message, int objectID)
. Once handed off
to the plugin, the netmessage is now owned by the plugin which must take
care of deleting it when finished with it.
For information on the specific netmessages and further details on the inner
working of the entire protocol, refer to the netmessage documentation found in
the olympus/docs/
directory.
The Remote File Interface, or RFI, is an extremely powerful
mechanism to manipulate remote configuration files programatically. An RFI
object takes the name of a .rif
file, fetches the appropriate
remote file, parses it, allows editing, and (if instructed to) puts the file
back together again and commits it to the remote machine (with backup for
rollback capabilities).
.rfi
files are an XML implementation designed specifically for
describing data structures in text configuration files (yes, those files we have
come to know and love in the Linux/UNIX world). The remote file is defined in a
platform independant manner in this file, though this can be over-ridden at
run-time.
Once retrieved and parsed, the file can be treated as a data structure in
memory complete with []
and =
operators. Some of the
important features of RFI are:
.rfi
files, so any plugin can
parse any system file for which one is defined
For more detailed information on using the RFI and writing .rfi
files, refer to the RFI documentation found in the olympus/docs/
directory.
CommandXML is a concept that will allow platform independant complex comand execution. CommandXML is only a fig-newton of our fertile imaginations. The design of this technology is pretty well done. Wait a week or two and the code should be ready as well.
Graphical interface standards are still being formed and decided upon. For now, use common sense and make something pretty, easy to master, powerful and fun. You know, just simply whip up a power-interface. Seriously though, more concepts for standard methods of doing things will find themselves appearing here over the next few months.
The Olympus client and server are released under the GNU GPL version 2 as included with the official Olympus source distribution. The only exception to this is the header for the plugin superclass, which is found at
olympus/src/client/include/plugin.h
The Mount Linux Olympus development team feels that while the GPL is the optimal license for the Olympus system itself, that due to the opinions and/or needs of plugin authors the GPL may either be undesirable or unworkable. Therefore a plugin may be licensed as the author sees fit. This includes closed source plugins, plugins under Artistic or BSD style licenses, etc.
However, only plugins copyrighted under a license that is compliant with the Debian Free Software Guidelines will be considered for inclusion within the official Olympus distribution.