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