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