1 /*	$NetBSD: ntservice.c,v 1.1.1.3 2010/12/12 15:22:09 adam Exp $	*/
2 
3 /* OpenLDAP: pkg/ldap/libraries/liblutil/ntservice.c,v 1.31.2.5 2010/04/13 20:23:06 kurt Exp */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 1998-2010 The OpenLDAP Foundation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 
18 /*
19  * NT Service manager utilities for OpenLDAP services
20  */
21 
22 #include "portable.h"
23 
24 #ifdef HAVE_NT_SERVICE_MANAGER
25 
26 #include <ac/stdlib.h>
27 #include <ac/string.h>
28 
29 #include <stdio.h>
30 
31 #include <windows.h>
32 #include <winsvc.h>
33 
34 #include <ldap.h>
35 
36 #include "ldap_log.h"
37 #include "ldap_pvt_thread.h"
38 
39 
40 #include "ldap_defaults.h"
41 
42 #include "slapdmsg.h"
43 
44 #define SCM_NOTIFICATION_INTERVAL	5000
45 #define THIRTY_SECONDS				(30 * 1000)
46 
47 int	  is_NT_Service;	/* is this is an NT service? */
48 
49 SERVICE_STATUS			lutil_ServiceStatus;
50 SERVICE_STATUS_HANDLE	hlutil_ServiceStatus;
51 
52 ldap_pvt_thread_cond_t	started_event,		stopped_event;
53 ldap_pvt_thread_t		start_status_tid,	stop_status_tid;
54 
55 void (*stopfunc)(int);
56 
57 static char *GetLastErrorString( void );
58 
59 int lutil_srv_install(LPCTSTR lpszServiceName, LPCTSTR lpszDisplayName,
60 		LPCTSTR lpszBinaryPathName, int auto_start)
61 {
62 	HKEY		hKey;
63 	DWORD		dwValue, dwDisposition;
64 	SC_HANDLE	schSCManager, schService;
65 	char *sp = strchr( lpszBinaryPathName, ' ');
66 
67 	if ( sp ) *sp = '\0';
68 	fprintf( stderr, "The install path is %s.\n", lpszBinaryPathName );
69 	if ( sp ) *sp = ' ';
70 	if ((schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT|SC_MANAGER_CREATE_SERVICE ) ) != NULL )
71 	{
72 	 	if ((schService = CreateService(
73 							schSCManager,
74 							lpszServiceName,
75 							lpszDisplayName,
76 							SERVICE_ALL_ACCESS,
77 							SERVICE_WIN32_OWN_PROCESS,
78 							auto_start ? SERVICE_AUTO_START : SERVICE_DEMAND_START,
79 							SERVICE_ERROR_NORMAL,
80 							lpszBinaryPathName,
81 							NULL, NULL, NULL, NULL, NULL)) != NULL)
82 		{
83 			char regpath[132];
84 			CloseServiceHandle(schService);
85 			CloseServiceHandle(schSCManager);
86 
87 			snprintf( regpath, sizeof regpath,
88 				"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s",
89 				lpszServiceName );
90 			/* Create the registry key for event logging to the Windows NT event log. */
91 			if ( RegCreateKeyEx(HKEY_LOCAL_MACHINE,
92 				regpath, 0,
93 				"REG_SZ", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
94 				&dwDisposition) != ERROR_SUCCESS)
95 			{
96 				fprintf( stderr, "RegCreateKeyEx() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
97 				RegCloseKey(hKey);
98 				return(0);
99 			}
100 			if ( sp ) *sp = '\0';
101 			if ( RegSetValueEx(hKey, "EventMessageFile", 0, REG_EXPAND_SZ, lpszBinaryPathName, strlen(lpszBinaryPathName) + 1) != ERROR_SUCCESS)
102 			{
103 				fprintf( stderr, "RegSetValueEx(EventMessageFile) failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
104 				RegCloseKey(hKey);
105 				return(0);
106 			}
107 
108 			dwValue = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
109 			if ( RegSetValueEx(hKey, "TypesSupported", 0, REG_DWORD, (LPBYTE) &dwValue, sizeof(DWORD)) != ERROR_SUCCESS)
110 			{
111 				fprintf( stderr, "RegCreateKeyEx(TypesSupported) failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
112 				RegCloseKey(hKey);
113 				return(0);
114 			}
115 			RegCloseKey(hKey);
116 			return(1);
117 		}
118 		else
119 		{
120 			fprintf( stderr, "CreateService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
121 			CloseServiceHandle(schSCManager);
122 			return(0);
123 		}
124 	}
125 	else
126 		fprintf( stderr, "OpenSCManager() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
127 	return(0);
128 }
129 
130 
131 int lutil_srv_remove(LPCTSTR lpszServiceName, LPCTSTR lpszBinaryPathName)
132 {
133 	SC_HANDLE schSCManager, schService;
134 
135 	fprintf( stderr, "The installed path is %s.\n", lpszBinaryPathName );
136 	if ((schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT|SC_MANAGER_CREATE_SERVICE)) != NULL )
137 	{
138 	 	if ((schService = OpenService(schSCManager, lpszServiceName, DELETE)) != NULL)
139 		{
140 			if ( DeleteService(schService) == TRUE)
141 			{
142 				CloseServiceHandle(schService);
143 				CloseServiceHandle(schSCManager);
144 				return(1);
145 			} else {
146 				fprintf( stderr, "DeleteService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
147 				fprintf( stderr, "The %s service has not been removed.\n", lpszBinaryPathName);
148 				CloseServiceHandle(schService);
149 				CloseServiceHandle(schSCManager);
150 				return(0);
151 			}
152 		} else {
153 			fprintf( stderr, "OpenService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
154 			CloseServiceHandle(schSCManager);
155 			return(0);
156 		}
157 	}
158 	else
159 		fprintf( stderr, "OpenSCManager() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
160 	return(0);
161 }
162 
163 
164 #if 0 /* unused */
165 DWORD
166 svc_installed (LPTSTR lpszServiceName, LPTSTR lpszBinaryPathName)
167 {
168 	char buf[256];
169 	HKEY key;
170 	DWORD rc;
171 	DWORD type;
172 	long len;
173 
174 	strcpy(buf, TEXT("SYSTEM\\CurrentControlSet\\Services\\"));
175 	strcat(buf, lpszServiceName);
176 	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, buf, 0, KEY_QUERY_VALUE, &key) != ERROR_SUCCESS)
177 		return(-1);
178 
179 	rc = 0;
180 	if (lpszBinaryPathName) {
181 		len = sizeof(buf);
182 		if (RegQueryValueEx(key, "ImagePath", NULL, &type, buf, &len) == ERROR_SUCCESS) {
183 			if (strcmp(lpszBinaryPathName, buf))
184 				rc = -1;
185 		}
186 	}
187 	RegCloseKey(key);
188 	return(rc);
189 }
190 
191 
192 DWORD
193 svc_running (LPTSTR lpszServiceName)
194 {
195 	SC_HANDLE service;
196 	SC_HANDLE scm;
197 	DWORD rc;
198 	SERVICE_STATUS ss;
199 
200 	if (!(scm = OpenSCManager(NULL, NULL, GENERIC_READ)))
201 		return(GetLastError());
202 
203 	rc = 1;
204 	service = OpenService(scm, lpszServiceName, SERVICE_QUERY_STATUS);
205 	if (service) {
206 		if (!QueryServiceStatus(service, &ss))
207 			rc = GetLastError();
208 		else if (ss.dwCurrentState != SERVICE_STOPPED)
209 			rc = 0;
210 		CloseServiceHandle(service);
211 	}
212 	CloseServiceHandle(scm);
213 	return(rc);
214 }
215 #endif
216 
217 static void *start_status_routine( void *ptr )
218 {
219 	DWORD	wait_result;
220 	int		done = 0;
221 
222 	while ( !done )
223 	{
224 		wait_result = WaitForSingleObject( started_event, SCM_NOTIFICATION_INTERVAL );
225 		switch ( wait_result )
226 		{
227 			case WAIT_ABANDONED:
228 			case WAIT_OBJECT_0:
229 				/* the object that we were waiting for has been destroyed (ABANDONED) or
230 				 * signalled (TIMEOUT_0). We can assume that the startup process is
231 				 * complete and tell the Service Control Manager that we are now runnng */
232 				lutil_ServiceStatus.dwCurrentState	= SERVICE_RUNNING;
233 				lutil_ServiceStatus.dwWin32ExitCode	= NO_ERROR;
234 				lutil_ServiceStatus.dwCheckPoint++;
235 				lutil_ServiceStatus.dwWaitHint		= 1000;
236 				SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
237 				done = 1;
238 				break;
239 			case WAIT_TIMEOUT:
240 				/* We've waited for the required time, so send an update to the Service Control
241 				 * Manager saying to wait again. */
242 				lutil_ServiceStatus.dwCheckPoint++;
243 				lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2;
244 				SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
245 				break;
246 			case WAIT_FAILED:
247 				/* theres been some problem with WaitForSingleObject so tell the Service
248 				 * Control Manager to wait 30 seconds before deploying its assasin and
249 				 * then leave the thread. */
250 				lutil_ServiceStatus.dwCheckPoint++;
251 				lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS;
252 				SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
253 				done = 1;
254 				break;
255 		}
256 	}
257 	ldap_pvt_thread_exit(NULL);
258 	return NULL;
259 }
260 
261 
262 
263 static void *stop_status_routine( void *ptr )
264 {
265 	DWORD	wait_result;
266 	int		done = 0;
267 
268 	while ( !done )
269 	{
270 		wait_result = WaitForSingleObject( stopped_event, SCM_NOTIFICATION_INTERVAL );
271 		switch ( wait_result )
272 		{
273 			case WAIT_ABANDONED:
274 			case WAIT_OBJECT_0:
275 				/* the object that we were waiting for has been destroyed (ABANDONED) or
276 				 * signalled (TIMEOUT_0). The shutting down process is therefore complete
277 				 * and the final SERVICE_STOPPED message will be sent to the service control
278 				 * manager prior to the process terminating. */
279 				done = 1;
280 				break;
281 			case WAIT_TIMEOUT:
282 				/* We've waited for the required time, so send an update to the Service Control
283 				 * Manager saying to wait again. */
284 				lutil_ServiceStatus.dwCheckPoint++;
285 				lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2;
286 				SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
287 				break;
288 			case WAIT_FAILED:
289 				/* theres been some problem with WaitForSingleObject so tell the Service
290 				 * Control Manager to wait 30 seconds before deploying its assasin and
291 				 * then leave the thread. */
292 				lutil_ServiceStatus.dwCheckPoint++;
293 				lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS;
294 				SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
295 				done = 1;
296 				break;
297 		}
298 	}
299 	ldap_pvt_thread_exit(NULL);
300 	return NULL;
301 }
302 
303 
304 
305 static void WINAPI lutil_ServiceCtrlHandler( IN DWORD Opcode)
306 {
307 	switch (Opcode)
308 	{
309 	case SERVICE_CONTROL_STOP:
310 	case SERVICE_CONTROL_SHUTDOWN:
311 
312 		lutil_ServiceStatus.dwCurrentState	= SERVICE_STOP_PENDING;
313 		lutil_ServiceStatus.dwCheckPoint++;
314 		lutil_ServiceStatus.dwWaitHint		= SCM_NOTIFICATION_INTERVAL * 2;
315 		SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
316 
317 		ldap_pvt_thread_cond_init( &stopped_event );
318 		if ( stopped_event == NULL )
319 		{
320 			/* the event was not created. We will ask the service control manager for 30
321 			 * seconds to shutdown */
322 			lutil_ServiceStatus.dwCheckPoint++;
323 			lutil_ServiceStatus.dwWaitHint		= THIRTY_SECONDS;
324 			SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
325 		}
326 		else
327 		{
328 			/* start a thread to report the progress to the service control manager
329 			 * until the stopped_event is fired. */
330 			if ( ldap_pvt_thread_create( &stop_status_tid, 0, stop_status_routine, NULL ) == 0 )
331 			{
332 
333 			}
334 			else {
335 				/* failed to create the thread that tells the Service Control Manager that the
336 				 * service stopping is proceeding.
337 				 * tell the Service Control Manager to wait another 30 seconds before deploying its
338 				 * assasin.  */
339 				lutil_ServiceStatus.dwCheckPoint++;
340 				lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS;
341 				SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
342 			}
343 		}
344 		stopfunc( -1 );
345 		break;
346 
347 	case SERVICE_CONTROL_INTERROGATE:
348 		SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
349 		break;
350 	}
351 	return;
352 }
353 
354 void *lutil_getRegParam( char *svc, char *value )
355 {
356 	HKEY hkey;
357 	char path[255];
358 	DWORD vType;
359 	static char vValue[1024];
360 	DWORD valLen = sizeof( vValue );
361 
362 	if ( svc != NULL )
363 		snprintf ( path, sizeof path, "SOFTWARE\\%s", svc );
364 	else
365 		snprintf ( path, sizeof path, "SOFTWARE\\OpenLDAP\\Parameters" );
366 
367 	if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, path, 0, KEY_READ, &hkey ) != ERROR_SUCCESS )
368 	{
369 		return NULL;
370 	}
371 
372 	if ( RegQueryValueEx( hkey, value, NULL, &vType, vValue, &valLen ) != ERROR_SUCCESS )
373 	{
374 		RegCloseKey( hkey );
375 		return NULL;
376 	}
377 	RegCloseKey( hkey );
378 
379 	switch ( vType )
380 	{
381 	case REG_BINARY:
382 	case REG_DWORD:
383 		return (void*)&vValue;
384 	case REG_SZ:
385 		return (void*)&vValue;
386 	}
387 	return (void*)NULL;
388 }
389 
390 void lutil_LogStartedEvent( char *svc, int slap_debug, char *configfile, char *urls )
391 {
392 	char *Inserts[5];
393 	WORD i = 0, j;
394 	HANDLE hEventLog;
395 
396 	hEventLog = RegisterEventSource( NULL, svc );
397 
398 	Inserts[i] = (char *)malloc( 20 );
399 	itoa( slap_debug, Inserts[i++], 10 );
400 	Inserts[i++] = strdup( configfile );
401 	Inserts[i++] = strdup( urls ? urls : "ldap:///" );
402 
403 	ReportEvent( hEventLog, EVENTLOG_INFORMATION_TYPE, 0,
404 		MSG_SVC_STARTED, NULL, i, 0, (LPCSTR *) Inserts, NULL );
405 
406 	for ( j = 0; j < i; j++ )
407 		ldap_memfree( Inserts[j] );
408 	DeregisterEventSource( hEventLog );
409 }
410 
411 
412 
413 void lutil_LogStoppedEvent( char *svc )
414 {
415 	HANDLE hEventLog;
416 
417 	hEventLog = RegisterEventSource( NULL, svc );
418 	ReportEvent( hEventLog, EVENTLOG_INFORMATION_TYPE, 0,
419 		MSG_SVC_STOPPED, NULL, 0, 0, NULL, NULL );
420 	DeregisterEventSource( hEventLog );
421 }
422 
423 
424 void lutil_CommenceStartupProcessing( char *lpszServiceName,
425 							   void (*stopper)(int) )
426 {
427 	hlutil_ServiceStatus = RegisterServiceCtrlHandler( lpszServiceName, (LPHANDLER_FUNCTION)lutil_ServiceCtrlHandler);
428 
429 	stopfunc = stopper;
430 
431 	/* initialize the Service Status structure */
432 	lutil_ServiceStatus.dwServiceType				= SERVICE_WIN32_OWN_PROCESS;
433 	lutil_ServiceStatus.dwCurrentState				= SERVICE_START_PENDING;
434 	lutil_ServiceStatus.dwControlsAccepted			= SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
435 	lutil_ServiceStatus.dwWin32ExitCode				= NO_ERROR;
436 	lutil_ServiceStatus.dwServiceSpecificExitCode	= 0;
437 	lutil_ServiceStatus.dwCheckPoint					= 1;
438 	lutil_ServiceStatus.dwWaitHint					= SCM_NOTIFICATION_INTERVAL * 2;
439 
440 	SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
441 
442 	/* start up a thread to keep sending SERVICE_START_PENDING to the Service Control Manager
443 	 * until the slapd listener is completed and listening. Only then should we send
444 	 * SERVICE_RUNNING to the Service Control Manager. */
445 	ldap_pvt_thread_cond_init( &started_event );
446 	if ( started_event == NULL)
447 	{
448 		/* failed to create the event to determine when the startup process is complete so
449 		 * tell the Service Control Manager to wait another 30 seconds before deploying its
450 		 * assasin  */
451 		lutil_ServiceStatus.dwCheckPoint++;
452 		lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS;
453 		SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
454 	}
455 	else
456 	{
457 		/* start a thread to report the progress to the service control manager
458 		 * until the started_event is fired.  */
459 		if ( ldap_pvt_thread_create( &start_status_tid, 0, start_status_routine, NULL ) == 0 )
460 		{
461 
462 		}
463 		else {
464 			/* failed to create the thread that tells the Service Control Manager that the
465 			 * service startup is proceeding.
466 			 * tell the Service Control Manager to wait another 30 seconds before deploying its
467 			 * assasin.  */
468 			lutil_ServiceStatus.dwCheckPoint++;
469 			lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS;
470 			SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
471 		}
472 	}
473 }
474 
475 void lutil_ReportShutdownComplete(  )
476 {
477 	if ( is_NT_Service )
478 	{
479 		/* stop sending SERVICE_STOP_PENDING messages to the Service Control Manager */
480 		ldap_pvt_thread_cond_signal( &stopped_event );
481 		ldap_pvt_thread_cond_destroy( &stopped_event );
482 
483 		/* wait for the thread sending the SERVICE_STOP_PENDING messages to the Service Control Manager to die.
484 		 * if the wait fails then put ourselves to sleep for half the Service Control Manager update interval */
485 		if (ldap_pvt_thread_join( stop_status_tid, (void *) NULL ) == -1)
486 			ldap_pvt_thread_sleep( SCM_NOTIFICATION_INTERVAL / 2 );
487 
488 		lutil_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
489 		lutil_ServiceStatus.dwCheckPoint++;
490 		lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL;
491 		SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
492 	}
493 }
494 
495 static char *GetErrorString( int err )
496 {
497 	static char msgBuf[1024];
498 
499 	FormatMessage(
500 		FORMAT_MESSAGE_FROM_SYSTEM,
501 		NULL,
502 		err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
503 		msgBuf, 1024, NULL );
504 
505 	return msgBuf;
506 }
507 
508 static char *GetLastErrorString( void )
509 {
510 	return GetErrorString( GetLastError() );
511 }
512 #endif
513