You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1037 lines
26 KiB
1037 lines
26 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
service.cxx
|
|
|
|
Abstract:
|
|
|
|
Process init and service controller interaction
|
|
|
|
Author:
|
|
|
|
Mario Goertzel [MarioGo]
|
|
|
|
Revision History:
|
|
|
|
MarioGo 06-14-95 Cloned RPCSS from the old endpoint mapper.
|
|
jroberts 06-29-00 Cloned BITS from RPCSS
|
|
--*/
|
|
|
|
#include "qmgrlib.h"
|
|
#include "trust.h"
|
|
|
|
#include "service.tmh"
|
|
|
|
//
|
|
// This #define allows BITS to load an unsigned replacement qmgr.dll when the appropriate regkey is set.
|
|
// Without the #define, BITS only loads certs signed by a Microsoft root authority.
|
|
//
|
|
// #define ENABLE_TEST_DLL
|
|
|
|
#define SERVICE_NAME _T("BITS")
|
|
#define DEVICE_PREFIX _T("\\\\.\\")
|
|
|
|
VOID WINAPI ServiceMain(DWORD, LPTSTR*);
|
|
VOID UpdateState(DWORD dwNewState);
|
|
|
|
extern BOOL CatalogDllMain (
|
|
HINSTANCE hInst,
|
|
DWORD dwReason,
|
|
LPVOID lpReserved
|
|
);
|
|
|
|
// Array of service status blocks and pointers to service control
|
|
// functions for each component service.
|
|
|
|
SERVICE_TABLE_ENTRY gaServiceEntryTable[] = {
|
|
{ SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
HINSTANCE g_hInstance;
|
|
|
|
SERVICE_STATUS gServiceStatus;
|
|
SERVICE_STATUS_HANDLE ghServiceHandle;
|
|
|
|
// This event is set when we receive a SERVICE_CONTROL_STOP/SHUTDOWN
|
|
HANDLE g_hServiceStopEvent = NULL;
|
|
|
|
typedef SERVICE_STATUS_HANDLE (*PREGISTER_FUNC)(
|
|
LPCTSTR lpServiceName,
|
|
LPHANDLER_FUNCTION_EX lpHandlerProc,
|
|
LPVOID lpContext );
|
|
|
|
typedef VOID (*PSERVICE_MAIN_FUNC)(
|
|
DWORD argc,
|
|
LPTSTR *lpszArgv,
|
|
PREGISTER_FUNC RegisterFunc );
|
|
|
|
extern "C"
|
|
VOID
|
|
BITSServiceMain(
|
|
DWORD argc,
|
|
LPTSTR *argv,
|
|
PREGISTER_FUNC lpRegisterFunc
|
|
);
|
|
|
|
|
|
BOOL
|
|
GetModuleVersion64(
|
|
HMODULE hDll,
|
|
ULONG64 * pVer
|
|
);
|
|
|
|
BOOL
|
|
GetFileVersion64(
|
|
LPTSTR szFullPath,
|
|
ULONG64 * pVer
|
|
);
|
|
|
|
LPHANDLER_FUNCTION_EX g_RealHandler = NULL;
|
|
HINSTANCE g_RealLibrary = NULL;
|
|
LONG g_RealLibraryRefs = 0;
|
|
|
|
ULONG
|
|
ServiceHandlerThunk(
|
|
DWORD dwCode,
|
|
DWORD dwEventType,
|
|
PVOID EventData,
|
|
PVOID pData )
|
|
{
|
|
InterlockedIncrement( &g_RealLibraryRefs );
|
|
|
|
ULONG Result =
|
|
g_RealHandler( dwCode, dwEventType, EventData, pData );
|
|
|
|
if (!InterlockedDecrement( &g_RealLibraryRefs ) )
|
|
{
|
|
FreeLibrary( g_RealLibrary );
|
|
g_RealLibrary = NULL;
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
SERVICE_STATUS_HANDLE
|
|
RegisterServiceHandlerThunk(
|
|
LPCTSTR lpServiceName,
|
|
LPHANDLER_FUNCTION_EX lpHandlerProc,
|
|
LPVOID lpContext )
|
|
{
|
|
g_RealHandler = lpHandlerProc;
|
|
|
|
return
|
|
RegisterServiceCtrlHandlerEx( lpServiceName,
|
|
ServiceHandlerThunk,
|
|
lpContext
|
|
);
|
|
}
|
|
|
|
bool
|
|
JumpToRealDLL(
|
|
DWORD argc,
|
|
LPTSTR *argv )
|
|
{
|
|
#define MAX_DLLNAME (MAX_PATH+1)
|
|
|
|
HKEY BitsKey = NULL;
|
|
bool bAllowTestBinaries = false;
|
|
|
|
LONG Result =
|
|
RegOpenKey( HKEY_LOCAL_MACHINE, C_QMGR_REG_KEY, &BitsKey );
|
|
|
|
if ( Result )
|
|
goto noload;
|
|
|
|
//
|
|
// Read the key naming an override DLL.
|
|
//
|
|
static TCHAR DLLName[MAX_DLLNAME];
|
|
DWORD Type;
|
|
DWORD NameSize = sizeof(DLLName);
|
|
|
|
Result = RegQueryValueEx(
|
|
BitsKey,
|
|
C_QMGR_SERVICEDLL,
|
|
NULL,
|
|
&Type,
|
|
(LPBYTE)DLLName,
|
|
&NameSize );
|
|
|
|
if ( Result ||
|
|
(( Type != REG_SZ ) && (Type != REG_EXPAND_SZ)) )
|
|
{
|
|
goto noload;
|
|
}
|
|
|
|
if (Type == REG_EXPAND_SZ)
|
|
{
|
|
static TCHAR ExpandedDLLName[MAX_DLLNAME];
|
|
|
|
DWORD size = ExpandEnvironmentStrings( DLLName, ExpandedDLLName, MAX_DLLNAME );
|
|
|
|
if (size == 0)
|
|
{
|
|
// out of resources
|
|
return true;
|
|
}
|
|
|
|
HRESULT hr;
|
|
hr = StringCchCopy( DLLName, RTL_NUMBER_OF(DLLName), ExpandedDLLName );
|
|
if (FAILED(hr))
|
|
{
|
|
// too long; must be badly formatted. Ignore it.
|
|
goto noload;
|
|
}
|
|
}
|
|
|
|
#ifdef ENABLE_TEST_DLL
|
|
|
|
//
|
|
// Read the key that controls whether to allow test-signed binaries.
|
|
//
|
|
{
|
|
DWORD b;
|
|
DWORD Size = sizeof(b);
|
|
DWORD Type;
|
|
|
|
Result = RegQueryValueEx(
|
|
BitsKey,
|
|
C_QMGR_ALLOW_TEST_DLL,
|
|
NULL,
|
|
&Type,
|
|
(LPBYTE)&b,
|
|
&Size );
|
|
|
|
if ( (Result ==0) && ( Type == REG_DWORD ) && (b == 1))
|
|
{
|
|
bAllowTestBinaries = true;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
RegCloseKey( BitsKey );
|
|
BitsKey = NULL;
|
|
|
|
//
|
|
// At this point, we know that the registry specifies an alternate DLL.
|
|
// See whether it has a later version than the current one.
|
|
//
|
|
ULONG64 AlternateDllVersion = 0;
|
|
ULONG64 MyDllVersion = 0;
|
|
|
|
if (!QMgrFileExists( DLLName ))
|
|
{
|
|
goto noload;
|
|
}
|
|
|
|
if (!GetFileVersion64( DLLName, &AlternateDllVersion ))
|
|
{
|
|
// can't ascertain the version. Don't start the service at all.
|
|
return true;
|
|
}
|
|
|
|
if (!GetModuleVersion64( g_hInstance, &MyDllVersion ))
|
|
{
|
|
// can't ascertain the version. Don't start the service at all.
|
|
return true;
|
|
}
|
|
|
|
if (MyDllVersion >= AlternateDllVersion)
|
|
{
|
|
goto noload;
|
|
}
|
|
|
|
if (!bAllowTestBinaries)
|
|
{
|
|
//
|
|
// Verify that the file is signed with a Microsoft certificate, using the default cert list and checking the CRL.
|
|
//
|
|
if (FAILED(VerifyFileTrust( DLLName, NULL, TRUE )))
|
|
{
|
|
goto noload;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The file appears valid; load it and call BitsServiceMain.
|
|
//
|
|
g_RealLibrary = LoadLibrary( DLLName );
|
|
|
|
if ( !g_RealLibrary )
|
|
goto noload;
|
|
|
|
PSERVICE_MAIN_FUNC ServiceMainFunc =
|
|
(PSERVICE_MAIN_FUNC)GetProcAddress( g_RealLibrary, "BITSServiceMain" );
|
|
|
|
if ( !ServiceMainFunc )
|
|
goto noload;
|
|
|
|
g_RealLibraryRefs = 1;
|
|
|
|
// Ok to call into real library now.
|
|
|
|
( *ServiceMainFunc ) ( argc, argv, RegisterServiceHandlerThunk );
|
|
|
|
if (!InterlockedDecrement( &g_RealLibraryRefs ) )
|
|
{
|
|
FreeLibrary( g_RealLibrary );
|
|
g_RealLibrary = NULL;
|
|
}
|
|
|
|
return true;
|
|
|
|
noload:
|
|
if ( BitsKey )
|
|
RegCloseKey( BitsKey );
|
|
if ( g_RealLibrary )
|
|
FreeLibrary( g_RealLibrary );
|
|
return false;
|
|
}
|
|
|
|
VOID WINAPI
|
|
ServiceMain(
|
|
DWORD argc,
|
|
LPTSTR *argv
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Callback by the service controller when starting this service.
|
|
|
|
Arguments:
|
|
|
|
argc - number of arguments, usually 1
|
|
|
|
argv - argv[0] is the name of the service.
|
|
argv[>0] are arguments passed to the service.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
|
|
volatile static LONG ThreadRunning = 0;
|
|
|
|
if ( InterlockedCompareExchange( &ThreadRunning, 1, 0 ) == 1 )
|
|
{
|
|
|
|
// A thread is already running ServiceMain, just exit.
|
|
// The service controller has a bug where it can create multiple
|
|
// threads to call ServiceMain in high stress conditions.
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
if (!JumpToRealDLL( argc, argv) )
|
|
{
|
|
|
|
BITSServiceMain(
|
|
argc,
|
|
argv,
|
|
RegisterServiceCtrlHandlerEx );
|
|
|
|
}
|
|
|
|
ThreadRunning = 0;
|
|
|
|
}
|
|
|
|
DWORD g_LastServiceControl;
|
|
|
|
|
|
ULONG WINAPI
|
|
BITSServiceHandler(
|
|
DWORD dwCode,
|
|
DWORD dwEventType,
|
|
PVOID EventData,
|
|
PVOID pData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Lowest level callback from the service controller to
|
|
cause this service to change our status. (stop, start, pause, etc).
|
|
|
|
Arguments:
|
|
|
|
opCode - One of the service "Controls" value.
|
|
SERVICE_CONTROL_{STOP, PAUSE, CONTINUE, INTERROGATE, SHUTDOWN}.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
switch(dwCode)
|
|
{
|
|
case SERVICE_CONTROL_STOP:
|
|
{
|
|
LogService( "STOP request" );
|
|
|
|
//
|
|
// only relevant in running state; damaging if we are stopping
|
|
// and g_hServiceStopEvent is deleted.
|
|
//
|
|
if (gServiceStatus.dwCurrentState == SERVICE_RUNNING)
|
|
{
|
|
g_LastServiceControl = dwCode;
|
|
|
|
UpdateState( SERVICE_STOP_PENDING );
|
|
SetEvent( g_hServiceStopEvent );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SERVICE_CONTROL_INTERROGATE:
|
|
// Service controller wants us to call SetServiceStatus.
|
|
|
|
LogService( "INTERROGATE request" );
|
|
UpdateState(gServiceStatus.dwCurrentState);
|
|
break ;
|
|
|
|
case SERVICE_CONTROL_SHUTDOWN:
|
|
// The machine is shutting down. We'll be killed once we return.
|
|
|
|
LogService( "SHUTDOWN request" );
|
|
|
|
g_LastServiceControl = dwCode;
|
|
|
|
UpdateState( SERVICE_STOP_PENDING );
|
|
SetEvent( g_hServiceStopEvent );
|
|
|
|
while (gServiceStatus.dwCurrentState == SERVICE_STOP_PENDING)
|
|
{
|
|
LogService( "service pending; sleeping..." );
|
|
Sleep(100);
|
|
}
|
|
break;
|
|
|
|
case SERVICE_CONTROL_DEVICEEVENT:
|
|
{
|
|
if (gServiceStatus.dwCurrentState == SERVICE_STOP_PENDING ||
|
|
gServiceStatus.dwCurrentState == SERVICE_STOPPED)
|
|
{
|
|
LogService("ignoring device event due to service shutdown" );
|
|
break;
|
|
}
|
|
|
|
return DeviceEventCallback( dwEventType, EventData );
|
|
}
|
|
|
|
case SERVICE_CONTROL_SESSIONCHANGE:
|
|
{
|
|
WTSSESSION_NOTIFICATION* pswtsi = (WTSSESSION_NOTIFICATION*) EventData;
|
|
DWORD dwSessionId = pswtsi->dwSessionId;
|
|
|
|
if (gServiceStatus.dwCurrentState == SERVICE_STOP_PENDING ||
|
|
gServiceStatus.dwCurrentState == SERVICE_STOPPED)
|
|
{
|
|
LogService("ignoring session change for session %d due to service shutdown", dwSessionId );
|
|
break;
|
|
}
|
|
|
|
switch (dwEventType)
|
|
{
|
|
case WTS_SESSION_LOGON:
|
|
{
|
|
LogService("logon at session %d", dwSessionId);
|
|
|
|
SessionLogonCallback( dwSessionId );
|
|
break;
|
|
}
|
|
|
|
case WTS_SESSION_LOGOFF:
|
|
{
|
|
LogService("logoff at session %d", dwSessionId);
|
|
|
|
SessionLogoffCallback( dwSessionId );
|
|
break;
|
|
}
|
|
|
|
default: //Is there a default?
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
LogError( "%!ts!: Unexpected service control message %d.\n", SERVICE_NAME, dwCode);
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
bool
|
|
IsServiceShuttingDown()
|
|
{
|
|
return (gServiceStatus.dwCurrentState == SERVICE_STOP_PENDING);
|
|
}
|
|
|
|
|
|
VOID
|
|
UpdateState(
|
|
DWORD dwNewState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Updates this services state with the service controller.
|
|
|
|
Arguments:
|
|
|
|
dwNewState - The next start for this service. One of
|
|
SERVICE_START_PENDING
|
|
SERVICE_RUNNING
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
|
|
LogService("state change: old %d new %d", gServiceStatus.dwCurrentState, dwNewState );
|
|
|
|
switch (dwNewState)
|
|
{
|
|
|
|
case SERVICE_RUNNING:
|
|
case SERVICE_STOPPED:
|
|
gServiceStatus.dwCheckPoint = 0;
|
|
gServiceStatus.dwWaitHint = 0;
|
|
break;
|
|
|
|
case SERVICE_START_PENDING:
|
|
case SERVICE_STOP_PENDING:
|
|
++gServiceStatus.dwCheckPoint;
|
|
gServiceStatus.dwWaitHint = 30000L;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
status = ERROR_INVALID_SERVICE_CONTROL;
|
|
break;
|
|
}
|
|
|
|
if (status == ERROR_SUCCESS)
|
|
{
|
|
gServiceStatus.dwCurrentState = dwNewState;
|
|
if (!SetServiceStatus(ghServiceHandle, &gServiceStatus))
|
|
{
|
|
status = GetLastError();
|
|
}
|
|
}
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
LogError( "%!ts!: Failed to update service state: %d\n", SERVICE_NAME, status);
|
|
}
|
|
|
|
// We could return a status but how would we recover? Ignore it, the
|
|
// worst thing is that services will kill us and there's nothing
|
|
// we can about it if this call fails.
|
|
|
|
LogInfo( "Finished updating service state to %u", dwNewState );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
extern "C"
|
|
VOID
|
|
BITSServiceMain(
|
|
DWORD argc,
|
|
LPTSTR *argv,
|
|
PREGISTER_FUNC lpRegisterFunc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Callback by the service controller when starting this service.
|
|
|
|
Arguments:
|
|
|
|
argc - number of arguments, usually 1
|
|
|
|
argv - argv[0] is the name of the service.
|
|
argv[>0] are arguments passed to the service.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
BOOL f = FALSE;
|
|
HRESULT hr = S_OK;
|
|
|
|
bool bGlobals = false;
|
|
bool bQmgr = false;
|
|
|
|
try
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
|
|
FILETIME ftStartTime;
|
|
GetSystemTimeAsFileTime( &ftStartTime );
|
|
|
|
Log_Init();
|
|
Log_StartLogger();
|
|
|
|
LogInfo("Service started at %!TIMESTAMP!", FILETIMEToUINT64( ftStartTime ) );
|
|
|
|
//
|
|
// Set up for service notifications.
|
|
//
|
|
gServiceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
|
|
gServiceStatus.dwCurrentState = SERVICE_START_PENDING;
|
|
gServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
|
|
|
|
// The SESSIONCHANGE notification is only available on WindowsXP
|
|
// and the service controller will become confused if this is given on windows 2000
|
|
|
|
if ( WINDOWSXP_PLATFORM == g_PlatformVersion )
|
|
gServiceStatus.dwControlsAccepted |= SERVICE_ACCEPT_SESSIONCHANGE;
|
|
|
|
gServiceStatus.dwWin32ExitCode = 0;
|
|
gServiceStatus.dwServiceSpecificExitCode = 0;
|
|
gServiceStatus.dwCheckPoint = 0;
|
|
|
|
gServiceStatus.dwWaitHint = 30000L;
|
|
|
|
ghServiceHandle = (*lpRegisterFunc)( SERVICE_NAME,
|
|
BITSServiceHandler,
|
|
0
|
|
);
|
|
if (0 == ghServiceHandle)
|
|
{
|
|
status = GetLastError();
|
|
ASSERT(status != ERROR_SUCCESS);
|
|
|
|
LogError( "RegisterServiceCtrlHandlerEx failed %!winerr!", status);
|
|
|
|
THROW_HRESULT( HRESULT_FROM_WIN32( status ));
|
|
}
|
|
|
|
UpdateState(SERVICE_START_PENDING);
|
|
|
|
// Set up an event that will be signaled when the service is
|
|
// stopped or shutdown.
|
|
|
|
g_hServiceStopEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
|
|
if( NULL == g_hServiceStopEvent )
|
|
{
|
|
status = GetLastError();
|
|
LogError( "CreateEvent failed %!winerr!", status );
|
|
|
|
THROW_HRESULT( HRESULT_FROM_WIN32( status ));
|
|
}
|
|
|
|
if ( WINDOWS2000_PLATFORM == g_PlatformVersion )
|
|
{
|
|
|
|
HRESULT Hr;
|
|
bool CoInitCalled = false;
|
|
|
|
Hr = CoInitializeEx( NULL, COINIT_MULTITHREADED );
|
|
|
|
if ( FAILED( Hr ) &&
|
|
( Hr != RPC_E_CHANGED_MODE ) )
|
|
THROW_HRESULT( Hr );
|
|
|
|
CoInitCalled = true;
|
|
|
|
Hr =
|
|
CoInitializeSecurity(
|
|
NULL, // pSecDesc
|
|
-1, // cAuthSvc
|
|
NULL, // asAuthSvc
|
|
NULL, // pReserved
|
|
RPC_C_AUTHN_LEVEL_PKT, // dwAuthnLevel
|
|
RPC_C_IMP_LEVEL_IDENTIFY, // dwImpLevel
|
|
NULL, // pReserved2
|
|
EOAC_NO_CUSTOM_MARSHAL | // dwCapabilities
|
|
EOAC_DISABLE_AAA |
|
|
EOAC_STATIC_CLOAKING,
|
|
NULL ); // pReserved3
|
|
|
|
if ( FAILED( Hr ) &&
|
|
( Hr != RPC_E_TOO_LATE ) )
|
|
{
|
|
LogError( "Unable to initialize security on Win2k, error %!winerr!", Hr );
|
|
|
|
if ( CoInitCalled )
|
|
CoUninitialize();
|
|
|
|
THROW_HRESULT( Hr );
|
|
}
|
|
|
|
}
|
|
|
|
LogInfo( "Initializing globalinfo\n" );
|
|
THROW_HRESULT( GlobalInfo::Init() );
|
|
|
|
bGlobals = true;
|
|
|
|
LogInfo( "Initializing qmgr\n" );
|
|
THROW_HRESULT( InitQmgr() );
|
|
|
|
bQmgr = true;
|
|
|
|
LogInfo( "Setting service to running.");
|
|
|
|
//
|
|
// Allow service controller to resume other duties.
|
|
//
|
|
UpdateState(SERVICE_RUNNING);
|
|
|
|
//
|
|
// wait for the stop signal.
|
|
//
|
|
|
|
if( WAIT_OBJECT_0 != WaitForSingleObject( g_hServiceStopEvent, INFINITE ))
|
|
{
|
|
status = GetLastError();
|
|
LogError( "ServiceMain failed waiting for stop signal %!winerr!", status);
|
|
|
|
hr = HRESULT_FROM_WIN32( status );
|
|
}
|
|
|
|
hr = S_OK;
|
|
}
|
|
catch ( ComError exception )
|
|
{
|
|
hr = exception.Error();
|
|
}
|
|
|
|
if (bQmgr)
|
|
{
|
|
HRESULT hr2 = UninitQmgr();
|
|
if (FAILED(hr2))
|
|
{
|
|
LogError( "uninit Qmgr failed %!winerr!", hr2);
|
|
}
|
|
}
|
|
|
|
if (bGlobals)
|
|
{
|
|
HRESULT hr2 = GlobalInfo::Uninit();
|
|
if (FAILED(hr2))
|
|
{
|
|
LogError( "uninit GlobalInfo failed %!winerr!", hr2);
|
|
}
|
|
}
|
|
|
|
if (g_hServiceStopEvent)
|
|
{
|
|
CloseHandle( g_hServiceStopEvent );
|
|
g_hServiceStopEvent = NULL;
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
gServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
|
|
gServiceStatus.dwServiceSpecificExitCode = hr;
|
|
}
|
|
|
|
LogService( "ServiceMain returning, hr = %x", hr );
|
|
Log_Close();
|
|
|
|
UpdateState(SERVICE_STOPPED);
|
|
|
|
|
|
}
|
|
|
|
enum
|
|
{
|
|
STARTUP_UNKNOWN,
|
|
STARTUP_DEMAND,
|
|
STARTUP_AUTO
|
|
}
|
|
g_ServiceStartupState = STARTUP_UNKNOWN;
|
|
|
|
HRESULT
|
|
SetServiceStartup( bool bAutoStart )
|
|
{
|
|
|
|
LogService( "Setting startup to %s", bAutoStart ? ("Auto") : ("Demand") );
|
|
HRESULT Hr = S_OK;
|
|
|
|
//
|
|
// Changing the service state is expensive, so avoid it if possible.
|
|
//
|
|
// No need to monitor external changes to the startup state, though:
|
|
//
|
|
// If the admin changes our state from AUTO to DEMAND or DISABLED, then
|
|
// the consequent lack of progress is his fault, and admins should know that.
|
|
//
|
|
// If the admin changes our state from DEMAND to AUTO, then we will
|
|
// start more often but the result is otherwise harmless.
|
|
//
|
|
if ((g_ServiceStartupState == STARTUP_DEMAND && bAutoStart == FALSE) ||
|
|
(g_ServiceStartupState == STARTUP_AUTO && bAutoStart == TRUE))
|
|
{
|
|
LogService( "startup state is already correct" );
|
|
return S_OK;
|
|
}
|
|
|
|
if (gServiceStatus.dwCurrentState != SERVICE_RUNNING)
|
|
{
|
|
LogService("can't change startup state in state %d", gServiceStatus.dwCurrentState);
|
|
return S_OK;
|
|
}
|
|
|
|
SC_HANDLE hServiceManager = NULL;
|
|
SC_HANDLE hService = NULL;
|
|
try
|
|
{
|
|
try
|
|
{
|
|
hServiceManager =
|
|
OpenSCManager( NULL,
|
|
NULL,
|
|
SC_MANAGER_ALL_ACCESS );
|
|
|
|
if ( !hServiceManager ) throw (DWORD)GetLastError();
|
|
|
|
hService =
|
|
OpenService( hServiceManager,
|
|
SERVICE_NAME,
|
|
SERVICE_CHANGE_CONFIG );
|
|
if ( !hService ) throw (DWORD)GetLastError();
|
|
|
|
BOOL bResult =
|
|
ChangeServiceConfig( hService, // service handle
|
|
SERVICE_NO_CHANGE, // dwServiceType
|
|
bAutoStart ? SERVICE_AUTO_START : SERVICE_DEMAND_START, // dwStartType
|
|
SERVICE_NO_CHANGE, // dwErrorControl
|
|
NULL, // lpBinaryPathName
|
|
NULL, // lpLoadOrderGroup
|
|
NULL, // lpdwTagId
|
|
NULL, // lpDependencies
|
|
NULL, // lpServiceStartName
|
|
NULL, // lpPassword
|
|
NULL); // lpDisplayName
|
|
|
|
if ( !bResult ) throw (DWORD)GetLastError();
|
|
|
|
if (bAutoStart)
|
|
{
|
|
g_ServiceStartupState = STARTUP_AUTO;
|
|
}
|
|
else
|
|
{
|
|
g_ServiceStartupState = STARTUP_DEMAND;
|
|
}
|
|
}
|
|
catch( DWORD dwException )
|
|
{
|
|
throw (HRESULT)HRESULT_FROM_WIN32( dwException );
|
|
}
|
|
|
|
}
|
|
catch (HRESULT HrException)
|
|
{
|
|
Hr = HrException;
|
|
LogError( "An error occurred setting service startup, %!winerr!", Hr );
|
|
}
|
|
|
|
if ( hService )
|
|
{
|
|
CloseServiceHandle( hService );
|
|
}
|
|
|
|
if ( hServiceManager )
|
|
{
|
|
CloseServiceHandle( hServiceManager );
|
|
}
|
|
|
|
LogService( " HR: %!winerr!", Hr );
|
|
return Hr;
|
|
}
|
|
|
|
int InitializeBitsAllocator();
|
|
|
|
extern "C"
|
|
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
|
|
{
|
|
|
|
if (dwReason == DLL_PROCESS_ATTACH)
|
|
{
|
|
g_hInstance = hInstance;
|
|
DisableThreadLibraryCalls(hInstance);
|
|
|
|
if (!InitCompilerLibrary())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !DetectProductVersion() )
|
|
{
|
|
UninitCompilerLibrary();
|
|
return FALSE;
|
|
}
|
|
|
|
if (0 != InitializeBitsAllocator())
|
|
{
|
|
UninitCompilerLibrary();
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if ( dwReason == DLL_PROCESS_DETACH )
|
|
{
|
|
UninitCompilerLibrary();
|
|
}
|
|
|
|
return TRUE; // ok
|
|
}
|
|
|
|
//
|
|
// This ungainly typedef seems to have no global definition. There are several identical
|
|
// definitions in the Windows NT sources, each of which has that bizarre bit-stripping
|
|
// on szKey. I got mine from \nt\base\ntsetup\srvpack\update\splib\common.h.
|
|
//
|
|
typedef struct tagVERHEAD {
|
|
WORD wTotLen;
|
|
WORD wValLen;
|
|
WORD wType; /* always 0 */
|
|
WCHAR szKey[(sizeof("VS_VERSION_INFO")+3)&~03];
|
|
VS_FIXEDFILEINFO vsf;
|
|
} VERHEAD ;
|
|
|
|
/*
|
|
** Purpose:
|
|
** Gets the file version values from the given file and sets the
|
|
** given ULONG64 variable.
|
|
** Arguments:
|
|
** szFullPath: a zero terminated character string containing the fully
|
|
** qualified path (including disk drive) to the file.
|
|
** Returns:
|
|
** fTrue if file and file version resource found and retrieved,
|
|
** fFalse if not.
|
|
+++
|
|
** Implementation:
|
|
**************************************************************************/
|
|
BOOL
|
|
GetFileVersion64(
|
|
LPTSTR szFullPath,
|
|
ULONG64 * pVer
|
|
)
|
|
{
|
|
BOOL fRet = false;
|
|
DWORD dwHandle;
|
|
DWORD InfoSize;
|
|
|
|
try
|
|
{
|
|
//
|
|
// Get the file version info size
|
|
//
|
|
|
|
if ((InfoSize = GetFileVersionInfoSize( szFullPath, &dwHandle)) == 0)
|
|
{
|
|
return (fRet);
|
|
}
|
|
|
|
//
|
|
// Allocate enough size to hold version info
|
|
//
|
|
auto_ptr<TCHAR> lpData ( LPTSTR(new byte[ InfoSize ]));
|
|
|
|
//
|
|
// Get the version info
|
|
//
|
|
fRet = GetFileVersionInfo( szFullPath, dwHandle, InfoSize, lpData.get());
|
|
|
|
if (fRet)
|
|
{
|
|
UINT dwLen;
|
|
VS_FIXEDFILEINFO *pvsfi;
|
|
|
|
fRet = VerQueryValue(
|
|
lpData.get(),
|
|
L"\\",
|
|
(LPVOID *)&pvsfi,
|
|
&dwLen
|
|
);
|
|
|
|
//
|
|
// Convert two DWORDs into a 64-bit integer.
|
|
//
|
|
if (fRet)
|
|
{
|
|
*pVer = ( ULONG64(pvsfi->dwFileVersionMS) << 32) | (pvsfi->dwFileVersionLS);
|
|
}
|
|
}
|
|
|
|
return (fRet);
|
|
}
|
|
catch ( ComError err )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
GetModuleVersion64(
|
|
HMODULE hDll,
|
|
ULONG64 * pVer
|
|
)
|
|
{
|
|
DWORD* pdwTranslation;
|
|
VS_FIXEDFILEINFO* pFileInfo;
|
|
UINT uiSize;
|
|
|
|
HRSRC hrsrcVersion = FindResource(
|
|
hDll,
|
|
MAKEINTRESOURCE(VS_VERSION_INFO),
|
|
RT_VERSION);
|
|
|
|
if (!hrsrcVersion) return false;
|
|
|
|
HGLOBAL hglobalVersion = LoadResource(hDll, hrsrcVersion);
|
|
if (!hglobalVersion) return false;
|
|
|
|
VERHEAD * pVerHead = (VERHEAD *) LockResource(hglobalVersion);
|
|
if (!pVerHead) return false;
|
|
|
|
// I stole this code from \nt\com\complus\src\shared\util\svcerr.cpp,
|
|
// and the comment is theirs:
|
|
//
|
|
// VerQueryValue will write to the memory, for some reason.
|
|
// Therefore we must make a writable copy of the version
|
|
// resource info before calling that API.
|
|
auto_ptr<char> pvVersionInfo ( new char[pVerHead->wTotLen + pVerHead->wTotLen/2] );
|
|
|
|
memcpy(pvVersionInfo.get(), pVerHead, pVerHead->wTotLen); // SEC: REVIEWED 2002-03-28
|
|
|
|
// Retrieve file version info
|
|
BOOL fRet = VerQueryValue( pvVersionInfo.get(),
|
|
L"\\",
|
|
(void**)&pFileInfo,
|
|
&uiSize);
|
|
if (fRet)
|
|
{
|
|
*pVer = (ULONG64(pFileInfo->dwFileVersionMS) << 32) | (pFileInfo->dwFileVersionLS);
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|