blob: 542ea31a1d1812a4506a712480fb830639a740e5 [file] [log] [blame]
/*
* interpool.c -- Interpreter Pool Manager for Apache Module
* nca-073-9
*
* Copyright (c) 1996-2000 by Netcetera AG.
* Copyright (c) 2001 by Apache Software Foundation.
* All rights reserved.
*
* See the file "license.terms" for information on usage and
* redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* @(#) $Id$
*
*/
#include "hashutl.h"
#include "interpool.h"
#include "log.h"
#include "log.h"
#include "logtoap.h"
#include "macros.h"
#include "modwebsh.h"
#include "tcl.h"
#include "webutl.h"
#include "web.h"
#include "mod_websh.h"
#include "request.h"
#include <time.h>
#include <sys/stat.h>
#ifndef WIN32
#include <unistd.h>
#else
/* define access mode constants if they are not defined yet
if we're under Windows (they are defined in unistd.h, which doesn't
exist under Windows) currently only R_OK is used.
*/
#ifndef R_OK
#define R_OK 4
#endif
#endif
/* init script for main interpreter */
#define MAININTERP_INITCODE "proc web::interpmap {filename} {return $filename}"
/* ----------------------------------------------------------------------------
* createWebInterpClass
* ------------------------------------------------------------------------- */
WebInterpClass *createWebInterpClass(websh_server_conf * conf, char *filename,
long mtime)
{
WebInterpClass *webInterpClass = WebAllocInternalData(WebInterpClass);
if (webInterpClass != NULL) {
webInterpClass->conf = conf;
webInterpClass->filename = allocAndSet(filename);
webInterpClass->maxrequests = 1L;
webInterpClass->maxttl = 0L;
webInterpClass->maxidletime = 0L;
webInterpClass->mtime = mtime;
webInterpClass->nextid = 0;
webInterpClass->first = NULL;
webInterpClass->last = NULL;
webInterpClass->code = NULL; /* will be loaded on demand by first interp */
}
return webInterpClass;
}
/* ----------------------------------------------------------------------------
* destroyWebInterpClass
* ------------------------------------------------------------------------- */
int destroyWebInterpClass(WebInterpClass * webInterpClass)
{
if (webInterpClass == NULL)
return TCL_ERROR;
while ((webInterpClass->first) != NULL) {
destroyWebInterp(webInterpClass->first);
}
if (webInterpClass->code != NULL) {
Tcl_DecrRefCount(webInterpClass->code);
}
Tcl_Free(webInterpClass->filename);
Tcl_Free((char *) webInterpClass);
return TCL_OK;
}
/* ----------------------------------------------------------------------------
* createWebInterp
* ------------------------------------------------------------------------- */
WebInterp *createWebInterp(websh_server_conf * conf,
WebInterpClass * webInterpClass, char *filename,
long mtime, request_rec *r)
{
LogPlugIn *logtoap = NULL;
Tcl_Obj *code = NULL;
ApFuncs *apFuncs = NULL;
WebInterp *webInterp = (WebInterp *) Tcl_Alloc(sizeof(WebInterp));
webInterp->interp = Tcl_CreateInterp();
if (webInterp->interp == NULL) {
Tcl_Free((char *) webInterp);
#ifndef APACHE2
ap_log_printf(conf->server, "createWebInterp: Could not create interpreter (id %ld, class %s)", webInterpClass->nextid, filename);
#else /* APACHE2 */
ap_log_error(APLOG_MARK, APLOG_ERR, 0, conf->server,
"createWebInterp: Could not create interpreter (id %ld, class %s)", webInterpClass->nextid, filename);
#endif /* APACHE2 */
return NULL;
}
/* just to be sure the memory command is imported if
the corresponding Tcl features it */
#ifdef TCL_MEM_DEBUG
Tcl_InitMemory(webInterp->interp);
#endif
/* now register here all websh modules */
Tcl_Init(webInterp->interp);
apFuncs = Tcl_GetAssocData(conf->mainInterp, WEB_APFUNCS_ASSOC_DATA, NULL);
if (apFuncs == NULL)
return NULL;
Tcl_SetAssocData(webInterp->interp, WEB_APFUNCS_ASSOC_DATA, NULL, (ClientData *) apFuncs);
Websh_Init(webInterp->interp);
/* also register the destrcutor, etc. functions, passing webInterp as
client data */
/* ------------------------------------------------------------------------
* register log handler "apachelog"
* --------------------------------------------------------------------- */
logtoap = createLogPlugIn();
if (logtoap == NULL)
return NULL;
logtoap->constructor = createLogToAp;
logtoap->destructor = destroyLogToAp;
logtoap->handler = (int (*)(Tcl_Interp *, ClientData, char *)) logToAp;
registerLogPlugIn(webInterp->interp, APCHANNEL, logtoap);
/* ------------------------------------------------------------------------
* create commands
* --------------------------------------------------------------------- */
Tcl_CreateObjCommand(webInterp->interp, "web::initializer",
Web_Initializer, (ClientData) webInterp, NULL);
Tcl_CreateObjCommand(webInterp->interp, "web::finalizer",
Web_Finalizer, (ClientData) webInterp, NULL);
Tcl_CreateObjCommand(webInterp->interp, "web::finalize",
Web_Finalize, (ClientData) webInterp, NULL);
Tcl_CreateObjCommand(webInterp->interp, "web::maineval",
Web_MainEval, (ClientData) webInterp, NULL);
Tcl_CreateObjCommand(webInterp->interp, "web::interpcfg",
Web_InterpCfg, (ClientData) webInterp, NULL);
Tcl_CreateObjCommand(webInterp->interp, "web::interpclasscfg",
Web_InterpClassCfg, (ClientData) conf, NULL);
/* ------------------------------------------------------------------------
* rename exit !
* --------------------------------------------------------------------- */
code =
Tcl_NewStringObj
("rename exit exit.apache; proc exit {} {if {[info tclversion] >= 8.5} {return -level [expr {[info level] + 1}]} else {return -code error \"cannot exit script in mod_websh because process would terminate (use Tcl 8.5 or later if you want to use exit)\"}}", -1);
Tcl_IncrRefCount(code);
Tcl_EvalObjEx(webInterp->interp, code, 0);
Tcl_DecrRefCount(code);
Tcl_ResetResult(webInterp->interp);
webInterp->dtor = NULL;
webInterp->state = WIP_FREE;
webInterp->numrequests = 0;
webInterp->starttime = (long) r->request_time;
webInterp->lastusedtime = (long) r->request_time;
webInterp->interpClass = webInterpClass;
webInterp->id = webInterpClass->nextid++;
/* add to beginning of list of webInterpClass */
webInterp->next = webInterpClass->first;
if (webInterp->next != NULL)
webInterp->next->prev = webInterp;
webInterpClass->first = webInterp;
webInterp->prev = NULL;
if (webInterpClass->last == NULL)
webInterpClass->last = webInterp;
if (webInterpClass->code != NULL) {
/* copy code from class */
webInterp->code = Tcl_DuplicateObj(webInterpClass->code);
Tcl_IncrRefCount(webInterp->code);
}
else {
/* load the code into the object */
if (readWebInterpCode(webInterp, filename) == TCL_OK) {
/* copy code to class */
webInterpClass->code = Tcl_DuplicateObj(webInterp->code);
Tcl_IncrRefCount(webInterpClass->code);
webInterpClass->mtime = mtime;
}
else {
webInterp->code = NULL;
#ifndef APACHE2
ap_log_printf(r->server,
"Could not readWebInterpCode (id %ld, class %s): %s",
webInterp->id, filename, Tcl_GetStringResult(webInterp->interp));
#else /* APACHE2 */
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
"Could not readWebInterpCode (id %ld, class %s): %s",
webInterp->id, filename, Tcl_GetStringResult(webInterp->interp));
#endif /* APACHE2 */
}
}
return webInterp;
}
/* ----------------------------------------------------------------------------
* destroyWebInterp
* ------------------------------------------------------------------------- */
void destroyWebInterp(WebInterp * webInterp)
{
request_rec *r;
if (webInterp->dtor != NULL) {
int result;
result = Tcl_Eval(webInterp->interp, "web::finalize");
if (result != TCL_OK) {
r = (request_rec *) Tcl_GetAssocData(webInterp->interp, WEB_AP_ASSOC_DATA, NULL);
#ifndef APACHE2
ap_log_printf(r->server,
"web::finalize error: %s",
Tcl_GetStringResult(webInterp->interp));
#else /* APACHE2 */
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
"web::finalize error: %s",
Tcl_GetStringResult(webInterp->interp));
#endif /* APACHE2 */
}
Tcl_ResetResult(webInterp->interp);
}
/* --------------------------------------------------------------------------
* cleanup code cache
* ----------------------------------------------------------------------- */
if (webInterp->dtor) {
Tcl_DecrRefCount(webInterp->dtor);
/* webInterp->dtor = NULL; */
}
if (webInterp->code) {
Tcl_DecrRefCount(webInterp->code);
webInterp->code = NULL;
}
Tcl_DeleteInterp(webInterp->interp);
/* --------------------------------------------------------------------------
* fixup list linkage
* ----------------------------------------------------------------------- */
if (webInterp->prev != NULL)
/* we are not the first */
webInterp->prev->next = webInterp->next;
else
webInterp->interpClass->first = webInterp->next;
if (webInterp->next != NULL)
/* we are not the last */
webInterp->next->prev = webInterp->prev;
else
webInterp->interpClass->last = webInterp->prev;
Tcl_Free((char *) webInterp);
}
/* ----------------------------------------------------------------------------
* poolGetWebInterp - request new interp from pool
* ------------------------------------------------------------------------- */
WebInterp *poolGetWebInterp(websh_server_conf * conf, char *filename,
long mtime, request_rec * r)
{
Tcl_HashEntry *entry = NULL;
WebInterp *found = NULL;
WebInterpClass *webInterpClass = NULL;
char *id = NULL;
Tcl_Obj *idObj = NULL;
Tcl_Obj *mapCmd = NULL;
Tcl_Obj *cmdList[2];
int res;
/* get interpreter id for filename */
Tcl_MutexLock(&(conf->mainInterpLock));
cmdList[0] = Tcl_NewStringObj("web::interpmap", -1);
cmdList[1] = Tcl_NewStringObj(filename, -1);
Tcl_IncrRefCount(cmdList[0]);
Tcl_IncrRefCount(cmdList[1]);
mapCmd = Tcl_NewListObj(2, cmdList);
Tcl_IncrRefCount(mapCmd);
res = Tcl_EvalObjEx(conf->mainInterp, mapCmd, 0);
Tcl_DecrRefCount(mapCmd);
Tcl_DecrRefCount(cmdList[0]);
Tcl_DecrRefCount(cmdList[1]);
if (res != TCL_OK) {
/* no valid id for filename */
Tcl_MutexUnlock(&(conf->mainInterpLock));
#ifndef APACHE2
ap_log_printf(r->server,
"web::interpmap: %s",
Tcl_GetStringResult(conf->mainInterp));
#else /* APACHE2 */
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
"web::interpmap: %s",
Tcl_GetStringResult(conf->mainInterp));
#endif /* APACHE2 */
return NULL;
}
idObj = Tcl_DuplicateObj(Tcl_GetObjResult(conf->mainInterp));
Tcl_IncrRefCount(idObj);
Tcl_ResetResult(conf->mainInterp);
/* get absolute filename (if already absolute, same as
Tcl_GetString(idObj) -> no DecrRefCount yet) */
id = (char *) ap_server_root_relative(r->pool, Tcl_GetString(idObj));
/* get last modified time for id */
if (strcmp(id, filename)) {
struct stat statPtr;
if (Tcl_Access(id, R_OK) != 0 ||
Tcl_Stat(id, &statPtr) != TCL_OK)
{
Tcl_MutexUnlock(&(conf->mainInterpLock));
#ifndef APACHE2
ap_log_printf(r->server,
"cannot access or stat webInterpClass file '%s'", id);
#else /* APACHE2 */
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
"cannot access or stat webInterpClass file '%s'", id);
#endif /* APACHE2 */
Tcl_DecrRefCount(idObj);
return NULL;
}
mtime = statPtr.st_mtime;
}
Tcl_MutexUnlock(&(conf->mainInterpLock));
Tcl_MutexLock(&(conf->webshPoolLock));
/* see if we have that id */
entry = Tcl_FindHashEntry(conf->webshPool, id);
if (entry != NULL) {
/* yes, we have such a class */
WebInterp *webInterp = NULL;
webInterpClass = (WebInterpClass *) Tcl_GetHashValue(entry);
/* check if mtime is ok */
if (mtime > webInterpClass->mtime) {
/* invalidate all interpreters, code must be loaded from scratch */
webInterp = webInterpClass->first;
while (webInterp != NULL) {
logToAp(webInterp->interp, NULL,
"interpreter expired: source changed (id %ld, class %s)", webInterp->id, webInterp->interpClass->filename);
if (webInterp->state == WIP_INUSE)
webInterp->state = WIP_EXPIREDINUSE;
else
webInterp->state = WIP_EXPIRED;
webInterp = webInterp->next;
}
/* free code (will be loaded on demand) */
if (webInterpClass->code) {
Tcl_DecrRefCount(webInterpClass->code);
webInterpClass->code = NULL;
}
}
/* search a free interp */
webInterp = webInterpClass->first;
while (webInterp != NULL) {
if ((webInterp->state) == WIP_FREE) {
if (webInterpClass->maxidletime
&& (r->request_time - webInterp->lastusedtime) >
webInterpClass->maxidletime) {
logToAp(webInterp->interp, NULL,
"interpreter expired: idle time reached (id %ld, claa %s)", webInterp->id, webInterp->interpClass->filename);
webInterp->state = WIP_EXPIRED;
}
else {
if (webInterpClass->maxttl
&& (r->request_time - webInterp->starttime) >
webInterpClass->maxttl) {
logToAp(webInterp->interp, NULL,
"interpreter expired: time to live reached (id %ld, class %s)", webInterp->id, webInterp->interpClass->filename);
webInterp->state = WIP_EXPIRED;
}
else {
found = webInterp;
break;
}
}
}
webInterp = webInterp->next;
}
}
else {
/* no, we have to create this new interpreter class */
webInterpClass = createWebInterpClass(conf, id, mtime);
if (webInterpClass != NULL) {
int isnew = 0;
entry = Tcl_CreateHashEntry(conf->webshPool, id, &isnew);
/* isnew must be 1, since we searched already */
Tcl_SetHashValue(entry, (ClientData) webInterpClass);
}
else {
Tcl_MutexUnlock(&(conf->webshPoolLock));
#ifndef APACHE2
ap_log_printf(conf->server,
"cannot create webInterpClass '%s'", id);
#else /* APACHE2 */
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, conf->server,
"cannot create webInterpClass '%s'", id);
#endif /* APACHE2 */
Tcl_DecrRefCount(idObj);
return NULL;
}
}
if (found == NULL) {
/* we have to create one */
found = createWebInterp(conf, webInterpClass, id, mtime, r);
}
if (found != NULL) {
/* mark the found one as INUSE */
found->state = WIP_INUSE;
}
Tcl_MutexUnlock(&(conf->webshPoolLock));
Tcl_DecrRefCount(idObj);
return found;
}
/* ----------------------------------------------------------------------------
* poolReleaseWebInterp - bring intrp back into pool
* ------------------------------------------------------------------------- */
void poolReleaseWebInterp(WebInterp * webInterp)
{
if (webInterp != NULL) {
WebInterpClass *webInterpClass = webInterp->interpClass;
Tcl_MutexLock(&(webInterpClass->conf->webshPoolLock));
webInterp->lastusedtime = (long) time(NULL);
webInterp->numrequests++;
if (webInterp->state == WIP_EXPIREDINUSE)
webInterp->state = WIP_EXPIRED;
else {
webInterp->state = WIP_FREE;
/* check for num requests */
if (webInterpClass->maxrequests && (webInterp->numrequests >= webInterpClass->maxrequests)) {
logToAp(webInterp->interp, NULL,
"interpreter expired: request count reached (id %ld, class %s)", webInterp->id, webInterp->interpClass->filename);
webInterp->state = WIP_EXPIRED;
}
}
/* cleanup all EXPIRED interps */
cleanupPool(webInterpClass->conf);
Tcl_MutexUnlock(&(webInterpClass->conf->webshPoolLock));
}
}
/* ----------------------------------------------------------------------------
* init
* ------------------------------------------------------------------------- */
int initPool(websh_server_conf * conf)
{
Tcl_FindExecutable(NULL);
if (conf->mainInterp != NULL || conf->webshPool != NULL) {
/* we have to cleanup */
#ifndef APACHE2
ap_log_printf(conf->server, "initPool: mainInterp or webshPool not NULL");
#else /* APACHE2 */
ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, conf->server,
"initPool: mainInterp or webshPool not NULL");
#endif /* APACHE2 */
return 0;
}
/* create a single main interpreter */
conf->mainInterp = createMainInterp(conf);
if (conf->mainInterp == NULL) {
errno = 0;
#ifndef APACHE2
ap_log_printf(conf->server, "could'nt create main interp");
#else /* APACHE2 */
ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, conf->server,
"could'nt create main interp");
#endif /* APACHE2 */
return 0;
}
/* create our table of interp classes */
HashUtlAllocInit(conf->webshPool, TCL_STRING_KEYS);
/* see if we have a config file to evaluate */
if (conf->scriptName != NULL) {
if (Tcl_EvalFile(conf->mainInterp, (char *) conf->scriptName) ==
TCL_ERROR) {
errno = 0;
#ifndef APACHE2
ap_log_printf(conf->server, "%s",
Tcl_GetStringResult(conf->mainInterp));
#else /* APACHE2 */
ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0,
conf->server, "%s", Tcl_GetStringResult(conf->mainInterp));
#endif /* APACHE2 */
return 0;
}
Tcl_ResetResult(conf->mainInterp);
}
/* if we're in threaded mode, spawn a watcher thread
that runs a possibly defined code and does cleanup, something like:
if (weAreAThreadedOne) {
CreateThread(interp);
}
*/
/* that's it */
return 1;
}
/* ----------------------------------------------------------------------------
* create main interpreter (including init stuff)
* ------------------------------------------------------------------------- */
Tcl_Interp *createMainInterp(websh_server_conf * conf)
{
LogPlugIn *logtoap = NULL;
Tcl_Interp *mainInterp = Tcl_CreateInterp();
ApFuncs *apFuncs;
if (mainInterp == NULL)
return NULL;
if (Tcl_InitStubs(mainInterp,"8.2",0) == NULL) {
Tcl_DeleteInterp(mainInterp);
return NULL;
}
/* just to be sure the memory command is imported if
the corresponding Tcl features it */
#ifdef TCL_MEM_DEBUG
Tcl_InitMemory(mainInterp);
#endif
apFuncs = createApFuncs();
if (apFuncs == NULL) {
Tcl_DeleteInterp(mainInterp);
return NULL;
}
Tcl_SetAssocData(mainInterp, WEB_APFUNCS_ASSOC_DATA, destroyApFuncs, (ClientData *) apFuncs);
/* standard Init */
if (Tcl_Init(mainInterp) == TCL_ERROR) {
Tcl_DeleteInterp(mainInterp);
return NULL;
}
/* Websh Library Init */
if (ModWebsh_Init(mainInterp) == TCL_ERROR) {
Tcl_DeleteInterp(mainInterp);
return NULL;
}
/* --------------------------------------------------------------------------
* register log handler "apachelog"
* ----------------------------------------------------------------------- */
logtoap = createLogPlugIn();
if (logtoap == NULL) {
Tcl_DeleteInterp(mainInterp);
return NULL;
}
logtoap->constructor = createLogToAp;
logtoap->destructor = destroyLogToAp;
logtoap->handler = (int (*)(Tcl_Interp *, ClientData, char *)) logToAp;
registerLogPlugIn(mainInterp, APCHANNEL, logtoap);
/* eval init code */
if (Tcl_Eval(mainInterp, MAININTERP_INITCODE) == TCL_ERROR) {
Tcl_DeleteInterp(mainInterp);
return NULL;
}
Tcl_CreateObjCommand(mainInterp, "web::interpclasscfg",
Web_InterpClassCfg, (ClientData) conf, NULL);
return mainInterp;
}
/* ----------------------------------------------------------------------------
* destroy
* ------------------------------------------------------------------------- */
void destroyPool(websh_server_conf * conf)
{
if (conf == NULL)
return;
if (conf->webshPool != NULL) {
Tcl_HashEntry *entry;
Tcl_HashSearch search;
Tcl_MutexLock(&(conf->webshPoolLock));
while ((entry = Tcl_FirstHashEntry(conf->webshPool, &search)) != NULL) {
/* loop through entries */
destroyWebInterpClass((WebInterpClass *) Tcl_GetHashValue(entry));
Tcl_DeleteHashEntry(entry);
}
Tcl_DeleteHashTable(conf->webshPool);
Tcl_MutexUnlock(&(conf->webshPoolLock));
conf->webshPool = NULL;
}
if (conf->mainInterp != NULL) {
/* now delete the interp */
Tcl_DeleteInterp(conf->mainInterp);
conf->mainInterp = NULL;
}
}
/* -------------------------------------------------------------------------
* cleanupPool NOTE: pool must be locked by caller
* ------------------------------------------------------------------------- */
void cleanupPool(websh_server_conf * conf)
{
if (conf->webshPool != NULL) {
Tcl_HashEntry *entry;
Tcl_HashSearch search;
WebInterpClass *webInterpClass;
WebInterp *webInterp, *expiredInterp;
time_t t;
time(&t);
entry = Tcl_FirstHashEntry(conf->webshPool, &search);
while (entry != NULL) {
/* loop through entries */
webInterpClass = (WebInterpClass *) Tcl_GetHashValue(entry);
webInterp = webInterpClass->first;
while (webInterp != NULL) {
/* NOTE: check on max requests is done by poolReleaseWebInterp */
if ((webInterp->state) == WIP_FREE) {
/* check for expiry */
if (webInterpClass->maxidletime
&& (t - webInterp->lastusedtime) >
webInterpClass->maxidletime) {
logToAp(webInterp->interp, NULL,
"interpreter expired: idle time reached (id %ld, class %s)", webInterp->id, webInterp->interpClass->filename);
webInterp->state = WIP_EXPIRED;
}
else {
if (webInterpClass->maxttl
&& (t - webInterp->starttime) >
webInterpClass->maxttl) {
logToAp(webInterp->interp, NULL,
"interpreter expired: time to live reached (id %ld, class %s)", webInterp->id, webInterp->interpClass->filename);
webInterp->state = WIP_EXPIRED;
}
}
}
expiredInterp = webInterp;
webInterp = webInterp->next;
if (expiredInterp->state == WIP_EXPIRED)
destroyWebInterp(expiredInterp);
}
entry = Tcl_NextHashEntry(&search);
}
}
return;
}
/* ----------------------------------------------------------------------------
* readWebInterpCode
* ------------------------------------------------------------------------- */
int readWebInterpCode(WebInterp * webInterp, char *filename)
{
Tcl_Channel chan;
Tcl_Obj *objPtr;
Tcl_Interp *interp = webInterp->interp;
objPtr = Tcl_NewObj();
Tcl_IncrRefCount(objPtr);
chan = Tcl_OpenFileChannel(interp, filename, "r", 0644);
if (chan == (Tcl_Channel) NULL) {
Tcl_ResetResult(interp);
Tcl_AppendResult(interp, "couldn't read file \"", filename,
"\": ", Tcl_ErrnoMsg(Tcl_GetErrno()), (char *) NULL);
}
else {
if (Tcl_ReadChars(chan, objPtr, -1, 0) < 0) {
Tcl_Close(interp, chan);
Tcl_AppendResult(interp, "couldn't read file \"", filename,
"\": ", Tcl_ErrnoMsg(Tcl_GetErrno()), (char *) NULL);
}
else {
if (Tcl_Close(interp, chan) == TCL_OK) {
/* finally success ... */
webInterp->code = objPtr;
return TCL_OK;
}
}
}
Tcl_DecrRefCount(objPtr);
return TCL_ERROR;
}
ApFuncs *createApFuncs() {
ApFuncs *apFuncs = (ApFuncs *) Tcl_Alloc(sizeof(ApFuncs));
if (apFuncs == NULL)
return NULL;
apFuncs->createDefaultResponseObj = createDefaultResponseObj_AP;
apFuncs->isDefaultResponseObj = isDefaultResponseObj_AP;
apFuncs->requestGetDefaultChannelName = requestGetDefaultChannelName_AP;
apFuncs->requestGetDefaultOutChannelName = requestGetDefaultOutChannelName_AP;
apFuncs->requestFillRequestValues = requestFillRequestValues_AP;
apFuncs->Web_Initializer = Web_Initializer_AP;
apFuncs->Web_Finalizer = Web_Finalizer_AP;
apFuncs->Web_Finalize = Web_Finalize_AP;
apFuncs->Web_InterpCfg = Web_InterpCfg_AP;
apFuncs->Web_InterpClassCfg = Web_InterpClassCfg_AP;
apFuncs->Web_MainEval = Web_MainEval_AP;
apFuncs->Web_ConfigPath = Web_ConfigPath_AP;
apFuncs->ModWebsh_Init = ModWebsh_Init_AP;
return apFuncs;
}
void destroyApFuncs(ClientData apFuncs, Tcl_Interp * interp) {
if (apFuncs != NULL)
Tcl_Free((char *) apFuncs);
}