]> git.sur5r.net Git - openldap/blob - libraries/liblutil/ntservice.c
Basic framework for DIT Content Rules (not yet enforced)
[openldap] / libraries / liblutil / ntservice.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2002 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 = 1;    /* assume this is an NT service until determined that */
49                                                         /* startup was from the command line */
50
51 SERVICE_STATUS                  SLAPDServiceStatus;
52 SERVICE_STATUS_HANDLE   hSLAPDServiceStatus;
53
54 ldap_pvt_thread_cond_t  started_event,          stopped_event;
55 ldap_pvt_thread_t               start_status_tid,       stop_status_tid;
56
57 void (*stopfunc)(int);
58
59 static char *GetLastErrorString( void );
60
61 int srv_install(LPCTSTR lpszServiceName, LPCTSTR lpszDisplayName,
62                 LPCTSTR lpszBinaryPathName, BOOL auto_start)
63 {
64         HKEY            hKey;
65         DWORD           dwValue, dwDisposition;
66         SC_HANDLE       schSCManager, schService;
67
68         fprintf( stderr, "The install path is %s.\n", lpszBinaryPathName );
69         if ((schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT|SC_MANAGER_CREATE_SERVICE ) ) != NULL )
70         {
71                 if ((schService = CreateService( 
72                                                         schSCManager, 
73                                                         lpszServiceName, 
74                                                         lpszDisplayName, 
75                                                         SERVICE_ALL_ACCESS, 
76                                                         SERVICE_WIN32_OWN_PROCESS, 
77                                                         auto_start ? SERVICE_AUTO_START : SERVICE_DEMAND_START, 
78                                                         SERVICE_ERROR_NORMAL, 
79                                                         lpszBinaryPathName, 
80                                                         NULL, NULL, NULL, NULL, NULL)) != NULL)
81                 {
82                         char regpath[132];
83                         CloseServiceHandle(schService);
84                         CloseServiceHandle(schSCManager);
85
86                         snprintf( regpath, sizeof regpath,
87                                 "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s",
88                                 lpszServiceName );
89                         /* Create the registry key for event logging to the Windows NT event log. */
90                         if ( RegCreateKeyEx(HKEY_LOCAL_MACHINE, 
91                                 regpath, 0, 
92                                 "REG_SZ", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, 
93                                 &dwDisposition) != ERROR_SUCCESS)
94                         {
95                                 fprintf( stderr, "RegCreateKeyEx() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
96                                 RegCloseKey(hKey);
97                                 return(0);
98                         }
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 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 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
213
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                                 SLAPDServiceStatus.dwCurrentState       = SERVICE_RUNNING;
230                                 SLAPDServiceStatus.dwWin32ExitCode      = NO_ERROR;
231                                 SLAPDServiceStatus.dwCheckPoint++;
232                                 SLAPDServiceStatus.dwWaitHint           = 1000;
233                                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
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                                 SLAPDServiceStatus.dwCheckPoint++;
240                                 SLAPDServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2;
241                                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
242                                 break;
243                         case WAIT_FAILED:
244                                 /* theres been some problem with WaitForSingleObject so tell the Service
245                                  * Control Manager to wait 30 seconds before deploying its assasin and 
246                                  * then leave the thread. */
247                                 SLAPDServiceStatus.dwCheckPoint++;
248                                 SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS;
249                                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
250                                 done = 1;
251                                 break;
252                 }
253         }
254         ldap_pvt_thread_exit(NULL);
255         return NULL;
256 }
257
258
259
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                                 SLAPDServiceStatus.dwCheckPoint++;
282                                 SLAPDServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2;
283                                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
284                                 break;
285                         case WAIT_FAILED:
286                                 /* theres been some problem with WaitForSingleObject so tell the Service
287                                  * Control Manager to wait 30 seconds before deploying its assasin and 
288                                  * then leave the thread. */
289                                 SLAPDServiceStatus.dwCheckPoint++;
290                                 SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS;
291                                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
292                                 done = 1;
293                                 break;
294                 }
295         }
296         ldap_pvt_thread_exit(NULL);
297         return NULL;
298 }
299
300
301
302 void WINAPI SLAPDServiceCtrlHandler( IN DWORD Opcode)
303 {
304         switch (Opcode)
305         {
306         case SERVICE_CONTROL_STOP:
307         case SERVICE_CONTROL_SHUTDOWN:
308
309                 Debug( LDAP_DEBUG_TRACE, "Service Shutdown ordered\n", 0, 0, 0 );
310                 SLAPDServiceStatus.dwCurrentState       = SERVICE_STOP_PENDING;
311                 SLAPDServiceStatus.dwCheckPoint++;
312                 SLAPDServiceStatus.dwWaitHint           = SCM_NOTIFICATION_INTERVAL * 2;
313                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
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                         SLAPDServiceStatus.dwCheckPoint++;
321                         SLAPDServiceStatus.dwWaitHint           = THIRTY_SECONDS;
322                         SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
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                                 SLAPDServiceStatus.dwCheckPoint++;
338                                 SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS;
339                                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
340                         }
341                 }
342                 stopfunc( -1 );
343                 break;
344
345         case SERVICE_CONTROL_INTERROGATE:
346                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
347                 break;
348         }
349         return;
350 }
351
352 void *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                 /*Debug( LDAP_DEBUG_ANY, "RegOpenKeyEx() %s\n", GetLastErrorString(), 0, 0); */
368                 return NULL;
369         }
370
371         if ( RegQueryValueEx( hkey, value, NULL, &vType, vValue, &valLen ) != ERROR_SUCCESS )
372         {
373                 /*Debug( LDAP_DEBUG_ANY, "RegQueryValueEx() %s\n", GetLastErrorString(), 0, 0 );*/
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 LogSlapdStartedEvent( 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         Inserts[i++] = strdup( is_NT_Service ? "svc" : "cmd" );
403
404         ReportEvent( hEventLog, EVENTLOG_INFORMATION_TYPE, 0,
405                 MSG_SLAPD_STARTED, NULL, i, 0, (LPCSTR *) Inserts, NULL );
406
407         for ( j = 0; j < i; j++ )
408                 ldap_memfree( Inserts[j] );
409         DeregisterEventSource( hEventLog );
410 }
411
412
413
414 void LogSlapdStoppedEvent( char *svc )
415 {
416         HANDLE hEventLog;
417         
418         hEventLog = RegisterEventSource( NULL, svc );
419         ReportEvent( hEventLog, EVENTLOG_INFORMATION_TYPE, 0,
420                 MSG_SLAPD_STOPPED, NULL, 0, 0, NULL, NULL );
421         DeregisterEventSource( hEventLog );
422 }
423
424
425 void CommenceStartupProcessing( LPCTSTR lpszServiceName,
426                                                            void (*stopper)(int) )
427 {
428         hSLAPDServiceStatus = RegisterServiceCtrlHandler( lpszServiceName, (LPHANDLER_FUNCTION)SLAPDServiceCtrlHandler);
429
430         stopfunc = stopper;
431
432         /* initialize the Service Status structure */
433         SLAPDServiceStatus.dwServiceType                                = SERVICE_WIN32_OWN_PROCESS;
434         SLAPDServiceStatus.dwCurrentState                               = SERVICE_START_PENDING;
435         SLAPDServiceStatus.dwControlsAccepted                   = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
436         SLAPDServiceStatus.dwWin32ExitCode                              = NO_ERROR;
437         SLAPDServiceStatus.dwServiceSpecificExitCode    = 0;
438         SLAPDServiceStatus.dwCheckPoint                                 = 1;
439         SLAPDServiceStatus.dwWaitHint                                   = SCM_NOTIFICATION_INTERVAL * 2;
440
441         SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
442
443         /* start up a thread to keep sending SERVICE_START_PENDING to the Service Control Manager
444          * until the slapd listener is completed and listening. Only then should we send 
445          * SERVICE_RUNNING to the Service Control Manager. */
446         ldap_pvt_thread_cond_init( &started_event );
447         if ( started_event == NULL)
448         {
449                 /* failed to create the event to determine when the startup process is complete so
450                  * tell the Service Control Manager to wait another 30 seconds before deploying its
451                  * assasin  */
452                 SLAPDServiceStatus.dwCheckPoint++;
453                 SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS;
454                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
455         }
456         else
457         {
458                 /* start a thread to report the progress to the service control manager 
459                  * until the started_event is fired.  */
460                 if ( ldap_pvt_thread_create( &start_status_tid, 0, start_status_routine, NULL ) == 0 )
461                 {
462                         
463                 }
464                 else {
465                         /* failed to create the thread that tells the Service Control Manager that the
466                          * service startup is proceeding. 
467                          * tell the Service Control Manager to wait another 30 seconds before deploying its
468                          * assasin.  */
469                         SLAPDServiceStatus.dwCheckPoint++;
470                         SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS;
471                         SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
472                 }
473         }
474 }
475
476 void ReportSlapdShutdownComplete(  )
477 {
478         if ( is_NT_Service )
479         {
480                 /* stop sending SERVICE_STOP_PENDING messages to the Service Control Manager */
481                 ldap_pvt_thread_cond_signal( &stopped_event );
482                 ldap_pvt_thread_cond_destroy( &stopped_event );
483
484                 /* wait for the thread sending the SERVICE_STOP_PENDING messages to the Service Control Manager to die.
485                  * if the wait fails then put ourselves to sleep for half the Service Control Manager update interval */
486                 if (ldap_pvt_thread_join( stop_status_tid, (void *) NULL ) == -1)
487                         ldap_pvt_thread_sleep( SCM_NOTIFICATION_INTERVAL / 2 );
488
489                 SLAPDServiceStatus.dwCurrentState = SERVICE_STOPPED;
490                 SLAPDServiceStatus.dwCheckPoint++;
491                 SLAPDServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL;
492                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
493         }
494 }
495
496 static char *GetErrorString( int err )
497 {
498         static char msgBuf[1024];
499
500         FormatMessage(
501                 FORMAT_MESSAGE_FROM_SYSTEM,
502                 NULL,
503                 err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
504                 msgBuf, 1024, NULL );
505
506         return msgBuf;
507 }
508
509 static char *GetLastErrorString( void )
510 {
511         return GetErrorString( GetLastError() );
512 }
513 #endif