]> git.sur5r.net Git - openldap/blob - libraries/liblutil/ntservice.c
2d761b316707df1e4f96701c808f1c9a9ec20a8d
[openldap] / libraries / liblutil / ntservice.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6
7 /*
8  * NT Service manager utilities for OpenLDAP services
9  *      these should NOT be slapd specific, but are
10  */
11
12 #include "portable.h"
13
14 #ifdef HAVE_NT_SERVICE_MANAGER
15
16 #include <ac/stdlib.h>
17 #include <ac/string.h>
18
19 #include <stdio.h>
20
21 #include <windows.h>
22 #include <winsvc.h>
23
24 #include <ldap.h>
25
26 /*
27  * The whole debug implementation is a bit of a hack.
28  * We have to define this LDAP_SLAPD_V macro here since the slap.h
29  * header file isn't included (and really shouldn't be).
30  * TODO: re-implement debug functions so that the debug level can
31  * be passed around instead of having to count on the existence of
32  * ldap_debug, slap_debug, etc.
33  */
34 #define ldap_debug slap_debug
35 LDAP_SLAPD_V (int) slap_debug;
36
37 #include "ldap_log.h"
38 #include "ldap_pvt_thread.h"
39
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                  SLAPDServiceStatus;
51 SERVICE_STATUS_HANDLE   hSLAPDServiceStatus;
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
60 int srv_install(LPCTSTR lpszServiceName, LPCTSTR lpszDisplayName,
61                 LPCTSTR lpszBinaryPathName, BOOL auto_start)
62 {
63         HKEY            hKey;
64         DWORD           dwValue, dwDisposition;
65         SC_HANDLE       schSCManager, schService;
66         char *sp = strchr( lpszBinaryPathName, ' ');
67
68         if ( sp ) *sp = '\0';
69         fprintf( stderr, "The install path is %s.\n", lpszBinaryPathName );
70         if ( sp ) *sp = ' ';
71         if ((schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT|SC_MANAGER_CREATE_SERVICE ) ) != NULL )
72         {
73                 if ((schService = CreateService( 
74                                                         schSCManager, 
75                                                         lpszServiceName, 
76                                                         lpszDisplayName, 
77                                                         SERVICE_ALL_ACCESS, 
78                                                         SERVICE_WIN32_OWN_PROCESS, 
79                                                         auto_start ? SERVICE_AUTO_START : SERVICE_DEMAND_START, 
80                                                         SERVICE_ERROR_NORMAL, 
81                                                         lpszBinaryPathName, 
82                                                         NULL, NULL, NULL, NULL, NULL)) != NULL)
83                 {
84                         char regpath[132];
85                         CloseServiceHandle(schService);
86                         CloseServiceHandle(schSCManager);
87
88                         snprintf( regpath, sizeof regpath,
89                                 "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s",
90                                 lpszServiceName );
91                         /* Create the registry key for event logging to the Windows NT event log. */
92                         if ( RegCreateKeyEx(HKEY_LOCAL_MACHINE, 
93                                 regpath, 0, 
94                                 "REG_SZ", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, 
95                                 &dwDisposition) != ERROR_SUCCESS)
96                         {
97                                 fprintf( stderr, "RegCreateKeyEx() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
98                                 RegCloseKey(hKey);
99                                 return(0);
100                         }
101                         if ( sp ) *sp = '\0';
102                         if ( RegSetValueEx(hKey, "EventMessageFile", 0, REG_EXPAND_SZ, lpszBinaryPathName, strlen(lpszBinaryPathName) + 1) != ERROR_SUCCESS)
103                         {
104                                 fprintf( stderr, "RegSetValueEx(EventMessageFile) failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
105                                 RegCloseKey(hKey);
106                                 return(0);
107                         }
108
109                         dwValue = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
110                         if ( RegSetValueEx(hKey, "TypesSupported", 0, REG_DWORD, (LPBYTE) &dwValue, sizeof(DWORD)) != ERROR_SUCCESS) 
111                         {
112                                 fprintf( stderr, "RegCreateKeyEx(TypesSupported) failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
113                                 RegCloseKey(hKey);
114                                 return(0);
115                         }
116                         RegCloseKey(hKey);
117                         return(1);
118                 }
119                 else
120                 {
121                         fprintf( stderr, "CreateService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
122                         CloseServiceHandle(schSCManager);
123                         return(0);
124                 }
125         }
126         else
127                 fprintf( stderr, "OpenSCManager() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
128         return(0);
129 }
130
131
132 int srv_remove(LPCTSTR lpszServiceName, LPCTSTR lpszBinaryPathName)
133 {
134         SC_HANDLE schSCManager, schService;
135
136         fprintf( stderr, "The installed path is %s.\n", lpszBinaryPathName );
137         if ((schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT|SC_MANAGER_CREATE_SERVICE)) != NULL ) 
138         {
139                 if ((schService = OpenService(schSCManager, lpszServiceName, DELETE)) != NULL) 
140                 {
141                         if ( DeleteService(schService) == TRUE) 
142                         {
143                                 CloseServiceHandle(schService);
144                                 CloseServiceHandle(schSCManager);
145                                 return(1);
146                         } else {
147                                 fprintf( stderr, "DeleteService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
148                                 fprintf( stderr, "The %s service has not been removed.\n", lpszBinaryPathName);
149                                 CloseServiceHandle(schService);
150                                 CloseServiceHandle(schSCManager);
151                                 return(0);
152                         }
153                 } else {
154                         fprintf( stderr, "OpenService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
155                         CloseServiceHandle(schSCManager);
156                         return(0);
157                 }
158         }
159         else
160                 fprintf( stderr, "OpenSCManager() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
161         return(0);
162 }
163
164
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
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                                 SLAPDServiceStatus.dwCurrentState       = SERVICE_RUNNING;
233                                 SLAPDServiceStatus.dwWin32ExitCode      = NO_ERROR;
234                                 SLAPDServiceStatus.dwCheckPoint++;
235                                 SLAPDServiceStatus.dwWaitHint           = 1000;
236                                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
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                                 SLAPDServiceStatus.dwCheckPoint++;
243                                 SLAPDServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2;
244                                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
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                                 SLAPDServiceStatus.dwCheckPoint++;
251                                 SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS;
252                                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
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                                 SLAPDServiceStatus.dwCheckPoint++;
285                                 SLAPDServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2;
286                                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
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                                 SLAPDServiceStatus.dwCheckPoint++;
293                                 SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS;
294                                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
295                                 done = 1;
296                                 break;
297                 }
298         }
299         ldap_pvt_thread_exit(NULL);
300         return NULL;
301 }
302
303
304
305 void WINAPI SLAPDServiceCtrlHandler( IN DWORD Opcode)
306 {
307         switch (Opcode)
308         {
309         case SERVICE_CONTROL_STOP:
310         case SERVICE_CONTROL_SHUTDOWN:
311
312                 Debug( LDAP_DEBUG_TRACE, "Service Shutdown ordered\n", 0, 0, 0 );
313                 SLAPDServiceStatus.dwCurrentState       = SERVICE_STOP_PENDING;
314                 SLAPDServiceStatus.dwCheckPoint++;
315                 SLAPDServiceStatus.dwWaitHint           = SCM_NOTIFICATION_INTERVAL * 2;
316                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
317
318                 ldap_pvt_thread_cond_init( &stopped_event );
319                 if ( stopped_event == NULL )
320                 {
321                         /* the event was not created. We will ask the service control manager for 30
322                          * seconds to shutdown */
323                         SLAPDServiceStatus.dwCheckPoint++;
324                         SLAPDServiceStatus.dwWaitHint           = THIRTY_SECONDS;
325                         SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
326                 }
327                 else
328                 {
329                         /* start a thread to report the progress to the service control manager 
330                          * until the stopped_event is fired. */
331                         if ( ldap_pvt_thread_create( &stop_status_tid, 0, stop_status_routine, NULL ) == 0 )
332                         {
333                                 
334                         }
335                         else {
336                                 /* failed to create the thread that tells the Service Control Manager that the
337                                  * service stopping is proceeding. 
338                                  * tell the Service Control Manager to wait another 30 seconds before deploying its
339                                  * assasin.  */
340                                 SLAPDServiceStatus.dwCheckPoint++;
341                                 SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS;
342                                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
343                         }
344                 }
345                 stopfunc( -1 );
346                 break;
347
348         case SERVICE_CONTROL_INTERROGATE:
349                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
350                 break;
351         }
352         return;
353 }
354
355 void *getRegParam( char *svc, char *value )
356 {
357         HKEY hkey;
358         char path[255];
359         DWORD vType;
360         static char vValue[1024];
361         DWORD valLen = sizeof( vValue );
362
363         if ( svc != NULL )
364                 snprintf ( path, sizeof path, "SOFTWARE\\%s", svc );
365         else
366                 snprintf ( path, sizeof path, "SOFTWARE\\OpenLDAP\\Parameters" );
367         
368         if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, path, 0, KEY_READ, &hkey ) != ERROR_SUCCESS )
369         {
370                 /*Debug( LDAP_DEBUG_ANY, "RegOpenKeyEx() %s\n", GetLastErrorString(), 0, 0); */
371                 return NULL;
372         }
373
374         if ( RegQueryValueEx( hkey, value, NULL, &vType, vValue, &valLen ) != ERROR_SUCCESS )
375         {
376                 /*Debug( LDAP_DEBUG_ANY, "RegQueryValueEx() %s\n", GetLastErrorString(), 0, 0 );*/
377                 RegCloseKey( hkey );
378                 return NULL;
379         }
380         RegCloseKey( hkey );
381         
382         switch ( vType )
383         {
384         case REG_BINARY:
385         case REG_DWORD:
386                 return (void*)&vValue;
387         case REG_SZ:
388                 return (void*)&vValue;
389         }
390         return (void*)NULL;
391 }
392
393 void LogSlapdStartedEvent( char *svc, int slap_debug, char *configfile, char *urls )
394 {
395         char *Inserts[5];
396         WORD i = 0, j;
397         HANDLE hEventLog;
398         
399         hEventLog = RegisterEventSource( NULL, svc );
400
401         Inserts[i] = (char *)malloc( 20 );
402         itoa( slap_debug, Inserts[i++], 10 );
403         Inserts[i++] = strdup( configfile );
404         Inserts[i++] = strdup( urls ? urls : "ldap:///" );
405         Inserts[i++] = strdup( is_NT_Service ? "svc" : "cmd" );
406
407         ReportEvent( hEventLog, EVENTLOG_INFORMATION_TYPE, 0,
408                 MSG_SLAPD_STARTED, NULL, i, 0, (LPCSTR *) Inserts, NULL );
409
410         for ( j = 0; j < i; j++ )
411                 ldap_memfree( Inserts[j] );
412         DeregisterEventSource( hEventLog );
413 }
414
415
416
417 void LogSlapdStoppedEvent( char *svc )
418 {
419         HANDLE hEventLog;
420         
421         hEventLog = RegisterEventSource( NULL, svc );
422         ReportEvent( hEventLog, EVENTLOG_INFORMATION_TYPE, 0,
423                 MSG_SLAPD_STOPPED, NULL, 0, 0, NULL, NULL );
424         DeregisterEventSource( hEventLog );
425 }
426
427
428 void CommenceStartupProcessing( LPCTSTR lpszServiceName,
429                                                            void (*stopper)(int) )
430 {
431         hSLAPDServiceStatus = RegisterServiceCtrlHandler( lpszServiceName, (LPHANDLER_FUNCTION)SLAPDServiceCtrlHandler);
432
433         stopfunc = stopper;
434
435         /* initialize the Service Status structure */
436         SLAPDServiceStatus.dwServiceType                                = SERVICE_WIN32_OWN_PROCESS;
437         SLAPDServiceStatus.dwCurrentState                               = SERVICE_START_PENDING;
438         SLAPDServiceStatus.dwControlsAccepted                   = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
439         SLAPDServiceStatus.dwWin32ExitCode                              = NO_ERROR;
440         SLAPDServiceStatus.dwServiceSpecificExitCode    = 0;
441         SLAPDServiceStatus.dwCheckPoint                                 = 1;
442         SLAPDServiceStatus.dwWaitHint                                   = SCM_NOTIFICATION_INTERVAL * 2;
443
444         SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
445
446         /* start up a thread to keep sending SERVICE_START_PENDING to the Service Control Manager
447          * until the slapd listener is completed and listening. Only then should we send 
448          * SERVICE_RUNNING to the Service Control Manager. */
449         ldap_pvt_thread_cond_init( &started_event );
450         if ( started_event == NULL)
451         {
452                 /* failed to create the event to determine when the startup process is complete so
453                  * tell the Service Control Manager to wait another 30 seconds before deploying its
454                  * assasin  */
455                 SLAPDServiceStatus.dwCheckPoint++;
456                 SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS;
457                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
458         }
459         else
460         {
461                 /* start a thread to report the progress to the service control manager 
462                  * until the started_event is fired.  */
463                 if ( ldap_pvt_thread_create( &start_status_tid, 0, start_status_routine, NULL ) == 0 )
464                 {
465                         
466                 }
467                 else {
468                         /* failed to create the thread that tells the Service Control Manager that the
469                          * service startup is proceeding. 
470                          * tell the Service Control Manager to wait another 30 seconds before deploying its
471                          * assasin.  */
472                         SLAPDServiceStatus.dwCheckPoint++;
473                         SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS;
474                         SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
475                 }
476         }
477 }
478
479 void ReportSlapdShutdownComplete(  )
480 {
481         if ( is_NT_Service )
482         {
483                 /* stop sending SERVICE_STOP_PENDING messages to the Service Control Manager */
484                 ldap_pvt_thread_cond_signal( &stopped_event );
485                 ldap_pvt_thread_cond_destroy( &stopped_event );
486
487                 /* wait for the thread sending the SERVICE_STOP_PENDING messages to the Service Control Manager to die.
488                  * if the wait fails then put ourselves to sleep for half the Service Control Manager update interval */
489                 if (ldap_pvt_thread_join( stop_status_tid, (void *) NULL ) == -1)
490                         ldap_pvt_thread_sleep( SCM_NOTIFICATION_INTERVAL / 2 );
491
492                 SLAPDServiceStatus.dwCurrentState = SERVICE_STOPPED;
493                 SLAPDServiceStatus.dwCheckPoint++;
494                 SLAPDServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL;
495                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
496         }
497 }
498
499 static char *GetErrorString( int err )
500 {
501         static char msgBuf[1024];
502
503         FormatMessage(
504                 FORMAT_MESSAGE_FROM_SYSTEM,
505                 NULL,
506                 err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
507                 msgBuf, 1024, NULL );
508
509         return msgBuf;
510 }
511
512 static char *GetLastErrorString( void )
513 {
514         return GetErrorString( GetLastError() );
515 }
516 #endif