From: Gary Williams Date: Wed, 16 Jun 1999 17:53:17 +0000 (+0000) Subject: new code to support slapd as an NT service X-Git-Tag: OPENLDAP_REL_ENG_2_BP~295 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=4d6ce8b87e408501ab04aa4e85f075c32fd25013;p=openldap new code to support slapd as an NT service --- diff --git a/libraries/liblutil/nt_err.c b/libraries/liblutil/nt_err.c new file mode 100644 index 0000000000..de279fcd45 --- /dev/null +++ b/libraries/liblutil/nt_err.c @@ -0,0 +1,23 @@ +#include +#include + +#define __RETSTR( x ) case x: return #x; + +char *GetErrorString( int err ) +{ + static char msgBuf[1024]; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + msgBuf, 1024, NULL ); + return msgBuf; +} + +char *GetLastErrorString( void ) +{ + return GetErrorString( GetLastError() ); +} + +#undef __RETSTR \ No newline at end of file diff --git a/libraries/liblutil/ntservice.c b/libraries/liblutil/ntservice.c new file mode 100644 index 0000000000..10459c7a79 --- /dev/null +++ b/libraries/liblutil/ntservice.c @@ -0,0 +1,428 @@ +// ntservice.c +#include "portable.h" +#include +#include +#include "ldap.h" +#include "ldap_log.h" +#include "ldap_pvt_thread.h" +#include +#include +#include + +#include "ldapconfig.h" +//#include "slap.h" + +#include "slapdmsg.h" + +#define SCM_NOTIFICATION_INTERVAL 5000 +#define THIRTY_SECONDS (30 * 1000) + +int is_NT_Service = 1; // assume this is an NT service until determined that + // startup was from the command line + +SERVICE_STATUS SLAPDServiceStatus; +SERVICE_STATUS_HANDLE hSLAPDServiceStatus; + +ldap_pvt_thread_cond_t started_event, stopped_event; +ldap_pvt_thread_t start_status_tid, stop_status_tid; + +void (*stopfunc)(int); + +// in main.c +void WINAPI ServiceMain( DWORD argc, LPTSTR *argv ); + + +// in wsa_err.c +char *WSAGetLastErrorString( void ); + +// in nt_err.c +char *GetLastErrorString( void ); + +int srv_install(LPCTSTR lpszServiceName, LPCTSTR lpszBinaryPathName) +{ + HKEY hKey; + DWORD dwValue, dwDisposition; + SC_HANDLE schSCManager, schService; + + fprintf( stderr, "The install path is %s.\n", lpszBinaryPathName ); + if ((schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT|SC_MANAGER_CREATE_SERVICE ) ) != NULL ) + { + if ((schService = CreateService( + schSCManager, + lpszServiceName, + TEXT("OpenLDAP Directory Service"), + SC_MANAGER_CREATE_SERVICE, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_DEMAND_START, + SERVICE_ERROR_NORMAL, + lpszBinaryPathName, + NULL, NULL, NULL, NULL, NULL)) != NULL) + { + char regpath[132]; + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); + + sprintf( regpath, "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s", + lpszServiceName ); + // Create the registry key for event logging to the Windows NT event log. + if ( RegCreateKeyEx(HKEY_LOCAL_MACHINE, + regpath, 0, + "REG_SZ", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, + &dwDisposition) != ERROR_SUCCESS) + { + fprintf( stderr, "RegCreateKeyEx() failed. GetLastError=%d (%s)\n", GetLastError(), GetLastErrorString() ); + RegCloseKey(hKey); + return(0); + } + if ( RegSetValueEx(hKey, "EventMessageFile", 0, REG_EXPAND_SZ, lpszBinaryPathName, strlen(lpszBinaryPathName) + 1) != ERROR_SUCCESS) + { + fprintf( stderr, "RegSetValueEx(EventMessageFile) failed. GetLastError=%d (%s)\n", GetLastError(), GetLastErrorString() ); + RegCloseKey(hKey); + return(0); + } + + dwValue = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; + if ( RegSetValueEx(hKey, "TypesSupported", 0, REG_DWORD, (LPBYTE) &dwValue, sizeof(DWORD)) != ERROR_SUCCESS) + { + fprintf( stderr, "RegCreateKeyEx(TypesSupported) failed. GetLastError=%d (%s)\n", GetLastError(), GetLastErrorString() ); + RegCloseKey(hKey); + return(0); + } + RegCloseKey(hKey); + return(1); + } + else + { + fprintf( stderr, "CreateService() failed. GetLastError=%d (%s)\n", GetLastError(), GetLastErrorString() ); + CloseServiceHandle(schSCManager); + return(0); + } + } + else + fprintf( stderr, "OpenSCManager() failed. GetLastError=%d (%s)\n", GetLastError(), GetLastErrorString() ); + return(0); +} + + +int srv_remove(LPCTSTR lpszServiceName, LPCTSTR lpszBinaryPathName) +{ + SC_HANDLE schSCManager, schService; + + fprintf( stderr, "The installed path is %s.\n", lpszBinaryPathName ); + if ((schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT|SC_MANAGER_CREATE_SERVICE)) != NULL ) + { + if ((schService = OpenService(schSCManager, lpszServiceName, DELETE)) != NULL) + { + if ( DeleteService(schService) == TRUE) + { + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); + return(1); + } else { + fprintf( stderr, "DeleteService() failed. GetLastError=%d (%s)\n", GetLastError(), GetLastErrorString() ); + fprintf( stderr, "The %s service has not been removed.\n", lpszBinaryPathName); + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); + return(0); + } + } else { + fprintf( stderr, "OpenService() failed. GetLastError=%d (%s)\n", GetLastError(), GetLastErrorString() ); + CloseServiceHandle(schSCManager); + return(0); + } + } + else + fprintf( stderr, "OpenSCManager() failed. GetLastError=%d (%s)\n", GetLastError(), GetLastErrorString() ); + return(0); +} + + + +static void *start_status_routine( void *ptr ) +{ + DWORD wait_result; + int done = 0; + + while ( !done ) + { + wait_result = WaitForSingleObject( started_event, SCM_NOTIFICATION_INTERVAL ); + switch ( wait_result ) + { + case WAIT_ABANDONED: + case WAIT_OBJECT_0: + // the object that we were waiting for has been destroyed (ABANDONED) or + // signalled (TIMEOUT_0). We can assume that the startup process is + // complete and tell the Service Control Manager that we are now runnng + SLAPDServiceStatus.dwCurrentState = SERVICE_RUNNING; + SLAPDServiceStatus.dwWin32ExitCode = NO_ERROR; + SLAPDServiceStatus.dwCheckPoint++; + SLAPDServiceStatus.dwWaitHint = 1000; + SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus); + done = 1; + break; + case WAIT_TIMEOUT: + // We've waited for the required time, so send an update to the Service Control + // Manager saying to wait again. + SLAPDServiceStatus.dwCheckPoint++; + SLAPDServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2; + SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus); + break; + case WAIT_FAILED: + // theres been some proble with WaitForSingleObject so tell the Service + // Control Manager to wait 30 seconds before deploying its assasin and + // then leave the thread. + SLAPDServiceStatus.dwCheckPoint++; + SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS; + SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus); + done = 1; + break; + } + } + ldap_pvt_thread_exit(NULL); + return NULL; +} + + + +static void *stop_status_routine( void *ptr ) +{ + DWORD wait_result; + int done = 0; + + while ( !done ) + { + wait_result = WaitForSingleObject( stopped_event, SCM_NOTIFICATION_INTERVAL ); + switch ( wait_result ) + { + case WAIT_ABANDONED: + case WAIT_OBJECT_0: + // the object that we were waiting for has been destroyed (ABANDONED) or + // signalled (TIMEOUT_0). The shutting down process is therefore complete + // and the final SERVICE_STOPPED message will be sent to the service control + // manager prior to the process terminating. + done = 1; + break; + case WAIT_TIMEOUT: + // We've waited for the required time, so send an update to the Service Control + // Manager saying to wait again. + SLAPDServiceStatus.dwCheckPoint++; + SLAPDServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2; + SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus); + break; + case WAIT_FAILED: + // theres been some proble with WaitForSingleObject so tell the Service + // Control Manager to wait 30 seconds before deploying its assasin and + // then leave the thread. + SLAPDServiceStatus.dwCheckPoint++; + SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS; + SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus); + done = 1; + break; + } + } + ldap_pvt_thread_exit(NULL); + return NULL; +} + + + +void WINAPI SLAPDServiceCtrlHandler( IN DWORD Opcode) +{ + switch (Opcode) + { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + + Debug( LDAP_DEBUG_TRACE, "Service Shutdown ordered\n", 0, 0 ); + SLAPDServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; + SLAPDServiceStatus.dwCheckPoint++; + SLAPDServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2; + SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus); + + ldap_pvt_thread_cond_init( &stopped_event ); + if ( stopped_event == NULL ) + { + // the event was not created. We will ask the service control manager for 30 + // seconds to shutdown + SLAPDServiceStatus.dwCheckPoint++; + SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS; + SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus); + } + else + { + // start a thread to report the progress to the service control manager + // until the stopped_event is fired. + if ( ldap_pvt_thread_create( &stop_status_tid, 0, stop_status_routine, NULL ) == 0 ) + { + + } + else { + // failed to create the thread that tells the Service Control Manager that the + // service stopping is proceeding. + // tell the Service Control Manager to wait another 30 seconds before deploying its + // assasin. + SLAPDServiceStatus.dwCheckPoint++; + SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS; + SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus); + } + } + stopfunc( -1 ); + break; + + case SERVICE_CONTROL_INTERROGATE: + SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus); + break; + } + return; +} + + +void *getRegParam( char *svc, char *value ) +{ + HANDLE hkey; + char path[255]; + int i = 0, rc; + char vName[256]; + DWORD vNameLen = 255; + DWORD vType; + static char vValue[1024]; + DWORD valLen = 1024; + + if ( svc != NULL ) + sprintf ( path, "SOFTWARE\\OpenLDAP\\%s\\Parameters", svc ); + else + strcpy (path, "SOFTWARE\\OpenLDAP\\Parameters" ); + + if( (rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE, + path, 0, KEY_READ, &hkey )) != ERROR_SUCCESS ) + { + //Debug( LDAP_DEBUG_ANY, "%s\n", GetLastErrorString(), 0, 0 ); + return NULL; + } + + while ( !RegEnumValue( hkey, i, vName, &vNameLen, NULL, + &vType, vValue, &valLen ) ) + { + if ( !strcasecmp( value, vName ) ) + { + switch ( vType ) + { + case REG_BINARY: + case REG_DWORD: + return (void*)&vValue; + case REG_SZ: + return (void*)strdup( vValue ); + } + } + i++; + vNameLen = 255; + valLen = 1024; + } + return (void*)NULL; +} + +void LogSlapdStartedEvent( char *svc, int slap_debug, char *configfile, short port, int udp ) +{ + char *Inserts[5]; + WORD i = 0, j; + HANDLE hEventLog; + + hEventLog = RegisterEventSource( NULL, svc ); + + Inserts[i] = (char *)malloc( 20 ); + itoa( slap_debug, Inserts[i++], 10 ); + Inserts[i++] = ldap_pvt_strdup( configfile ); + Inserts[i] = (char *)malloc( 20 ); + itoa( port, Inserts[i++], 10 ); + Inserts[i++] = ldap_pvt_strdup( udp ? "udp" : "tcp" ); + Inserts[i++] = ldap_pvt_strdup( is_NT_Service ? "svc" : "cmd" ); + + ReportEvent( hEventLog, EVENTLOG_INFORMATION_TYPE, 0, MSG_SLAPD_STARTED, NULL, i, 0, Inserts, NULL ); + + for ( j = 0; j < i; j++ ) + ldap_memfree( Inserts[j] ); + DeregisterEventSource( hEventLog ); +} + + + +void LogSlapdStoppedEvent( char *svc ) +{ + HANDLE hEventLog; + + hEventLog = RegisterEventSource( NULL, svc ); + ReportEvent( hEventLog, EVENTLOG_INFORMATION_TYPE, 0, MSG_SLAPD_STOPPED, NULL, 0, 0, NULL, NULL ); + DeregisterEventSource( hEventLog ); +} + + +void CommenceStartupProcessing( LPCTSTR lpszServiceName, + void (*stopper)(int) ) +{ + hSLAPDServiceStatus = RegisterServiceCtrlHandler( lpszServiceName, (LPHANDLER_FUNCTION)SLAPDServiceCtrlHandler); + + stopfunc = stopper; + + /* initialize the Service Status structure */ + SLAPDServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + SLAPDServiceStatus.dwCurrentState = SERVICE_START_PENDING; + SLAPDServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + SLAPDServiceStatus.dwWin32ExitCode = NO_ERROR; + SLAPDServiceStatus.dwServiceSpecificExitCode = 0; + SLAPDServiceStatus.dwCheckPoint = 1; + SLAPDServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2; + + SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus); + + // start up a thread to keep sending SERVICE_START_PENDING to the Service Control Manager + // until the slapd listener is completed and listening. Only then should we send + // SERVICE_RUNNING to the Service Control Manager. + ldap_pvt_thread_cond_init( &started_event ); + if ( started_event == NULL) + { + // failed to create the event to determine when the startup process is complete so + // tell the Service Control Manager to wait another 30 seconds before deploying its + // assasin + SLAPDServiceStatus.dwCheckPoint++; + SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS; + SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus); + } + else + { + // start a thread to report the progress to the service control manager + // until the started_event is fired. + if ( ldap_pvt_thread_create( &start_status_tid, 0, start_status_routine, NULL ) == 0 ) + { + + } + else { + // failed to create the thread that tells the Service Control Manager that the + // service startup is proceeding. + // tell the Service Control Manager to wait another 30 seconds before deploying its + // assasin. + SLAPDServiceStatus.dwCheckPoint++; + SLAPDServiceStatus.dwWaitHint = THIRTY_SECONDS; + SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus); + } + } +} + +void ReportSlapdShutdownComplete( ) +{ + if ( is_NT_Service ) + { + // stop sending SERVICE_STOP_PENDING messages to the Service Control Manager + ldap_pvt_thread_cond_signal( &stopped_event ); + ldap_pvt_thread_cond_destroy( &stopped_event ); + + // wait for the thread sending the SERVICE_STOP_PENDING messages to the Service Control Manager to die. + // if the wait fails then put ourselves to sleep for half the Service Control Manager update interval + if (ldap_pvt_thread_join( stop_status_tid, (void *) NULL ) == -1) + ldap_pvt_thread_sleep( SCM_NOTIFICATION_INTERVAL / 2 ); + + SLAPDServiceStatus.dwCurrentState = SERVICE_STOPPED; + SLAPDServiceStatus.dwCheckPoint++; + SLAPDServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL; + SetServiceStatus(hSLAPDServiceStatus, &SLAPDServiceStatus); + } +} diff --git a/libraries/liblutil/slapdmsg.mc b/libraries/liblutil/slapdmsg.mc new file mode 100644 index 0000000000..2c6c88367c --- /dev/null +++ b/libraries/liblutil/slapdmsg.mc @@ -0,0 +1,28 @@ +;// +;// This file contains message strings for the OpenLDAP slapd service. +;// +;// This file should be compiled as follows +;// mc -v slapdmsg.mc -r $(IntDir) +;// rc /v /r $(IntDir)\slapdmsg.rc +;// The mc (message compiler) command generates the .rc and .h files from this file. The +;// rc (resource compiler) takes the .rc file and produces a .res file that can be linked +;// with the final executable application. The application is then registered as a message +;// source with by creating the appropriate entries in the system registry. +;// + +MessageID=0x500 +Severity=Informational +SymbolicName=MSG_SLAPD_STARTED +Facility=Application +Language=English +OpenLDAP SLAPD service started. debuglevel=%1, conffile=%2, port=%3, ip=%4, mode=%5 +. + + +MessageID=0x501 +Severity=Informational +SymbolicName=MSG_SLAPD_STOPPED +Facility=Application +Language=English +OpenLDAP SLAPD service stopped. +.