rem: memcpy(&outbuf[dataOffset], readbuf, bytesRead);
add: memcpy(&outbuf[dataOffset], readbuf, (size_t)bytesRead);
context: static Ns_Callback ExtCleanup;/* * This lists all the callback functions and connects them to * IDs. */static Ns_DbProc ExtProcs[] = {    {DbFn_Name, (void *) ExtName},    {DbFn_DbType, (void *) ExtDbType},    {DbFn_ServerInit, (void *) ExtServerInit},    {DbFn_OpenDb, (void *) ExtOpenDb},    {DbFn_CloseDb, (void *) ExtCloseDb},    {DbFn_Flush, (void *) ExtFlush},    {DbFn_Cancel, (void *) Ns_ExtCancel},    {DbFn_Exec, (void *) ExtExec},    {DbFn_BindRow, (void *) ExtBindRow},    {DbFn_GetRow, (void *) ExtGetRow},    {DbFn_ResetHandle, (void *) ExtResetHandle},    {DbFn_SpStart, (void *) ExtSpStart},    {DbFn_SpSetParam, (void *) ExtSpSetParam},    {DbFn_SpExec, (void *) ExtSpExec},    {DbFn_SpReturnCode, (void *) ExtSpReturnCode},    {DbFn_SpGetParams, (void *) ExtSpGetParams},    {0, NULL}};static    int   verbose = NS_FALSE;int   Ns_ModuleVersion = 1;/* *========================================================================== * API functions *========================================================================== *//* *---------------------------------------------------------------------- * * Ns_DbDriverInit -- * *	Initialize this database driver.  * * Results: *	NS_OK/NS_ERROR.  * * Side effects: *	Allocates memory, initializes hash tables, etc.  * *---------------------------------------------------------------------- */intNs_DbDriverInit(char *hDriver, char *configPath){    int            status = NS_ERROR;    static int     initialized = NS_FALSE;    NsExtCtx      *ctx;    Tcl_HashEntry *he;    int            new;    if (initialized == NS_FALSE) {        Ns_MutexInit(&muCtx);        Ns_MutexSetName(&muCtx, "nsext");        Tcl_InitHashTable(&htCtx, TCL_STRING_KEYS);        Ns_RegisterShutdown(ExtCleanup, NULL);        initialized = NS_TRUE;    }    /*     * Register the external driver functions with nsdb. Nsdb will later call     * the ExtServerInit() function for each virtual server which uses     * nsdb.     */        if (Ns_DbRegisterDriver(hDriver, &(ExtProcs[0])) != NS_OK) {        Ns_Log(Error, "nsext: "	       "failed to register driver: %s", extName);    } else {        ctx = ns_malloc(sizeof(NsExtCtx));        ctx->connNum = 0;        ctx->ident[0] = '\0';        Ns_MutexInit(&ctx->muIdent);        ctx->param = Ns_ConfigGetValue(configPath, CONFIG_PARAM);        ctx->path = Ns_ConfigGetValue(configPath, CONFIG_LOCALDAEMON);        ctx->host = Ns_ConfigGetValue(configPath, CONFIG_REMOTEHOST);        if (Ns_ConfigGetInt(configPath, CONFIG_REMOTEPORT,                &ctx->port) != NS_TRUE) {            ctx->port = 0;        }        if (ctx->path == NULL && ctx->host == NULL) {            Ns_Log(Error, "nsext: "		   "bad config: localdaemon or remotehost required");        } else if (ctx->path == NULL && ctx->port == 0) {            Ns_Log(Error, "nsext: "		   "bad config: proxyhost requires proxyport");        } else {            if (!Ns_ConfigGetInt(configPath, CONFIG_TIMEOUT, &ctx->timeout)) {                ctx->timeout = DEFAULT_TIMEOUT;            }            if (!Ns_ConfigGetInt(configPath, 				 CONFIG_MAX_ELEMENT_SIZE,				 &ctx->maxElementSize)) {		                ctx->maxElementSize = DEFAULT_MAX_ELEMENT_SIZE;            }            if (!Ns_ConfigGetBool(configPath, CONFIG_IOTRACE,                    &ctx->ioTrace)) {                ctx->ioTrace = 0;            }            if (ctx->path != NULL) {                if (Ns_PathIsAbsolute(ctx->path)) {                    ctx->path = ns_strdup(ctx->path);                } else {                    Ns_DString ds;                    Ns_DStringInit(&ds);                    Ns_HomePath(&ds, "bin", ctx->path, NULL);                    ctx->path = Ns_DStringExport(&ds);                }            }            /*	     * Trim data	     */	                if (!Ns_ConfigGetBool(configPath, CONFIG_TRIMDATA,				  &ctx->trimdata)) {                ctx->trimdata = NS_FALSE;            }                        ctx->initOK = 1;            status = NS_OK;        }        if (status != NS_OK) {            ns_free(ctx);        } else {            Ns_MutexLock(&muCtx);            he = Tcl_CreateHashEntry(&htCtx, hDriver, &new);            Tcl_SetHashValue(he, ctx);            Ns_MutexUnlock(&muCtx);        }    }    Ns_Log (Notice, "nsext: module started; built on %s/%s)",	    __DATE__, __TIME__);    return status;}/* *---------------------------------------------------------------------- * * ExtName -- * *	Return the driver name  * * Results: *	Driver name.  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static char *ExtName(Ns_DbHandle *handle){    assert(handle != NULL);    return extName;}/* *---------------------------------------------------------------------- * * ExtDbType -- * *	Return the database type of the proxy daemon, parsed from the  *	identity string of the form: <dbtype> Proxy Daemon vX.X.  * * Results: *	String database type.  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static char *ExtDbType(Ns_DbHandle *handle){    NsExtConn *nsConn;    char      *identstr, *firstDelim;    int        typelen;    assert(handle != NULL);    nsConn = handle->connection;    Ns_MutexLock(&nsConn->ctx->muIdent);    identstr = nsConn->ctx->ident;    firstDelim = strchr(identstr, ' ');    typelen = firstDelim == NULL ? strlen(identstr) : firstDelim - identstr;    if (typelen >= MAX_DBTYPE) {        typelen = MAX_DBTYPE - 1;    }    strncpy(nsConn->ctx->dbtype, identstr, typelen);    Ns_MutexUnlock(&nsConn->ctx->muIdent);    nsConn->ctx->dbtype[typelen] = '\0';    return nsConn->ctx->dbtype;}/* *---------------------------------------------------------------------- * * ExtOpenDb -- * *	Open an external connection on an nsdb handle. The datasource  *	for the external driver is interpreted by the remote database  *	proxy daemon; it usually takes the form "host:port:database".  * * Results: *	NS_OK/NSERROR.  * * Side effects: *	May spawn the external driver.  * *---------------------------------------------------------------------- */static intExtOpenDb(Ns_DbHandle *handle){    NsExtConn     *nsConn;    NsExtCtx      *ctx;    int            status = NS_ERROR;    Ns_DString     dsOpenData;    Tcl_HashEntry *he;    int            ok = 0;    Ns_MutexLock(&muCtx);    he = Tcl_FindHashEntry(&htCtx, handle->driver);    if (he != NULL) {        ctx = Tcl_GetHashValue(he);        ok = ctx->initOK;    }    Ns_MutexUnlock(&muCtx);    if (ok) {        Ns_DStringInit(&dsOpenData);        Ns_DStringPrintf(&dsOpenData, "%s%c%s%c%s%c%s",	    handle->datasource == NULL ? "" : handle->datasource,            ARG_TOKEN_DELIMITER, handle->user == NULL ? "" : handle->user,	    ARG_TOKEN_DELIMITER,	    handle->password == NULL ? "" : handle->password,            ARG_TOKEN_DELIMITER, ctx->param == NULL ? "" : ctx->param);        handle->connection = NULL;        handle->connected = 0;        nsConn = ns_malloc(sizeof(NsExtConn));        nsConn->ctx = ctx;        nsConn->connNum = ctx->connNum++;        handle->connection = nsConn;        if (DbProxyStart(nsConn) == NS_OK) {	    /*	     * Ping daemon	     */	                if (DbProxyIsAlive(handle)) {                /*		 * daemon is alive, proceed...		 */		/*		 * consider this 'connected' so that we can shut down the		 * daemon when/if there's a problem opening.  otherwise		 * a 'zombie' nssybpd will be left lying around.		 */				handle->connected = 1;                if (DbProxySend(handle, Open, dsOpenData.string,                        strlen(dsOpenData.string)) == NS_OK &&                    DbProxyCheckStatus(nsConn, handle) == NS_OK) {		    char *temp = ns_malloc(RSP_BUFSIZE);		    		    if (DbProxyIdentify(handle, temp) == NS_OK) {			Ns_Log(Notice, "nsext: "			       "datasource opened: %s with %s",			       handle->datasource, ctx->ident); 			status = NS_OK;                        Ns_MutexLock(&ctx->muIdent);			strcpy(ctx->ident, temp);			Ns_MutexUnlock(&ctx->muIdent);		    }		    ns_free(temp);                }            }        }         Ns_DStringFree(&dsOpenData);    }    if (status == NS_ERROR) {	 DbProxyCleanup(handle);    }    return status;}/* *---------------------------------------------------------------------- * * ExtCloseDb -- * *	Close an external connection on an nsdb handle.  * * Results: *	NS_OK/NS_ERROR  * * Side effects: *	Will free the NsExtConn.  * *---------------------------------------------------------------------- */static intExtCloseDb(Ns_DbHandle *handle){    NsExtConn   *nsConn;    int          status = NS_OK;    if (handle == NULL || handle->connection == NULL) {	Ns_Log(Bug, "nsext: connection handle is null");	status = NS_ERROR;	goto done;    }        nsConn = handle->connection;    DbProxySend(handle, Close, NULL, 0);    if ((status = DbProxyCheckStatus(nsConn, handle)) != NS_OK) {        Ns_Log(Error, "nsext: error closing connection: %d",	       nsConn->connNum);    }     if (handle->connected) {            	 /*	  * shutdown the proxy daemon in any case	  */		 DbProxyStop(nsConn);    }    ns_free(nsConn);    handle->connection = NULL;    handle->connected = 0;    done:    return (status);}/* *---------------------------------------------------------------------- * * ExtExec -- * *	Send an external query.  * * Results: *	NS_OK/NS_ERROR  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static intExtExec(Ns_DbHandle *handle, char *sql){    NsExtConn *nsConn;    char       respBuf[RESPBUFMAX];    int        retcode = NS_ERROR;    assert(handle != NULL);    assert(handle->connection != NULL);    nsConn = handle->connection;    if (DbProxySend(handle, Exec, sql, strlen(sql)) == NS_OK &&        DbProxyCheckStatus(nsConn, handle) == NS_OK) {        if (DbProxyGetString(handle, respBuf, RESPBUFMAX) == NS_OK) {            if (strcmp(respBuf, EXEC_RET_ROWS) == 0) {                retcode = NS_ROWS;            } else if (strcmp(respBuf, EXEC_RET_DML) == 0) {                retcode = NS_DML;            } else {                retcode = NS_ERROR;            }        }    }    return retcode;}/* *---------------------------------------------------------------------- * * ExtBindRow -- * *	Put a row from a select into an Ns_Set.  * * Results: *	An Ns_Set contianing key/value pairs.  * * Side effects: *	Creates the Ns_Set returned.  * *---------------------------------------------------------------------- */static Ns_Set  *ExtBindRow(Ns_DbHandle *handle){    Ns_Set              *rows = NULL;    int                  status = NS_ERROR;    NsExtConn           *nsConn;    Ns_List             *colList, *currCol;    DbProxyInputElement *colEl;    assert(handle != NULL);    nsConn = handle->connection;        if (DbProxySend(handle, BindRow, NULL, 0) == NS_OK &&        DbProxyCheckStatus(nsConn, handle) == NS_OK) {        if ((colList = DbProxyGetList(handle)) != NULL) {	    rows = handle->row;	    Ns_SetTrunc(rows, 0);            for (currCol = colList; currCol != NULL;		 currCol = Ns_ListRest(currCol)) {		                colEl = Ns_ListFirst(currCol);		/*		 * colEl->data is NULL-terminated		 * (colEl->size included NULL)		 */		                Ns_SetPut(rows, colEl->data, NULL);                ns_free(colEl->data);                colEl->data = NULL;            }            Ns_ListFree(colList, (Ns_ElemVoidProc *) ExtFree);            status = NS_OK;        }    }    return rows;}/* *---------------------------------------------------------------------- * * ExtGetRow -- * *	Fetch rows after an ExtBindRow.  * * Results: *	NS_OK/NS_ERROR  * * Side effects: *	Puts a row into the passed-in set.  * *---------------------------------------------------------------------- */static intExtGetRow(Ns_DbHandle *handle, Ns_Set *row){    int                  status = NS_ERROR;    int                  i;    char                 colCountStr[32];    NsExtConn           *nsConn;    Ns_List             *rowList, *currRow;    DbProxyInputElement *rowEl;    int                  trimcnt;    assert(handle != NULL);    nsConn = handle->connection;    sprintf(colCountStr, "%d", Ns_SetSize(row));    if (DbProxySend(handle, GetRow, colCountStr,		    strlen(colCountStr)) == NS_OK &&        DbProxyCheckStatus(nsConn, handle) == NS_OK) {        if ((rowList = DbProxyGetList(handle)) != NULL) {            for (currRow = rowList, i = 0; currRow != NULL;                currRow = Ns_ListRest(currRow), i++) {                rowEl = Ns_ListFirst(currRow);		                /*		 * rowEl->data is NULL-terminated (rowEl->size included NULL)		 * String trim the value if called for		 */                if (nsConn->ctx->trimdata == NS_TRUE) {                    for (trimcnt = rowEl->size - 1;                         trimcnt >= 0;                         trimcnt--) {                        if (rowEl->data[trimcnt] == 0x20) {                            rowEl->data[trimcnt] = '\0';                            rowEl->size--;                            if (rowEl->data[trimcnt-1] != 0x20) {                                goto trimdone;                            }                        } else {                            if (rowEl->data[trimcnt-1] != 0x20) {                                goto trimdone;                            }                        }                    }                }		trimdone:                Ns_SetPutValue(row, i, rowEl->size == 0 ? NULL : rowEl->data);            }            Ns_ListFree(rowList, (Ns_ElemVoidProc *) ExtFree);            status = NS_OK;	    /*	     * got 0-length list, end of data	     */        } else {                                    status = NS_END_DATA;        }    }    return status;}/* *---------------------------------------------------------------------- * * ExtFlush -- * *	Flush any waiting rows not needed after an Ns_DbSelect().  * * Results: *	NS_OK/NS_ERROR  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static intExtFlush(Ns_DbHandle *handle){    NsExtConn *nsConn;    int        status;    assert(handle != NULL);    assert(handle->connection != NULL);    nsConn = handle->connection;    DbProxySend(handle, Flush, NULL, 0);    status = DbProxyCheckStatus(nsConn, handle);    return status;}/* *---------------------------------------------------------------------- * * Ns_ExtCancel -- * *	Cancel any pending request.  * * Results: *	NS_OK/NS_ERROR  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static intNs_ExtCancel(Ns_DbHandle *handle){    NsExtConn *nsConn;    int        status;    assert(handle != NULL);    assert(handle->connection != NULL);    nsConn = handle->connection;    DbProxySend(handle, Cancel, NULL, 0);    status = DbProxyCheckStatus(nsConn, handle);    return status;}/* *---------------------------------------------------------------------- * * ExtInterpInit -- * *	Add the "ns_ext" command to a single Tcl interpreter.  * * Results: *	NS_OK/NS_ERROR  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static intExtInterpInit(Tcl_Interp *interp, void *ignored){    Tcl_CreateCommand(interp, "ns_ext", ExtCmd, NULL, NULL);    return NS_OK;}/* *---------------------------------------------------------------------- * * ExtServerInit -- * *	Have ExtInterpInit called for each interpreter in the virtual  *	server which is being intialized.  * * Results: *	NS_OK/NS_ERROR.  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static intExtServerInit(char *hServer, char *hModule, char *hDriver){    if (!Ns_ConfigGetBool ("ns/parameters", "verbose", &verbose)) {	verbose = NS_FALSE;    }    return Ns_TclInitInterps(hServer, ExtInterpInit, NULL);}/* *---------------------------------------------------------------------- * * AllDigits -- * *	Determines if a string contains only digits.  * * Results: *	1 if all digits, 0 if not.  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static intAllDigits(char *str){    char *p = str;    if (*p == '-') {        p++;    }    while (*p) {        if (!isdigit( (unsigned char)*p++ ) ) {            return 0;        }    }    return 1;}/* *---------------------------------------------------------------------- * * ExtResetHandle -- * *	The function is only here for the Oracle external driver.  *	Oracle has autocommit off by default which means major damage  *	can occur if the transaction is munged. So before a handle is  *	put back into a pool (Ns_DbPoolPutHandle called from dbtcl.c  *	with `ns_db close` or `ns_db releasehandle`, this guy can  *	register a function to reset the handle (i.e., check its  *	status and perform whatever cleanup measures that are  *	necessary with a rollback, etc.). We may or may not have a  *	good use for this in the future.  * * Results: *	NS_OK/NS_ERROR  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static int ExtResetHandle(Ns_DbHandle *handle){    char         statusBuf[STATUS_BUFSIZE];    int          status = NS_ERROR;        if (handle == NULL || handle->connection == NULL) {		Ns_Log(Error, "nsext: %s is null", 	       ((handle == NULL)?("handle"):("connection")));    } else {	DbProxySend(handle, ResetHandle, NULL, 0);	if ((status = DbProxyGetString(handle, statusBuf,				       STATUS_BUFSIZE)) == NS_OK) {	    if (strncasecmp(statusBuf, OK_STATUS, 2) == 0) {		status = NS_OK;	    } else {		Ns_Log(Error,"nsext: "		       "protocol error: received|%s| expected|%s|",		       statusBuf, OK_STATUS);				DbProxyCleanup(handle);		status = NS_ERROR;	    }	}    }        return status;}/* *---------------------------------------------------------------------- * * ExtSpStart -- * *	Begin an SP.  * * Results: *	NS_OK/NS_ERROR.  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static int ExtSpStart(Ns_DbHandle *handle, char *procname){    NsExtConn   *nsConn;    int          status;    assert(handle != NULL);    assert(handle->connection != NULL);    nsConn = handle->connection;    DbProxySend(handle, SpStart, procname, strlen(procname));    status = DbProxyCheckStatus(nsConn, handle);    return status;}/* *---------------------------------------------------------------------- * * ExtSpSetParam -- * *	Set a param for an SP.  * * Results: *	NS_OK/NS_ERROR.  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static int ExtSpSetParam(Ns_DbHandle *handle, char *args){    NsExtConn   *nsConn;    int          status;    assert(handle != NULL);    assert(handle->connection != NULL);    nsConn = handle->connection;    DbProxySend(handle, SpSetParam, args, strlen(args));    status = DbProxyCheckStatus(nsConn, handle);    return status;}/* *---------------------------------------------------------------------- * * ExtSpExec -- * *	Run an SP.  * * Results: *	NS_OK/NS_ERROR  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static intExtSpExec(Ns_DbHandle *handle){    NsExtConn   *nsConn;    int          retcode = NS_ERROR;    char         respBuf[RESPBUFMAX];    assert(handle != NULL);    assert(handle->connection != NULL);    nsConn = handle->connection;    DbProxySend(handle, SpExec, NULL, 0);    retcode = DbProxyCheckStatus(nsConn, handle);    if (retcode == NS_OK) {        if (DbProxyGetString(handle, respBuf, RESPBUFMAX) == NS_OK) {            if (strcmp(respBuf, EXEC_RET_ROWS) == 0) {                retcode = NS_ROWS;            } else if (strcmp(respBuf, EXEC_RET_DML) == 0) {                retcode = NS_DML;            } else {                retcode = NS_ERROR;            }        }    }    return retcode;}/* *---------------------------------------------------------------------- * * ExtSpReturnCode -- * *	Returns the return code from the just-run SP.  * * Results: *	NS_OK/NS_ERROR  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static intExtSpReturnCode(Ns_DbHandle *dbhandle, char *returnCode, int bufsize){    int status = NS_ERROR;    if (DbProxySend(dbhandle, SpReturnCode, NULL, 0) == NS_OK &&        DbProxyGetString(dbhandle, returnCode, bufsize) == NS_OK) {        status = NS_OK;    } else {        Ns_Log(Error, "nsext: "	       "'returncode' command to proxy daemon failed");        returnCode[0] = '\0';    }    return status;}/* *---------------------------------------------------------------------- * * Ns_ExtGetParams -- * *	Fetch rows after an Ns_ExtExProc.  * * Results: *	A set of rows.  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static Ns_Set *ExtSpGetParams(Ns_DbHandle *handle){    int                  status = NS_ERROR;    int                  i;    NsExtConn           *nsConn;    Ns_Set              *paramSet = NULL;    Ns_List             *paramList, *rowList, *currParam, *currRow;    DbProxyInputElement *rowEl, *paramEl;    int                  trimcnt;    assert(handle != NULL);    nsConn = handle->connection;    if (DbProxySend(handle, SpGetParams, NULL, 0) == NS_OK &&        DbProxyCheckStatus(nsConn, handle) == NS_OK) {        if ((paramList = DbProxyGetList(handle)) != NULL) {            if ((rowList = DbProxyGetList(handle)) == NULL) {                Ns_Log(Error, "nsext: rowlist did not arrive");                Ns_ListFree(paramList, (Ns_ElemVoidProc *) ExtFree);                return NULL;            }            paramSet = Ns_SetCreate(NULL);            for (currParam = paramList, currRow = rowList, i = 0;                 currRow != NULL && currParam != NULL;                 currParam = Ns_ListRest(currParam),		     currRow = Ns_ListRest(currRow), i++) {		                paramEl = Ns_ListFirst(currParam);                rowEl   = Ns_ListFirst(currRow);                /*		 * rowEl->data is NULL-terminated (rowEl->size included NULL)		 * String trim the value if called for		 */                if (nsConn->ctx->trimdata == NS_TRUE) {                    for (trimcnt = rowEl->size - 1;                         trimcnt >= 0;                         trimcnt--) {                        if (rowEl->data[trimcnt] == 0x20) {                            rowEl->data[trimcnt] = '\0';                            rowEl->size--;                            if (rowEl->data[trimcnt-1] != 0x20) {                                goto trimdone;                            }                        } else {                            if (rowEl->data[trimcnt-1] != 0x20) {                                goto trimdone;                            }                        }                    }                }			     trimdone:                Ns_SetPut(paramSet, paramEl->data, NULL);                Ns_SetPutValue(paramSet, i,                               rowEl->size == 0 ? NULL : rowEl->data);            }            Ns_ListFree(paramList, (Ns_ElemVoidProc *) ExtFree);            Ns_ListFree(rowList,   (Ns_ElemVoidProc *) ExtFree);            status = NS_OK;        } else {	    /*	     * got 0-length list, end of data	     */            status = NS_NO_DATA;        }    }    if (status == NS_OK) {        return paramSet;    }     else {        return NULL;    }}/* *---------------------------------------------------------------------- * * ExtFree -- * *	Free a dbproxyinputelement.  * * Results: *	None.  * * Side effects: *	Frees both the data and the struct.  * *---------------------------------------------------------------------- */static voidExtFree(void *ptr){    DbProxyInputElement *element;    char                *data;    if (ptr != NULL) {        element = (DbProxyInputElement *) ptr;        data    = element->data;        ns_free(data);        ns_free(element);    }}/* *========================================================================== * Callback functions *========================================================================== *//* *---------------------------------------------------------------------- * * DbProxyStart -- * *	Starts a new connection.  * * Results: *	NS_OK/NS_ERROR.  * * Side effects: *	May spawn a new proxy daemon.  * *---------------------------------------------------------------------- */static intDbProxyStart(NsExtConn * nsConn){    if (nsConn->ctx->path != NULL) {        return LocalProxy(nsConn);    } else {        return NetProxy(nsConn, nsConn->ctx->host, nsConn->ctx->port);    }}/* *---------------------------------------------------------------------- * * DbProxyStop -- * *	Stops an external proxy daemon.  * * Results: *	None.  * * Side effects: *	Closes daemon sockets.  * *---------------------------------------------------------------------- */static voidDbProxyStop(NsExtConn * nsConn){    Ns_Log(Debug, "nsext: stopping db proxy daemon connection %d",	   nsConn->connNum);    close(nsConn->socks[0]);    close(nsConn->socks[1]);    nsConn->socks[0] = nsConn->socks[1] = -1;}/* *---------------------------------------------------------------------- * * DbProxySend -- * *	Write to the proxy.  * * Results: *	NS_OK/NS_ERROR.  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static intDbProxySend(Ns_DbHandle *dbhandle, Ns_ExtDbCommandCode msgType, char *arg,	    int argSize){    int             status = NS_ERROR;    int             arglen;    char           *msg;    int             msglen = 0;    NsExtConn      *nsConn = (NsExtConn *) dbhandle->connection;      Ns_DString      ds;    Ns_DStringInit(&ds);    if ((msg = Ns_ExtDbMsgCodeToName(msgType)) == NULL) {        Ns_Log(Bug, "nsext: "               "unknown message type received for connection %d",               nsConn->connNum);    } else if (arg == NULL && Ns_ExtDbMsgRequiresArg(msgType)) {        Ns_Log(Bug, "nsext: "	       "'%s' message requires argument (connection %d)",               Ns_ExtDbMsgCodeToName(msgType), nsConn->connNum);    } else {        if (arg == NULL) {            arglen = 0;        } else {            arglen = argSize;        }        Ns_DStringPrintf(&ds, "%s\n%d\n", msg, arglen);        msglen = ds.length;        if (arg != NULL) {            Ns_DStringAppend(&ds, arg);            msglen += arglen;        }        if (DbProxyTimedIO(nsConn->socks[1], ds.string, msglen, 0,                           Write, nsConn->ctx->timeout, dbhandle,			   0) == NS_ERROR) {	                Ns_Log(Error, "nsext: error sending buffer(%s): %s",                   ds.string, strerror(errno));        } else {            if (nsConn->ctx->ioTrace) {                Ns_Log(Notice, "SENT |%s|", ds.string);            }            status = NS_OK;        }    }    Ns_DStringFree(&ds);    return status;}/* *---------------------------------------------------------------------- * * DbProxyGetPingReply -- * *	Recieves the ping reply from the proxy.  * * Results: *	NS_OK/NS_ERROR  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static intDbProxyGetPingReply(Ns_DbHandle *dbhandle){    int  status;    char statusBuf[STATUS_BUFSIZE];    if ((status = DbProxyGetString(dbhandle, statusBuf,				   STATUS_BUFSIZE)) == NS_OK) {	        if (strcasecmp(statusBuf, OK_STATUS) == 0) {            status = NS_OK;        } else {            Ns_Log(Error, "nsext: protocol error on ping: "		   "received|%s| expected|%s|", statusBuf, OK_STATUS);	                DbProxyCleanup(dbhandle);            status = NS_ERROR;        }    }    return status;}/* *---------------------------------------------------------------------- * * DbProxyCheckStatus -- * *	Checks return status. On error, copies exception code/message  *	to handle  * * Results: *	NS_OK/NS_ERROR.  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static intDbProxyCheckStatus(NsExtConn * nsConn, Ns_DbHandle *handle){    int   status = NS_ERROR;    char  statusBuf[STATUS_BUFSIZE];    char *exceptionCode, *exceptionMsg, *p;    if (DbProxyGetString(handle, statusBuf, STATUS_BUFSIZE) == NS_OK) {        if (strcasecmp(statusBuf, OK_STATUS) == 0) {            status = NS_OK;	    	    /*	     * Clear out any old messages.  This was added because of	     * a problem encountered when processing multiple result	     * sets [e.g. multiple selects] from a Sybase stored	     * procedure.  You'd Ns_DbBindRow until you couldn't bind	     * no more.  The last bind would result in an error, which	     * would put a message into dsExceptionMsg.  After that	     * binding work is done, that message is still in there,	     * so on the next Exec, an Ns_DbLogSql would see there's	     * content there, and would output a message, leading to	     * end-user confusion due to an inexplicable error message	     * happening.  Hopefully this won't silence Real messages...	     */	    	    Ns_DStringTrunc(&handle->dsExceptionMsg, 0);	} else if (strstr(statusBuf, SILENT_ERROR_STATUS) != NULL) {	    if (verbose) {		Ns_Log(Debug, "nsext: "		       "silent error string '%s'", statusBuf);	    }        } else {	    /*	     * db error: exception form is	     * exceptionCode#exceptionMessage	     */	                exceptionCode = statusBuf;            if ((p = strchr(statusBuf, (int) ARG_TOKEN_DELIMITER)) == NULL) {                /*		 * generic error message, not a db exception, just log msg		 */		                Ns_Log(Error, "nsext: "		       "database error message: '%s'", statusBuf);            } else {                *p++ = '\0';                if (*p == '\0') {                    Ns_Log(Error, "nsext: "			   "invalid exception status string: '%s'", statusBuf);                } else {                    exceptionMsg = p;                    Ns_Log(Debug, "nsext: received exception code=%s msg=%s",			   exceptionCode, exceptionMsg);                    strcpy(handle->cExceptionCode, exceptionCode);                    Ns_DStringFree(&handle->dsExceptionMsg);                    Ns_DStringAppend(&handle->dsExceptionMsg, exceptionMsg);                }            }        }    }    return status;}/* *---------------------------------------------------------------------- * * DbProxyTimedIO -- * *	Perform IO with the proxy; may read or write depending on  *	iotype flg.  * * Results: *	NS_OK/NS_ERROR  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static intDbProxyTimedIO(int sock, char *buf, int nbytes, int flags,	       SockIOType iotype, int timeout, Ns_DbHandle *dbhandle,	       int readExact){    int status = NS_OK;    int ioreturn;    if (timeout > 0) {        fd_set         set;        int            nsel;        struct timeval tv;        int         max = sock;        tv.tv_sec = timeout;        tv.tv_usec = 0;        FD_ZERO(&set);        FD_SET(sock, &set);        if (iotype == Write) {            nsel = Ns_Select(max + 1, NULL, &set, NULL, &tv);        } else {            nsel = Ns_Select(max + 1, &set, NULL, NULL, &tv);        }        if (nsel <= 0 || !FD_ISSET(sock, &set)) {            if (nsel == 0) {                Ns_Log(Warning, "nsext: "		       "exceeded proxy i/o timeout (%d seconds)", timeout);                DbProxyCleanup(dbhandle);            } else {                Ns_Log(Error, "nsext: "		       "select() of %d failed: %s (code %d)",		       sock, strerror(errno), errno);            }            status = NS_ERROR;        }    }    if (status == NS_OK) {        if (iotype == Read) {            int bytesRead = 0;#ifdef USE_FIONREAD	    	    int bytesAvail;	    if (flags & MSG_PEEK) {		ioctl(sock, FIONREAD, &bytesAvail);		if (bytesAvail < nbytes) {		    nbytes = bytesAvail;		}	    }#endif	                while (bytesRead < nbytes) {                ioreturn = recv(sock, buf + bytesRead, nbytes - bytesRead,                                flags);                if (ioreturn < 0) {                    break;		}		bytesRead += ioreturn;		if (flags & MSG_PEEK) {		    break;		}            }            ioreturn = bytesRead;        } else {            ioreturn = send(sock, buf, nbytes, flags);        }        if (ioreturn != nbytes) {            if (iotype == Write || ioreturn <= 0 || readExact) {                status = NS_ERROR;                if (ErrnoPeerClose(errno)) {                    Ns_Log(Warning, "nsext: "			   "connection dropped by external proxy daemon");                } else {                    Ns_Log(Error, "nsext: socket %s %d failed: "			   "%s (code %d), ioreturn=%d, nbytes=%d",			   iotype == Write ? "write to" : "read from", sock,			   strerror(errno), errno,			   ioreturn, nbytes);                }                DbProxyCleanup(dbhandle);            }        }    }    return status;}/* *---------------------------------------------------------------------- * * DbProxyCleanup -- * *	Zero out an Ns_DbHandle.  * * Results: *	None.  * * Side effects: *	If connected, will stop the connection.  * *---------------------------------------------------------------------- */static voidDbProxyCleanup(Ns_DbHandle *handle){    if (handle != NULL) {	if (handle->connected) {	    DbProxyStop(handle->connection);	}	ns_free(handle->connection);	handle->connection = NULL;	handle->connected = 0;    }}/* *---------------------------------------------------------------------- * * DbProxyGetString -- * *	Read a single string from the proxy.  * * Results: *	NS_OK/NS_ERROR.  * * Side effects: *	Will read from proxy.  * *---------------------------------------------------------------------- */static intDbProxyGetString(Ns_DbHandle *dbhandle, char *buf, int maxbuf){    int        status = NS_ERROR;    char      *nlTailPos;    int        strSize;    NsExtConn *nsConn = dbhandle->connection;    if (DbProxyTimedIO(nsConn->socks[0], buf, maxbuf, MSG_PEEK,            Read, nsConn->ctx->timeout, dbhandle, 0) >= 0) {        if ((nlTailPos = strchr(buf, (int) '\n')) == NULL) {            Ns_Log(Error, "nsext: "		   "protocol error: no record separator in '%s'", buf);            DbProxyCleanup(dbhandle);        } else {            strSize = nlTailPos - buf;            strSize++;            if (DbProxyTimedIO(nsConn->socks[0], buf, strSize, 0,                    Read, nsConn->ctx->timeout, dbhandle, 0) == NS_OK) {		                *nlTailPos = '\0';                if (nsConn->ctx->ioTrace) {                    Ns_Log(Notice, "REC |%s|", buf);                }                status = NS_OK;            }        }    }    return status;}/* *---------------------------------------------------------------------- * * DbProxyGetList -- * *	Read a list from the proxy.  * * Results: *	A list or NULL if error.  * * Side effects: *	Allocates new list.  * *---------------------------------------------------------------------- */static Ns_List *DbProxyGetList(Ns_DbHandle *dbhandle){    Ns_List             *destList;    int                  status = NS_OK;    int                  done = 0;    char                *datum;    DbProxyInputElement *el;    NsExtConn           *nsConn = dbhandle->connection;    int                  size;    char                 sizebuf[MAX_SIZEDIGITS]; /* holds ASCII representation                                                   * of datasize */    destList = NULL;    while (status == NS_OK && !done) {        if ((status = DbProxyGetString(dbhandle, sizebuf,				       MAX_SIZEDIGITS)) == NS_OK) {            if (!AllDigits(sizebuf)) {                Ns_Log(Error, "nsext: "		       "protocol error: number expected, got '%s'", sizebuf);                status = NS_ERROR;            } else if ((size = atoi(sizebuf)) > nsConn->ctx->maxElementSize) {                Ns_Log(Error, "nsext: "		       "exceeded element size limit of %d", size);                status = NS_ERROR;            } else if (size == END_LIST_VAL) {                done = 1;            } else {                datum = ns_malloc(size + 1);		/*		 * add NULL until nsdb does		 * binary data		 */		                if (size && DbProxyTimedIO(nsConn->socks[0], datum, size, 0,                        Read, nsConn->ctx->timeout, dbhandle, 1) != NS_OK) {		                        Ns_Log(Error, "nsext: "			   "read error: %s", strerror(errno));                    status = NS_ERROR;                } else {                    datum[size] = '\0';                    if (nsConn->ctx->ioTrace) {                        Ns_Log(Notice, "REC |%s|", datum);                    }                    el = ns_malloc(sizeof(DbProxyInputElement));                    el->size = size == 0 ? 0 : size + 1;                    el->data = datum;                    Ns_ListPush(el, destList);                }            }        }    }    if (status == NS_ERROR) {        Ns_ListFree(destList, (Ns_ElemVoidProc *) ExtFree);        destList = NULL;        DbProxyCleanup(dbhandle);    } else {        destList = Ns_ListNreverse(destList);    }        return destList;}/* *---------------------------------------------------------------------- * * DbProxyIsAlive -- * *	Determines if the proxy is still running.  * * Results: *	Boolean: false=no, true=yes  * * Side effects: *	Pings the proxy.  * *---------------------------------------------------------------------- */static intDbProxyIsAlive(Ns_DbHandle *dbhandle){    return (DbProxySend(dbhandle, Ping, NULL, 0) == NS_OK &&            DbProxyGetPingReply(dbhandle) == NS_OK);}/* *---------------------------------------------------------------------- * * DbProxyIdentify -- * *	Get the proxy's name.  * * Results: *	NS_OK/NS_ERROR.  * * Side effects: *	Talks to the proxy.  * *---------------------------------------------------------------------- */static intDbProxyIdentify(Ns_DbHandle *dbhandle, char *identbuf){    int status = NS_OK;    if (DbProxySend(dbhandle, Identify, NULL, 0) == NS_OK &&        DbProxyGetString(dbhandle, identbuf, RSP_BUFSIZE) == NS_OK) {        /*	 * result is now in identbuf	 */    } else {        Ns_Log(Error, "nsext: "	       "'identify' command to proxy daemon failed");        sprintf(identbuf, "Error: Identify command to Proxy Daemon failed\n");	status = NS_ERROR;    }    return status;}/* *---------------------------------------------------------------------- * * DbProxyGetTypes -- * *	Issues the GetTypes command to the proxy daemon.  * * Results: *	NS_OK/NS_ERROR. Puts the results in typebuf.  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static intDbProxyGetTypes(Ns_DbHandle *dbhandle, char *typesbuf){    int status = NS_ERROR;    if (DbProxySend(dbhandle, GetTypes, NULL, 0) == NS_OK &&        DbProxyGetString(dbhandle, typesbuf, RSP_BUFSIZE) == NS_OK) {        status = NS_OK;    } else {        Ns_Log(Error, "nsext: "	       "'gettypes' command to proxy daemon failed");        typesbuf[0] = '\0';    }    return status;}/* *---------------------------------------------------------------------- * * DbProxyResultId -- * *	Issues the ResultId command to the proxy daemon.  * * Results: *	NS_OK/NS_ERROR; fills in idbuf with result.  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static intDbProxyResultId(Ns_DbHandle *dbhandle, char *idbuf){    int             status = NS_ERROR;    if (DbProxySend(dbhandle, ResultId, NULL, 0) == NS_OK &&        DbProxyGetString(dbhandle, idbuf, RSP_BUFSIZE) == NS_OK) {        status = NS_OK;    } else {        Ns_Log(Error, "nsext: "	       "'resultid' command to proxy daemon failed");        idbuf[0] = '\0';    }    return status;}/* *---------------------------------------------------------------------- * * DbProxyResultRows -- * *	Issues the ResultRows command to the proxy daemon.  * * Results: *	NS_OK/NS_ERROR; fills in rowCountStr with result.  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static intDbProxyResultRows(Ns_DbHandle *dbhandle, char *rowCountStr){    int status = NS_ERROR;    if (DbProxySend(dbhandle, ResultRows, NULL, 0) == NS_OK &&        DbProxyGetString(dbhandle, rowCountStr, RSP_BUFSIZE) == NS_OK) {        status = NS_OK;    } else {        Ns_Log(Error, "nsext: "	       "'resultrows' command to proxy daemon failed");        rowCountStr[0] = '\0';    }    return status;}/* *---------------------------------------------------------------------- * * DbProxySetMaxRows -- * *	Issues the SetMaxRows command to the proxy daemon.  * * Results: *	NS_OK/NS_ERROR; fills in maxRowsStr with result.  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static intDbProxySetMaxRows(Ns_DbHandle *dbhandle, char *maxRowsStr){    int  status = NS_ERROR;    if (DbProxySend(dbhandle, SetMaxRows, maxRowsStr,		    strlen(maxRowsStr)) == NS_OK &&        DbProxyGetPingReply(dbhandle) == NS_OK) {	        status = NS_OK;    } else {        Ns_Log(Error, "nsext: "	       "'setmaxrows' command to proxy daemon failed");    }    return status;}/* *---------------------------------------------------------------------- * * DbProxyTraceOn -- * *	Issues the TraceOn command to the proxy daemon.  * * Results: *	NS_OK/NS_ERROR; fills in filepath with result.  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static intDbProxyTraceOn(Ns_DbHandle *dbhandle, char *filepath){    int status = NS_ERROR;    if (DbProxySend(dbhandle, TraceOn, filepath, strlen(filepath)) == NS_OK &&        DbProxyGetPingReply(dbhandle) == NS_OK) {        status = NS_OK;    } else {        Ns_Log(Error, "nsext: "	       "'traceon' command to proxy daemon failed");    }        return status;}/* *---------------------------------------------------------------------- * * DbProxyTraceOff -- * *	Issues the TraceOff command to the proxy daemon.  * * Results: *	NS_OK/NS_ERROR; * * Side effects: *	None.  * *---------------------------------------------------------------------- */static intDbProxyTraceOff(Ns_DbHandle *dbhandle){    int status = NS_ERROR;    if (DbProxySend(dbhandle, TraceOff, NULL, 0) == NS_OK &&        DbProxyGetPingReply(dbhandle) == NS_OK) {	        status = NS_OK;    } else {        Ns_Log(Error, "nsext: "	       "'traceoff' command to proxy daemon failed");    }        return status;}/* *---------------------------------------------------------------------- * * DbProxyCreateRemoteTmpFile -- * *	Ask the proxy daemon to make a remote temp file.  * * Results: *	NS_OK/NS_ERROR; fills in remoteFileName with the remote  *	side's filename.  * * Side effects: *	Creates a remote temp file, or fills in an error in errbuf.  * *---------------------------------------------------------------------- */static intDbProxyCreateRemoteTmpFile(Ns_DbHandle *dbhandle, char *remoteFileName, char *errbuf){    int  status = NS_ERROR;    char respbuf[RSP_BUFSIZE];    if (DbProxySend(dbhandle, CreateTmpF, NULL, 0) != NS_OK) {        sprintf(errbuf, "Can't send CreateTmpF command to Proxy Daemon");    } else if (DbProxyGetString(dbhandle, respbuf, RSP_BUFSIZE) != NS_OK) {        sprintf(errbuf, "Can't get CreateTmpF status response "		"from Proxy Daemon");    } else if (strcasecmp(respbuf, OK_STATUS) != 0) {        strcpy(errbuf, respbuf);    } else if (DbProxyGetString(dbhandle, respbuf, RSP_BUFSIZE) != NS_OK) {        sprintf(errbuf, "Can't get CreateTmpF name response "		"from Proxy Daemon");    } else {        strcpy(remoteFileName, respbuf);        status = NS_OK;    }        return status;}/* *---------------------------------------------------------------------- * * DbProxyCopyToRemoteFile -- * *	Copy a local file to a remote file.  * * Results: *	NS_OK/NS_ERROR  * * Side effects: *	Fills in errbuf with an error on failure.  * *---------------------------------------------------------------------- */static intDbProxyCopyToRemoteFile(Ns_DbHandle *dbhandle, char *srcFile, char *remoteFileName,    char *errbuf){    int  status = NS_ERROR;    char remoteFdStr[4];    char readbuf[FILE_IOSIZE];    char outbuf[FILE_IOSIZE + 128];    char respbuf[RSP_BUFSIZE];    char remoteOpenParams[512];    int  dataOffset;    int  bytesRead;    int  localSrcFd = -1;    int  fileLoc;    int  done = 0;    remoteFdStr[0] = '\0';    sprintf(remoteOpenParams, "%d%c%s", O_RDWR, ARG_TOKEN_DELIMITER,        remoteFileName);    if ((localSrcFd = open(srcFile, O_RDONLY, 0)) < 0) {        sprintf(errbuf, "open on %s failed: %s", srcFile, strerror(errno));    } else if (DbProxySend(dbhandle, OpenF, remoteOpenParams,            strlen(remoteOpenParams)) != NS_OK) {        sprintf(errbuf, "Can't send OpenFcommand to Proxy Daemon");    } else if (DbProxyGetString(dbhandle, respbuf, RSP_BUFSIZE) != NS_OK) {        sprintf(errbuf, "Can't get OpenF status response from Proxy Daemon");    } else if (strcasecmp(respbuf, OK_STATUS) != 0) {        strcpy(errbuf, respbuf);    } else if (DbProxyGetString(dbhandle, respbuf, RSP_BUFSIZE) != NS_OK) {        sprintf(errbuf, "Can't get OpenF fd response from Proxy Daemon");    } else {        strcpy(remoteFdStr, respbuf);        /*         * we now have a local (source) and remote (destination) file         * descriptor         */        fileLoc = 0;        status = NS_OK;        while (!done && status == NS_OK) {            status = NS_ERROR;            if ((bytesRead = read(localSrcFd, readbuf, FILE_IOSIZE)) > 0) {                /*		 * package data for transmission		 */                dataOffset = sprintf(outbuf, "%s%c%d%c%d%c",				     remoteFdStr, ARG_TOKEN_DELIMITER,				     fileLoc, ARG_TOKEN_DELIMITER,				     bytesRead, ARG_TOKEN_DELIMITER);                memcpy(&outbuf[dataOffset], readbuf, bytesRead);                if (DbProxySend(dbhandle, WriteF, outbuf,                        dataOffset + bytesRead) != NS_OK) {                    sprintf(errbuf, "Remote write failure, file offset=%d",			    fileLoc);                } else if (DbProxyGetString(dbhandle, respbuf,					    RSP_BUFSIZE) != NS_OK) {		                        sprintf(errbuf, "Can't get WriteF status response "			    "from Proxy Daemon");                } else if (strcasecmp(respbuf, OK_STATUS) != 0) {                    strcpy(errbuf, respbuf);                } else {                    status = NS_OK;                    fileLoc += bytesRead;                }            } else if (bytesRead == 0) {                status = NS_OK;                done = 1;            } else {                sprintf(errbuf, "read on %s failed: %s",			srcFile, strerror(errno));            }        }    }    if (localSrcFd != -1) {        close(localSrcFd);    }    if (remoteFdStr[0] != '\0') {	/*	 * close remote fd	 */	        status = NS_ERROR;        if (DbProxySend(dbhandle, CloseF, remoteFdStr,                strlen(remoteFdStr)) != NS_OK) {	                sprintf(errbuf, "Can't send CloseF command to Proxy Daemon");        } else if (DbProxyGetString(dbhandle, respbuf, RSP_BUFSIZE) != NS_OK) {            sprintf(errbuf,		    "Can't get CloseF status response from Proxy Daemon");        } else if (strcasecmp(respbuf, OK_STATUS) != 0) {            strcpy(errbuf, respbuf);        } else {            status = NS_OK;        }    }        return status;}/* *---------------------------------------------------------------------- * * DbProxyCopyFromRemoteFile -- * *	Copy a remote file from the proxy to a local file.  * * Results: *	NS_OK/NS_ERROR  * * Side effects: *	Writes to a local file, or fills in errbuf on error.  * *---------------------------------------------------------------------- */static intDbProxyCopyFromRemoteFile(Ns_DbHandle *dbhandle, char *destFile,			  char *remoteFileName, char *errbuf){    int                  status = NS_ERROR;    char                 remoteFdStr[4];    char                 readRequest[128];    char                 respbuf[RSP_BUFSIZE];    char                 remoteOpenParams[512];    int                  localDestFd = -1;    int                  fileLoc;    int                  done = 0;    Ns_List             *readInfo;    DbProxyInputElement *readEl;    remoteFdStr[0] = '\0';    sprintf(remoteOpenParams, "%d%c%s", O_RDONLY, ARG_TOKEN_DELIMITER,	    remoteFileName);    if ((localDestFd = open(destFile, O_CREAT | O_TRUNC | O_WRONLY,			    0644)) < 0) {	        sprintf(errbuf, "open/create on %s failed: %s", destFile,		strerror(errno));    } else if (DbProxySend(dbhandle, OpenF, remoteOpenParams,			   strlen(remoteOpenParams)) != NS_OK) {        sprintf(errbuf, "Can't send OpenF command to Proxy Daemon");    } else if (DbProxyGetString(dbhandle, respbuf, RSP_BUFSIZE) != NS_OK) {        sprintf(errbuf, "Can't get OpenF status response from Proxy Daemon");    } else if (strcasecmp(respbuf, OK_STATUS) != 0) {        strcpy(errbuf, respbuf);    } else if (DbProxyGetString(dbhandle, respbuf, RSP_BUFSIZE) != NS_OK) {        sprintf(errbuf, "Can't get OpenF fd response from Proxy Daemon");    } else {        strcpy(remoteFdStr, respbuf);        /*         * we now have a local (destination) and remote (source) file         * descriptor         */	        fileLoc = 0;        status = NS_OK;        while (!done && status == NS_OK) {            status = NS_ERROR;            sprintf(readRequest, "%s%c%d%c%d", remoteFdStr,		    ARG_TOKEN_DELIMITER,		    fileLoc, ARG_TOKEN_DELIMITER, FILE_IOSIZE);            if (DbProxySend(dbhandle, ReadF, readRequest,			    strlen(readRequest)) != NS_OK) {                sprintf(errbuf, "Can't send ReadF command to Proxy Daemon");            } else if (DbProxyGetString(dbhandle, respbuf,					RSP_BUFSIZE) != NS_OK) {		                sprintf(errbuf, "Can't get ReadF status response "			"from Proxy Daemon");            } else if (strcasecmp(respbuf, OK_STATUS) != 0) {                strcpy(errbuf, respbuf);            } else if ((readInfo = DbProxyGetList(dbhandle)) == NULL) {                sprintf(errbuf, "Can't read data from Proxy Daemon");            } else {                readEl = Ns_ListFirst(readInfo);                if (readEl->size == 0) {                    status = NS_OK;                    done = 1;                } else if (readEl->size > 0) {                    readEl->size--;		    /*		     * ignore NULL added by DbProxyGetList		     */		                        if (write(localDestFd, readEl->data,			      readEl->size) != readEl->size) {			                        sprintf(errbuf, "Local write to %s failed: %s",				destFile, strerror(errno));                        status = NS_ERROR;                    } else {                        status = NS_OK;                        fileLoc += readEl->size;                    }                } else {		    /*		     * readEl->size < 0		     */		                        Ns_Log(Bug, "nsext: "			   "negative size from remote read");                    sprintf(errbuf,			    "Read error from Proxy Daemon (negative size)");                }                ns_free(readEl->data);                readEl->data = NULL;                Ns_ListFree(readInfo, (Ns_ElemVoidProc *) ExtFree);            }        }    }    if (localDestFd != -1) {        close(localDestFd);    }    if (remoteFdStr[0] != '\0') {	/*	 * close remote fd	 */	        status = NS_ERROR;        if (DbProxySend(dbhandle, CloseF, remoteFdStr,                strlen(remoteFdStr)) != NS_OK) {            sprintf(errbuf, "Can't send CloseF command to Proxy Daemon");        } else if (DbProxyGetString(dbhandle, respbuf, RSP_BUFSIZE) != NS_OK) {            sprintf(errbuf,		    "Can't get CloseF status response from Proxy Daemon");        } else if (strcasecmp(respbuf, OK_STATUS) != 0) {            strcpy(errbuf, respbuf);        } else {            status = NS_OK;        }    }    return status;}/* *---------------------------------------------------------------------- * * DbProxyDeleteRemoteFile -- * *	Deletes a file on the remote system  * * Results: *	NS_OK/NS_ERROR  * * Side effects: *	Fills in errbuf on failure.  * *---------------------------------------------------------------------- */static intDbProxyDeleteRemoteFile(Ns_DbHandle *dbhandle, char *remoteFileName, char *errbuf){    int  status = NS_ERROR;    char respbuf[RSP_BUFSIZE];    if (DbProxySend(dbhandle, DeleteF, remoteFileName,            strlen(remoteFileName)) != NS_OK) {        sprintf(errbuf, "Unable to send DeleteF command Proxy Daemon");    } else if (DbProxyGetString(dbhandle, respbuf, RSP_BUFSIZE) != NS_OK) {        sprintf(errbuf,		"Unable to get DeleteF status response from Proxy Daemon");    } else if (strcasecmp(respbuf, OK_STATUS) != 0) {        strcpy(errbuf, respbuf);    } else {        status = NS_OK;    }        return status;}/* *========================================================================== * Static functions *========================================================================== *//* *---------------------------------------------------------------------- * * NetProxy -- * *	Connect this NsExtConn to a host/port.  * * Results: *	NS_OK/NS_ERROR  * * Side effects: *	Sets the socket in nsConn  * *---------------------------------------------------------------------- */static intNetProxy(NsExtConn *nsConn, char *host, int port){    nsConn->socks[0] = nsConn->socks[1] = Ns_SockConnect(host, port);    if (nsConn->socks[0] == -1) {        Ns_Log(Error, "nsext: connect failure: %s:%d", host, port);        return NS_ERROR;    }    Ns_Log(Notice, "nsext: connect success: %s:%d", host, port);    return NS_OK;}/* *---------------------------------------------------------------------- * * LocalProxy -- * *	Connect to a proxy running on this very machine.  * * Results: *	NS_OK/NS_ERROR  * * Side effects: *	Forks and execs a proxy.  * *---------------------------------------------------------------------- */static intLocalProxy(NsExtConn * nsConn){    int  in[2], out[2];    char   *argv[3];    int     status, pid, code;    status = NS_ERROR;    if (ns_pipe(in) < 0) {        Ns_Log(Error, "nsext: failed to create input socket pipes");    } else {        if (ns_pipe(out) < 0) {	    close(in[0]);	    close(in[1]);            Ns_Log(Error, "nsext: failed to create output socket pipes");        } else {            /*             * Set CloseOnExec to avoid stuck-open connections.             */            Ns_CloseOnExec(in[0]);            Ns_CloseOnExec(in[1]);            Ns_CloseOnExec(out[0]);            Ns_CloseOnExec(out[1]);            argv[0] = nsConn->ctx->path;            argv[1] = NULL;            pid = Ns_ExecArgv(nsConn->ctx->path, NULL, out[0], in[1], argv,			      NULL);            close(out[0]);            close(in[1]);            if (pid == -1) {                Ns_Log(Error, "nsext: spawn failed for '%s'",		       nsConn->ctx->path);            } else {                if (Ns_WaitForProcess(pid, &code) == NS_OK) {                    if (code != 0) {                        Ns_Log(Error, "nsext: "			       "proxy returned non-zero exit code: %d", code);                    } else {                        nsConn->socks[0] = in[0];                        nsConn->socks[1] = out[1];                        status = NS_OK;                    }                }            }            if (status != NS_OK) {            	close(in[0]);            	close(out[1]);            }        }    }    return status;}/* *---------------------------------------------------------------------- * * ExtCmd -- * *	This function implements the "ns_ext" Tcl command installed  *	into each interpreter of each virtual server. It provides  *	access to features specific to the external driver.  * * Results: *	Tcl result.  * * Side effects: *	None.  * *---------------------------------------------------------------------- */static intExtCmd(ClientData dummy, Tcl_Interp *interp, int argc, char **argv){    Ns_DbHandle *handle;    char         rspbuf[RSP_BUFSIZE];    char         errbuf[512];    NsExtConn   *extconn;    errbuf[0] = '\0';    if (argc < 3) {        Tcl_AppendResult(interp, "insufficient args: should be \"",            argv[0], " command dbId (optional cmd arg)\"", NULL);        return TCL_ERROR;    }    if (Ns_TclDbGetHandle(interp, argv[2], &handle) != TCL_OK) {        return TCL_ERROR;    }    /*     * Make sure this is a external handle before accessing     * handle->connection as an NsExtConn.     */    if (strstr(Ns_DbDriverName(handle), extName) == NULL) {        Tcl_AppendResult(interp, "handle \"", argv[2], "\" is not of type \"",            extName, "\"", NULL);        return TCL_ERROR;    }    extconn = (NsExtConn *) handle->connection;    if (!strcmp(argv[1], "ping")) {        sprintf(interp->result, "%s", DbProxyIsAlive(handle) ? "ok" : "error");    } else if (!strcmp(argv[1], "isremote")) {        Tcl_SetResult(interp, extconn->ctx->path ? "0" : "1", TCL_STATIC);    } else if (!strcmp(argv[1], "number")) {        sprintf(interp->result, "%d", extconn->connNum);    } else if (!strcmp(argv[1], "identify")) {        Ns_MutexLock(&extconn->ctx->muIdent);	sprintf(interp->result, "%s", extconn->ctx->ident);	Ns_MutexUnlock(&extconn->ctx->muIdent);    } else if (!strcmp(argv[1], "dbtype")) {        sprintf(interp->result, "%s", ExtDbType(handle));    } else if (!strcmp(argv[1], "resethandle")) {      sprintf(interp->result, "%d", ExtResetHandle(handle));    } else if (!strcmp(argv[1], "traceon")) {        if (argc != 4) {            Tcl_AppendResult(interp, "insufficient args: should be \"",                argv[0], " traceon dbId filepath\"", NULL);            return TCL_ERROR;        }        if (!Ns_PathIsAbsolute(argv[3])) {            Tcl_AppendResult(interp, "filepath argument: \"",                argv[3], "\" must be absolute.", NULL);            return TCL_ERROR;        }        if (DbProxyTraceOn(handle, argv[3]) == NS_OK) {            sprintf(interp->result, "Trace is on, using file: %s", argv[3]);        } else {            sprintf(interp->result,                "Error: traceon command to file: %s failed", argv[3]);        }    } else if (!strcmp(argv[1], "traceoff")) {        if (DbProxyTraceOff(handle) == NS_OK) {            sprintf(interp->result, "Trace is off.");        } else {            sprintf(interp->result, "Error: traceoff command failed");        }    } else if (!strcmp(argv[1], "gettypes")) {        if (DbProxyGetTypes(handle, rspbuf) == NS_OK) {            char *p;            if ((p = ns_strtok(rspbuf, DELIMITERS)) != NULL) {                do {                    Tcl_AppendElement(interp, p);                } while ((p = ns_strtok(NULL, DELIMITERS)) != NULL);            }        } else {            sprintf(interp->result, "Error: gettypes command failed");            return TCL_ERROR;        }    } else if (!strcmp(argv[1], "resultid")) {        if (DbProxyResultId(handle, rspbuf) == NS_OK) {            sprintf(interp->result, rspbuf);        } else {            sprintf(interp->result, "Error: resultid command failed");            return TCL_ERROR;        }    } else if (!strcmp(argv[1], "resultrows")) {        if (DbProxyResultRows(handle, rspbuf) == NS_OK) {            sprintf(interp->result, rspbuf);        } else {            sprintf(interp->result, "Error: resultrows command failed");            return TCL_ERROR;        }    } else if (!strcmp(argv[1], "setmaxrows")) {        if (argc != 4) {            Tcl_AppendResult(interp, "insufficient args: should be \"",                argv[0], " setmaxrows dbId rowcount\"", NULL);            return TCL_ERROR;        }        if (!AllDigits(argv[3])) {            Tcl_AppendResult(interp, "rowcount argument: \"",                argv[3], "\" must be numeric.", NULL);            return TCL_ERROR;        }        if (DbProxySetMaxRows(handle, argv[3]) == NS_OK) {            sprintf(interp->result, "Max rows set to %s", argv[3]);        } else {            sprintf(interp->result, "Error: setmaxrows command failed");            return TCL_ERROR;        }    } else if (!strcmp(argv[1], "mktemp")) {        char            tmpName[512];        if (DbProxyCreateRemoteTmpFile(handle, tmpName, errbuf) == NS_OK) {            sprintf(interp->result, tmpName);        } else {            sprintf(interp->result, "mktemp error: %s", errbuf);            return TCL_ERROR;        }    } else if (!strcmp(argv[1], "cpto")) {        if (argc != 5) {            Tcl_AppendResult(interp, "insufficient args: should be \"",                argv[0], " cpto dbId localSrcFile remoteDestFile\"", NULL);            return TCL_ERROR;        }        if (DbProxyCopyToRemoteFile(handle, argv[3], argv[4],				    errbuf) != NS_OK) {	                sprintf(interp->result, "cpto error: %s", errbuf);            return TCL_ERROR;        }    } else if (!strcmp(argv[1], "cpfrom")) {        if (argc != 5) {            Tcl_AppendResult(interp, "insufficient args: should be \"",                argv[0], " cpfrom dbId localDestFile remoteSrcFile\"", NULL);            return TCL_ERROR;        }        if (DbProxyCopyFromRemoteFile(handle, argv[3], argv[4],				      errbuf) != NS_OK) {	                sprintf(interp->result, "cpfrom error: %s", errbuf);            return TCL_ERROR;        }    } else if (!strcmp(argv[1], "rm")) {        if (argc != 4) {            Tcl_AppendResult(interp, "insufficient args: should be \"",                argv[0], " rm dbId remoteFile\"", NULL);            return TCL_ERROR;        }        if (DbProxyDeleteRemoteFile(handle, argv[3], errbuf) != NS_OK) {            sprintf(interp->result, "rm error: %s", errbuf);        }    } else {        Tcl_AppendResult(interp, "unknown command \"", argv[1],            "\": should be ping, gettypes, number, traceon, traceoff, "            "identify, resultid, resultrows, setmaxrows, "            "mktemp, rm, cpto, cpfrom.",            NULL);        return TCL_ERROR;    }    return TCL_OK;}/* *---------------------------------------------------------------------- * * ExtCleanup -- * *	Clean up all data structures  * * Results: *	None.  * * Side effects: *	Frees everything.  * *---------------------------------------------------------------------- */static voidExtCleanup(void *ignored){    Tcl_HashEntry  *he;    Tcl_HashSearch  hs;    he = Tcl_FirstHashEntry(&htCtx, &hs);    while (he != NULL) {        ns_free(Tcl_GetHashValue(he));        he = Tcl_NextHashEntry(&hs);    }    Tcl_DeleteHashTable(&htCtx);    Ns_MutexDestroy(&muCtx);}