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

  1. /*
  2. IisRestart.cpp
  3. Implementation of CIisRestart ( IIisServiceControl )
  4. FILE HISTORY:
  5. Phillich 06-Oct-1998 Created
  6. */
  7. #include "stdafx.h"
  8. #include "IisRsta.h"
  9. #include "IisRstam.h"
  10. #include "IisRestart.h"
  11. #include "common.h"
  12. #define MAX_TASKS 8
  13. #define SLEEP_INTERVAL 1000
  14. typedef BOOL (*PFNQUERYSERVICECONFIG2)(SC_HANDLE,DWORD,LPBYTE,DWORD,LPDWORD) ;
  15. typedef BOOL (*PFNCHANGESERVICECONFIG2)(SC_HANDLE,DWORD,LPVOID);
  16. //
  17. // control block for control command requests
  18. //
  19. typedef struct {
  20. HRESULT hres;
  21. LONG lRefCount;
  22. DWORD dwCmd;
  23. SC_HANDLE hServiceHandle;
  24. } SERVICE_COMMAND_CONTROL_BLOCK;
  25. //
  26. // Global functions
  27. //
  28. BOOL
  29. W3SVCandHTTPFilter(
  30. DWORD currentIndex,
  31. ENUM_SERVICE_STATUS* pessRoot,
  32. DWORD dwNumServices
  33. );
  34. VOID EnableShutdownPrivilege(
  35. VOID
  36. );
  37. HRESULT
  38. EnumStartServices(
  39. SC_HANDLE schSCM,
  40. LPTSTR pszRoot,
  41. DWORD dwTargetState,
  42. LPBYTE abServiceList,
  43. DWORD dwInitializeServiceListSize,
  44. LPBYTE* ppbServiceList,
  45. LPDWORD pdwNumServices,
  46. BOOL fAddIisadmin
  47. );
  48. HRESULT
  49. SerializeEnumServiceBuffer(
  50. LPENUM_SERVICE_STATUS pessDependentServices,
  51. DWORD dwNumServices,
  52. LPBYTE pbBuffer,
  53. DWORD dwBufferSize,
  54. LPDWORD pdwMDRequiredBufferSize
  55. );
  56. BOOL
  57. IsEnableRemote(
  58. );
  59. HRESULT
  60. StopIIsAdmin(
  61. DWORD dwTimeoutMsecs
  62. );
  63. HRESULT
  64. StartStopAll(
  65. LPTSTR pszRoot,
  66. BOOL fStart,
  67. DWORD dwTimeoutMsecs
  68. );
  69. BOOL
  70. WaitForServiceStatus(
  71. SC_HANDLE schDependent,
  72. DWORD dwDesiredServiceState,
  73. DWORD dwTimeoutMsecs
  74. );
  75. HRESULT
  76. KillTaskByName(
  77. LPTSTR pname,
  78. LPSTR pszMandatoryModule
  79. );
  80. VOID
  81. ReportStatus(
  82. DWORD dwId,
  83. DWORD dwStatus
  84. );
  85. HRESULT
  86. SendControlToService(
  87. SC_HANDLE hServiceHandle,
  88. DWORD dwCmd,
  89. LPDWORD pdwTimeoutOutMsecs
  90. );
  91. StartStopAllRecursive(
  92. SC_HANDLE schSCM,
  93. ENUM_SERVICE_STATUS* pessRoot,
  94. DWORD dwNumServices,
  95. BOOL fStart,
  96. BOOL fForceDemandStart,
  97. LPDWORD pdwTimeoutMsecs
  98. );
  99. HRESULT
  100. WhoAmI(
  101. LPTSTR* pPrincipal
  102. );
  103. BOOL
  104. CloseSystemExceptionHandler(
  105. LPCTSTR pszWindowName
  106. );
  107. /////////////////////////////////////////////////////////////////////////////
  108. // CIisRestart
  109. STDMETHODIMP
  110. CIisRestart::Stop(
  111. DWORD dwTimeoutMsecs,
  112. DWORD dwForce
  113. )
  114. /*++
  115. Stop
  116. Stop all internet services ( services dependent on IISADMIN )
  117. first using SCM then optionaly using TerminateProcess if failure
  118. Arguments:
  119. dwTimeoutMsecs - timeout for status check ( in ms )
  120. dwForce - !0 to force TerminateProcess if failure to stop services using SCM
  121. Returns:
  122. ERROR_RESOURCE_DISABLED if remote access to IIisRestart disabled
  123. ERROR_SERVICE_REQUEST_TIMEOUT if timeout waiting for all internet services status
  124. to be stopped
  125. otherwise COM status
  126. --*/
  127. {
  128. HRESULT hres = S_OK;
  129. if ( !IsEnableRemote() )
  130. {
  131. hres = HRESULT_FROM_WIN32( ERROR_RESOURCE_DISABLED );
  132. }
  133. else
  134. {
  135. //
  136. // Always kill Dr Watson, as the Dr Watson window may be still present after inetinfo process
  137. // was terminated after an exception, and in this case the Dr Watson process apparently still owns
  138. // some sockets resources preventing inetinfo to properly restart ( specifically binding TCP/IP sockets
  139. // fails during inetinfo restart )
  140. //
  141. KillTaskByName(_T("drwtsn32"), NULL);
  142. hres = StartStopAll( _T("IISADMIN"), FALSE, dwTimeoutMsecs );
  143. if ( dwForce && FAILED( hres ) )
  144. {
  145. ReportStatus( IRSTAM_KILL_DUE_TO_FORCE, hres );
  146. hres = Kill();
  147. }
  148. }
  149. ReportStatus( IRSTAM_STOP, hres );
  150. return hres;
  151. }
  152. STDMETHODIMP
  153. CIisRestart::Start(
  154. DWORD dwTimeoutMsecs
  155. )
  156. /*++
  157. Start
  158. Start all internet services ( services dependent on IISADMIN )
  159. using SCM
  160. Arguments:
  161. dwTimeoutMsecs - timeout for status check ( in ms )
  162. Returns:
  163. ERROR_RESOURCE_DISABLED if remote access to IIisRestart disabled
  164. ERROR_SERVICE_REQUEST_TIMEOUT if timeout waiting for all internet services status
  165. to be started
  166. otherwise COM status
  167. --*/
  168. {
  169. HRESULT hres = S_OK;
  170. if ( !IsEnableRemote() )
  171. {
  172. hres = HRESULT_FROM_WIN32( ERROR_RESOURCE_DISABLED );
  173. }
  174. else
  175. {
  176. //
  177. // In 6.0 we will use IIS Reset /Start to bring up the
  178. // service again without stopping the services that can
  179. // keep running. We still want to kill any dr watson's
  180. // thou. This should be harmless on a regular start.
  181. //
  182. //
  183. // Always kill Dr Watson, as the Dr Watson window may be still present after inetinfo process
  184. // was terminated after an exception, and in this case the Dr Watson process apparently still owns
  185. // some sockets resources preventing inetinfo to properly restart ( specifically binding TCP/IP sockets
  186. // fails during inetinfo restart )
  187. //
  188. KillTaskByName(_T("drwtsn32"), NULL);
  189. hres = StartStopAll( _T("IISADMIN"), TRUE, dwTimeoutMsecs );
  190. }
  191. ReportStatus( IRSTAM_START, hres );
  192. return hres;
  193. }
  194. STDMETHODIMP
  195. CIisRestart::Reboot(
  196. DWORD dwTimeoutMsecs,
  197. DWORD dwForceAppsClosed
  198. )
  199. /*++
  200. Reboot
  201. Reboot the computer
  202. Arguments:
  203. dwTimeoutMsecs - timeout for apps to be closed by user ( in ms )
  204. dwForceAppsClosed - force apps to be closed if hung
  205. Returns:
  206. ERROR_RESOURCE_DISABLED if remote access to IIisRestart disabled
  207. otherwise COM status
  208. --*/
  209. {
  210. HRESULT hres = S_OK;
  211. if ( !IsEnableRemote() )
  212. {
  213. hres = HRESULT_FROM_WIN32( ERROR_RESOURCE_DISABLED );
  214. }
  215. else
  216. {
  217. //
  218. // If this fails then we will get an error back from ExitWindowsEx()
  219. //
  220. EnableShutdownPrivilege();
  221. //
  222. // Make sure we will always reboot even if process(es) stuck
  223. //
  224. TCHAR* pPrincipal;
  225. TCHAR* pBuf;
  226. //
  227. // Format message to operator, includes name of user requesting shutdown.
  228. //
  229. if ( SUCCEEDED( hres = WhoAmI( &pPrincipal ) ) )
  230. {
  231. if ( FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_HMODULE|FORMAT_MESSAGE_ARGUMENT_ARRAY,
  232. (LPCVOID)NULL, // no handle to module so this module's resource will be used.
  233. IRSTAM_SYSSHUT,
  234. 0,
  235. (LPTSTR)&pBuf,
  236. 1,
  237. (va_list *)&pPrincipal ) )
  238. {
  239. if (InitiateSystemShutdownEx( NULL,
  240. pBuf,
  241. dwTimeoutMsecs/1000, // timeout in seconds
  242. dwForceAppsClosed,
  243. TRUE,
  244. SHTDN_REASON_FLAG_PLANNED |
  245. SHTDN_REASON_MAJOR_OPERATINGSYSTEM |
  246. SHTDN_REASON_MINOR_RECONFIG) == 0)
  247. {
  248. hres = HRESULT_FROM_WIN32( GetLastError() );
  249. }
  250. LocalFree( (LPVOID)pBuf );
  251. }
  252. LocalFree( pPrincipal );
  253. }
  254. }
  255. ReportStatus( IRSTAM_REBOOT, hres );
  256. return hres;
  257. }
  258. STDMETHODIMP
  259. CIisRestart::Kill(
  260. )
  261. /*++
  262. Kill
  263. Kill all internet services ( services dependent on IISADMIN )
  264. using TerminateProcess()
  265. Arguments:
  266. None
  267. Returns:
  268. ERROR_RESOURCE_DISABLED if remote access to IIisRestart disabled
  269. otherwise COM status
  270. --*/
  271. {
  272. HRESULT hres = S_OK;
  273. HRESULT hresReapply = S_OK;
  274. HRESULT hresKill = S_OK;
  275. BYTE abServiceList[2048];
  276. LPBYTE pbServiceList = NULL;
  277. DWORD dwNumServices = 0;
  278. SC_HANDLE schSCM = NULL;
  279. SC_HANDLE schSrv;
  280. LPBYTE* ppInfo = NULL;
  281. LPENUM_SERVICE_STATUS pessDependentServices = NULL;
  282. DWORD dwNeeded;
  283. HINSTANCE hAdvapi;
  284. PFNQUERYSERVICECONFIG2 pfnQueryServiceConfig2 = NULL;
  285. PFNCHANGESERVICECONFIG2 pfnChangeServiceConfig2 = NULL;
  286. SERVICE_FAILURE_ACTIONS sfaNoAction;
  287. SC_ACTION saNoAction[3];
  288. DWORD i;
  289. BYTE abTemp[64]; // work-around for NT5 bug
  290. DWORD* adwPid = NULL;
  291. SERVICE_STATUS_PROCESS status;
  292. DWORD cbNeeded = 0;
  293. if ( !IsEnableRemote() )
  294. {
  295. return HRESULT_FROM_WIN32( ERROR_RESOURCE_DISABLED );
  296. }
  297. //
  298. // Take a snapshot of Restart configuration
  299. // If unable to get ptr to service config2 API then consider this a success:
  300. // there is nothing to preserve.
  301. //
  302. hAdvapi = LoadLibrary(_T("ADVAPI32.DLL"));
  303. if ( hAdvapi != NULL )
  304. {
  305. pfnQueryServiceConfig2 = (PFNQUERYSERVICECONFIG2)GetProcAddress( hAdvapi, "QueryServiceConfig2W" );
  306. pfnChangeServiceConfig2 = (PFNCHANGESERVICECONFIG2)GetProcAddress( hAdvapi, "ChangeServiceConfig2W" );
  307. }
  308. if ( pfnQueryServiceConfig2
  309. && pfnChangeServiceConfig2 )
  310. {
  311. schSCM = OpenSCManager(NULL,
  312. NULL,
  313. SC_MANAGER_ENUMERATE_SERVICE);
  314. if ( schSCM == NULL )
  315. {
  316. hres = HRESULT_FROM_WIN32( GetLastError() );
  317. }
  318. else
  319. {
  320. //
  321. // Setup control block for no restart action.
  322. // We will replace existing actions with this control block
  323. //
  324. sfaNoAction.dwResetPeriod = INFINITE;
  325. sfaNoAction.lpCommand = _T("");
  326. sfaNoAction.lpRebootMsg = _T("");
  327. sfaNoAction.cActions = 3;
  328. sfaNoAction.lpsaActions = saNoAction;
  329. saNoAction[0].Type = SC_ACTION_NONE;
  330. saNoAction[0].Delay = 0;
  331. saNoAction[1].Type = SC_ACTION_NONE;
  332. saNoAction[1].Delay = 0;
  333. saNoAction[2].Type = SC_ACTION_NONE;
  334. saNoAction[2].Delay = 0;
  335. //
  336. // Enumerate all services dependent on IISADMIN ( including itself )
  337. //
  338. hres = EnumStartServices( schSCM,
  339. _T("IISADMIN"),
  340. SERVICE_STATE_ALL,
  341. abServiceList,
  342. sizeof(abServiceList),
  343. &pbServiceList,
  344. &dwNumServices,
  345. TRUE );
  346. if ( SUCCEEDED( hres ) )
  347. {
  348. //
  349. // Store existing info in ppInfo array
  350. //
  351. adwPid = new DWORD[ dwNumServices ];
  352. // we don't check the adwPid here because
  353. // we will only use it below if we succeeded in allocating it.
  354. if ( adwPid )
  355. {
  356. memset ( adwPid, 0, sizeof(DWORD) * dwNumServices );
  357. }
  358. ppInfo = (LPBYTE*)LocalAlloc( LMEM_FIXED|LMEM_ZEROINIT, sizeof(LPBYTE) * dwNumServices );
  359. if ( ppInfo )
  360. {
  361. pessDependentServices = (LPENUM_SERVICE_STATUS)pbServiceList;
  362. for ( i = 0 ;
  363. (i < dwNumServices) && SUCCEEDED(hres) ;
  364. ++i )
  365. {
  366. schSrv = OpenService( schSCM,
  367. pessDependentServices[i].lpServiceName,
  368. SERVICE_QUERY_CONFIG |
  369. SERVICE_CHANGE_CONFIG |
  370. SERVICE_QUERY_STATUS );
  371. if ( schSrv )
  372. {
  373. if ( adwPid )
  374. {
  375. if ( QueryServiceStatusEx( schSrv,
  376. SC_STATUS_PROCESS_INFO,
  377. (LPBYTE)&status,
  378. sizeof( status ),
  379. &cbNeeded ) )
  380. {
  381. adwPid[i] = status.dwProcessId;
  382. }
  383. }
  384. //
  385. // 1st query config size, then alloc buffer and retrieve
  386. // config. Note than ppInfo[] may be NULL is no config
  387. // associated with this service.
  388. //
  389. // WARNING: must specify ptr to writable buffer even if specified
  390. // buffer size is 0 due to bug in NT5 implementation of
  391. // QueryServiceConfig2. Not sure about minimum buffer size
  392. // ( sizeof(SERVICE_FAILURE_ACTIONS) ) ?
  393. //
  394. if ( !pfnQueryServiceConfig2( schSrv,
  395. SERVICE_CONFIG_FAILURE_ACTIONS,
  396. (LPBYTE)abTemp,
  397. 0,
  398. &dwNeeded ) )
  399. {
  400. if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
  401. {
  402. // ppInfo is an array of ptrs to bytes.
  403. ppInfo[i] = (LPBYTE)LocalAlloc( LMEM_FIXED, dwNeeded );
  404. if ( ppInfo[i] != NULL )
  405. {
  406. if ( !pfnQueryServiceConfig2( schSrv,
  407. SERVICE_CONFIG_FAILURE_ACTIONS,
  408. ppInfo[i],
  409. dwNeeded,
  410. &dwNeeded ) )
  411. {
  412. hres = HRESULT_FROM_WIN32( GetLastError() );
  413. }
  414. }
  415. else
  416. {
  417. hres = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  418. }
  419. }
  420. else
  421. {
  422. hres = HRESULT_FROM_WIN32( GetLastError() );
  423. }
  424. }
  425. if ( SUCCEEDED( hres ) )
  426. {
  427. if ( !pfnChangeServiceConfig2( schSrv,
  428. SERVICE_CONFIG_FAILURE_ACTIONS,
  429. &sfaNoAction ) )
  430. {
  431. hres = HRESULT_FROM_WIN32( GetLastError() );
  432. }
  433. }
  434. CloseServiceHandle( schSrv );
  435. }
  436. else
  437. {
  438. hres = HRESULT_FROM_WIN32( GetLastError() );
  439. }
  440. } // Close of the for loop
  441. }
  442. else
  443. {
  444. hres = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  445. }
  446. }
  447. CloseServiceHandle( schSCM );
  448. }
  449. }
  450. //
  451. // Graceful exit failed, kill the IIS processes.
  452. // First, kill inetinfo, then kill the WAM instances.
  453. //
  454. // Issue: Not sure why we do this, and if we need to do it for other exe's.
  455. CloseSystemExceptionHandler( _T("inetinfo.exe") );
  456. //
  457. // Always kill Dr Watson, as the Dr Watson window may be still present after inetinfo process
  458. // was terminated after an exception, and in this case the Dr Watson process apparently still owns
  459. // some sockets resources preventing inetinfo to properly restart ( specifically binding TCP/IP sockets
  460. // fails during inetinfo restart )
  461. //
  462. // If we removed all the SCM config above then
  463. // we will attempt the kills
  464. if ( SUCCEEDED ( hres ) )
  465. {
  466. HRESULT hresKillTemp = S_OK;
  467. KillTaskByName(_T("drwtsn32"), NULL);
  468. hresKillTemp = KillTaskByName(_T("SVCHOST"), "iisw3adm.dll"); // MTS WAM containers
  469. if ( SUCCEEDED ( hresKill ) )
  470. {
  471. hresKill = hresKillTemp;
  472. }
  473. hresKillTemp = KillTaskByName(_T("W3WP"), NULL); // MTS WAM containers
  474. if ( SUCCEEDED ( hresKill ) )
  475. {
  476. hresKill = hresKillTemp;
  477. }
  478. hresKillTemp = KillTaskByName(_T("INETINFO"), NULL);
  479. if ( SUCCEEDED ( hresKill ) )
  480. {
  481. hresKill = hresKillTemp;
  482. }
  483. hresKillTemp = KillTaskByName(_T("DLLHOST"),"wam.dll"); // COM+ WAM Containers
  484. if ( SUCCEEDED ( hresKill ) )
  485. {
  486. hresKill = hresKillTemp;
  487. }
  488. hresKillTemp = KillTaskByName(_T("ASPNET_WP"), NULL); // ASP + Processes.
  489. if ( SUCCEEDED ( hresKill ) )
  490. {
  491. hresKill = hresKillTemp;
  492. }
  493. hresKillTemp = KillTaskByName(_T("DAVCDATA"), NULL); // Dav support process.
  494. if ( SUCCEEDED ( hresKill ) )
  495. {
  496. hresKill = hresKillTemp;
  497. }
  498. // the following code will check the IISAdmin registry parameters for
  499. // a KillProcsOnFailure MULTI_SZ. Any process names in this list will
  500. // be killed.
  501. HKEY hKey;
  502. DWORD dwType;
  503. DWORD dwSize;
  504. TCHAR achBuffer[1024];
  505. if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  506. TEXT("system\\CurrentControlSet\\services\\IISAdmin"),
  507. 0,
  508. KEY_READ,
  509. &hKey ) == ERROR_SUCCESS )
  510. {
  511. DWORD Success = ERROR_SUCCESS;
  512. dwSize = sizeof( achBuffer );
  513. Success = RegQueryValueEx( hKey,
  514. TEXT("KillProcsOnFailure"),
  515. 0,
  516. &dwType,
  517. (LPBYTE)achBuffer,
  518. &dwSize );
  519. if ( Success == ERROR_SUCCESS &&
  520. dwType == REG_MULTI_SZ &&
  521. dwSize > 2 )
  522. {
  523. TCHAR *pT = achBuffer;
  524. // parse the multisz. The format is NULL Terminated strings
  525. // with an extra null terminator after the list.
  526. while (*pT)
  527. {
  528. hresKillTemp = KillTaskByName(pT, NULL);
  529. if ( SUCCEEDED ( hresKill ) )
  530. {
  531. hresKill = hresKillTemp;
  532. }
  533. // _tcsnbcnt figures out how many bytes are in the
  534. // the first number of characters that _tcslen declares.
  535. // So this calculation works out to be the number of bytes
  536. // that we just looked at plus the null terminator.
  537. dwSize -= (DWORD) _tcsnbcnt(pT,_tcslen(pT)) + sizeof(TCHAR);
  538. pT += _tcslen(pT) + 1;
  539. }
  540. } // end of successfull opening of the key
  541. RegCloseKey( hKey );
  542. }
  543. }
  544. hresReapply = S_OK;
  545. //
  546. // Reapply restart configuration
  547. //
  548. //
  549. // At this point pessDependentServices if pessDependentServices
  550. // is null then we didn't touch the services so don't reset.
  551. //
  552. if ( ppInfo && pessDependentServices )
  553. {
  554. schSCM = OpenSCManager(NULL,
  555. NULL,
  556. SC_MANAGER_ENUMERATE_SERVICE);
  557. if ( schSCM == NULL )
  558. {
  559. hresReapply = HRESULT_FROM_WIN32( GetLastError() );
  560. }
  561. else
  562. {
  563. for ( i = 0 ; i < dwNumServices ; ++i )
  564. {
  565. if ( ppInfo[i] )
  566. {
  567. schSrv = OpenService( schSCM,
  568. pessDependentServices[i].lpServiceName,
  569. SERVICE_QUERY_CONFIG |
  570. SERVICE_CHANGE_CONFIG |
  571. SERVICE_QUERY_STATUS |
  572. SERVICE_START );
  573. if ( schSrv )
  574. {
  575. // OutputDebugStringW(L"Service = ");
  576. // OutputDebugStringW(pessDependentServices[i].lpServiceName);
  577. // OutputDebugStringW(L"\n");
  578. //
  579. // If any of these things are not true, then we want to wait
  580. // for the pid to change, or the service to become stopped
  581. // before adding back in the changes that we removed. If these
  582. // things are all true, then adding back in the actions won't really
  583. // matter because the SCM won't do anything with the actions.
  584. //
  585. if ( ((LPSERVICE_FAILURE_ACTIONS) ppInfo[i])->cActions != 3 ||
  586. ((LPSERVICE_FAILURE_ACTIONS) ppInfo[i])->lpsaActions == NULL ||
  587. ((LPSERVICE_FAILURE_ACTIONS) ppInfo[i])->lpsaActions[0].Type != SC_ACTION_NONE ||
  588. ((LPSERVICE_FAILURE_ACTIONS) ppInfo[i])->lpsaActions[1].Type != SC_ACTION_NONE ||
  589. ((LPSERVICE_FAILURE_ACTIONS) ppInfo[i])->lpsaActions[2].Type != SC_ACTION_NONE )
  590. {
  591. // Wait for the service to be marked as stopped,
  592. // just for a certain amount of time.
  593. for ( DWORD x = 0; x < 10; x++ )
  594. {
  595. if ( QueryServiceStatusEx( schSrv,
  596. SC_STATUS_PROCESS_INFO,
  597. (LPBYTE)&status,
  598. sizeof( status ),
  599. &cbNeeded ) )
  600. {
  601. if ( adwPid && status.dwProcessId != adwPid[i] )
  602. {
  603. // pid didn't match, not the same
  604. // process we were looking at when
  605. // we went to kill.
  606. break;
  607. }
  608. if ( status.dwCurrentState == SERVICE_STOPPED )
  609. {
  610. break;
  611. }
  612. }
  613. else
  614. {
  615. break;
  616. }
  617. Sleep ( 100 );
  618. }
  619. }
  620. if ( !pfnChangeServiceConfig2( schSrv,
  621. SERVICE_CONFIG_FAILURE_ACTIONS,
  622. ppInfo[i] ) )
  623. {
  624. hresReapply = HRESULT_FROM_WIN32( GetLastError() );
  625. }
  626. CloseServiceHandle( schSrv );
  627. }
  628. else
  629. {
  630. hresReapply = HRESULT_FROM_WIN32( GetLastError() );
  631. }
  632. }
  633. }
  634. CloseServiceHandle( schSCM );
  635. }
  636. }
  637. // If we tried the kills and they failed then
  638. // we want to report back that error. Note that
  639. // we check that the setup phase worked before
  640. // we even try the kill phase, so we can just
  641. // assume that.
  642. if ( FAILED(hresKill) )
  643. {
  644. hres = hresKill;
  645. }
  646. if ( SUCCEEDED(hres) && FAILED(hresReapply) )
  647. {
  648. hres = hresReapply;
  649. }
  650. ReportStatus( IRSTAM_KILL, hres );
  651. if ( hAdvapi )
  652. {
  653. FreeLibrary( hAdvapi );
  654. }
  655. //
  656. // cleanup
  657. //
  658. if ( ppInfo )
  659. {
  660. for ( i = 0 ; i < dwNumServices ; ++i )
  661. {
  662. if ( ppInfo[i] )
  663. {
  664. LocalFree( ppInfo[i] );
  665. }
  666. }
  667. LocalFree( ppInfo );
  668. }
  669. if ( pbServiceList != NULL
  670. && pbServiceList != abServiceList )
  671. {
  672. LocalFree( pbServiceList );
  673. }
  674. if ( adwPid )
  675. {
  676. delete [] adwPid;
  677. }
  678. return hres;
  679. }
  680. //
  681. // Helper functions
  682. //
  683. VOID
  684. EnableShutdownPrivilege(
  685. VOID
  686. )
  687. /*++
  688. EnableShutdownPrivilege
  689. Enable shutdown privilege ( required to call ExitWindowsEx )
  690. Arguments:
  691. None
  692. Returns:
  693. Nothing. If error enabling the privilege the dependent operation
  694. will fail.
  695. --*/
  696. {
  697. HANDLE ProcessHandle;
  698. HANDLE TokenHandle = NULL;
  699. BOOL Result;
  700. LUID ShutdownValue;
  701. TOKEN_PRIVILEGES TokenPrivileges;
  702. ProcessHandle = OpenProcess(
  703. PROCESS_QUERY_INFORMATION,
  704. FALSE,
  705. GetCurrentProcessId()
  706. );
  707. if ( ProcessHandle == NULL ) {
  708. //
  709. // This should not happen
  710. //
  711. goto Cleanup;
  712. }
  713. Result = OpenProcessToken (
  714. ProcessHandle,
  715. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
  716. &TokenHandle
  717. );
  718. if ( !Result ) {
  719. //
  720. // This should not happen
  721. //
  722. goto Cleanup;
  723. }
  724. //
  725. // Find out the value of Shutdown privilege
  726. //
  727. Result = LookupPrivilegeValue(
  728. NULL,
  729. SE_SHUTDOWN_NAME,
  730. &ShutdownValue
  731. );
  732. if ( !Result ) {
  733. goto Cleanup;
  734. }
  735. //
  736. // Set up the privilege set we will need
  737. //
  738. TokenPrivileges.PrivilegeCount = 1;
  739. TokenPrivileges.Privileges[0].Luid = ShutdownValue;
  740. TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  741. (VOID) AdjustTokenPrivileges (
  742. TokenHandle,
  743. FALSE,
  744. &TokenPrivileges,
  745. sizeof(TokenPrivileges),
  746. NULL,
  747. NULL
  748. );
  749. Cleanup:
  750. if ( TokenHandle )
  751. {
  752. CloseHandle( TokenHandle );
  753. }
  754. if ( ProcessHandle )
  755. {
  756. CloseHandle( ProcessHandle );
  757. }
  758. }
  759. HRESULT
  760. StartStopAll(
  761. LPTSTR pszRoot,
  762. BOOL fStart,
  763. DWORD dwTimeoutMsecs
  764. )
  765. /*++
  766. StartStopAll
  767. start or stop services dependency tree starting with specified root service
  768. Arguments:
  769. pszRoot - root of the service tree
  770. fStart - TRUE to start services, FALSE to stop
  771. dwTimeoutMsecs - timeout for status check ( in ms )
  772. Returns:
  773. COM status
  774. --*/
  775. {
  776. SC_HANDLE schSCM = NULL;
  777. SC_HANDLE schRoot = NULL;
  778. HRESULT hresReturn = S_OK;
  779. ENUM_SERVICE_STATUS ess;
  780. schSCM = OpenSCManager(NULL,
  781. NULL,
  782. SC_MANAGER_CONNECT);
  783. if ( schSCM == NULL )
  784. {
  785. hresReturn = HRESULT_FROM_WIN32( GetLastError() );
  786. }
  787. else
  788. {
  789. schRoot = OpenService( schSCM,
  790. pszRoot,
  791. SERVICE_ALL_ACCESS );
  792. if ( schRoot != NULL )
  793. {
  794. if ( !QueryServiceStatus( schRoot, &ess.ServiceStatus ) )
  795. {
  796. hresReturn = HRESULT_FROM_WIN32( GetLastError() );
  797. }
  798. CloseServiceHandle( schRoot );
  799. if ( SUCCEEDED( hresReturn )
  800. && ( fStart
  801. || ess.ServiceStatus.dwCurrentState != SERVICE_STOPPED) )
  802. {
  803. ess.lpServiceName = pszRoot;
  804. // if it's stopped, then whack the dllhosts that have wam.dll loaded
  805. if (ess.ServiceStatus.dwCurrentState == SERVICE_STOPPED)
  806. {
  807. KillTaskByName(_T("DLLHOST"),"wam.dll"); // COM+ WAM Containers
  808. }
  809. hresReturn = StartStopAllRecursive( schSCM, &ess, 1, fStart, TRUE, &dwTimeoutMsecs );
  810. }
  811. // check out the current state of the service
  812. schRoot = OpenService( schSCM, pszRoot, SERVICE_ALL_ACCESS );
  813. if ( schRoot != NULL )
  814. {
  815. if ( QueryServiceStatus( schRoot, &ess.ServiceStatus ) )
  816. {
  817. // if it's stopped, then whack the dllhosts that have wam.dll loaded
  818. if (ess.ServiceStatus.dwCurrentState == SERVICE_STOPPED)
  819. {
  820. KillTaskByName(_T("DLLHOST"),"wam.dll"); // COM+ WAM Containers
  821. }
  822. }
  823. CloseServiceHandle( schRoot );
  824. }
  825. }
  826. else
  827. {
  828. hresReturn = HRESULT_FROM_WIN32( GetLastError() );
  829. }
  830. CloseServiceHandle( schSCM );
  831. }
  832. return hresReturn;
  833. }
  834. StartStopAllRecursive(
  835. SC_HANDLE schSCM,
  836. ENUM_SERVICE_STATUS* pessRoot,
  837. DWORD dwNumServices,
  838. BOOL fStart,
  839. BOOL fForceDemandStart,
  840. LPDWORD pdwTimeoutMsecs
  841. )
  842. /*++
  843. StartStopAllRecursive
  844. start or stop services dependency tree starting with specified root service
  845. Arguments:
  846. schSCM - handle to SCM
  847. pessRoot - list of services to start/stop recursively
  848. fStart - TRUE to start services, FALSE to stop
  849. fForceDemandStart - for start requests: TRUE to force start
  850. if SERVICE_DEMAND_START. Otherwise only start if service
  851. is auto start ( including boot & system start )
  852. dwTimeoutMsecs - timeout for status check ( in ms )
  853. Returns:
  854. COM status
  855. --*/
  856. {
  857. DWORD dwBytesNeeded;
  858. DWORD dwNumRecServices = 0;
  859. HRESULT hresReturn = S_OK;
  860. BYTE abServiceList[2048];
  861. LPBYTE pbServiceList = NULL;
  862. BYTE abServiceConfig[1024];
  863. LPQUERY_SERVICE_CONFIG pServiceConfig = NULL;
  864. SC_HANDLE* phServiceHandle = NULL;
  865. DWORD i;
  866. DWORD dwServiceConfigSize;
  867. SERVICE_STATUS ServiceStatus;
  868. DWORD dwSleepInterval = SLEEP_INTERVAL;
  869. if ( dwNumServices != 0 &&
  870. ( pessRoot == NULL ||
  871. pdwTimeoutMsecs == NULL ) )
  872. {
  873. return E_INVALIDARG;
  874. }
  875. if ( (phServiceHandle = (SC_HANDLE*)LocalAlloc( LMEM_FIXED|LMEM_ZEROINIT,
  876. dwNumServices * sizeof(SC_HANDLE) )) == NULL )
  877. {
  878. hresReturn = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  879. }
  880. if ( SUCCEEDED(hresReturn) )
  881. {
  882. //
  883. // All services will be started/stopped at once
  884. // then periodically checked for status until all of them are running/stopped
  885. // or some error occured or timeout
  886. //
  887. if ( dwNumServices != 0 )
  888. {
  889. pServiceConfig = (LPQUERY_SERVICE_CONFIG)abServiceConfig;
  890. dwServiceConfigSize = sizeof( abServiceConfig );
  891. //
  892. // Open handles and send service control start command
  893. //
  894. for ( i = 0 ;
  895. i < dwNumServices && SUCCEEDED(hresReturn) ;
  896. i++)
  897. {
  898. //
  899. // Send command to Services
  900. //
  901. phServiceHandle[i] = OpenService( schSCM,
  902. pessRoot[i].lpServiceName,
  903. SERVICE_ALL_ACCESS );
  904. if ( phServiceHandle[i] != NULL )
  905. {
  906. if ( fStart )
  907. {
  908. //
  909. // Query service config to check if service should be started
  910. // based on its Start Type.
  911. //
  912. if ( !QueryServiceConfig( phServiceHandle[i],
  913. (LPQUERY_SERVICE_CONFIG)abServiceConfig,
  914. dwServiceConfigSize,
  915. &dwBytesNeeded ) )
  916. {
  917. if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
  918. {
  919. // If we are re-allocating and we all ready allocated once
  920. // before first free up the memory.
  921. if ( pServiceConfig != (LPQUERY_SERVICE_CONFIG) abServiceConfig )
  922. {
  923. LocalFree( pServiceConfig );
  924. }
  925. if ( (pServiceConfig = (LPQUERY_SERVICE_CONFIG)LocalAlloc(
  926. LMEM_FIXED,
  927. dwBytesNeeded )) == NULL )
  928. {
  929. hresReturn = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  930. }
  931. else
  932. {
  933. dwServiceConfigSize = dwBytesNeeded;
  934. if ( !QueryServiceConfig( phServiceHandle[i],
  935. (LPQUERY_SERVICE_CONFIG)pServiceConfig,
  936. dwServiceConfigSize,
  937. &dwBytesNeeded ) )
  938. {
  939. hresReturn = HRESULT_FROM_WIN32( GetLastError() );
  940. }
  941. }
  942. }
  943. else
  944. {
  945. hresReturn = HRESULT_FROM_WIN32( GetLastError() );
  946. }
  947. }
  948. if ( SUCCEEDED(hresReturn) )
  949. {
  950. //
  951. // Check if service auto start except if fForceDemandStart
  952. // specified. ForceDemandStart will only be specified for
  953. // the service that the command is directly issued on. This
  954. // means that it will only be specified for IISADMIN.
  955. //
  956. if ( ( fForceDemandStart && pServiceConfig->dwStartType == SERVICE_DEMAND_START )
  957. || ( pServiceConfig->dwStartType == SERVICE_BOOT_START ||
  958. pServiceConfig->dwStartType == SERVICE_SYSTEM_START ||
  959. pServiceConfig->dwStartType == SERVICE_AUTO_START ) )
  960. {
  961. StartService( phServiceHandle[i], 0, NULL );
  962. //
  963. // Ask for only the services that are inactive. So, for instance,
  964. // if we are attempting to restart the iisadmin service,
  965. // and W3SVC is still active, we won't send it a command to restart.
  966. //
  967. hresReturn = EnumStartServices( schSCM,
  968. pessRoot[i].lpServiceName,
  969. SERVICE_INACTIVE,
  970. abServiceList,
  971. sizeof(abServiceList),
  972. &pbServiceList,
  973. &dwNumRecServices,
  974. FALSE );
  975. if ( SUCCEEDED( hresReturn ) )
  976. {
  977. hresReturn = StartStopAllRecursive( schSCM,
  978. (ENUM_SERVICE_STATUS*)pbServiceList,
  979. dwNumRecServices,
  980. fStart,
  981. FALSE,
  982. pdwTimeoutMsecs );
  983. if ( pbServiceList != NULL
  984. && pbServiceList != abServiceList )
  985. {
  986. LocalFree( pbServiceList );
  987. }
  988. }
  989. }
  990. else
  991. {
  992. //
  993. // Don't want to start this service, so mark it
  994. // as already running
  995. //
  996. if (wcscmp(pessRoot[i].lpServiceName,_T("IISADMIN")) == 0)
  997. {
  998. hresReturn = HRESULT_FROM_WIN32(ERROR_SERVICE_NOT_ACTIVE);
  999. }
  1000. else
  1001. {
  1002. pessRoot[i].ServiceStatus.dwCurrentState = SERVICE_RUNNING;
  1003. }
  1004. }
  1005. }
  1006. }
  1007. else // handle stopping the service
  1008. {
  1009. if ( W3SVCandHTTPFilter(i, pessRoot, dwNumServices) )
  1010. {
  1011. continue;
  1012. }
  1013. // Remember if the service was stopped to start with.
  1014. BOOL fServiceWasStoppedToStartWith = ( pessRoot[i].ServiceStatus.dwCurrentState == SERVICE_STOPPED );
  1015. // We will also need to stop dependent services if the are started all ready.
  1016. BOOL fHasDependentServices = FALSE;
  1017. if ( !fServiceWasStoppedToStartWith )
  1018. {
  1019. //
  1020. // if the service was not stopped to start with
  1021. // we need to tell the service to stop.
  1022. //
  1023. hresReturn = SendControlToService( phServiceHandle[i],
  1024. SERVICE_CONTROL_STOP,
  1025. pdwTimeoutMsecs );
  1026. if ( hresReturn == HRESULT_FROM_WIN32( ERROR_SERVICE_REQUEST_TIMEOUT ) )
  1027. {
  1028. //
  1029. // WARNING!
  1030. //
  1031. // We're in trouble. Service did not respond in a timely fashion,
  1032. // and further attempt to use this handle ( including closing it )
  1033. // will also hang, so cancel the handle and leak it
  1034. //
  1035. phServiceHandle[i] = NULL;
  1036. }
  1037. else if ( hresReturn == HRESULT_FROM_WIN32( ERROR_DEPENDENT_SERVICES_RUNNING ) )
  1038. {
  1039. fHasDependentServices = TRUE;
  1040. }
  1041. }
  1042. //
  1043. // If it was stopped to start with, or if it was not but it couldn't
  1044. // be stopped because it has dependent services. Go ahead and stop
  1045. // the dependent services.
  1046. //
  1047. if ( fHasDependentServices || fServiceWasStoppedToStartWith )
  1048. {
  1049. //
  1050. // Get the services that are active because we
  1051. // are only interested in stopping services that
  1052. // are actually running.
  1053. //
  1054. hresReturn = EnumStartServices( schSCM,
  1055. pessRoot[i].lpServiceName,
  1056. SERVICE_ACTIVE,
  1057. abServiceList,
  1058. sizeof(abServiceList),
  1059. &pbServiceList,
  1060. &dwNumRecServices,
  1061. FALSE );
  1062. if ( SUCCEEDED( hresReturn ) )
  1063. {
  1064. hresReturn = StartStopAllRecursive( schSCM,
  1065. (ENUM_SERVICE_STATUS*)pbServiceList,
  1066. dwNumRecServices,
  1067. fStart,
  1068. FALSE,
  1069. pdwTimeoutMsecs );
  1070. if ( pbServiceList != NULL
  1071. && pbServiceList != abServiceList )
  1072. {
  1073. LocalFree( pbServiceList );
  1074. }
  1075. }
  1076. if ( SUCCEEDED( hresReturn ) )
  1077. {
  1078. //
  1079. // If the service itself is not all ready stopped, then stop
  1080. // the service. It could be that it is stopped ( due to a crash )
  1081. // and the other services that were dependent on them are still
  1082. // running.
  1083. //
  1084. if ( !fServiceWasStoppedToStartWith )
  1085. {
  1086. hresReturn = SendControlToService( phServiceHandle[i],
  1087. SERVICE_CONTROL_STOP,
  1088. pdwTimeoutMsecs );
  1089. // if we can hang above we could hang here...
  1090. if ( hresReturn == HRESULT_FROM_WIN32( ERROR_SERVICE_REQUEST_TIMEOUT ) )
  1091. {
  1092. //
  1093. // WARNING!
  1094. //
  1095. // We're in trouble. Service did not respond in a timely fashion,
  1096. // and further attempt to use this handle ( including closing it )
  1097. // will also hang, so cancel the handle and leak it
  1098. //
  1099. phServiceHandle[i] = NULL;
  1100. }
  1101. }
  1102. }
  1103. }
  1104. if ( FAILED( hresReturn ) )
  1105. {
  1106. break;
  1107. }
  1108. } // end of stopping code
  1109. } // end of valid service handle
  1110. } // end of loop
  1111. //
  1112. // Check service running
  1113. //
  1114. if ( (*pdwTimeoutMsecs < dwSleepInterval) && *pdwTimeoutMsecs )
  1115. {
  1116. dwSleepInterval = *pdwTimeoutMsecs;
  1117. }
  1118. for ( ;
  1119. SUCCEEDED( hresReturn );
  1120. )
  1121. {
  1122. for ( i = 0 ;
  1123. i < dwNumServices;
  1124. i++)
  1125. {
  1126. //
  1127. // Only query status for services known to be not running
  1128. //
  1129. if ( pessRoot[i].ServiceStatus.dwCurrentState
  1130. != (DWORD)(fStart ? SERVICE_RUNNING : SERVICE_STOPPED) )
  1131. {
  1132. if ( QueryServiceStatus( phServiceHandle[i], &ServiceStatus ) )
  1133. {
  1134. //
  1135. // remember status
  1136. //
  1137. pessRoot[i].ServiceStatus.dwCurrentState = ServiceStatus.dwCurrentState;
  1138. if ( fStart && ServiceStatus.dwCurrentState == SERVICE_STOPPED )
  1139. {
  1140. //
  1141. // Service died during startup. no point keeping polling
  1142. // for service state : return an error
  1143. //
  1144. hresReturn = HRESULT_FROM_WIN32( ERROR_SERVICE_NOT_ACTIVE );
  1145. break;
  1146. }
  1147. if ( ServiceStatus.dwCurrentState != (DWORD)(fStart ? SERVICE_RUNNING : SERVICE_STOPPED) )
  1148. {
  1149. //
  1150. // will keep looping waiting for target service state
  1151. //
  1152. break;
  1153. }
  1154. }
  1155. else
  1156. {
  1157. hresReturn = HRESULT_FROM_WIN32( GetLastError() );
  1158. break;
  1159. }
  1160. }
  1161. }
  1162. //
  1163. // if we did not checked all services then at least one of them
  1164. // is not running ( or some error occured )
  1165. //
  1166. if ( SUCCEEDED( hresReturn ) && i != dwNumServices )
  1167. {
  1168. if ( dwSleepInterval > *pdwTimeoutMsecs )
  1169. {
  1170. hresReturn = HRESULT_FROM_WIN32( ERROR_SERVICE_REQUEST_TIMEOUT );
  1171. }
  1172. else
  1173. {
  1174. Sleep( dwSleepInterval );
  1175. *pdwTimeoutMsecs -= dwSleepInterval;
  1176. }
  1177. }
  1178. else
  1179. {
  1180. break;
  1181. }
  1182. }
  1183. //
  1184. // close service handles
  1185. //
  1186. for ( i = 0 ;
  1187. i < dwNumServices;
  1188. i++)
  1189. {
  1190. if ( phServiceHandle[i] != NULL )
  1191. {
  1192. CloseServiceHandle( phServiceHandle[i] );
  1193. }
  1194. }
  1195. }
  1196. LocalFree( phServiceHandle );
  1197. }
  1198. if ( pServiceConfig != NULL
  1199. && pServiceConfig != (LPQUERY_SERVICE_CONFIG)abServiceConfig )
  1200. {
  1201. LocalFree( pServiceConfig );
  1202. }
  1203. return hresReturn;
  1204. }
  1205. extern "C"
  1206. DWORD WINAPI
  1207. ControlServiceThread(
  1208. LPVOID p
  1209. )
  1210. /*++
  1211. ControlServiceThread
  1212. Send a command to a service
  1213. Arguments:
  1214. p - ptr to SERVICE_COMMAND_CONTROL_BLOCK
  1215. Returns:
  1216. 0
  1217. --*/
  1218. {
  1219. SERVICE_STATUS ssStatus;
  1220. SERVICE_COMMAND_CONTROL_BLOCK* pCB = (SERVICE_COMMAND_CONTROL_BLOCK*)p;
  1221. if ( pCB == NULL )
  1222. {
  1223. return ERROR_INVALID_PARAMETER;
  1224. }
  1225. if ( !ControlService( pCB->hServiceHandle, pCB->dwCmd, &ssStatus ) )
  1226. {
  1227. DWORD err = GetLastError();
  1228. // do not report error if try to stop a stopped service
  1229. if (err == ERROR_SERVICE_NOT_ACTIVE && pCB->dwCmd == SERVICE_CONTROL_STOP)
  1230. {
  1231. pCB->hres = S_OK;
  1232. }
  1233. else
  1234. {
  1235. pCB->hres = HRESULT_FROM_WIN32( err );
  1236. }
  1237. }
  1238. else
  1239. {
  1240. pCB->hres = S_OK;
  1241. }
  1242. //nasty side affect of this function, if the count hit's
  1243. //zero we delete it here. Leaving P to point off, however
  1244. //p does hold a ref count for itself so this should actually work fine.
  1245. if ( InterlockedDecrement( &pCB->lRefCount ) == 0 )
  1246. {
  1247. delete pCB;
  1248. }
  1249. return ERROR_SUCCESS;
  1250. }
  1251. HRESULT
  1252. SendControlToService(
  1253. SC_HANDLE hServiceHandle,
  1254. DWORD dwCmd,
  1255. LPDWORD pdwTimeoutMsecs
  1256. )
  1257. /*++
  1258. ControlServiceThread
  1259. Send a command to a service with timeout
  1260. Arguments:
  1261. hServiceHandle - service to control
  1262. dwCmd - command to send to service
  1263. pdwTimeoutMsecs - timeout ( in ms ). updated on output based on time
  1264. spent waiting for service status
  1265. Returns:
  1266. ERROR_SERVICE_REQUEST_TIMEOUT if timeout
  1267. otherwise COM status
  1268. --*/
  1269. {
  1270. HANDLE hT;
  1271. DWORD dwID;
  1272. DWORD dwBefore;
  1273. DWORD dwAfter;
  1274. SERVICE_COMMAND_CONTROL_BLOCK* pCB;
  1275. DWORD dwTimeoutMsecs;
  1276. HRESULT hres;
  1277. if ( pdwTimeoutMsecs == NULL )
  1278. {
  1279. return E_INVALIDARG;
  1280. }
  1281. dwTimeoutMsecs = *pdwTimeoutMsecs;
  1282. //
  1283. // Default timeout for ControlService is 120s, which is too long for us
  1284. // so we create a thread to call ControlService and wait for thread
  1285. // termination.
  1286. // Communication between threads is handled by a refcounted control block
  1287. //
  1288. pCB = new SERVICE_COMMAND_CONTROL_BLOCK;
  1289. if ( pCB != NULL )
  1290. {
  1291. pCB->lRefCount = 2; // 1 for caller, 1 for callee
  1292. pCB->dwCmd = dwCmd;
  1293. pCB->hServiceHandle = hServiceHandle;
  1294. pCB->hres = S_OK;
  1295. dwBefore = GetTickCount();
  1296. hT = CreateThread( NULL,
  1297. 0,
  1298. (LPTHREAD_START_ROUTINE)ControlServiceThread,
  1299. (LPVOID)pCB,
  1300. 0,
  1301. &dwID );
  1302. if ( hT != NULL )
  1303. {
  1304. if ( WaitForSingleObject( hT, dwTimeoutMsecs ) == WAIT_OBJECT_0 )
  1305. {
  1306. hres = pCB->hres;
  1307. }
  1308. else
  1309. {
  1310. hres = HRESULT_FROM_WIN32( ERROR_SERVICE_REQUEST_TIMEOUT );
  1311. }
  1312. CloseHandle( hT );
  1313. if ( InterlockedDecrement( &pCB->lRefCount ) == 0 )
  1314. {
  1315. delete pCB;
  1316. }
  1317. //
  1318. // Update caller's timeout
  1319. //
  1320. dwAfter = GetTickCount();
  1321. if ( dwAfter > dwBefore )
  1322. {
  1323. if ( dwAfter - dwBefore <= dwTimeoutMsecs )
  1324. {
  1325. *pdwTimeoutMsecs -= dwAfter - dwBefore;
  1326. }
  1327. else
  1328. {
  1329. *pdwTimeoutMsecs = 0;
  1330. }
  1331. }
  1332. }
  1333. else
  1334. {
  1335. delete pCB;
  1336. hres = HRESULT_FROM_WIN32( GetLastError() );
  1337. }
  1338. }
  1339. else
  1340. {
  1341. hres = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  1342. }
  1343. return hres;
  1344. }
  1345. HRESULT
  1346. SerializeEnumServiceBuffer(
  1347. LPENUM_SERVICE_STATUS pessDependentServices,
  1348. DWORD dwNumServices,
  1349. LPBYTE pbBuffer,
  1350. DWORD dwBufferSize,
  1351. LPDWORD pdwMDRequiredBufferSize
  1352. )
  1353. /*++
  1354. SerializeEnumServiceBuffer
  1355. Serialize array of ENUM_SERVICE_STATUS to buffer,
  1356. replacing ptr by offset in buffer
  1357. Arguments:
  1358. pessDependentServices - array of ENUM_SERVICE_STATUS to serialize
  1359. dwNumServices - # of entries in pessDependentServices
  1360. pbBuffer - buffer filled with serialized status as array of SERIALIZED_ENUM_SERVICE_STATUS
  1361. dwBufferSize - maximum size of pbBuffer
  1362. pdwMDRequiredBufferSize - updated with required size if dwBufferSize too small
  1363. Returns:
  1364. ERROR_INSUFFICIENT_BUFFER if dwBufferSize too small
  1365. otherwise COM status
  1366. --*/
  1367. {
  1368. HRESULT hresReturn = S_OK;
  1369. DWORD dwMinSize = 0;
  1370. UINT i;
  1371. if ( dwNumServices != 0 &&
  1372. pessDependentServices == NULL )
  1373. {
  1374. return E_INVALIDARG;
  1375. }
  1376. if ( !pbBuffer )
  1377. {
  1378. dwBufferSize = 0;
  1379. }
  1380. //
  1381. // size of output buffer is based on size of array of SERIALIZED_ENUM_SERVICE_STATUS
  1382. // plus size of all strings : service name & display name for each entry
  1383. //
  1384. dwMinSize = sizeof(SERIALIZED_ENUM_SERVICE_STATUS) * dwNumServices;
  1385. for ( i = 0 ;
  1386. i < dwNumServices ;
  1387. ++i )
  1388. {
  1389. UINT cServiceName = (DWORD) _tcslen( pessDependentServices[i].lpServiceName ) + 1;
  1390. UINT cDisplayName = (DWORD) _tcslen( pessDependentServices[i].lpDisplayName ) + 1;
  1391. //
  1392. // do not update if output buffer is too small, but keep looping to determine
  1393. // total size
  1394. //
  1395. if ( dwBufferSize >= dwMinSize + (cServiceName + cDisplayName) * sizeof(TCHAR) )
  1396. {
  1397. //
  1398. // copy service status as is
  1399. //
  1400. ((SERIALIZED_ENUM_SERVICE_STATUS*)pbBuffer)[i].ServiceStatus =
  1401. pessDependentServices[i].ServiceStatus;
  1402. //
  1403. // copy string and convert ptr to string to index in output buffer
  1404. //
  1405. memcpy( pbBuffer + dwMinSize, pessDependentServices[i].lpServiceName, cServiceName * sizeof(TCHAR) );
  1406. ((SERIALIZED_ENUM_SERVICE_STATUS*)pbBuffer)[i].iServiceName = dwMinSize ;
  1407. memcpy( pbBuffer + dwMinSize + cServiceName * sizeof(TCHAR), pessDependentServices[i].lpDisplayName, cDisplayName * sizeof(TCHAR) );
  1408. ((SERIALIZED_ENUM_SERVICE_STATUS*)pbBuffer)[i].iDisplayName = dwMinSize + cServiceName * sizeof(TCHAR) ;
  1409. }
  1410. dwMinSize += (cServiceName + cDisplayName) * sizeof(TCHAR) ;
  1411. }
  1412. if ( dwBufferSize < dwMinSize )
  1413. {
  1414. if ( pdwMDRequiredBufferSize )
  1415. {
  1416. *pdwMDRequiredBufferSize = dwMinSize;
  1417. }
  1418. hresReturn = HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER );
  1419. }
  1420. return hresReturn;
  1421. }
  1422. STDMETHODIMP
  1423. CIisRestart::Status(
  1424. DWORD dwBufferSize,
  1425. unsigned char * pbBuffer,
  1426. DWORD * pdwMDRequiredBufferSize,
  1427. DWORD * pdwNumServices
  1428. )
  1429. /*++
  1430. Status
  1431. Return status of all internet services as array of ENUM_SERVICE_STATUS
  1432. Arguments:
  1433. dwBufferSize - maximum size of pbBuffer
  1434. pbBuffer - buffer filled with serialized status as array of SERIALIZED_ENUM_SERVICE_STATUS
  1435. pdwMDRequiredBufferSize - updated with required size if dwBufferSize too small
  1436. pdwNumServices - updated with number of entries stored in pbBuffer
  1437. Returns:
  1438. ERROR_RESOURCE_DISABLED if access to restart commands disabled
  1439. ERROR_INSUFFICIENT_BUFFER if dwBufferSize too small
  1440. otherwise COM status
  1441. --*/
  1442. {
  1443. SC_HANDLE schSCM = NULL;
  1444. HRESULT hresReturn = E_FAIL;
  1445. BYTE abServiceList[2048];
  1446. LPBYTE pbServiceList = NULL;
  1447. LPENUM_SERVICE_STATUS pessDependentServices;
  1448. if ( pdwNumServices == NULL )
  1449. {
  1450. return E_INVALIDARG;
  1451. }
  1452. if ( !IsEnableRemote() )
  1453. {
  1454. hresReturn = HRESULT_FROM_WIN32( ERROR_RESOURCE_DISABLED );
  1455. }
  1456. else
  1457. {
  1458. schSCM = OpenSCManager(NULL,
  1459. NULL,
  1460. SC_MANAGER_CONNECT);
  1461. if ( schSCM == NULL )
  1462. {
  1463. hresReturn = HRESULT_FROM_WIN32( GetLastError() );
  1464. }
  1465. else
  1466. {
  1467. hresReturn = EnumStartServices( schSCM,
  1468. _T("IISADMIN"),
  1469. SERVICE_STATE_ALL,
  1470. abServiceList,
  1471. sizeof(abServiceList),
  1472. &pbServiceList,
  1473. pdwNumServices,
  1474. FALSE );
  1475. if ( SUCCEEDED(hresReturn) )
  1476. {
  1477. pessDependentServices = (LPENUM_SERVICE_STATUS)pbServiceList;
  1478. hresReturn = SerializeEnumServiceBuffer( (LPENUM_SERVICE_STATUS)pbServiceList,
  1479. *pdwNumServices,
  1480. pbBuffer,
  1481. dwBufferSize,
  1482. pdwMDRequiredBufferSize );
  1483. if ( pbServiceList != NULL
  1484. && pbServiceList != abServiceList )
  1485. {
  1486. LocalFree( pbServiceList );
  1487. }
  1488. }
  1489. CloseServiceHandle(schSCM);
  1490. }
  1491. }
  1492. return hresReturn;
  1493. }
  1494. HRESULT
  1495. EnumStartServices(
  1496. SC_HANDLE schSCM,
  1497. LPTSTR pszRoot,
  1498. DWORD dwTargetState,
  1499. LPBYTE abServiceList,
  1500. DWORD dwInitializeServiceListSize,
  1501. LPBYTE* ppbServiceList,
  1502. LPDWORD pdwNumServices,
  1503. BOOL fAddIisadmin
  1504. )
  1505. /*++
  1506. EnumStartServices
  1507. Enumerate dependent services to output buffer as array of ENUM_SERVICE_STATUS
  1508. Arguments:
  1509. schSCM - handle to SCM
  1510. pszRoot - service for which to enumerate dependencies
  1511. dwTargetState - dwServiceState for call to EnumDependentServices()
  1512. abServiceList - initial output buffer
  1513. dwInitializeServiceListSize - maximum size of abServiceList
  1514. ppbServiceList - updated with output buffer, may be abServiceList if long enough
  1515. otherwise returned buffer is to be freed using LocalFree()
  1516. pdwNumServices - updated with number of entries stored in pbBuffer
  1517. fAddIisadmin - TRUE to add IISADMIN to list of dependent services
  1518. Returns:
  1519. COM status
  1520. --*/
  1521. {
  1522. HRESULT hresReturn = S_OK;
  1523. SC_HANDLE schIISADMIN = NULL;
  1524. DWORD dwBytesNeeded;
  1525. DWORD dwAddSize = 0;
  1526. DWORD dwOffsetSize = 0;
  1527. if ( ppbServiceList == NULL ||
  1528. pdwNumServices == NULL )
  1529. {
  1530. return E_INVALIDARG;
  1531. }
  1532. *ppbServiceList = NULL;
  1533. schIISADMIN = OpenService(schSCM,
  1534. pszRoot,
  1535. STANDARD_RIGHTS_REQUIRED |
  1536. SERVICE_ENUMERATE_DEPENDENTS);
  1537. if (schIISADMIN == NULL)
  1538. {
  1539. hresReturn = HRESULT_FROM_WIN32(GetLastError());
  1540. }
  1541. else
  1542. {
  1543. if ( fAddIisadmin )
  1544. {
  1545. //
  1546. // if initial size too small for Iisadmin description then fail
  1547. //
  1548. dwOffsetSize = sizeof(ENUM_SERVICE_STATUS );
  1549. dwAddSize = dwOffsetSize;
  1550. if ( dwAddSize > dwInitializeServiceListSize )
  1551. {
  1552. hresReturn = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  1553. goto Cleanup;
  1554. }
  1555. //
  1556. // Use global static name for IISADMIN, no need to copy it to output buffer
  1557. //
  1558. ((LPENUM_SERVICE_STATUS)abServiceList)->lpDisplayName = _T("IISADMIN");
  1559. ((LPENUM_SERVICE_STATUS)abServiceList)->lpServiceName = _T("IISADMIN");
  1560. //
  1561. // don't want to check service status at this point as it may be stuck
  1562. // so assume RUNNING.
  1563. //
  1564. ((LPENUM_SERVICE_STATUS)abServiceList)->ServiceStatus.dwCurrentState = SERVICE_RUNNING;
  1565. }
  1566. if (!EnumDependentServices( schIISADMIN,
  1567. dwTargetState,
  1568. (LPENUM_SERVICE_STATUS)(abServiceList + dwOffsetSize),
  1569. dwInitializeServiceListSize - dwAddSize,
  1570. &dwBytesNeeded,
  1571. pdwNumServices))
  1572. {
  1573. if (GetLastError() == ERROR_MORE_DATA)
  1574. {
  1575. if ( (*ppbServiceList = (LPBYTE)LocalAlloc( LMEM_FIXED,
  1576. dwBytesNeeded + dwAddSize )) == NULL )
  1577. {
  1578. hresReturn = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  1579. }
  1580. else
  1581. {
  1582. // dwOffsetSize is set to what dwAddSize was set to, so it is fine
  1583. // to memcpy this data, since we allocated more than dwAddSize above.
  1584. memcpy( *ppbServiceList, abServiceList, dwOffsetSize );
  1585. if (!EnumDependentServices( schIISADMIN,
  1586. SERVICE_INACTIVE,
  1587. (LPENUM_SERVICE_STATUS)(*ppbServiceList + dwOffsetSize),
  1588. dwBytesNeeded,
  1589. &dwBytesNeeded,
  1590. pdwNumServices))
  1591. {
  1592. hresReturn = HRESULT_FROM_WIN32( GetLastError() );
  1593. LocalFree( *ppbServiceList );
  1594. *ppbServiceList = NULL;
  1595. }
  1596. }
  1597. }
  1598. else
  1599. {
  1600. hresReturn = HRESULT_FROM_WIN32( GetLastError() );
  1601. }
  1602. }
  1603. else
  1604. {
  1605. *ppbServiceList = abServiceList;
  1606. }
  1607. }
  1608. Cleanup:
  1609. if ( schIISADMIN )
  1610. {
  1611. CloseServiceHandle( schIISADMIN );
  1612. }
  1613. if ( fAddIisadmin && SUCCEEDED( hresReturn ) )
  1614. {
  1615. ++*pdwNumServices;
  1616. }
  1617. return hresReturn;
  1618. }
  1619. BOOL
  1620. IsEnableRemote(
  1621. )
  1622. /*++
  1623. IsEnableRemote
  1624. Check if restart I/F enabled
  1625. ( based on HKLM\SOFTWARE\Microsoft\INetStp::EnableRestart::REG_DWORD )
  1626. Arguments:
  1627. None
  1628. Returns:
  1629. TRUE if enabled, otherwise FALSE
  1630. --*/
  1631. {
  1632. BOOL fSt = FALSE;
  1633. HKEY hKey;
  1634. DWORD dwValue;
  1635. DWORD dwType;
  1636. DWORD dwSize;
  1637. if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  1638. TEXT("SOFTWARE\\Microsoft\\INetStp"),
  1639. 0,
  1640. KEY_READ,
  1641. &hKey ) == ERROR_SUCCESS )
  1642. {
  1643. dwSize = sizeof( dwValue );
  1644. if ( RegQueryValueEx( hKey,
  1645. TEXT("EnableRestart"),
  1646. 0,
  1647. &dwType,
  1648. (LPBYTE)&dwValue,
  1649. &dwSize ) == ERROR_SUCCESS )
  1650. {
  1651. if ( dwType == REG_DWORD )
  1652. {
  1653. fSt = dwValue == 1;
  1654. }
  1655. else
  1656. {
  1657. fSt = TRUE;
  1658. }
  1659. }
  1660. else
  1661. {
  1662. fSt = TRUE;
  1663. }
  1664. RegCloseKey( hKey );
  1665. }
  1666. return fSt;
  1667. }
  1668. BOOL
  1669. CloseSystemExceptionHandler(
  1670. LPCTSTR pszWindowName
  1671. )
  1672. /*++
  1673. CloseSystemExceptionHandler
  1674. Send a close ( e.g. terminate apps without debugging ) command to the window
  1675. created by NT when a debugger is not configured to automatically start after app exception.
  1676. This window stays on screen until interactive user select either OK or debug app, which is
  1677. a problem for automated restart.
  1678. So we locate this window and send it a command requesting stop w/o debugging.
  1679. We locate the window by enumerating all windows and checking for window name beginning with the name
  1680. of the application that raised an exception, e.g. "inetinfo.exe"
  1681. Arguments:
  1682. pszWindowName - window name where to send terminate command
  1683. Returns:
  1684. TRUE if SUCCESS, otherwise FALSE
  1685. --*/
  1686. {
  1687. BOOL fSt = TRUE;
  1688. DWORD dwPid = 0;
  1689. HWND hwnd;
  1690. GetPidFromTitle( &dwPid, &hwnd, pszWindowName );
  1691. if ( dwPid )
  1692. {
  1693. //
  1694. // WARNING: major hack: turns out that WM_COMMAND 1 is the command to send to
  1695. // the exception handler to ask it to close application w/o debugging
  1696. // This works for NT5, may change in the future...
  1697. //
  1698. PostMessage( hwnd, WM_COMMAND, 1, 0 );
  1699. Sleep( 1000 );
  1700. }
  1701. else
  1702. {
  1703. fSt = TRUE;
  1704. }
  1705. return fSt;
  1706. }
  1707. HRESULT
  1708. KillTaskByName(
  1709. LPTSTR pname,
  1710. LPSTR pszMandatoryModule
  1711. )
  1712. /*++
  1713. KillTaskByName
  1714. Kill a process by name
  1715. Most of the code was taken from the Platform SDK kill,c sample, and
  1716. utilizes the common.c module included in this project.
  1717. Works only on NT platforms ( NOT Win 9x )
  1718. Arguments:
  1719. pname - name of process to kill ( name of executable w/o extension )
  1720. pszMandatoryModule - module name to look for, e.g. "wam.dll"
  1721. can be NULL for unconditional kill
  1722. Returns:
  1723. COM status
  1724. --*/
  1725. {
  1726. HRESULT hres = S_OK;
  1727. //
  1728. // Obtain the ability to manipulate other processes
  1729. //
  1730. EnableDebugPrivNT();
  1731. //
  1732. // get the task list for the system
  1733. //
  1734. hres = KillTask( pname, pszMandatoryModule );
  1735. return hres;
  1736. }
  1737. VOID
  1738. ReportStatus(
  1739. DWORD dwId,
  1740. DWORD dwStatus
  1741. )
  1742. /*++
  1743. ReportStatus
  1744. Log status event
  1745. Arguments:
  1746. dwId - ID of event to log ( source is "IISCTLS" , SYSTEM log )
  1747. dwStatus - status of operation ( HRESULT )
  1748. Returns:
  1749. Nothing
  1750. --*/
  1751. {
  1752. HANDLE hLog;
  1753. LPTSTR aParams[1];
  1754. if ( (hLog = RegisterEventSource( NULL, _T("IISCTLS") )) != NULL )
  1755. {
  1756. if ( SUCCEEDED( WhoAmI( aParams + 0 ) ) )
  1757. {
  1758. ReportEvent( hLog,
  1759. EVENTLOG_INFORMATION_TYPE,
  1760. 0,
  1761. dwId,
  1762. NULL,
  1763. 1,
  1764. sizeof(DWORD),
  1765. (LPCTSTR*)aParams,
  1766. &dwStatus );
  1767. LocalFree( aParams[0] );
  1768. }
  1769. DeregisterEventSource( hLog );
  1770. }
  1771. }
  1772. HRESULT
  1773. WhoAmI(
  1774. LPTSTR* ppPrincipal
  1775. )
  1776. /*++
  1777. WhoAmI
  1778. Return currently impersonated user
  1779. As this is a COM server running under the identity of the invoker
  1780. this means we access the process token. So we might end up getting
  1781. the wrong user name if the object is invoked in close succession
  1782. ( within the 5s server exit timeout ) by different users.
  1783. Arguments:
  1784. ppPrincipal - update with ptr to string containing user name ( domain\acct )
  1785. must be freed using LocalFree()
  1786. Returns:
  1787. Error status
  1788. --*/
  1789. {
  1790. TCHAR* pPrincipal;
  1791. TCHAR achUserName[512];
  1792. TCHAR achDomain[512];
  1793. DWORD dwLen;
  1794. DWORD dwDomainLen;
  1795. SID_NAME_USE SIDtype = SidTypeUser;
  1796. HRESULT hres = E_FAIL;
  1797. //
  1798. // So we have to access the process token and retrieve account & user name
  1799. // by using LookupAccountSid()
  1800. //
  1801. HANDLE hAccTok = NULL;
  1802. if ( OpenProcessToken( GetCurrentProcess(),
  1803. TOKEN_EXECUTE|TOKEN_QUERY,
  1804. &hAccTok ) )
  1805. {
  1806. BYTE abSidAndInfo[512];
  1807. DWORD dwReq;
  1808. //
  1809. // provide a reasonably sized buffer. If this fails we don't
  1810. // retry with a bigger one.
  1811. //
  1812. if ( GetTokenInformation( hAccTok,
  1813. TokenUser,
  1814. (LPVOID)abSidAndInfo,
  1815. sizeof(abSidAndInfo),
  1816. &dwReq) )
  1817. {
  1818. dwLen = sizeof( achUserName ) / sizeof(TCHAR);
  1819. dwDomainLen = sizeof(achDomain) / sizeof(TCHAR);
  1820. //
  1821. // provide a reasonably sized buffer. If this fails we don't
  1822. // retry with a bigger one.
  1823. //
  1824. if ( LookupAccountSid( NULL,
  1825. ((SID_AND_ATTRIBUTES*)abSidAndInfo)->Sid,
  1826. achUserName,
  1827. &dwLen,
  1828. achDomain,
  1829. &dwDomainLen,
  1830. &SIDtype) )
  1831. {
  1832. //
  1833. // We return a LocalAlloc'ed buffer
  1834. //
  1835. dwLen = (DWORD) _tcslen( achUserName );
  1836. dwDomainLen = (DWORD) _tcslen( achDomain );
  1837. pPrincipal = (LPTSTR)LocalAlloc( LMEM_FIXED,
  1838. (dwLen + 1 + dwDomainLen + 1 ) * sizeof(TCHAR) );
  1839. if ( pPrincipal != NULL )
  1840. {
  1841. memcpy( pPrincipal,
  1842. achDomain,
  1843. sizeof(TCHAR)*dwDomainLen );
  1844. pPrincipal[dwDomainLen] = '\\';
  1845. memcpy( pPrincipal + dwDomainLen + 1,
  1846. achUserName,
  1847. sizeof(TCHAR)*(dwLen+1) );
  1848. *ppPrincipal = pPrincipal;
  1849. hres = S_OK;
  1850. }
  1851. else
  1852. {
  1853. hres = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  1854. }
  1855. }
  1856. else
  1857. {
  1858. hres = HRESULT_FROM_WIN32( GetLastError() );
  1859. }
  1860. }
  1861. else
  1862. {
  1863. hres = HRESULT_FROM_WIN32( GetLastError() );
  1864. }
  1865. CloseHandle( hAccTok );
  1866. }
  1867. else
  1868. {
  1869. hres = HRESULT_FROM_WIN32( GetLastError() );
  1870. }
  1871. return hres;
  1872. }
  1873. BOOL
  1874. W3SVCandHTTPFilter(
  1875. DWORD currentIndex,
  1876. ENUM_SERVICE_STATUS* pessRoot,
  1877. DWORD dwNumServices
  1878. )
  1879. {
  1880. /*++
  1881. W3SVCandHTTPFilter
  1882. Return currently impersonated user
  1883. As this is a COM server running under the identity of the invoker
  1884. this means we access the process token. So we might end up getting
  1885. the wrong user name if the object is invoked in close succession
  1886. ( within the 5s server exit timeout ) by different users.
  1887. Arguments:
  1888. DWORD currentIndex - index of the service we are deciding if we should process.
  1889. ENUM_SERVICE_STATUS* pessRoot - the set of services we are working on.
  1890. DWORD dwNumServices - the number of services in the set.
  1891. Returns:
  1892. TRUE if we found the w3svc and HTTPFilter on the same line and we are looking at the w3svc
  1893. --*/
  1894. BOOL bResult = FALSE;
  1895. // check if we are looking at the w3svc. If we are find out if the
  1896. // HTTPFilter is on the same level. Note the HTTPFilter will always be listed
  1897. // after the w3svc.
  1898. if ( _wcsicmp( pessRoot[currentIndex].lpServiceName, L"w3svc" ) == 0 )
  1899. {
  1900. for ( DWORD i = currentIndex + 1;
  1901. ( i < dwNumServices ) && ( bResult == FALSE );
  1902. i++ )
  1903. {
  1904. if ( _wcsicmp( pessRoot[i].lpServiceName, L"HTTPFilter" ) == 0 )
  1905. {
  1906. bResult = TRUE;
  1907. }
  1908. }
  1909. }
  1910. return bResult;
  1911. }