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.
590 lines
12 KiB
590 lines
12 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
wpipm.cxx
|
|
|
|
Abstract:
|
|
|
|
Contains the WPIPM class that handles communication with
|
|
the admin service. WPIPM responds to pings, and tells
|
|
the process when to shut down.
|
|
|
|
Author:
|
|
|
|
Michael Courage (MCourage) 22-Feb-1999
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include <precomp.hxx>
|
|
#include "dbgutil.h"
|
|
#include "wpipm.hxx"
|
|
|
|
#include "RwpFunctions.hxx"
|
|
extern PFN_ULATQ_COLLECT_PERF_COUNTERS g_pfnCollectCounters;
|
|
|
|
/**
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* Initializes WPIPM.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* pWpContext - pointer to the wp context (so we can tell it to shutdown)
|
|
*
|
|
* Return Value:
|
|
*
|
|
* HRESULT
|
|
*/
|
|
HRESULT
|
|
WP_IPM::Initialize(
|
|
WP_CONTEXT * pWpContext
|
|
)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
m_pWpContext = pWpContext;
|
|
|
|
DWORD dwId = GetCurrentProcessId();
|
|
|
|
//
|
|
// create pipe
|
|
//
|
|
hr = IPM_MESSAGE_PIPE::CreateIpmMessagePipe(this,
|
|
pWpContext->QueryConfig()->QueryNamedPipeId(),
|
|
FALSE, // not server side
|
|
NULL, // security descriptor
|
|
&m_pPipe);
|
|
if (FAILED(hr))
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Send the real pid over the pipe
|
|
//
|
|
if(!RWP_IPM_BadParamTest(RWP_IPM_OP_GETPID, &hr, m_pPipe))
|
|
{
|
|
hr = m_pPipe->WriteMessage(IPM_OP_GETPID,
|
|
sizeof(dwId),
|
|
&dwId);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
hr = S_OK;
|
|
exit:
|
|
if (FAILED(hr))
|
|
{
|
|
Terminate();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* Terminates WPIPM.
|
|
*
|
|
* If the message pipe is open this function will disconnect it
|
|
* and wait for the pipe's disconnection callback.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* None.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* HRESULT
|
|
*/
|
|
HRESULT
|
|
WP_IPM::Terminate(
|
|
VOID
|
|
)
|
|
{
|
|
if (m_pPipe)
|
|
{
|
|
m_pPipe->DestroyIpmMessagePipe();
|
|
|
|
// pipe deletes itself
|
|
m_pPipe = NULL;
|
|
}
|
|
|
|
m_pWpContext = NULL;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* This is a callback from the message pipe that means
|
|
* the pipe has received a message.
|
|
*
|
|
* We decode the message and respond appropriately.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* pPipeMessage - the message that we received
|
|
*
|
|
* Return Value:
|
|
*
|
|
* HRESULT
|
|
*
|
|
*/
|
|
VOID
|
|
WP_IPM::AcceptMessage(
|
|
IN const IPM_MESSAGE * pPipeMessage
|
|
)
|
|
{
|
|
HRESULT hr = NO_ERROR;
|
|
BOOL fRet = FALSE;
|
|
|
|
switch (pPipeMessage->GetOpcode())
|
|
{
|
|
case IPM_OP_PING:
|
|
//
|
|
// Pings must go through the same mechanism that requests go through
|
|
// to verify that requests are being picked off of the completion port
|
|
//
|
|
fRet = ThreadPoolPostCompletion(0, HandlePing, (LPOVERLAPPED)this);
|
|
if (FALSE == fRet)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
DBGPRINTF((DBG_CONTEXT, "Posting completion for ping handling failed"));
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case IPM_OP_SHUTDOWN:
|
|
hr = HandleShutdown(
|
|
*( reinterpret_cast<const BOOL *>( pPipeMessage->GetData() ) )
|
|
);
|
|
break;
|
|
|
|
case IPM_OP_REQUEST_COUNTERS:
|
|
hr = HandleCounterRequest();
|
|
break;
|
|
|
|
case IPM_OP_PERIODIC_PROCESS_RESTART_PERIOD_IN_MINUTES:
|
|
|
|
DBG_ASSERT( pPipeMessage->GetData() != NULL );
|
|
hr = WP_RECYCLER::StartTimeBased(
|
|
*( reinterpret_cast<const DWORD *>( pPipeMessage->GetData() ) )
|
|
);
|
|
hr = NO_ERROR;
|
|
break;
|
|
|
|
case IPM_OP_PERIODIC_PROCESS_RESTART_MEMORY_USAGE_IN_KB:
|
|
{
|
|
|
|
DBG_ASSERT( pPipeMessage->GetData() != NULL );
|
|
// there are 2 DWORDS sent with memory based recycling
|
|
// first is Max Virtual Memory, second is Max Private Bytes
|
|
|
|
DWORD dwMaxVirtualMemoryKbUsage =
|
|
*( reinterpret_cast<const DWORD *>( pPipeMessage->GetData() ) );
|
|
DWORD dwMaxPrivateBytesKbUsage =
|
|
*( reinterpret_cast<const DWORD *>( pPipeMessage->GetData() ) + 1 );
|
|
|
|
hr = WP_RECYCLER::StartMemoryBased(
|
|
dwMaxVirtualMemoryKbUsage,
|
|
dwMaxPrivateBytesKbUsage );
|
|
hr = NO_ERROR;
|
|
|
|
break;
|
|
}
|
|
case IPM_OP_PERIODIC_PROCESS_RESTART_SCHEDULE:
|
|
|
|
DBG_ASSERT( pPipeMessage->GetData() != NULL );
|
|
hr = WP_RECYCLER::StartScheduleBased(
|
|
( reinterpret_cast<const WCHAR *>( pPipeMessage->GetData() ) )
|
|
);
|
|
hr = NO_ERROR;
|
|
break;
|
|
|
|
default:
|
|
DBG_ASSERT(FALSE);
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* This is a callback from the message pipe that means
|
|
* the pipe has been connected and is ready for use.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* None.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* VOID
|
|
*/
|
|
VOID
|
|
WP_IPM::PipeConnected(
|
|
VOID
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* This is a callback from the message pipe that means
|
|
* the pipe has been disconnected and you won't be receiving
|
|
* any more messages.
|
|
*
|
|
* Tells WPIPM::Terminate that it's ok to exit now.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* hr - the error code associated with the pipe disconnection
|
|
*
|
|
* Return Value:
|
|
*
|
|
* VOID
|
|
*/
|
|
VOID
|
|
WP_IPM::PipeDisconnected(
|
|
IN HRESULT hr
|
|
)
|
|
{
|
|
if (FAILED(hr))
|
|
{
|
|
DBGPRINTF( (DBG_CONTEXT, "PipeDisconnected with hr ( %d).\n", hr));
|
|
}
|
|
|
|
//
|
|
// All sorts of miscreant shutdown behaviors (for testing)
|
|
//
|
|
if (RwpBehaviorExhibited = RWP_Shutdown_Behavior(&hr))
|
|
return;
|
|
|
|
//
|
|
// If the pipe disappears out from under us, WAS has probably orphaned
|
|
// us, initiate fast shutdown of this worker process.
|
|
//
|
|
|
|
if (!m_pWpContext->IsInShutdown() &&
|
|
hr != HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED) &&
|
|
IsDebuggerPresent())
|
|
{
|
|
DBG_ASSERT( !"w3wp.exe is getting orphaned" );
|
|
}
|
|
|
|
m_pWpContext->IndicateShutdown( TRUE );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* This is a callback from the message pipe that means
|
|
* that the pipe received an invalid message.
|
|
* Therefore, we signal to shutdown.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* VOID
|
|
*
|
|
* Return Value:
|
|
*
|
|
* VOID
|
|
*/
|
|
VOID
|
|
WP_IPM::PipeMessageInvalid(
|
|
VOID
|
|
)
|
|
{
|
|
return PipeDisconnected(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* Handles the ping message. Sends the ping response message.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* None.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* HRESULT
|
|
*/
|
|
//static
|
|
VOID
|
|
WP_IPM::HandlePing(
|
|
DWORD,
|
|
DWORD dwNumberOfBytesTransferred,
|
|
LPOVERLAPPED lpOverlapped
|
|
)
|
|
{
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
if (0 != dwNumberOfBytesTransferred)
|
|
{
|
|
DBG_ASSERT(0 == dwNumberOfBytesTransferred);
|
|
return;
|
|
}
|
|
|
|
DBG_ASSERT(NULL != lpOverlapped);
|
|
|
|
WP_IPM * pThis = (WP_IPM*) lpOverlapped;
|
|
|
|
DBG_ASSERT(pThis->m_pPipe);
|
|
|
|
DBGPRINTF( (DBG_CONTEXT, "Handle Ping\n\n"));
|
|
|
|
// If we're supposed to do this test, just do it here. Don't care about result
|
|
RWP_IPM_BadParamTest(RWP_IPM_OP_INVALID, &hr, pThis->m_pPipe);
|
|
|
|
RWP_IPM_BadParamTest(RWP_IPM_OP_PING_REPLY, &hr, pThis->m_pPipe);
|
|
|
|
// Even if we're testing this opcode, just do regular ping response so the app pool
|
|
// doesn't get shut down by WAS
|
|
if (RWP_NO_MISBEHAVE == RWP_Ping_Behavior(&hr, pThis->m_pPipe))
|
|
{
|
|
hr = pThis->m_pPipe->WriteMessage(
|
|
IPM_OP_PING_REPLY, // ping reply opcode
|
|
0, // no data to send
|
|
NULL // pointer to no data
|
|
);
|
|
|
|
if ( FAILED ( hr ) )
|
|
{
|
|
DBGPRINTF( (DBG_CONTEXT, "Failed to respond to ping\n\n"));
|
|
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if we are not healthy then we need to to ask WAS to
|
|
// shut us down.
|
|
//
|
|
if ( !( g_pwpContext->GetUnhealthy()))
|
|
{
|
|
DBGPRINTF( (DBG_CONTEXT, "Requesting shutdown due to isapi reporting unhealthiness\n\n"));
|
|
|
|
hr = pThis->SendMsgToAdminProcess( IPM_WP_RESTART_ISAPI_REQUESTED_RECYCLE );
|
|
|
|
if ( FAILED ( hr ) )
|
|
{
|
|
DBGPRINTF( (DBG_CONTEXT, "Failed telling WAS to shut us down\n\n"));
|
|
|
|
goto exit;
|
|
}
|
|
|
|
}
|
|
|
|
exit:
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* Handles the counter request message.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* None.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* HRESULT
|
|
*/
|
|
HRESULT
|
|
WP_IPM::HandleCounterRequest(
|
|
VOID
|
|
)
|
|
{
|
|
DBGPRINTF( (DBG_CONTEXT, "Handle Counter Request\n\n"));
|
|
|
|
HRESULT hr;
|
|
PBYTE pCounterData;
|
|
DWORD dwCounterData;
|
|
|
|
DBG_ASSERT ( m_pPipe );
|
|
|
|
if (FAILED(hr = g_pfnCollectCounters(&pCounterData, &dwCounterData)))
|
|
{
|
|
DBGPRINTF( (DBG_CONTEXT, "Didn't collect counters\n\n"));
|
|
return hr;
|
|
}
|
|
|
|
if(!RWP_IPM_BadParamTest(RWP_IPM_OP_SEND_COUNTERS, &hr, m_pPipe))
|
|
{
|
|
// If we're not testing this opcode, just do regular valid call
|
|
hr = m_pPipe->WriteMessage(IPM_OP_SEND_COUNTERS, // ping reply opcode
|
|
dwCounterData, // no data to send
|
|
pCounterData); // pointer to no data
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Routine Description:
|
|
*
|
|
*
|
|
* Handles the shutdown message. Shuts down the process
|
|
*
|
|
* Arguments:
|
|
*
|
|
* None.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* HRESULT
|
|
*/
|
|
HRESULT
|
|
WP_IPM::HandleShutdown(
|
|
BOOL fDoImmediate
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
DBGPRINTF( (DBG_CONTEXT, "Handle ******************** Shutdown\n\n"));
|
|
|
|
//
|
|
// All sorts of miscreant shutdown behaviors (for testing)
|
|
//
|
|
if (RwpBehaviorExhibited = RWP_Shutdown_Behavior(&hr))
|
|
return (hr);
|
|
|
|
m_pWpContext->IndicateShutdown( fDoImmediate );
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* Sends the message to indicate the worker process has either finished
|
|
* initializing or has failed to initialize.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* HRESULT indicating success/failure of initialization
|
|
*
|
|
* Return Value:
|
|
*
|
|
* HRESULT
|
|
*/
|
|
HRESULT
|
|
WP_IPM::SendInitCompleteMessage(
|
|
HRESULT hrToSend
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if ( m_pPipe )
|
|
{
|
|
|
|
if(!RWP_IPM_BadParamTest(RWP_IPM_OP_HRESULT, &hr, m_pPipe))
|
|
{
|
|
// If we're not testing this opcode, just do regular valid call
|
|
hr = m_pPipe->WriteMessage(
|
|
IPM_OP_HRESULT, // opcode
|
|
sizeof( hrToSend ), // data length
|
|
reinterpret_cast<BYTE*>( &hrToSend ) // pointer to data
|
|
);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// if the pipe did not exist then we started up
|
|
// without the IPM, probably we are attempting
|
|
// to run without WAS support. ( from the cmd line )
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* Sends the message to indicate the worker process has reach certain state.
|
|
* Main use is in shutdown. See IPM_WP_SHUTDOWN_MSG for reasons.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* None.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* HRESULT
|
|
*/
|
|
HRESULT
|
|
WP_IPM::SendMsgToAdminProcess(
|
|
IPM_WP_SHUTDOWN_MSG reason
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (m_pPipe)
|
|
{
|
|
//
|
|
// All sorts of miscreant process rotation behaviors (for testing)
|
|
//
|
|
if (RwpBehaviorExhibited = RWP_Rotation_Behavior(&hr, m_pPipe))
|
|
return (hr);
|
|
|
|
if(!RWP_IPM_BadParamTest(RWP_IPM_OP_WORKER_REQUESTS_SHUTDOWN, &hr, m_pPipe))
|
|
{
|
|
// If we're not testing this opcode, just do regular valid call
|
|
hr = m_pPipe->WriteMessage(
|
|
IPM_OP_WORKER_REQUESTS_SHUTDOWN, // sends message indicate shutdown
|
|
sizeof(reason), // no data to send
|
|
(BYTE *)&reason // pointer to no data
|
|
);
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|