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