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