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