windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/mdadmin/main.cxx

1298 lines
32 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/**********************************************************************/
/** Microsoft Windows NT **/
/** Copyright(c) Microsoft Corp., 1993 **/
/**********************************************************************/
/*
main.cxx
This module contains the main startup code for the IISADMIN Service.
Functions exported by this module:
ServiceEntry
FILE HISTORY:
michth - created
*/
extern "C" {
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
}
#define DEFAULT_TRACE_FLAGS (DEBUG_ERROR)
#include <dbgutil.h>
#include <apiutil.h>
#include <loadmd.hxx>
#include <loadadm.hxx>
#include <ole2.h>
#include <inetsvcs.h>
#include <ntsec.h>
#include <iadmext.h>
#include <string.hxx>
#include <admsub.hxx>
#include <registry.hxx>
#include <imd.h>
#include <irtlmisc.h>
#include "mdwriter.hxx"
#include "iisadminmb.hxx"
#define IISADMIN_SERVICE_NAME TEXT("IISADMIN")
#define QueryServiceName() IISADMIN_SERVICE_NAME
#define NULL_SERVICE_STATUS_HANDLE ((SERVICE_STATUS_HANDLE ) NULL)
#define IISADMIN_SVC_KEY "SYSTEM\\CurrentControlSet\\Services\\IISADMIN"
#define IISADMIN_STARTUP_WAITHINT_VALUE "StartupWaitHintInMilliseconds"
//
// Note: Due to how the system starts up, we can not have another thread that lies to the SCM
// and tells it we are still starting, so instead we are going to have a hard coded startup time
// limit of 3 minutes. However, there is also a registry key that can override this value if we
// ever need to bump the start time limit to a larger value.
//
#define SERVICE_START_WAIT_HINT (180000) // milliseconds = 180 seconds = 3 minutes
//
// For shutdown it is fine to have the thread lie-ing to the SCM because it will not block any
// vital system operations ( like startup )
//
#define SERVICE_STOP_WAIT_HINT (10000) // milliseconds = 10 seconds
#define SERVICE_UPDATE_STATUS (9000) // milliseconds = 9 seconds (must be less that the stop wait hint)
//
// Default timeout for SaveMetabase
//
#define MB_SAVE_TIMEOUT (10000) // milliseconds
BOOL g_fIgnoreSC = FALSE;
BOOL g_fAsExe = FALSE;
SERVICE_STATUS g_svcStatus;
SERVICE_STATUS_HANDLE g_hsvcStatus;
HANDLE g_hShutdownEvent = NULL;
HANDLE g_hComHackThread = NULL;
//
// Debugging stuff
//
#ifndef _NO_TRACING_
#include <initguid.h>
DEFINE_GUID(IisAdminGuid,
0x784d8918, 0xaa8c, 0x11d2, 0x92, 0x5e, 0x00, 0xc0, 0x4f, 0x72, 0xd9, 0x0e);
#else
DECLARE_DEBUG_VARIABLE();
#endif
DECLARE_DEBUG_PRINTS_OBJECT();
typedef struct _THREAD_PARAMS {
HANDLE hInitEvent;
BOOL bInitSuccess;
} THREAD_PARAMS, *PTHREAD_PARAMS;
extern "C" {
BOOL
WINAPI
DLLEntry(
HINSTANCE hDll,
DWORD dwReason,
LPVOID lpvReserved
);
}
DWORD
GetStartupWaitHint();
BOOL
WINAPI
DLLEntry(
HINSTANCE hDll,
DWORD dwReason,
LPVOID lpvReserved
)
/*++
Routine Description:
DLL entrypoint.
Arguments:
hDLL - Instance handle.
Reason - The reason the entrypoint was called.
DLL_PROCESS_ATTACH
DLL_PROCESS_DETACH
DLL_THREAD_ATTACH
DLL_THREAD_DETACH
Reserved - Reserved.
Return Value:
BOOL - TRUE if the action succeeds.
--*/
{
BOOL bReturn = TRUE;
switch ( dwReason )
{
case DLL_PROCESS_ATTACH:
#ifdef _NO_TRACING_
CREATE_DEBUG_PRINT_OBJECT("IISADMIN");
SET_DEBUG_FLAGS(DEBUG_ERROR);
#else
CREATE_DEBUG_PRINT_OBJECT("IISADMIN", IisAdminGuid);
#endif
break;
case DLL_PROCESS_DETACH:
DELETE_DEBUG_PRINT_OBJECT( );
break;
default:
break;
}
return bReturn;
}
DWORD
ReportServiceStatus( VOID)
/*++
Description:
Wraps the call to SetServiceStatus() function.
Prints the service status data if need be
Arguments:
None
Returns:
NO_ERROR if successful. other Win32 error code on failure.
If successfull the new status has been reported to the service
controller.
--*/
{
DWORD err = NO_ERROR;
if (!g_fIgnoreSC) {
IF_DEBUG( DLL_SERVICE_INFO)
{
DBGPRINTF(( DBG_CONTEXT, "dwServiceType = %08lX\n",
g_svcStatus.dwServiceType ));
DBGPRINTF(( DBG_CONTEXT, "dwCurrentState = %08lX\n",
g_svcStatus.dwCurrentState ));
DBGPRINTF(( DBG_CONTEXT, "dwControlsAccepted = %08lX\n",
g_svcStatus.dwControlsAccepted ));
DBGPRINTF(( DBG_CONTEXT, "dwWin32ExitCode = %08lX\n",
g_svcStatus.dwWin32ExitCode ));
DBGPRINTF(( DBG_CONTEXT, "dwServiceSpecificExitCode = %08lX\n",
g_svcStatus.dwServiceSpecificExitCode ));
DBGPRINTF(( DBG_CONTEXT, "dwCheckPoint = %08lX\n",
g_svcStatus.dwCheckPoint ));
DBGPRINTF(( DBG_CONTEXT, "dwWaitHint = %08lX\n",
g_svcStatus.dwWaitHint ));
}
if( !SetServiceStatus( g_hsvcStatus, &g_svcStatus ) ) {
err = GetLastError();
} else {
err = NO_ERROR;
}
}
return err;
} // ReportServiceStatus()
DWORD
UpdateServiceStatus(
IN DWORD dwState,
IN DWORD dwWin32ExitCode,
IN DWORD dwServiceSpecificExitCode,
IN DWORD dwCheckPoint,
IN DWORD dwWaitHint
)
/*++
Description:
Updates the local copy status of service controller status
and reports it to the service controller.
Arguments:
dwState - New service state.
dwWin32ExitCode - Service exit code.
dwCheckPoint - Check point for lengthy state transitions.
dwWaitHint - Wait hint for lengthy state transitions.
Returns:
NO_ERROR on success and returns Win32 error if failure.
On success the status is reported to service controller.
--*/
{
g_svcStatus.dwCurrentState = dwState;
g_svcStatus.dwWin32ExitCode = dwWin32ExitCode;
g_svcStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
g_svcStatus.dwCheckPoint = dwCheckPoint;
g_svcStatus.dwWaitHint = dwWaitHint;
return ReportServiceStatus();
} // UpdateServiceStatus()
VOID
InterrogateService( VOID )
/*++
Description:
This function interrogates with the service status.
Actually, nothing needs to be done here; the
status is always updated after a service control.
We have this function here to provide useful
debug info.
HISTORY:
KeithMo 07-Mar-1993 Created.
MuraliK 15-Nov-1994 Ported to Tcpsvcs.dll
--*/
{
IF_DEBUG( DLL_SERVICE_INFO) {
DBGPRINTF(( DBG_CONTEXT, "Interrogating service status for %s\n",
QueryServiceName())
);
}
return;
} // InterrogateService()
VOID
PauseService( VOID )
/*++
Description:
This function pauses the service. When the service is paused,
no new user sessions are to be accepted, but existing connections
are not effected.
This function must update the SERVICE_STATUS::dwCurrentState
field before returning.
Returns:
None. If successful the service is paused.
--*/
{
IF_DEBUG( DLL_SERVICE_INFO) {
DBGPRINTF(( DBG_CONTEXT, "pausing service %s\n",
QueryServiceName())
);
}
g_svcStatus.dwCurrentState = SERVICE_PAUSED;
return;
} // PauseService()
VOID
ContinueService( VOID )
/*++
Description:
This function restarts ( continues) a paused service. This
will return the service to the running state.
This function must update the g_svcStatus.dwCurrentState
field to running mode before returning.
Returns:
None. If successful then the service is running.
--*/
{
IF_DEBUG( DLL_SERVICE_INFO) {
DBGPRINTF(( DBG_CONTEXT, "continuing service %s\n",
QueryServiceName())
);
}
g_svcStatus.dwCurrentState = SERVICE_RUNNING;
return;
} // ContinueService()
VOID
StopService( VOID )
/*++
Description:
This function performs the shutdown on a service.
This is called during system shutdown.
This function is time constrained. The service controller gives a
maximum of 20 seconds for shutdown for all active services.
Only timely operations should be performed in this function.
Returns:
None. If successful, the service is shutdown.
--*/
{
IF_DEBUG( DLL_SERVICE_INFO) {
DBGPRINTF(( DBG_CONTEXT, "shutting down service %s\n",
QueryServiceName())
);
}
SetEvent( g_hShutdownEvent );
return;
} // StopService()
BOOL
SaveMetabase( VOID )
/*++
Description:
This function tells the metabase to save itself.
Returns:
If TRUE, the metabase has been saved.
--*/
{
HRESULT hRes;
METADATA_HANDLE mdhRoot;
IMDCOM * pMDCom = NULL;
hRes = CoCreateInstance(CLSID_MDCOM,
NULL,
CLSCTX_SERVER,
IID_IMDCOM,
(void**) &pMDCom);
if (SUCCEEDED(hRes))
{
hRes = pMDCom->ComMDInitialize();
if (SUCCEEDED(hRes))
{
//
// Try to lock the tree
//
hRes = pMDCom->ComMDOpenMetaObjectW(METADATA_MASTER_ROOT_HANDLE,
NULL,
METADATA_PERMISSION_READ,
MB_SAVE_TIMEOUT,
&mdhRoot);
//
// If failed, then someone has a write handle open,
// and there might be an inconsistent data state, so don't save.
//
if (SUCCEEDED(hRes))
{
//
// Call metadata com api to save
//
hRes = pMDCom->ComMDSaveData(mdhRoot);
pMDCom->ComMDCloseMetaObject(mdhRoot);
}
pMDCom->ComMDTerminate(TRUE);
}
pMDCom->Release();
}
if ( SUCCEEDED( hRes ))
{
return TRUE;
}
DBGPRINTF(( DBG_CONTEXT, "IISAdmin Service, SaveMetabase() failed, hr=%lu\n", hRes ));
SetLastError( HRESULTTOWIN32( hRes ));
return FALSE;
} // SaveMetabase()
VOID
ServiceControlHandler (
IN DWORD dwOpCode
)
/*++
Description:
This function received control requests from the service controller.
It runs in the context of service controller's dispatcher thread and
performs the requested function.
( Note: Avoid time consuming operations in this function.)
Arguments:
dwOpCode
indicates the requested operation. This should be
one of the SERVICE_CONTROL_* manifests.
Returns:
None. If successful, then the state of the service might be changed.
Note:
if an operation ( especially SERVICE_CONTROL_STOP) is very lengthy,
then this routine should report a STOP_PENDING status and create
a worker thread to do the dirty work. The worker thread would then
perform the necessary work and for reporting timely wait hints and
final SERVICE_STOPPED status.
History:
KeithMo 07-March-1993 Created
MuraliK 15-Nov-1994 Generalized it for all services.
--*/
{
//
// Interpret the opcode.
//
switch( dwOpCode ) {
case SERVICE_CONTROL_INTERROGATE :
InterrogateService();
break;
case SERVICE_CONTROL_STOP :
DBGPRINTF(( DBG_CONTEXT, "IISAdmin Service received stop notice\n"));
StopService();
break;
case SERVICE_CONTROL_PAUSE :
PauseService();
break;
case SERVICE_CONTROL_CONTINUE :
ContinueService();
break;
case SERVICE_CONTROL_SHUTDOWN :
#if 0
//
// On shutdown, service controller doesn't respect ordering so
// this call can block here or force unloading of some stuff that
// a subsequent service needs.
StopService();
#else
//
// Although we aren't cleanly shutting down everything, we want
// to at least make sure the metabase has been saved.
//
DBGPRINTF(( DBG_CONTEXT, "IISAdmin Service saving metabase\n"));
DBG_REQUIRE( SaveMetabase() );
DBGPRINTF(( DBG_CONTEXT, "IISAdmin Service IGNORING shutdown notice\n"));
#endif
break;
default :
DBGPRINTF(( DBG_CONTEXT, "Unrecognized Service Opcode %lu\n",
dwOpCode ));
break;
}
//
// Report the current service status back to the Service
// Controller. The workers called to implement the OpCodes
// should set the g_svcStatus.dwCurrentState field if
// the service status changed.
//
if ((dwOpCode != SERVICE_CONTROL_STOP) && (dwOpCode != SERVICE_CONTROL_SHUTDOWN)) {
// there is a race condition between this thread and the main thread, which
// was kicked off in StopService.
// The dll can get unloaded while this call is in progress.
// Main thread reports status anyways, so don't report it.
ReportServiceStatus();
}
} // ServiceControlHandler()
/*
//
// Must start a thread with CoInitialize(NULL)
// and keep it for the life of the service
// for OLE. The web server also does this,
// so this is only an issue if the iisadmin
// runs without the web server.
//
DWORD
OleHackThread(
PVOID pv
)
{
DWORD dwWaitReturn;
HRESULT hRes;
PTHREAD_PARAMS ptpParams = (PTHREAD_PARAMS)pv;
hRes = CoInitialize(NULL);
ptpParams->bInitSuccess = SUCCEEDED(hRes);
if (!(ptpParams->bInitSuccess)) {
SetLastError(hRes);
IF_DEBUG( DLL_SERVICE_INFO) {
DBGPRINTF(( DBG_CONTEXT, "CoInitialize Failed\n"));
}
}
SetEvent(ptpParams->hInitEvent);
if (ptpParams->bInitSuccess) {
dwWaitReturn = WaitForSingleObject( g_hShutdownEvent,
INFINITE );
CoUninitialize();
}
return 0;
}
*/
//
// While we are in the process of shutting down the timer
// code will call us so we can let the SCM no that we are still
// alive.
//
VOID CALLBACK ShutdownCallback(
PVOID pIgnored,
BOOLEAN IgnoredReason
)
{
UpdateServiceStatus( SERVICE_STOP_PENDING,
0,
0,
g_svcStatus.dwCheckPoint + 1,
SERVICE_STOP_WAIT_HINT );
};
//
// We must init com in a separate thread, to avoid conflicts
// between CoInitializeEx(NULL, MULTI_THREADED)
// and CoInitialize(NULL)
// In the normal case of a service, this is not an issue,
// as IISADMIN is in a different thread from the other services
//
DWORD
ComHackThread(
PVOID pv
)
{
DWORD dwWaitReturn;
BOOL bInitSucceeded;
PTHREAD_PARAMS ptpParams = (PTHREAD_PARAMS)pv;
//
// Bug 80253: Set thread desktop here. The next time CoCreateInstance is
// called, COM will cache the desktop. Note that InitComAdminData
// (below) calls CoCreateInstance, so we are groovy.
//
if ( !SUCCEEDED( SetDesktop(FALSE) ) )
{
return FALSE;
}
bInitSucceeded = ptpParams->bInitSuccess = InitComAdmindata( g_fAsExe );
if (bInitSucceeded) {
//
// Don't bother starting extensions if init failed
//
StartServiceExtensions();
RegisterServiceExtensionCLSIDs();
}
//
// Always set init event to free up calling thread
//
SetEvent(ptpParams->hInitEvent);
if (bInitSucceeded) {
//
// If succeeded, wait until termination event,
// else die immeditately
//
dwWaitReturn = WaitForSingleObject( g_hShutdownEvent,
INFINITE );
StopServiceExtensions();
}
//
// Terminate can always be called.
// It will detect if it's not needed.
//
TerminateComAdmindata();
return 0;
}
BOOL
StartThread(
IN LPTHREAD_START_ROUTINE pStartAddress,
OUT PHANDLE phThread
)
{
BOOL bReturn = FALSE;
HANDLE hThread = NULL;
DWORD dwThreadID;
DWORD dwWaitReturn;
THREAD_PARAMS tpParams;
tpParams.bInitSuccess = FALSE;
tpParams.hInitEvent = IIS_CREATE_EVENT(
"THREAD_PARAMS::hInitEvent",
&tpParams,
TRUE, // fManualReset
FALSE // fInitialState
);
if( tpParams.hInitEvent != NULL ) {
hThread = CreateThread(NULL,
0,
pStartAddress,
(PVOID)&tpParams,
0,
&dwThreadID);
if (hThread != NULL) {
//
// Wait for the init event.
//
dwWaitReturn = WaitForSingleObject( tpParams.hInitEvent,
10000 );
bReturn = tpParams.bInitSuccess;
}
CloseHandle(tpParams.hInitEvent);
}
*phThread = hThread;
return(bReturn);
}
VOID
ServiceEntry(
DWORD cArgs,
LPWSTR pArgs[],
PTCPSVCS_GLOBAL_DATA pGlobalData
)
/*++
Routine:
This is the "real" entrypoint for the service. When
the Service Controller dispatcher is requested to
start a service, it creates a thread that will begin
executing this routine.
Arguments:
cArgs - Number of command line arguments to this service.
pArgs - Pointers to the command line arguments.
Returns:
None. Does not return until service is stopped.
--*/
{
DWORD err = NO_ERROR;
HRESULT hr = S_OK;
HANDLE hAsExe;
HANDLE hShutdownCallbackTimer = NULL;
HANDLE hMDWriterEvent = NULL;
CIISAdminMB Metabase;
RPC_STATUS rpcStatus;
//
// Figure out if we are running with or without the SCM.
//
if ( !(hAsExe = CreateSemaphore( NULL, 1, 1, IIS_AS_EXE_OBJECT_NAME )))
{
g_fIgnoreSC = (GetLastError() == ERROR_INVALID_HANDLE);
}
else
{
CloseHandle( hAsExe );
}
//
// If we are running as a service, tell the SCM what we are doing.
//
//
// First initalize the global status structure. This shouldn't be used
// if we are not running as a service, but since this is legacy code I
// will continue to initialize this here.
//
g_svcStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
g_svcStatus.dwCurrentState = SERVICE_STOPPED;
g_svcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_PAUSE_CONTINUE
| SERVICE_ACCEPT_SHUTDOWN;
g_svcStatus.dwWin32ExitCode = NO_ERROR;
g_svcStatus.dwServiceSpecificExitCode = NO_ERROR;
g_svcStatus.dwCheckPoint = 0;
g_svcStatus.dwWaitHint = 0;
//
// If we are running as a service then we need to register
// with SCM.
//
if (!g_fIgnoreSC) {
g_hsvcStatus = RegisterServiceCtrlHandler(
QueryServiceName(),
ServiceControlHandler
);
//
// Register the Control Handler routine.
//
if( g_hsvcStatus == NULL_SERVICE_STATUS_HANDLE ) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
}
//
// Update the service status. (This will not update
// the service if we are not running as a service, but
// it will adjust the global service status
//
err = UpdateServiceStatus( SERVICE_START_PENDING,
NO_ERROR,
S_OK,
1,
GetStartupWaitHint() );
if( err != NO_ERROR ) {
hr = HRESULT_FROM_WIN32(err);
goto Cleanup;
}
//
// Do the OLE security hack to setup the NT desktop
//
if ( !SUCCEEDED( err = InitDesktopWinsta(FALSE) ) )
{
hr = HRESULT_FROM_WIN32(err);
goto Cleanup;
}
InitializeIISRTL ();
//
// This should happen before any other pieces
// of code attempt to use the metabase. This
// is the sanity check to make sure the metabase
// is is usable form.
//
hr = Metabase.InitializeMetabase();
if ( FAILED ( hr ) )
{
DBGPRINTF(( DBG_CONTEXT,
"Failed to initialize the metabase %x\n",
hr
));
goto Cleanup;
}
//
// Create metabase writer event.
//
hMDWriterEvent = IIS_CREATE_EVENT( "hMDWriterEvent",
&hMDWriterEvent,
TRUE, // fManualReset
FALSE // fInitialState
);
if( hMDWriterEvent == NULL ) {
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Cleanup;
}
//
// Initialize IIS metabase writer.
//
hr = InitializeMDWriter( hMDWriterEvent );
if( FAILED( hr ) )
{
DBGPRINTF(( DBG_CONTEXT,
"Failed to initialize IIS metabase writer %x\n",
hr ));
goto Cleanup;
}
//
// Bug 80253: Set thread desktop here. The next time CoCreateInstance is
// called, COM will cache the desktop. Note that InitComAdminData
// (below) calls CoCreateInstance, so we are groovy.
//
if ( !SUCCEEDED( err = SetDesktop(FALSE) ) )
{
hr = HRESULT_FROM_WIN32(err);
goto Cleanup;
}
if ( !InitComAdmindata(g_fIgnoreSC) )
{
//
// InitComAdmindata does not return error
// information or SetLastError, but we should
// still pass back a bad hresult to show that
// something went wrong.
//
hr = E_UNEXPECTED;
goto Cleanup;
}
//
// Create shutdown event.
//
g_hShutdownEvent = IIS_CREATE_EVENT(
"g_hShutdownEvent",
&g_hShutdownEvent,
TRUE, // fManualReset
FALSE // fInitialState
);
if( g_hShutdownEvent == NULL ) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
StartServiceExtensions();
RegisterServiceExtensionCLSIDs();
//
// Start service RPC listening
//
if( pGlobalData != NULL )
{
rpcStatus = pGlobalData->StartRpcServerListen();
if( rpcStatus != RPC_S_OK )
{
hr = HRESULT_FROM_WIN32( rpcStatus );
goto Cleanup;
}
}
//
// Update the service status.
// it is officially running now.
//
err = UpdateServiceStatus( SERVICE_RUNNING,
NO_ERROR,
S_OK,
0,
0 );
if( err != NO_ERROR ) {
hr = HRESULT_FROM_WIN32(err);
goto Cleanup;
}
//
// Wait for the shutdown event.
//
err = WaitForSingleObject( g_hShutdownEvent,
INFINITE );
if ( err != WAIT_OBJECT_0) {
//
// Error. Unable to wait for single object.
//
}
//
// Stop time. Tell the Service Controller that we're stopping,
// then terminate the various service components.
//
err = UpdateServiceStatus( SERVICE_STOP_PENDING,
NO_ERROR,
S_OK,
1,
SERVICE_STOP_WAIT_HINT );
if( err != NO_ERROR) {
hr = HRESULT_FROM_WIN32(err);
//
// The event has already been logged.
//
}
//
// Now setup a timer callback function that will tell
// the service we are still stopping. Note, the COM
// thread will all ready be stopping too, but it won't
// setup the callback function. Since both this routine
// and the com thread routine must finish before the
// timer is cancelled, it is fine that we just create it
// here.
//
DBG_ASSERT ( hShutdownCallbackTimer == NULL );
if ( !CreateTimerQueueTimer(&hShutdownCallbackTimer,
NULL, // handle to timer queue, use default queue
&ShutdownCallback,
NULL,
SERVICE_UPDATE_STATUS,
SERVICE_UPDATE_STATUS,
WT_EXECUTEINIOTHREAD) )
{
//
// Spew out that there was an error, but don't propogate
// the error, we should still try and shutdown.
//
DBGPRINTF(( DBG_CONTEXT,
"Failed to create the timer queue for shutdown %x\n",
HRESULT_FROM_WIN32(GetLastError())
));
};
Cleanup:
//
// Stop service RPC listening
//
if( pGlobalData != NULL )
{
pGlobalData->StopRpcServerListen();
}
StopServiceExtensions();
TerminateComAdmindata();
//
// Terminate IIS metabase wirter
//
if( hMDWriterEvent != NULL )
{
TerminateMDWriter( hMDWriterEvent );
CloseHandle( hMDWriterEvent );
hMDWriterEvent = NULL;
}
//
// This will only terminate the metabase if
// it was initialized in the first place.
//
Metabase.TerminateMetabase();
TerminateIISRTL();
//
// Now tell the SCM that we have shutdown the service
// and mean it!!
//
if ( g_hsvcStatus != NULL_SERVICE_STATUS_HANDLE )
{
DWORD ExitErr = NO_ERROR;
//
// Setup the error codes as they will be reported back
// to the SCM.
//
if ( FAILED ( hr ) )
{
if ( HRESULT_FACILITY( hr ) == FACILITY_WIN32 )
{
ExitErr = HRESULT_CODE ( hr );
}
else
{
ExitErr = ERROR_SERVICE_SPECIFIC_ERROR;
}
}
//
// If the shutdown timer is still in play we need to
// shut it down before we start the service. Note that
// we should never have both timers running at the same
// time.
//
if ( hShutdownCallbackTimer )
{
//
// Ping the server for more time, so we know we will
// have enough time to shutdown the callback timer before
// we end the service.
//
UpdateServiceStatus( SERVICE_STOP_PENDING,
NO_ERROR,
S_OK,
g_svcStatus.dwCheckPoint + 1,
SERVICE_STOP_WAIT_HINT );
//
// Stop the callback function.
//
if ( !DeleteTimerQueueTimer(NULL,
hShutdownCallbackTimer,
INVALID_HANDLE_VALUE ) )
{
//
// Spew out that there was an error, but don't propogate
// the error, we should still try and shutdown.
//
DBGPRINTF(( DBG_CONTEXT,
"Failed to delete the timer queue for shutdown %x\n",
HRESULT_FROM_WIN32(GetLastError())
));
};
hShutdownCallbackTimer = NULL;
};
//
// Now let SCM know that we are done.
//
UpdateServiceStatus( SERVICE_STOPPED,
ExitErr,
hr,
0,
0 );
};
} // ServiceEntry()
BOOL
ExeEntry(
BOOL fAsExe, // TRUE for exe
BOOL fIgnoreWinstaError, // FALSE for exe
BOOL fInitWam // FALSE for exe
)
{
BOOL bReturn = TRUE;
DWORD err;
g_fAsExe = fAsExe;
//
// Do the OLE security hack to setup the NT desktop
//
if ( !SUCCEEDED( err = InitDesktopWinsta(fIgnoreWinstaError) ))
{
if ( !fIgnoreWinstaError )
{
return FALSE;
}
}
//
// Create shutdown event.
//
g_hShutdownEvent = IIS_CREATE_EVENT(
"g_hShutdownEvent",
&g_hShutdownEvent,
TRUE, // fManualReset
FALSE // fInitialState
);
if( g_hShutdownEvent == NULL ) {
bReturn = FALSE;
}
else {
bReturn = StartThread(ComHackThread, &g_hComHackThread);
if ( bReturn && fInitWam )
{
}
/*
if (bReturn) {
StartThread(OleHackThread);
}
*/
}
return bReturn;
}
VOID
ExeExit()
{
if (g_hShutdownEvent != NULL) {
SetEvent(g_hShutdownEvent);
if ( g_hComHackThread != NULL ) {
DWORD err;
err = WaitForSingleObject(g_hComHackThread,10000);
if ( err != WAIT_OBJECT_0 ) {
IIS_PRINTF((buff,
"Wait for ComHackThread death returns %d[err %d]\n",
err, GetLastError()));
}
CloseHandle(g_hComHackThread);
g_hComHackThread = NULL;
}
CloseHandle(g_hShutdownEvent);
g_hShutdownEvent = NULL;
}
}
/***************************************************************************++
Routine Description:
Checks the registry to see if there is an override set for the
iisadmin startup time limit. If there is we use that value, if there
is not we will use the default 3 minutes.
Arguments:
None.
Return Value:
DWORD - time to tell scm to wait for our startup.
--***************************************************************************/
DWORD
GetStartupWaitHint(
)
{
HKEY hKey;
DWORD dwType;
DWORD cbData;
DWORD dwWaitHint;
DWORD dwActualWaitHint = SERVICE_START_WAIT_HINT;
//
// Read the registry key to see if we are trying to
// override the startup wait hint limit. If there are
// any problems just return the default.
//
if ( !RegOpenKeyEx( HKEY_LOCAL_MACHINE,
IISADMIN_SVC_KEY,
0,
KEY_QUERY_VALUE,
&hKey ) )
{
cbData = sizeof( DWORD );
if( !RegQueryValueEx( hKey,
IISADMIN_STARTUP_WAITHINT_VALUE,
NULL,
&dwType,
( LPBYTE )&dwWaitHint,
&cbData ) )
{
if( dwType == REG_DWORD && dwWaitHint != 0 )
{
dwActualWaitHint = dwWaitHint;
}
}
RegCloseKey( hKey );
}
return dwActualWaitHint;
}
/************************ End Of File ************************/