Skip to content
Snippets Groups Projects
ntsvcs.c 38.1 KiB
Newer Older
/* Synchronet BBS as a set of Windows NT Services */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

/* Synchronet-specific headers */
#include "sbbs.h"		/* various */
#include "sbbs_ini.h"	/* sbbs_read_ini() */
#include "ftpsrvr.h"	/* ftp_startup_t, ftp_server */
#include "websrvr.h"	/* web_startup_t, web_server */
#include "mailsrvr.h"	/* mail_startup_t, mail_server */
#include "services.h"	/* services_startup_t, services_thread */
#include "ntsvcs.h"		/* NT service names */

/* Windows-specific headers */
#include <winsvc.h>

#define NTSVC_TIMEOUT_STARTUP	30000	/* Milliseconds */
#define NTSVC_TIMEOUT_TERMINATE	30000	/* Milliseconds */

#define STRLEN_SYNCHRONET		10		/* this number ain't change'n */
#define SERVICE_NOT_INSTALLED	(SERVICE_DISABLED+1)

static void WINAPI bbs_ctrl_handler(DWORD dwCtrlCode);
static void WINAPI ftp_ctrl_handler(DWORD dwCtrlCode);
static void WINAPI web_ctrl_handler(DWORD dwCtrlCode);
static void WINAPI mail_ctrl_handler(DWORD dwCtrlCode);
static void WINAPI services_ctrl_handler(DWORD dwCtrlCode);
bbs_startup_t		bbs_startup;
ftp_startup_t		ftp_startup;
mail_startup_t		mail_startup;
services_startup_t	services_startup;
web_startup_t		web_startup;
link_list_t			login_attempt_list;
typedef struct {
	char*					name;
	char*					display_name;
	char*					description;
	void*					startup;
	void 					(*thread)(void* arg);
	void					(*terminate)(void);
	void					(WINAPI *ctrl_handler)(DWORD);
	HANDLE					event_handle;
	SERVICE_STATUS			status;
	SERVICE_STATUS_HANDLE	status_handle;
} sbbs_ntsvc_t;

sbbs_ntsvc_t bbs ={	
	"Synchronet Terminal Server",
	"Provides support for Telnet, RLogin, and SSH clients and executes timed events. " \
		"This service provides the critical functions of your Synchronet BBS.",
	&bbs_startup,
	bbs_ctrl_handler,
	INVALID_HANDLE_VALUE
/* This is not (currently) a separate service, use this for logging only */
sbbs_ntsvc_t event ={	
	NTSVC_NAME_EVENT,
	NULL,
	NULL,
	NULL,
	NULL,
	"Synchronet FTP Server",
	"Provides support for FTP clients (including web browsers) for file transfers.",
	&ftp_startup,
	ftp_ctrl_handler,
	INVALID_HANDLE_VALUE
	"Synchronet Web Server",
	"Provides support for Web (HTML/HTTP) clients (browsers).",
	&web_startup,
	web_ctrl_handler,
	INVALID_HANDLE_VALUE
	"Synchronet SMTP/POP3 Mail Server",
	"Sends and receives Internet e-mail (using SMTP) and allows users to remotely " \
		"access their e-mail using an Internet mail client (using POP3).",
	&mail_startup,
	mail_ctrl_handler,
	INVALID_HANDLE_VALUE
	"Synchronet Services",
	"Plugin servers (usually in JavaScript) for any TCP/UDP protocol. " \
		"Stock services include Finger, Gopher, NNTP, and IRC. Edit your ctrl/services.ini " \
		"file for configuration of individual Synchronet Services.",
	&services_startup,
	&services_startup.options,
	services_ctrl_handler,
	INVALID_HANDLE_VALUE
/* This list is used for enumerating all services */
sbbs_ntsvc_t* ntsvc_list[] = {
	&bbs,
	&ftp,
	&web,
	&mail,
	&services,
	NULL
};
							
/****************************************/
/* Service Control Handlers (Callbacks) */
/****************************************/
/* Common control handler for all services */
static void svc_ctrl_handler(sbbs_ntsvc_t* svc, DWORD dwCtrlCode)
{
	switch(dwCtrlCode) {
		case SERVICE_CONTROL_RECYCLE:
			*svc->recycle_now=TRUE;
			break;
		case SERVICE_CONTROL_STOP:
		case SERVICE_CONTROL_SHUTDOWN:
			svc->status.dwWaitHint=NTSVC_TIMEOUT_TERMINATE;
			svc->status.dwCurrentState=SERVICE_STOP_PENDING;
			break;
	}
	SetServiceStatus(svc->status_handle, &svc->status);
}

/* Service-specific control handler stub functions */
static void WINAPI bbs_ctrl_handler(DWORD dwCtrlCode)
{
	svc_ctrl_handler(&bbs, dwCtrlCode);
}

static void WINAPI ftp_ctrl_handler(DWORD dwCtrlCode)
{
	svc_ctrl_handler(&ftp, dwCtrlCode);
static void WINAPI web_ctrl_handler(DWORD dwCtrlCode)
	svc_ctrl_handler(&web, dwCtrlCode);
static void WINAPI mail_ctrl_handler(DWORD dwCtrlCode)
	svc_ctrl_handler(&mail, dwCtrlCode);
static void WINAPI services_ctrl_handler(DWORD dwCtrlCode)
	svc_ctrl_handler(&services, dwCtrlCode);
static WORD event_type(int level)
{
	switch(level) {
		case LOG_WARNING:
			return(EVENTLOG_WARNING_TYPE);
		case LOG_NOTICE:
		case LOG_INFO:
		case LOG_DEBUG:
			return(EVENTLOG_INFORMATION_TYPE);	/* same as EVENT_LOG_SUCCESS */
	}
/*
	LOG_EMERG
	LOG_ALERT
	LOG_CRIT
	LOG_ERR
*/
	return(EVENTLOG_ERROR_TYPE);
}

/**************************************/
/* Common Service Log Ouptut Function */
/**************************************/
static int svc_lputs(void* p, int level, const char* str)
	sbbs_ntsvc_t* svc = (sbbs_ntsvc_t*)p;
	/* Debug Logging */
	if(svc==NULL || svc->debug) {
		snprintf(debug,sizeof(debug),"%s: %s",svc==NULL ? "Synchronet" : svc->name, str);
		OutputDebugString(debug);
	}
	if(svc==NULL)
		return(0);

	len = strlen(str);
	SAFECOPY(msg.buf, str);
	msg.level = level;
	/* Mailslot Logging (for sbbsctrl) */
	if(svc->log_handle != INVALID_HANDLE_VALUE /* Invalid log handle? */
		&& !GetMailslotInfo(svc->log_handle, NULL, NULL, NULL, NULL)) {
		/* Close and try to re-open */
		CloseHandle(svc->log_handle);
		svc->log_handle=INVALID_HANDLE_VALUE;
	}

	if(svc->log_handle == INVALID_HANDLE_VALUE) {
		SAFEPRINTF(fname,"\\\\.\\mailslot\\%s.log",svc->name);
			fname,					/* pointer to name of the file */
			GENERIC_WRITE,			/* access (read-write) mode */
			FILE_SHARE_READ,		/* share mode */
			NULL,					/* pointer to security attributes */
			OPEN_EXISTING,			/* how to create */
			FILE_ATTRIBUTE_NORMAL,  /* file attributes */
			NULL					/* handle to file with attributes to copy */
	if(svc->log_handle != INVALID_HANDLE_VALUE) {
		len=sizeof(msg);
		if(!WriteFile(svc->log_handle,&msg,len,&wr,NULL) || wr!=len) {
			/* This most likely indicates the server closed the mailslot */
			safe_snprintf(debug, sizeof(debug), "!ERROR %d writing %u bytes to %s pipe (wr=%d)"
				,GetLastError(),len,svc->name,wr);
			OutputDebugString(debug);
			/* So close the handle and attempt re-open next time */
			CloseHandle(svc->log_handle);
			svc->log_handle=INVALID_HANDLE_VALUE;
	
	/* Event Logging */
	if(level <= (*svc->log_level)) {
		if(svc->event_handle == NULL)
			svc->event_handle = RegisterEventSource(
				NULL,		/* server name for source (NULL = local computer) */
				svc->name   /* source name for registered handle */
				);
		if(svc->event_handle != NULL)
			ReportEvent(svc->event_handle,  /* event log handle */
				event_type(level),		/* event type */
				0,						/* category zero */
				0,						/* event identifier */
				NULL,					/* no user security identifier */
				1,						/* one string */
				0,						/* no data */
				(LPCSTR*)&str,			/* pointer to string array */
				NULL);					/* pointer to data */
    return(0);
/************************************/
/* Shared Service Callback Routines */
/************************************/

static void svc_set_state(void* p, enum server_state state)
	if(state == SERVER_READY) {
		svc->status.dwCurrentState=SERVICE_RUNNING;
		svc->status.dwControlsAccepted|=SERVICE_ACCEPT_STOP;
		SetServiceStatus(svc->status_handle, &svc->status);
	}
static void read_ini(sbbs_ntsvc_t* svc)
{
	char	str[MAX_PATH*2];
	FILE*	fp;
	bbs_startup_t*		bbs_startup=NULL;
	ftp_startup_t*		ftp_startup=NULL;
	mail_startup_t*		mail_startup=NULL;
	services_startup_t*	services_startup=NULL;
	web_startup_t*		web_startup=NULL;

	if(svc==&bbs)
		bbs_startup=svc->startup;
	else if(svc==&ftp)
		ftp_startup=svc->startup;
	else if(svc==&web)
		web_startup=svc->startup;
	else if(svc==&mail)
		mail_startup=svc->startup;
	else if(svc==&services)
		services_startup=svc->startup;

	if((fp=fopen(ini_file,"r"))!=NULL) {
		SAFEPRINTF(str,"Reading %s",ini_file);

	/* We call this function to set defaults, even if there's no .ini file */
		,NULL	/* global_startup */
		,NULL	,bbs_startup
		,NULL	,ftp_startup 
		,NULL	,web_startup
		,NULL	,mail_startup 
		,NULL	,services_startup
		);

	/* close .ini file here */
	if(fp!=NULL)
		fclose(fp);
}

static void svc_recycle(void *p)
{
	read_ini((sbbs_ntsvc_t*)p);
}

static void svc_terminated(void* p, int code)
	sbbs_ntsvc_t* svc = (sbbs_ntsvc_t*)p;

	if(code) {
		svc->status.dwWin32ExitCode=ERROR_SERVICE_SPECIFIC_ERROR;
		svc->status.dwServiceSpecificExitCode=code;
		SetServiceStatus(svc->status_handle, &svc->status);
static void svc_clients(void* p, int active)
{
	sbbs_ntsvc_t* svc = (sbbs_ntsvc_t*)p;
}

/***************/
/* ServiceMain */
/***************/

/* Common ServiceMain for all services */
static void WINAPI svc_main(sbbs_ntsvc_t* svc, DWORD argc, LPTSTR *argv)
	DWORD	i;
	char*	arg;

	for(i=0;i<argc;i++) {
		arg=argv[i];
		if(*arg=='-' || *arg=='/')
			arg++;
		if(!stricmp(arg,"debug"))
			svc->debug=TRUE;
		if(!stricmp(arg,"loglevel") && i+1<argc)
			(*svc->log_level)=strtol(argv[++i],NULL,0);
	SAFEPRINTF(str,"Starting NT Service: %s",svc->display_name);
    if((svc->status_handle = RegisterServiceCtrlHandler(svc->name, svc->ctrl_handler))==0) {
		SAFEPRINTF(str,"!ERROR %d registering service control handler",GetLastError());
	memset(&svc->status,0,sizeof(SERVICE_STATUS));
	svc->status.dwServiceType=SERVICE_WIN32_SHARE_PROCESS;
	svc->status.dwControlsAccepted=SERVICE_ACCEPT_SHUTDOWN;
	svc->status.dwWaitHint=NTSVC_TIMEOUT_STARTUP;
	svc->status.dwCurrentState=SERVICE_START_PENDING;
	SetServiceStatus(svc->status_handle, &svc->status);
	svc->thread(svc->startup);
	svc->status.dwCurrentState=SERVICE_STOPPED;
	SetServiceStatus(svc->status_handle, &svc->status);
	if(svc->log_handle!=INVALID_HANDLE_VALUE) {
		CloseHandle(svc->log_handle);
		svc->log_handle=INVALID_HANDLE_VALUE;

	if(svc->event_handle!=NULL) {
		DeregisterEventSource(svc->event_handle);
		svc->event_handle=NULL;
	}
/* Service-specific ServiceMain stub functions */
static void WINAPI bbs_start(DWORD dwArgc, LPTSTR *lpszArgv)

	/* Events are (currently) part of the BBS service */
	if(event.log_handle!=INVALID_HANDLE_VALUE) {
		CloseHandle(event.log_handle);
		event.log_handle=INVALID_HANDLE_VALUE;
	}
static void WINAPI ftp_start(DWORD dwArgc, LPTSTR *lpszArgv)
static void WINAPI web_start(DWORD dwArgc, LPTSTR *lpszArgv)
static void WINAPI mail_start(DWORD dwArgc, LPTSTR *lpszArgv)
	svc_main(&mail, dwArgc, lpszArgv);
static void WINAPI services_start(DWORD dwArgc, LPTSTR *lpszArgv)
	svc_main(&services, dwArgc, lpszArgv);
}

/******************************************/
/* NT Serivce Install/Uninstall Functions */
/******************************************/

/* ChangeServiceConfig2 is a Win2K+ API function, must call dynamically */
typedef WINADVAPI BOOL (WINAPI *ChangeServiceConfig2_t)(SC_HANDLE, DWORD, LPCVOID);

static void describe_service(HANDLE hSCMlib, SC_HANDLE hService, char* description)
{
	ChangeServiceConfig2_t changeServiceConfig2;
	static SERVICE_DESCRIPTION service_desc;
  
	if(hSCMlib==NULL)
		return;

	service_desc.lpDescription=description;

	if((changeServiceConfig2 = (ChangeServiceConfig2_t)GetProcAddress(hSCMlib, "ChangeServiceConfig2A"))!=NULL)
		changeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &service_desc);
}

static BOOL register_event_source(char* name, char* path)
{
	char	keyname[256];
	HKEY	hKey;
	DWORD	type;
	DWORD	disp;
	LONG	retval;
	char*	value;

	SAFEPRINTF(keyname,"system\\CurrentControlSet\\services\\eventlog\\application\\%s",name);
		HKEY_LOCAL_MACHINE,			/* handle to an open key */
		keyname,			/* address of subkey name */
		0,				/* reserved */
		"",				/* address of class string */
		0,				/* special options flag */
		KEY_ALL_ACCESS, /* desired security access */
		NULL,           /* address of key security structure */
		&hKey,			/* address of buffer for opened handle */
		&disp			/* address of disposition value buffer */
		);

	if(retval!=ERROR_SUCCESS) {
		fprintf(stderr,"!Error %d creating/opening registry key (HKLM\\%s)\n"
			,retval,keyname);
		return(FALSE);
	}

	value="EventMessageFile";
	retval=RegSetValueEx(
		hKey,			/* handle to key to set value for */
		value,			/* name of the value to set */
		0,				/* reserved */
		REG_SZ,			/* flag for value type */
		path,			/* address of value data */
		strlen(path)	/* size of value data */
		);

	if(retval!=ERROR_SUCCESS) {
		RegCloseKey(hKey);
		fprintf(stderr,"!Error %d setting registry key value (%s)\n"
			,retval,value);
		return(FALSE);
	}

	value="TypesSupported";
	type=EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
	retval=RegSetValueEx(
		hKey,			/* handle to key to set value for */
		value,			/* name of the value to set */
		0,				/* reserved */
		REG_DWORD,		/* flag for value type */
		(BYTE*)&type,	/* address of value data */
		sizeof(type)	/* size of value data */
		);

	RegCloseKey(hKey);

	if(retval!=ERROR_SUCCESS) {
		fprintf(stderr,"!Error %d setting registry key value (%s)\n"
			,retval,value);
		return(FALSE);
	}

	return(TRUE);
}

static const char* start_type_desc(DWORD start_type)
{
	static char str[128];

	switch(start_type) {
		case SERVICE_AUTO_START:			return("Startup: Automatic");
		case SERVICE_DEMAND_START:			return("Startup: Manual");
		case SERVICE_DISABLED:				return("Disabled");
		case SERVICE_NOT_INSTALLED:			return("Not installed");
	}

	SAFEPRINTF(str,"Start_type: %d", start_type);
	return(str);
}

static const char* state_desc(DWORD state)
{
	static char str[128];

	switch(state) {
		case SERVICE_STOPPED:				return("Stopped");
		case SERVICE_START_PENDING:			return("Start Pending");
		case SERVICE_STOP_PENDING:			return("Stop Pending");
		case SERVICE_RUNNING:				return("Running");
		case SERVICE_CONTINUE_PENDING:		return("Continue Pending");
		case SERVICE_PAUSE_PENDING:			return("Pause Pending");
		case SERVICE_PAUSED:				return("Paused");
	}

	SAFEPRINTF(str,"State: %d", state);
static const char* control_desc(DWORD ctrl)
{
	static char str[128];

	switch(ctrl) {
		case SERVICE_CONTROL_STOP:
			return("Stopping");
		case SERVICE_CONTROL_PAUSE:
			return("Pausing");
		case SERVICE_CONTROL_CONTINUE:
			return("Continuing");
		case SERVICE_CONTROL_INTERROGATE:
			return("Interrogating");
		case SERVICE_CONTROL_SHUTDOWN:
			return("Shutting-down");

		/* Synchronet-specific */
		case SERVICE_CONTROL_RECYCLE:
			return("Recycling");
	}
	SAFEPRINTF(str,"Control: %d", ctrl);
	return(str);
}

/****************************************************************************/
/* Utility function to detect if a service is currently disabled			*/
/****************************************************************************/
static DWORD get_service_info(SC_HANDLE hSCManager, char* name, DWORD* state)
{
    SC_HANDLE		hService;
	DWORD			size;
	DWORD			err;
deuce's avatar
deuce committed
	DWORD			ret;
	SERVICE_STATUS	status;
	LPQUERY_SERVICE_CONFIG service_config;

	if(state!=NULL)
		*state=SERVICE_STOPPED;

	if((hService = OpenService(hSCManager, name, SERVICE_ALL_ACCESS))==NULL) {
		if((err=GetLastError())==ERROR_SERVICE_DOES_NOT_EXIST)
			return(SERVICE_NOT_INSTALLED);
		printf("\n!ERROR %d opening service: %s\n",err,name);
		return(-1);
	}

	if(QueryServiceConfig(
		hService,		/* handle of service */
		NULL,			/* address of service config. structure */
		0,				/* size of service configuration buffer */
		&size			/* address of variable for bytes needed */
		) || GetLastError()!=ERROR_INSUFFICIENT_BUFFER) {
		printf("\n!Unexpected QueryServiceConfig ERROR %u\n",err=GetLastError());
		CloseServiceHandle(hService);
		return(-1);
	}

	if(state!=NULL && QueryServiceStatus(hService,&status))
		*state=status.dwCurrentState;

deuce's avatar
deuce committed
	if((service_config=malloc(size))==NULL) {
		printf("\n!ERROR allocating %u bytes of memory\n", size);
		CloseServiceHandle(hService);
		hService,		/* handle of service */
		service_config,	/* address of service config. structure */
		size,			/* size of service configuration buffer */
		&size			/* address of variable for bytes needed */
		)) {
		printf("\n!QueryServiceConfig ERROR %u\n",GetLastError());
		CloseServiceHandle(hService);
deuce's avatar
deuce committed
		free(service_config);
		return(-1);
	}
    CloseServiceHandle(hService);
mcmlxxix's avatar
mcmlxxix committed
	ret=service_config->dwStartType;
deuce's avatar
deuce committed
	free(service_config);
deuce's avatar
deuce committed
	return ret;
/****************************************************************************/
/* Utility function to create a service with description (on Win2K+)		*/
/****************************************************************************/
static void create_service(HANDLE hSCMlib, SC_HANDLE hSCManager
								,char* name, char* display_name, char* description, char* path
								,BOOL autostart)
{
    SC_HANDLE   hService;
	DWORD		err;
	DWORD		start_type = autostart ? SERVICE_AUTO_START : SERVICE_DEMAND_START;
	printf("Installing service: %-*s ... ", STRLEN_MAX_DISPLAY_NAME, display_name);

    hService = CreateService(
        hSCManager,						/* SCManager database */
        name,							/* name of service */
        display_name,					/* name to display */
        SERVICE_ALL_ACCESS,				/* desired access */
        SERVICE_WIN32_SHARE_PROCESS,	/* service type */
		start_type,						/* start type (auto or manual) */
        SERVICE_ERROR_NORMAL,			/* error control type */
        path,							/* service's binary */
        NULL,							/* no load ordering group */
        NULL,							/* no tag identifier */
        "",								/* dependencies */
        NULL,							/* LocalSystem account */
        NULL);							/* no password */
	if(hService==NULL) {
		if((err=GetLastError())==ERROR_SERVICE_EXISTS)
			printf("Already installed\n");
		else
			printf("!ERROR %d\n",err);
	}
	else {
		describe_service(hSCMlib, hService,description);
		CloseServiceHandle(hService);
		printf("%s\n", start_type_desc(start_type));
rswindell's avatar
rswindell committed
		register_event_source(NTSVC_NAME_EVENT,path);	/* Create SynchronetEvent event source */
/****************************************************************************/
/* Install one or all services												*/
/****************************************************************************/
static int install(const char* svc_name)
	HANDLE		hSCMlib;
    SC_HANDLE   hSCManager;
    char		path[MAX_PATH+1];

	printf("Installing Synchronet NT Services...\n");

    if(GetModuleFileName(NULL,path,sizeof(path))==0)
    {
        fprintf(stderr,"!ERROR %d getting module file name\n",GetLastError());
        return(-1);
    }

    hSCManager = OpenSCManager(
                        NULL,                   /* machine (NULL == local) */
                        NULL,                   /* database (NULL == default) */
                        SC_MANAGER_ALL_ACCESS   /* access required */
                        );
    if(hSCManager==NULL) {
		fprintf(stderr,"!ERROR %d opening SC manager\n",GetLastError());
		return(-1);
	}

	hSCMlib = LoadLibrary("ADVAPI32.DLL");

		if(svc_name==NULL	/* All? */
			|| !stricmp(ntsvc_list[i]->name, svc_name)
			|| !stricmp(ntsvc_list[i]->name+STRLEN_SYNCHRONET, svc_name))
			create_service(hSCMlib
				,hSCManager
				,ntsvc_list[i]->name
				,ntsvc_list[i]->display_name
				,ntsvc_list[i]->description
				,path
				,ntsvc_list[i]->autostart);

	if(hSCMlib!=NULL)
		FreeLibrary(hSCMlib);

	CloseServiceHandle(hSCManager);

	return(0);
}

static SC_HANDLE open_service(SC_HANDLE hSCManager, char* name)
{
	SC_HANDLE	hService;
	DWORD		err;

	if((hService = OpenService(hSCManager, name, SERVICE_ALL_ACCESS))==NULL) {
		if((err=GetLastError())==ERROR_SERVICE_DOES_NOT_EXIST)
			printf("Not installed\n");
		else
			printf("\n!ERROR %d opening service: %s\n",err,name);
		return(NULL);
	}

	return(hService);
}

/****************************************************************************/
/* Utility function to remove a service cleanly (stopping if necessary)		*/
/****************************************************************************/
static void remove_service(SC_HANDLE hSCManager, char* name, char* disp_name)
{
    SC_HANDLE		hService;
	SERVICE_STATUS	status;

	printf("Removing: %-*s ... ", STRLEN_MAX_DISPLAY_NAME, disp_name);
    if((hService=open_service(hSCManager, name))==NULL)
    /* try to stop the service */
    if(ControlService( hService, SERVICE_CONTROL_STOP, &status))
    {
        printf("\nStopping: %-*s ... ", STRLEN_MAX_DISPLAY_NAME, disp_name);

        while(QueryServiceStatus(hService, &status) && status.dwCurrentState == SERVICE_STOP_PENDING)
			Sleep(1000);

        if(status.dwCurrentState == SERVICE_STOPPED)
    /* now remove the service */
    if(DeleteService(hService))
	else
		printf("!ERROR %d\n",GetLastError());
    CloseServiceHandle(hService);
}

/****************************************************************************/
/* Utility function to stop a service										*/
/****************************************************************************/
static void stop_service(SC_HANDLE hSCManager, char* name, char* disp_name)
{
    SC_HANDLE		hService;
	SERVICE_STATUS	status;

    printf("Stopping service: %-*s ... ", STRLEN_MAX_DISPLAY_NAME, disp_name);
    if((hService=open_service(hSCManager, name))==NULL)
    /* try to stop the service */
    if(ControlService( hService, SERVICE_CONTROL_STOP, &status))
    {
        while(QueryServiceStatus(hService, &status) && status.dwCurrentState == SERVICE_STOP_PENDING)
			Sleep(1000);

        if(status.dwCurrentState == SERVICE_STOPPED)
/****************************************************************************/
/* Utility function to stop a service										*/
/****************************************************************************/
static void control_service(SC_HANDLE hSCManager, char* name, char* disp_name, DWORD ctrl)
{
    SC_HANDLE		hService;
	SERVICE_STATUS	status;
	DWORD			err;

    printf("%s service: %-*s ... ", control_desc(ctrl), STRLEN_MAX_DISPLAY_NAME, disp_name);
    if((hService=open_service(hSCManager, name))==NULL)
		return;

    /* try to stop the service */
    if(!ControlService( hService, ctrl, &status)) {
		if((err=GetLastError())==ERROR_SERVICE_NOT_ACTIVE)
			printf("Not active\n");
		else
			printf("!ERROR %d\n",err);
    } else
		printf("Successful\n");

    CloseServiceHandle(hService);
}



/****************************************************************************/
/* Control one or all services												*/
/****************************************************************************/
static int control(const char* svc_name, DWORD ctrl)
{
	int			i;
    SC_HANDLE   hSCManager;

    hSCManager = OpenSCManager(
                        NULL,                   /* machine (NULL == local) */
                        NULL,                   /* database (NULL == default) */
                        SC_MANAGER_ALL_ACCESS   /* access required */
                        );
    if(hSCManager==NULL) {
		fprintf(stderr,"!ERROR %d opening SC manager\n",GetLastError());
		return(-1);
	}

	for(i=0;ntsvc_list[i]!=NULL;i++) {
#if 0
		if(svc_name==NULL 
			&& get_service_info(hSCManager, ntsvc_list[i]->name,NULL)==SERVICE_DISABLED)
			continue;
#endif
		if(svc_name==NULL	/* All? */
			|| !stricmp(ntsvc_list[i]->name, svc_name)
			|| !stricmp(ntsvc_list[i]->name+STRLEN_SYNCHRONET, svc_name))
			switch(ctrl) {
				case SERVICE_CONTROL_STOP:
					stop_service(hSCManager
						,ntsvc_list[i]->name
						,ntsvc_list[i]->display_name);
					break;
				default:
					control_service(hSCManager
						,ntsvc_list[i]->name
						,ntsvc_list[i]->display_name
						,ctrl);
					break;
		}
	}

	CloseServiceHandle(hSCManager);

	return(0);
}

/****************************************************************************/
/* Utility function to start a service										*/
/****************************************************************************/
static void start_service(SC_HANDLE hSCManager, char* name, char* disp_name
						  ,int argc, char** argv)
{
    SC_HANDLE		hService;
	SERVICE_STATUS	status;

    printf("Starting service: %-*s ... ", STRLEN_MAX_DISPLAY_NAME, disp_name);
    if((hService=open_service(hSCManager, name))==NULL)
		return;

	if(QueryServiceStatus(hService, &status) && status.dwCurrentState == SERVICE_RUNNING)
		/* Start the service */
		if(StartService(hService, argc, (LPCTSTR*)argv))
		{
			while(QueryServiceStatus(hService, &status) && status.dwCurrentState == SERVICE_START_PENDING)
				Sleep(1000);

			if(status.dwCurrentState == SERVICE_RUNNING)
			else
				printf("FAILED!\n");
		} else
			printf("!ERROR %u\n", GetLastError());
	}

    CloseServiceHandle(hService);
}

/****************************************************************************/
/* Uninstall one or all services											*/
/****************************************************************************/
static int uninstall(const char* svc_name)
    SC_HANDLE   hSCManager;

    hSCManager = OpenSCManager(
                        NULL,                   /* machine (NULL == local) */
                        NULL,                   /* database (NULL == default) */
                        SC_MANAGER_ALL_ACCESS   /* access required */
                        );
    if(hSCManager==NULL) {
		fprintf(stderr,"!ERROR %d opening SC manager\n",GetLastError());
		return(-1);
	}

		if(svc_name==NULL	/* All? */
			|| !stricmp(ntsvc_list[i]->name, svc_name)
			|| !stricmp(ntsvc_list[i]->name+STRLEN_SYNCHRONET, svc_name))
			remove_service(hSCManager
				,ntsvc_list[i]->name
				,ntsvc_list[i]->display_name);