Leaked source code of windows server 2003
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

/*++
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;
}