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.

877 lines
26 KiB

  1. /*
  2. ****************************************************************************
  3. | Copyright (C) 2001 Microsoft Corporation
  4. |
  5. | Module Name:
  6. |
  7. | Utils.cpp
  8. |
  9. | Abstract:
  10. | This is the core code for the IIS6 Monitor tool
  11. |
  12. | Author:
  13. | Ivo Jeglov (ivelinj)
  14. |
  15. | Revision History:
  16. | November 2001
  17. |
  18. ****************************************************************************
  19. */
  20. #include "stdafx.h"
  21. #include "Utils.h"
  22. // This is the title for all wizard pages as well as the string that is shown in Add/Remove programs
  23. LPCWSTR MAIN_TITLE = L"IIS 6.0 Monitor v1.2";
  24. static LPCWSTR MON_REGKEY = L"Software\\Microsoft\\IISMon";
  25. static LPCTSTR MSG_ALREADYINSTALLED = _T("IIS 6.0 Monitor is already installed. If you would like to re-install IIS 6.0 Monitor, please remove your current version using the 'Add or Remove Programs' interface in the Control Panel.");
  26. static LPCTSTR MSG_NOTADMIN = _T("Only members of the Administrator group can install IIS 6.0 Monitor. Please add yourself to the Administrators group, and then run IIS 6.0 Monitor installation again. If you cannot add yourself to the Administrators group, contact your network administrator.");
  27. static LPCTSTR MSG_NOTASERVER = _T("IIS 6.0 Monitor can be installed only on pre-release versions of the Windows Server 2003 family of products.");
  28. static LPCTSTR MSG_NOIIS = _T("Internet Information Services ( IIS ) is not installed. Please install IIS from Add/Remove Windows Components in Control Panel.");
  29. static LPCTSTR MSG_IA64NOTSUPPORTED = _T("IIS 6.0 Monitor cannot be installed on IA64 platforms.");
  30. static LPCTSTR MSG_SCHEDULERSTOPPED = _T("IIS 6.0 Monitor depends on Task Scheduler service to function properly. You need to enable Task Scheduler on this server by clicking Start, pointing to All Programs, then Administrative Tools, and clicking Services. From the list of services, right-click Task Scheduler, and click Start.");
  31. static LPCTSTR ERR_COPYFAILED = _T("IIS 6.0 Monitor installation has failed to copy the necessary files to your file system.");
  32. static LPCTSTR ERR_REGERROR = _T("The registry contains Windows configuration information. IIS 6.0 Monitor installation has failed to create a set of entries in the registry that are required to ensure IIS 6.0 Monitor functions properly.");
  33. static LPCTSTR ERR_TASKERROR = _T("IIS 6.0 Monitor installation failed while trying to use the Task Scheduler service on your server to schedule the necessary IIS 6.0 Monitor scripts to run on a periodic basis.");
  34. static LPCTSTR ERR_DIRERROR = _T("IIS 6.0 Monitor requires a specific set of directories to be created for proper functionality. IIS 6.0 Monitor installation has failed to create the necessary directories.");
  35. static LPCTSTR ERR_SETACLAILED = _T("IIS 6.0 Monitor installation has failed to setup the access rights of the required directories.");
  36. static LPCWSTR TSK_DYN = L"This scheduled task runs a JScript that is part of the IIS 6.0 Monitor tool. This script is scheduled to run every two minutes to sample performance counter information, and then to generate an XML file that contains both aggregated performance counter information and entries from the Event Viewer.";
  37. static LPCWSTR TSK_STAT = L"This scheduled task runs a JScript that is part of the IIS 6.0 Monitor tool. This script is scheduled to run once a week to collect system hardware information and registry settings, and then to generate an XML file containing this information.";
  38. static LPCWSTR TSK_META = L"This scheduled task runs a JScript that is part of the IIS 6.0 Monitor tool. This script is scheduled to run once a week to copy your XML metabase file, and then parses the copied metabase to remove sensitive information.";
  39. static LPCWSTR TSK_UPLOAD = L"This scheduled task runs a JScript that is part of the IIS 6.0 Monitor tool. This script uploads the XML files generated by the the following IIS 6.0 Monitor scripts to Microsoft: iismDyn.js, iismStat.js, and iismMeta.js. These scripts are located in %Systemroot%\\System32\\Inetsrv\\IISMon directory. Files that are successfully uploaded to Microsoft will be copied to the %Systemdrive%\\IISMon directory if you have enabled the audit trail option.";
  40. BOOL IsMonInstalled()
  41. {
  42. // Check if the reg key exists. If so - the Monitor is installed
  43. HKEY hKey = NULL;
  44. BOOL bRes = FALSE;
  45. if ( ::RegOpenKeyExW( HKEY_LOCAL_MACHINE, MON_REGKEY, 0, KEY_READ, &hKey ) == ERROR_SUCCESS )
  46. {
  47. bRes = TRUE;
  48. }
  49. if ( hKey != NULL )
  50. {
  51. VERIFY( ::RegCloseKey( hKey ) == ERROR_SUCCESS );
  52. }
  53. return bRes;
  54. }
  55. // IsAdmin() - tests to see if the current user is an admin
  56. BOOL IsAdmin()
  57. {
  58. // Try an Admin Privilaged API - if it works return TRUE - else FALSE
  59. SC_HANDLE hSC = ::OpenSCManager( NULL, NULL, GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE );
  60. BOOL bAdmin = hSC != NULL;
  61. if ( hSC != NULL )
  62. {
  63. VERIFY( ::CloseServiceHandle( hSC ) );
  64. }
  65. return bAdmin;
  66. }
  67. BOOL IsIISInstalled( void )
  68. {
  69. LPCWSTR SERVICE_NAME = L"W3SVC";
  70. BOOL bRes = FALSE;
  71. // Open the SCM on the local machine
  72. SC_HANDLE schSCManager = ::OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
  73. _ASSERT( schSCManager != NULL ); // We alredy checked that we are Admins
  74. SC_HANDLE schService = ::OpenServiceW( schSCManager, SERVICE_NAME, SERVICE_QUERY_STATUS );
  75. if ( schService != NULL )
  76. {
  77. bRes = TRUE;
  78. VERIFY( ::CloseServiceHandle( schService ) );
  79. }
  80. VERIFY( ::CloseServiceHandle( schSCManager ) );
  81. return bRes;
  82. }
  83. BOOL IsWhistlerSrv()
  84. {
  85. OSVERSIONINFOEXW osVersion = { 0 };
  86. osVersion.dwOSVersionInfoSize = sizeof( osVersion );
  87. VERIFY( ::GetVersionExW( reinterpret_cast<OSVERSIONINFOW*>( &osVersion ) ) );
  88. if ( ( osVersion.dwMajorVersion == 5 ) &&
  89. ( osVersion.dwMinorVersion == 2 ) &&
  90. ( ( osVersion.wProductType == VER_NT_SERVER ) || ( osVersion.wProductType == VER_NT_DOMAIN_CONTROLLER ) ) )
  91. {
  92. return TRUE;
  93. }
  94. return FALSE;
  95. }
  96. BOOL IsIA64()
  97. {
  98. SYSTEM_INFO Info;
  99. ::GetSystemInfo( &Info );
  100. return ( ( Info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64 ) ||
  101. ( Info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 ) ||
  102. ( Info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ) );
  103. }
  104. BOOL IsNTFS()
  105. {
  106. const UINT BUFF_LEN = 32; // Should be large enough to hold the volume and the file system type
  107. WCHAR wszBuffer[ BUFF_LEN ];
  108. // Get the system drive letter
  109. VERIFY( ::ExpandEnvironmentStringsW( L"%SystemDrive%", wszBuffer, BUFF_LEN ) != 0 );
  110. // wszBuffer containts the drive only - add the slash to make the volume string
  111. ::wcscat( wszBuffer, L"\\" );
  112. DWORD dwMaxComponentLength = 0;
  113. DWORD dwSystemFlags = 0;
  114. WCHAR wszFileSystem[ BUFF_LEN ];
  115. VERIFY( ::GetVolumeInformationW( wszBuffer,
  116. NULL,
  117. 0,
  118. NULL,
  119. &dwMaxComponentLength,
  120. &dwSystemFlags,
  121. wszFileSystem,
  122. BUFF_LEN ) );
  123. return ::wcscmp( wszFileSystem, L"NTFS" ) == 0;
  124. }
  125. BOOL IsTaskSchRunning()
  126. {
  127. LPCWSTR SERVICE_NAME = L"Schedule";
  128. BOOL bRunning = FALSE;
  129. // Open the SCM on the local machine
  130. SC_HANDLE schSCManager = ::OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
  131. _ASSERT( schSCManager != NULL ); // We alredy checked that we are Admins
  132. SC_HANDLE schService = ::OpenServiceW( schSCManager, SERVICE_NAME, SERVICE_QUERY_STATUS );
  133. _ASSERT( schService != NULL ); // This service is part of the OS and must exist
  134. SERVICE_STATUS ssStatus;
  135. VERIFY( ::QueryServiceStatus( schService, &ssStatus ) );
  136. bRunning = ( ssStatus.dwCurrentState == SERVICE_RUNNING );
  137. VERIFY( ::CloseServiceHandle( schService ) );
  138. VERIFY( ::CloseServiceHandle( schSCManager ) );
  139. return bRunning;
  140. }
  141. BOOL IsW3SVCEnabled()
  142. {
  143. LPCWSTR SERVICE_NAME = L"W3SVC";
  144. BOOL bSvcOK = FALSE;
  145. // Open the SCM on the local machine
  146. SC_HANDLE schSCManager = ::OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS );
  147. _ASSERT( schSCManager != NULL ); // We alredy checked that we are Admins
  148. SC_HANDLE schService = ::OpenServiceW( schSCManager, SERVICE_NAME, SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG );
  149. _ASSERT( schService != NULL ); // We already checked that this service exist ( IsIISInstalled(...) )
  150. SERVICE_STATUS ssStatus;
  151. LPQUERY_SERVICE_CONFIGW pSvcConfig = reinterpret_cast<LPQUERY_SERVICE_CONFIGW>( ::malloc( 4096 ) );
  152. // Do not let an out of mem condition to ruin the Setup process at this step - pretend the service is OK
  153. if ( NULL == pSvcConfig )
  154. {
  155. return TRUE;
  156. }
  157. DWORD dwNeeded = 0;
  158. VERIFY( ::QueryServiceStatus( schService, &ssStatus ) );
  159. VERIFY( ::QueryServiceConfigW( schService, pSvcConfig, 4096, &dwNeeded ) );
  160. _ASSERT( dwNeeded <= 4096 );
  161. bSvcOK = ( SERVICE_RUNNING == ssStatus.dwCurrentState ) && ( SERVICE_DISABLED != pSvcConfig->dwStartType );
  162. VERIFY( ::CloseServiceHandle( schService ) );
  163. VERIFY( ::CloseServiceHandle( schSCManager ) );
  164. ::free( pSvcConfig );
  165. return bSvcOK;
  166. }
  167. LPCTSTR CanInstall()
  168. {
  169. LPCTSTR szError = NULL;
  170. // Check all install requirements:\
  171. if ( IsMonInstalled() )
  172. {
  173. szError = MSG_ALREADYINSTALLED;
  174. }
  175. else if ( !IsAdmin() )
  176. {
  177. szError = MSG_NOTADMIN;
  178. }
  179. else if ( !IsWhistlerSrv() )
  180. {
  181. szError = MSG_NOTASERVER;
  182. }
  183. else if ( !IsIISInstalled() )
  184. {
  185. szError = MSG_NOIIS;
  186. }
  187. else if ( IsIA64() )
  188. {
  189. szError = MSG_IA64NOTSUPPORTED;
  190. }
  191. else if ( !IsTaskSchRunning() )
  192. {
  193. szError = MSG_SCHEDULERSTOPPED;
  194. }
  195. return szError;
  196. }
  197. HRESULT SetupTasks()
  198. {
  199. DECLARE_HR_SUCCESS;
  200. ITaskSchedulerPtr spTaskScheduler;
  201. TASK_TRIGGER Trigger;
  202. // Get an interface to the Task Scheduler
  203. IF_SUCCEEDED( spTaskScheduler.CreateInstance( CLSID_CTaskScheduler ) );
  204. // iismDyn.js - will run every 2 minutes. Timeout 2min
  205. //////////////////////////////////////////////////////////////////
  206. ::InitTrigger( /*r*/Trigger );
  207. Trigger.TriggerType = TASK_TIME_TRIGGER_DAILY;
  208. Trigger.Type.Daily.DaysInterval = 1; // Every Day
  209. Trigger.MinutesDuration = 24 * 60; // The task have to be active all the day long
  210. Trigger.MinutesInterval = 2; // Run every 2 minutes
  211. IF_SUCCEEDED( ::AddTask( spTaskScheduler, L"DynData", L"iismDyn.js", TSK_DYN, 2 * 60 * 1000, Trigger ) );
  212. // iismUpload.js - will run every 2 minutes. Timeout: 12min
  213. //////////////////////////////////////////////////////////////////
  214. ::InitTrigger( /*r*/Trigger );
  215. Trigger.TriggerType = TASK_TIME_TRIGGER_DAILY;
  216. Trigger.Type.Daily.DaysInterval = 1; // Every Day
  217. Trigger.MinutesDuration = 24 * 60; // The task have to be active all the day long
  218. Trigger.MinutesInterval = 120; // Run every 2 hours
  219. IF_SUCCEEDED( ::AddTask( spTaskScheduler, L"Upload", L"iismUpld.js", TSK_UPLOAD, 12 * 60 * 1000, Trigger ) );
  220. // iismStat.js - will run once every week on Sunday, 3:00AM. Timeout: 2min
  221. //////////////////////////////////////////////////////////////////
  222. ::InitTrigger( /*r*/Trigger );
  223. Trigger.TriggerType = TASK_TIME_TRIGGER_WEEKLY;
  224. Trigger.Type.Weekly.WeeksInterval = 1; // Every week
  225. Trigger.Type.Weekly.rgfDaysOfTheWeek = TASK_SUNDAY;
  226. Trigger.wStartHour = 3;
  227. Trigger.wStartMinute = 00;
  228. IF_SUCCEEDED( ::AddTask( spTaskScheduler, L"StatData", L"iismStat.js", TSK_STAT, 2 * 60 * 1000, Trigger ) );
  229. // iismMeta.js - will run once every week on Sunday, 3:15AM. Timeout: 5min
  230. //////////////////////////////////////////////////////////////////
  231. ::InitTrigger( /*r*/Trigger );
  232. Trigger.TriggerType = TASK_TIME_TRIGGER_WEEKLY;
  233. Trigger.Type.Weekly.WeeksInterval = 1; // Every week
  234. Trigger.Type.Weekly.rgfDaysOfTheWeek = TASK_SUNDAY;
  235. Trigger.wStartHour = 3;
  236. Trigger.wStartMinute = 15;
  237. IF_SUCCEEDED( ::AddTask( spTaskScheduler, L"MetaData", L"iismMeta.js", TSK_META, 5 * 60 * 1000, Trigger ) );
  238. return hr;
  239. }
  240. void DeleteTasks()
  241. {
  242. ITaskSchedulerPtr spTaskScheduler;
  243. // Get an interface to the Task Scheduler
  244. if ( SUCCEEDED( spTaskScheduler.CreateInstance( CLSID_CTaskScheduler ) ) )
  245. {
  246. // Try to delete the tasks. The result is for information purposes only
  247. VERIFY( SUCCEEDED( spTaskScheduler->Delete( L"IIS Monitor ( DynData )" ) ) );
  248. VERIFY( SUCCEEDED( spTaskScheduler->Delete( L"IIS Monitor ( Upload )" ) ) );
  249. VERIFY( SUCCEEDED( spTaskScheduler->Delete( L"IIS Monitor ( StatData )" ) ) );
  250. VERIFY( SUCCEEDED( spTaskScheduler->Delete( L"IIS Monitor ( MetaData )" ) ) );
  251. }
  252. }
  253. HRESULT AddTask( const ITaskSchedulerPtr& spTaskScheduler,
  254. LPCWSTR wszSubname,
  255. LPCWSTR wszFileName,
  256. LPCWSTR wszComment,
  257. DWORD dwTimeout,
  258. TASK_TRIGGER& Trigger )
  259. {
  260. DECLARE_HR_SUCCESS;
  261. static LPCWSTR TASK_NAME_FMT = L"IIS Monitor ( %s )";
  262. static LPCWSTR TASK_COMMENT = L"This task is used to collect IIS statistic info and send it back to Microsoft.";
  263. WCHAR wszPath[ MAX_PATH + 1 ];
  264. WCHAR wszName[ 512 ];
  265. ITaskPtr spTask;
  266. IPersistFilePtr spPersistFile;
  267. // Create the task name
  268. ::swprintf( wszName, TASK_NAME_FMT, wszSubname );
  269. // Get the path ( for the working dir and for the executable )
  270. GetIISMonPath( wszPath );
  271. // Add the new task
  272. IF_SUCCEEDED( spTaskScheduler->NewWorkItem( wszName,
  273. CLSID_CTask,
  274. IID_ITask,
  275. reinterpret_cast<IUnknown**>( &spTask ) ) );
  276. // If the taks alredy exists - use it and modify it
  277. if ( HRESULT_FROM_WIN32( ERROR_FILE_EXISTS ) == hr )
  278. {
  279. hr = spTaskScheduler->Activate( wszName, IID_ITask, reinterpret_cast<IUnknown**>( &spTask ) );
  280. }
  281. // Setup the task
  282. IF_SUCCEEDED( spTask->SetWorkingDirectory( wszPath ) );
  283. IF_SUCCEEDED( spTask->SetComment( wszComment ) );
  284. IF_SUCCEEDED( spTask->SetPriority( NORMAL_PRIORITY_CLASS ) );
  285. IF_SUCCEEDED( spTask->SetMaxRunTime( dwTimeout ) );
  286. IF_SUCCEEDED( spTask->SetAccountInformation( L"", NULL ) ); // Use Local System account
  287. // Set task command line
  288. VERIFY( ::PathAppendW( wszPath, wszFileName ) );
  289. IF_SUCCEEDED( spTask->SetApplicationName( L"cscript.exe" ) );
  290. IF_SUCCEEDED( spTask->SetParameters( wszPath ) );
  291. // Set the trigger
  292. IScheduledWorkItemPtr spItem;
  293. ITaskTriggerPtr spTrigger;
  294. WORD wUnused = 0;
  295. IF_SUCCEEDED( spTask.QueryInterface( IID_IScheduledWorkItem, &spItem ) );
  296. IF_SUCCEEDED( spItem->CreateTrigger( &wUnused, &spTrigger ) );
  297. IF_SUCCEEDED( spTrigger->SetTrigger( &Trigger ) );
  298. // Store the changes
  299. IF_SUCCEEDED( spTask.QueryInterface( IID_IPersistFile, &spPersistFile ) );
  300. IF_SUCCEEDED( spPersistFile->Save( NULL, TRUE ) );
  301. // Cleanup
  302. if ( FAILED( hr ) )
  303. {
  304. // Remove the task
  305. if ( spTaskScheduler != NULL )
  306. {
  307. spTaskScheduler->Delete( wszName );
  308. }
  309. }
  310. return hr;
  311. }
  312. void InitTrigger( TASK_TRIGGER& rTrigger )
  313. {
  314. ::ZeroMemory( &rTrigger, sizeof( TASK_TRIGGER ) );
  315. rTrigger.cbTriggerSize = sizeof( TASK_TRIGGER );
  316. // Set the start time for something in the pas. We don't use this feature
  317. rTrigger.wBeginYear = 2000;
  318. rTrigger.wBeginMonth = 1;
  319. rTrigger.wBeginDay = 1;
  320. }
  321. HRESULT SetupRegistry( BOOL bEnableTrail, DWORD dwDaysToKeep )
  322. {
  323. DECLARE_HR_SUCCESS;
  324. // Generete the GUID for this machine
  325. GUID guid;
  326. DWORD dwTrail = bEnableTrail ? 1 : 0;
  327. IF_SUCCEEDED( ::CoCreateGuid( &guid ) );
  328. // Create the unsintall string
  329. WCHAR wszUninstall[ MAX_PATH + 1 ];
  330. GetIISMonPath( wszUninstall );
  331. VERIFY( ::PathAppendW( wszUninstall, L"iismoni.exe -uninstinter" ) );
  332. // Convert it to string
  333. WCHAR wszBuffer[ 64 ]; // SHould be large enough to hold a string GUID
  334. VERIFY( ::StringFromGUID2( guid, wszBuffer, 64 ) != 0 );
  335. // Store GUID
  336. IF_SUCCEEDED( SetIISMonRegData( MON_REGKEY,
  337. L"ServerGUID",
  338. REG_SZ,
  339. reinterpret_cast<BYTE*>( wszBuffer ),
  340. ::wcslen( wszBuffer ) * sizeof( WCHAR ) ) );
  341. // Store the audit trail value
  342. IF_SUCCEEDED( SetIISMonRegData( MON_REGKEY,
  343. L"AuditTrailEnabled",
  344. REG_DWORD,
  345. reinterpret_cast<BYTE*>( &dwTrail ),
  346. sizeof( DWORD ) ) );
  347. // Set the DaysToKeep Value
  348. IF_SUCCEEDED( SetIISMonRegData( MON_REGKEY,
  349. L"AuditTrailTimeLimit",
  350. REG_DWORD,
  351. reinterpret_cast<BYTE*>( &dwDaysToKeep ),
  352. sizeof( DWORD ) ) );
  353. // Set the uninstall string
  354. IF_SUCCEEDED( SetIISMonRegData( L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\IISMon",
  355. L"UninstallString",
  356. REG_SZ,
  357. reinterpret_cast<BYTE*>( wszUninstall ),
  358. ::wcslen( wszUninstall ) * sizeof( WCHAR ) ) );
  359. // Set the uninstall display name
  360. IF_SUCCEEDED( SetIISMonRegData( L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\IISMon",
  361. L"DisplayName",
  362. REG_SZ,
  363. ( BYTE* )( MAIN_TITLE ),
  364. ::wcslen( MAIN_TITLE ) * sizeof( WCHAR ) ) );
  365. return hr;
  366. }
  367. HRESULT SetIISMonRegData( LPCWSTR wszSubkey, LPCWSTR wszName, DWORD dwType, const BYTE* pbtData, DWORD dwSize )
  368. {
  369. DECLARE_HR_SUCCESS;
  370. _ASSERT( wszName != NULL );
  371. _ASSERT( dwSize > 0 );
  372. _ASSERT( pbtData != NULL );
  373. _ASSERT( wszSubkey != NULL );
  374. HKEY hKey = NULL;
  375. // If the key does not exists - create it
  376. if ( ::RegCreateKeyExW( HKEY_LOCAL_MACHINE,
  377. wszSubkey,
  378. 0,
  379. NULL,
  380. REG_OPTION_NON_VOLATILE,
  381. KEY_SET_VALUE,
  382. NULL,
  383. &hKey,
  384. NULL ) != ERROR_SUCCESS )
  385. {
  386. hr = E_FAIL;
  387. }
  388. if ( SUCCEEDED( hr ) )
  389. {
  390. if ( ::RegSetValueExW( hKey, wszName, 0, dwType, pbtData, dwSize ) != ERROR_SUCCESS )
  391. {
  392. hr = E_FAIL;
  393. }
  394. }
  395. if ( hKey != NULL )
  396. {
  397. ::RegCloseKey( hKey );
  398. }
  399. return hr;
  400. }
  401. void DelIISMonKey()
  402. {
  403. VERIFY( ::SHDeleteKeyW( HKEY_LOCAL_MACHINE, MON_REGKEY ) == ERROR_SUCCESS );
  404. VERIFY( ::SHDeleteKeyW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\IISMon" ) == ERROR_SUCCESS );
  405. }
  406. void GetIISMonPath( LPWSTR wszPath )
  407. {
  408. // wszPath should be a buffer with MAX_PATH + 1 length
  409. VERIFY( ::GetSystemDirectoryW( wszPath, MAX_PATH + 1 ) != 0 );
  410. VERIFY( ::PathAppendW( wszPath, L"Inetsrv\\IISMon" ) );
  411. }
  412. HRESULT SetupDirStruct()
  413. {
  414. BOOL bRes = TRUE;
  415. WCHAR wszRoot[ MAX_PATH + 1 ];
  416. // Create the Log and Upload folders
  417. // Do not fail if they already exist
  418. if ( bRes )
  419. {
  420. GetIISMonPath( wszRoot );
  421. VERIFY( ::PathAppendW( wszRoot, L"Upload" ) );
  422. bRes = ::CreateDirectoryW( wszRoot, NULL );
  423. if ( !bRes && ( ::GetLastError() == ERROR_ALREADY_EXISTS ) )
  424. {
  425. bRes = TRUE;
  426. }
  427. }
  428. if ( bRes )
  429. {
  430. GetIISMonPath( wszRoot );
  431. VERIFY( ::PathAppendW( wszRoot, L"Log" ) );
  432. bRes = ::CreateDirectoryW( wszRoot, NULL );
  433. if ( !bRes && ( ::GetLastError() == ERROR_ALREADY_EXISTS ) )
  434. {
  435. bRes = TRUE;
  436. }
  437. }
  438. return bRes ? S_OK : E_FAIL;
  439. }
  440. HRESULT SetupACLs( void )
  441. {
  442. // ACLs are set so that only Administrators have access to IISMon folders
  443. // The ACLs are not inherited from parent dirs
  444. SECURITY_DESCRIPTOR* pSD = NULL;
  445. ACL* pDACL = NULL;
  446. SECURITY_INFORMATION si = ( DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION );
  447. BOOL bHaveDACL = FALSE;
  448. BOOL bDefaulted = FALSE;
  449. // This is the ACL that we will set. String ACL is used for simplicity
  450. // See ACE strings documentation in the MSDN ( Search for "SDDL" )
  451. VERIFY( ::ConvertStringSecurityDescriptorToSecurityDescriptorW( L"D:P(A;CIOI;GA;;;BA)(A;CIOI;GA;;;SY)",
  452. SDDL_REVISION_1,
  453. reinterpret_cast<void**>( &pSD ),
  454. NULL ) );
  455. VERIFY( ::GetSecurityDescriptorDacl( pSD, &bHaveDACL, &pDACL, &bDefaulted ) );
  456. // Set theDACL to all the folders
  457. LPCWSTR awszDirs[] = { L"%systemdrive%\\IISMon",
  458. L"%systemroot%\\system32\\inetsrv\\IISMon"
  459. };
  460. for ( int i = 0; i < ARRAY_SIZE( awszDirs ); ++i )
  461. {
  462. WCHAR wszPath[ MAX_PATH + 1 ];
  463. ::wcscpy( wszPath, awszDirs[ i ] );
  464. VERIFY( LOWORD( ::DoEnvironmentSubstW( wszPath, MAX_PATH + 1 ) ) );
  465. if ( ::SetNamedSecurityInfoW( wszPath,
  466. SE_FILE_OBJECT,
  467. si,
  468. NULL,
  469. NULL,
  470. pDACL,
  471. NULL ) != ERROR_SUCCESS )
  472. {
  473. return E_FAIL;
  474. }
  475. }
  476. return S_OK;
  477. }
  478. void DeleteDirStruct( BOOL bRemoveTrail )
  479. {
  480. WCHAR wszRoot[ MAX_PATH + 1 ];
  481. ::GetIISMonPath( wszRoot );
  482. VERIFY( ::PathAppendW( wszRoot, L"Upload\\Incomplete" ) );
  483. DelDirWithFiles( wszRoot );
  484. ::GetIISMonPath( wszRoot );
  485. VERIFY( ::PathAppendW( wszRoot, L"Upload" ) );
  486. DelDirWithFiles( wszRoot );
  487. ::GetIISMonPath( wszRoot );
  488. VERIFY( ::PathAppendW( wszRoot, L"Log" ) );
  489. DelDirWithFiles( wszRoot );
  490. ::GetIISMonPath( wszRoot );
  491. VERIFY( ::PathAppendW( wszRoot, L"1033" ) );
  492. DelDirWithFiles( wszRoot );
  493. ::GetIISMonPath( wszRoot );
  494. DelDirWithFiles( wszRoot );
  495. if ( bRemoveTrail )
  496. {
  497. VERIFY( ::ExpandEnvironmentStringsW( L"%SystemDrive%", wszRoot, MAX_PATH + 1 ) != 0 );
  498. VERIFY( ::PathAppendW( wszRoot, L"IISMon" ) );
  499. DelDirWithFiles( wszRoot );
  500. }
  501. }
  502. void DelDirWithFiles( LPCWSTR wszDir )
  503. {
  504. WIN32_FIND_DATAW fd;
  505. WCHAR wszPath[ MAX_PATH + 1 ];
  506. ::wcscpy( wszPath, wszDir );
  507. VERIFY( ::PathAppendW( wszPath, L"*.*" ) );
  508. HANDLE hSearch = ::FindFirstFileW( wszPath, &fd );
  509. // this is not a normal case.
  510. if ( INVALID_HANDLE_VALUE == hSearch ) return;
  511. do
  512. {
  513. ::wcscpy( wszPath, wszDir );
  514. // Skip directories. Delete only files
  515. if ( 0 == ( fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) )
  516. {
  517. VERIFY( ::PathAppendW( wszPath,fd.cFileName ) );
  518. // If we can't delete the file right now - may be it is locked. Schedule it for deletion at next boot
  519. if ( !::DeleteFileW( wszPath ) )
  520. {
  521. VERIFY( ::MoveFileExW( wszPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT ) );
  522. }
  523. }
  524. }while( ::FindNextFileW( hSearch, &fd ) );
  525. VERIFY( ::FindClose( hSearch ) );
  526. // Remove the directory ( should be empty now )
  527. // Again - if it cannot be deleted right now - schedule it for next boot
  528. if ( !::RemoveDirectoryW( wszDir ) )
  529. {
  530. VERIFY( ::MoveFileExW( wszDir, NULL, MOVEFILE_DELAY_UNTIL_REBOOT ) );
  531. }
  532. }
  533. // Installs a section
  534. HRESULT InstallFromINF()
  535. {
  536. DECLARE_HR_SUCCESS;
  537. // The INF file must be in the same dir as this EXE.Build the path to ther INF file
  538. WCHAR wszPath[ _MAX_PATH + 1 ];
  539. WCHAR wszDrive[ _MAX_DRIVE + 1 ];
  540. WCHAR wszFolder[ _MAX_DIR + 1 ];
  541. VERIFY( ::GetModuleFileNameW( NULL, wszPath, MAX_PATH ) != 0 );
  542. ::_wsplitpath( wszPath, wszDrive, wszFolder, NULL, NULL );
  543. ::_wmakepath( wszPath, wszDrive, wszFolder, L"IISMon", L"inf" );
  544. HINF hInf = ::SetupOpenInfFileW( wszPath, NULL, INF_STYLE_WIN4, 0 );
  545. // The file MUST exist - it is installed by the IExpress tool
  546. _ASSERT( hInf != INVALID_HANDLE_VALUE );
  547. BOOL bRes = ::SetupInstallFromInfSectionW( NULL,
  548. hInf,
  549. L"DefaultInstall",
  550. SPINST_FILES | SPINST_REGISTRY,
  551. NULL,
  552. NULL,
  553. SP_COPY_NEWER_OR_SAME,
  554. INFInstallCallback,
  555. NULL,
  556. NULL,
  557. NULL );
  558. ::SetupCloseInfFile( hInf );
  559. return bRes ? S_OK : E_FAIL;
  560. }
  561. UINT CALLBACK INFInstallCallback( PVOID pvCtx, UINT nNotif, UINT_PTR nP1, UINT_PTR nP2 )
  562. {
  563. // Abort the installation for all errors
  564. if ( ( SPFILENOTIFY_COPYERROR == nNotif ) ||
  565. ( SPFILENOTIFY_RENAMEERROR == nNotif ) )
  566. {
  567. return FILEOP_ABORT;
  568. }
  569. // Allow the operation to execute
  570. return FILEOP_DOIT;
  571. }
  572. LPCTSTR Install( HINSTANCE hInstance, BOOL bAuditTrailEnabled, DWORD dwDaysToKeep )
  573. {
  574. DECLARE_HR_SUCCESS;
  575. LPCTSTR szLocalError = NULL;
  576. WCHAR wszPath[ MAX_PATH + 1 ];
  577. ::swprintf( wszPath, L"wmiadap.exe /F" );
  578. STARTUPINFOW si = { 0 };
  579. PROCESS_INFORMATION pi = { 0 };
  580. si.cb = sizeof( si );
  581. // Execute wmiadap.exe /f to refresh perf counters on this machine
  582. VERIFY( ::CreateProcessW( NULL, wszPath, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi ) );
  583. ::CloseHandle( pi.hProcess );
  584. ::CloseHandle( pi.hThread );
  585. if ( SUCCEEDED( hr ) )
  586. {
  587. szLocalError = ERR_COPYFAILED;
  588. hr = InstallFromINF();
  589. }
  590. // Setup ACLs
  591. if ( SUCCEEDED( hr ) )
  592. {
  593. szLocalError = ERR_SETACLAILED;
  594. hr = SetupACLs();
  595. }
  596. // Register the iismon.wsc component
  597. if ( SUCCEEDED( hr ) )
  598. {
  599. ::swprintf( wszPath, L"regsvr32 /s \"%%systemroot%%\\system32\\inetsrv\\iismon\\iismon.wsc\"" );
  600. VERIFY( LOWORD( ::DoEnvironmentSubstW( wszPath, MAX_PATH + 1 ) ) );
  601. ::ZeroMemory( &pi, sizeof( pi ) );
  602. if ( !::CreateProcessW( NULL, wszPath , NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi ) )
  603. {
  604. hr = E_FAIL;
  605. }
  606. else
  607. {
  608. ::CloseHandle( pi.hProcess );
  609. ::CloseHandle( pi.hThread );
  610. }
  611. }
  612. // Setup the registry
  613. if ( SUCCEEDED( hr ) )
  614. {
  615. hr = SetupRegistry( bAuditTrailEnabled, dwDaysToKeep );
  616. szLocalError = ERR_REGERROR;
  617. }
  618. // Use local system account for now
  619. if ( SUCCEEDED( hr ) )
  620. {
  621. hr = SetupTasks();
  622. szLocalError = ERR_TASKERROR;
  623. }
  624. // Setup dir structure
  625. if ( SUCCEEDED( hr ) )
  626. {
  627. hr = SetupDirStruct();
  628. szLocalError = ERR_DIRERROR;
  629. }
  630. // Error handling
  631. if ( FAILED( hr ) )
  632. {
  633. // Try to not leave side effects
  634. Uninstall( FALSE );
  635. }
  636. return SUCCEEDED( hr ) ? NULL : szLocalError;
  637. }
  638. void Uninstall( BOOL bRemoveTrail )
  639. {
  640. // Unregister iismon.wsc
  641. WCHAR wszPath[ MAX_PATH + 1 ];
  642. STARTUPINFOW si = { 0 };
  643. PROCESS_INFORMATION pi = { 0 };
  644. si.cb = sizeof( si );
  645. ::swprintf( wszPath, L"regsvr32 /s /u \"%%systemroot%%\\system32\\inetsrv\\iismon\\iismon.wsc\"" );
  646. VERIFY( LOWORD( ::DoEnvironmentSubstW( wszPath, MAX_PATH + 1 ) ) );
  647. VERIFY( ::CreateProcessW( NULL, wszPath, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi ) );
  648. ::CloseHandle( pi.hProcess );
  649. ::CloseHandle( pi.hThread );
  650. // Remove the tasks
  651. DeleteTasks();
  652. // Remove the files
  653. DeleteDirStruct( bRemoveTrail );
  654. // Remove the reg key
  655. DelIISMonKey();
  656. }