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.
1153 lines
32 KiB
1153 lines
32 KiB
/**********************************************************************/
|
|
/** 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
|
|
*/
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
|
|
#define DEFAULT_TRACE_FLAGS (DEBUG_ERROR)
|
|
|
|
#include <dbgutil.h>
|
|
#include <apiutil.h>
|
|
#include <loadadm.hxx>
|
|
#include <ole2.h>
|
|
#include <inetsvcs.h>
|
|
#include <ntsec.h>
|
|
#include <iadmext.h>
|
|
#include <string.hxx>
|
|
#include <admsub.hxx>
|
|
#include <imd.h>
|
|
#include <irtlmisc.h>
|
|
#include "mdwriter.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
|
|
|
|
DECLARE_PLATFORM_TYPE();
|
|
|
|
SERVICE_STATUS g_svcStatus;
|
|
SERVICE_STATUS_HANDLE g_hsvcStatus;
|
|
HANDLE g_hShutdownEvent = NULL;
|
|
HANDLE g_hSCMNotifyThread = NULL;
|
|
HANDLE g_hSCMNotifyEvent = NULL;
|
|
|
|
//
|
|
// Debugging stuff
|
|
//
|
|
|
|
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 );
|
|
}
|
|
|
|
DWORD
|
|
GetStartupWaitHint();
|
|
|
|
VOID
|
|
StartUpIndicateClientActivity(VOID);
|
|
|
|
DWORD
|
|
SCMNotifyThread(
|
|
PVOID pv);
|
|
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
DLLEntry(
|
|
HINSTANCE ,
|
|
DWORD dwReason,
|
|
LPVOID )
|
|
/*++
|
|
|
|
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:
|
|
|
|
INITIALIZE_PLATFORM_TYPE();
|
|
|
|
CREATE_DEBUG_PRINT_OBJECT( "iisadmin" );
|
|
LOAD_DEBUG_FLAGS_FROM_REG_STR( "System\\CurrentControlSet\\Services\\iisadmin\\Parameters", 0 );
|
|
|
|
//
|
|
// Create shutdown event.
|
|
//
|
|
|
|
g_hShutdownEvent = IIS_CREATE_EVENT( "g_hShutdownEvent",
|
|
&g_hShutdownEvent,
|
|
TRUE, // fManualReset
|
|
FALSE ); // fInitialState
|
|
|
|
if( g_hShutdownEvent == NULL )
|
|
{
|
|
// Last Error is all ready set because the create event failed.
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
|
|
CloseHandle(g_hShutdownEvent);
|
|
g_hShutdownEvent = NULL;
|
|
|
|
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_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() ));
|
|
}
|
|
} // 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;
|
|
} // 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;
|
|
} // 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() ));
|
|
}
|
|
|
|
// Transition the server status now to stop
|
|
// pending so that net.exe sees that we are
|
|
// planning on listening to them. This has to
|
|
// be done before the set event, because the set
|
|
// event will startup the other thread which will
|
|
// start updating these values as well.
|
|
|
|
// I considered if there was any other way for this
|
|
// event to get set that would cause a race with this call.
|
|
// If the exe exits on it's own while we are being asked to
|
|
// stop, but have not started stopping the service yet, it is
|
|
// possible, but this situation all ready exists even if you
|
|
// are transitioning to paused or continue. So I did not worry
|
|
// about it here.
|
|
UpdateServiceStatus( SERVICE_STOP_PENDING,
|
|
NO_ERROR,
|
|
S_OK,
|
|
1,
|
|
SERVICE_STOP_WAIT_HINT );
|
|
|
|
|
|
SetEvent( g_hShutdownEvent );
|
|
} // 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_INPROC_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" ));
|
|
|
|
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()
|
|
|
|
//
|
|
// 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 ,
|
|
BOOLEAN )
|
|
{
|
|
UpdateServiceStatus( SERVICE_STOP_PENDING,
|
|
0,
|
|
0,
|
|
g_svcStatus.dwCheckPoint + 1,
|
|
SERVICE_STOP_WAIT_HINT );
|
|
|
|
}
|
|
|
|
|
|
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 ,
|
|
LPWSTR [],
|
|
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 hShutdownCallbackTimer = NULL;
|
|
HANDLE hMDWriterThread = NULL;
|
|
RPC_STATUS rpcStatus;
|
|
BOOL bTempFlag = FALSE;
|
|
DWORD dwWait;
|
|
BOOL fComInitialized = FALSE;
|
|
|
|
//
|
|
// 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;
|
|
|
|
|
|
//
|
|
// We are running as a service then we need to register
|
|
// with SCM.
|
|
//
|
|
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;
|
|
}
|
|
|
|
InitializeIISRTL();
|
|
|
|
//
|
|
// Do the OLE security hack to setup the NT desktop
|
|
//
|
|
hr = InitDesktopWinsta();
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = CoInitializeEx( NULL, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY | COINIT_DISABLE_OLE1DDE );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
// This should be the 1st call in the thread.
|
|
DBG_ASSERT( hr != RPC_E_CHANGED_MODE );
|
|
|
|
goto Cleanup;
|
|
}
|
|
fComInitialized = TRUE;
|
|
|
|
// Now initialize Com security
|
|
// When called CoInitializeSecurity will make sure that the Ole Resolver connects to the OleSCM,
|
|
// which will cache the IIS WindowStation\Desktop in the Resolver
|
|
hr = InitComSecurity();
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Revert back to the service winsta\desktop
|
|
hr = RevertToServiceDesktopWinsta();
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Initalize ABO
|
|
// This should happen before any other pieces
|
|
// of code attempt to use the metabase.
|
|
hr = InitComAdmindata();
|
|
if( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Failed to initialize ABO 0x%08x\n",
|
|
hr ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Initialize IIS metabase writer.
|
|
//
|
|
hr = InitializeMDWriter( &hMDWriterThread );
|
|
if( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Failed to initialize IIS metabase writer %x\n",
|
|
hr ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
// if this is a dc then wait for it to be ready before starting extensions
|
|
// the reason for this is that the svcext.dll will try to create a user accounts
|
|
// but it cannot do this on a dc, unless the dc is ready.
|
|
//
|
|
// this is also needed in the case where this is not a DC.
|
|
// when a replica DC gets demoted, the server will restart
|
|
// as a server not a DC and the create user/sync iwam stuff
|
|
// needs to run and can timeout the SCM
|
|
g_hSCMNotifyEvent = IIS_CREATE_EVENT( "g_hSCMNotifyEvent",
|
|
&g_hSCMNotifyEvent,
|
|
TRUE, // fManualReset
|
|
FALSE ); // fInitialState
|
|
|
|
if( g_hSCMNotifyEvent == NULL )
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Cleanup;
|
|
}
|
|
|
|
// start up thread to let scm know we are still alive
|
|
// the StartServiceExtensions() function will take longer than
|
|
// usual on a domain controlller
|
|
bTempFlag = StartThread( SCMNotifyThread, &g_hSCMNotifyThread );
|
|
|
|
hr = StartServiceExtensions();
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
if (bTempFlag)
|
|
{
|
|
if ( g_hSCMNotifyThread != NULL )
|
|
{
|
|
if(g_hSCMNotifyEvent)
|
|
{
|
|
SetEvent(g_hSCMNotifyEvent);
|
|
}
|
|
|
|
dwWait = WaitForSingleObject(g_hSCMNotifyThread,10000);
|
|
if ( dwWait != WAIT_OBJECT_0 )
|
|
{
|
|
IIS_PRINTF((buff, "Wait for SCMNotifyThread death returns %d[err %d]\n",err, GetLastError()));
|
|
}
|
|
CloseHandle(g_hSCMNotifyThread);
|
|
g_hSCMNotifyThread = NULL;
|
|
}
|
|
bTempFlag = FALSE;
|
|
}
|
|
|
|
StartUpIndicateClientActivity();
|
|
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();
|
|
|
|
//
|
|
// Terminate IIS metabase wirter
|
|
//
|
|
if( hMDWriterThread != NULL )
|
|
{
|
|
TerminateMDWriter( hMDWriterThread );
|
|
|
|
CloseHandle( hMDWriterThread );
|
|
hMDWriterThread = NULL;
|
|
}
|
|
|
|
// This will only terminate the metabase if
|
|
// it was initialized in the first place.
|
|
TerminateComAdmindata();
|
|
|
|
TerminateIISRTL();
|
|
|
|
if ( fComInitialized )
|
|
{
|
|
CoUninitialize();
|
|
}
|
|
|
|
// Close the IIS windows station and desktop
|
|
ShutdownDesktopWinsta();
|
|
|
|
//
|
|
// 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()
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
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(VOID)
|
|
{
|
|
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;
|
|
}
|
|
|
|
VOID
|
|
StartUpIndicateClientActivity(VOID)
|
|
{
|
|
if (g_svcStatus.dwCurrentState == SERVICE_START_PENDING)
|
|
{
|
|
UpdateServiceStatus( SERVICE_START_PENDING,
|
|
0,
|
|
0,
|
|
g_svcStatus.dwCheckPoint + 1,
|
|
GetStartupWaitHint());
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
SCMNotifyThread(
|
|
PVOID pv)
|
|
{
|
|
DWORD dwWaitReturn = WAIT_TIMEOUT;
|
|
BOOL bInitSucceeded = FALSE;
|
|
DWORD dwCount = 0;
|
|
PTHREAD_PARAMS ptpParams = (PTHREAD_PARAMS)pv;
|
|
const DWORD dwMaxTime = 1000 * 60 * 3; // max wait is 3 minutes
|
|
const DWORD dwTimeOut = 10000; // check every 10 seconds
|
|
|
|
bInitSucceeded = ptpParams->bInitSuccess = TRUE;
|
|
SetEvent(ptpParams->hInitEvent);
|
|
|
|
do
|
|
{
|
|
// tell SCM we're still alive
|
|
StartUpIndicateClientActivity();
|
|
|
|
// wait 10 seconds
|
|
dwWaitReturn = WaitForSingleObject(g_hSCMNotifyEvent,dwTimeOut);
|
|
dwCount=dwCount+dwTimeOut;
|
|
}
|
|
while ( (dwWaitReturn != WAIT_OBJECT_0) && dwCount < dwMaxTime );
|
|
|
|
return 0;
|
|
}
|
|
|
|
/************************ End Of File ************************/
|