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