add: Ns_MutexInit(&modPtr->lock); Ns_MutexSetName2(&modPtr->lock, "nscgi", server);
context: static Ns_OpProc CgiRequest;static void     CgiRegister(Mod *modPtr, char *map);static Ns_Callback CgiFreeMap;static Ns_DString *CgiDs(Cgi *cgiPtr);static int	CgiInit(Cgi *cgiPtr, Map *mapPtr, Ns_Conn *conn);static void	CgiFree(Cgi *cgiPtr);static int  	CgiExec(Cgi *cgiPtr, Ns_Conn *conn);static int	CgiSpool(Cgi *cgiPtr, Ns_Conn *conn);static int	CgiCopy(Cgi *cgiPtr, Ns_Conn *conn);static int	CgiRead(Cgi *cgiPtr);static int	CgiReadLine(Cgi *cgiPtr, Ns_DString *dsPtr);static char    *NextWord(char *s);static void	SetAppend(Ns_Set *set, int index, char *sep, char *value);static void	SetUpdate(Ns_Set *set, char *key, char *value);int Ns_ModuleVersion = 1;	/* *---------------------------------------------------------------------- * * Ns_ModuleInit -- * *	Create a new CGI module instance.  Note: This module can *	be loaded multiple times. * * Results: *	NS_OK/NS_ERROR. * * Side effects: *	URL's may be registered for CGI. * *---------------------------------------------------------------------- */ intNs_ModuleInit(char *server, char *module){    char           *path, *key, *value, *section;    int             i;    Ns_Set         *set;    Ns_DString      ds;    Mod	   *modPtr;    static int	    initialized;    /*     * On the first (and likely only) load, register     * the temp file cleanup routine and open /dev/null     * for requests without content data.     */    if (!initialized) {	devNull = open("/dev/null", O_RDONLY);	if (devNull < 0) {	    Ns_Log(Error, "nscgi: open(/dev/null) failed: %s", strerror(errno));	    return NS_ERROR;	}	Ns_DupHigh(&devNull);	Ns_CloseOnExec(devNull);	initialized = 1;    }    /*     * Config basic options.     */    path = Ns_ConfigGetPath(server, module, NULL);    modPtr = ns_calloc(1, sizeof(Mod));    modPtr->module = module;    modPtr->server = server;    if (!Ns_ConfigGetInt(path, "maxinput", &modPtr->maxInput)) {        modPtr->maxInput = DEFAULT_MAXINPUT;    }    if (!Ns_ConfigGetInt(path, "limit", &modPtr->maxCgi)) {        modPtr->maxCgi = 0;    }    if (!Ns_ConfigGetInt(path, "maxwait", &modPtr->maxWait)) {        modPtr->maxWait = 30;    }    if (!Ns_ConfigGetBool(path, "gethostbyaddr", &i)) {        i = 0;    }    if (i) {	modPtr->flags |= CGI_GETHOST;    }    /*     * Configure the various interp and env options.     */    Ns_DStringInit(&ds);    section = Ns_ConfigGetValue(path, "interps");    if (section != NULL) {        Ns_DStringVarAppend(&ds, "ns/interps/", section, NULL);        modPtr->interps = Ns_ConfigGetSection(ds.string);        if (modPtr->interps == NULL) {            Ns_Log(Warning, "nscgi: no such interps section: %s",		   ds.string);        }    	Ns_DStringTrunc(&ds, 0);    }    section = Ns_ConfigGetValue(path, "environment");    if (section != NULL) {        Ns_DStringVarAppend(&ds, "ns/environment/", section, NULL);        modPtr->mergeEnv = Ns_ConfigGetSection(ds.string);        if (modPtr->mergeEnv == NULL) {            Ns_Log(Warning, "nscgi: no such environment section: %s",		   ds.string);        }    	Ns_DStringTrunc(&ds, 0);    }    if (!Ns_ConfigGetBool(path, "systemenvironment", &i)) {        i = 0;    }    if (i) {	modPtr->flags |= CGI_SYSENV;    }    /*     * Register all requested mappings.     */    set = Ns_ConfigGetSection(path);    for (i = 0; set != NULL && i < Ns_SetSize(set); ++i) {        key = Ns_SetKey(set, i);        value = Ns_SetValue(set, i);        if (STRIEQ(key, "map")) {            CgiRegister(modPtr, value);        }    }    Ns_DStringFree(&ds);    return NS_OK;}/* *---------------------------------------------------------------------- * * CgiRequest - * *	Process a CGI request. * * Results: *	Standard AOLserver request result. * * Side effects: *	Program may be executed. * *---------------------------------------------------------------------- */static intCgiRequest(void *arg, Ns_Conn *conn){    Map		   *mapPtr;    Mod		   *modPtr;    Cgi		    cgi;    int             status;    mapPtr = arg;    modPtr = mapPtr->modPtr;    /*     * Check for input overflow and initialize the CGI context.     */    if (modPtr->maxInput > 0 && conn->contentLength > modPtr->maxInput) {        return Ns_ReturnBadRequest(conn, "Exceeded maximum CGI input size");    }    if (CgiInit(&cgi, mapPtr, conn) != NS_OK) {	return Ns_ReturnNotFound(conn);    } else if (cgi.interp == NULL && access(cgi.exec, X_OK) != 0) {        if (STREQ(conn->request->method, "GET") ||	    STREQ(conn->request->method, "HEAD")) {	    /*	     * Evidently people are storing images and such in	     * their cgi bin directory and they expect us to	     * return these files directly.	     */            status = Ns_ConnReturnFile(conn, 200, NULL, cgi.exec);        } else {	    status = Ns_ReturnNotFound(conn);	}	goto done;    }    /*     * Spool input to temp file if necessary.     */    if (conn->contentLength > 0 && CgiSpool(&cgi, conn) != NS_OK) {	if (cgi.flags & CGI_ECONTENT) {	    status = Ns_ConnReturnBadRequest(conn, "Insufficient Content");	} else {	    status = Ns_ConnReturnInternalError(conn);	}	goto done;    }    /*     * Wait for CGI access if necessary.     */    if (modPtr->maxCgi > 0) {	Ns_Time timeout;	int wait = NS_OK;	Ns_GetTime(&timeout);	Ns_IncrTime(&timeout, modPtr->maxWait, 0);        Ns_MutexLock(&modPtr->lock);	while (wait == NS_OK && modPtr->activeCgi >= modPtr->maxCgi) {	    wait = Ns_CondTimedWait(&modPtr->cond, &modPtr->lock, &timeout);	}	if (wait == NS_OK) {	    ++modPtr->activeCgi;	}	Ns_MutexUnlock(&modPtr->lock);	if (wait != NS_OK) {	    status = Ns_ConnReturnStatus(conn, 503);	    goto done;	}    }    /*     * Execute the CGI and copy output.     */        if (CgiExec(&cgi, conn) != NS_OK) {	status = Ns_ConnReturnInternalError(conn);    } else {	status = CgiCopy(&cgi, conn);    }        /*     * Release CGI access.     */         if (modPtr->maxCgi > 0) {	Ns_MutexLock(&modPtr->lock);	--modPtr->activeCgi;	Ns_CondSignal(&modPtr->cond);	Ns_MutexUnlock(&modPtr->lock);    }done:    CgiFree(&cgi);    return status;}