Dynamic Module Interface Definition



Name

Dynamic Module Interface Definition

Caveats

It is assumed that the reader is familiar with programming in both the Lite / W3-mSQL and C programming languages

Description

This document describes the Dynamic Module Interface (DMI) used by the Lite Virtual Machine (LVM) for extending the functionality of the Lite interpreter at run-time. The LVM supports both statically linked and dynamically loaded modules and provides a call mechanism to allow scripts to access the functionality of the module via standard Lite function calls. The LVM includes two statically linked modules by default. They are the Standard Module (providing string handling, file IO etc) and the mSQL Module (providing full access to the Mini SQL API). Access to any other library of C functions can be made available to a Lite script by loading a dynamic module that provides interface routines for the API of the library.

Several dynamic modules have been developed by Hughes Technologies, including mod_snmp (SNMPv1 functionality), mod_graph (Graphing library based on GD), and mod_proc (access to process related functions). Each module definition provides nothing more than a set of "bounce functions" that, when called, process the parameters passed to the bounce function by the LVM and calls the appropriate underlying C Library function. It must be noted that the C Library functions must be linked into and loaded with the dynamic module if they are not part of a library that has linked into the Lite binary (i.e. the standard C library or libmsql).

Module Definition

A module is comprised of 3 components : the module header, the module implementation, and the optional C library providing the module functionality. The module header is used to inform the LVM of the functions available in the module and the calling requirements of each of the module functions. The module header for mod_proc is included below as an example

/*
**	mod_proc.h - Lite / W3-mSQL module for process related routines
**
**
** Copyright (c) 1996  Hughes Technologies Pty Ltd.
**
** Permission to use, copy, and distribute for non-commercial purposes,
** is hereby granted without fee, providing that the above copyright
** notice appear in all copies and that both the copyright notice and this
** permission notice appear in supporting documentation.
**
** The software may be modified for your own purposes, but modified versions
** may not be distributed.
**
** This software is provided "as is" without any expressed or implied warranty.
**
*/


/*
** External function prototypes
*/

void doFork();
void doExec();
void doPipe();
void doDup2();
void doWait();
void doSignal();

/*
** Note : As this is a dynamic loading module (rather than a static
** linked module) we don't need the function pointer field to be
** completed.  It will be filled at run-time as the module is loaded
** into Lite's process address space.
*/

/*
** Process related module function definitions
**
** This table maps from internal Lite function names to the C code.
** The format is :-
**
**	{LiteName, CFunctPtr, CFunctName, NumArgs, { Arg1Type, Arg2Type.., 0}}
**
**	Setting NumArgs to -1 indicates varargs
*/

efunct_t proc_efuncts[] = {
	{ "fork", NULL, "_doFork", 0, { 0} },
	{ "exec", NULL, "_doExec", 2, { P_TEXT, P_ARRAY,  0} },
	{ "pipe", NULL, "_doPipe", 0, { 0} },
	{ "dup2", NULL, "_doDup2", 2, { P_INT, P_INT, 0} },
	{ "wait", NULL, "_doWait", 1, { P_INT, 0} },
	{ "signal", NULL, "_doSignal", 2, { P_INT, P_TEXT, 0} },
		{ NULL, 0 }
};

 

The header is split into two sections, the prototype section and the interface definition section. The prototype section does nothing more than provide a pre-declaration of the bounce functions that are to be referenced in the interface definition. It is recommended style to name the bounce functions "doSomething" where "Something" is the name of the Lite function name. For example, if the LVM was to provide a fork() function, the bounce function defined in the module would be called doFork(). Once all the module's bounce functions have been pre-declared the module interface can be defined.

The module interface definition is an array of structures where each structure provides the definition for a single function. The structure elements are : the name of the Lite function to be provided by the LVM, a pointer to the C function code of the bounce function, the C symbol name of the bounce function, the number of arguments accepted by the function, and the type definition of those arguments. This structure is used by both the static and dynamic modules of Lite and as such includes information for both styles of modules. Static linked modules require a function pointer as the second element and do not require the symbol name. Dynamic module cannot provide a pointer to the C function as it will be dynamically loaded at run-time. They therefore require the C symbol name to be provided in the definition so that a pointer to the function can be determined during module loading. It is important to remember that the symbol name of a function is prefixed with the '_' (underscore) character. If the interface definition does not include the '_' in the symbol names the functions will not be accessible.

During a scripts function call to a module function, the LVM will use the argument information provided in the interface definition to ensure that the function is being called correctly (i.e. correct number and type of arguments). If the function can take a variable number of arguments, known as varargs, the interface definition for that function stipulates an argument count of -1. In such a case it is the responsibility of the bounce function to check the validity of the arguments passed during the function call. And example of a vararg function is the printf() routine as there can be any number of arguments passed with the format string as output.

The types of the arguments are specified as an array of standard Lite types terminated with a zero. The available types in the mSQL 2.x LVM are

P_REAL

Floating point numbers

P_INT

Integer value

P_TEXT

Character string

P_UINT

Unsigned integer value

P_ARRAY

Array of standard type elements

 

In future versions of the LVM, further types will be provided. The list of available types is defined in the lite.h header file in the mSQL distribution.

 

Module Initialisation

Each module must include an integer function called init_mod(). This function is called by the LVM when the module is loaded. It is the responsibility of the to locate the modules bounce functions in the process's address space and store pointers to the functions in the LVM's jump table. A function is provided within the LVM to perform this task so the module implementer only needs to call the function. A standard init_mod() function is provided below:

int init_mod(modPtr)
	void *modPtr;
{
    if (modLoadModuleFunctions(modPtr, proc_efuncts) < 0)
    {
        return(-1);
    }
    else
    {
        return(0);
    }
}

The modLoadModuleFunctions( ) routines is passed the Module Pointer (the pointer to the dynamically loaded object as passed to init_mod() by the LVM) and the module's interface definition from the module's header file. If the loader cannot install pointers to the modules bounce functions into the LVM's jump table it will return a negative value indicating an error. If the init_mod() function return a negative value, the LVM knows that the module load has failed.

A module need to define macros in the LVM for the script programmer to use. For example, if an implementation of the standard UNIX open() function was to be provided by the module, the script programmer may need access to macros such as O_RDONLY, O_CREAT etc. These macros can be created during the module load by defining the macros in the init_mod() function. The LVM provides functions to create simple type macros in the LVM's symbol table as outlined below:

symCreateIntMacro("Macro Name", MacroValue);
symCreateCharMacro("MacroName", "MacroValue");
symCreateRealMacro("MacroName", MacroValue);

for example

symCreateIntMacro("O_CREAT", O_CREAT);
symCreateCharMacro("SNMP_MODULE_VERSION","mod_snmp_1.2");

As these macros are created as global entries in the LVM's symbol table it is important to provide a unique name for the macro. It is convention to prefix the macro name with an identifier for the module itself. As an example, all macros defined in by the graphing module, mod_graph, are prefixed with HTG_ indicating they are part of the Hughes Technologies Graph module. It is also convention to define macros with upper case names as it is not possible for a script programmer to create a script variable with a capital letter as the first letter of variable name. This greatly reduces the chance of a name conflict.

 

 Argument Handling

Arguments specified in the Lite script's call to the module function are passed to the bounce function after being checked by the LVM against the module function definition. The arguments are passed as a linked list where each list entry contains a pointer to a symbol table element and a pointer to the next list entry. Significant fields within the symbol structure are

char	array;     /* set to 1 if element is an array */
short	type;      /* symbol type */
int	length;    /* length in bytes of the value */
char	*val;      /* pointer to symbol value */

 An implementation of a bounce function for the standard UNIX write() system call could therefore be implemented as

void doWrite(params)
    plist_t  *params;
{
    int    fd,
           length;
    char   *buf;

    fd = (int) * (int*)params->sym->val;
    buf = params->next->sym->val;
    length = (int) * (int *)params->next->next->sym->val;

    write(fd, buf, length);
}

 

You can see from the example that each argument is extracted from the parameter list and assigned to a local variable. For char pointer arguments, the pointer is simply assigned to the local variable. For integer values, the pointer is cast to an int pointer and dereferenced to extract the integer value (which is cast to int just to be pedantic). An array parameter is passed such that the argument's symbol value is a pointer to a symbol table entry holding the array header. In such a case, the sym->val entry should be cast to the sym_t type and the array contents must be accessed using the array access functions shown below.

sym_t * symGetArrayElement(arrayPtr, elementNum);
int     symGetNumArrayElements(arrayPtr);
void    symSetArrayElement(arrayPtr, elementNum, symbolPtr);
sym_t * createArray();

Returning Data

The LVM monitors the value of a globally available sym_t pointer variable called externReturn after a call to a module function. If a value has been assigned to externReturn during the execution of the bounce function it is made available as a return value to the calling script. The script may then choose to assign this value to a variable, us it as an expression value, or ignore it. If the value is ignored, it is destroyed prior to the next module function call. If the calling script accesses a return value from a module function that did not generate a return value, a dummy "blank" value is used as the return value. Several convenience routines are provided by Lite to ease the creation of correctly formatted symbol table entries. These routines should be used to create return values. The available routines and examples of their use from mod_proc are shown below. You may note the use of the setError() function which is a convenience routine used to assign a textual message to the $ERRMSG variable in the global symbol table of the calling script. In this case it is used to assign a standard system error message reflecting the contents of errno.

sym_t * createIntSymbol(intVal);
sym_t * createUintSymbol(uintVal);
sym_t * createRealSymbol(doubleVal);
sym_t * createCharSymbol(charPtr);
sym_t * createArray();


void doFork()
{
    int   pid;

    pid = fork();
    if (pid < 0)
        setError(sys_errlist[errno]);
    externReturn = createIntSymbol(pid);
}

void doPipe()
{
    int   fds[2];
    sym_t *array;

    pipe(fds);
    array = craeteArray();
    symSetArrayElement(array, 0, createIntSymbol(fds[0]));
    symSetArrayElement(array, 1, craeteIntSymbol(fds[1]));
    externReturn = array;
}

 

Summary of Functions and Types

Types

Symbol table entries

sym_t *

Parameter list

plist_t *

Basic Symbol Creation

Create an int symbol

sym_t *createIntSymbol(intVal)

Create a uint symbol

sym_t *createUintSymbol(uintVal)

Create a real symbol

sym_t *createRealSymbol(doubleVal)

Create a char symbol

sym_t *createCharSymbol(charPointer)

Array Handling

Create an empty array

sym_t *createArray()

Set the value of an element

void symSetArrayElement(symPtr, itemNum, symPtr)

Get the value of an element

sym_t *symGetArrayElement(symPtr, itemNum)

Get the number of elements

int symGetNumArrayElements(symPtr)

Misc Functions

Set error message

void setError(charPtr)

Generate runtime error

void runError(charPtr)

 

 

 


Copyright © 2001, Hughes Technologies Pty Ltd. All Rights Reserved.
Last updated 18 Jan 2002.