]> git.sur5r.net Git - openldap/blob - libraries/liblutil/ntservice.c
More contrib cleanout
[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 DWORD
154 svc_installed (LPTSTR lpszServiceName, LPTSTR lpszBinaryPathName)
155 {
156         char buf[256];
157         HKEY key;
158         DWORD rc;
159         DWORD type;
160         long len;
161
162         strcpy(buf, TEXT("SYSTEM\\CurrentControlSet\\Services\\"));
163         strcat(buf, lpszServiceName);
164         if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, buf, 0, KEY_QUERY_VALUE, &key) != ERROR_SUCCESS)
165                 return(-1);
166
167         rc = 0;
168         if (lpszBinaryPathName) {
169                 len = sizeof(buf);
170                 if (RegQueryValueEx(key, "ImagePath", NULL, &type, buf, &len) == ERROR_SUCCESS) {
171                         if (strcmp(lpszBinaryPathName, buf))
172                                 rc = -1;
173                 }
174         }
175         RegCloseKey(key);
176         return(rc);
177 }
178
179
180 DWORD
181 svc_running (LPTSTR lpszServiceName)
182 {
183         SC_HANDLE service;
184         SC_HANDLE scm;
185         DWORD rc;
186         SERVICE_STATUS ss;
187
188         if (!(scm = OpenSCManager(NULL, NULL, GENERIC_READ)))
189                 return(GetLastError());
190
191         rc = 1;
192         service = OpenService(scm, lpszServiceName, SERVICE_QUERY_STATUS);
193         if (service) {
194                 if (!QueryServiceStatus(service, &ss))
195                         rc = GetLastError();
196                 else if (ss.dwCurrentState != SERVICE_STOPPED)
197                         rc = 0;
198                 CloseServiceHandle(service);
199         }
200         CloseServiceHandle(scm);
201         return(rc);
202 }
203
204
205 static void *start_status_routine( void *ptr )
206 {
207         DWORD   wait_result;
208         int             done = 0;
209
210         while ( !done )
211         {
212                 wait_result = WaitForSingleObject( started_event, SCM_NOTIFICATION_INTERVAL );
213                 switch ( wait_result )
214                 {
215                         case WAIT_ABANDONED:
216                         case WAIT_OBJECT_0:
217                                 /* the object that we were waiting for has been destroyed (ABANDONED) or
218                                  * signalled (TIMEOUT_0). We can assume that the startup process is
219                                  * complete and tell the Service Control Manager that we are now runnng */
220                                 SLAPDServiceStatus.dwCurrentState       = SERVICE_RUNNING;
221                                 SLAPDServiceStatus.dwWin32ExitCode      = NO_ERROR;
222                                 SLAPDServiceStatus.dwCheckPoint++;
223                                 SLAPDServiceStatus.dwWaitHint           = 1000;
224                                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
225                                 done = 1;
226                                 break;
227                         case WAIT_TIMEOUT:
228                                 /* We've waited for the required time, so send an update to the Service Control 
229                                  * Manager saying to wait again. */
230                                 SLAPDServiceStatus.dwCheckPoint++;
231                                 SLAPDServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2;
232                                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
233                                 break;
234                         case WAIT_FAILED:
235                                 /* theres been some problem with WaitForSingleObject so tell the Service
236                                  * Control Manager to wait 30 seconds before deploying its assasin and 
237                                  * then leave the thread. */
238                                 SLAPDServiceStatus.dwCheckPoint++;
239                                 SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS;
240                                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
241                                 done = 1;
242                                 break;
243                 }
244         }
245         ldap_pvt_thread_exit(NULL);
246         return NULL;
247 }
248
249
250
251 static void *stop_status_routine( void *ptr )
252 {
253         DWORD   wait_result;
254         int             done = 0;
255
256         while ( !done )
257         {
258                 wait_result = WaitForSingleObject( stopped_event, SCM_NOTIFICATION_INTERVAL );
259                 switch ( wait_result )
260                 {
261                         case WAIT_ABANDONED:
262                         case WAIT_OBJECT_0:
263                                 /* the object that we were waiting for has been destroyed (ABANDONED) or
264                                  * signalled (TIMEOUT_0). The shutting down process is therefore complete 
265                                  * and the final SERVICE_STOPPED message will be sent to the service control
266                                  * manager prior to the process terminating. */
267                                 done = 1;
268                                 break;
269                         case WAIT_TIMEOUT:
270                                 /* We've waited for the required time, so send an update to the Service Control 
271                                  * Manager saying to wait again. */
272                                 SLAPDServiceStatus.dwCheckPoint++;
273                                 SLAPDServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2;
274                                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
275                                 break;
276                         case WAIT_FAILED:
277                                 /* theres been some problem with WaitForSingleObject so tell the Service
278                                  * Control Manager to wait 30 seconds before deploying its assasin and 
279                                  * then leave the thread. */
280                                 SLAPDServiceStatus.dwCheckPoint++;
281                                 SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS;
282                                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
283                                 done = 1;
284                                 break;
285                 }
286         }
287         ldap_pvt_thread_exit(NULL);
288         return NULL;
289 }
290
291
292
293 void WINAPI SLAPDServiceCtrlHandler( IN DWORD Opcode)
294 {
295         switch (Opcode)
296         {
297         case SERVICE_CONTROL_STOP:
298         case SERVICE_CONTROL_SHUTDOWN:
299
300                 Debug( LDAP_DEBUG_TRACE, "Service Shutdown ordered\n", 0, 0, 0 );
301                 SLAPDServiceStatus.dwCurrentState       = SERVICE_STOP_PENDING;
302                 SLAPDServiceStatus.dwCheckPoint++;
303                 SLAPDServiceStatus.dwWaitHint           = SCM_NOTIFICATION_INTERVAL * 2;
304                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
305
306                 ldap_pvt_thread_cond_init( &stopped_event );
307                 if ( stopped_event == NULL )
308                 {
309                         /* the event was not created. We will ask the service control manager for 30
310                          * seconds to shutdown */
311                         SLAPDServiceStatus.dwCheckPoint++;
312                         SLAPDServiceStatus.dwWaitHint           = THIRTY_SECONDS;
313                         SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
314                 }
315                 else
316                 {
317                         /* start a thread to report the progress to the service control manager 
318                          * until the stopped_event is fired. */
319                         if ( ldap_pvt_thread_create( &stop_status_tid, 0, stop_status_routine, NULL ) == 0 )
320                         {
321                                 
322                         }
323                         else {
324                                 /* failed to create the thread that tells the Service Control Manager that the
325                                  * service stopping is proceeding. 
326                                  * tell the Service Control Manager to wait another 30 seconds before deploying its
327                                  * assasin.  */
328                                 SLAPDServiceStatus.dwCheckPoint++;
329                                 SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS;
330                                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
331                         }
332                 }
333                 stopfunc( -1 );
334                 break;
335
336         case SERVICE_CONTROL_INTERROGATE:
337                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
338                 break;
339         }
340         return;
341 }
342
343 void *getRegParam( char *svc, char *value )
344 {
345         HKEY hkey;
346         char path[255];
347         DWORD vType;
348         static char vValue[1024];
349         DWORD valLen = sizeof( vValue );
350
351         if ( svc != NULL )
352                 sprintf ( path, "SOFTWARE\\%s", svc );
353         else
354                 strcpy (path, "SOFTWARE\\OpenLDAP\\Parameters" );
355         
356         if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, path, 0, KEY_READ, &hkey ) != ERROR_SUCCESS )
357         {
358                 /*Debug( LDAP_DEBUG_ANY, "RegOpenKeyEx() %s\n", GetLastErrorString(), 0, 0); */
359                 return NULL;
360         }
361
362         if ( RegQueryValueEx( hkey, value, NULL, &vType, vValue, &valLen ) != ERROR_SUCCESS )
363         {
364                 /*Debug( LDAP_DEBUG_ANY, "RegQueryValueEx() %s\n", GetLastErrorString(), 0, 0 );*/
365                 RegCloseKey( hkey );
366                 return NULL;
367         }
368         RegCloseKey( hkey );
369         
370         switch ( vType )
371         {
372         case REG_BINARY:
373         case REG_DWORD:
374                 return (void*)&vValue;
375         case REG_SZ:
376                 return (void*)&vValue;
377         }
378         return (void*)NULL;
379 }
380
381 void LogSlapdStartedEvent( char *svc, int slap_debug, char *configfile, char *urls )
382 {
383         char *Inserts[5];
384         WORD i = 0, j;
385         HANDLE hEventLog;
386         
387         hEventLog = RegisterEventSource( NULL, svc );
388
389         Inserts[i] = (char *)malloc( 20 );
390         itoa( slap_debug, Inserts[i++], 10 );
391         Inserts[i++] = strdup( configfile );
392         Inserts[i++] = strdup( urls ? urls : "ldap:///" );
393         Inserts[i++] = strdup( is_NT_Service ? "svc" : "cmd" );
394
395         ReportEvent( hEventLog, EVENTLOG_INFORMATION_TYPE, 0,
396                 MSG_SLAPD_STARTED, NULL, i, 0, (LPCSTR *) Inserts, NULL );
397
398         for ( j = 0; j < i; j++ )
399                 ldap_memfree( Inserts[j] );
400         DeregisterEventSource( hEventLog );
401 }
402
403
404
405 void LogSlapdStoppedEvent( char *svc )
406 {
407         HANDLE hEventLog;
408         
409         hEventLog = RegisterEventSource( NULL, svc );
410         ReportEvent( hEventLog, EVENTLOG_INFORMATION_TYPE, 0,
411                 MSG_SLAPD_STOPPED, NULL, 0, 0, NULL, NULL );
412         DeregisterEventSource( hEventLog );
413 }
414
415
416 void CommenceStartupProcessing( LPCTSTR lpszServiceName,
417                                                            void (*stopper)(int) )
418 {
419         hSLAPDServiceStatus = RegisterServiceCtrlHandler( lpszServiceName, (LPHANDLER_FUNCTION)SLAPDServiceCtrlHandler);
420
421         stopfunc = stopper;
422
423         /* initialize the Service Status structure */
424         SLAPDServiceStatus.dwServiceType                                = SERVICE_WIN32_OWN_PROCESS;
425         SLAPDServiceStatus.dwCurrentState                               = SERVICE_START_PENDING;
426         SLAPDServiceStatus.dwControlsAccepted                   = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
427         SLAPDServiceStatus.dwWin32ExitCode                              = NO_ERROR;
428         SLAPDServiceStatus.dwServiceSpecificExitCode    = 0;
429         SLAPDServiceStatus.dwCheckPoint                                 = 1;
430         SLAPDServiceStatus.dwWaitHint                                   = SCM_NOTIFICATION_INTERVAL * 2;
431
432         SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
433
434         /* start up a thread to keep sending SERVICE_START_PENDING to the Service Control Manager
435          * until the slapd listener is completed and listening. Only then should we send 
436          * SERVICE_RUNNING to the Service Control Manager. */
437         ldap_pvt_thread_cond_init( &started_event );
438         if ( started_event == NULL)
439         {
440                 /* failed to create the event to determine when the startup process is complete so
441                  * tell the Service Control Manager to wait another 30 seconds before deploying its
442                  * assasin  */
443                 SLAPDServiceStatus.dwCheckPoint++;
444                 SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS;
445                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
446         }
447         else
448         {
449                 /* start a thread to report the progress to the service control manager 
450                  * until the started_event is fired.  */
451                 if ( ldap_pvt_thread_create( &start_status_tid, 0, start_status_routine, NULL ) == 0 )
452                 {
453                         
454                 }
455                 else {
456                         /* failed to create the thread that tells the Service Control Manager that the
457                          * service startup is proceeding. 
458                          * tell the Service Control Manager to wait another 30 seconds before deploying its
459                          * assasin.  */
460                         SLAPDServiceStatus.dwCheckPoint++;
461                         SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS;
462                         SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
463                 }
464         }
465 }
466
467 void ReportSlapdShutdownComplete(  )
468 {
469         if ( is_NT_Service )
470         {
471                 /* stop sending SERVICE_STOP_PENDING messages to the Service Control Manager */
472                 ldap_pvt_thread_cond_signal( &stopped_event );
473                 ldap_pvt_thread_cond_destroy( &stopped_event );
474
475                 /* wait for the thread sending the SERVICE_STOP_PENDING messages to the Service Control Manager to die.
476                  * if the wait fails then put ourselves to sleep for half the Service Control Manager update interval */
477                 if (ldap_pvt_thread_join( stop_status_tid, (void *) NULL ) == -1)
478                         ldap_pvt_thread_sleep( SCM_NOTIFICATION_INTERVAL / 2 );
479
480                 SLAPDServiceStatus.dwCurrentState = SERVICE_STOPPED;
481                 SLAPDServiceStatus.dwCheckPoint++;
482                 SLAPDServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL;
483                 SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus);
484         }
485 }
486
487 static char *GetErrorString( int err )
488 {
489         static char msgBuf[1024];
490
491         FormatMessage(
492                 FORMAT_MESSAGE_FROM_SYSTEM,
493                 NULL,
494                 err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
495                 msgBuf, 1024, NULL );
496
497         return msgBuf;
498 }
499
500 static char *GetLastErrorString( void )
501 {
502         return GetErrorString( GetLastError() );
503 }
504 #endif