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