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