CommandXML is a technology designed to make calling similar commands on different operating systems not only easy but possible without typing ones' fingers to the bone writing error prone platform dependant code or even having to know the differences between commands on different platforms.
CommandXML is actually something of a misnomer, as it only exists as tagged data outside of the Olympus system. Inside the descriptor file used by the server to build its database are blocks of CommandXML associated with individual commands. The syntax for these blocks is definied in Section 4: CommandXML Tags.
When the server processes the descriptorfile it parses the blocks of CommandXML into a more compact and well-formed block of data. This block of data is passed between the Olympus client and server to create CommandXML objects which allows the client and its plugins to create complex command lines in a platform neutral manner.
The specifics of working with a CommandXML object in the client or a plugin is detailed in Section 2: The CommandXML Class. The CommandXML class gives easy access to the options available for that command.
In practical usage, a plugin requests the CommandXML from the server. The server returns the correct block which is turned into a CommandXML class for use by the plugin. The plugin can set the values as desired, preview what the command will look like and check for validity of the command. The plugin then loads an nmExec netmessage with the CommandXML object and which is sent to the server. Once on the server, the CommandXML object is checked for validity and executed.
In general usage, the CommandXML class is the most common part of CommandXML. A plugin rarely, if ever, creates its own CommandXML object. Rather, it receives one from a netmessage reply. The CommandXML class has a very useful copy constructor allowing a plugin to create several copies of the same command to usage later.
Each possible command line option is represented in a CommandXML object by a common name. For
instance, the -a
flag from mount might be called "all" (even if it isn't -a on that
particular system).
All the available options can be retrieved by name from a CommandXML object with the following:
cmdXML.getOptions(value);
.
This returns a const vector
. If you delete any of the items or reassign them
the only thing it will affect is your own ability to interact with object. So don't.
With a CommandXML object in hand, it is simple to set or unset any given option using the operator[] and setValue(). The usage is:
cmdXML[optionName].setValue(value);
This is safe even if the option doesn't actually exist. If the value given is not a legal value,
it is simply discarded.
All options can be set with a bool
, an int
or a
char*
. Depending on how each of these types are interpreted depends on the type of
the option. Here is a chart showing how each is treated:
bool |
int |
char* |
|
---|---|---|---|
Boolean | true = true, false = false | positive = true, <1 = false | "true" = true, "false" or NULL = false |
Scalar | true = "true", false = "false" | integer is translated to char* (e.g. 143 = "143") | used verbatim |
List | true = "true", false = "false" | integer is translated to char* (e.g. 143 = "143") | used verbatim |
Of course, the value is only set if it is a legal value as defined in the XML.
It also helps to know what values are legal on that system. These are defined in the CommandXML data and you can retreive the legal values for any given option with:
cmdXML[optionName].getLegalValues();
This will return a const vector
. Modifying these values will only
affect the local copy and not affect server-side processing. Just as with the options list
it should not be modified.
To preview what the command line would look like if executed at the moment one can call:
cmdXML.commandLine(char*& buffer)();
This will set the char*
passed in to the value of the command line options. This
vairable must either be NULL or point to valid data, otherwise a core-dump will ensue. Any
data that it points to will be deleted.
To make use of CommandXML over the Olympus network, a working knowledge of the Olympus network protocol "netmessage" is needed. The execution of CommandXML is done through the use of the nmExec class.
There are two command IDs specific to CommandXML : COMMAND_FETCHXML and COMMAND_EXECXML. These commandis are really oblivious to the coder, but it is important to understand the separate steps taking place to execute a block of CommandXML on the server.
The first step is to use the nmExec netmessage to request a block of compiled XML from the databse for execution. Using the constructor :
nmExec(transport *socket, uint32 sessionID, const char *_key1, const char *_key2, uint16 _action);
The parameter _action is there only to avoid constructor confusion. A #define
has been created for this purpose : NM_EXEC_FETCHXML.
When the COMMAND_FETCHXML is received back to the client, the first step is complete. We must now create a commandXML object from the pre-compiled XML returned from the method :
const char *nmExec::getCompiledXML();
Once the commandXML object has been created, the object can be modified via the commandXML
class methods. Normally one would use the
setValue(const char *_value)
The last step is sending a second nmExec netmessage (COMMAND_EXECXML) to the server requesting the execution of the commandXML. Using the constructor :
nmExec(transport *socket, uint32 sessionID, const char *_key1, const char *_key2, commandXML *_cmdXML);
The network handles this as it would any other request for execution of a process. A COMMAND_EXECDATA will be received on the client side with the process information. Please see the documentation for the Olympus network protocol, and in particular the nmExec netmessage.
To add a new CommandXML block to the database, one needs first to know how to write CommandXML that the server can understand. You may notice that it isn't perfect XML (e.g. no closing tags), but it does the job while remaining compact. It is also very resilient: writing poorly formed CommandXML blocks, while not recommended, shouldn't hurt much.
There are three types of tags: one for each type of option. The syntax for each is as follows:
The basic boolean tag looks like:
<boolean name="all" prefix="-a " default="false">
The name
element is the name of the option. The prefix
element is
what will be put on the command line if the option is set to true
. One may also
set a default, either true or false.
The scalar tag introduces a few new elements:
<scalar name="special" prefix="-o " values="value1,value2,value3,value4" default="value1" required="true">
name
and default
behave the same as in a boolean
tag. The
prefix
tag works as a prefix to the value set. If no value is set, the prefix is not
passed. The values
tag is a comman seperated list of legal values for that option.
default
defines what the value should be initialized to and required
states
wether or not (true/false) a value must be set for this option for validation purposes.
A list tag looks like:
<list name="options" delim="," prefix="-o " values="exec,ro,rw,users" default="exec" required ="false">
The only difference between the elements in a list
tag and a scalar
tag is the
delim
element which defines what the items in the list should be seperated with on the
command line. In the above case, it is a comma.
One future addition that is planned for CommandXML is the ability to define dependancy rules between the elements. This would allow defining one element as required if another element is set, or not available if another given element is set or available for setting if another element is set... etc...
This will be appearing shortly, so watch CVS!