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.
 
 
 
 
 
 

1074 lines
26 KiB

/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
w3wplist.cxx
Abstract:
implementation for w3wplist utility
Author:
Hamid Mahmood (t-hamidm) 06-08-2001
Revision History:
--*/
#include <precomp.hxx>
#include "w3wplist.hxx"
#include <ntrtl.h>
DECLARE_DEBUG_PRINTS_OBJECT();
DECLARE_DEBUG_VARIABLE();
w3wpList::w3wpList()
/*++
Routine Description:
Constructor.
Arguments:
none
Return Value:
none
--*/
{
m_pdwProcessId = NULL;
m_pszTargetAppPoolId = NULL;
m_dwCurrSizeOfProcessIdArray = 0;
m_chVerbosity = 0;
m_dwNumProcessId = 0;
m_startingIndex = 0;
m_dwTargetPID = 0;
InitializeListHead(&m_listHead);
//
// initialize global data
//
CONSOLE_HANDLER_VAR::g_HAS_CONSOLE_EVENT_OCCURED = FALSE;
CONSOLE_HANDLER_VAR::g_THREAD_EXIT_CODE = 1;
CONSOLE_HANDLER_VAR::g_PROCESS_EXIT_CODE = 1;
}
w3wpList::~w3wpList()
/*++
Routine Description:
Destructor.
Arguments:
none
Return Value:
none
--*/
{
DBG_ASSERT (m_dwCurrSizeOfProcessIdArray == 0);
DBG_ASSERT (m_chVerbosity == 0);
DBG_ASSERT (m_dwNumProcessId == 0);
DBG_ASSERT (m_startingIndex == 0);
DBG_ASSERT (m_dwTargetPID == 0);
DBG_ASSERT (m_pdwProcessId == NULL);
DBG_ASSERT (m_pszTargetAppPoolId == NULL);
DBG_ASSERT (IsListEmpty (&m_listHead) == TRUE );
}
HRESULT
w3wpList::Init( IN UCHAR chVerbosity,
IN BOOL fIsListMode,
IN WCHAR* pszInputAppPoolId,
IN DWORD dwPID
)
/*++
Routine Description:
Initializes all the class members.
Arguments:
chVerbosity -- verbosity level
Return Value:
none
--*/
{
HRESULT hr = S_OK;
SYSTEM_INFO systemInfo;
m_dwCurrSizeOfProcessIdArray = MIN_SIZE;
m_chVerbosity = chVerbosity;
m_fIsListMode = fIsListMode;
m_dwTargetPID = dwPID;
m_startingIndex = 0;
InitializeListHead(&m_listHead);
InitializeCriticalSection(&m_CriticalSection);
m_pdwProcessId = new DWORD[m_dwCurrSizeOfProcessIdArray];
if ( m_pdwProcessId == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto end;
}
if ( pszInputAppPoolId != NULL )
{
m_pszTargetAppPoolId = new WCHAR[wcslen(pszInputAppPoolId) + 1];
if ( m_pszTargetAppPoolId == NULL )
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto cleanup;
}
wcscpy ( m_pszTargetAppPoolId,
pszInputAppPoolId );
}
// figure out how to read the num of processors
GetSystemInfo( &systemInfo );
m_dwNumThreads = systemInfo.dwNumberOfProcessors;
goto end;
cleanup:
delete [] m_pdwProcessId;
end:
return hr;
}
VOID w3wpList::DestroyObject()
/*++
Routine Description:
deallocates memory.
Arguments:
none
Return Value:
none
--*/
{
W3WPLIST_NODE* pW3wpListNode = NULL;
PLIST_ENTRY pListEntry = NULL;
m_fIsListMode = FALSE;
m_dwCurrSizeOfProcessIdArray = 0;
m_chVerbosity = 0;
m_dwNumThreads = 0;
m_dwNumProcessId = 0;
m_startingIndex = 0;
m_dwTargetPID = 0;
delete [] m_pdwProcessId;
m_pdwProcessId = NULL;
delete [] m_pszTargetAppPoolId;
m_pszTargetAppPoolId = NULL;
DeleteCriticalSection(&m_CriticalSection);
//
// deleting the link list
//
while ( ! IsListEmpty (&m_listHead) )
{
pListEntry = RemoveHeadList(&m_listHead);
pW3wpListNode = CONTAINING_RECORD ( pListEntry,
W3WPLIST_NODE,
m_listEntry
);
//
// terminate the ProcessDetails obj
// before deleting
//
DBG_ASSERT(pW3wpListNode->CheckSignature());
pW3wpListNode->m_wpDetails.Terminate();
delete pW3wpListNode;
}
pW3wpListNode = NULL;
}
HRESULT
w3wpList::GetProcesses()
/*++
Routine Description:
Gets all the PIDs and starts up threads
to go through them.
Arguments:
None
Return Value:
HRESULT hr -- S_OK if successful, else failed with
error code
--*/
{
BOOL fIsFull = FALSE;
BOOL fCreateThreadFailed = FALSE;
DWORD dwBytesReceived;
DWORD dwBufferSize;
HRESULT hr;
DWORD dwIndex;
DWORD dwCount;
HANDLE* pHandles = NULL;
//
// Enumerate all the processes. Memory is
// re-allocated if the original size
// was not enough.
//
do
{
dwBufferSize = sizeof (DWORD) * m_dwCurrSizeOfProcessIdArray;
if ( EnumProcesses ( m_pdwProcessId,
dwBufferSize,
&dwBytesReceived
) == FALSE )
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto end;
}
m_dwNumProcessId = dwBytesReceived/sizeof(DWORD);
//
// Check for overflow, if yes increase size of
// m_pdwProcessId array
//
if ( m_dwNumProcessId == m_dwCurrSizeOfProcessIdArray )
{
DWORD * pdwTemp = m_pdwProcessId;
m_dwCurrSizeOfProcessIdArray *= 2;
m_pdwProcessId = new DWORD[m_dwCurrSizeOfProcessIdArray];
delete [] pdwTemp;
pdwTemp = NULL;
if ( m_pdwProcessId == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto end;
}
fIsFull = TRUE;
}
else
{
fIsFull = FALSE;
}
} while (fIsFull == TRUE);
//
// enable debug privilege for the current process
//
hr = EnableDebugPrivilege();
if ( hr != S_OK )
{
goto end;
}
//
// Create array for thread handles
//
pHandles = new HANDLE[m_dwNumThreads];
if ( pHandles == NULL )
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto end;
}
//
// spin off threads in suspended mode
//
for ( dwIndex = 0; dwIndex < m_dwNumThreads; dwIndex++ )
{
pHandles[dwIndex] = CreateThread( NULL, /* no security descriptor */
0, /* default stack size */
SpinNewThread,
this, /* thread parameters */
CREATE_SUSPENDED , /* suspended mode*/
NULL /* not getting thread id */
);
if ( pHandles[dwIndex] == NULL ) // thread creation failed
{
hr = HRESULT_FROM_WIN32(GetLastError());
fCreateThreadFailed = TRUE;
break;
}
}
//
// we need dwCount b/c CreateThread may fail,
// then we just need to loop through dwCount
// threads
//
dwCount = dwIndex;
if ( fCreateThreadFailed == TRUE )
{
for ( dwIndex = 0; dwIndex < dwCount; dwIndex++)
{
TerminateThread( pHandles[dwIndex],
GetLastError()
);
CloseHandle( pHandles[dwIndex]);
}
goto end;
}
//
// Set our own event handler for CTRL_C_EVENT,
// CTRL_BREAK_EVENT, CTRL_LOGOFF_EVENT,
// CTRL_SHUTDOWN_EVENT if verbosity > 0
// else we don't debug
//
if ( m_chVerbosity > 0 )
{
SetConsoleCtrlHandler( ConsoleCtrlHandler,
TRUE
);
}
//
// resume thread
//
for ( dwIndex = 0; dwIndex < dwCount; dwIndex++)
{
ResumeThread( pHandles[dwIndex]);
}
//
// wait for all threads to exit
//
WaitForMultipleObjects( dwCount, // number of handles in array
pHandles, // object-handle array
TRUE, // wait option
INFINITE // time-out interval
);
//
// check for Ctrl+C, Ctrl+Break, etc
//
if ( CONSOLE_HANDLER_VAR::g_HAS_CONSOLE_EVENT_OCCURED == TRUE )
{
ExitProcess(CONSOLE_HANDLER_VAR::g_PROCESS_EXIT_CODE);
}
//
// Turn back the default handler for CTRL_C_EVENT,
// CTRL_BREAK_EVENT, CTRL_LOGOFF_EVENT,
// CTRL_SHUTDOWN_EVENT
//
if ( m_chVerbosity > 0 )
{
SetConsoleCtrlHandler( ConsoleCtrlHandler,
FALSE
);
}
for ( dwIndex = 0; dwIndex < dwCount; dwIndex++)
{
CloseHandle(pHandles[dwIndex]);
}
end:
delete [] pHandles;
return hr;
}
DWORD WINAPI
w3wpList::SpinNewThread( LPVOID lpParam )
/*++
Routine Description:
static Thread function
Arguments:
None
Return Value:
DWORD
--*/
{
w3wpList* pw3wpList = (w3wpList*) lpParam;
pw3wpList->EnumAllWPThread();
return TRUE;
}
VOID
w3wpList::EnumAllWPThread()
/*++
Routine Description:
Each thread goes through the PID array
and processess specific process.
So, thread n will process the first
available PID and then PIDs at an
increment of NumOfThreads positions
in the array
Arguments:
None
Return Value:
None
--*/
{
//
// get the first available index to start with
//
DWORD dwStartIndex = InterlockedIncrement(&m_startingIndex);
//
// since m_startingIndex is initialized to zero and we want to start
// from index 0
//
dwStartIndex--;
//
// walk throught the array of PIDs and
// call DoWork on each of them. DoWork
// creates ProcessDetails obj for
// each PID and gets the requests
// if it were a worker process
//
for ( DWORD i = dwStartIndex;
i < m_dwNumProcessId;
i += m_dwNumThreads)
{
//
// continue until we find the PID
// of our interest. Only interesting
// if lInputPID != -1, i.e were looking
// at the app pool id
//
if ( (m_dwTargetPID != -1 ) &&
(m_dwTargetPID != m_pdwProcessId[i])
)
{
continue;
}
DoWork( m_pdwProcessId[i] );
if (m_dwTargetPID == m_pdwProcessId[i])
{
break;
}
}// end for
}
VOID
w3wpList::DoWork( IN DWORD dwPID )
/*++
Routine Description:
Ccreates ProcessDetails obj for
each PID and gets the requests
if it were a worker process.
It then adds that obj to the list
Arguments:
dwPID -- PID of the process
Return Value:
None
--*/
{
HRESULT hr;
W3WPLIST_NODE* pW3wpListNode = NULL;
//
// create new Node for the list
//
pW3wpListNode = new W3WPLIST_NODE();
if ( pW3wpListNode == NULL )
{
goto end;
}
//
// Initialize the ProcessDetails obj
// in the node
//
pW3wpListNode->m_wpDetails.Init( dwPID,
m_chVerbosity,
m_fIsListMode
);
//
// return value is S_OK if this was a worker process,
// else it is S_FALSE
//
hr = pW3wpListNode->m_wpDetails.GetProcessDetails(m_pszTargetAppPoolId);
if ( hr == S_FALSE )
{
goto cleanup;
}
//
// It was a worker process, add the node to
// the list
//
EnterCriticalSection( &m_CriticalSection );
InsertTailList( &m_listHead,
&(pW3wpListNode->m_listEntry)
);
LeaveCriticalSection( &m_CriticalSection );
goto end;
cleanup:
pW3wpListNode->m_wpDetails.Terminate();
delete pW3wpListNode;
end:
return;
}
HRESULT
w3wpList::EnableDebugPrivilege()
/*++
Routine Description:
Changes the privilege of the current process
so that it can debug other processes.
Arguments:
None
Return Value:
HRESULT hr -- S_FALSE, if exe name does not match
S_OK if name matched and other funcitons
called from within also passed
Error code: if anything else failed
--*/
{
HRESULT Status = S_OK;
HANDLE Token;
PTOKEN_PRIVILEGES NewPrivileges;
BYTE OldPriv[1024];
ULONG cbNeeded;
LUID LuidPrivilege;
//
// Make sure we have access to adjust and to get the
// old token privileges
//
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&Token)
)
{
Status = GetLastError();
goto EH_Exit;
}
cbNeeded = 0;
//
// Initialize the privilege adjustment structure
//
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &LuidPrivilege);
NewPrivileges = (PTOKEN_PRIVILEGES)
calloc(1, sizeof(TOKEN_PRIVILEGES) +
(1 - ANYSIZE_ARRAY) * sizeof(LUID_AND_ATTRIBUTES));
if (NewPrivileges == NULL)
{
Status = E_OUTOFMEMORY;
goto EH_Token;
}
//
// set new privilege
//
NewPrivileges->PrivilegeCount = 1;
NewPrivileges->Privileges[0].Luid = LuidPrivilege;
NewPrivileges->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
//
// Enable the privilege
//
if (!AdjustTokenPrivileges( Token,
FALSE,
NewPrivileges,
sizeof(OldPriv),
(PTOKEN_PRIVILEGES)OldPriv,
&cbNeeded )
)
{
//
// If the stack was too small to hold the privileges
// then allocate off the heap
//
if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
{
PBYTE pbOldPriv;
BOOL Adjusted;
pbOldPriv = (PUCHAR)calloc(1, cbNeeded);
if ( pbOldPriv == NULL )
{
Status = E_OUTOFMEMORY;
goto EH_NewPriv;
}
Adjusted = AdjustTokenPrivileges( Token,
FALSE,
NewPrivileges,
cbNeeded,
(PTOKEN_PRIVILEGES)pbOldPriv,
&cbNeeded );
free(pbOldPriv);
if ( !Adjusted )
{
Status = GetLastError();
}
}
}
EH_NewPriv:
free(NewPrivileges);
EH_Token:
CloseHandle(Token);
EH_Exit:
return Status;
}
VOID
w3wpList::OutputRequests()
/*++
Routine Description:
Outputs the information for each
of the ProcessDetails obj
Arguments:
None
Return Value:
None
--*/
{
PLIST_ENTRY pListEntry;
W3WPLIST_NODE* pListNode;
for ( pListEntry = m_listHead.Flink;
pListEntry != &m_listHead;
pListEntry = pListEntry->Flink
)
{
pListNode = CONTAINING_RECORD( pListEntry,
W3WPLIST_NODE,
m_listEntry
);
DBG_ASSERT (pListNode->CheckSignature());
pListNode->m_wpDetails.DumpRequests();
}
}
BOOL
w3wpList::ListEmpty()
/*++
Routine Description:
Returns TRUE if the list of
ProcessDetails obj is empty
Arguments:
None
Return Value:
BOOL
--*/
{
return IsListEmpty(&m_listHead);
}
BOOL WINAPI
w3wpList::ConsoleCtrlHandler( DWORD dwCtrlType )
{
BOOL fReturnValue = TRUE;
switch (dwCtrlType)
{
// Handle the CTRL+C signal.
case CTRL_C_EVENT:
case CTRL_BREAK_EVENT:
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT:
{
CONSOLE_HANDLER_VAR::g_HAS_CONSOLE_EVENT_OCCURED = TRUE;
break;
}
default:
{
CONSOLE_HANDLER_VAR::g_HAS_CONSOLE_EVENT_OCCURED = FALSE;
fReturnValue = FALSE;
}
}
return fReturnValue;
}
int __cdecl wmain(DWORD argc, WCHAR ** argv)
{
#define COL_WIDTH 30
WCHAR pszIndent[] = { L" " };
w3wpList w3wplstMyList;
BOOL fIsListMode = FALSE;
WCHAR* pszInputAppPoolId = NULL;
UCHAR chInputVerbosity = 0;
LONG lInputPID = -1;
HRESULT hr;
if ( argc <= 1 ) // no input args; list mode
{
fIsListMode = TRUE;
}
else if ( argc == 2 ) // better be /?, else it's error
{
if ( _wcsicmp (argv[1], L"/?") == 0 ) // help
{
wprintf (L"Copyright (c) Microsoft Corporation. All rights reserved.\n\n\n");
wprintf (L"%s\n%s\n%s\n\n",
L"Description: Use this tool to obtain worker process information, determine which",
L"worker process is assigned to a given application pool and show the requests",
L"that are queued in the worker process.");
wprintf (L"Syntax: w3wplist [{/p | /a} <identifier> [<level of details>]]\n\n");
wprintf (L"Parameters:\n");
wprintf ( L"%-*s%s\n\n",
COL_WIDTH,
L"Value",
L"Description");
wprintf ( L"%-*s%s\n%-*s%s\n",
COL_WIDTH,
L"/p",
L"Indicates that the <identifier> is a worker",
COL_WIDTH,
pszIndent,
L"process PID number."
);
wprintf ( L"%-*s%s\n%-*s%s\n\n",
COL_WIDTH,
L"/a",
L"Indicates that the <identifier> is an application",
COL_WIDTH,
pszIndent,
L"pool identifier."
);
wprintf ( L"%-*s\n",
COL_WIDTH,
L"<identifier>"
);
wprintf ( L"%-*s%s\n%-*s%s\n%-*s%s\n",
COL_WIDTH,
L"application pool identifier",
L"Displays information associated with all active ",
COL_WIDTH,
pszIndent,
L"worker processes for the application pool",
COL_WIDTH,
pszIndent,
L"specified."
);
wprintf ( L"%-*s%s\n%-*s%s\n\n",
COL_WIDTH,
L"worker process PID",
L"Displays information associated with the worker",
COL_WIDTH,
pszIndent,
L"process specified."
);
wprintf ( L"%-*s%s\n",
COL_WIDTH,
L"<level of details>",
L"Verbosity levels are cumulative"
);
wprintf ( L"%-*s%s\n%-*s%s\n%-*s%s\n%-*s%s\n",
COL_WIDTH,
L"/v or /v0",
L"Displays the worker process PID, the application",
COL_WIDTH,
pszIndent,
L"pool identifier, the application pool friendly-",
COL_WIDTH,
pszIndent,
L"name, and the total number of requests that have",
COL_WIDTH,
pszIndent,
L"been processed."
);
wprintf ( L"%-*s%s\n%-*s%s\n",
COL_WIDTH,
L"/v1",
L"Adds the Universal Resource Identifier, host name,",
COL_WIDTH,
pszIndent,
L"and the HTTP verb."
);
wprintf ( L"%-*s%s\n",
COL_WIDTH,
L"/v2",
L"Adds the URI query and the protocol version."
);
wprintf ( L"%-*s%s\n%-*s%s\n",
COL_WIDTH,
L"/v3",
L"Adds the time, date, client-ip, and the referer,",
COL_WIDTH,
pszIndent,
L"user-agent, and cookie headers if available."
);
wprintf ( L"%-*s%s\n%-*s%s\n\n",
COL_WIDTH,
L"/v4",
L"Adds the remaining available headers for the ",
COL_WIDTH,
pszIndent,
L"current request."
);
wprintf ( L"%s\n%s\n%s\n%s\n%s\n%s\n",
L"Examples:",
L" C:\\>w3wplist",
L" C:\\>w3wplist /a app_pool_014",
L" C:\\>w3wplist /p 4852",
L" C:\\>w3wplist /a app_pool_014 /v",
L" C:\\>w3wplist /p 4852 /v1"
);
// MORE STUFF GOES HERE
goto end;
}
else if ( _wcsicmp (argv[1], L"/p") == 0 )
{
wprintf (L"%s\n%s\n",
L"No worker process PID (W3wp.exe) was specified. Use Windows Task Manager to",
L"obtain a valid W3wp.exe PID number.");
goto end;
}
else if ( _wcsicmp (argv[1], L"/a") == 0 )
{
wprintf (L"%s\n%s\n",
L"No application pool identifier was specified. Search in Metabase.xml to obtain",
L"a valid application pool identifier.");
goto end;
}
else // invalid syntax
{
wprintf (L"Invalid syntax. Type w3wplist /? for help.\n");
goto end;
}
}
else if (argc == 4)
{
if ( (_wcsicmp(argv[3], L"/v") == 0) ||
(_wcsicmp(argv[3], L"/v0") == 0 )
)
{
chInputVerbosity = 0;
}
else if (_wcsicmp(argv[3], L"/v1") == 0)
{
chInputVerbosity = 1;
}
else if (_wcsicmp(argv[3], L"/v2") == 0)
{
chInputVerbosity = 2;
}
else if (_wcsicmp(argv[3], L"/v3") == 0)
{
chInputVerbosity = 3;
}
else if (_wcsicmp(argv[3], L"/v4") == 0)
{
chInputVerbosity = 4;
}
else if ( argc != 3 )
{
wprintf (L"Invalid syntax. Type w3wplist /? for help.\n");
goto end;
}
}
if ( (argc == 3 ) ||
(argc == 4)
)
{
// default verbosity of 0
if ( _wcsicmp (argv[1], L"/p") == 0 )
{
lInputPID = wcstol(argv[2], L'\0', 10);
}
else if ( _wcsicmp (argv[1], L"/a") == 0 )
{
pszInputAppPoolId = argv[2];
}
}
//
// initialize the object
//
hr = w3wplstMyList.Init( chInputVerbosity,
fIsListMode,
pszInputAppPoolId,
lInputPID );
if ( FAILED (hr) )
{
goto error;
}
hr = w3wplstMyList.GetProcesses();
if ( FAILED (hr) )
{
goto error;
}
//
// if list is empty, it means that either the input
// PID number was not a worker process or was invalid,
// or the input app pool id was invalid
//
if ( w3wplstMyList.ListEmpty() )
{
// some message of not found
if ( lInputPID != -1 ) // /p switch was input
{
wprintf( L"%s\n%s\n%s\n",
L"The worker process PID number (W3wp.exe) is not valid or is not associated with",
L"a W3wp.exe process. Use Windows Task Manager to obtain a valid W3wp.exe PID",
L"number." );
}
else if ( pszInputAppPoolId != NULL ) // /a switch was input
{
wprintf( L"%s\n%s\n",
L"The application pool identifier is not valid or does not exist. Search in",
L"Metabase.xml to obtain a valid application pool identifier." );
}
}
// print out requests
w3wplstMyList.OutputRequests();
// deallocate memory
w3wplstMyList.DestroyObject();
goto end;
error:
// message of error
wprintf ( L"Error occured. Error code : %d\n",
HRESULT_CODE(hr)
);
end:
return 0;
}