ntsvcs.c 39.5 KB
Newer Older
1
2
3
4
5
6
7
8
/* Synchronet BBS as a set of Windows NT Services */

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
9
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
 *																			*
 * 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										*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * 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 */
43
#include "ntsvcs.h"		/* NT service names */
44
45
46
47

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

48
49
50
#define NTSVC_TIMEOUT_STARTUP	30000	/* Milliseconds */
#define NTSVC_TIMEOUT_TERMINATE	30000	/* Milliseconds */

51
52
#define STRLEN_MAX_DISPLAY_NAME	35

53
#define STRLEN_SYNCHRONET		10		/* this number ain't change'n */
54

55
56
#define SERVICE_NOT_INSTALLED	(SERVICE_DISABLED+1)

57
58
59
60
61
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);
62

63
/* Global variables */
64

65
char				ini_file[MAX_PATH+1];
66
67
68
69
70
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;
71
link_list_t			login_attempt_list;
72

73
74
75
76
77
typedef struct {
	char*					name;
	char*					display_name;
	char*					description;
	void*					startup;
78
	DWORD*					options;
79
	BOOL*					recycle_now;
80
	int*					log_level;
rswindell's avatar
rswindell committed
81
82
	void DLLCALL			(*thread)(void* arg);
	void DLLCALL			(*terminate)(void);
83
	void					(WINAPI *ctrl_handler)(DWORD);
84
	HANDLE					log_handle;
85
	HANDLE					event_handle;
86
	BOOL					autostart;
87
	BOOL					debug;
88
89
90
91
92
	SERVICE_STATUS			status;
	SERVICE_STATUS_HANDLE	status_handle;
} sbbs_ntsvc_t;

sbbs_ntsvc_t bbs ={	
93
	NTSVC_NAME_BBS,
94
	"Synchronet Terminal Server",
95
	"Provides support for Telnet, RLogin, and SSH clients and executes timed events. " \
96
97
		"This service provides the critical functions of your Synchronet BBS.",
	&bbs_startup,
98
	&bbs_startup.options,
99
	&bbs_startup.recycle_now,
100
	&bbs_startup.log_level,
101
	bbs_thread,
102
	bbs_terminate,
103
104
	bbs_ctrl_handler,
	INVALID_HANDLE_VALUE
105
106
};

107
108
109
110
111
112
113
/* This is not (currently) a separate service, use this for logging only */
sbbs_ntsvc_t event ={	
	NTSVC_NAME_EVENT,
	NULL,
	NULL,
	NULL,
	NULL,
114
	NULL,
115
	&bbs_startup.log_level,
116
117
	NULL,
	NULL,
118
	NULL,
119
120
121
	INVALID_HANDLE_VALUE
};

122
sbbs_ntsvc_t ftp = {
123
	NTSVC_NAME_FTP,
124
125
126
	"Synchronet FTP Server",
	"Provides support for FTP clients (including web browsers) for file transfers.",
	&ftp_startup,
127
	&ftp_startup.options,
128
	&ftp_startup.recycle_now,
129
	&ftp_startup.log_level,
130
	ftp_server,
131
	ftp_terminate,
132
133
	ftp_ctrl_handler,
	INVALID_HANDLE_VALUE
134
135
136
};

sbbs_ntsvc_t web = {
137
	NTSVC_NAME_WEB,
138
139
140
	"Synchronet Web Server",
	"Provides support for Web (HTML/HTTP) clients (browsers).",
	&web_startup,
141
	&web_startup.options,
142
	&web_startup.recycle_now,
143
	&web_startup.log_level,
144
	web_server,
145
	web_terminate,
146
147
	web_ctrl_handler,
	INVALID_HANDLE_VALUE
148
149
150
};

sbbs_ntsvc_t mail = {
151
	NTSVC_NAME_MAIL,
152
153
154
155
	"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,
156
	&mail_startup.options,
157
	&mail_startup.recycle_now,
158
	&mail_startup.log_level,
159
	mail_server,
160
	mail_terminate,
161
162
	mail_ctrl_handler,
	INVALID_HANDLE_VALUE
163
164
165
};

sbbs_ntsvc_t services = {
166
	NTSVC_NAME_SERVICES,
167
168
169
170
171
	"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,
172
	&services_startup.options,
173
	&services_startup.recycle_now,
174
	&services_startup.log_level,
175
	services_thread,
176
	services_terminate,
177
178
	services_ctrl_handler,
	INVALID_HANDLE_VALUE
179
};
180

181
/* This list is used for enumerating all services */
182
183
184
185
186
187
188
189
190
sbbs_ntsvc_t* ntsvc_list[] = {
	&bbs,
	&ftp,
	&web,
	&mail,
	&services,
	NULL
};
							
191
/****************************************/
192
/* Service Control Handlers (Callbacks) */
193
/****************************************/
194

195
196
197
198
/* Common control handler for all services */
static void svc_ctrl_handler(sbbs_ntsvc_t* svc, DWORD dwCtrlCode)
{
	switch(dwCtrlCode) {
199
200
201
		case SERVICE_CONTROL_RECYCLE:
			*svc->recycle_now=TRUE;
			break;
202
203
204
205
206
207
		case SERVICE_CONTROL_MUTE:
			*svc->options|=BBS_OPT_MUTE;
			break;
		case SERVICE_CONTROL_UNMUTE:
			*svc->options&=~BBS_OPT_MUTE;
			break;
208
209
		case SERVICE_CONTROL_STOP:
		case SERVICE_CONTROL_SHUTDOWN:
210
			svc->terminate();
211
212
213
214
215
216
217
218
			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 */
219
220
static void WINAPI bbs_ctrl_handler(DWORD dwCtrlCode)
{
221
	svc_ctrl_handler(&bbs, dwCtrlCode);
222
223
224
225
}

static void WINAPI ftp_ctrl_handler(DWORD dwCtrlCode)
{
226
	svc_ctrl_handler(&ftp, dwCtrlCode);
227
228
}

229
static void WINAPI web_ctrl_handler(DWORD dwCtrlCode)
230
{
231
	svc_ctrl_handler(&web, dwCtrlCode);
232
233
}

234
static void WINAPI mail_ctrl_handler(DWORD dwCtrlCode)
235
{
236
	svc_ctrl_handler(&mail, dwCtrlCode);
237
238
}

239
static void WINAPI services_ctrl_handler(DWORD dwCtrlCode)
240
{
241
	svc_ctrl_handler(&services, dwCtrlCode);
242
243
}

244
245
246
247
248
249
250
251
static WORD event_type(int level)
{
	switch(level) {
		case LOG_WARNING:
			return(EVENTLOG_WARNING_TYPE);
		case LOG_NOTICE:
		case LOG_INFO:
		case LOG_DEBUG:
252
			return(EVENTLOG_INFORMATION_TYPE);	/* same as EVENT_LOG_SUCCESS */
253
254
255
256
257
258
259
260
261
262
	}
/*
	LOG_EMERG
	LOG_ALERT
	LOG_CRIT
	LOG_ERR
*/
	return(EVENTLOG_ERROR_TYPE);
}

263
264
265
/**************************************/
/* Common Service Log Ouptut Function */
/**************************************/
266
static int svc_lputs(void* p, int level, const char* str)
267
{
268
269
	char	debug[1024];
	char	fname[256];
270
271
	DWORD	len;
	DWORD	wr;
272
	log_msg_t	msg;
273
	sbbs_ntsvc_t* svc = (sbbs_ntsvc_t*)p;
rswindell's avatar
rswindell committed
274

275
	/* Debug Logging */
276
277
278
279
280
281
282
283
	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);
284
285
	SAFECOPY(msg.buf, str);
	msg.level = level;
286
	msg.repeated = 0;
287
	GetLocalTime(&msg.time);
288

289
290
	/* Mailslot Logging (for sbbsctrl) */
	if(svc->log_handle != INVALID_HANDLE_VALUE /* Invalid log handle? */
291
292
293
294
295
296
297
298
299
		&& !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) {
		sprintf(fname,"\\\\.\\mailslot\\%s.log",svc->name);
		svc->log_handle = CreateFile(
300
301
302
303
304
305
306
			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 */
307
308
			);
	}
309
	if(svc->log_handle != INVALID_HANDLE_VALUE) {
310
311
		len=sizeof(msg);
		if(!WriteFile(svc->log_handle,&msg,len,&wr,NULL) || wr!=len) {
312
313
			/* This most likely indicates the server closed the mailslot */
			sprintf(debug,"!ERROR %d writing %u bytes to %s pipe (wr=%d)"
314
				,GetLastError(),len,svc->name,wr);
315
316
317
318
			OutputDebugString(debug);
			/* So close the handle and attempt re-open next time */
			CloseHandle(svc->log_handle);
			svc->log_handle=INVALID_HANDLE_VALUE;
319
320
		}
	}
321
322
	
	/* Event Logging */
323
	if(level <= (*svc->log_level)) {
324
325
		if(svc->event_handle == NULL)
			svc->event_handle = RegisterEventSource(
326
327
				NULL,		/* server name for source (NULL = local computer) */
				svc->name   /* source name for registered handle */
328
329
				);
		if(svc->event_handle != NULL)
330
331
332
333
334
335
336
			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 */
rswindell's avatar
rswindell committed
337
				(LPCSTR*)&str,			/* pointer to string array */
338
				NULL);					/* pointer to data */
339
340
	}

rswindell's avatar
rswindell committed
341
    return(0);
342
343
}

344
345
346
347
/************************************/
/* Shared Service Callback Routines */
/************************************/

348
static void svc_started(void* p)
349
{
350
351
352
	sbbs_ntsvc_t* svc = (sbbs_ntsvc_t*)p;

	svc->status.dwCurrentState=SERVICE_RUNNING;
353
	svc->status.dwControlsAccepted|=SERVICE_ACCEPT_STOP;
354
	SetServiceStatus(svc->status_handle, &svc->status);
355
356
}

357
static void read_ini(sbbs_ntsvc_t* svc)
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
{
	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;

378
379
380
381
	if((fp=fopen(ini_file,"r"))!=NULL) {
		sprintf(str,"Reading %s",ini_file);
		svc_lputs(NULL,LOG_INFO,str);
	}
382
383

	/* We call this function to set defaults, even if there's no .ini file */
384
	sbbs_read_ini(fp, ini_file
385
386
387
388
389
390
391
392
393
394
395
396
397
		,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);
}

398
399
400
401
402
static void svc_recycle(void *p)
{
	read_ini((sbbs_ntsvc_t*)p);
}

403
static void svc_terminated(void* p, int code)
404
{
405
406
407
408
409
410
	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);
411
412
413
	}
}

414
415
416
417
418
static void svc_clients(void* p, int active)
{
	sbbs_ntsvc_t* svc = (sbbs_ntsvc_t*)p;
}

419
420
421
422
423
/***************/
/* ServiceMain */
/***************/

/* Common ServiceMain for all services */
424
static void WINAPI svc_main(sbbs_ntsvc_t* svc, DWORD argc, LPTSTR *argv)
425
{
426
	char	str[256];
427
428
429
430
431
432
433
434
435
	DWORD	i;
	char*	arg;

	for(i=0;i<argc;i++) {
		arg=argv[i];
		if(*arg=='-' || *arg=='/')
			arg++;
		if(!stricmp(arg,"debug"))
			svc->debug=TRUE;
436
437
		if(!stricmp(arg,"loglevel") && i+1<argc)
			(*svc->log_level)=strtol(argv[++i],NULL,0);
438
439
	}

440
	sprintf(str,"Starting NT Service: %s",svc->display_name);
441
	svc_lputs(svc,LOG_INFO,str);
rswindell's avatar
rswindell committed
442

443
    if((svc->status_handle = RegisterServiceCtrlHandler(svc->name, svc->ctrl_handler))==0) {
444
		sprintf(str,"!ERROR %d registering service control handler",GetLastError());
445
		svc_lputs(NULL,LOG_ERR,str);
446
447
448
		return;
	}

449
450
	memset(&svc->status,0,sizeof(SERVICE_STATUS));
	svc->status.dwServiceType=SERVICE_WIN32_SHARE_PROCESS;
451
452
	svc->status.dwControlsAccepted=SERVICE_ACCEPT_SHUTDOWN;
	svc->status.dwWaitHint=NTSVC_TIMEOUT_STARTUP;
453

454
455
	svc->status.dwCurrentState=SERVICE_START_PENDING;
	SetServiceStatus(svc->status_handle, &svc->status);
456

457
458
	read_ini(svc);

rswindell's avatar
rswindell committed
459
	svc->thread(svc->startup);
460

461
462
	svc->status.dwCurrentState=SERVICE_STOPPED;
	SetServiceStatus(svc->status_handle, &svc->status);
463

464
465
466
	if(svc->log_handle!=INVALID_HANDLE_VALUE) {
		CloseHandle(svc->log_handle);
		svc->log_handle=INVALID_HANDLE_VALUE;
467
	}
468
469
470
471
472

	if(svc->event_handle!=NULL) {
		DeregisterEventSource(svc->event_handle);
		svc->event_handle=NULL;
	}
473
474
}

475
/* Service-specific ServiceMain stub functions */
476

477
static void WINAPI bbs_start(DWORD dwArgc, LPTSTR *lpszArgv)
478
{
479
	svc_main(&bbs, dwArgc, lpszArgv);
480
481
482
483
484
485

	/* 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;
	}
486
487
}

488
static void WINAPI ftp_start(DWORD dwArgc, LPTSTR *lpszArgv)
489
{
490
	svc_main(&ftp, dwArgc, lpszArgv);
491
492
}

493
static void WINAPI web_start(DWORD dwArgc, LPTSTR *lpszArgv)
494
{
495
	svc_main(&web, dwArgc, lpszArgv);
496
497
}

498
static void WINAPI mail_start(DWORD dwArgc, LPTSTR *lpszArgv)
499
{
500
	svc_main(&mail, dwArgc, lpszArgv);
501
502
}

503
static void WINAPI services_start(DWORD dwArgc, LPTSTR *lpszArgv)
504
{
505
	svc_main(&services, dwArgc, lpszArgv);
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
}

/******************************************/
/* 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);
}

529
530
531
532
533
534
535
536
537
538
539
540
static BOOL register_event_source(char* name, char* path)
{
	char	keyname[256];
	HKEY	hKey;
	DWORD	type;
	DWORD	disp;
	LONG	retval;
	char*	value;

	sprintf(keyname,"system\\CurrentControlSet\\services\\eventlog\\application\\%s",name);

	retval=RegCreateKeyEx(
541
542
543
544
545
546
547
548
549
		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 */
550
551
552
553
554
555
556
557
558
559
		);

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

	value="EventMessageFile";
	retval=RegSetValueEx(
560
561
562
563
564
565
		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 */
566
567
568
569
570
571
572
573
574
575
576
577
		);

	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(
578
579
580
581
582
583
		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 */
584
585
586
587
588
589
590
591
592
593
594
595
596
		);

	RegCloseKey(hKey);

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

	return(TRUE);
}

597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
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");
	}

	sprintf(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");
	}

	sprintf(str,"State: %d", state);
	return(str);
}

630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
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");
		case SERVICE_CONTROL_MUTE:
			return("Muting");
		case SERVICE_CONTROL_UNMUTE:
			return("Un-muting");
		case SERVICE_CONTROL_SYSOP_AVAILABLE:
			return("Sysop Available");
		case SERVICE_CONTROL_SYSOP_UNAVAILABLE:	
			return("Sysop Unavailable");
	}
	sprintf(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
670
	DWORD			ret;
671
672
673
674
675
676
677
678
679
680
681
682
683
684
	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(
685
686
687
688
		hService,		/* handle of service */
		NULL,			/* address of service config. structure */
		0,				/* size of service configuration buffer */
		&size			/* address of variable for bytes needed */
689
690
691
692
693
694
695
696
		) || GetLastError()!=ERROR_INSUFFICIENT_BUFFER) {
		printf("\n!Unexpected QueryServiceConfig ERROR %u\n",err=GetLastError());
		return(-1);
	}

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

deuce's avatar
deuce committed
697
	if((service_config=malloc(size))==NULL) {
698
699
700
701
702
		printf("\n!ERROR allocating %u bytes of memory\n", size);
		return(-1);
	}

	if(!QueryServiceConfig(
703
704
705
706
		hService,		/* handle of service */
		service_config,	/* address of service config. structure */
		size,			/* size of service configuration buffer */
		&size			/* address of variable for bytes needed */
707
708
		)) {
		printf("\n!QueryServiceConfig ERROR %u\n",GetLastError());
deuce's avatar
deuce committed
709
		free(service_config);
710
711
712
		return(-1);
	}
    CloseServiceHandle(hService);
mcmlxxix's avatar
mcmlxxix committed
713
	ret=service_config->dwStartType;
deuce's avatar
deuce committed
714
	free(service_config);
715

deuce's avatar
deuce committed
716
	return ret;
717
718
}

719
720
721
/****************************************************************************/
/* Utility function to create a service with description (on Win2K+)		*/
/****************************************************************************/
722
static SC_HANDLE create_service(HANDLE hSCMlib, SC_HANDLE hSCManager
723
724
								,char* name, char* display_name, char* description, char* path
								,BOOL autostart)
725
726
{
    SC_HANDLE   hService;
727
728
	DWORD		err;
	DWORD		start_type = autostart ? SERVICE_AUTO_START : SERVICE_DEMAND_START;
729

730
	printf("Installing service: %-*s ... ", STRLEN_MAX_DISPLAY_NAME, display_name);
731
732

    hService = CreateService(
733
734
735
736
737
738
739
740
741
742
743
744
745
        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 */
746

747
748
749
750
751
752
	if(hService==NULL) {
		if((err=GetLastError())==ERROR_SERVICE_EXISTS)
			printf("Already installed\n");
		else
			printf("!ERROR %d\n",err);
	}
753
754
755
	else {
		describe_service(hSCMlib, hService,description);
		CloseServiceHandle(hService);
756
		printf("%s\n", start_type_desc(start_type));
757
758

		register_event_source(name,path);
rswindell's avatar
rswindell committed
759
		register_event_source(NTSVC_NAME_EVENT,path);	/* Create SynchronetEvent event source */
760
761
762
763
764
	}

	return(hService);
}

765
766
767
/****************************************************************************/
/* Install one or all services												*/
/****************************************************************************/
rswindell's avatar
rswindell committed
768
static int install(const char* svc_name)
769
{
770
	int			i;
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
	HANDLE		hSCMlib;
    SC_HANDLE   hSCManager;
    char		path[MAX_PATH+1];

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

	hSCMlib = LoadLibrary("ADVAPI32.DLL");

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

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

795
	for(i=0;ntsvc_list[i]!=NULL;i++)
rswindell's avatar
rswindell committed
796
		if(svc_name==NULL	/* All? */
797
798
			|| !stricmp(ntsvc_list[i]->name, svc_name)
			|| !stricmp(ntsvc_list[i]->name+STRLEN_SYNCHRONET, svc_name))
rswindell's avatar
rswindell committed
799
800
801
802
803
			create_service(hSCMlib
				,hSCManager
				,ntsvc_list[i]->name
				,ntsvc_list[i]->display_name
				,ntsvc_list[i]->description
804
805
				,path
				,ntsvc_list[i]->autostart);
806
807
808
809
810
811
812
813
814

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

	CloseServiceHandle(hSCManager);

	return(0);
}

815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
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);
}

831
832
833
/****************************************************************************/
/* Utility function to remove a service cleanly (stopping if necessary)		*/
/****************************************************************************/
834
static void remove_service(SC_HANDLE hSCManager, char* name, char* disp_name)
835
836
837
838
{
    SC_HANDLE		hService;
	SERVICE_STATUS	status;

839
	printf("Removing: %-*s ... ", STRLEN_MAX_DISPLAY_NAME, disp_name);
840

841
    if((hService=open_service(hSCManager, name))==NULL)
842
843
		return;

844
    /* try to stop the service */
845
846
    if(ControlService( hService, SERVICE_CONTROL_STOP, &status))
    {
847
        printf("\nStopping: %-*s ... ", STRLEN_MAX_DISPLAY_NAME, disp_name);
848
849
850
851
852

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

        if(status.dwCurrentState == SERVICE_STOPPED)
853
            printf("Stopped, ");
854
        else
855
            printf("FAILED!, ");
856
857
    }

858
    /* now remove the service */
859
    if(DeleteService(hService))
860
		printf("Removed\n");
861
862
863
864
865
	else
		printf("!ERROR %d\n",GetLastError());
    CloseServiceHandle(hService);
}

866
867
868
869
870
871
872
873
/****************************************************************************/
/* Utility function to stop a service										*/
/****************************************************************************/
static void stop_service(SC_HANDLE hSCManager, char* name, char* disp_name)
{
    SC_HANDLE		hService;
	SERVICE_STATUS	status;

874
875
    printf("Stopping service: %-*s ... ", STRLEN_MAX_DISPLAY_NAME, disp_name);
    if((hService=open_service(hSCManager, name))==NULL)
876
877
		return;

878
    /* try to stop the service */
879
880
881
882
883
884
    if(ControlService( hService, SERVICE_CONTROL_STOP, &status))
    {
        while(QueryServiceStatus(hService, &status) && status.dwCurrentState == SERVICE_STOP_PENDING)
			Sleep(1000);

        if(status.dwCurrentState == SERVICE_STOPPED)
885
            printf("Stopped\n");
886
887
888
        else
            printf("FAILED!\n");
    } else
889
		printf("Already stopped\n");
890
891
892
893

    CloseServiceHandle(hService);
}

894
895
896
897
898
899
900
901
902
903
904
905
906
/****************************************************************************/
/* 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;

907
    /* try to stop the service */
908
    if(!ControlService( hService, ctrl, &status)) {
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
		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(
930
931
932
                        NULL,                   /* machine (NULL == local) */
                        NULL,                   /* database (NULL == default) */
                        SC_MANAGER_ALL_ACCESS   /* access required */
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
                        );
    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);
}

968
969
970
971
972
973
974
975
976
/****************************************************************************/
/* 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;

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

	if(QueryServiceStatus(hService, &status) && status.dwCurrentState == SERVICE_RUNNING)
982
		printf("Already running\n");
983
	else {
984
		/* Start the service */
rswindell's avatar
rswindell committed
985
		if(StartService(hService, argc, (LPCTSTR*)argv))
986
987
988
989
990
		{
			while(QueryServiceStatus(hService, &status) && status.dwCurrentState == SERVICE_START_PENDING)
				Sleep(1000);

			if(status.dwCurrentState == SERVICE_RUNNING)
991
				printf("Started\n");
992
993
994
995
996
997
998
999
1000
			else
				printf("FAILED!\n");
		} else
			printf("!ERROR %u\n", GetLastError());
	}

    CloseServiceHandle(hService);
}

For faster browsing, not all history is shown. View entire blame