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