rem: int		   debug = 0;
context: Ns_Main(int argc, char **argv, Ns_ServerInitProc *initProc){    int            i, fd;    char          *config;    Ns_Time 	   timeout;    char	   buf[PATH_MAX];#ifndef _WIN32    uid_t	   uid = 0;    gid_t	   gid = 0;    int		   debug = 0;    int	   	   mode = 0;    char	  *root = NULL;    char	  *garg = NULL;    char	  *uarg = NULL;    char	  *bindargs = NULL;    char	  *bindfile = NULL;    char	  *procname = NULL;    char	  *server = NULL;    Ns_Set	  *servers;    struct rlimit  rl;#else    /*     * The following variables are declared static so they     * preserve their values when Ns_Main is re-entered by     * the Win32 service control manager.     */    static int	   mode = 0;    static Ns_Set *servers;    static char	  *procname;    static char	  *server;#endif    /*     * For static builds only, we have to initialize     * otherwise dynamically loaded shared libraries.     */#if (STATIC_BUILD == 1)    NsthreadsInit();    NsdInit();#endif    /*     * Mark the server stopped until initialization is complete.     */    Ns_MutexLock(&nsconf.state.lock);    nsconf.state.started = 0;    Ns_MutexUnlock(&nsconf.state.lock);    /*     * When run as a Win32 service, Ns_Main will be re-entered     * in the service main thread.  In this case, jump past     * the point where the initial thread blocked when     * connected to the service control manager.     */     #ifdef _WIN32    if (mode == 'S') {    	goto contservice;    }#endif    /*     * Set up configuration defaults and initial values.     */    nsconf.argv0         = argv[0];    /*     * AOLserver requires file descriptor 0 be open on /dev/null to     * ensure the server never blocks reading stdin.     */         fd = open(DEVNULL, O_RDONLY);    if (fd > 0) {    	dup2(fd, 0);	close(fd);    }    /*          * Also, file descriptors 1 and 2 may not be open if the server     * is starting from /etc/init.  If so, open them on /dev/null     * as well because the server will assume they're open during     * initialization.  In particular, the log file will be duped     * to fd's 1 and 2.     */    fd = open(DEVNULL, O_WRONLY);    if (fd > 0 && fd != 1) {	close(fd);    }    fd = open(DEVNULL, O_WRONLY);    if (fd > 0 && fd != 2) {	close(fd);    }    /*     * Parse the command line arguments.     */    opterr = 0;    while ((i = getopt(argc, argv, "hpzifVs:t:IRSkKdr:u:g:b:B:")) != -1) {        switch (i) {	case 'h':	    UsageError(NULL);	    break;	case 'f':	case 'i':	case 'V':#ifdef _WIN32	case 'I':	case 'R':	case 'S':#endif	    if (mode != 0) {#ifdef _WIN32		UsageError("only one of -i, -f, -V, -I, -R, or -S may be specified");#else		UsageError("only one of -i, -f, or -V may be specified");#endif	    }	    mode = i;	    break;        case 's':	    if (server != NULL) {		UsageError("multiple -s <server> options");	    }	    nsconf.server = server = optarg;            break;	case 't':	    if (nsconf.config != NULL) {		UsageError("multiple -t <file> options");	    }            nsconf.config = optarg;            break;        case 'p':        case 'z':	    /* NB: Ignored. */            break;#ifndef _WIN32	case 'b':	    bindargs = optarg;	    break;	case 'B':	    bindfile = optarg;	    break;	case 'r':	    root = optarg;	    break;	    break;        case 'd':	    debug = 1;            break;	case 'g':	    garg = optarg;	    break;	case 'u':	    uarg = optarg;	    break;#endif	case ':':	    sprintf(buf, "option -%c requires a parameter", optopt);            UsageError(buf);	    break;        default:	    sprintf(buf, "invalid option: -%c", optopt);            UsageError(buf);            break;        }    }    if (mode == 'V') {        printf("AOLserver/%s (%s)\n", NSD_VERSION, Ns_InfoLabel()); 	printf("   CVS Tag:         %s\n", Ns_InfoTag());	printf("   Built:           %s\n", Ns_InfoBuildDate());	printf("   Tcl version:     %s\n", nsconf.tcl.version);	printf("   Platform:        %s\n", Ns_InfoPlatform());        return 0;    } else if (nsconf.config == NULL) {        UsageError("required -t <config> option not specified");    }    /*     * Find the absolute config pathname and read the config data     * before a possible chroot().     */    nsconf.config = FindConfig(nsconf.config);    config = NsConfigRead(nsconf.config);#ifndef _WIN32    /*     * Verify the uid/gid args.     */    if (uarg != NULL) {	uid = Ns_GetUid(uarg);	gid = Ns_GetUserGid(uarg);	if (uid < 0) {	    uid = atoi(uarg);	    /*	     * In the case where uarg isn't a username, we set it to NULL,	     * causing supplementary groups to be ignored later.	     */	    uarg = NULL;	}	if (uid == 0) {	    Ns_Fatal("nsmain: invalid user '%s'", uarg);	}    }    if (garg != NULL) {	gid = Ns_GetGid(garg);	if (gid < 0) {	    gid = atoi(garg);	    if (gid == 0) {		Ns_Fatal("nsmain: invalid group '%s'", garg);	    }	}    }    /*     * AOLserver uses select() extensively so adjust the open file     * limit to be no greater than FD_SETSIZE on Unix.  It's possible     * you could define FD_SETSIZE to a larger value and recompile but     * this has not been verified.  Note also that on some platforms     * (e.g., Solaris, SGI o32) you're still left with the hardcoded     * limit of 255  open streams due to the definition of the FILE     * structure with an unsigned char member for the file descriptor.     * Note this limit must be set now to ensure it's inherited by     * all future threads on certain platforms such as Linux.     */    if (getrlimit(RLIMIT_NOFILE, &rl) != 0) {	Ns_Log(Warning, "nsmain: getrlimit(RLIMIT_NOFILE) failed: '%s'",	       strerror(errno));    } else {	if (rl.rlim_max > FD_SETSIZE) {	    rl.rlim_max = FD_SETSIZE;	}	if (rl.rlim_cur != rl.rlim_max) {    	    rl.rlim_cur = rl.rlim_max;    	    if (setrlimit(RLIMIT_NOFILE, &rl) != 0) {	        Ns_Log(Warning, "nsmain: "		       "setrlimit(RLIMIT_NOFILE, %d) failed: '%s'",		       rl.rlim_max, strerror(errno));	    } 	}    }    /*     * Pre-bind any sockets now, before a possible setuid from root     * or chroot which may hide /etc/resolv.conf required to     * resolve name-based addresses.     */    NsPreBind(bindargs, bindfile);    /*     * Chroot() if requested before setuid from root.     */    if (root != NULL) {	if (chroot(root) != 0) {	    Ns_Fatal("nsmain: chroot(%s) failed: '%s'", root, strerror(errno));	}	if (chdir("/") != 0) {	    Ns_Fatal("nsmain: chdir(/) failed: '%s'", strerror(errno));	}	nsconf.home = "/";    }    /*     * If root, determine and change to the run time user and/or group.     */    if (getuid() == 0) {	if (uid == 0) {	    Ns_Fatal("nsmain: server will not run as root; "		     "must specify '-u username' parameter");	}	if (gid == 0) {	    Ns_Fatal("nsmain: server will not run as gid 0; "		     "must specify '-g group' parameter");	}	/*	 * Set or clear supplementary groups.	 */	if (uarg != NULL) {	    if (initgroups(uarg, gid) != 0) {		Ns_Fatal("nsmain: initgroups(%s, %d) failed: '%s'",			uarg, gid, strerror(errno));	    }	} else {	    if (setgroups(0, NULL) != 0) {		Ns_Fatal("nsmain: setgroups(0, NULL) failed: '%s'",			strerror(errno));	    }	}	if (gid != getgid() && setgid(gid) != 0) {	    Ns_Fatal("nsmain: setgid(%d) failed: '%s'", gid, strerror(errno));	}	if (setuid(uid) != 0) {	    Ns_Fatal("nsmain: setuid(%d) failed: '%s'", uid, strerror(errno));	}    }#ifdef __linux    /*     * On Linux, once a process changes uid/gid, the dumpable flag     * is cleared, preventing a core file from being written.  On     * Linux 2.4+, it can be set again using prctl() so that we can     * get core files.     */         if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {        Ns_Fatal("nsmain: prctl(PR_SET_DUMPABLE) failed: '%s'",                strerror(errno));    }#endif    /*     * Fork into the background and create a new session if running      * in daemon mode.     */    if (mode == 0) {	i = ns_fork();	if (i < 0) {	    Ns_Fatal("nsmain: fork() failed: '%s'", strerror(errno));	}	if (i > 0) {	    return 0;	}	nsconf.pid = getpid();	setsid();    }    /*     * Finally, block all signals for the duration of startup to ensure any     * new threads inherit the blocked state.     */    NsBlockSignals(debug);#endif    /*     * Initialize Tcl and eval the config file.     */    Tcl_FindExecutable(argv[0]);    nsconf.nsd = (char *) Tcl_GetNameOfExecutable();    NsConfigEval(config, argc, argv, optind);    ns_free(config);    /*     * Ensure servers where defined.     */    servers = Ns_ConfigGetSection("ns/servers");    if (servers == NULL || Ns_SetSize(servers) == 0) {	Ns_Fatal("nsmain: no servers defined");    }    /*     * If a single server was specified, ensure it exists     * and update the pointer to the config string (the     * config server strings are considered the unique     * server "handles").     */    if (server != NULL) {	i = Ns_SetFind(servers, server);	if (i < 0) {	    Ns_Fatal("nsmain: no such server '%s'", server);	}	server = Ns_SetKey(servers, i);    }    /*     * Set the procname used for the pid file.     */    procname = (server ? server : Ns_SetKey(servers, 0));    /*     * Verify and change to the home directory.     */         nsconf.home = Ns_ConfigGetValue(NS_CONFIG_PARAMETERS, "home");    if (nsconf.home == NULL) {	Ns_Fatal("nsmain: missing: [%s]home", NS_CONFIG_PARAMETERS);    }    if (chdir(nsconf.home) != 0) {	Ns_Fatal("nsmain: chdir(%s) failed: '%s'", nsconf.home, strerror(errno));    }#ifdef _WIN32    /*     * On Win32, first perform some additional cleanup of     * home, ensuring forward slashes and lowercase.     */    nsconf.home = getcwd(buf, sizeof(buf));    if (nsconf.home == NULL) {	Ns_Fatal("nsmain: getcwd failed: '%s'", strerror(errno));    }    while (*nsconf.home != '\0') {	if (*nsconf.home == '\\') {	    *nsconf.home = '/';	} else if (isupper(*nsconf.home)) {	    *nsconf.home = tolower(*nsconf.home);	}	++nsconf.home;    }    nsconf.home = buf;    /*     * Then, connect to the service control manager if running     * as a service (see service comment above).     */    if (mode == 'I' || mode == 'R' || mode == 'S') {	int status;	Ns_ThreadSetName("-service-");	switch (mode) {	case 'I':	    status = NsInstallService(procname);	    break;	case 'R':	    status = NsRemoveService(procname);	    break;	case 'S':    	    status = NsConnectService();	    break;	}	return (status == NS_OK ? 0 : 1);    }    contservice:#endif    /*     * Update core config values.     */    NsConfUpdate();    /*     * Open the log file now that the home directory and runtime     * user id have been set.     */         if (mode != 'f') {    	NsLogOpen();    }    /*     * Log the first startup message which should be the first     * output to the open log file unless the config script      * generated some messages.     */         StatusMsg(0);#ifndef _WIN32    /*     * Log the current open file limit.     */         if (getrlimit(RLIMIT_NOFILE, &rl) != 0) {	Ns_Log(Warning, "nsmain: "	       "getrlimit(RLIMIT_NOFILE) failed: '%s'", strerror(errno));    } else {	Ns_Log(Notice, "nsmain: "	       "max files: FD_SETSIZE = %d, rl_cur = %d, rl_max = %d",	       FD_SETSIZE, rl.rlim_cur, rl.rlim_max);    }#endif    /*     * Create the pid file used.     */    NsCreatePidFile(procname);    /*     * Initialize the virtual servers.     */    if (server != NULL) {    	NsInitServer(server, initProc);    } else {	for (i = 0; i < Ns_SetSize(servers); ++i) {	    server = Ns_SetKey(servers, i);    	    NsInitServer(server, initProc);	}    }    /*     * Load non-server modules.     */    NsLoadModules(NULL);    /*     * Run pre-startups and start the servers.     */    NsRunPreStartupProcs();    NsStartPools();    /*     * Signal startup is complete.     */    StatusMsg(1);    Ns_MutexLock(&nsconf.state.lock);    nsconf.state.started = 1;    Ns_CondBroadcast(&nsconf.state.cond);    Ns_MutexUnlock(&nsconf.state.lock);    /*     * Run any post-startup procs.     */    NsRunStartupProcs();    /*     * Start the drivers now that the server appears ready     * and then close any remaining pre-bound sockets.     */    if (NsStartDrivers() != NS_OK) {        Ns_Fatal("could not start drivers");    }#ifndef _WIN32    NsClosePreBound();#endif    /*     * Once the drivers listen thread is started, this thread will just     * endlessly wait for Unix signals, calling NsRunSignalProcs()     * whenever SIGHUP arrives.     */    NsHandleSignals();    /*     * Print a "server shutting down" status message, set     * the nsconf.stopping flag for any threads calling     * Ns_InfoShutdownPending(), and set the absolute     * timeout for all systems to complete shutown.     */    StatusMsg(2);    Ns_MutexLock(&nsconf.state.lock);    nsconf.state.stopping = 1;    if (nsconf.shutdowntimeout < 0) {	nsconf.shutdowntimeout = 0;    }    Ns_GetTime(&timeout);    Ns_IncrTime(&timeout, nsconf.shutdowntimeout, 0);    Ns_MutexUnlock(&nsconf.state.lock);    /*     * First, stop the drivers and servers threads.     */    NsStopDrivers();    NsStopPools(&timeout);    /*     * Next, start simultaneous shutdown in other systems and wait     * for them to complete.     */    NsStartSchedShutdown();     NsStartSockShutdown();    NsStartJobsShutdown();    NsStartShutdownProcs();    NsWaitSchedShutdown(&timeout);    NsWaitSockShutdown(&timeout);    NsWaitJobsShutdown(&timeout);    NsWaitDriversShutdown(&timeout);    NsWaitShutdownProcs(&timeout);    /*     * Finally, execute the exit procs directly.  Note that     * there is not timeout check for the exit procs so they     * should be well behaved.     */    NsRunAtExitProcs();    /*     * Remove the pid maker file, print a final "server exiting"     * status message and return to main.     */    NsRemovePidFile(procname);    StatusMsg(3);    return 0;}