|
|
// Copyright (c) 1996-1999 Microsoft Corporation
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// File: svcctrl.cxx
//
// Contents: Class for service control interface.
//
// Classes:
//
// Functions:
//
//
//
// History: 18-Nov-96 BillMo Created.
//
// Notes:
//
// Codework:
//
//--------------------------------------------------------------------------
#include "pch.cxx"
#pragma hdrstop
#include "trklib.hxx"
#define THIS_FILE_NUMBER SVCCTRL_CXX_FILE_NO
// This is static so that we can handle a PNP timing problem
// (see the comment in CSvcCtrlInterface::ServiceHandler).
BOOL CSvcCtrlInterface::_fStoppedOrStopping = TRUE;
//+----------------------------------------------------------------------------
//
// CSvcCtrlInterface::Initialize
//
// Register our service control handler with the control dispatcher, and set our state
// to start-pending.
//
//+----------------------------------------------------------------------------
void CSvcCtrlInterface::Initialize(const TCHAR *ptszServiceName, IServiceHandler *pServiceHandler) { _fInitializeCalled = TRUE; _pServiceHandler = pServiceHandler; _fStoppedOrStopping = FALSE; _dwCheckPoint = 0;
// Register with the control dispatcher.
_ssh = RegisterServiceCtrlHandlerEx(ptszServiceName, ServiceHandler, this ); if (_ssh == 0) { TrkReportInternalError( THIS_FILE_NUMBER, __LINE__, HRESULT_FROM_WIN32(GetLastError()), ptszServiceName ); TrkRaiseLastError(); }
// Go to the start-pending state.
SetServiceStatus(SERVICE_START_PENDING, 0, NO_ERROR);
}
//+----------------------------------------------------------------------------
//
// CSvcCtrlInterface::ServiceHandler
//
// This method is called by the control dispatcher. If we get a stop
// or shutdown request, automatically send a stop-pending before calling
// the service's handler. Interrogate is handled automatically in
// this routine without bothering to call the service.
//
//+----------------------------------------------------------------------------
DWORD // static
CSvcCtrlInterface::ServiceHandler(DWORD dwControl, DWORD dwEventType, PVOID EventData, PVOID pData) { // NOTE: In services.exe, this method is called on the one and only ServiceHandler
// thread. So while we execute, no other service in this process can
// receive notifications. Thus it is important that we do nothing
// blocking or time-consuming here.
DWORD dwRet = NO_ERROR; CSvcCtrlInterface *pThis = (CSvcCtrlInterface*)pData;
#if DBG
if( SERVICE_CONTROL_STOP == dwControl ) TrkLog(( TRKDBG_SVR|TRKDBG_WKS, TEXT("\n") )); TrkLog(( TRKDBG_SVR|TRKDBG_WKS, TEXT("ServiceHandler(%s)"), StringizeServiceControl(dwControl) )); #endif
// On a stop or shutdown, flag it (e.g. so we don't try to accept new
// requests from clients) and tell the SCM that we're stopping.
switch (dwControl) { case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: pThis->SetServiceStatus(SERVICE_STOP_PENDING, 0, NO_ERROR); _fStoppedOrStopping = TRUE;
break; }
// Check for PNP timing issues. The problem is that during a stop
// or shutdown, we unregister with PNP so that we don't get any more
// notifications. This is fine, except that between now and the time
// we do that unregister, more PNP notifications might get queued. So
// when we get called to process those undesired notifications, we need
// to ignore them here in the static function.
//
// As a quick fix, since only trkwks receives PNP notifications, we'll
// just check to see if it's alive. A better fix (raided) is to have
// a static function for each service, so that only the trkwks has to
// deal with it.
if( SERVICE_CONTROL_DEVICEEVENT == dwControl && _fStoppedOrStopping ) { TrkLog(( TRKDBG_WARNING, TEXT("Ignoring SERVICE_CONTROL_DEVICEEVENT; service is stopped") )); return dwRet; }
// Call this service's service handler. As a final safety measure,
// catch any exceptions (there should be none). We must be sure that we don't
// kill this thread, since it's shared by everyone in services.exe.
__try { dwRet = pThis->_pServiceHandler->ServiceHandler(dwControl, dwEventType, EventData, pData);
switch (dwControl) { case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: break; case SERVICE_CONTROL_PAUSE: pThis->SetServiceStatus(SERVICE_PAUSED, pThis->_dwControlsAccepted, NO_ERROR); break; case SERVICE_CONTROL_CONTINUE: pThis->SetServiceStatus(SERVICE_RUNNING, pThis->_dwControlsAccepted, NO_ERROR); break; case SERVICE_CONTROL_INTERROGATE: pThis->SetServiceStatus(pThis->_dwState, pThis->_dwControlsAccepted, NO_ERROR); break; case SERVICE_CONTROL_DEVICEEVENT: break; } } __except( BREAK_THEN_RETURN( EXCEPTION_EXECUTE_HANDLER )) { TrkLog(( TRKDBG_ERROR, TEXT("Unexpected exception in CSvcCtrlInterface::ServiceHandler (%08x)"), GetExceptionCode() )); dwRet = ERROR_EXCEPTION_IN_SERVICE; }
return dwRet; }
//+----------------------------------------------------------------------------
//
// CSvcCtrlInterface::SetServiceStatus
//
// Send a SetServiceStatus to the SCM. The checkpoint is automatically
// maintained by this class.
//
//+----------------------------------------------------------------------------
void CSvcCtrlInterface::SetServiceStatus(DWORD dwState, DWORD dwControlsAccepted, DWORD dwWin32ExitCode) { SERVICE_STATUS ss;
_dwState = dwState; _dwControlsAccepted = dwControlsAccepted;
if( SERVICE_START_PENDING != dwState && SERVICE_STOP_PENDING != dwState ) { _dwCheckPoint = 0; }
ss.dwServiceType = SERVICE_WIN32; // XX_SC
ss.dwCurrentState = _dwState; ss.dwControlsAccepted = _dwControlsAccepted; ss.dwWin32ExitCode = dwWin32ExitCode; ss.dwServiceSpecificExitCode = 0; ss.dwCheckPoint = _dwCheckPoint++; ss.dwWaitHint = DEFAULT_WAIT_HINT;
if (_ssh != 0) { if( !::SetServiceStatus(_ssh, &ss) ) { TrkLog(( TRKDBG_ERROR, TEXT("SetServiceStatus(%s) failed, gle=%lu"), (const TCHAR*)CDebugString(SServiceState(dwState)), GetLastError() )); } else { TrkLog(( TRKDBG_MISC, TEXT("SetServiceStatus(%s)"), (const TCHAR*)CDebugString(SServiceState(dwState)) )); } } }
//+----------------------------------------------------------------------------
//
// CSvcCtrlInterface::UpdateWaitHint
//
// Send a non-default wait hint to the SCM.
//
//+----------------------------------------------------------------------------
void CSvcCtrlInterface::UpdateWaitHint(DWORD dwMilliseconds) { SERVICE_STATUS ss;
ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ss.dwCurrentState = _dwState; ss.dwControlsAccepted = _dwControlsAccepted; ss.dwWin32ExitCode = NO_ERROR; ss.dwServiceSpecificExitCode = 0; ss.dwCheckPoint = _dwCheckPoint++; ss.dwWaitHint = dwMilliseconds;
if (_ssh != 0) ::SetServiceStatus(_ssh, &ss); }
//+----------------------------------------------------------------------------
//
// StringizeServiceControl (debug only)
//
//+----------------------------------------------------------------------------
#if DBG
TCHAR * StringizeServiceControl( DWORD dwControl ) { switch( dwControl ) { case SERVICE_CONTROL_STOP: return TEXT("SERVICE_CONTROL_STOP");
case SERVICE_CONTROL_PAUSE: return TEXT("SERVICE_CONTROL_PAUSE");
case SERVICE_CONTROL_CONTINUE: return TEXT("SERVICE_CONTROL_CONTINUE");
case SERVICE_CONTROL_INTERROGATE: return TEXT("SERVICE_CONTROL_INTERROGATE");
case SERVICE_CONTROL_SHUTDOWN: return TEXT("SERVICE_CONTROL_SHUTDOWN");
case SERVICE_CONTROL_PARAMCHANGE: return TEXT("SERVICE_CONTROL_PARAMCHANGE");
case SERVICE_CONTROL_NETBINDADD: return TEXT("SERVICE_CONTROL_NETBINDADD");
case SERVICE_CONTROL_NETBINDREMOVE: return TEXT("SERVICE_CONTROL_NETBINDREMOVE");
case SERVICE_CONTROL_NETBINDENABLE: return TEXT("SERVICE_CONTROL_NETBINDENABLE");
case SERVICE_CONTROL_NETBINDDISABLE: return TEXT("SERVICE_CONTROL_NETBINDDISABLE");
case SERVICE_CONTROL_DEVICEEVENT: return TEXT("SERVICE_CONTROL_DEVICEEVENT");
default: return TEXT("Unknown"); } } #endif
|