C Interface Examples
The following examples provide an introduction to the AOLserver C API. More examples are provided in the "C Examples" appendix on C Examples.
"Hello World" in AOLserver C
The first example is analogous to the Hello World Tcl script example.

Hello World in C:
- In your favorite editor, create the
hello.c file with the following content. (You can also find this example in the examples/c/hello directory.)
#include "ns.h"
static Ns_OpProc Hello;
DllExport int Ns_ModuleVersion = 1;
DllExport int
Ns_ModuleInit(char *hServer, char *hModule)
{
Ns_RegisterRequest(hServer, "GET", "/HelloWorld", Hello,
NULL, NULL, 0);
return NS_OK;
}
static int
Hello(Ns_OpContext context, Ns_Conn *conn)
{
char html[] = "<HTML><BODY>Hello World!</BODY></HTML>";
return Ns_ConnReturnHtml(conn, 200, html, strlen(html));
}
- Compile the module as described in the platform-specific instructions given above.
- Copy the compiled module to the bin subdirectory of the AOLserver directory.
- Enter the following entry to the Modules section of the AOLserver configuration file (normally the nsd.ini file in the AOLserver home directory):
hello=hello.so
- Stop and restart the server. The AOLserver loads the hello.so module on startup and invokes the HelloWorldInit function. The HelloWorldInit procedure registers the Hello function to handle the /HelloWorld URL.
After restarting the AOLserver with the hello.so module, you can open the /HelloWorld module in any browser and receive a short "Hello World" message.
An AOLserver Statistics Module
The following example is a an example C module that maintains statistics on the number of connections and bytes sent by a virtual server. The example exists in the examples/c/stats directory under the AOLserver home directory.
/*
* stat.c - Example C module.
*
* This example incorporates many of the features of the
* AOLserver C API in a single simple, but useful, statistics
* gathering module.
* The module maitains statistics on the number of connections and
* bytes sent by a virtual server. The data is stored in a simple
* 'StatContext' structure which is allocated when the module is
* loaded. The StatContext stores the current, last, and total
* numbers and is updated after each connection by the 'StatTrace'
* connection trace procedure. At a regular interval (the
* interval in seconds is configurable), the 'StatUpdate'
* funcation is called to log the current and total number to the
* AOLserver log file and then makes the current data the last
* data. Access to these data is provided in two forms:
*
* 1. A simple C request function, 'StatRequest', will return the
* current data as a simple notice page. The StatRequest function
* is registered at the GET /NS/Stat URL of the virtual server.
*
* 2. The 'ns_stat' Tcl command is added to each Tcl interpreter
* of the virtual server interpreter pool. This allows a Tcl
* script to access the current, last, or total data at any time.
*
* Because the AOLserver is completely multithreaded, the
* statistics data can be updated and queried simultaneously. An
* Ns_Mutex is included in the ServerContext structure to keep
* multiple threads from accessing the data at the same time.
*
* Finally, on shutdown, the 'StatShutdown' function is called to
* print out a final tally and clean up the ServerContext
* structure.
*
* Note that all the features of this module are specific to the
* virtual server it is loaded into. The server handle,
* 'hServer', is used to makes sure the trace, request, Tcl
* command, and shutdown functions are all run in the context of
* the single virtual server.
* Other virtaul servers may ignore or load the
* stat module as well and the AOLserver guarantees the data
* will be keep separate for each server.
*
*/
#include "ns.h"
#include "nstcl.h"
/*
* The default statistics update interval is 1 minute or 60
* seconds.
*/
#define DEFAULT_INTERVAL 60
/*
* Definitions of the StatContext structure which is allocated
* on a per-virtual server basis.
*/
typedef struct {
int bytes;
int conns;
} StatData;
typedef struct {
Ns_Mutex lock;
int interval;
char *hServer;
StatData current;
StatData last;
StatData total;
} StatContext;
/*
* Forward declarations of functions which will be referenced
* in the stat module initialization function.
*/
static Tcl_CmdProc StatCmd;
static Ns_TclInterpInitProc StatTclInit;
static Ns_TraceProc StatTrace;
static Ns_Callback StatUpdate;
static Ns_Callback StatShutdown;
static Ns_OpProc StatRequest;
/*
* The Ns_ModuleVersion exported integer is used to verify
* this module version when loaded. For AOLserver 2.0,
* 1 (one) is the only valid value for this variable.
*/
DllExport int Ns_ModuleVersion = 1;
/*
* The Ns_ModuleInit function is the function the AOLserver
* will call each time the module is loaded into a virtual
* server. The function is passed two parameters:
*
* hServer: The server 'handle' as a string. This is the
* short name given to the virutal server such
* as 'server1'.
*
* hModule: The module 'handle' as a string. This is the
* short name given to the module such as 'stat'
*
* For example, if this module is known as 'stat' and loaded
* into the 'server1' server with entries similar to the following
* in the nsd.ini file:
*
* [ns/servers]
* server1=My First Server
*
* [ns/server1/modules]
* stat=stat.dll
*
* This function would be called with "server1" and "stat" as
* its arguments.
*
*/
DllExport int
Ns_ModuleInit(char *hServer, char *hModule)
{
char *configPath;
StatContext *ctx;
/*
* Create and initalize the statistics context.
*/
ctx = ns_malloc(sizeof(StatContext));
Ns_InitializeMutex(&ctx->lock);
ctx->hServer = hServer;
ctx->current.bytes = 0;
ctx->current.conns = 0;
ctx->last.bytes = 0;
ctx->last.conns = 0;
ctx->total.bytes = 0;
ctx->total.conns = 0;
/*
* Determine the statistics interval from the config file.
* The Ns_ConfigGetPath function will expand to the
* 'server module specific' configuration section, e.g.,
* [ns/server1/module/stat].
*/
configPath = Ns_ConfigGetPath(hServer, hModule, NULL);
if (!Ns_ConfigGetInt(configPath, "Interval",
&ctx->interval))
{
ctx->interval = DEFAULT_INTERVAL;
}
/*
* Register the trace to accumulate the statistics.
*/
Ns_RegisterServerTrace(hServer, StatTrace, ctx);
/*
* Register the statistics update function to run at
* regular intervals.
*/
Ns_ScheduleProc(StatUpdate, ctx, 0, ctx->interval);
/*
* Add the GET /NS/Stat request function.
*/
Ns_RegisterRequest(hServer, "GET", "/NS/Stat", StatRequest,
NULL, ctx, 0);
/*
* Add the Tcl "ns_stat" command to the interpreter pool.
* Note how the context is passed to StatTclInit which
* then passes it to the Tcl_CreateCommand function.
*/
Ns_TclInitInterps(hServer, StatTclInit, ctx);
/*
* Register the statistics shutdown procedure which
* cleans up the context on server shutdown.
*/
Ns_RegisterServerShutdown(hServer, StatShutdown, ctx);
return NS_OK;
}
/*
* StatTrace is called after each connection and accumulates the
* statistics.
*/
static void
StatTrace(void *ctx, Ns_Conn *conn)
{
StatContext *sc;
int bytes;
sc = (StatContext *) ctx;
Ns_LockMutex(&sc->lock);
bytes = Ns_ConnResponseLength(conn);
sc->current.bytes += bytes;
sc->total.bytes += bytes;
++sc->current.conns;
++sc->total.conns;
Ns_UnlockMutex(&sc->lock);
}
/*
* StatUpdate aggregates the statistics and logs the data to
* the AOLserver server log.
*/
static void
StatUpdate(void *ctx)
{
StatContext *sc;
sc = (StatContext *) ctx;
Ns_LockMutex(&sc->lock);
Ns_Log(Notice, "StatUpdate(%s): Last: conns %d, bytes %d
Total: conns %d, bytes %d",
sc->hServer, sc->current.conns, sc->current.bytes,
sc->total.conns, sc->total.bytes);
sc->last.conns = sc->current.conns;
sc->last.bytes = sc->current.bytes;
sc->current.conns = 0;
sc->current.bytes = 0;
Ns_UnlockMutex(&sc->lock);
}
/*
* StatTclInit is called once for each Tcl interpreter
* in the virutal server's Tcl interpreter pool.
*/
static int
StatTclInit(Tcl_Interp *interp, void *ctx)
{
Tcl_CreateCommand(interp, "ns_stat", StatCmd, ctx, NULL);
return NS_OK;
}
/*
* StatCmd implements the Tcl "ns_stat" command.
*/
static int
StatCmd(ClientData ctx, Tcl_Interp *interp, int argc,
char **argv)
{
StatContext *sc;
StatData *sd;
sc = (StatContext *) ctx;
if (argc != 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " command\"", NULL);
return TCL_ERROR;
}
if (strcmp(argv[1], "current") == 0) {
sd = &sc->current;
} else if (strcmp(argv[1], "last") == 0) {
sd = &sc->last;
} else if (strcmp(argv[1], "total") == 0) {
sd = &sc->total;
} else {
Tcl_AppendResult(interp, "unknown command \"",
argv[1], "\": should be current, last, or total",
NULL);
return TCL_ERROR;
}
Ns_LockMutex(&sc->lock);
sprintf(interp->result, "%d %d", sd->conns, sd->bytes);
Ns_UnlockMutex(&sc->lock);
return TCL_OK;
}
/*
* StatRequest is a simple AOLserver request function which
* returns the current data in a simple HTML page. The page is
* generated with the Ns_ConnReturnNotice() function which is
* used throughout the AOLserver to generate simple HTML page
* responses with the AOLserver banner logo.
* Ns_ConnReturnNotice() takes a string as
* the HTML page content. We use an Ns_DString to quickly build
* up the string - Ns_DString's grow as needed so we don't have
* to worry about buffer overflow. The HTML page is a simple
* HTML3 <TABLE> which formats the current, last, and total
* statistics for the virtual server.
*/
static int
StatRequest(void *ctx, Ns_Conn *conn)
{
StatContext *sc;
Ns_DString ds;
int retcode;
sc = (StatContext *) ctx;
Ns_DStringInit(&ds);
/*
* Build up the HTML3 <TABLE> with the latest data.
*/
Ns_DStringAppend(&ds, "<table border cellpadding=\"10\">");
Ns_DStringVarAppend(&ds, "<tr><th>", sc->hServer, "</th>",
NULL);
Ns_DStringAppend(&ds,
"<th>Current</th><th>Last</th><th>Total</th></tr>");
Ns_LockMutex(&sc->lock);
Ns_DStringPrintf(&ds,
"<tr><th># connections</th><td>%d</td>"
"<td>%d</td><td>%d</td></tr>",
sc->current.conns, sc->last.conns, sc->total.conns);
Ns_DStringPrintf(&ds,
"<tr><th># bytes</th><td>%d</td>"
"<td>%d</td><td>%d</td></tr>",
sc->current.bytes, sc->last.bytes, sc->total.bytes);
Ns_UnlockMutex(&sc->lock);
Ns_DStringAppend(&ds, "</table>");
/*
* Return the HTML page using Ns_ConnReturnNotice().
*/
retcode = Ns_ConnReturnNotice(conn, 200, "Server Statistics",
ds.string);
/*
* Don't forget to free the dstring!
*/
Ns_DStringFree(&ds);
return retcode;
}
/*
* StatShutdown simple prints a final statistics entry to the
* server log and then cleans up the StatContext structure.
*/
static void
StatShutdown(void *ctx)
{
StatContext *sc;
sc = (StatContext *) ctx;
Ns_Log(Notice, "StatShutdown(%s): Total: conns %d,
bytes %d.",
sc->hServer, sc->total.conns, sc->total.bytes);
Ns_DestroyMutex(&sc->lock);
ns_free(ctx);
}