rem: Ns_Log(Notice, "nswin32: removing service '%s'", name.string);
context: static Ns_ThreadProc ServiceTicker;static void StopTicker(void);static void StartTicker(DWORD pending);static VOID WINAPI ServiceMain(DWORD argc, LPTSTR *argv);static VOID WINAPI ServiceHandler(DWORD code);static BOOL WINAPI ConsoleHandler(DWORD code);static void ReportStatus(DWORD state, DWORD code, DWORD hint);static void ExitService(void);static char *GetServiceName(Ns_DString *dsPtr);static SERVICE_STATUS_HANDLE hStatus = 0;static SERVICE_STATUS curStatus;static Ns_Tls   tls;static int service;static int tick;static int sigpending;static Ns_ServerInitProc *serverInit;#define SysErrMsg()    (NsWin32ErrMsg(GetLastError()))/* *---------------------------------------------------------------------- * * DllMain -- * *	Init routine for the nsd.dll which setups TLS for Win32 errors *  	disables thread attach/detach calls. * * Results: *  	TRUE or FALSE. * * Side effects: *  	None. * *---------------------------------------------------------------------- */BOOL APIENTRYDllMain(HANDLE hModule, DWORD why, LPVOID lpReserved){    WSADATA         wsd;    if (why == DLL_PROCESS_ATTACH) {	Ns_TlsAlloc(&tls, ns_free);	if (WSAStartup(MAKEWORD(1, 1), &wsd) != 0) {            return FALSE;        }        DisableThreadLibraryCalls(hModule);    } else if (why == DLL_PROCESS_DETACH) {        WSACleanup();    }    return TRUE;}/* *---------------------------------------------------------------------- * * NsWin32ErrMsg -- * *	Get a string message for an error code in either the kernel or *  	wsock dll's. * * Results: *  	Pointer to per-thread LocalAlloc'ed memory. * * Side effects: *  	None. * *---------------------------------------------------------------------- */char *NsWin32ErrMsg(int err){    char           *msg;    msg = Ns_TlsGet(&tls);    if (msg == NULL) {	msg = ns_malloc(100);	Ns_TlsSet(&tls, msg);    }    sprintf(msg, "win32 error code: %d", err);    return msg;}/* *---------------------------------------------------------------------- * * NsConnectService -- * *	Attach to the service control manager at startup. * * Results: *  	None. * * Side effects: *  	Service control manager will create a new thread running *  	ServiceMain(). * *---------------------------------------------------------------------- */intNsConnectService(Ns_ServerInitProc *initProc){    SERVICE_TABLE_ENTRY table[2];    char buf[PATH_MAX];    char *log, *null;    int fd;    BOOL ok;    /*     * Open a temporary log, ensuring it's opened     * on fd 2 for later dup2 in NsLogOpen().     */    serverInit = initProc;    log = null = "nul:";    if (GetTempPath(sizeof(buf) - sizeof(NSD_NAME) - 7, buf) > 0) {	strcat(buf, NSD_NAME ".XXXXXX");	log = _mktemp(buf);    }    if (log == NULL) {	log = null;    }    _fcloseall();    for (fd = 0; fd < 3; ++fd) {	close(fd);    }    freopen(null, "rt", stdin);    freopen(log, "wt", stdout);    freopen(log, "wt", stderr);    Ns_Log(Notice, "nswin32: connecting to service control manager");    service = 1;    table[0].lpServiceName = NSD_NAME;    table[0].lpServiceProc = ServiceMain;    table[1].lpServiceName = NULL;    table[1].lpServiceProc = NULL;    ok = StartServiceCtrlDispatcher(table);    if (!ok) {        Ns_Log(Error, "nswin32: "	       "failed to contact service control dispatcher: '%s'", SysErrMsg());    } else if (log != null) {	unlink(log);    }    return (ok ? NS_OK : NS_ERROR);}/* *---------------------------------------------------------------------- * * NsRemoveService -- * *	Remove a previously installed service. * * Results: *  	None. * * Side effects: *  	Service should stop and then disappear from the list in the *  	services control panel. * *---------------------------------------------------------------------- */intNsRemoveService(void){    SC_HANDLE       hmgr, hsrv;    SERVICE_STATUS  status;    Ns_DString	    name;    BOOL            ok;    Ns_DStringInit(&name);    GetServiceName(&name);    Ns_Log(Notice, "nswin32: removing service '%s'", name.string);    ok = FALSE;    hmgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);    if (hmgr != NULL) {        hsrv = OpenService(hmgr, name.string, SERVICE_ALL_ACCESS);        if (hsrv != NULL) {            ControlService(hsrv, SERVICE_CONTROL_STOP, &status);            ok = DeleteService(hsrv);            CloseServiceHandle(hsrv);        }        CloseServiceHandle(hmgr);    }    if (!ok) {	Ns_Log(Error, "nswin32: failed to remove service '%s': '%s'",	       name.string, SysErrMsg());    }    Ns_DStringFree(&name);    return (ok ? NS_OK : NS_ERROR);}/* *---------------------------------------------------------------------- * * NsInstallService -- * *	Install as an NT service. * * Results: *  	None. * * Side effects: *  	Service should appear in the list in the services control panel. * *---------------------------------------------------------------------- */intNsInstallService(void){    SC_HANDLE       hmgr, hsrv;    BOOL	    ok;    char	    nsd[PATH_MAX], carg[10], config[PATH_MAX];    Ns_DString	    name, cmd;    ok = FALSE;    if (_fullpath(config, nsconf.config, sizeof(config)) == NULL) {	Ns_Log(Error, "nswin32: invalid config path '%s'", nsconf.config);    } else if (!GetModuleFileName(NULL, nsd, sizeof(nsd))) {	Ns_Log(Error, "nswin32: failed to find nsd.exe: '%s'", SysErrMsg());    } else {	sprintf(carg, " -%c ", nsconf.configfmt);	Ns_DStringInit(&name);	Ns_DStringInit(&cmd);	Ns_DStringVarAppend(&cmd, nsd,	    " -S -s ", nsServer, carg, config, NULL);    	if (nsMemPools) {	    Ns_DStringAppend(&cmd, " -z");	}	GetServiceName(&name);	Ns_Log(Notice, "nswin32: installing service '%s'", name.string);	hmgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);	if (hmgr != NULL) {	    hsrv = CreateService(hmgr, name.string, name.string,		SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,		SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,		cmd.string, NULL, NULL, "TcpIp\0", NULL, NULL);	    if (hsrv != NULL) {		CloseServiceHandle(hsrv);		ok = TRUE;	    }	    CloseServiceHandle(hmgr);	}	if (!ok) {	    Ns_Log(Error, "nswin32: failed to install service '%s': '%s'",		   name.string, SysErrMsg());	}	Ns_DStringFree(&name);	Ns_DStringFree(&cmd);    }    return (ok ? NS_OK : NS_ERROR);}/* *---------------------------------------------------------------------- * * NsHandleSignals -- * *	Loop endlessly, processing HUP and TCL signals until a TERM *  	signal arrives. * * Results: *  	None. * * Side effects: *  	None. * *---------------------------------------------------------------------- */voidNsHandleSignals(void){    int pending;    /*     * If running as a service, stop the ticker thread and report     * startup complete. Otherwise, register a handler which will     * initiate an orderly shutdown on Ctrl-C.     */         if (!service) {    	SetConsoleCtrlHandler(ConsoleHandler, TRUE);    } else {	StopTicker();	ReportStatus(SERVICE_RUNNING, NO_ERROR, 0);    }    do {    	Ns_MutexLock(&lock);	while (sigpending == 0) {    	    Ns_CondWait(&cond, &lock);	}	pending = sigpending;	sigpending = 0;	Ns_MutexUnlock(&lock);	if (pending & NS_SIGHUP) {	    NsRunSignalProcs();	}	if (pending & NS_SIGTCL) {	    NsTclRunInits();	}    } while (!(pending & NS_SIGTERM));    /*     * If running as a service, startup the ticker thread again     * to keep updating status until shutdown is complete.     */         if (service) {	StartTicker(SERVICE_STOP_PENDING);    }}/* *---------------------------------------------------------------------- * * NsSendSignal -- * *	Send a signal to wakeup NsHandleSignals. As on Unix, a signal *  	sent multilple times is only received once. * * Results: *  	None. * * Side effects: *  	Main thread will wakeup. * *---------------------------------------------------------------------- */voidNsSendSignal(int sig){    switch (sig) {	case NS_SIGTERM:	case NS_SIGHUP:	case NS_SIGTCL:	    Ns_MutexLock(&lock);	    sigpending |= sig;	    Ns_CondSignal(&cond);	    Ns_MutexUnlock(&lock);    	    break;	default:    	    Ns_Fatal("nswin32: invalid signal: %d", sig);	    break;    }}/* *---------------------------------------------------------------------- * * ns_socknbclose -- * *	Perform a non-blocking socket close via the socket callback *  	thread.  This is only called by a timeout in Ns_SockTimedConnect. * * Results: *  	0 or SOCKET_ERROR. * * Side effects: *  	Socket will be closed when writable. * *---------------------------------------------------------------------- */intns_socknbclose(SOCKET sock){    if (Ns_SockCloseLater(sock) != NS_OK) {	return SOCKET_ERROR;    }    return 0;}/* *---------------------------------------------------------------------- * * ns_sockdup -- * *	Duplicate a socket.  This is used in the old ns_sock Tcl cmds. * * Results: *  	New handle to underlying socket. * * Side effects: *  	None. * *---------------------------------------------------------------------- */SOCKETns_sockdup(SOCKET sock){    HANDLE hp, src, dup;        src = (HANDLE) sock;    hp = GetCurrentProcess();        if (!DuplicateHandle(hp, src, hp, &dup, 0, FALSE, DUPLICATE_SAME_ACCESS)) {	return INVALID_SOCKET;    }    return (SOCKET) dup;}   /* *---------------------------------------------------------------------- * * ns_pipe -- * *	Create a pipe marked close-on-exec. * * Results: *  	0 if ok, -1 on error. * * Side effects: *  	None. * *---------------------------------------------------------------------- */intns_pipe(int *fds){    return _pipe(fds, 4096, _O_NOINHERIT|_O_BINARY);}/* *---------------------------------------------------------------------- * * ns_sockpair -- * *	Create a pair of connected sockets via brute force.  Sock pairs *  	are used as trigger pipes in various subsystems. * * Results: *  	0 if ok, -1 on error. * * Side effects: *  	None. * *---------------------------------------------------------------------- */intns_sockpair(SOCKET socks[2]){    SOCKET          sock;    struct sockaddr_in ia[2];    int             size;    size = sizeof(struct sockaddr_in);    sock = Ns_SockListen("127.0.0.1", 0);    if (sock == INVALID_SOCKET ||    	getsockname(sock, (struct sockaddr *) &ia[0], &size) != 0) {	return -1;    }    size = sizeof(struct sockaddr_in);    socks[1] = Ns_SockConnect("127.0.0.1", (int) ntohs(ia[0].sin_port));    if (socks[1] == INVALID_SOCKET ||    	getsockname(socks[1], (struct sockaddr *) &ia[1], &size) != 0) {	ns_sockclose(sock);	return -1;    }    size = sizeof(struct sockaddr_in);    socks[0] = accept(sock, (struct sockaddr *) &ia[0], &size);    ns_sockclose(sock);    if (socks[0] == INVALID_SOCKET) {	ns_sockclose(socks[1]);	return -1;    }    if (ia[0].sin_addr.s_addr != ia[1].sin_addr.s_addr ||	ia[0].sin_port != ia[1].sin_port) {	ns_sockclose(socks[0]);	ns_sockclose(socks[1]);	return -1;    }    return 0;}/* *---------------------------------------------------------------------- * * Ns_SockListen -- * *	Simple socket listen implementation for Win32 without privileged *  	port issues. * * Results: *	Socket descriptor or INVALID_SOCKET on error. * * Side effects: *	None. * *---------------------------------------------------------------------- */SOCKETNs_SockListen(char *address, int port){    SOCKET          sock;    struct sockaddr_in sa;    if (Ns_GetSockAddr(&sa, address, port) != NS_OK) {	return -1;    }    sock = Ns_SockBind(&sa);    if (sock != INVALID_SOCKET && listen(sock, nsconf.backlog) != 0) {	ns_sockclose(sock);	sock = INVALID_SOCKET;    }    return sock;}/* *---------------------------------------------------------------------- * * link, symlink, kill -- * *  	Stubs for missing Unix routines.  This is done simply to avoid *  	more ifdef's in the code. * * Results: *  	-1. * * Side effects: *  	Sets errno to EINVAL. * *---------------------------------------------------------------------- */intlink(char *from, char *to){    errno = EINVAL;    return -1;}intsymlink(char *from, char *to){    errno = EINVAL;    return -1;}intkill(int pid, int sig){    errno = EINVAL;    return -1;}/* *---------------------------------------------------------------------- * * truncate -- * *	Implement Unix truncate. * * Results: *  	0 if ok or -1 on error. * * Side effects: *  	File is opened, truncated, and closed. * *---------------------------------------------------------------------- */inttruncate(char *file, off_t size){    int fd;    fd = open(file, O_WRONLY|O_BINARY);    if (fd < 0) {	return -1;    }    size = _chsize(fd, size);    close(fd);    if (size != 0) {	return -1;    }    return 0;}/* *---------------------------------------------------------------------- * * ConsoleHandler -- * *	Callback when the Ctrl-C is pressed. * * Results: *  	TRUE. * * Side effects: *  	Shutdown is initiated. * *---------------------------------------------------------------------- */static BOOL WINAPIConsoleHandler(DWORD ignored){    SetConsoleCtrlHandler(ConsoleHandler, FALSE);    NsSendSignal(NS_SIGTERM);    return TRUE;}/* *---------------------------------------------------------------------- * * GetServiceName -- * *	Construct the service name for the corresponding server. * * Results: *  	Pointer to given dstring string. * * Side effects: *  	None. * *---------------------------------------------------------------------- */static char *GetServiceName(Ns_DString *dsPtr){    Ns_DStringVarAppend(dsPtr, NSD_NAME, "-", nsServer, NULL);    return dsPtr->string;}/* *---------------------------------------------------------------------- * * StartTicker, StopTicker -- * *	Start and stop the background ticker thread which keeps the *  	service control manager informed of during startup and *  	shutdown. * * Results: *  	None. * * Side effects: *  	Thread is created or signalled to stop and joined. * *---------------------------------------------------------------------- */static voidStartTicker(DWORD pending){    Ns_MutexLock(&lock);    tick = 1;    Ns_MutexUnlock(&lock);    Ns_ThreadCreate(ServiceTicker, (void *) pending, 0, &tickThread);}static voidStopTicker(void){    Ns_MutexLock(&lock);    tick = 0;    Ns_CondBroadcast(&cond);    Ns_MutexUnlock(&lock);    Ns_ThreadJoin(&tickThread, NULL);}/* *---------------------------------------------------------------------- * * ServiceTicker -- * *	Background ticker created by StartTicker which does nothing *  	but send the message repeatedly until signaled to stop. * * Results: *  	None. * * Side effects: *  	Service control manager is kept informed of progress. * *---------------------------------------------------------------------- */static voidServiceTicker(void *arg){    Ns_Time timeout;    DWORD pending = (DWORD) arg;    Ns_ThreadSetName("-ticker-");    Ns_MutexLock(&lock);    do {        ReportStatus(pending, NO_ERROR, 2000);	Ns_GetTime(&timeout);	Ns_IncrTime(&timeout, 1, 0);	Ns_CondTimedWait(&cond, &lock, &timeout);    } while (tick);    Ns_MutexUnlock(&lock);}