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.
 
 
 
 
 
 

2341 lines
74 KiB

/*
IisRestart.cpp
Implementation of CIisRestart ( IIisServiceControl )
FILE HISTORY:
Phillich 06-Oct-1998 Created
*/
#include "stdafx.h"
#include "IisRsta.h"
#include "IisRstam.h"
#include "IisRestart.h"
#include "common.h"
#define MAX_TASKS 8
#define SLEEP_INTERVAL 1000
typedef BOOL (*PFNQUERYSERVICECONFIG2)(SC_HANDLE,DWORD,LPBYTE,DWORD,LPDWORD) ;
typedef BOOL (*PFNCHANGESERVICECONFIG2)(SC_HANDLE,DWORD,LPVOID);
//
// control block for control command requests
//
typedef struct {
HRESULT hres;
LONG lRefCount;
DWORD dwCmd;
SC_HANDLE hServiceHandle;
} SERVICE_COMMAND_CONTROL_BLOCK;
//
// Global functions
//
BOOL
W3SVCandHTTPFilter(
DWORD currentIndex,
ENUM_SERVICE_STATUS* pessRoot,
DWORD dwNumServices
);
VOID EnableShutdownPrivilege(
VOID
);
HRESULT
EnumStartServices(
SC_HANDLE schSCM,
LPTSTR pszRoot,
DWORD dwTargetState,
LPBYTE abServiceList,
DWORD dwInitializeServiceListSize,
LPBYTE* ppbServiceList,
LPDWORD pdwNumServices,
BOOL fAddIisadmin
);
HRESULT
SerializeEnumServiceBuffer(
LPENUM_SERVICE_STATUS pessDependentServices,
DWORD dwNumServices,
LPBYTE pbBuffer,
DWORD dwBufferSize,
LPDWORD pdwMDRequiredBufferSize
);
BOOL
IsEnableRemote(
);
HRESULT
StopIIsAdmin(
DWORD dwTimeoutMsecs
);
HRESULT
StartStopAll(
LPTSTR pszRoot,
BOOL fStart,
DWORD dwTimeoutMsecs
);
BOOL
WaitForServiceStatus(
SC_HANDLE schDependent,
DWORD dwDesiredServiceState,
DWORD dwTimeoutMsecs
);
HRESULT
KillTaskByName(
LPTSTR pname,
LPSTR pszMandatoryModule
);
VOID
ReportStatus(
DWORD dwId,
DWORD dwStatus
);
HRESULT
SendControlToService(
SC_HANDLE hServiceHandle,
DWORD dwCmd,
LPDWORD pdwTimeoutOutMsecs
);
StartStopAllRecursive(
SC_HANDLE schSCM,
ENUM_SERVICE_STATUS* pessRoot,
DWORD dwNumServices,
BOOL fStart,
BOOL fForceDemandStart,
LPDWORD pdwTimeoutMsecs
);
HRESULT
WhoAmI(
LPTSTR* pPrincipal
);
BOOL
CloseSystemExceptionHandler(
LPCTSTR pszWindowName
);
/////////////////////////////////////////////////////////////////////////////
// CIisRestart
STDMETHODIMP
CIisRestart::Stop(
DWORD dwTimeoutMsecs,
DWORD dwForce
)
/*++
Stop
Stop all internet services ( services dependent on IISADMIN )
first using SCM then optionaly using TerminateProcess if failure
Arguments:
dwTimeoutMsecs - timeout for status check ( in ms )
dwForce - !0 to force TerminateProcess if failure to stop services using SCM
Returns:
ERROR_RESOURCE_DISABLED if remote access to IIisRestart disabled
ERROR_SERVICE_REQUEST_TIMEOUT if timeout waiting for all internet services status
to be stopped
otherwise COM status
--*/
{
HRESULT hres = S_OK;
if ( !IsEnableRemote() )
{
hres = HRESULT_FROM_WIN32( ERROR_RESOURCE_DISABLED );
}
else
{
//
// Always kill Dr Watson, as the Dr Watson window may be still present after inetinfo process
// was terminated after an exception, and in this case the Dr Watson process apparently still owns
// some sockets resources preventing inetinfo to properly restart ( specifically binding TCP/IP sockets
// fails during inetinfo restart )
//
KillTaskByName(_T("drwtsn32"), NULL);
hres = StartStopAll( _T("IISADMIN"), FALSE, dwTimeoutMsecs );
if ( dwForce && FAILED( hres ) )
{
ReportStatus( IRSTAM_KILL_DUE_TO_FORCE, hres );
hres = Kill();
}
}
ReportStatus( IRSTAM_STOP, hres );
return hres;
}
STDMETHODIMP
CIisRestart::Start(
DWORD dwTimeoutMsecs
)
/*++
Start
Start all internet services ( services dependent on IISADMIN )
using SCM
Arguments:
dwTimeoutMsecs - timeout for status check ( in ms )
Returns:
ERROR_RESOURCE_DISABLED if remote access to IIisRestart disabled
ERROR_SERVICE_REQUEST_TIMEOUT if timeout waiting for all internet services status
to be started
otherwise COM status
--*/
{
HRESULT hres = S_OK;
if ( !IsEnableRemote() )
{
hres = HRESULT_FROM_WIN32( ERROR_RESOURCE_DISABLED );
}
else
{
//
// In 6.0 we will use IIS Reset /Start to bring up the
// service again without stopping the services that can
// keep running. We still want to kill any dr watson's
// thou. This should be harmless on a regular start.
//
//
// Always kill Dr Watson, as the Dr Watson window may be still present after inetinfo process
// was terminated after an exception, and in this case the Dr Watson process apparently still owns
// some sockets resources preventing inetinfo to properly restart ( specifically binding TCP/IP sockets
// fails during inetinfo restart )
//
KillTaskByName(_T("drwtsn32"), NULL);
hres = StartStopAll( _T("IISADMIN"), TRUE, dwTimeoutMsecs );
}
ReportStatus( IRSTAM_START, hres );
return hres;
}
STDMETHODIMP
CIisRestart::Reboot(
DWORD dwTimeoutMsecs,
DWORD dwForceAppsClosed
)
/*++
Reboot
Reboot the computer
Arguments:
dwTimeoutMsecs - timeout for apps to be closed by user ( in ms )
dwForceAppsClosed - force apps to be closed if hung
Returns:
ERROR_RESOURCE_DISABLED if remote access to IIisRestart disabled
otherwise COM status
--*/
{
HRESULT hres = S_OK;
if ( !IsEnableRemote() )
{
hres = HRESULT_FROM_WIN32( ERROR_RESOURCE_DISABLED );
}
else
{
//
// If this fails then we will get an error back from ExitWindowsEx()
//
EnableShutdownPrivilege();
//
// Make sure we will always reboot even if process(es) stuck
//
TCHAR* pPrincipal;
TCHAR* pBuf;
//
// Format message to operator, includes name of user requesting shutdown.
//
if ( SUCCEEDED( hres = WhoAmI( &pPrincipal ) ) )
{
if ( FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_HMODULE|FORMAT_MESSAGE_ARGUMENT_ARRAY,
(LPCVOID)NULL, // no handle to module so this module's resource will be used.
IRSTAM_SYSSHUT,
0,
(LPTSTR)&pBuf,
1,
(va_list *)&pPrincipal ) )
{
if (InitiateSystemShutdownEx( NULL,
pBuf,
dwTimeoutMsecs/1000, // timeout in seconds
dwForceAppsClosed,
TRUE,
SHTDN_REASON_FLAG_PLANNED |
SHTDN_REASON_MAJOR_OPERATINGSYSTEM |
SHTDN_REASON_MINOR_RECONFIG) == 0)
{
hres = HRESULT_FROM_WIN32( GetLastError() );
}
LocalFree( (LPVOID)pBuf );
}
LocalFree( pPrincipal );
}
}
ReportStatus( IRSTAM_REBOOT, hres );
return hres;
}
STDMETHODIMP
CIisRestart::Kill(
)
/*++
Kill
Kill all internet services ( services dependent on IISADMIN )
using TerminateProcess()
Arguments:
None
Returns:
ERROR_RESOURCE_DISABLED if remote access to IIisRestart disabled
otherwise COM status
--*/
{
HRESULT hres = S_OK;
HRESULT hresReapply = S_OK;
HRESULT hresKill = S_OK;
BYTE abServiceList[2048];
LPBYTE pbServiceList = NULL;
DWORD dwNumServices = 0;
SC_HANDLE schSCM = NULL;
SC_HANDLE schSrv;
LPBYTE* ppInfo = NULL;
LPENUM_SERVICE_STATUS pessDependentServices = NULL;
DWORD dwNeeded;
HINSTANCE hAdvapi;
PFNQUERYSERVICECONFIG2 pfnQueryServiceConfig2 = NULL;
PFNCHANGESERVICECONFIG2 pfnChangeServiceConfig2 = NULL;
SERVICE_FAILURE_ACTIONS sfaNoAction;
SC_ACTION saNoAction[3];
DWORD i;
BYTE abTemp[64]; // work-around for NT5 bug
DWORD* adwPid = NULL;
SERVICE_STATUS_PROCESS status;
DWORD cbNeeded = 0;
if ( !IsEnableRemote() )
{
return HRESULT_FROM_WIN32( ERROR_RESOURCE_DISABLED );
}
//
// Take a snapshot of Restart configuration
// If unable to get ptr to service config2 API then consider this a success:
// there is nothing to preserve.
//
hAdvapi = LoadLibrary(_T("ADVAPI32.DLL"));
if ( hAdvapi != NULL )
{
pfnQueryServiceConfig2 = (PFNQUERYSERVICECONFIG2)GetProcAddress( hAdvapi, "QueryServiceConfig2W" );
pfnChangeServiceConfig2 = (PFNCHANGESERVICECONFIG2)GetProcAddress( hAdvapi, "ChangeServiceConfig2W" );
}
if ( pfnQueryServiceConfig2
&& pfnChangeServiceConfig2 )
{
schSCM = OpenSCManager(NULL,
NULL,
SC_MANAGER_ENUMERATE_SERVICE);
if ( schSCM == NULL )
{
hres = HRESULT_FROM_WIN32( GetLastError() );
}
else
{
//
// Setup control block for no restart action.
// We will replace existing actions with this control block
//
sfaNoAction.dwResetPeriod = INFINITE;
sfaNoAction.lpCommand = _T("");
sfaNoAction.lpRebootMsg = _T("");
sfaNoAction.cActions = 3;
sfaNoAction.lpsaActions = saNoAction;
saNoAction[0].Type = SC_ACTION_NONE;
saNoAction[0].Delay = 0;
saNoAction[1].Type = SC_ACTION_NONE;
saNoAction[1].Delay = 0;
saNoAction[2].Type = SC_ACTION_NONE;
saNoAction[2].Delay = 0;
//
// Enumerate all services dependent on IISADMIN ( including itself )
//
hres = EnumStartServices( schSCM,
_T("IISADMIN"),
SERVICE_STATE_ALL,
abServiceList,
sizeof(abServiceList),
&pbServiceList,
&dwNumServices,
TRUE );
if ( SUCCEEDED( hres ) )
{
//
// Store existing info in ppInfo array
//
adwPid = new DWORD[ dwNumServices ];
// we don't check the adwPid here because
// we will only use it below if we succeeded in allocating it.
if ( adwPid )
{
memset ( adwPid, 0, sizeof(DWORD) * dwNumServices );
}
ppInfo = (LPBYTE*)LocalAlloc( LMEM_FIXED|LMEM_ZEROINIT, sizeof(LPBYTE) * dwNumServices );
if ( ppInfo )
{
pessDependentServices = (LPENUM_SERVICE_STATUS)pbServiceList;
for ( i = 0 ;
(i < dwNumServices) && SUCCEEDED(hres) ;
++i )
{
schSrv = OpenService( schSCM,
pessDependentServices[i].lpServiceName,
SERVICE_QUERY_CONFIG |
SERVICE_CHANGE_CONFIG |
SERVICE_QUERY_STATUS );
if ( schSrv )
{
if ( adwPid )
{
if ( QueryServiceStatusEx( schSrv,
SC_STATUS_PROCESS_INFO,
(LPBYTE)&status,
sizeof( status ),
&cbNeeded ) )
{
adwPid[i] = status.dwProcessId;
}
}
//
// 1st query config size, then alloc buffer and retrieve
// config. Note than ppInfo[] may be NULL is no config
// associated with this service.
//
// WARNING: must specify ptr to writable buffer even if specified
// buffer size is 0 due to bug in NT5 implementation of
// QueryServiceConfig2. Not sure about minimum buffer size
// ( sizeof(SERVICE_FAILURE_ACTIONS) ) ?
//
if ( !pfnQueryServiceConfig2( schSrv,
SERVICE_CONFIG_FAILURE_ACTIONS,
(LPBYTE)abTemp,
0,
&dwNeeded ) )
{
if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
{
// ppInfo is an array of ptrs to bytes.
ppInfo[i] = (LPBYTE)LocalAlloc( LMEM_FIXED, dwNeeded );
if ( ppInfo[i] != NULL )
{
if ( !pfnQueryServiceConfig2( schSrv,
SERVICE_CONFIG_FAILURE_ACTIONS,
ppInfo[i],
dwNeeded,
&dwNeeded ) )
{
hres = HRESULT_FROM_WIN32( GetLastError() );
}
}
else
{
hres = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
}
else
{
hres = HRESULT_FROM_WIN32( GetLastError() );
}
}
if ( SUCCEEDED( hres ) )
{
if ( !pfnChangeServiceConfig2( schSrv,
SERVICE_CONFIG_FAILURE_ACTIONS,
&sfaNoAction ) )
{
hres = HRESULT_FROM_WIN32( GetLastError() );
}
}
CloseServiceHandle( schSrv );
}
else
{
hres = HRESULT_FROM_WIN32( GetLastError() );
}
} // Close of the for loop
}
else
{
hres = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
}
CloseServiceHandle( schSCM );
}
}
//
// Graceful exit failed, kill the IIS processes.
// First, kill inetinfo, then kill the WAM instances.
//
// Issue: Not sure why we do this, and if we need to do it for other exe's.
CloseSystemExceptionHandler( _T("inetinfo.exe") );
//
// Always kill Dr Watson, as the Dr Watson window may be still present after inetinfo process
// was terminated after an exception, and in this case the Dr Watson process apparently still owns
// some sockets resources preventing inetinfo to properly restart ( specifically binding TCP/IP sockets
// fails during inetinfo restart )
//
// If we removed all the SCM config above then
// we will attempt the kills
if ( SUCCEEDED ( hres ) )
{
HRESULT hresKillTemp = S_OK;
KillTaskByName(_T("drwtsn32"), NULL);
hresKillTemp = KillTaskByName(_T("SVCHOST"), "iisw3adm.dll"); // MTS WAM containers
if ( SUCCEEDED ( hresKill ) )
{
hresKill = hresKillTemp;
}
hresKillTemp = KillTaskByName(_T("W3WP"), NULL); // MTS WAM containers
if ( SUCCEEDED ( hresKill ) )
{
hresKill = hresKillTemp;
}
hresKillTemp = KillTaskByName(_T("INETINFO"), NULL);
if ( SUCCEEDED ( hresKill ) )
{
hresKill = hresKillTemp;
}
hresKillTemp = KillTaskByName(_T("DLLHOST"),"wam.dll"); // COM+ WAM Containers
if ( SUCCEEDED ( hresKill ) )
{
hresKill = hresKillTemp;
}
hresKillTemp = KillTaskByName(_T("ASPNET_WP"), NULL); // ASP + Processes.
if ( SUCCEEDED ( hresKill ) )
{
hresKill = hresKillTemp;
}
hresKillTemp = KillTaskByName(_T("DAVCDATA"), NULL); // Dav support process.
if ( SUCCEEDED ( hresKill ) )
{
hresKill = hresKillTemp;
}
// the following code will check the IISAdmin registry parameters for
// a KillProcsOnFailure MULTI_SZ. Any process names in this list will
// be killed.
HKEY hKey;
DWORD dwType;
DWORD dwSize;
TCHAR achBuffer[1024];
if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
TEXT("system\\CurrentControlSet\\services\\IISAdmin"),
0,
KEY_READ,
&hKey ) == ERROR_SUCCESS )
{
DWORD Success = ERROR_SUCCESS;
dwSize = sizeof( achBuffer );
Success = RegQueryValueEx( hKey,
TEXT("KillProcsOnFailure"),
0,
&dwType,
(LPBYTE)achBuffer,
&dwSize );
if ( Success == ERROR_SUCCESS &&
dwType == REG_MULTI_SZ &&
dwSize > 2 )
{
TCHAR *pT = achBuffer;
// parse the multisz. The format is NULL Terminated strings
// with an extra null terminator after the list.
while (*pT)
{
hresKillTemp = KillTaskByName(pT, NULL);
if ( SUCCEEDED ( hresKill ) )
{
hresKill = hresKillTemp;
}
// _tcsnbcnt figures out how many bytes are in the
// the first number of characters that _tcslen declares.
// So this calculation works out to be the number of bytes
// that we just looked at plus the null terminator.
dwSize -= (DWORD) _tcsnbcnt(pT,_tcslen(pT)) + sizeof(TCHAR);
pT += _tcslen(pT) + 1;
}
} // end of successfull opening of the key
RegCloseKey( hKey );
}
}
hresReapply = S_OK;
//
// Reapply restart configuration
//
//
// At this point pessDependentServices if pessDependentServices
// is null then we didn't touch the services so don't reset.
//
if ( ppInfo && pessDependentServices )
{
schSCM = OpenSCManager(NULL,
NULL,
SC_MANAGER_ENUMERATE_SERVICE);
if ( schSCM == NULL )
{
hresReapply = HRESULT_FROM_WIN32( GetLastError() );
}
else
{
for ( i = 0 ; i < dwNumServices ; ++i )
{
if ( ppInfo[i] )
{
schSrv = OpenService( schSCM,
pessDependentServices[i].lpServiceName,
SERVICE_QUERY_CONFIG |
SERVICE_CHANGE_CONFIG |
SERVICE_QUERY_STATUS |
SERVICE_START );
if ( schSrv )
{
// OutputDebugStringW(L"Service = ");
// OutputDebugStringW(pessDependentServices[i].lpServiceName);
// OutputDebugStringW(L"\n");
//
// If any of these things are not true, then we want to wait
// for the pid to change, or the service to become stopped
// before adding back in the changes that we removed. If these
// things are all true, then adding back in the actions won't really
// matter because the SCM won't do anything with the actions.
//
if ( ((LPSERVICE_FAILURE_ACTIONS) ppInfo[i])->cActions != 3 ||
((LPSERVICE_FAILURE_ACTIONS) ppInfo[i])->lpsaActions == NULL ||
((LPSERVICE_FAILURE_ACTIONS) ppInfo[i])->lpsaActions[0].Type != SC_ACTION_NONE ||
((LPSERVICE_FAILURE_ACTIONS) ppInfo[i])->lpsaActions[1].Type != SC_ACTION_NONE ||
((LPSERVICE_FAILURE_ACTIONS) ppInfo[i])->lpsaActions[2].Type != SC_ACTION_NONE )
{
// Wait for the service to be marked as stopped,
// just for a certain amount of time.
for ( DWORD x = 0; x < 10; x++ )
{
if ( QueryServiceStatusEx( schSrv,
SC_STATUS_PROCESS_INFO,
(LPBYTE)&status,
sizeof( status ),
&cbNeeded ) )
{
if ( adwPid && status.dwProcessId != adwPid[i] )
{
// pid didn't match, not the same
// process we were looking at when
// we went to kill.
break;
}
if ( status.dwCurrentState == SERVICE_STOPPED )
{
break;
}
}
else
{
break;
}
Sleep ( 100 );
}
}
if ( !pfnChangeServiceConfig2( schSrv,
SERVICE_CONFIG_FAILURE_ACTIONS,
ppInfo[i] ) )
{
hresReapply = HRESULT_FROM_WIN32( GetLastError() );
}
CloseServiceHandle( schSrv );
}
else
{
hresReapply = HRESULT_FROM_WIN32( GetLastError() );
}
}
}
CloseServiceHandle( schSCM );
}
}
// If we tried the kills and they failed then
// we want to report back that error. Note that
// we check that the setup phase worked before
// we even try the kill phase, so we can just
// assume that.
if ( FAILED(hresKill) )
{
hres = hresKill;
}
if ( SUCCEEDED(hres) && FAILED(hresReapply) )
{
hres = hresReapply;
}
ReportStatus( IRSTAM_KILL, hres );
if ( hAdvapi )
{
FreeLibrary( hAdvapi );
}
//
// cleanup
//
if ( ppInfo )
{
for ( i = 0 ; i < dwNumServices ; ++i )
{
if ( ppInfo[i] )
{
LocalFree( ppInfo[i] );
}
}
LocalFree( ppInfo );
}
if ( pbServiceList != NULL
&& pbServiceList != abServiceList )
{
LocalFree( pbServiceList );
}
if ( adwPid )
{
delete [] adwPid;
}
return hres;
}
//
// Helper functions
//
VOID
EnableShutdownPrivilege(
VOID
)
/*++
EnableShutdownPrivilege
Enable shutdown privilege ( required to call ExitWindowsEx )
Arguments:
None
Returns:
Nothing. If error enabling the privilege the dependent operation
will fail.
--*/
{
HANDLE ProcessHandle;
HANDLE TokenHandle = NULL;
BOOL Result;
LUID ShutdownValue;
TOKEN_PRIVILEGES TokenPrivileges;
ProcessHandle = OpenProcess(
PROCESS_QUERY_INFORMATION,
FALSE,
GetCurrentProcessId()
);
if ( ProcessHandle == NULL ) {
//
// This should not happen
//
goto Cleanup;
}
Result = OpenProcessToken (
ProcessHandle,
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&TokenHandle
);
if ( !Result ) {
//
// This should not happen
//
goto Cleanup;
}
//
// Find out the value of Shutdown privilege
//
Result = LookupPrivilegeValue(
NULL,
SE_SHUTDOWN_NAME,
&ShutdownValue
);
if ( !Result ) {
goto Cleanup;
}
//
// Set up the privilege set we will need
//
TokenPrivileges.PrivilegeCount = 1;
TokenPrivileges.Privileges[0].Luid = ShutdownValue;
TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
(VOID) AdjustTokenPrivileges (
TokenHandle,
FALSE,
&TokenPrivileges,
sizeof(TokenPrivileges),
NULL,
NULL
);
Cleanup:
if ( TokenHandle )
{
CloseHandle( TokenHandle );
}
if ( ProcessHandle )
{
CloseHandle( ProcessHandle );
}
}
HRESULT
StartStopAll(
LPTSTR pszRoot,
BOOL fStart,
DWORD dwTimeoutMsecs
)
/*++
StartStopAll
start or stop services dependency tree starting with specified root service
Arguments:
pszRoot - root of the service tree
fStart - TRUE to start services, FALSE to stop
dwTimeoutMsecs - timeout for status check ( in ms )
Returns:
COM status
--*/
{
SC_HANDLE schSCM = NULL;
SC_HANDLE schRoot = NULL;
HRESULT hresReturn = S_OK;
ENUM_SERVICE_STATUS ess;
schSCM = OpenSCManager(NULL,
NULL,
SC_MANAGER_CONNECT);
if ( schSCM == NULL )
{
hresReturn = HRESULT_FROM_WIN32( GetLastError() );
}
else
{
schRoot = OpenService( schSCM,
pszRoot,
SERVICE_ALL_ACCESS );
if ( schRoot != NULL )
{
if ( !QueryServiceStatus( schRoot, &ess.ServiceStatus ) )
{
hresReturn = HRESULT_FROM_WIN32( GetLastError() );
}
CloseServiceHandle( schRoot );
if ( SUCCEEDED( hresReturn )
&& ( fStart
|| ess.ServiceStatus.dwCurrentState != SERVICE_STOPPED) )
{
ess.lpServiceName = pszRoot;
// if it's stopped, then whack the dllhosts that have wam.dll loaded
if (ess.ServiceStatus.dwCurrentState == SERVICE_STOPPED)
{
KillTaskByName(_T("DLLHOST"),"wam.dll"); // COM+ WAM Containers
}
hresReturn = StartStopAllRecursive( schSCM, &ess, 1, fStart, TRUE, &dwTimeoutMsecs );
}
// check out the current state of the service
schRoot = OpenService( schSCM, pszRoot, SERVICE_ALL_ACCESS );
if ( schRoot != NULL )
{
if ( QueryServiceStatus( schRoot, &ess.ServiceStatus ) )
{
// if it's stopped, then whack the dllhosts that have wam.dll loaded
if (ess.ServiceStatus.dwCurrentState == SERVICE_STOPPED)
{
KillTaskByName(_T("DLLHOST"),"wam.dll"); // COM+ WAM Containers
}
}
CloseServiceHandle( schRoot );
}
}
else
{
hresReturn = HRESULT_FROM_WIN32( GetLastError() );
}
CloseServiceHandle( schSCM );
}
return hresReturn;
}
StartStopAllRecursive(
SC_HANDLE schSCM,
ENUM_SERVICE_STATUS* pessRoot,
DWORD dwNumServices,
BOOL fStart,
BOOL fForceDemandStart,
LPDWORD pdwTimeoutMsecs
)
/*++
StartStopAllRecursive
start or stop services dependency tree starting with specified root service
Arguments:
schSCM - handle to SCM
pessRoot - list of services to start/stop recursively
fStart - TRUE to start services, FALSE to stop
fForceDemandStart - for start requests: TRUE to force start
if SERVICE_DEMAND_START. Otherwise only start if service
is auto start ( including boot & system start )
dwTimeoutMsecs - timeout for status check ( in ms )
Returns:
COM status
--*/
{
DWORD dwBytesNeeded;
DWORD dwNumRecServices = 0;
HRESULT hresReturn = S_OK;
BYTE abServiceList[2048];
LPBYTE pbServiceList = NULL;
BYTE abServiceConfig[1024];
LPQUERY_SERVICE_CONFIG pServiceConfig = NULL;
SC_HANDLE* phServiceHandle = NULL;
DWORD i;
DWORD dwServiceConfigSize;
SERVICE_STATUS ServiceStatus;
DWORD dwSleepInterval = SLEEP_INTERVAL;
if ( dwNumServices != 0 &&
( pessRoot == NULL ||
pdwTimeoutMsecs == NULL ) )
{
return E_INVALIDARG;
}
if ( (phServiceHandle = (SC_HANDLE*)LocalAlloc( LMEM_FIXED|LMEM_ZEROINIT,
dwNumServices * sizeof(SC_HANDLE) )) == NULL )
{
hresReturn = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
if ( SUCCEEDED(hresReturn) )
{
//
// All services will be started/stopped at once
// then periodically checked for status until all of them are running/stopped
// or some error occured or timeout
//
if ( dwNumServices != 0 )
{
pServiceConfig = (LPQUERY_SERVICE_CONFIG)abServiceConfig;
dwServiceConfigSize = sizeof( abServiceConfig );
//
// Open handles and send service control start command
//
for ( i = 0 ;
i < dwNumServices && SUCCEEDED(hresReturn) ;
i++)
{
//
// Send command to Services
//
phServiceHandle[i] = OpenService( schSCM,
pessRoot[i].lpServiceName,
SERVICE_ALL_ACCESS );
if ( phServiceHandle[i] != NULL )
{
if ( fStart )
{
//
// Query service config to check if service should be started
// based on its Start Type.
//
if ( !QueryServiceConfig( phServiceHandle[i],
(LPQUERY_SERVICE_CONFIG)abServiceConfig,
dwServiceConfigSize,
&dwBytesNeeded ) )
{
if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
{
// If we are re-allocating and we all ready allocated once
// before first free up the memory.
if ( pServiceConfig != (LPQUERY_SERVICE_CONFIG) abServiceConfig )
{
LocalFree( pServiceConfig );
}
if ( (pServiceConfig = (LPQUERY_SERVICE_CONFIG)LocalAlloc(
LMEM_FIXED,
dwBytesNeeded )) == NULL )
{
hresReturn = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
else
{
dwServiceConfigSize = dwBytesNeeded;
if ( !QueryServiceConfig( phServiceHandle[i],
(LPQUERY_SERVICE_CONFIG)pServiceConfig,
dwServiceConfigSize,
&dwBytesNeeded ) )
{
hresReturn = HRESULT_FROM_WIN32( GetLastError() );
}
}
}
else
{
hresReturn = HRESULT_FROM_WIN32( GetLastError() );
}
}
if ( SUCCEEDED(hresReturn) )
{
//
// Check if service auto start except if fForceDemandStart
// specified. ForceDemandStart will only be specified for
// the service that the command is directly issued on. This
// means that it will only be specified for IISADMIN.
//
if ( ( fForceDemandStart && pServiceConfig->dwStartType == SERVICE_DEMAND_START )
|| ( pServiceConfig->dwStartType == SERVICE_BOOT_START ||
pServiceConfig->dwStartType == SERVICE_SYSTEM_START ||
pServiceConfig->dwStartType == SERVICE_AUTO_START ) )
{
StartService( phServiceHandle[i], 0, NULL );
//
// Ask for only the services that are inactive. So, for instance,
// if we are attempting to restart the iisadmin service,
// and W3SVC is still active, we won't send it a command to restart.
//
hresReturn = EnumStartServices( schSCM,
pessRoot[i].lpServiceName,
SERVICE_INACTIVE,
abServiceList,
sizeof(abServiceList),
&pbServiceList,
&dwNumRecServices,
FALSE );
if ( SUCCEEDED( hresReturn ) )
{
hresReturn = StartStopAllRecursive( schSCM,
(ENUM_SERVICE_STATUS*)pbServiceList,
dwNumRecServices,
fStart,
FALSE,
pdwTimeoutMsecs );
if ( pbServiceList != NULL
&& pbServiceList != abServiceList )
{
LocalFree( pbServiceList );
}
}
}
else
{
//
// Don't want to start this service, so mark it
// as already running
//
if (wcscmp(pessRoot[i].lpServiceName,_T("IISADMIN")) == 0)
{
hresReturn = HRESULT_FROM_WIN32(ERROR_SERVICE_NOT_ACTIVE);
}
else
{
pessRoot[i].ServiceStatus.dwCurrentState = SERVICE_RUNNING;
}
}
}
}
else // handle stopping the service
{
if ( W3SVCandHTTPFilter(i, pessRoot, dwNumServices) )
{
continue;
}
// Remember if the service was stopped to start with.
BOOL fServiceWasStoppedToStartWith = ( pessRoot[i].ServiceStatus.dwCurrentState == SERVICE_STOPPED );
// We will also need to stop dependent services if the are started all ready.
BOOL fHasDependentServices = FALSE;
if ( !fServiceWasStoppedToStartWith )
{
//
// if the service was not stopped to start with
// we need to tell the service to stop.
//
hresReturn = SendControlToService( phServiceHandle[i],
SERVICE_CONTROL_STOP,
pdwTimeoutMsecs );
if ( hresReturn == HRESULT_FROM_WIN32( ERROR_SERVICE_REQUEST_TIMEOUT ) )
{
//
// WARNING!
//
// We're in trouble. Service did not respond in a timely fashion,
// and further attempt to use this handle ( including closing it )
// will also hang, so cancel the handle and leak it
//
phServiceHandle[i] = NULL;
}
else if ( hresReturn == HRESULT_FROM_WIN32( ERROR_DEPENDENT_SERVICES_RUNNING ) )
{
fHasDependentServices = TRUE;
}
}
//
// If it was stopped to start with, or if it was not but it couldn't
// be stopped because it has dependent services. Go ahead and stop
// the dependent services.
//
if ( fHasDependentServices || fServiceWasStoppedToStartWith )
{
//
// Get the services that are active because we
// are only interested in stopping services that
// are actually running.
//
hresReturn = EnumStartServices( schSCM,
pessRoot[i].lpServiceName,
SERVICE_ACTIVE,
abServiceList,
sizeof(abServiceList),
&pbServiceList,
&dwNumRecServices,
FALSE );
if ( SUCCEEDED( hresReturn ) )
{
hresReturn = StartStopAllRecursive( schSCM,
(ENUM_SERVICE_STATUS*)pbServiceList,
dwNumRecServices,
fStart,
FALSE,
pdwTimeoutMsecs );
if ( pbServiceList != NULL
&& pbServiceList != abServiceList )
{
LocalFree( pbServiceList );
}
}
if ( SUCCEEDED( hresReturn ) )
{
//
// If the service itself is not all ready stopped, then stop
// the service. It could be that it is stopped ( due to a crash )
// and the other services that were dependent on them are still
// running.
//
if ( !fServiceWasStoppedToStartWith )
{
hresReturn = SendControlToService( phServiceHandle[i],
SERVICE_CONTROL_STOP,
pdwTimeoutMsecs );
// if we can hang above we could hang here...
if ( hresReturn == HRESULT_FROM_WIN32( ERROR_SERVICE_REQUEST_TIMEOUT ) )
{
//
// WARNING!
//
// We're in trouble. Service did not respond in a timely fashion,
// and further attempt to use this handle ( including closing it )
// will also hang, so cancel the handle and leak it
//
phServiceHandle[i] = NULL;
}
}
}
}
if ( FAILED( hresReturn ) )
{
break;
}
} // end of stopping code
} // end of valid service handle
} // end of loop
//
// Check service running
//
if ( (*pdwTimeoutMsecs < dwSleepInterval) && *pdwTimeoutMsecs )
{
dwSleepInterval = *pdwTimeoutMsecs;
}
for ( ;
SUCCEEDED( hresReturn );
)
{
for ( i = 0 ;
i < dwNumServices;
i++)
{
//
// Only query status for services known to be not running
//
if ( pessRoot[i].ServiceStatus.dwCurrentState
!= (DWORD)(fStart ? SERVICE_RUNNING : SERVICE_STOPPED) )
{
if ( QueryServiceStatus( phServiceHandle[i], &ServiceStatus ) )
{
//
// remember status
//
pessRoot[i].ServiceStatus.dwCurrentState = ServiceStatus.dwCurrentState;
if ( fStart && ServiceStatus.dwCurrentState == SERVICE_STOPPED )
{
//
// Service died during startup. no point keeping polling
// for service state : return an error
//
hresReturn = HRESULT_FROM_WIN32( ERROR_SERVICE_NOT_ACTIVE );
break;
}
if ( ServiceStatus.dwCurrentState != (DWORD)(fStart ? SERVICE_RUNNING : SERVICE_STOPPED) )
{
//
// will keep looping waiting for target service state
//
break;
}
}
else
{
hresReturn = HRESULT_FROM_WIN32( GetLastError() );
break;
}
}
}
//
// if we did not checked all services then at least one of them
// is not running ( or some error occured )
//
if ( SUCCEEDED( hresReturn ) && i != dwNumServices )
{
if ( dwSleepInterval > *pdwTimeoutMsecs )
{
hresReturn = HRESULT_FROM_WIN32( ERROR_SERVICE_REQUEST_TIMEOUT );
}
else
{
Sleep( dwSleepInterval );
*pdwTimeoutMsecs -= dwSleepInterval;
}
}
else
{
break;
}
}
//
// close service handles
//
for ( i = 0 ;
i < dwNumServices;
i++)
{
if ( phServiceHandle[i] != NULL )
{
CloseServiceHandle( phServiceHandle[i] );
}
}
}
LocalFree( phServiceHandle );
}
if ( pServiceConfig != NULL
&& pServiceConfig != (LPQUERY_SERVICE_CONFIG)abServiceConfig )
{
LocalFree( pServiceConfig );
}
return hresReturn;
}
extern "C"
DWORD WINAPI
ControlServiceThread(
LPVOID p
)
/*++
ControlServiceThread
Send a command to a service
Arguments:
p - ptr to SERVICE_COMMAND_CONTROL_BLOCK
Returns:
0
--*/
{
SERVICE_STATUS ssStatus;
SERVICE_COMMAND_CONTROL_BLOCK* pCB = (SERVICE_COMMAND_CONTROL_BLOCK*)p;
if ( pCB == NULL )
{
return ERROR_INVALID_PARAMETER;
}
if ( !ControlService( pCB->hServiceHandle, pCB->dwCmd, &ssStatus ) )
{
DWORD err = GetLastError();
// do not report error if try to stop a stopped service
if (err == ERROR_SERVICE_NOT_ACTIVE && pCB->dwCmd == SERVICE_CONTROL_STOP)
{
pCB->hres = S_OK;
}
else
{
pCB->hres = HRESULT_FROM_WIN32( err );
}
}
else
{
pCB->hres = S_OK;
}
//nasty side affect of this function, if the count hit's
//zero we delete it here. Leaving P to point off, however
//p does hold a ref count for itself so this should actually work fine.
if ( InterlockedDecrement( &pCB->lRefCount ) == 0 )
{
delete pCB;
}
return ERROR_SUCCESS;
}
HRESULT
SendControlToService(
SC_HANDLE hServiceHandle,
DWORD dwCmd,
LPDWORD pdwTimeoutMsecs
)
/*++
ControlServiceThread
Send a command to a service with timeout
Arguments:
hServiceHandle - service to control
dwCmd - command to send to service
pdwTimeoutMsecs - timeout ( in ms ). updated on output based on time
spent waiting for service status
Returns:
ERROR_SERVICE_REQUEST_TIMEOUT if timeout
otherwise COM status
--*/
{
HANDLE hT;
DWORD dwID;
DWORD dwBefore;
DWORD dwAfter;
SERVICE_COMMAND_CONTROL_BLOCK* pCB;
DWORD dwTimeoutMsecs;
HRESULT hres;
if ( pdwTimeoutMsecs == NULL )
{
return E_INVALIDARG;
}
dwTimeoutMsecs = *pdwTimeoutMsecs;
//
// Default timeout for ControlService is 120s, which is too long for us
// so we create a thread to call ControlService and wait for thread
// termination.
// Communication between threads is handled by a refcounted control block
//
pCB = new SERVICE_COMMAND_CONTROL_BLOCK;
if ( pCB != NULL )
{
pCB->lRefCount = 2; // 1 for caller, 1 for callee
pCB->dwCmd = dwCmd;
pCB->hServiceHandle = hServiceHandle;
pCB->hres = S_OK;
dwBefore = GetTickCount();
hT = CreateThread( NULL,
0,
(LPTHREAD_START_ROUTINE)ControlServiceThread,
(LPVOID)pCB,
0,
&dwID );
if ( hT != NULL )
{
if ( WaitForSingleObject( hT, dwTimeoutMsecs ) == WAIT_OBJECT_0 )
{
hres = pCB->hres;
}
else
{
hres = HRESULT_FROM_WIN32( ERROR_SERVICE_REQUEST_TIMEOUT );
}
CloseHandle( hT );
if ( InterlockedDecrement( &pCB->lRefCount ) == 0 )
{
delete pCB;
}
//
// Update caller's timeout
//
dwAfter = GetTickCount();
if ( dwAfter > dwBefore )
{
if ( dwAfter - dwBefore <= dwTimeoutMsecs )
{
*pdwTimeoutMsecs -= dwAfter - dwBefore;
}
else
{
*pdwTimeoutMsecs = 0;
}
}
}
else
{
delete pCB;
hres = HRESULT_FROM_WIN32( GetLastError() );
}
}
else
{
hres = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
return hres;
}
HRESULT
SerializeEnumServiceBuffer(
LPENUM_SERVICE_STATUS pessDependentServices,
DWORD dwNumServices,
LPBYTE pbBuffer,
DWORD dwBufferSize,
LPDWORD pdwMDRequiredBufferSize
)
/*++
SerializeEnumServiceBuffer
Serialize array of ENUM_SERVICE_STATUS to buffer,
replacing ptr by offset in buffer
Arguments:
pessDependentServices - array of ENUM_SERVICE_STATUS to serialize
dwNumServices - # of entries in pessDependentServices
pbBuffer - buffer filled with serialized status as array of SERIALIZED_ENUM_SERVICE_STATUS
dwBufferSize - maximum size of pbBuffer
pdwMDRequiredBufferSize - updated with required size if dwBufferSize too small
Returns:
ERROR_INSUFFICIENT_BUFFER if dwBufferSize too small
otherwise COM status
--*/
{
HRESULT hresReturn = S_OK;
DWORD dwMinSize = 0;
UINT i;
if ( dwNumServices != 0 &&
pessDependentServices == NULL )
{
return E_INVALIDARG;
}
if ( !pbBuffer )
{
dwBufferSize = 0;
}
//
// size of output buffer is based on size of array of SERIALIZED_ENUM_SERVICE_STATUS
// plus size of all strings : service name & display name for each entry
//
dwMinSize = sizeof(SERIALIZED_ENUM_SERVICE_STATUS) * dwNumServices;
for ( i = 0 ;
i < dwNumServices ;
++i )
{
UINT cServiceName = (DWORD) _tcslen( pessDependentServices[i].lpServiceName ) + 1;
UINT cDisplayName = (DWORD) _tcslen( pessDependentServices[i].lpDisplayName ) + 1;
//
// do not update if output buffer is too small, but keep looping to determine
// total size
//
if ( dwBufferSize >= dwMinSize + (cServiceName + cDisplayName) * sizeof(TCHAR) )
{
//
// copy service status as is
//
((SERIALIZED_ENUM_SERVICE_STATUS*)pbBuffer)[i].ServiceStatus =
pessDependentServices[i].ServiceStatus;
//
// copy string and convert ptr to string to index in output buffer
//
memcpy( pbBuffer + dwMinSize, pessDependentServices[i].lpServiceName, cServiceName * sizeof(TCHAR) );
((SERIALIZED_ENUM_SERVICE_STATUS*)pbBuffer)[i].iServiceName = dwMinSize ;
memcpy( pbBuffer + dwMinSize + cServiceName * sizeof(TCHAR), pessDependentServices[i].lpDisplayName, cDisplayName * sizeof(TCHAR) );
((SERIALIZED_ENUM_SERVICE_STATUS*)pbBuffer)[i].iDisplayName = dwMinSize + cServiceName * sizeof(TCHAR) ;
}
dwMinSize += (cServiceName + cDisplayName) * sizeof(TCHAR) ;
}
if ( dwBufferSize < dwMinSize )
{
if ( pdwMDRequiredBufferSize )
{
*pdwMDRequiredBufferSize = dwMinSize;
}
hresReturn = HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER );
}
return hresReturn;
}
STDMETHODIMP
CIisRestart::Status(
DWORD dwBufferSize,
unsigned char * pbBuffer,
DWORD * pdwMDRequiredBufferSize,
DWORD * pdwNumServices
)
/*++
Status
Return status of all internet services as array of ENUM_SERVICE_STATUS
Arguments:
dwBufferSize - maximum size of pbBuffer
pbBuffer - buffer filled with serialized status as array of SERIALIZED_ENUM_SERVICE_STATUS
pdwMDRequiredBufferSize - updated with required size if dwBufferSize too small
pdwNumServices - updated with number of entries stored in pbBuffer
Returns:
ERROR_RESOURCE_DISABLED if access to restart commands disabled
ERROR_INSUFFICIENT_BUFFER if dwBufferSize too small
otherwise COM status
--*/
{
SC_HANDLE schSCM = NULL;
HRESULT hresReturn = E_FAIL;
BYTE abServiceList[2048];
LPBYTE pbServiceList = NULL;
LPENUM_SERVICE_STATUS pessDependentServices;
if ( pdwNumServices == NULL )
{
return E_INVALIDARG;
}
if ( !IsEnableRemote() )
{
hresReturn = HRESULT_FROM_WIN32( ERROR_RESOURCE_DISABLED );
}
else
{
schSCM = OpenSCManager(NULL,
NULL,
SC_MANAGER_CONNECT);
if ( schSCM == NULL )
{
hresReturn = HRESULT_FROM_WIN32( GetLastError() );
}
else
{
hresReturn = EnumStartServices( schSCM,
_T("IISADMIN"),
SERVICE_STATE_ALL,
abServiceList,
sizeof(abServiceList),
&pbServiceList,
pdwNumServices,
FALSE );
if ( SUCCEEDED(hresReturn) )
{
pessDependentServices = (LPENUM_SERVICE_STATUS)pbServiceList;
hresReturn = SerializeEnumServiceBuffer( (LPENUM_SERVICE_STATUS)pbServiceList,
*pdwNumServices,
pbBuffer,
dwBufferSize,
pdwMDRequiredBufferSize );
if ( pbServiceList != NULL
&& pbServiceList != abServiceList )
{
LocalFree( pbServiceList );
}
}
CloseServiceHandle(schSCM);
}
}
return hresReturn;
}
HRESULT
EnumStartServices(
SC_HANDLE schSCM,
LPTSTR pszRoot,
DWORD dwTargetState,
LPBYTE abServiceList,
DWORD dwInitializeServiceListSize,
LPBYTE* ppbServiceList,
LPDWORD pdwNumServices,
BOOL fAddIisadmin
)
/*++
EnumStartServices
Enumerate dependent services to output buffer as array of ENUM_SERVICE_STATUS
Arguments:
schSCM - handle to SCM
pszRoot - service for which to enumerate dependencies
dwTargetState - dwServiceState for call to EnumDependentServices()
abServiceList - initial output buffer
dwInitializeServiceListSize - maximum size of abServiceList
ppbServiceList - updated with output buffer, may be abServiceList if long enough
otherwise returned buffer is to be freed using LocalFree()
pdwNumServices - updated with number of entries stored in pbBuffer
fAddIisadmin - TRUE to add IISADMIN to list of dependent services
Returns:
COM status
--*/
{
HRESULT hresReturn = S_OK;
SC_HANDLE schIISADMIN = NULL;
DWORD dwBytesNeeded;
DWORD dwAddSize = 0;
DWORD dwOffsetSize = 0;
if ( ppbServiceList == NULL ||
pdwNumServices == NULL )
{
return E_INVALIDARG;
}
*ppbServiceList = NULL;
schIISADMIN = OpenService(schSCM,
pszRoot,
STANDARD_RIGHTS_REQUIRED |
SERVICE_ENUMERATE_DEPENDENTS);
if (schIISADMIN == NULL)
{
hresReturn = HRESULT_FROM_WIN32(GetLastError());
}
else
{
if ( fAddIisadmin )
{
//
// if initial size too small for Iisadmin description then fail
//
dwOffsetSize = sizeof(ENUM_SERVICE_STATUS );
dwAddSize = dwOffsetSize;
if ( dwAddSize > dwInitializeServiceListSize )
{
hresReturn = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto Cleanup;
}
//
// Use global static name for IISADMIN, no need to copy it to output buffer
//
((LPENUM_SERVICE_STATUS)abServiceList)->lpDisplayName = _T("IISADMIN");
((LPENUM_SERVICE_STATUS)abServiceList)->lpServiceName = _T("IISADMIN");
//
// don't want to check service status at this point as it may be stuck
// so assume RUNNING.
//
((LPENUM_SERVICE_STATUS)abServiceList)->ServiceStatus.dwCurrentState = SERVICE_RUNNING;
}
if (!EnumDependentServices( schIISADMIN,
dwTargetState,
(LPENUM_SERVICE_STATUS)(abServiceList + dwOffsetSize),
dwInitializeServiceListSize - dwAddSize,
&dwBytesNeeded,
pdwNumServices))
{
if (GetLastError() == ERROR_MORE_DATA)
{
if ( (*ppbServiceList = (LPBYTE)LocalAlloc( LMEM_FIXED,
dwBytesNeeded + dwAddSize )) == NULL )
{
hresReturn = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
else
{
// dwOffsetSize is set to what dwAddSize was set to, so it is fine
// to memcpy this data, since we allocated more than dwAddSize above.
memcpy( *ppbServiceList, abServiceList, dwOffsetSize );
if (!EnumDependentServices( schIISADMIN,
SERVICE_INACTIVE,
(LPENUM_SERVICE_STATUS)(*ppbServiceList + dwOffsetSize),
dwBytesNeeded,
&dwBytesNeeded,
pdwNumServices))
{
hresReturn = HRESULT_FROM_WIN32( GetLastError() );
LocalFree( *ppbServiceList );
*ppbServiceList = NULL;
}
}
}
else
{
hresReturn = HRESULT_FROM_WIN32( GetLastError() );
}
}
else
{
*ppbServiceList = abServiceList;
}
}
Cleanup:
if ( schIISADMIN )
{
CloseServiceHandle( schIISADMIN );
}
if ( fAddIisadmin && SUCCEEDED( hresReturn ) )
{
++*pdwNumServices;
}
return hresReturn;
}
BOOL
IsEnableRemote(
)
/*++
IsEnableRemote
Check if restart I/F enabled
( based on HKLM\SOFTWARE\Microsoft\INetStp::EnableRestart::REG_DWORD )
Arguments:
None
Returns:
TRUE if enabled, otherwise FALSE
--*/
{
BOOL fSt = FALSE;
HKEY hKey;
DWORD dwValue;
DWORD dwType;
DWORD dwSize;
if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
TEXT("SOFTWARE\\Microsoft\\INetStp"),
0,
KEY_READ,
&hKey ) == ERROR_SUCCESS )
{
dwSize = sizeof( dwValue );
if ( RegQueryValueEx( hKey,
TEXT("EnableRestart"),
0,
&dwType,
(LPBYTE)&dwValue,
&dwSize ) == ERROR_SUCCESS )
{
if ( dwType == REG_DWORD )
{
fSt = dwValue == 1;
}
else
{
fSt = TRUE;
}
}
else
{
fSt = TRUE;
}
RegCloseKey( hKey );
}
return fSt;
}
BOOL
CloseSystemExceptionHandler(
LPCTSTR pszWindowName
)
/*++
CloseSystemExceptionHandler
Send a close ( e.g. terminate apps without debugging ) command to the window
created by NT when a debugger is not configured to automatically start after app exception.
This window stays on screen until interactive user select either OK or debug app, which is
a problem for automated restart.
So we locate this window and send it a command requesting stop w/o debugging.
We locate the window by enumerating all windows and checking for window name beginning with the name
of the application that raised an exception, e.g. "inetinfo.exe"
Arguments:
pszWindowName - window name where to send terminate command
Returns:
TRUE if SUCCESS, otherwise FALSE
--*/
{
BOOL fSt = TRUE;
DWORD dwPid = 0;
HWND hwnd;
GetPidFromTitle( &dwPid, &hwnd, pszWindowName );
if ( dwPid )
{
//
// WARNING: major hack: turns out that WM_COMMAND 1 is the command to send to
// the exception handler to ask it to close application w/o debugging
// This works for NT5, may change in the future...
//
PostMessage( hwnd, WM_COMMAND, 1, 0 );
Sleep( 1000 );
}
else
{
fSt = TRUE;
}
return fSt;
}
HRESULT
KillTaskByName(
LPTSTR pname,
LPSTR pszMandatoryModule
)
/*++
KillTaskByName
Kill a process by name
Most of the code was taken from the Platform SDK kill,c sample, and
utilizes the common.c module included in this project.
Works only on NT platforms ( NOT Win 9x )
Arguments:
pname - name of process to kill ( name of executable w/o extension )
pszMandatoryModule - module name to look for, e.g. "wam.dll"
can be NULL for unconditional kill
Returns:
COM status
--*/
{
HRESULT hres = S_OK;
//
// Obtain the ability to manipulate other processes
//
EnableDebugPrivNT();
//
// get the task list for the system
//
hres = KillTask( pname, pszMandatoryModule );
return hres;
}
VOID
ReportStatus(
DWORD dwId,
DWORD dwStatus
)
/*++
ReportStatus
Log status event
Arguments:
dwId - ID of event to log ( source is "IISCTLS" , SYSTEM log )
dwStatus - status of operation ( HRESULT )
Returns:
Nothing
--*/
{
HANDLE hLog;
LPTSTR aParams[1];
if ( (hLog = RegisterEventSource( NULL, _T("IISCTLS") )) != NULL )
{
if ( SUCCEEDED( WhoAmI( aParams + 0 ) ) )
{
ReportEvent( hLog,
EVENTLOG_INFORMATION_TYPE,
0,
dwId,
NULL,
1,
sizeof(DWORD),
(LPCTSTR*)aParams,
&dwStatus );
LocalFree( aParams[0] );
}
DeregisterEventSource( hLog );
}
}
HRESULT
WhoAmI(
LPTSTR* ppPrincipal
)
/*++
WhoAmI
Return currently impersonated user
As this is a COM server running under the identity of the invoker
this means we access the process token. So we might end up getting
the wrong user name if the object is invoked in close succession
( within the 5s server exit timeout ) by different users.
Arguments:
ppPrincipal - update with ptr to string containing user name ( domain\acct )
must be freed using LocalFree()
Returns:
Error status
--*/
{
TCHAR* pPrincipal;
TCHAR achUserName[512];
TCHAR achDomain[512];
DWORD dwLen;
DWORD dwDomainLen;
SID_NAME_USE SIDtype = SidTypeUser;
HRESULT hres = E_FAIL;
//
// So we have to access the process token and retrieve account & user name
// by using LookupAccountSid()
//
HANDLE hAccTok = NULL;
if ( OpenProcessToken( GetCurrentProcess(),
TOKEN_EXECUTE|TOKEN_QUERY,
&hAccTok ) )
{
BYTE abSidAndInfo[512];
DWORD dwReq;
//
// provide a reasonably sized buffer. If this fails we don't
// retry with a bigger one.
//
if ( GetTokenInformation( hAccTok,
TokenUser,
(LPVOID)abSidAndInfo,
sizeof(abSidAndInfo),
&dwReq) )
{
dwLen = sizeof( achUserName ) / sizeof(TCHAR);
dwDomainLen = sizeof(achDomain) / sizeof(TCHAR);
//
// provide a reasonably sized buffer. If this fails we don't
// retry with a bigger one.
//
if ( LookupAccountSid( NULL,
((SID_AND_ATTRIBUTES*)abSidAndInfo)->Sid,
achUserName,
&dwLen,
achDomain,
&dwDomainLen,
&SIDtype) )
{
//
// We return a LocalAlloc'ed buffer
//
dwLen = (DWORD) _tcslen( achUserName );
dwDomainLen = (DWORD) _tcslen( achDomain );
pPrincipal = (LPTSTR)LocalAlloc( LMEM_FIXED,
(dwLen + 1 + dwDomainLen + 1 ) * sizeof(TCHAR) );
if ( pPrincipal != NULL )
{
memcpy( pPrincipal,
achDomain,
sizeof(TCHAR)*dwDomainLen );
pPrincipal[dwDomainLen] = '\\';
memcpy( pPrincipal + dwDomainLen + 1,
achUserName,
sizeof(TCHAR)*(dwLen+1) );
*ppPrincipal = pPrincipal;
hres = S_OK;
}
else
{
hres = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
}
else
{
hres = HRESULT_FROM_WIN32( GetLastError() );
}
}
else
{
hres = HRESULT_FROM_WIN32( GetLastError() );
}
CloseHandle( hAccTok );
}
else
{
hres = HRESULT_FROM_WIN32( GetLastError() );
}
return hres;
}
BOOL
W3SVCandHTTPFilter(
DWORD currentIndex,
ENUM_SERVICE_STATUS* pessRoot,
DWORD dwNumServices
)
{
/*++
W3SVCandHTTPFilter
Return currently impersonated user
As this is a COM server running under the identity of the invoker
this means we access the process token. So we might end up getting
the wrong user name if the object is invoked in close succession
( within the 5s server exit timeout ) by different users.
Arguments:
DWORD currentIndex - index of the service we are deciding if we should process.
ENUM_SERVICE_STATUS* pessRoot - the set of services we are working on.
DWORD dwNumServices - the number of services in the set.
Returns:
TRUE if we found the w3svc and HTTPFilter on the same line and we are looking at the w3svc
--*/
BOOL bResult = FALSE;
// check if we are looking at the w3svc. If we are find out if the
// HTTPFilter is on the same level. Note the HTTPFilter will always be listed
// after the w3svc.
if ( _wcsicmp( pessRoot[currentIndex].lpServiceName, L"w3svc" ) == 0 )
{
for ( DWORD i = currentIndex + 1;
( i < dwNumServices ) && ( bResult == FALSE );
i++ )
{
if ( _wcsicmp( pessRoot[i].lpServiceName, L"HTTPFilter" ) == 0 )
{
bResult = TRUE;
}
}
}
return bResult;
}