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