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