Skip to content
Snippets Groups Projects
ntsvcs.c 39.5 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_MAX_DISPLAY_NAME 35
#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);
char               ini_file[MAX_PATH + 1];
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;
	char* name;
	char* display_name;
	char* description;
	void* startup;
	DWORD* options;
	bool* recycle_now;
	int* log_level;
	void (*thread)(void* arg);
	void (*terminate)(void);
	void                    (WINAPI * ctrl_handler)(DWORD);
	HANDLE log_handle;
	HANDLE event_handle;
	bool autostart;
	bool debug;
	SERVICE_STATUS status;
	SERVICE_STATUS_HANDLE status_handle;
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_ctrl_handler,
	INVALID_HANDLE_VALUE
/* This is not (currently) a separate service, use this for logging only */
sbbs_ntsvc_t  event = {
sbbs_ntsvc_t  ftp = {
	"Provides support for FTP and FTPS clients for file transfers.",
	ftp_ctrl_handler,
	INVALID_HANDLE_VALUE
sbbs_ntsvc_t  web = {
	"Provides support for Web (HTTP/HTTPS) clients (e.g. browsers).",
	web_ctrl_handler,
	INVALID_HANDLE_VALUE
sbbs_ntsvc_t  mail = {
	"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_ctrl_handler,
	INVALID_HANDLE_VALUE
sbbs_ntsvc_t  services = {
	"Plug-in 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.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) {
			*svc->recycle_now = TRUE;
		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)
{
		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)
	char          debug[1024];
	char          fname[256];
	DWORD         len;
	DWORD         wr;
	log_msg_t     msg;
	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);
	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 */
/************************************/
/* 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);
		svc_lputs(NULL, LOG_INFO, str);

	/* 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
	              );
static void svc_recycle(void *p)
{
	read_ini((sbbs_ntsvc_t*)p);
}

static void svc_terminated(void* p, int code)
	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)
	char  str[256];
	DWORD i;
	char* arg;
	for (i = 0; i < argc; i++) {
		arg = argv[i];
		if (*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);
	svc_lputs(svc, LOG_INFO, str);
	if ((svc->status_handle = RegisterServiceCtrlHandler(svc->name, svc->ctrl_handler)) == 0) {
		SAFEPRINTF(str, "!ERROR %d registering service control handler", GetLastError());
		svc_lputs(NULL, LOG_ERR, str);
	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) {
		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) {
		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 Service Install/Uninstall Functions */
/******************************************/

static void describe_service(SC_HANDLE hService, char* description)
	static SERVICE_DESCRIPTION service_desc = {0};
	service_desc.lpDescription = description;
	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);

	retval = RegCreateKeyEx(
		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);
	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) {
		fprintf(stderr, "!Error %d setting registry key value (%s)\n"
		        , retval, value);
	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 */
	if (retval != ERROR_SUCCESS) {
		fprintf(stderr, "!Error %d setting registry key value (%s)\n"
		        , retval, value);
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);
}

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

		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];

			return "Continuing";
		case SERVICE_CONTROL_INTERROGATE:
			return "Interrogating";
			return "Shutting-down";

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

/****************************************************************************/
/* 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;
	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);
	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);
	if (state != NULL && QueryServiceStatus(hService, &status))
		*state = status.dwCurrentState;
	if ((service_config = malloc(size)) == NULL) {
		printf("\n!ERROR allocating %u bytes of memory\n", size);
		CloseServiceHandle(hService);
	if (!QueryServiceConfig(
			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);
	CloseServiceHandle(hService);
	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(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("!ERROR %d\n", err);
		describe_service(hService, description);
		CloseServiceHandle(hService);
		printf("%s\n", start_type_desc(start_type));
		register_event_source(name, path);
		register_event_source(NTSVC_NAME_EVENT, path);   /* Create SynchronetEvent event source */
/****************************************************************************/
/* Install one or all services												*/
/****************************************************************************/
static int install(const char* svc_name)
	int       i;
	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());
	}

	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());
	for (i = 0; ntsvc_list[i] != NULL; i++)
		if (svc_name == NULL   /* All? */
		    || !stricmp(ntsvc_list[i]->name, svc_name)
		    || !stricmp(ntsvc_list[i]->name + STRLEN_SYNCHRONET, svc_name))
			create_service(hSCManager
			               , ntsvc_list[i]->name
			               , ntsvc_list[i]->display_name
			               , ntsvc_list[i]->description
			               , path
			               , ntsvc_list[i]->autostart);

	CloseServiceHandle(hSCManager);

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("\n!ERROR %d opening service: %s\n", err, name);
/****************************************************************************/
/* 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)
			printf("Stopped, ");
		else
			printf("FAILED!, ");
	}
	/* now remove the service */
	if (DeleteService(hService))
		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)
		if (status.dwCurrentState == SERVICE_STOPPED)
			printf("Stopped\n");
		else
			printf("FAILED!\n");
	} else
	CloseServiceHandle(hService);
/****************************************************************************/
/* 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)
	/* try to stop the service */
	if (!ControlService( hService, ctrl, &status)) {
		if ((err = GetLastError()) == ERROR_SERVICE_NOT_ACTIVE)
			printf("!ERROR %d\n", err);
	} else
	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());
	for (i = 0; ntsvc_list[i] != NULL; i++) {
		if (svc_name == NULL
		    && get_service_info(hSCManager, ntsvc_list[i]->name, NULL) == SERVICE_DISABLED)
		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);
/****************************************************************************/
/* 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)
	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)
			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)
	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());
	for (i = 0; ntsvc_list[i] != NULL; i++)
		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);

	CloseServiceHandle(hSCManager);

/****************************************************************************/
/* Utility function to disable a service									*/
/****************************************************************************/
static void set_service_start_type(SC_HANDLE hSCManager, char* name
                                   , char* disp_name, DWORD start_type)
	SC_HANDLE hService;