/*++ Copyright (c) 1998-2001 Microsoft Corporation Module Name: tul.c Abstract: Stupid test file for HTTP.SYS (formerly UL.SYS). Author: Keith Moore (keithmo) 17-Jun-1998 Revision History: --*/ #include "precomp.h" #pragma hdrstop #include <..\..\drv\config.h> // this funny business gets me the UL_DEBUG_* flags on free builds #if !DBG #undef DBG #define DBG 1 #define DBG_FLIP #endif #include <..\..\drv\debug.h> #ifdef DBG_FLIP #undef DBG_FLIP #undef DBG #define DBG 0 #endif // // Our command table. // typedef INT (WINAPI * PFN_COMMAND)( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ); typedef struct _COMMAND_ENTRY { PWSTR pCommandName; PWSTR pUsageHelp; PFN_COMMAND pCommandHandler; BOOL AutoStartUl; } COMMAND_ENTRY, *PCOMMAND_ENTRY; // // Performance counters. // #if LATER typedef enum _COUNTER_TYPE { Cumulative, Percentage, Average } COUNTER_TYPE, *PCOUNTER_TYPE; typedef struct _PERF_COUNTER { PWSTR pDisplayName; LONG FieldOffset; COUNTER_TYPE Type; } PERF_COUNTER, *PPERF_COUNTER; #define MAKE_CUMULATIVE( name ) \ { \ (PWSTR)L#name, \ FIELD_OFFSET( UL_PERF_COUNTERS_USER, name ), \ Cumulative \ } #define MAKE_PERCENTAGE( name ) \ { \ (PWSTR)L#name, \ FIELD_OFFSET( UL_PERF_COUNTERS_USER, name ), \ Percentage \ } #define MAKE_AVERAGE( name ) \ { \ (PWSTR)L#name, \ FIELD_OFFSET( UL_PERF_COUNTERS_USER, name ), \ Average \ } PERF_COUNTER UlPerfCounters[] = { MAKE_CUMULATIVE( BytesReceived ), MAKE_CUMULATIVE( BytesSent ), MAKE_CUMULATIVE( ConnectionsReceived ), MAKE_CUMULATIVE( FilesSent ), MAKE_CUMULATIVE( FileCacheAttempts ), MAKE_PERCENTAGE( FileCacheHits ), MAKE_CUMULATIVE( FileCacheNegativeHits ), MAKE_CUMULATIVE( FileCacheEntries ), MAKE_CUMULATIVE( FastAcceptAttempted ), MAKE_PERCENTAGE( FastAcceptSucceeded ), MAKE_CUMULATIVE( FastReceiveAttempted ), MAKE_PERCENTAGE( FastReceiveSucceeded ), MAKE_CUMULATIVE( FastSendAttempted ), MAKE_PERCENTAGE( FastSendSucceeded ), MAKE_CUMULATIVE( MdlReadAttempted ), MAKE_PERCENTAGE( MdlReadSucceeded ), MAKE_CUMULATIVE( DecAvailThreads ), MAKE_AVERAGE( DecAvailThreadsRetry ), MAKE_CUMULATIVE( IncAvailThreads ), MAKE_AVERAGE( IncAvailThreadsRetry ), MAKE_CUMULATIVE( DecAvailPending ), MAKE_AVERAGE( DecAvailPendingRetry ), MAKE_CUMULATIVE( DecNumberOfThreads ), MAKE_AVERAGE( DecNumberOfThreadsRetry ), MAKE_CUMULATIVE( IncNumberOfThreads ), MAKE_AVERAGE( IncNumberOfThreadsRetry ), MAKE_CUMULATIVE( CreateSession ), MAKE_AVERAGE( CreateSessionRetry ), MAKE_CUMULATIVE( DestroySession ), MAKE_AVERAGE( DestroySessionRetry ), MAKE_CUMULATIVE( IncAcceptPending ), MAKE_AVERAGE( IncAcceptPendingRetry ), MAKE_CUMULATIVE( DecAcceptPending ), MAKE_AVERAGE( DecAcceptPendingRetry ), MAKE_CUMULATIVE( PerCpuNetworkDpcCounters[0] ), MAKE_CUMULATIVE( PerCpuNetworkDpcCounters[1] ), MAKE_CUMULATIVE( PerCpuNetworkDpcCounters[2] ), MAKE_CUMULATIVE( PerCpuNetworkDpcCounters[3] ), MAKE_CUMULATIVE( PerCpuFileSystemDpcCounters[0] ), MAKE_CUMULATIVE( PerCpuFileSystemDpcCounters[1] ), MAKE_CUMULATIVE( PerCpuFileSystemDpcCounters[2] ), MAKE_CUMULATIVE( PerCpuFileSystemDpcCounters[3] ), MAKE_CUMULATIVE( PerCpuThreadPoolActivity[0] ), MAKE_CUMULATIVE( PerCpuThreadPoolActivity[1] ), MAKE_CUMULATIVE( PerCpuThreadPoolActivity[2] ), MAKE_CUMULATIVE( PerCpuThreadPoolActivity[3] ) }; #define NUM_PERF_COUNTERS (sizeof(UlPerfCounters) / sizeof(UlPerfCounters[0])) #define GET_LONGLONG( buffer, offset ) \ *(LONGLONG *)(((PCHAR)(buffer)) + (offset)) #endif // LATER // // Configuration stuff. // typedef struct _CONFIG_ENTRY { PWSTR pConfigName; PWSTR pDisplayFormat; ULONG Type; LONGLONG SavedValue; LONG Status; } CONFIG_ENTRY, *PCONFIG_ENTRY; CONFIG_ENTRY ConfigTable[] = { { REGISTRY_IRP_STACK_SIZE, L"%lu", REG_DWORD, DEFAULT_IRP_STACK_SIZE }, { REGISTRY_PRIORITY_BOOST, L"%lu", REG_DWORD, DEFAULT_PRIORITY_BOOST }, { REGISTRY_DEBUG_FLAGS, L"%08lx", REG_DWORD, DEFAULT_DEBUG_FLAGS }, { REGISTRY_BREAK_ON_STARTUP, L"%lu", REG_DWORD, DEFAULT_BREAK_ON_STARTUP }, { REGISTRY_BREAK_ON_ERROR, L"%lu", REG_DWORD, DEFAULT_BREAK_ON_ERROR }, { REGISTRY_VERBOSE_ERRORS, L"%lu", REG_DWORD, DEFAULT_VERBOSE_ERRORS }, { REGISTRY_ENABLE_UNLOAD, L"%lu", REG_DWORD, DEFAULT_ENABLE_UNLOAD }, { REGISTRY_ENABLE_SECURITY, L"%lu", REG_DWORD, DEFAULT_ENABLE_SECURITY }, { REGISTRY_MIN_IDLE_CONNECTIONS, L"%lu", REG_DWORD, DEFAULT_MIN_IDLE_CONNECTIONS }, { REGISTRY_MAX_IDLE_CONNECTIONS, L"%lu", REG_DWORD, DEFAULT_MAX_IDLE_CONNECTIONS }, { REGISTRY_IRP_CONTEXT_LOOKASIDE_DEPTH, L"%lu", REG_DWORD, DEFAULT_IRP_CONTEXT_LOOKASIDE_DEPTH }, { REGISTRY_RCV_BUFFER_SIZE, L"%lu", REG_DWORD, DEFAULT_RCV_BUFFER_SIZE }, { REGISTRY_RCV_BUFFER_LOOKASIDE_DEPTH, L"%lu", REG_DWORD, DEFAULT_RCV_BUFFER_LOOKASIDE_DEPTH }, { REGISTRY_RESOURCE_LOOKASIDE_DEPTH, L"%lu", REG_DWORD, DEFAULT_RESOURCE_LOOKASIDE_DEPTH }, { REGISTRY_REQ_BUFFER_LOOKASIDE_DEPTH, L"%lu", REG_DWORD, DEFAULT_REQ_BUFFER_LOOKASIDE_DEPTH }, { REGISTRY_INT_REQUEST_LOOKASIDE_DEPTH, L"%lu", REG_DWORD, DEFAULT_INT_REQUEST_LOOKASIDE_DEPTH }, { REGISTRY_RESP_BUFFER_SIZE, L"%lu", REG_DWORD, DEFAULT_RESP_BUFFER_SIZE }, { REGISTRY_RESP_BUFFER_LOOKASIDE_DEPTH, L"%lu", REG_DWORD, DEFAULT_RESP_BUFFER_LOOKASIDE_DEPTH }, { REGISTRY_SEND_TRACKER_LOOKASIDE_DEPTH, L"%lu", REG_DWORD, DEFAULT_SEND_TRACKER_LOOKASIDE_DEPTH }, { REGISTRY_LOG_BUFFER_LOOKASIDE_DEPTH, L"%lu", REG_DWORD, DEFAULT_LOG_BUFFER_LOOKASIDE_DEPTH }, { REGISTRY_MAX_INTERNAL_URL_LENGTH, L"%lu", REG_DWORD, DEFAULT_MAX_INTERNAL_URL_LENGTH }, { REGISTRY_MAX_REQUEST_BYTES, L"%lu", REG_DWORD, DEFAULT_MAX_REQUEST_BYTES }, { REGISTRY_ENABLE_CONNECTION_REUSE, L"%lu", REG_DWORD, DEFAULT_ENABLE_CONNECTION_REUSE }, { REGISTRY_ENABLE_NAGLING, L"%lu", REG_DWORD, DEFAULT_ENABLE_NAGLING }, { REGISTRY_ENABLE_THREAD_AFFINITY, L"%lu", REG_DWORD, DEFAULT_ENABLE_THREAD_AFFINITY }, { REGISTRY_THREAD_AFFINITY_MASK, L"%I64x", REG_QWORD, 0 }, { REGISTRY_THREADS_PER_CPU, L"%lu", REG_DWORD, DEFAULT_THREADS_PER_CPU }, { REGISTRY_MAX_WORK_QUEUE_DEPTH, L"%lu", REG_DWORD, DEFAULT_MAX_WORK_QUEUE_DEPTH, }, { REGISTRY_MIN_WORK_DEQUEUE_DEPTH, L"%lu", REG_DWORD, DEFAULT_MIN_WORK_DEQUEUE_DEPTH, }, { REGISTRY_MAX_URL_LENGTH, L"%lu", REG_DWORD, DEFAULT_MAX_URL_LENGTH }, { REGISTRY_MAX_FIELD_LENGTH, L"%lu", REG_DWORD, DEFAULT_MAX_FIELD_LENGTH }, { REGISTRY_DEBUG_LOGTIMER_CYCLE, L"%lu", REG_DWORD, DEFAULT_DEBUG_LOGTIMER_CYCLE }, { REGISTRY_DEBUG_LOG_BUFFER_PERIOD, L"%lu", REG_DWORD, DEFAULT_DEBUG_LOG_BUFFER_PERIOD }, { REGISTRY_LOG_BUFFER_SIZE, L"%lu", REG_DWORD, DEFAULT_LOG_BUFFER_SIZE }, { REGISTRY_ENABLE_NON_UTF8_URL, L"%lu", REG_DWORD, DEFAULT_ENABLE_NON_UTF8_URL }, { REGISTRY_ENABLE_DBCS_URL, L"%lu", REG_DWORD, DEFAULT_ENABLE_DBCS_URL }, { REGISTRY_FAVOR_DBCS_URL, L"%lu", REG_DWORD, DEFAULT_FAVOR_DBCS_URL }, { REGISTRY_CACHE_ENABLED, L"%lu", REG_DWORD, DEFAULT_CACHE_ENABLED }, { REGISTRY_MAX_CACHE_URI_COUNT, L"%lu", REG_DWORD, DEFAULT_MAX_CACHE_URI_COUNT }, { REGISTRY_MAX_CACHE_MEGABYTE_COUNT, L"%lu", REG_DWORD, DEFAULT_MAX_CACHE_MEGABYTE_COUNT }, { REGISTRY_CACHE_SCAVENGER_PERIOD, L"%lu", REG_DWORD, DEFAULT_CACHE_SCAVENGER_PERIOD }, { REGISTRY_MAX_URI_BYTES, L"%lu", REG_DWORD, DEFAULT_MAX_URI_BYTES }, { REGISTRY_HASH_TABLE_BITS, L"%ld", REG_DWORD, DEFAULT_HASH_TABLE_BITS }, { REGISTRY_LARGE_MEM_MEGABYTES, L"%ld", REG_DWORD, DEFAULT_LARGE_MEM_MEGABYTES }, { REGISTRY_OPAQUE_ID_TABLE_SIZE, L"%lu", REG_DWORD, DEFAULT_OPAQUE_ID_TABLE_SIZE }, }; #define NUM_CONFIG_ENTRIES (sizeof(ConfigTable) / sizeof(ConfigTable[0])) #define DEFAULT_SUFFIX_SZ L" [default]" typedef struct _FLAG_ENTRY { PWSTR pName; PWSTR pDisplayFormat; LONG Value; } FLAG_ENTRY, *PFLAG_ENTRY; #define MAKE_FLAG( name, display ) \ { \ (PWSTR)L#name, \ (display), \ UL_DEBUG_ ## name \ } FLAG_ENTRY FlagTable[] = { MAKE_FLAG(OPEN_CLOSE, L"file object create/close"), MAKE_FLAG(SEND_RESPONSE, L"send response ioctl"), MAKE_FLAG(SEND_BUFFER, L"send buffer"), MAKE_FLAG(TDI, L"low level network stuff"), MAKE_FLAG(FILE_CACHE, L"open/close files"), MAKE_FLAG(CONFIG_GROUP_FNC, L"config group changes"), MAKE_FLAG(CONFIG_GROUP_TREE, L"cgroup tree operations"), MAKE_FLAG(REFCOUNT, L"object refcounting"), MAKE_FLAG(HTTP_IO, L"high level network & buffers"), MAKE_FLAG(ROUTING, L"request to process routing"), MAKE_FLAG(URI_CACHE, L"uri content cache"), MAKE_FLAG(PARSER, L"request parsing"), MAKE_FLAG(SITE, L"sites and endpoints"), MAKE_FLAG(WORK_ITEM, L"thread pool work queue"), MAKE_FLAG(FILTER, L"filters and ssl"), MAKE_FLAG(LOGGING, L"ul logging"), MAKE_FLAG(TC, L"traffic control"), MAKE_FLAG(OPAQUE_ID, L"opaque ids"), MAKE_FLAG(PERF_COUNTERS, L"perf counters"), MAKE_FLAG(LKRHASH, L"LKRhash hashtables"), MAKE_FLAG(TIMEOUTS, L"timeout monitor"), MAKE_FLAG(LIMITS, L"connection limits"), MAKE_FLAG(LARGE_MEM, L"large memory"), MAKE_FLAG(IOCTL, L"ioctl"), MAKE_FLAG(VERBOSE, L"verbose"), }; #define NUM_FLAG_ENTRIES (sizeof(FlagTable) / sizeof(FlagTable[0])) DEFINE_COMMON_GLOBALS(); VOID Usage( VOID ); NTSTATUS OpenUlDevice( PHANDLE pHandle ); BOOL TryToStartUlDevice( VOID ); INT LongLongToString( LONGLONG Value, PWSTR pBuffer ); LONG CalcPercentage( LONGLONG High, LONGLONG Low ); PCOMMAND_ENTRY FindCommandByName( IN PWSTR pCommand ); PCONFIG_ENTRY FindConfigByName( IN PWSTR pConfig ); ULONG FindFlagByName( IN PWSTR pFlagName ); INT ControlHttpServer( IN HANDLE UlHandle, IN ULONG Command ); VOID DumpConfiguration( IN HKEY Key ); VOID DumpFlags( IN HKEY Key ); #if LATER INT WINAPI DoStart( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ); INT WINAPI DoStop( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ); INT WINAPI DoPause( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ); INT WINAPI DoContinue( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ); INT WINAPI DoQuery( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ); INT WINAPI DoPerf( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ); INT WINAPI DoClear( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ); INT WINAPI DoFlush( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ); #endif // LATER INT WINAPI DoConfig( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ); INT WINAPI DoFlags( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ); COMMAND_ENTRY CommandTable[] = { #if LATER { L"start", L"starts the server", &DoStart, TRUE }, { L"stop", L"stops the server", &DoStop, TRUE }, { L"pause", L"pauses the server", &DoPause, TRUE }, { L"continue", L"continues the server", &DoContinue, TRUE }, { L"query", L"queries the current state", &DoQuery, TRUE }, { L"perf", L"displays perf counters", &DoPerf, TRUE }, { L"clear", L"clears perf counters", &DoClear, TRUE }, { L"flush", L"flushes file cache", &DoFlush, TRUE }, #endif // LATER { L"config", L"configures the server", &DoConfig, FALSE }, { L"flags", L"configures debug flags", &DoFlags, FALSE } }; #define NUM_COMMAND_ENTRIES (sizeof(CommandTable) / sizeof(CommandTable[0])) INT __cdecl wmain( INT argc, PWSTR argv[] ) { NTSTATUS status; HANDLE handle; PCOMMAND_ENTRY pEntry; INT result; ULONG err; // // Initialize. // setvbuf( stdin, NULL, _IONBF, 0 ); setvbuf( stdout, NULL, _IONBF, 0 ); // // Setup locals so we know how to cleanup on exit. // handle = NULL; // // Find the command handler. // if (argc == 1) { pEntry = NULL; } else { pEntry = FindCommandByName( argv[1] ); } if (pEntry == NULL) { Usage(); result = 1; goto cleanup; } // // Open the UL.SYS device. // status = OpenUlDevice( &handle ); if (!NT_SUCCESS(status)) { if (pEntry->AutoStartUl) { if (TryToStartUlDevice()) { status = OpenUlDevice( &handle ); } } else { status = STATUS_SUCCESS; } } if (!NT_SUCCESS(status)) { wprintf( L"Cannot open %s, error %08lx\n", HTTP_CONTROL_DEVICE_NAME, status ); result = 1; goto cleanup; } // // Call the handler. // argc--; argv++; result = (pEntry->pCommandHandler)( handle, argc, argv ); cleanup: if (handle != NULL) { NtClose( handle ); } return result; } // main PCOMMAND_ENTRY FindCommandByName( IN PWSTR pCommand ) { PCOMMAND_ENTRY pEntry; INT i; for (i = NUM_COMMAND_ENTRIES, pEntry = &CommandTable[0] ; i > 0 ; i--, pEntry++) { if (_wcsicmp( pCommand, pEntry->pCommandName ) == 0) { return pEntry; } } return NULL; } // FindCommandByName PCONFIG_ENTRY FindConfigByName( IN PWSTR pConfig ) { PCONFIG_ENTRY pEntry; INT i; INT len; // // First off, validate that the incoming configuration name // is of the form "property=". The trailing '=' is required. // len = wcslen( pConfig ); if (pConfig[len - 1] != L'=') { return NULL; } len--; for (i = NUM_CONFIG_ENTRIES, pEntry = &ConfigTable[0] ; i > 0 ; i--, pEntry++) { if ((INT)wcslen( pEntry->pConfigName ) == len && _wcsnicmp( pConfig, pEntry->pConfigName, len ) == 0) { return pEntry; } } return NULL; } // FindConfigByName ULONG FindFlagByName( IN PWSTR pFlagName ) { INT len; ULONG flags; ULONG i; len = wcslen(pFlagName); if ((len > 2) && (wcsncmp(pFlagName, L"0x", 2) == 0)) { // numeric flag flags = wcstoul(pFlagName, NULL, 16); } else { // named flag flags = 0; for (i = 0; i < NUM_FLAG_ENTRIES; i++) { if (_wcsicmp(pFlagName, FlagTable[i].pName) == 0) { flags = FlagTable[i].Value; break; } } } return flags; } VOID Usage( VOID ) { PCOMMAND_ENTRY pEntry; INT i; INT maxLength; INT len; // // Scan the command table, searching for the longest command name. // (This makes the output much prettier...) // maxLength = 0; for (i = NUM_COMMAND_ENTRIES, pEntry = &CommandTable[0] ; i > 0 ; i--, pEntry++) { len = wcslen( pEntry->pCommandName ); if (len > maxLength) { maxLength = len; } } // // Now display the usage information. // wprintf( L"use: tul action [options]\n" L"\n" L"valid actions are:\n" L"\n" ); for (i = NUM_COMMAND_ENTRIES, pEntry = &CommandTable[0] ; i > 0 ; i--, pEntry++) { wprintf( L" %-*s - %s\n", maxLength, pEntry->pCommandName, pEntry->pUsageHelp ); } } // Usage NTSTATUS OpenUlDevice( PHANDLE pHandle ) { NTSTATUS status; OBJECT_ATTRIBUTES objectAttributes; UNICODE_STRING deviceName; IO_STATUS_BLOCK ioStatusBlock; // // Open the UL device. // RtlInitUnicodeString( &deviceName, HTTP_CONTROL_DEVICE_NAME ); InitializeObjectAttributes( &objectAttributes, // ObjectAttributes &deviceName, // ObjectName OBJ_CASE_INSENSITIVE, // Attributes NULL, // RootDirectory NULL // SecurityDescriptor ); status = NtCreateFile( pHandle, // FileHandle GENERIC_READ | // DesiredAccess GENERIC_WRITE | SYNCHRONIZE, &objectAttributes, // ObjectAttributes &ioStatusBlock, // IoStatusBlock NULL, // AllocationSize 0, // FileAttributes FILE_SHARE_READ | // ShareAccess FILE_SHARE_WRITE, FILE_OPEN_IF, // CreateDisposition FILE_SYNCHRONOUS_IO_NONALERT, // CreateOptions NULL, // EaBuffer 0 // EaLength ); if (!NT_SUCCESS(status)) { *pHandle = NULL; } return status; } // OpenHdhDevice BOOL TryToStartUlDevice( VOID ) { BOOL result = FALSE; SC_HANDLE scHandle = NULL; SC_HANDLE svcHandle = NULL; scHandle = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS ); if (scHandle == NULL) { goto exit; } svcHandle = OpenService( scHandle, L"Ul", SERVICE_ALL_ACCESS ); if (svcHandle == NULL) { goto exit; } if (!StartService( svcHandle, 0, NULL )) { goto exit; } result = TRUE; exit: if (svcHandle != NULL) { CloseServiceHandle( svcHandle ); } if (scHandle != NULL) { CloseServiceHandle( scHandle ); } return result; } // TryToStartHdhDevice INT LongLongToString( LONGLONG Value, PWSTR pBuffer ) { PWSTR p1; PWSTR p2; WCHAR ch; INT digit; BOOL negative; INT count; BOOL needComma; INT length; ULONGLONG unsignedValue; // // Handling zero specially makes everything else a bit easier. // if (Value == 0) { wcscpy( pBuffer, L"0" ); return 1; } // // Remember if the value is negative. // if (Value < 0) { negative = TRUE; unsignedValue = (ULONGLONG)-Value; } else { negative = FALSE; unsignedValue = (ULONGLONG)Value; } // // Pull the least signifigant digits off the value and store them // into the buffer. Note that this will store the digits in the // reverse order. // p1 = p2 = pBuffer; count = 3; needComma = FALSE; while (unsignedValue != 0) { if (needComma) { *p1++ = L','; needComma = FALSE; } digit = (INT)( unsignedValue % 10 ); unsignedValue = unsignedValue / 10; *p1++ = L'0' + digit; count--; if (count == 0) { count = 3; needComma = TRUE; } } // // Tack on a leading L'-' if necessary. // if (negative) { *p1++ = L'-'; } length = (INT)( p1 - pBuffer ); // // Reverse the digits in the buffer. // *p1-- = L'\0'; while (p1 > p2) { ch = *p1; *p1 = *p2; *p2 = ch; p2++; p1--; } return length; } // LongLongToString LONG CalcPercentage( LONGLONG High, LONGLONG Low ) { LONG result; if (High == 0 || Low == 0) { result = 0; } else { result = (LONG)( ( Low * 100 ) / High ); } return result; } // CalcPercentage #if LATER INT ControlHttpServer( IN HANDLE UlHandle, IN ULONG Command ) { NTSTATUS status; IO_STATUS_BLOCK ioStatusBlock; UL_CONTROL_HTTP_SERVER_INFO controlInfo; controlInfo.Command = Command; // // Issue the request. // status = NtDeviceIoControlFile( UlHandle, // FileHandle NULL, // Event NULL, // ApcRoutine NULL, // ApcContext &ioStatusBlock, // IoStatusBlock IOCTL_UL_CONTROL_HTTP_SERVER, // IoControlCode &controlInfo, // InputBuffer sizeof(controlInfo), // InputBufferLength, &controlInfo, // OutputBuffer, sizeof(controlInfo) // OutputBufferLength ); if (!NT_SUCCESS(status)) { wprintf( L"NtDeviceIoControlFile() failed, error %08lx\n", status ); return 1; } wprintf( L"HTTP server state = %lu (%s)\n", controlInfo.State, UlStateToString( controlInfo.State ) ); return 0; } // ControlHttpServer #endif // LATER VOID DumpConfiguration( IN HKEY Key ) { PCONFIG_ENTRY pEntry; INT i; INT len; INT maxNameLength; INT maxValueLength; LONG err; LONG longValue; DWORD type; DWORD length; PWSTR pDefaultSuffix; WCHAR stringValue[MAX_PATH]; // // Scan the config table, searching for the longest parameter name. // (This makes the output much prettier...) // maxNameLength = 0; maxValueLength = 0; for (i = NUM_CONFIG_ENTRIES, pEntry = &ConfigTable[0] ; i > 0 ; i--, pEntry++) { len = wcslen( pEntry->pConfigName ); if (len > maxNameLength) { maxNameLength = len; } if (pEntry->Type == REG_DWORD) { length = sizeof(pEntry->SavedValue); pEntry->Status = RegQueryValueEx( Key, pEntry->pConfigName, NULL, &type, (LPBYTE)&pEntry->SavedValue, &length ); len = swprintf( stringValue, pEntry->pDisplayFormat, (LONG) pEntry->SavedValue ); if (len > maxValueLength) { maxValueLength = len; } } else if (pEntry->Type == REG_QWORD) { length = sizeof(pEntry->SavedValue); pEntry->Status = RegQueryValueEx( Key, pEntry->pConfigName, NULL, &type, (LPBYTE)&pEntry->SavedValue, &length ); len = swprintf( stringValue, pEntry->pDisplayFormat, (LONGLONG) pEntry->SavedValue ); if (len > maxValueLength) { maxValueLength = len; } } } // // Now display the parameters. // wprintf( L"Configuration:\n" ); for (i = NUM_CONFIG_ENTRIES, pEntry = &ConfigTable[0] ; i > 0 ; i--, pEntry++) { len = 0; pDefaultSuffix = L""; if (pEntry->Type == REG_DWORD) { swprintf( stringValue, pEntry->pDisplayFormat, (LONG) pEntry->SavedValue ); if (pEntry->Status != NO_ERROR) { pDefaultSuffix = DEFAULT_SUFFIX_SZ; } len = maxValueLength; } else if (pEntry->Type == REG_QWORD) { swprintf( stringValue, pEntry->pDisplayFormat, (LONGLONG) pEntry->SavedValue ); if (pEntry->Status != NO_ERROR) { pDefaultSuffix = DEFAULT_SUFFIX_SZ; } len = maxValueLength; } else { length = sizeof(stringValue) / sizeof(stringValue[0]); err = RegQueryValueEx( Key, pEntry->pConfigName, NULL, &type, (LPBYTE)stringValue, &length ); if (err != NO_ERROR) { wcscpy( stringValue, ( pEntry->SavedValue == 0 ) ? L"(null)" : (PWSTR)pEntry->SavedValue ); pDefaultSuffix = DEFAULT_SUFFIX_SZ; } } wprintf( L" %-*s : %*s%s\n", maxNameLength, pEntry->pConfigName, len, stringValue, pDefaultSuffix ); } } // DumpConfiguration VOID DumpFlags( IN HKEY Key ) { LONG err; DWORD length; DWORD flags; DWORD flagsDisplayed; ULONG i; // // Read the flags from the registry // flags = DEFAULT_DEBUG_FLAGS; length = sizeof(flags); err = RegQueryValueEx( Key, // key REGISTRY_DEBUG_FLAGS, // name NULL, // reserved NULL, // type (LPBYTE) &flags, // value &length // value length ); // // Now display the flags // flagsDisplayed = 0; wprintf( L"\n"); for (i = 0; i < NUM_FLAG_ENTRIES; i++) { wprintf( L"%-20s", FlagTable[i].pName ); if (flags & FlagTable[i].Value) { wprintf(L"[on] "); flagsDisplayed |= FlagTable[i].Value; } else { wprintf(L" "); } wprintf(L"%s\n", FlagTable[i].pDisplayFormat); } wprintf( L"\n" ); // // dump any set flags that we missed // flags &= ~flagsDisplayed; if (flags) { wprintf(L"The following set flags are not in the table 0x%08x\n\n", flags); } // // a handy thing to cut and paste // wprintf(L"tul flags 0x%08x\n", flags | flagsDisplayed); } // DumpFlags #if LATER INT WINAPI DoStart( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ) { INT result; // // Validate the arguments. // if (argc != 1) { wprintf( L"use: tul start\n" ); return 1; } // // Do it. // result = ControlHttpServer( UlHandle, UL_HTTP_SERVER_COMMAND_START ); return result; } // DoStart INT WINAPI DoStop( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ) { INT result; // // Validate the arguments. // if (argc != 1) { wprintf( L"use: tul stop\n" ); return 1; } // // Do it. // result = ControlHttpServer( UlHandle, UL_HTTP_SERVER_COMMAND_STOP ); return result; } // DoStop INT WINAPI DoPause( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ) { INT result; // // Validate the arguments. // if (argc != 1) { wprintf( L"use: tul pause\n" ); return 1; } // // Do it. // result = ControlHttpServer( UlHandle, UL_HTTP_SERVER_COMMAND_PAUSE ); return result; } // DoPause INT WINAPI DoContinue( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ) { INT result; // // Validate the arguments. // if (argc != 1) { wprintf( L"use: tul continue\n" ); return 1; } // // Do it. // result = ControlHttpServer( UlHandle, UL_HTTP_SERVER_COMMAND_CONTINUE ); return result; } // DoContinue INT WINAPI DoQuery( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ) { INT result; // // Validate the arguments. // if (argc != 1) { wprintf( L"use: tul query\n" ); return 1; } // // Do it. // result = ControlHttpServer( UlHandle, UL_HTTP_SERVER_COMMAND_QUERY ); return result; } // DoQuery INT WINAPI DoPerf( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ) { UL_PERF_COUNTERS_USER perfCounters; IO_STATUS_BLOCK ioStatusBlock; NTSTATUS status; INT i; INT maxNameLength; INT maxValueLength; INT len; PPERF_COUNTER counter; WCHAR value[32]; WCHAR suffix[32]; // // Validate the arguments. // if (argc != 1) { wprintf( L"use: tul perf\n" ); return 1; } // // Read the perf counters. // status = NtDeviceIoControlFile( UlHandle, // FileHandle NULL, // Event NULL, // ApcRoutine NULL, // ApcContext &ioStatusBlock, // IoStatusBlock IOCTL_UL_QUERY_PERF_COUNTERS, // IoControlCode NULL, // InputBuffer 0, // InputBufferLength, &perfCounters, // OutputBuffer, sizeof(perfCounters) // OutputBufferLength ); if (!NT_SUCCESS(status)) { wprintf( L"NtDeviceIoControlFile() failed, error %08lx\n", status ); return 1; } // // Pass 1: Calculate the maximum lengths of the display names and // the printable values. // maxNameLength = 0; maxValueLength = 0; for( i = NUM_PERF_COUNTERS, counter = UlPerfCounters ; i > 0 ; i--, counter++ ) { len = wcslen( counter->DisplayName ); if (len > maxNameLength) { maxNameLength = len; } len = LongLongToString( GET_LONGLONG( &perfCounters, counter->FieldOffset ), value ); if (len > maxValueLength) { maxValueLength = len; } } // // Pass 2: Display the counters. // wprintf( L"Performance Counters:\n" ); for( i = NUM_PERF_COUNTERS, counter = UlPerfCounters ; i > 0 ; i--, counter++ ) { LongLongToString( GET_LONGLONG( &perfCounters, counter->FieldOffset ), value ); suffix[0] = '\0'; // until proven otherwise... if (counter->Type == Percentage) { LONGLONG high; LONGLONG low; high = GET_LONGLONG( &perfCounters, counter[-1].FieldOffset ); low = GET_LONGLONG( &perfCounters, counter->FieldOffset ); if (high != 0 || low != 0) { swprintf( suffix, L" [%ld%%]", CalcPercentage( high, low ) ); } } else if (counter->Type == Average) { LONGLONG numerator; LONGLONG divisor; float average; numerator = GET_LONGLONG( &perfCounters, counter->FieldOffset ); divisor = GET_LONGLONG( &perfCounters, counter[-1].FieldOffset ); if (divisor != 0) { average = (float)numerator / (float)divisor; swprintf( suffix, L" [%.3f]", average ); } } wprintf( L" %-*s = %*s%s\n", maxNameLength, counter->DisplayName, maxValueLength, value, suffix ); } return 0; } // DoPerf INT WINAPI DoClear( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ) { NTSTATUS status; IO_STATUS_BLOCK ioStatusBlock; // // Validate the arguments. // if (argc != 1) { wprintf( L"use: tul clear\n" ); return 1; } // // Issue the request. // status = NtDeviceIoControlFile( UlHandle, // FileHandle NULL, // Event NULL, // ApcRoutine NULL, // ApcContext &ioStatusBlock, // IoStatusBlock IOCTL_UL_CLEAR_PERF_COUNTERS, // IoControlCode NULL, // InputBuffer 0, // InputBufferLength, NULL, // OutputBuffer, 0 // OutputBufferLength ); if (!NT_SUCCESS(status)) { wprintf( L"NtDeviceIoControlFile() failed, error %08lx\n", status ); return 1; } wprintf( L"Performance counters cleared\n" ); return 0; } // DoClear INT WINAPI DoFlush( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ) { NTSTATUS status; IO_STATUS_BLOCK ioStatusBlock; // // Validate the arguments. // if (argc != 1) { wprintf( L"use: tul flush\n" ); return 1; } // // Issue the request. // status = NtDeviceIoControlFile( UlHandle, // FileHandle NULL, // Event NULL, // ApcRoutine NULL, // ApcContext &ioStatusBlock, // IoStatusBlock IOCTL_UL_FLUSH_FILE_CACHE, // IoControlCode NULL, // InputBuffer 0, // InputBufferLength, NULL, // OutputBuffer, 0 // OutputBufferLength ); if (!NT_SUCCESS(status)) { wprintf( L"NtDeviceIoControlFile() failed, error %08lx\n", status ); return 1; } wprintf( L"File cache flushed\n" ); return 0; } // DoFlush #endif // LATER INT WINAPI DoConfig( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ) { PCONFIG_ENTRY pEntry; LONG longValue; LONGLONG longlongValue; HKEY key; INT result; LONG err; CONST BYTE * pNewValue; DWORD newValueLength; // // Setup locals so we know how to cleanup on exit. // key = NULL; // // Try to open the registry. // err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Services\\Http\\Parameters", 0, KEY_ALL_ACCESS, &key ); if (err != NO_ERROR) { wprintf( L"tul: cannot open registry, error %ld\n", err ); result = 1; goto cleanup; } // // Validate the arguments. // if (argc == 1) { DumpConfiguration( key ); result = 0; goto cleanup; } else if (argc != 3) { wprintf( L"use: tul config [property= value]\n" ); result = 1; goto cleanup; } // // Find the entry. // pEntry = FindConfigByName( argv[1] ); if (pEntry == NULL) { wprintf( L"tul: invalid property %s\n", argv[1] ); result = 1; goto cleanup; } // // Interpret it. // if (pEntry->Type == REG_DWORD) { if (argv[2][0] == L'-') longValue = wcstol( argv[2], NULL, 0 ); else longValue = (LONG) wcstoul( argv[2], NULL, 0 ); pNewValue = (CONST BYTE *)&longValue; newValueLength = sizeof(longValue); } else if (pEntry->Type == REG_QWORD) { // Hack: because link fails for Win32 and we don't need top 32 bits on Win32 #ifndef _WIN64 # define _wcstoi64(nptr, endptr, base) \ (__int64) wcstol((nptr), (endptr), (base)) # define _wcstoui64(nptr, endptr, base) \ (unsigned __int64) wcstoul((nptr), (endptr), (base)) #endif if (argv[2][0] == L'-') longlongValue = _wcstoi64( argv[2], NULL, 0 ); else longlongValue = (LONGLONG) _wcstoui64( argv[2], NULL, 0 ); pNewValue = (CONST BYTE *)&longlongValue; newValueLength = sizeof(longlongValue); } else { pNewValue = (CONST BYTE *)argv[2]; newValueLength = (DWORD)( wcslen( argv[2] ) + 1 ) * sizeof(argv[0][0]); } if (_wcsicmp( argv[2], L"/delete" ) == 0) { err = RegDeleteValue( key, pEntry->pConfigName ); } else { err = RegSetValueEx( key, pEntry->pConfigName, 0, pEntry->Type, pNewValue, newValueLength ); } if (err != NO_ERROR) { wprintf( L"tul: cannot write to registry, error %ld\n", err ); result = 1; goto cleanup; } // // Success! // DumpConfiguration( key ); result = 0; cleanup: if (key != NULL) { RegCloseKey( key ); } return result; } // DoConfig INT WINAPI DoFlags( IN HANDLE UlHandle, IN INT argc, IN PWSTR argv[] ) { HKEY key; INT result; LONG err; LONG i; ULONG flags; // // Setup locals so we know how to cleanup on exit. // key = NULL; // // Try to open the registry. // err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Services\\Http\\Parameters", 0, KEY_ALL_ACCESS, &key ); if (err != NO_ERROR) { wprintf( L"tul: cannot open registry, error %ld\n", err ); result = 1; goto cleanup; } // // Validate the arguments. // if (argc == 1) { DumpFlags( key ); result = 0; goto cleanup; } // // read each flag on the command line and set it // flags = 0; for (i = 1; i < argc; i++) { flags |= FindFlagByName(argv[i]); } err = RegSetValueEx( key, REGISTRY_DEBUG_FLAGS, 0, REG_DWORD, (LPBYTE)&flags, sizeof(flags) ); // // Success! // DumpFlags( key ); result = 0; cleanup: if (key != NULL) { RegCloseKey( key ); } return result; } // DoFlags