453 lines
11 KiB
C++
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();
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
|