Back to index

openldap  2.4.31
ntservice.c
Go to the documentation of this file.
00001 /* $OpenLDAP$ */
00002 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00003  *
00004  * Copyright 1998-2012 The OpenLDAP Foundation.
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted only as authorized by the OpenLDAP
00009  * Public License.
00010  *
00011  * A copy of this license is available in the file LICENSE in the
00012  * top-level directory of the distribution or, alternatively, at
00013  * <http://www.OpenLDAP.org/license.html>.
00014  */
00015 
00016 /*
00017  * NT Service manager utilities for OpenLDAP services
00018  */
00019 
00020 #include "portable.h"
00021 
00022 #ifdef HAVE_NT_SERVICE_MANAGER
00023 
00024 #include <ac/stdlib.h>
00025 #include <ac/string.h>
00026 
00027 #include <stdio.h>
00028 
00029 #include <windows.h>
00030 #include <winsvc.h>
00031 
00032 #include <ldap.h>
00033 
00034 #include "ldap_pvt_thread.h"
00035 
00036 #include "ldap_defaults.h"
00037 
00038 #include "slapdmsg.h"
00039 
00040 #define SCM_NOTIFICATION_INTERVAL  5000
00041 #define THIRTY_SECONDS                           (30 * 1000)
00042 
00043 int      is_NT_Service;     /* is this is an NT service? */
00044 
00045 SERVICE_STATUS                     lutil_ServiceStatus;
00046 SERVICE_STATUS_HANDLE       hlutil_ServiceStatus;
00047 
00048 ldap_pvt_thread_cond_t      started_event,              stopped_event;
00049 ldap_pvt_thread_t           start_status_tid,    stop_status_tid;
00050 
00051 void (*stopfunc)(int);
00052 
00053 static char *GetLastErrorString( void );
00054 
00055 int lutil_srv_install(LPCTSTR lpszServiceName, LPCTSTR lpszDisplayName,
00056               LPCTSTR lpszBinaryPathName, int auto_start)
00057 {
00058        HKEY          hKey;
00059        DWORD         dwValue, dwDisposition;
00060        SC_HANDLE     schSCManager, schService;
00061        char *sp = strchr( lpszBinaryPathName, ' ');
00062 
00063        if ( sp ) *sp = '\0';
00064        fprintf( stderr, "The install path is %s.\n", lpszBinaryPathName );
00065        if ( sp ) *sp = ' ';
00066        if ((schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT|SC_MANAGER_CREATE_SERVICE ) ) != NULL )
00067        {
00068               if ((schService = CreateService( 
00069                                                  schSCManager, 
00070                                                  lpszServiceName, 
00071                                                  lpszDisplayName, 
00072                                                  SERVICE_ALL_ACCESS, 
00073                                                  SERVICE_WIN32_OWN_PROCESS, 
00074                                                  auto_start ? SERVICE_AUTO_START : SERVICE_DEMAND_START, 
00075                                                  SERVICE_ERROR_NORMAL, 
00076                                                  lpszBinaryPathName, 
00077                                                  NULL, NULL, NULL, NULL, NULL)) != NULL)
00078               {
00079                      char regpath[132];
00080                      CloseServiceHandle(schService);
00081                      CloseServiceHandle(schSCManager);
00082 
00083                      snprintf( regpath, sizeof regpath,
00084                             "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s",
00085                             lpszServiceName );
00086                      /* Create the registry key for event logging to the Windows NT event log. */
00087                      if ( RegCreateKeyEx(HKEY_LOCAL_MACHINE, 
00088                             regpath, 0, 
00089                             "REG_SZ", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, 
00090                             &dwDisposition) != ERROR_SUCCESS)
00091                      {
00092                             fprintf( stderr, "RegCreateKeyEx() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
00093                             RegCloseKey(hKey);
00094                             return(0);
00095                      }
00096                      if ( sp ) *sp = '\0';
00097                      if ( RegSetValueEx(hKey, "EventMessageFile", 0, REG_EXPAND_SZ, lpszBinaryPathName, strlen(lpszBinaryPathName) + 1) != ERROR_SUCCESS)
00098                      {
00099                             fprintf( stderr, "RegSetValueEx(EventMessageFile) failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
00100                             RegCloseKey(hKey);
00101                             return(0);
00102                      }
00103 
00104                      dwValue = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
00105                      if ( RegSetValueEx(hKey, "TypesSupported", 0, REG_DWORD, (LPBYTE) &dwValue, sizeof(DWORD)) != ERROR_SUCCESS) 
00106                      {
00107                             fprintf( stderr, "RegCreateKeyEx(TypesSupported) failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
00108                             RegCloseKey(hKey);
00109                             return(0);
00110                      }
00111                      RegCloseKey(hKey);
00112                      return(1);
00113               }
00114               else
00115               {
00116                      fprintf( stderr, "CreateService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
00117                      CloseServiceHandle(schSCManager);
00118                      return(0);
00119               }
00120        }
00121        else
00122               fprintf( stderr, "OpenSCManager() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
00123        return(0);
00124 }
00125 
00126 
00127 int lutil_srv_remove(LPCTSTR lpszServiceName, LPCTSTR lpszBinaryPathName)
00128 {
00129        SC_HANDLE schSCManager, schService;
00130 
00131        fprintf( stderr, "The installed path is %s.\n", lpszBinaryPathName );
00132        if ((schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT|SC_MANAGER_CREATE_SERVICE)) != NULL ) 
00133        {
00134               if ((schService = OpenService(schSCManager, lpszServiceName, DELETE)) != NULL) 
00135               {
00136                      if ( DeleteService(schService) == TRUE) 
00137                      {
00138                             CloseServiceHandle(schService);
00139                             CloseServiceHandle(schSCManager);
00140                             return(1);
00141                      } else {
00142                             fprintf( stderr, "DeleteService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
00143                             fprintf( stderr, "The %s service has not been removed.\n", lpszBinaryPathName);
00144                             CloseServiceHandle(schService);
00145                             CloseServiceHandle(schSCManager);
00146                             return(0);
00147                      }
00148               } else {
00149                      fprintf( stderr, "OpenService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
00150                      CloseServiceHandle(schSCManager);
00151                      return(0);
00152               }
00153        }
00154        else
00155               fprintf( stderr, "OpenSCManager() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
00156        return(0);
00157 }
00158 
00159 
00160 #if 0 /* unused */
00161 DWORD
00162 svc_installed (LPTSTR lpszServiceName, LPTSTR lpszBinaryPathName)
00163 {
00164        char buf[256];
00165        HKEY key;
00166        DWORD rc;
00167        DWORD type;
00168        long len;
00169 
00170        strcpy(buf, TEXT("SYSTEM\\CurrentControlSet\\Services\\"));
00171        strcat(buf, lpszServiceName);
00172        if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, buf, 0, KEY_QUERY_VALUE, &key) != ERROR_SUCCESS)
00173               return(-1);
00174 
00175        rc = 0;
00176        if (lpszBinaryPathName) {
00177               len = sizeof(buf);
00178               if (RegQueryValueEx(key, "ImagePath", NULL, &type, buf, &len) == ERROR_SUCCESS) {
00179                      if (strcmp(lpszBinaryPathName, buf))
00180                             rc = -1;
00181               }
00182        }
00183        RegCloseKey(key);
00184        return(rc);
00185 }
00186 
00187 
00188 DWORD
00189 svc_running (LPTSTR lpszServiceName)
00190 {
00191        SC_HANDLE service;
00192        SC_HANDLE scm;
00193        DWORD rc;
00194        SERVICE_STATUS ss;
00195 
00196        if (!(scm = OpenSCManager(NULL, NULL, GENERIC_READ)))
00197               return(GetLastError());
00198 
00199        rc = 1;
00200        service = OpenService(scm, lpszServiceName, SERVICE_QUERY_STATUS);
00201        if (service) {
00202               if (!QueryServiceStatus(service, &ss))
00203                      rc = GetLastError();
00204               else if (ss.dwCurrentState != SERVICE_STOPPED)
00205                      rc = 0;
00206               CloseServiceHandle(service);
00207        }
00208        CloseServiceHandle(scm);
00209        return(rc);
00210 }
00211 #endif
00212 
00213 static void *start_status_routine( void *ptr )
00214 {
00215        DWORD  wait_result;
00216        int           done = 0;
00217 
00218        while ( !done )
00219        {
00220               wait_result = WaitForSingleObject( started_event, SCM_NOTIFICATION_INTERVAL );
00221               switch ( wait_result )
00222               {
00223                      case WAIT_ABANDONED:
00224                      case WAIT_OBJECT_0:
00225                             /* the object that we were waiting for has been destroyed (ABANDONED) or
00226                              * signalled (TIMEOUT_0). We can assume that the startup process is
00227                              * complete and tell the Service Control Manager that we are now runnng */
00228                             lutil_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
00229                             lutil_ServiceStatus.dwWin32ExitCode       = NO_ERROR;
00230                             lutil_ServiceStatus.dwCheckPoint++;
00231                             lutil_ServiceStatus.dwWaitHint            = 1000;
00232                             SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
00233                             done = 1;
00234                             break;
00235                      case WAIT_TIMEOUT:
00236                             /* We've waited for the required time, so send an update to the Service Control 
00237                              * Manager saying to wait again. */
00238                             lutil_ServiceStatus.dwCheckPoint++;
00239                             lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2;
00240                             SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
00241                             break;
00242                      case WAIT_FAILED:
00243                             /* theres been some problem with WaitForSingleObject so tell the Service
00244                              * Control Manager to wait 30 seconds before deploying its assasin and 
00245                              * then leave the thread. */
00246                             lutil_ServiceStatus.dwCheckPoint++;
00247                             lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS;
00248                             SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
00249                             done = 1;
00250                             break;
00251               }
00252        }
00253        ldap_pvt_thread_exit(NULL);
00254        return NULL;
00255 }
00256 
00257 
00258 
00259 static void *stop_status_routine( void *ptr )
00260 {
00261        DWORD  wait_result;
00262        int           done = 0;
00263 
00264        while ( !done )
00265        {
00266               wait_result = WaitForSingleObject( stopped_event, SCM_NOTIFICATION_INTERVAL );
00267               switch ( wait_result )
00268               {
00269                      case WAIT_ABANDONED:
00270                      case WAIT_OBJECT_0:
00271                             /* the object that we were waiting for has been destroyed (ABANDONED) or
00272                              * signalled (TIMEOUT_0). The shutting down process is therefore complete 
00273                              * and the final SERVICE_STOPPED message will be sent to the service control
00274                              * manager prior to the process terminating. */
00275                             done = 1;
00276                             break;
00277                      case WAIT_TIMEOUT:
00278                             /* We've waited for the required time, so send an update to the Service Control 
00279                              * Manager saying to wait again. */
00280                             lutil_ServiceStatus.dwCheckPoint++;
00281                             lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2;
00282                             SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
00283                             break;
00284                      case WAIT_FAILED:
00285                             /* theres been some problem with WaitForSingleObject so tell the Service
00286                              * Control Manager to wait 30 seconds before deploying its assasin and 
00287                              * then leave the thread. */
00288                             lutil_ServiceStatus.dwCheckPoint++;
00289                             lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS;
00290                             SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
00291                             done = 1;
00292                             break;
00293               }
00294        }
00295        ldap_pvt_thread_exit(NULL);
00296        return NULL;
00297 }
00298 
00299 
00300 
00301 static void WINAPI lutil_ServiceCtrlHandler( IN DWORD Opcode)
00302 {
00303        switch (Opcode)
00304        {
00305        case SERVICE_CONTROL_STOP:
00306        case SERVICE_CONTROL_SHUTDOWN:
00307 
00308               lutil_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
00309               lutil_ServiceStatus.dwCheckPoint++;
00310               lutil_ServiceStatus.dwWaitHint            = SCM_NOTIFICATION_INTERVAL * 2;
00311               SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
00312 
00313               ldap_pvt_thread_cond_init( &stopped_event );
00314               if ( stopped_event == NULL )
00315               {
00316                      /* the event was not created. We will ask the service control manager for 30
00317                       * seconds to shutdown */
00318                      lutil_ServiceStatus.dwCheckPoint++;
00319                      lutil_ServiceStatus.dwWaitHint            = THIRTY_SECONDS;
00320                      SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
00321               }
00322               else
00323               {
00324                      /* start a thread to report the progress to the service control manager 
00325                       * until the stopped_event is fired. */
00326                      if ( ldap_pvt_thread_create( &stop_status_tid, 0, stop_status_routine, NULL ) == 0 )
00327                      {
00328                             
00329                      }
00330                      else {
00331                             /* failed to create the thread that tells the Service Control Manager that the
00332                              * service stopping is proceeding. 
00333                              * tell the Service Control Manager to wait another 30 seconds before deploying its
00334                              * assasin.  */
00335                             lutil_ServiceStatus.dwCheckPoint++;
00336                             lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS;
00337                             SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
00338                      }
00339               }
00340               stopfunc( -1 );
00341               break;
00342 
00343        case SERVICE_CONTROL_INTERROGATE:
00344               SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
00345               break;
00346        }
00347        return;
00348 }
00349 
00350 void *lutil_getRegParam( char *svc, char *value )
00351 {
00352        HKEY hkey;
00353        char path[255];
00354        DWORD vType;
00355        static char vValue[1024];
00356        DWORD valLen = sizeof( vValue );
00357 
00358        if ( svc != NULL )
00359               snprintf ( path, sizeof path, "SOFTWARE\\%s", svc );
00360        else
00361               snprintf ( path, sizeof path, "SOFTWARE\\OpenLDAP\\Parameters" );
00362        
00363        if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, path, 0, KEY_READ, &hkey ) != ERROR_SUCCESS )
00364        {
00365               return NULL;
00366        }
00367 
00368        if ( RegQueryValueEx( hkey, value, NULL, &vType, vValue, &valLen ) != ERROR_SUCCESS )
00369        {
00370               RegCloseKey( hkey );
00371               return NULL;
00372        }
00373        RegCloseKey( hkey );
00374        
00375        switch ( vType )
00376        {
00377        case REG_BINARY:
00378        case REG_DWORD:
00379               return (void*)&vValue;
00380        case REG_SZ:
00381               return (void*)&vValue;
00382        }
00383        return (void*)NULL;
00384 }
00385 
00386 void lutil_LogStartedEvent( char *svc, int slap_debug, char *configfile, char *urls )
00387 {
00388        char *Inserts[5];
00389        WORD i = 0, j;
00390        HANDLE hEventLog;
00391        
00392        hEventLog = RegisterEventSource( NULL, svc );
00393 
00394        Inserts[i] = (char *)malloc( 20 );
00395        itoa( slap_debug, Inserts[i++], 10 );
00396        Inserts[i++] = strdup( configfile );
00397        Inserts[i++] = strdup( urls ? urls : "ldap:///" );
00398 
00399        ReportEvent( hEventLog, EVENTLOG_INFORMATION_TYPE, 0,
00400               MSG_SVC_STARTED, NULL, i, 0, (LPCSTR *) Inserts, NULL );
00401 
00402        for ( j = 0; j < i; j++ )
00403               ldap_memfree( Inserts[j] );
00404        DeregisterEventSource( hEventLog );
00405 }
00406 
00407 
00408 
00409 void lutil_LogStoppedEvent( char *svc )
00410 {
00411        HANDLE hEventLog;
00412        
00413        hEventLog = RegisterEventSource( NULL, svc );
00414        ReportEvent( hEventLog, EVENTLOG_INFORMATION_TYPE, 0,
00415               MSG_SVC_STOPPED, NULL, 0, 0, NULL, NULL );
00416        DeregisterEventSource( hEventLog );
00417 }
00418 
00419 
00420 void lutil_CommenceStartupProcessing( char *lpszServiceName,
00421                                                     void (*stopper)(int) )
00422 {
00423        hlutil_ServiceStatus = RegisterServiceCtrlHandler( lpszServiceName, (LPHANDLER_FUNCTION)lutil_ServiceCtrlHandler);
00424 
00425        stopfunc = stopper;
00426 
00427        /* initialize the Service Status structure */
00428        lutil_ServiceStatus.dwServiceType                       = SERVICE_WIN32_OWN_PROCESS;
00429        lutil_ServiceStatus.dwCurrentState                      = SERVICE_START_PENDING;
00430        lutil_ServiceStatus.dwControlsAccepted                  = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
00431        lutil_ServiceStatus.dwWin32ExitCode                            = NO_ERROR;
00432        lutil_ServiceStatus.dwServiceSpecificExitCode    = 0;
00433        lutil_ServiceStatus.dwCheckPoint                               = 1;
00434        lutil_ServiceStatus.dwWaitHint                                 = SCM_NOTIFICATION_INTERVAL * 2;
00435 
00436        SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
00437 
00438        /* start up a thread to keep sending SERVICE_START_PENDING to the Service Control Manager
00439         * until the slapd listener is completed and listening. Only then should we send 
00440         * SERVICE_RUNNING to the Service Control Manager. */
00441        ldap_pvt_thread_cond_init( &started_event );
00442        if ( started_event == NULL)
00443        {
00444               /* failed to create the event to determine when the startup process is complete so
00445                * tell the Service Control Manager to wait another 30 seconds before deploying its
00446                * assasin  */
00447               lutil_ServiceStatus.dwCheckPoint++;
00448               lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS;
00449               SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
00450        }
00451        else
00452        {
00453               /* start a thread to report the progress to the service control manager 
00454                * until the started_event is fired.  */
00455               if ( ldap_pvt_thread_create( &start_status_tid, 0, start_status_routine, NULL ) == 0 )
00456               {
00457                      
00458               }
00459               else {
00460                      /* failed to create the thread that tells the Service Control Manager that the
00461                       * service startup is proceeding. 
00462                       * tell the Service Control Manager to wait another 30 seconds before deploying its
00463                       * assasin.  */
00464                      lutil_ServiceStatus.dwCheckPoint++;
00465                      lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS;
00466                      SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
00467               }
00468        }
00469 }
00470 
00471 void lutil_ReportShutdownComplete(  )
00472 {
00473        if ( is_NT_Service )
00474        {
00475               /* stop sending SERVICE_STOP_PENDING messages to the Service Control Manager */
00476               ldap_pvt_thread_cond_signal( &stopped_event );
00477               ldap_pvt_thread_cond_destroy( &stopped_event );
00478 
00479               /* wait for the thread sending the SERVICE_STOP_PENDING messages to the Service Control Manager to die.
00480                * if the wait fails then put ourselves to sleep for half the Service Control Manager update interval */
00481               if (ldap_pvt_thread_join( stop_status_tid, (void *) NULL ) == -1)
00482                      ldap_pvt_thread_sleep( SCM_NOTIFICATION_INTERVAL / 2 );
00483 
00484               lutil_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
00485               lutil_ServiceStatus.dwCheckPoint++;
00486               lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL;
00487               SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
00488        }
00489 }
00490 
00491 static char *GetErrorString( int err )
00492 {
00493        static char msgBuf[1024];
00494 
00495        FormatMessage(
00496               FORMAT_MESSAGE_FROM_SYSTEM,
00497               NULL,
00498               err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
00499               msgBuf, 1024, NULL );
00500 
00501        return msgBuf;
00502 }
00503 
00504 static char *GetLastErrorString( void )
00505 {
00506        return GetErrorString( GetLastError() );
00507 }
00508 #endif