windows-nt/Source/XPSP1/NT/admin/pchealth/sr/service/ntservice.cpp
2020-09-26 16:20:57 +08:00

453 lines
11 KiB
C++

/******************************************************************************
*
* Copyright (c) 2000 Microsoft Corporation
*
* Module Name:
* NTService.cpp
*
* Abstract:
* This file contains the implementation of CNTService class.
*
* Revision History:
* Ashish Sikka ( ashishs ) 05/08/2000
* created
*****************************************************************************/
#include "precomp.h"
#include "ntservmsg.h" // generated from the MC message compiler
#ifdef THIS_FILE
#undef THIS_FILE
#endif
static char __szTraceSourceFile[] = __FILE__;
#define THIS_FILE __szTraceSourceFile
#define TRACEID 8970
CNTService * g_pSRService=NULL;
#define SERVICE_WAIT_HINT 30 // seconds
extern "C" void CALLBACK
StopCallback(
PVOID pv,
BOOLEAN TimerOrWaitFired);
//
// static variables
//
CNTService::CNTService()
{
TraceFunctEnter("CNTService:CNTService");
m_hEventSource = NULL;
//
// Set up the initial service status
//
m_hServiceStatus = NULL;
m_Status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
m_Status.dwCurrentState = SERVICE_START_PENDING;
m_Status.dwControlsAccepted = SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN;
m_Status.dwWin32ExitCode = 0;
m_Status.dwServiceSpecificExitCode = 0;
m_Status.dwCheckPoint = 0;
m_Status.dwWaitHint = 0;
TraceFunctLeave();
}
CNTService::~CNTService()
{
TENTER("CNTService::~CNTService");
if (m_hEventSource)
{
::DeregisterEventSource(m_hEventSource);
}
TLEAVE();
}
//
// Logging functions
//
void CNTService::LogEvent(
WORD wType, DWORD dwID,
void * pRawData,
DWORD dwDataSize,
const WCHAR* pszS1,
const WCHAR* pszS2,
const WCHAR* pszS3)
{
TraceFunctEnter("CNTService::LogEvent");
//
// Check the event source has been registered and if
// not then register it now
//
if (!m_hEventSource)
{
m_hEventSource = ::RegisterEventSource(NULL, s_cszServiceName);
}
SRLogEvent (m_hEventSource, wType, dwID, pRawData,
dwDataSize, pszS1, pszS2, pszS3);
TraceFunctLeave();
}
//
// ServiceMain
//
//This function immediately reports the service as having started.
//However, all the initialization is done after the service is
//started. We chose to do this becuase this service may have a long
//initialization time and it may be tricky to keep giving hints to the
//SCM during this time. Also, the service does all the work itself and
//does not service any clients. So it is OK to do initialization after
//the service is reported to be started.
void CNTService::ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv)
{
DWORD dwRc = ERROR_SUCCESS;
HANDLE hSRStopWait = NULL;
TENTER("CNTService::ServiceMain()");
// Register the control request handler
m_hServiceStatus = RegisterServiceCtrlHandler(s_cszServiceName,
SRServiceHandler);
if (m_hServiceStatus != NULL)
{
HKEY hKey = NULL;
DWORD dwBreak = 0;
/*
// tell the service manager that we are starting
// Also inform the Controls accepted
m_Status.dwCheckPoint = 0;
m_Status.dwWaitHint = SERVICE_WAIT_HINT*1000;
SetStatus(SERVICE_START_PENDING);
*/
//
// Tell the service manager we are running
//
m_Status.dwCheckPoint = 0;
m_Status.dwWaitHint = 0;
SetStatus(SERVICE_RUNNING);
// break into debugger if need to debug
// this is controlled by setting regkey SRService\DebugBreak to 1
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
s_cszServiceRegKey,
0,
KEY_READ,
&hKey))
{
RegReadDWORD(hKey, s_cszDebugBreak, &dwBreak);
ASSERT (dwBreak != 1);
RegCloseKey(hKey);
}
//
// do boot time tasks - including firstrun if necessary
//
g_pEventHandler = new CEventHandler;
if ( ! g_pEventHandler )
{
dwRc = GetLastError();
TRACE(TRACEID, "! out of memory");
goto done;
}
dwRc = g_pEventHandler->OnBoot( );
if ( ERROR_SUCCESS != dwRc )
{
TRACE(TRACEID, "g_pEventHandler->OnBoot : error=%ld", dwRc);
goto done;
}
// bind the stop event to a callback
// so that this gets called on a thread pool thread
// when the stop event is signalled
if (FALSE == RegisterWaitForSingleObject(&hSRStopWait,
g_pSRConfig->m_hSRStopEvent,
StopCallback,
NULL,
INFINITE,
WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE))
{
dwRc = GetLastError();
trace(0, "! RegisterWaitForSingleObject : %ld", dwRc);
goto done;
}
}
else
{
// There is not much we can do here if we do not have the
// Service handle. So we will just log an error and exit.
dwRc = GetLastError();
DebugTrace(TRACEID, "RegisterServiceCtrlHandler failed %d", dwRc);
LogEvent(EVENTLOG_ERROR_TYPE, EVMSG_CTRLHANDLERNOTINSTALLED);
}
done:
if (dwRc != ERROR_SUCCESS)
{
if (g_pEventHandler)
{
g_pEventHandler->OnStop();
delete g_pEventHandler;
g_pEventHandler = NULL;
}
m_Status.dwWin32ExitCode = (dwRc != ERROR_SERVICE_DISABLED) ?
dwRc : ERROR_SUCCESS;
SetStatus(SERVICE_STOPPED);
if (dwRc != ERROR_SERVICE_DISABLED)
LogEvent(EVENTLOG_ERROR_TYPE, EVMSG_FAILEDINI, &dwRc, sizeof(dwRc));
}
TLEAVE();
}
//
// SetStatus:
//
void CNTService::SetStatus(DWORD dwState)
{
TENTER("CNTService::SetStatus");
TRACE(TRACEID, "SetStatus(%lu, %lu)", m_hServiceStatus, dwState);
m_Status.dwCurrentState = dwState;
::SetServiceStatus(m_hServiceStatus, &m_Status);
TLEAVE();
}
void CNTService::OnStop()
{
TENTER("CNTService::OnStop");
// BUGBUG what happens if the service has not started completely
// yet ? We need to make sure that OnStop can only be called after
// g_pEventHandler has been initialized.
if (NULL != g_pEventHandler)
{
// Tell SCM we are stopping
m_Status.dwWin32ExitCode = 0;
m_Status.dwCheckPoint = 0;
// we will stop in half the time we took to start or lesser
m_Status.dwWaitHint = (SERVICE_WAIT_HINT/2)*1000;
SetStatus(SERVICE_STOP_PENDING);
// complete all tasks
g_pEventHandler->SignalStop();
}
TLEAVE();
}
// OnInterrogate is called by the SCM to get information about the
// current status of the service. Since this must report information
// about the service immediately to the SCM, we will run this in the
// same thread on which the handler function is called.
void CNTService::OnInterrogate()
{
TENTER("CNTService::OnInterrogate");
// report the status
::SetServiceStatus(m_hServiceStatus, &m_Status);
TLEAVE();
}
//
// Handler : static member function (callback) to handle commands from the
// service control manager
//
void WINAPI SRServiceHandler(DWORD dwOpcode)
{
//
// Get a pointer to the object
//
TENTER("CNTService::Handler");
TRACE(TRACEID, "CNTService::Handler(%lu)", dwOpcode);
switch (dwOpcode)
{
case SERVICE_CONTROL_STOP: // 1
//
// if someone disables the service explicitly
// then disable all of SR
//
if (NULL != g_pSRService)
{
DWORD dwStart = 0;
if (ERROR_SUCCESS == GetServiceStartup(s_cszServiceName, &dwStart))
{
if (dwStart == SERVICE_DISABLED || dwStart == SERVICE_DEMAND_START)
{
if (g_pEventHandler)
g_pEventHandler->DisableSRS(NULL);
break;
}
}
else
{
trace(TRACEID, "! GetServiceStartup");
}
}
//
// else fallover
//
case SERVICE_CONTROL_SHUTDOWN: // 5
// BUGBUG - g_pSRService should be accessed in critical section
if (NULL != g_pSRService)
{
g_pSRService->OnStop();
}
break;
case SERVICE_CONTROL_PAUSE: // 2
case SERVICE_CONTROL_CONTINUE: // 3
// we do not do anything here
break;
case SERVICE_CONTROL_INTERROGATE: // 4
// BUGBUG - g_pSRService should be accessed in critical section
if (NULL != g_pSRService)
{
g_pSRService->OnInterrogate();
}
break;
default:
break;
}
TLEAVE();
}
/////////////////////////////////////////////////////////////////////////////
// Exported functions
/////////////////////////////////////////////////////////////////////////////
extern "C"
{
VOID WINAPI ServiceMain(
DWORD dwArgc,
LPWSTR *lpwzArgv )
{
// Initialize tracing
InitAsyncTrace();
TraceFunctEnter("ServiceMain");
g_pSRService = new CNTService();
if (NULL == g_pSRService)
{
// in this case we will just exit. This is because we cannot
// report any status here.
// SCM will assume that the service has failed since it did
// not call RegisterServiceCtrlHandler().
goto cleanup;
}
g_pEventHandler = NULL;
g_pSRService->ServiceMain( dwArgc, NULL );
cleanup:
TraceFunctLeave();
}
BOOL WINAPI DllMain(
HINSTANCE hInstance,
DWORD dwReason,
LPVOID /* lpReserved */
)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hInstance);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
}
return TRUE;
}
// callback for service stop event
void CALLBACK
StopCallback(
PVOID pv,
BOOLEAN TimerOrWaitFired)
{
if (g_pEventHandler)
{
g_pEventHandler->OnStop();
delete g_pEventHandler;
g_pEventHandler = NULL;
}
if (g_pSRService)
{
g_pSRService->SetStatus(SERVICE_STOPPED);
delete g_pSRService;
g_pSRService = NULL;
}
TermAsyncTrace();
}
}