Source code of Windows XP (NT5)
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.

2135 lines
61 KiB

  1. /*-----------------------------------------------------------------------------
  2. Copyright (c) 1995-1997 Microsoft Corporation
  3. Module Name :
  4. wamexec.cxx
  5. Abstract:
  6. This module executes a wam request
  7. Author:
  8. David Kaplan ( DaveK ) 11-Mar-1997
  9. Lei Jin (leijin) 24-Apr-1997 (WamInfo & WamDictator)
  10. Environment:
  11. User Mode - Win32
  12. Project:
  13. W3 services DLL
  14. -----------------------------------------------------------------------------*/
  15. #include <w3p.hxx>
  16. #include "wamexec.hxx"
  17. #include "wamreq.hxx"
  18. #include <wmrgexp.h>
  19. #include <ole2.h>
  20. #include <imd.h>
  21. #include <mb.hxx>
  22. #include <issched.hxx>
  23. #define dwMDDefaultTimeOut 2000
  24. #define PERIODIC_RESTART_TIME_DEFAULT 0
  25. #define PERIODIC_RESTART_REQUESTS_DEFAULT 0
  26. #define SHUTDOWN_TIME_LIMIT_DEFAULT 600
  27. /*-----------------------------------------------------------------------------
  28. Globals
  29. -----------------------------------------------------------------------------*/
  30. WAM_DICTATOR * g_pWamDictator; // global wam dictator
  31. PFN_INTERLOCKED_COMPARE_EXCHANGE g_pfnInterlockedCompareExchange = NULL;
  32. static VOID UnloadNTApis(VOID);
  33. static VOID LoadNTApis(VOID);
  34. // Default Package name, defined in wamreg.h, shared by wamreg.dll.
  35. WCHAR g_szIISInProcWAMCLSID[] = W3_INPROC_WAM_CLSID;
  36. WCHAR g_szIISOOPPoolWAMCLSID[] = W3_OOP_POOL_WAM_CLSID;
  37. WCHAR g_szIISOOPPoolPackageID[] = W3_OOP_POOL_PACKAGE_ID;
  38. // Sink function at wamreg.dll
  39. // This function will get called when user change application configuration on the fly.
  40. //
  41. HRESULT W3SVC_WamRegSink( LPCSTR szAppPath,
  42. const DWORD dwCommand,
  43. DWORD* pdwResult
  44. );
  45. /*-----------------------------------------------------------------------------
  46. CreateWamRequestInstance
  47. Similar to CoCreateInstance followed by QueryInterface
  48. Arguments:
  49. pHttpRequest - pointer to the HTTP REQUEST object containing all information
  50. about the current request.
  51. pExec - Execution descriptor block
  52. pstrPath - Fully qualified path to Isapi DLL
  53. pWamInfo - pointer to wam-info which will process this request
  54. ppWamRequestOut -pointer to a pointer that contains the return WamRequest.
  55. Return Value:
  56. HRESULT
  57. -----------------------------------------------------------------------------*/
  58. HRESULT
  59. CreateWamRequestInstance
  60. (
  61. HTTP_REQUEST * pHttpRequest,
  62. EXEC_DESCRIPTOR * pExec,
  63. const STR * pstrPath,
  64. CWamInfo * pWamInfo,
  65. WAM_REQUEST ** ppWamRequestOut
  66. )
  67. {
  68. HRESULT hr = NOERROR;
  69. WAM_REQUEST * pWamRequest =
  70. new WAM_REQUEST(
  71. pHttpRequest
  72. , pExec
  73. , pWamInfo
  74. );
  75. *ppWamRequestOut = NULL;
  76. if( pWamRequest == NULL)
  77. {
  78. hr = E_OUTOFMEMORY;
  79. goto LExit;
  80. }
  81. // Init the wam req
  82. hr = pWamRequest->InitWamRequest( pstrPath );
  83. if ( FAILED( hr ) )
  84. {
  85. DBGPRINTF((
  86. DBG_CONTEXT
  87. , "CreateWamRequestInstance - "
  88. "failed to init wam request. "
  89. "pHttpRequest(%08x) "
  90. "pWamInfo(%08x) "
  91. "pstrPath(%s) "
  92. "hr(%d) "
  93. "\n"
  94. , pHttpRequest
  95. , pWamInfo
  96. , pstrPath
  97. , hr
  98. ));
  99. delete pWamRequest;
  100. pWamRequest = NULL;
  101. goto LExit;
  102. }
  103. pWamRequest->AddRef();
  104. *ppWamRequestOut = pWamRequest;
  105. LExit:
  106. return hr;
  107. }
  108. /*-----------------------------------------------------------------------------
  109. WAM_DICTATOR::ProcessWamRequest
  110. This function finds a WamInfo and makes a call to the WamInfo to process a Http Server
  111. Extension Request.
  112. It uses the HTTP_REQUEST passed in as well as the EXEC_DESCRIPTOR.
  113. Arguments:
  114. pHttpRequest - pointer to the HTTP REQUEST object containing all information
  115. about the current request.
  116. pExec - Execution descriptor block
  117. pstrPath - Fully qualified path to Module (DLL or ComIsapi ProgId)
  118. pfHandled - Indicates we handled this request
  119. pfFinished - Indicates no further processing is required
  120. Return Value:
  121. HRESULT
  122. -----------------------------------------------------------------------------*/
  123. HRESULT
  124. WAM_DICTATOR::ProcessWamRequest
  125. (
  126. HTTP_REQUEST * pHttpRequest,
  127. EXEC_DESCRIPTOR * pExec,
  128. const STR * pstrPath,
  129. BOOL * pfHandled,
  130. BOOL * pfFinished
  131. )
  132. {
  133. HRESULT hr = NOERROR;
  134. CWamInfo* pWamInfo = NULL;
  135. STR * pstrAppPath = NULL;
  136. CLSID clsidWam;
  137. BOOL fRet = FALSE;
  138. DBG_ASSERT(pHttpRequest);
  139. DBG_ASSERT(pExec);
  140. if (m_fShutdownInProgress)
  141. {
  142. DBGPRINTF((DBG_CONTEXT, "Wam Dictator: Shut down in progress, stops serving requests.\n"));
  143. // Signal the child event to indicate that the request should terminate.
  144. if ( pExec->IsChild() ) {
  145. pExec->SetChildEvent();
  146. }
  147. return E_FAIL;
  148. }
  149. pstrAppPath = pExec->QueryAppPath();
  150. if (pstrAppPath == NULL)
  151. {
  152. hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
  153. goto LExit;
  154. }
  155. hr = GetWamInfo( pstrAppPath, pHttpRequest, &pWamInfo );
  156. if ( FAILED(hr) )
  157. {
  158. DBGPRINTF(( DBG_CONTEXT,
  159. "WAM_DICTATOR::ProcessWamRequest: GetWamInfo failed, hr = %08x\n",
  160. hr
  161. ));
  162. //
  163. // For Child execution just return the error and don't
  164. // disconnect, otherwise a race ensues between SSI and the disconnect
  165. // cleanup
  166. //
  167. if ( pExec->IsChild() )
  168. {
  169. SetLastError( hr );
  170. goto LExit;
  171. }
  172. //
  173. // Since WAM_DICTATOR is going to send some message to the browser, we need
  174. // to set pfHandled = TRUE here.
  175. //
  176. *pfHandled = TRUE;
  177. if (W3PlatformType == PtWindows95)
  178. {
  179. pHttpRequest->SetLogStatus( HT_SERVER_ERROR, hr);
  180. // Win95 platform, no event log, special message without mention EventLog.
  181. pHttpRequest->Disconnect(HT_SERVER_ERROR, IDS_WAM_FAILTOLOADONW95_ERROR, TRUE, pfFinished);
  182. }
  183. else
  184. {
  185. //
  186. // Log to Event Log first.
  187. //
  188. const CHAR *pszEventLog[2];
  189. CHAR szErrorDescription[2048];
  190. CHAR * pszErrorDescription = NULL;
  191. HANDLE hMetabase = GetModuleHandle( "METADATA.DLL" );
  192. if(FormatMessage(
  193. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  194. FORMAT_MESSAGE_FROM_HMODULE |
  195. FORMAT_MESSAGE_FROM_SYSTEM |
  196. FORMAT_MESSAGE_IGNORE_INSERTS,
  197. hMetabase,
  198. hr,
  199. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  200. (LPTSTR) &pszErrorDescription,
  201. sizeof(szErrorDescription) - 1,
  202. NULL
  203. ) && pszErrorDescription )
  204. {
  205. strcpy(szErrorDescription, pszErrorDescription);
  206. LocalFree(pszErrorDescription);
  207. } else {
  208. wsprintf(szErrorDescription, "%08x", hr);
  209. }
  210. pszEventLog[0] = pstrAppPath->QueryStr();
  211. pszEventLog[1] = szErrorDescription;
  212. // Event log
  213. g_pInetSvc->LogEvent(W3_EVENT_FAIL_LOADWAM,
  214. 2,
  215. pszEventLog,
  216. 0);
  217. pHttpRequest->SetLogStatus( HT_SERVER_ERROR, hr);
  218. pHttpRequest->Disconnect(HT_SERVER_ERROR, IDS_WAM_FAILTOLOAD_ERROR, TRUE, pfFinished);
  219. }
  220. //
  221. // Since we already called Disconnect, WAM_DICTATOR should return TRUE to high level.
  222. //
  223. hr = NOERROR;
  224. goto LExit;
  225. }
  226. hr = pWamInfo->ProcessWamRequest(pHttpRequest, pExec, pstrPath, pfHandled,pfFinished);
  227. LExit:
  228. return hr;
  229. } // WAM_DICTATOR::ProcessWamRequest()
  230. /*-----------------------------------------------------------------------------*
  231. WAM_DICTATOR::WAM_DICTATOR
  232. Constructor
  233. Arguments:
  234. None
  235. Return Value:
  236. None
  237. -----------------------------------------------------------------------------*/
  238. WAM_DICTATOR::WAM_DICTATOR
  239. (
  240. )
  241. :
  242. //m_pPackageCtl(NULL),
  243. m_cRef(0),
  244. m_pMetabase(NULL),
  245. m_fCleanupInProgress(FALSE),
  246. m_fShutdownInProgress(FALSE),
  247. m_hW3Svc ( (HANDLE)NULL ),
  248. m_dwScheduledId (0),
  249. m_strRootAppPath("/LM/W3SVC"),
  250. m_HashTable(LK_DFLT_MAXLOAD, LK_DFLT_INITSIZE, LK_DFLT_NUM_SUBTBLS),
  251. m_pidInetInfo(0)
  252. {
  253. IF_DEBUG( WAM_ISA_CALLS )
  254. DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::WAM_DICTATOR\n"));
  255. InitializeListHead(&m_DyingListHead);
  256. INITIALIZE_CRITICAL_SECTION(&m_csWamCreation);
  257. INITIALIZE_CRITICAL_SECTION(&m_csDyingList);
  258. m_PTable.Init();
  259. //
  260. // Init wamreq allocation cache
  261. //
  262. DBG_REQUIRE( WAM_REQUEST::InitClass() );
  263. }//WAM_DICTATOR::WAM_DICTATOR
  264. /*-----------------------------------------------------------------------------*
  265. WAM_DICTATOR::~WAM_DICTATOR
  266. Destructor
  267. Arguments:
  268. None
  269. // (only interesting if out of process)
  270. Return Value:
  271. None
  272. -----------------------------------------------------------------------------*/
  273. WAM_DICTATOR::~WAM_DICTATOR
  274. (
  275. )
  276. {
  277. IF_DEBUG( WAM_ISA_CALLS )
  278. DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::~WAM_DICTATOR\n"));
  279. DBG_ASSERT(m_cRef == 0);
  280. DeleteCriticalSection(&m_csWamCreation);
  281. DeleteCriticalSection(&m_csDyingList);
  282. m_PTable.UnInit();
  283. //
  284. // Uninit wamreq allocation cache
  285. //
  286. WAM_REQUEST::CleanupClass();
  287. }//WAM_DICTATOR::~WAMDICTATOR
  288. /*-----------------------------------------------------------------------------
  289. WAM_DICTATOR::InitWamDictator
  290. Initializes Wam dictator
  291. Arguments:
  292. None
  293. Return Value:
  294. HRESULT
  295. -----------------------------------------------------------------------------*/
  296. HRESULT
  297. WAM_DICTATOR::InitWamDictator
  298. (
  299. )
  300. {
  301. IF_DEBUG( WAM_ISA_CALLS )
  302. DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::InitWamDictator\n"));
  303. HRESULT hr = NOERROR;
  304. IMDCOM * pMetabase;
  305. DBG_ASSERT(g_pInetSvc->QueryMDObject());
  306. m_pMetabase = new MB( (IMDCOM*) g_pInetSvc->QueryMDObject() );
  307. if (m_pMetabase == NULL)
  308. {
  309. hr = E_OUTOFMEMORY;
  310. goto LExit;
  311. }
  312. // Save the handle of the W3Svc process for use when copying tokens to out of proc Wam's
  313. // NOTE: this is actually a "pseudo-handle", which is good enough. Note that pseudo-handles
  314. // do NOT need to be closed. CloseHandle is a no-op on a pseudo-handle.
  315. m_hW3Svc = GetCurrentProcess();
  316. //
  317. // Get the process id for inetinfo. Under some circumstances com
  318. // svcs my get hosed. This causes object activation to happen in
  319. // process even if the object is registered to be launched in the
  320. // surrogate. In order to prevent inetinfo from AV'ing we want to
  321. // prevent these applications from running.
  322. //
  323. m_pidInetInfo = GetCurrentProcessId();
  324. DBG_REQUIRE( m_ServerVariableMap.Initialize() );
  325. // Register the Sink function at WamReg.
  326. WamReg_RegisterSinkNotify(W3SVC_WamRegSink);
  327. LoadNTApis();
  328. // Make the reference count to 1.
  329. Reference();
  330. LExit:
  331. return hr;
  332. }//WAM_DICTATOR::InitWamDictator
  333. /*-----------------------------------------------------------------------------
  334. WAM_DICTATOR::
  335. -----------------------------------------------------------------------------*/
  336. LK_PREDICATE
  337. WAM_DICTATOR::DeleteInShutdown
  338. (
  339. CWamInfo* pWamInfo,
  340. void* pvState
  341. )
  342. {
  343. IF_DEBUG( WAM_ISA_CALLS )
  344. DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::DeleteInShutdown\n"));
  345. DWORD dwPriorState;
  346. dwPriorState = pWamInfo->ChangeToState(WIS_SHUTDOWN);
  347. DBGPRINTF((DBG_CONTEXT, "Start shutdown, change state to WIS_SHUTDOWN\n"));
  348. DBG_REQUIRE(dwPriorState == WIS_RUNNING || dwPriorState == WIS_PAUSE || dwPriorState == WIS_CPUPAUSE);
  349. // remove from the hash table here. and clean up later.
  350. g_pWamDictator->InsertDyingList(pWamInfo, TRUE);
  351. return LKP_PERFORM;
  352. }//WAM_DICTATOR::DeleteInShutdown
  353. /*-----------------------------------------------------------------------------
  354. WAM_DICTATOR::StartShutdown
  355. Starts to shutdown Wam dictator
  356. Arguments:
  357. None
  358. Return Value:
  359. HRESULT
  360. -----------------------------------------------------------------------------*/
  361. HRESULT
  362. WAM_DICTATOR::StartShutdown
  363. (
  364. )
  365. {
  366. IF_DEBUG( WAM_ISA_CALLS )
  367. DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::StartShutdown\n"));
  368. HRESULT hr = NOERROR;
  369. CWamInfo* pWamInfo;
  370. DWORD dwErr;
  371. DWORD dwPriorState;
  372. InterlockedExchange((LPLONG)&m_fShutdownInProgress, (LONG)TRUE);
  373. WamReg_UnRegisterSinkNotify();
  374. m_HashTable.DeleteIf(DeleteInShutdown, NULL);
  375. return hr;
  376. }
  377. /*-----------------------------------------------------------------------------*
  378. WAM_DICTATOR::UninitWamDictator
  379. Un-initializes Wam dictator
  380. Arguments:
  381. None
  382. Return Value:
  383. HRESULT
  384. -----------------------------------------------------------------------------*/
  385. HRESULT
  386. WAM_DICTATOR::UninitWamDictator
  387. (
  388. )
  389. {
  390. IF_DEBUG( WAM_ISA_CALLS )
  391. DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::UninitWamDictator\n"));
  392. HRESULT hr = NOERROR;
  393. // UNDONE leijin thinks we can get rid of this RemoveWorkItem code???
  394. ScheduledId_Lock();
  395. if (m_dwScheduledId != 0)
  396. {
  397. DBGPRINTF((DBG_CONTEXT, "remove work item %d\n", m_dwScheduledId));
  398. // Cancel the ScheduleWorkItem
  399. RemoveWorkItem(m_dwScheduledId);
  400. m_dwScheduledId = 0;
  401. }
  402. ScheduledId_UnLock();
  403. DBG_ASSERT(m_dwScheduledId == 0);
  404. // Clean up the dying list.
  405. CleanUpDyingList();
  406. // After the CleanUpDyingList() call, this should be an empty list.
  407. //
  408. DBG_ASSERT(IsListEmpty(&m_DyingListHead));
  409. //
  410. // Allow all other references to WAM_DICTATOR to drain away.
  411. // This loop is especially important in shutdown during stress scenario.
  412. // Unload Application while w3svc is still running, a busy application might
  413. // still have some outstanding HTTPREQUEST unfinished which hold the reference to
  414. // this WAM_DICTATOR.
  415. //
  416. while (m_cRef != 1)
  417. {
  418. DBGPRINTF((DBG_CONTEXT, "Still have out-standing reference(%d) to this WAM_DICTATOR\n",
  419. m_cRef));
  420. Sleep(20);
  421. }
  422. // Delete m_pMetabase
  423. // pMetabase should be there
  424. DBG_REQUIRE(m_pMetabase);
  425. delete m_pMetabase;
  426. m_pMetabase = NULL;
  427. UnloadNTApis();
  428. //
  429. // Dereference to balance the reference in InitWamDictator. after
  430. // this Dereference, ref count of WAM_DICTATOR should be 0.
  431. //
  432. Dereference();
  433. DBGPRINTF((DBG_CONTEXT, "Wam Dictator Exits.\n"));
  434. return hr;
  435. }//WAM_DICTATOR::UninitWamDictator
  436. /*-----------------------------------------------------------------------------
  437. WAM_DICTATOR::MDGetAppVariables
  438. Giving a Metabase Path, find out the Application Path, WAM CLSID, and Context of WAM object of
  439. the application that apply to the metabase path.
  440. Argument:
  441. szMetabasePath: [in] A metabase path.
  442. pfAllowApptoRun: [out] True, if application is enabled to run.
  443. pclsidWam: [out] a pointer to the buffer for WAM CLSID
  444. pfInProcess: [out] a pointer to DWORD for Context
  445. pfEnableTryExcept [out] a pointer to DWORD for EnableTryExcept flag
  446. Return:
  447. HRESULT
  448. -----------------------------------------------------------------------------*/
  449. HRESULT
  450. WAM_DICTATOR::MDGetAppVariables
  451. (
  452. LPCSTR szMetabasePath,
  453. BOOL* pfAllowAppToRun,
  454. CLSID* pclsidWam,
  455. BOOL* pfInProcess,
  456. BOOL* pfInPool,
  457. BOOL* pfEnableTryExcept,
  458. DWORD *pdwOOPCrashThreshold,
  459. BOOL* pfJobEnabled,
  460. WCHAR* wszPackageID,
  461. DWORD* pdwPeriodicRestartRequests,
  462. DWORD* pdwPeriodicRestartTime,
  463. MULTISZ*pmszPeriodicRestartSchedule,
  464. DWORD* pdwShutdownTimeLimit
  465. )
  466. {
  467. HRESULT hr = NOERROR;
  468. METADATA_RECORD recMetaData;
  469. DWORD dwRequiredLen;
  470. CHAR szclsidWam[uSizeCLSIDStr];
  471. WCHAR wszclsidWam[uSizeCLSIDStr];
  472. BOOL fKeyOpen = FALSE;
  473. DWORD dwAppMode = 0;
  474. BOOL fEnableTryExcept = TRUE;
  475. DWORD dwAppState = 0;
  476. DBG_ASSERT(szMetabasePath);
  477. DBG_ASSERT(pfAllowAppToRun);
  478. DBG_ASSERT(pclsidWam);
  479. DBG_ASSERT(pfInProcess);
  480. DBG_ASSERT(pfInPool);
  481. DBG_ASSERT(pfEnableTryExcept);
  482. DBG_ASSERT(pdwOOPCrashThreshold);
  483. DBG_ASSERT(pfJobEnabled);
  484. DBG_ASSERT(m_pMetabase);
  485. DBG_ASSERT(wszPackageID);
  486. DBG_ASSERT(pdwPeriodicRestartRequests);
  487. DBG_ASSERT(pdwPeriodicRestartTime);
  488. DBG_ASSERT(pmszPeriodicRestartSchedule);
  489. DBG_ASSERT(pdwShutdownTimeLimit);
  490. wszPackageID[0] = L'\0';
  491. // Open Key
  492. if (!m_pMetabase->Open(szMetabasePath, METADATA_PERMISSION_READ))
  493. {
  494. hr = HRESULT_FROM_WIN32(GetLastError());
  495. goto LErrExit;
  496. }
  497. fKeyOpen = TRUE;
  498. // if MD_APP_STATE exists, and is defined with a value APPSTATUS_PAUSE,
  499. // then, fAllowAppToRun is set to FALSE
  500. // otherwise, fAllowAppToRun is set to TRUE
  501. if (m_pMetabase->GetDword("", MD_APP_STATE, IIS_MD_UT_WAM, (DWORD *)&dwAppState, METADATA_INHERIT))
  502. {
  503. if (dwAppState == APPSTATUS_PAUSE)
  504. {
  505. *pfAllowAppToRun = FALSE;
  506. goto LErrExit;
  507. }
  508. }
  509. *pfAllowAppToRun = TRUE;
  510. if (!m_pMetabase->GetDword("", MD_APP_ISOLATED, IIS_MD_UT_WAM, &dwAppMode, METADATA_INHERIT))
  511. {
  512. hr = HRESULT_FROM_WIN32(GetLastError());
  513. goto LErrExit;
  514. }
  515. *pfInProcess = ( dwAppMode == eAppInProc) ? TRUE : FALSE;
  516. *pfInPool = ( dwAppMode == eAppInProc || dwAppMode == eAppOOPPool ) ? TRUE : FALSE;
  517. if (dwAppMode == eAppOOPIsolated)
  518. {
  519. DWORD dwRet = 0;
  520. CHAR szPackageID[uSizeCLSIDStr];
  521. //
  522. // Disable job objects for IIS5.1, original code would read the MD_CPU_APP_ENABLED
  523. // property from metabase
  524. //
  525. *pfJobEnabled = FALSE;
  526. dwRequiredLen= uSizeCLSIDStr;
  527. if (!m_pMetabase->GetString("", MD_APP_PACKAGE_ID, IIS_MD_UT_WAM, szPackageID, &dwRequiredLen, METADATA_INHERIT))
  528. {
  529. DBG_ASSERT(dwRequiredLen <= uSizeCLSIDStr);
  530. hr = HRESULT_FROM_WIN32(GetLastError());
  531. goto LErrExit;
  532. }
  533. dwRet = MultiByteToWideChar(CP_ACP, 0, szPackageID, -1, wszPackageID, uSizeCLSIDStr);
  534. DBG_ASSERT(dwRet != 0);
  535. }
  536. else if (dwAppMode == eAppOOPPool)
  537. {
  538. wcsncpy(wszPackageID, g_szIISOOPPoolPackageID, sizeof(g_szIISOOPPoolPackageID)/sizeof(WCHAR));
  539. // Always disable Job object for pool process.
  540. *pfJobEnabled = FALSE;
  541. }
  542. else
  543. {
  544. *pfJobEnabled = FALSE;
  545. }
  546. // If not present assume the default (TRUE) (For Debug builds set default to FALSE)
  547. if (m_pMetabase->GetDword("", MD_ASP_EXCEPTIONCATCHENABLE, ASP_MD_UT_APP, (DWORD *)&fEnableTryExcept, METADATA_INHERIT))
  548. {
  549. *pfEnableTryExcept = fEnableTryExcept ? TRUE : FALSE;
  550. }
  551. else
  552. {
  553. #if DBG
  554. *pfEnableTryExcept = FALSE;
  555. #else
  556. *pfEnableTryExcept = TRUE;
  557. #endif
  558. }
  559. DWORD dwThreshold;
  560. if (dwAppMode == eAppInProc)
  561. {
  562. hr = CLSIDFromString((LPOLESTR) g_szIISInProcWAMCLSID, (LPCLSID)pclsidWam);
  563. }
  564. else if (dwAppMode == eAppOOPPool)
  565. {
  566. hr = CLSIDFromString((LPOLESTR) g_szIISOOPPoolWAMCLSID, (LPCLSID)pclsidWam);
  567. *pdwOOPCrashThreshold = 0XFFFFFFFF;
  568. }
  569. else
  570. {
  571. if (m_pMetabase->GetDword("", MD_APP_OOP_RECOVER_LIMIT, IIS_MD_UT_WAM, (DWORD *)&dwThreshold, METADATA_INHERIT))
  572. {
  573. // It used to be OOP_CRASH_LIMIT (range 1 - 5)
  574. // Now, the name is changed to RECOVER_LIMIT with range of (0-xxx).
  575. // 0 means no recovery, same as 1 in crash limit, 1 crash, and it's over, (0 recovery).
  576. // Add 1 because internally this threshold is implemented in crash_limit concept.
  577. // 0xFFFFFFFF is unlimited.
  578. // Changed to default to unlimited - Bug 240012. The idea of making this time
  579. // dependent seems much smarter than making it infinite. But no one was willing
  580. // to step up and decide what the right parameters were.
  581. if (dwThreshold != 0xFFFFFFFF)
  582. {
  583. dwThreshold++;
  584. }
  585. }
  586. else
  587. {
  588. dwThreshold = APP_OOP_RECOVER_LIMIT_DEFAULT;
  589. }
  590. *pdwOOPCrashThreshold = dwThreshold;
  591. dwRequiredLen= uSizeCLSIDStr;
  592. if (!m_pMetabase->GetString("", MD_APP_WAM_CLSID, IIS_MD_UT_WAM, szclsidWam, &dwRequiredLen, METADATA_INHERIT))
  593. {
  594. DBG_ASSERT(dwRequiredLen <= uSizeCLSIDStr);
  595. hr = HRESULT_FROM_WIN32(GetLastError());
  596. goto LErrExit;
  597. }
  598. MultiByteToWideChar(CP_ACP, 0, szclsidWam, -1, wszclsidWam, uSizeCLSIDStr);
  599. hr = CLSIDFromString((LPOLESTR)wszclsidWam, (LPCLSID)pclsidWam);
  600. }
  601. if ( dwAppMode != eAppInProc )
  602. {
  603. //
  604. // Get the application recycle settings
  605. //
  606. //
  607. // Disable wam recycling for IIS5.1, original code would read these properties
  608. // from metabase
  609. //
  610. *pdwPeriodicRestartTime = PERIODIC_RESTART_TIME_DEFAULT;
  611. *pdwPeriodicRestartRequests = PERIODIC_RESTART_REQUESTS_DEFAULT;
  612. *pdwShutdownTimeLimit = SHUTDOWN_TIME_LIMIT_DEFAULT;
  613. pmszPeriodicRestartSchedule->Reset();
  614. }
  615. if (FAILED(hr))
  616. goto LErrExit;
  617. LErrExit:
  618. if (fKeyOpen)
  619. {
  620. m_pMetabase->Close();
  621. }
  622. return hr;
  623. }//WAM_DICTATOR::MDGetAppVariables
  624. /*-----------------------------------------------------------------------------
  625. WAM_DICTATOR::GetWamInfo
  626. Returns the interface pointer for a wam, NULL if wam not found
  627. Arguments:
  628. pstrPath [in] wam key: path to extension dll, prog id, etc.
  629. fInProc [in] run the wam in-proc?
  630. ppIWam [out] interface pointer for the wam
  631. Return Value:
  632. TRUE or FALSE
  633. -----------------------------------------------------------------------------*/
  634. HRESULT
  635. WAM_DICTATOR::GetWamInfo
  636. (
  637. const STR* pstrAppPath,
  638. const HTTP_REQUEST * pHttpRequest,
  639. CWamInfo ** ppWamInfo
  640. )
  641. {
  642. HRESULT hr = NOERROR;
  643. INT AppState;
  644. CWamInfo* pWamInfo = NULL;
  645. DBG_ASSERT(pstrAppPath);
  646. AppState = FindWamInfo(pstrAppPath, &pWamInfo);
  647. if (-1 == AppState)
  648. {
  649. //
  650. // Lock the Creation process
  651. // to avoid multiple threads try to create the same WAMInfo,
  652. // (init WAM object, very expensive in out-proc cases)
  653. //
  654. CreateWam_Lock();
  655. //
  656. // Try to find it again, in case another thread already created it.
  657. // This check is better that creating another WamInfo.
  658. //
  659. AppState = FindWamInfo(pstrAppPath, &pWamInfo);
  660. if (-1 == AppState)
  661. {
  662. //
  663. // We are out of luck in our search for a valid Wam Info
  664. // Let us just create a new one and make it happen.
  665. // Note: Creation also adds the WamInfo to the hashtable
  666. //
  667. DBG_ASSERT(pHttpRequest != NULL);
  668. hr = CreateWamInfo( *pstrAppPath, &pWamInfo, pHttpRequest->QueryW3Instance());
  669. }
  670. CreateWam_UnLock();
  671. }
  672. if (m_fShutdownInProgress && pWamInfo != NULL)
  673. {
  674. hr = HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS);
  675. pWamInfo->Dereference();
  676. pWamInfo = NULL;
  677. }
  678. *ppWamInfo = pWamInfo;
  679. return hr;
  680. }//WAM_DICTATOR::GetWamInfo
  681. /*-----------------------------------------------------------------------------
  682. WAM_DICTATOR::CreateWamInfo()
  683. Description:
  684. Creates a new CWamInfo object for the specified application root path.
  685. On successful creation, it adds the object to the hash table internal
  686. to the WAM_DICTATOR that contains the active WamInfos.
  687. Finally, it returns the object via the pointer supplied.
  688. Arguments:
  689. strAppRootPath - string containing the metabase path for for the
  690. Application Root
  691. ppWamInfo - pointer to pointer to CWamInfo object. On successful
  692. return this contains the pointer to the new
  693. created CWamInfo
  694. Returns:
  695. HRESULT
  696. NOERROR - on success
  697. and specific error codes on failure
  698. Note:
  699. This function should be called with the WAM_DICTATOR WamInfo lock held.
  700. -----------------------------------------------------------------------------*/
  701. HRESULT
  702. WAM_DICTATOR::CreateWamInfo
  703. (
  704. const STR & strAppRootPath,
  705. CWamInfo ** ppWamInfo,
  706. PW3_SERVER_INSTANCE pwsiInstance
  707. )
  708. {
  709. HRESULT hr = NOERROR;
  710. CLSID clsidWam;
  711. BOOL fInProcess;
  712. BOOL fEnableTryExcept;
  713. CWamInfo * pWamInfo;
  714. DWORD dwThreshold; // OOP crash threshold
  715. BOOL fAllowAppToRun;
  716. BOOL fJobEnabled;
  717. BOOL fInPool;
  718. WCHAR wszPackageID[uSizeCLSIDStr];
  719. DWORD dwPeriodicRestartRequests;
  720. DWORD dwPeriodicRestartTime;
  721. DWORD dwShutdownTimeLimit;
  722. MULTISZ mzPeriodicRestartSchedule;
  723. DBG_ASSERT( NULL != ppWamInfo );
  724. DBG_ASSERT( NULL == *ppWamInfo);
  725. // Get the latest Application path, CLSID, and inproc/out-of-proc flag
  726. hr = MDGetAppVariables(strAppRootPath.QueryStr(),
  727. &fAllowAppToRun,
  728. &clsidWam,
  729. &fInProcess,
  730. &fInPool,
  731. &fEnableTryExcept,
  732. &dwThreshold,
  733. &fJobEnabled,
  734. wszPackageID,
  735. &dwPeriodicRestartRequests,
  736. &dwPeriodicRestartTime,
  737. &mzPeriodicRestartSchedule,
  738. &dwShutdownTimeLimit
  739. );
  740. if ( SUCCEEDED( hr) && fAllowAppToRun)
  741. {
  742. //
  743. // Create a New CWamInfo
  744. //
  745. if (fInProcess)
  746. {
  747. pWamInfo = new CWamInfo( strAppRootPath,
  748. fInProcess,
  749. fInPool,
  750. fEnableTryExcept,
  751. clsidWam
  752. );
  753. }
  754. else
  755. {
  756. //
  757. // Check to see if we have scheduled restart times. If so,
  758. // set the restart time to the earlier of the configured
  759. // restart time, or the earliest scheduled time.
  760. //
  761. if ( mzPeriodicRestartSchedule.QueryStringCount() )
  762. {
  763. SYSTEMTIME st;
  764. DWORD dwTimeOfDayInMinutes;
  765. DWORD dwScheduleInMinutes;
  766. LPSTR szHour;
  767. LPSTR szMinute;
  768. GetLocalTime( &st );
  769. dwTimeOfDayInMinutes = st.wHour * 60 + st.wMinute;
  770. IF_DEBUG( WAM_ISA_CALLS )
  771. DBGPRINTF((
  772. DBG_CONTEXT,
  773. "Current Time is %d.\r\n",
  774. dwTimeOfDayInMinutes
  775. ));
  776. szHour = mzPeriodicRestartSchedule.QueryStrA();
  777. DBG_ASSERT( szHour );
  778. while ( *szHour )
  779. {
  780. IF_DEBUG( WAM_ISA_CALLS )
  781. DBGPRINTF((
  782. DBG_CONTEXT,
  783. "Considering recycle at %s.\r\n",
  784. szHour
  785. ));
  786. szMinute = strchr( szHour, ':' );
  787. if ( !szMinute )
  788. {
  789. //
  790. // Parsing error - hour and minute not separated
  791. // by a ':'
  792. //
  793. break;
  794. }
  795. szMinute++;
  796. dwScheduleInMinutes = atol( szHour ) * 60;
  797. dwScheduleInMinutes += atol( szMinute );
  798. if ( dwScheduleInMinutes <= dwTimeOfDayInMinutes )
  799. {
  800. dwScheduleInMinutes += 24 * 60;
  801. }
  802. dwScheduleInMinutes -= dwTimeOfDayInMinutes;
  803. IF_DEBUG( WAM_ISA_CALLS )
  804. DBGPRINTF((
  805. DBG_CONTEXT,
  806. "Scheduled time is %d minutes from now.\r\n",
  807. dwScheduleInMinutes
  808. ));
  809. //
  810. // If the scheduled time is earlier than the current
  811. // dwPeriodicRestartTime, then replace the existing
  812. // value.
  813. //
  814. if ( dwScheduleInMinutes < dwPeriodicRestartTime ||
  815. dwPeriodicRestartTime == 0 )
  816. {
  817. dwPeriodicRestartTime = dwScheduleInMinutes;
  818. }
  819. szHour += strlen( szHour ) + 1;
  820. }
  821. }
  822. if ( dwPeriodicRestartRequests || dwPeriodicRestartTime )
  823. {
  824. DBGPRINTF((
  825. DBG_CONTEXT,
  826. "Recycling will occur in %d minutes. or when %d requests have been served.\r\n",
  827. dwPeriodicRestartTime,
  828. dwPeriodicRestartRequests
  829. ));
  830. }
  831. pWamInfo = new CWamInfoOutProc(strAppRootPath,
  832. fInProcess,
  833. fInPool,
  834. fEnableTryExcept,
  835. clsidWam,
  836. dwThreshold,
  837. pwsiInstance,
  838. fJobEnabled,
  839. dwPeriodicRestartRequests,
  840. dwPeriodicRestartTime,
  841. dwShutdownTimeLimit
  842. );
  843. }
  844. if (pWamInfo)
  845. {
  846. //
  847. // This is WAM_DICTATOR's reference on the CWamInfo. In "normal"
  848. // operations, this will be the last reference.
  849. //
  850. pWamInfo->Reference();
  851. BOOL fInitialize = (fInProcess || !fJobEnabled || !pwsiInstance->AreProcsCPUStopped());
  852. if (fInitialize)
  853. {
  854. //
  855. //Init the CWamInfo. this call will CoCreateInstance WAM object.
  856. //
  857. hr = pWamInfo->Init( wszPackageID, m_pidInetInfo );
  858. if (SUCCEEDED(hr))
  859. {
  860. pWamInfo->ChangeToState(WIS_RUNNING);
  861. }
  862. else
  863. {
  864. pWamInfo->UnInit();
  865. }
  866. }
  867. else
  868. {
  869. pWamInfo->ChangeToState(WIS_CPUPAUSE);
  870. }
  871. if (SUCCEEDED(hr))
  872. {
  873. if ( LK_SUCCESS != m_HashTable.InsertRecord(pWamInfo))
  874. {
  875. if (fInitialize)
  876. {
  877. pWamInfo->UnInit();
  878. }
  879. pWamInfo->Dereference(); // should be last reference
  880. //delete pWamInfo;
  881. hr = E_FAIL; // NYI: What is the right failure code?
  882. }
  883. else
  884. {
  885. //
  886. // Finally we are successful with a valid WAmInfo.
  887. // Return this. Will be balanced by a DeleteRecord
  888. // in CProcessTable::RemoveWamInfoFromProcessTable
  889. //
  890. pWamInfo->Reference();
  891. *ppWamInfo = pWamInfo;
  892. }
  893. }
  894. else
  895. {
  896. DBG_ASSERT( NOERROR != hr);
  897. pWamInfo->Dereference();
  898. //delete pWamInfo;
  899. }
  900. }
  901. else
  902. {
  903. // pWamInfo == NULL
  904. hr = E_OUTOFMEMORY;
  905. }
  906. }
  907. else
  908. {
  909. if (SUCCEEDED(hr))
  910. {
  911. hr = HRESULT_FROM_WIN32(ERROR_SERVER_DISABLED);
  912. *ppWamInfo = NULL;
  913. }
  914. //
  915. // NYI: Isn't pstrNewPath going away?
  916. // Should I bother deleting pstrNewPath ??
  917. //
  918. }
  919. //
  920. // Either we should be returning an error or send back the proper pointer
  921. //
  922. DBG_ASSERT( (hr != NOERROR) || (*ppWamInfo != NULL));
  923. return ( hr);
  924. } // WAM_DICTATOR::CreateWamInfo()
  925. /*-----------------------------------------------------------------------------
  926. WAM_DICTATOR::FindWamInfo
  927. Returns the interface pointer for a wam, NULL if wam not found
  928. Arguments:
  929. pstrPath - [in] wam key: path to extension dll, prog id, etc.
  930. ppIWam - [out] interface pointer for the wam
  931. Return Value:
  932. TRUE or FALSE
  933. -----------------------------------------------------------------------------*/
  934. INT
  935. WAM_DICTATOR::FindWamInfo
  936. (
  937. const STR* pstrMetabasePath,
  938. CWamInfo** ppWamInfo
  939. )
  940. {
  941. INT AppState = -1;
  942. CWamInfo* pWamInfo = NULL;
  943. char* pszKey;
  944. DBG_ASSERT(pstrMetabasePath);
  945. DBG_ASSERT(ppWamInfo);
  946. pszKey = pstrMetabasePath->QueryStr();
  947. HashReadLock();
  948. m_HashTable.FindKey(pszKey, &pWamInfo);
  949. HashReadUnlock();
  950. if (pWamInfo)
  951. {
  952. AppState = pWamInfo->QueryState();
  953. DBG_ASSERT(AppState != WIS_START || AppState != WIS_END);
  954. }
  955. *ppWamInfo = pWamInfo;
  956. return AppState;
  957. }//WAM_DICTATOR::FindWamInfo
  958. /*-----------------------------------------------------------------------------
  959. WAM_DICTATOR::UnLoadWamInfo
  960. Unload an OOP Wam.
  961. Arguments:
  962. pstrWamPath - [in] wam key: path to extension dll, prog id, etc.
  963. fCPUPause - [in] If true, CPU pauses the WAM. Kills the process
  964. but keeps the info around.
  965. pfAppCpuUnloaded - [out] If nonNULL, set to TRUE iff an app was killed.
  966. Return Value:
  967. HRESULT - Error code.
  968. -----------------------------------------------------------------------------*/
  969. HRESULT
  970. WAM_DICTATOR::UnLoadWamInfo
  971. (
  972. STR *pstrAppPath,
  973. BOOL fCPUPause,
  974. BOOL *pfAppCpuUnloaded
  975. )
  976. {
  977. IF_DEBUG( WAM_ISA_CALLS )
  978. DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::UnLoadWamInfo\n"));
  979. int iAppState;
  980. CWamInfo* pWamInfo = NULL;
  981. DWORD eWISPrevState;
  982. DWORD eWISState;
  983. HRESULT hr = NOERROR;
  984. BOOL fInProc;
  985. BOOL fAppCpuUnloaded = FALSE;
  986. CreateWam_Lock();
  987. iAppState = FindWamInfo(pstrAppPath,
  988. &pWamInfo);
  989. CreateWam_UnLock();
  990. if (iAppState != -1)
  991. {
  992. bool fRet = FALSE;
  993. CProcessEntry* pProcEntry = NULL;
  994. fInProc = pWamInfo->FInProcess();
  995. pProcEntry = pWamInfo->QueryProcessEntry();
  996. DBG_ASSERT(pProcEntry != NULL);
  997. pProcEntry->AddRef();
  998. while (pWamInfo != NULL)
  999. {
  1000. eWISState = (fCPUPause == TRUE) ? WIS_CPUPAUSE : WIS_SHUTDOWN;
  1001. eWISPrevState = pWamInfo->ChangeToState(WIS_RUNNING, eWISState);
  1002. //
  1003. // Terminate process and kill off current requests.
  1004. //
  1005. if (WIS_RUNNING == eWISPrevState)
  1006. {
  1007. DBG_ASSERT(pWamInfo->HWamProcess() != NULL);
  1008. hr = ShutdownWamInfo(pWamInfo,
  1009. HASH_TABLE_REF + FIND_KEY_REF
  1010. );
  1011. if (!fCPUPause)
  1012. {
  1013. LK_RETCODE LkReturn;
  1014. LkReturn = m_HashTable.DeleteKey(
  1015. pWamInfo->QueryApplicationPath().QueryStr());
  1016. DBG_ASSERT(LK_SUCCESS == LkReturn);
  1017. //
  1018. // Release FIND_KEY_REF
  1019. //
  1020. pWamInfo->Dereference();
  1021. if (LK_SUCCESS == LkReturn)
  1022. {
  1023. pWamInfo->Dereference();
  1024. //delete pWamInfo;
  1025. pWamInfo = NULL;
  1026. }
  1027. }
  1028. else
  1029. {
  1030. //
  1031. // Application is running. Kill It
  1032. //
  1033. DBG_ASSERT(!pWamInfo->FInProcess());
  1034. //
  1035. // Get rid of the shutting down flag
  1036. //
  1037. pWamInfo->ClearMembers();
  1038. //
  1039. // Release FIND_KEY_REF
  1040. //
  1041. pWamInfo->Dereference();
  1042. fAppCpuUnloaded = TRUE;
  1043. }
  1044. }
  1045. if (fInProc)
  1046. {
  1047. pWamInfo = NULL;
  1048. }
  1049. else
  1050. {
  1051. fRet = m_PTable.FindWamInfo(pProcEntry,&pWamInfo);
  1052. DBG_ASSERT(fRet == TRUE);
  1053. }
  1054. }
  1055. pProcEntry->Release();
  1056. }
  1057. if (pfAppCpuUnloaded != NULL) {
  1058. *pfAppCpuUnloaded = fAppCpuUnloaded;
  1059. }
  1060. return hr;
  1061. }
  1062. /*-----------------------------------------------------------------------------
  1063. WAM_DICTATOR::CPUResumeWamInfo
  1064. Resumes a CPU Paused OOP Wam.
  1065. Arguments:
  1066. pstrWamPath - [in] wam key: path to extension dll, prog id, etc.
  1067. Return Value:
  1068. -----------------------------------------------------------------------------*/
  1069. VOID
  1070. WAM_DICTATOR::CPUResumeWamInfo
  1071. (
  1072. STR *pstrWamPath
  1073. )
  1074. {
  1075. int iAppState;
  1076. CWamInfo* pWICurrentApplication = NULL;
  1077. DWORD eWISPrevState;
  1078. CLSID clsidWam;
  1079. BOOL fInProcess;
  1080. BOOL fInPool;
  1081. BOOL fEnableTryExcept;
  1082. DWORD dwThreshold; // OOP crash threshold
  1083. BOOL fAllowAppToRun;
  1084. BOOL fJobEnabled;
  1085. WCHAR wszPackageID[uSizeCLSIDStr];
  1086. DWORD dwPeriodicRestartRequests;
  1087. DWORD dwPeriodicRestartTime;
  1088. DWORD dwShutdownTimeLimit;
  1089. MULTISZ mzPeriodicRestartSchedule;
  1090. HRESULT hr;
  1091. CreateWam_Lock();
  1092. iAppState = FindWamInfo(pstrWamPath,
  1093. &pWICurrentApplication);
  1094. CreateWam_UnLock();
  1095. if (iAppState != -1)
  1096. {
  1097. // Application may not be loaded. This method is called for every
  1098. // application defined under the site being paused.
  1099. // Get the latest Application path, CLSID, and inproc/out-of-proc flag
  1100. hr = MDGetAppVariables(pWICurrentApplication->QueryApplicationPath().QueryStr(),
  1101. &fAllowAppToRun,
  1102. &clsidWam,
  1103. &fInProcess,
  1104. &fInPool,
  1105. &fEnableTryExcept,
  1106. &dwThreshold,
  1107. &fJobEnabled,
  1108. wszPackageID,
  1109. &dwPeriodicRestartRequests,
  1110. &dwPeriodicRestartTime,
  1111. &mzPeriodicRestartSchedule,
  1112. &dwShutdownTimeLimit);
  1113. if (iAppState == WIS_CPUPAUSE && SUCCEEDED(hr))
  1114. {
  1115. if (SUCCEEDED(pWICurrentApplication->Init(wszPackageID, m_pidInetInfo)))
  1116. {
  1117. pWICurrentApplication->ChangeToState(WIS_CPUPAUSE, WIS_RUNNING);
  1118. }
  1119. }
  1120. //
  1121. // FindWamInfo References this, so dereference it.
  1122. //
  1123. pWICurrentApplication->Dereference();
  1124. }
  1125. }//WAM_DICTATOR::CPUResumeWamInfo
  1126. /*-----------------------------------------------------------------------------
  1127. WAM_DICTATOR::ShutdownWamInfo
  1128. This function currently can only call a blocking method of WAM, UnInitWam.
  1129. In the future, this function might support a non-blocking method provided by WAM in
  1130. case of IIS shutdown.
  1131. Arguments:
  1132. pWamInfo [in] a pointer to WamInfo
  1133. cIgnoreRef [in] The reference count that need to be ignored in StartShutdown.
  1134. Return Value:
  1135. TRUE or FALSE
  1136. -----------------------------------------------------------------------------*/
  1137. HRESULT
  1138. WAM_DICTATOR::ShutdownWamInfo
  1139. (
  1140. CWamInfo* pWamInfo,
  1141. INT cIgnoreRefs
  1142. )
  1143. {
  1144. IF_DEBUG( WAM_ISA_CALLS )
  1145. DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::ShutdownWamInfo\n"));
  1146. HRESULT hr = NOERROR;
  1147. WCHAR szPackageID[uSizeCLSIDStr];
  1148. DBG_ASSERT(pWamInfo);
  1149. LPCSTR pszAppPath = (pWamInfo->QueryApplicationPath()).QueryStr();
  1150. // Might create IPackageUtil * for later use.
  1151. DBGPRINTF((DBG_CONTEXT, "Shutting down WamInfo %08p, Inproc(%d), AppRoot(%s).\n",
  1152. pWamInfo,
  1153. pWamInfo->FInProcess(),
  1154. pszAppPath));
  1155. hr = pWamInfo->StartShutdown(cIgnoreRefs);
  1156. hr = pWamInfo->UnInit();
  1157. if (FAILED(hr))
  1158. {
  1159. const CHAR *pszEventLog[2];
  1160. char szErr[20];
  1161. pszEventLog[0] = pszAppPath;
  1162. wsprintf(szErr, "0x%08X", hr);
  1163. pszEventLog[1] = szErr;
  1164. // Event login
  1165. g_pInetSvc->LogEvent(W3_EVENT_FAIL_SHUTDOWN,
  1166. 2,
  1167. pszEventLog,
  1168. 0);
  1169. }
  1170. //delete pWamInfo;
  1171. //pWamInfo = NULL;
  1172. return hr;
  1173. } // wAM_DICTATOR::ShutdownWamInfo
  1174. /*-----------------------------------------------------------------------------
  1175. WAM_DICTATOR::CPUUpdateWamInfo
  1176. Arguments:
  1177. Return Value:
  1178. TRUE or FALSE
  1179. -----------------------------------------------------------------------------*/
  1180. void
  1181. WAM_DICTATOR::CPUUpdateWamInfo
  1182. (
  1183. STR *pstrAppPath
  1184. )
  1185. {
  1186. CWamInfo* pWamInfo = NULL;
  1187. INT AppState;
  1188. DBG_ASSERT(pstrAppPath != NULL);
  1189. // Find the WamInfo.
  1190. AppState = FindWamInfo(pstrAppPath, &pWamInfo);
  1191. if (-1 != AppState)
  1192. {
  1193. BOOL fWasShutDown = FALSE;
  1194. DWORD eWISPrevState;
  1195. if ( !pWamInfo->FInProcess() )
  1196. {
  1197. //
  1198. // It's out of process and the job state has changed
  1199. // Shut it down so new value will get picked up
  1200. //
  1201. //
  1202. // change the state of the WamInfo
  1203. //
  1204. eWISPrevState = pWamInfo->ChangeToState(WIS_RUNNING, WIS_SHUTDOWN);
  1205. //
  1206. // Need to shutdown the Application
  1207. // remove from the hash table here. and clean up later.
  1208. if (WIS_RUNNING == eWISPrevState)
  1209. {
  1210. ShutdownWamInfo(pWamInfo, HASH_TABLE_REF + FIND_KEY_REF);
  1211. // NoNeed to go through the Dying List.
  1212. // But since we already have the Dying List, we might just used those functions.
  1213. // this means we might release other old WamInfo in the Dying List as well.
  1214. m_HashTable.DeleteKey(pstrAppPath->QueryStr());
  1215. fWasShutDown = TRUE;
  1216. }
  1217. }
  1218. pWamInfo->Dereference();
  1219. if (fWasShutDown)
  1220. {
  1221. pWamInfo->Dereference();
  1222. //delete pWamInfo;
  1223. }
  1224. }
  1225. }
  1226. /*-----------------------------------------------------------------------------
  1227. HRESULT
  1228. WAM_DICTATOR::WamRegSink
  1229. Argument:
  1230. szAppPath [in] Application Path.
  1231. dwCommand [in] Delete, Change, Stop..etc.
  1232. Return:
  1233. HRESULT
  1234. -----------------------------------------------------------------------------*/
  1235. HRESULT
  1236. WAM_DICTATOR::WamRegSink
  1237. (
  1238. LPCSTR szAppPath,
  1239. const DWORD dwCommand,
  1240. DWORD* pdwResult
  1241. )
  1242. {
  1243. CWamInfo* pWamInfo = NULL;
  1244. INT AppState;
  1245. HRESULT hrReturn = NOERROR;
  1246. DBG_ASSERT(szAppPath != NULL);
  1247. *pdwResult = APPSTATUS_UnLoaded;
  1248. //
  1249. // Set up the Application Path
  1250. //
  1251. STR* pstrAppPath = new STR(szAppPath);
  1252. if (NULL == pstrAppPath)
  1253. {
  1254. return E_OUTOFMEMORY;
  1255. }
  1256. Reference();
  1257. DBGPRINTF((DBG_CONTEXT,
  1258. "Wam Dictator received a SinkNotify on MD Path %s, cmd = %d\n",
  1259. szAppPath,
  1260. dwCommand));
  1261. if (dwCommand == APPCMD_UNLOAD)
  1262. {
  1263. hrReturn = UnLoadWamInfo(pstrAppPath, FALSE);
  1264. *pdwResult = (SUCCEEDED(hrReturn)) ? APPSTATUS_UnLoaded : APPSTATUS_Error;
  1265. }
  1266. else
  1267. {
  1268. // Find the WamInfo.
  1269. AppState = FindWamInfo(pstrAppPath, &pWamInfo);
  1270. if (-1 != AppState)
  1271. {
  1272. LK_RETCODE LkReturn;
  1273. DWORD eWISPrevState;
  1274. if (dwCommand == APPCMD_DELETE ||
  1275. dwCommand == APPCMD_CHANGETOINPROC ||
  1276. dwCommand == APPCMD_CHANGETOOUTPROC)
  1277. {
  1278. eWISPrevState = pWamInfo->ChangeToState(WIS_RUNNING, WIS_SHUTDOWN);
  1279. if (WIS_RUNNING == eWISPrevState)
  1280. {
  1281. LkReturn = m_HashTable.DeleteKey(pstrAppPath->QueryStr());
  1282. DBG_ASSERT(LK_SUCCESS == LkReturn);
  1283. if (LK_SUCCESS == LkReturn)
  1284. {
  1285. DyingList_Lock();
  1286. InsertDyingList(pWamInfo, FALSE);
  1287. DyingList_UnLock();
  1288. ScheduledId_Lock();
  1289. if (m_dwScheduledId == 0)
  1290. {
  1291. // DebugBreak();
  1292. m_dwScheduledId = ScheduleWorkItem
  1293. (
  1294. WAM_DICTATOR::CleanupScheduled,
  1295. NULL,
  1296. 0,
  1297. FALSE
  1298. );
  1299. DBGPRINTF((DBG_CONTEXT, "add schedule item %d\n", m_dwScheduledId));
  1300. DBG_ASSERT(m_dwScheduledId);
  1301. }
  1302. ScheduledId_UnLock();
  1303. *pdwResult = (SUCCEEDED(hrReturn)) ? APPSTATUS_UnLoaded : APPSTATUS_Error;
  1304. }
  1305. }
  1306. else
  1307. {
  1308. //
  1309. // Previous state is not running state. Leave it alone.
  1310. //
  1311. pWamInfo->Dereference();
  1312. }
  1313. }
  1314. else
  1315. {
  1316. // AppGetStatus
  1317. if (WIS_RUNNING == pWamInfo->QueryState())
  1318. {
  1319. *pdwResult = APPSTATUS_Running;
  1320. }
  1321. else
  1322. {
  1323. *pdwResult = APPSTATUS_Stopped;
  1324. }
  1325. pWamInfo->Dereference();
  1326. }
  1327. }
  1328. else
  1329. {
  1330. //
  1331. // This Application is not loaded.
  1332. *pdwResult = APPSTATUS_NotFoundInW3SVC;
  1333. }
  1334. }
  1335. Dereference();
  1336. return hrReturn;
  1337. }//WAM_DICTATOR::WamRegSink
  1338. /*-----------------------------------------------------------------------------
  1339. WAM_DICTATOR::InsertDyingList
  1340. This function insert an invalid WamInfo into DyingList. No blocking.
  1341. Arguments:
  1342. pWamInfo [in] a pointer to WamInfo
  1343. fNeedReference [in] if TRUE, then we Rereference the WamInfo. Because we get the WamInfo by
  1344. Hashtable's Interator or LookUp. When we get a WamInfo from Hash Table by
  1345. hash table's interator or LookUp call, the Hash table does a AddRef to
  1346. WamInfo. Therefore, this parameter tells whether we need to balance that
  1347. Addref or not.
  1348. Return Value:
  1349. NOERROR.
  1350. -----------------------------------------------------------------------------*/
  1351. HRESULT
  1352. WAM_DICTATOR::InsertDyingList
  1353. (
  1354. CWamInfo* pWamInfo,
  1355. BOOL fNeedReference
  1356. )
  1357. {
  1358. DBG_ASSERT(pWamInfo);
  1359. DyingList_Lock();
  1360. //
  1361. // Note: Delete Dereference WamInfo. This Dereference() within Delete() balances
  1362. // the AddRef() in Insert() call to the hash table.
  1363. //
  1364. InsertHeadList(&m_DyingListHead, &pWamInfo->ListEntry);
  1365. //
  1366. // Unfortunately, I can not move this Dereference() out of Critical Section.
  1367. // If I move the Dereference() after the Critical Section, then, since the WamInfo is
  1368. // already on the DyingList. therefore, any other thread happens to do the CleanUpDyingList()
  1369. // call before this Dereference() will have an unbalanced reference to the CWamInfo.
  1370. //
  1371. if (fNeedReference)
  1372. {
  1373. pWamInfo->Reference();
  1374. }
  1375. DyingList_UnLock();
  1376. return NOERROR;
  1377. }//WAM_DICTATOR::InsertDyingList
  1378. /*-----------------------------------------------------------------------------
  1379. WAM_DICTATOR::CleanUpDyingList
  1380. This function clean up any remaining WamInfo on the dying list.
  1381. Arguments:
  1382. Return Value:
  1383. HRESULT
  1384. -----------------------------------------------------------------------------*/
  1385. HRESULT
  1386. WAM_DICTATOR::CleanUpDyingList
  1387. (
  1388. VOID
  1389. )
  1390. {
  1391. IF_DEBUG( WAM_ISA_CALLS )
  1392. DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::CleanUpDyingList\n"));
  1393. CWamInfo * pWamInfo = NULL;
  1394. HRESULT hr = NOERROR;
  1395. PLIST_ENTRY pleTemp;
  1396. BOOL fNeedCleanupAction = FALSE;
  1397. DyingList_Lock();
  1398. if (!IsListEmpty(&m_DyingListHead))
  1399. {
  1400. if (!m_fCleanupInProgress)
  1401. {
  1402. m_fCleanupInProgress = TRUE;
  1403. fNeedCleanupAction = TRUE;
  1404. }
  1405. }
  1406. DBGPRINTF((DBG_CONTEXT, "Clean up dying list. (ScheduledId:%d).\n",
  1407. m_dwScheduledId));
  1408. DyingList_UnLock();
  1409. if (!fNeedCleanupAction)
  1410. {
  1411. goto Egress;
  1412. }
  1413. // From now on, only one thread is working on the killing dying list.
  1414. while (!IsListEmpty(&m_DyingListHead))
  1415. {
  1416. DyingList_Lock();
  1417. pleTemp = RemoveHeadList(&m_DyingListHead);
  1418. DyingList_UnLock();
  1419. DBG_ASSERT(pleTemp);
  1420. pWamInfo = CONTAINING_RECORD(
  1421. pleTemp,
  1422. CWamInfo,
  1423. ListEntry);
  1424. ShutdownWamInfo(pWamInfo, DYING_LIST_REF);
  1425. pWamInfo->Dereference();
  1426. pWamInfo->Dereference();
  1427. //delete pWamInfo;
  1428. pWamInfo = NULL;
  1429. }
  1430. InterlockedExchange((LPLONG)&m_fCleanupInProgress, (LONG)FALSE);
  1431. DBGPRINTF((DBG_CONTEXT, "CleanupDyingList done, ScheduledId(%d)\n", m_dwScheduledId));
  1432. Egress:
  1433. IF_DEBUG( WAM_ISA_CALLS )
  1434. DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::CleanUpDyingList done\n"));
  1435. m_dwScheduledId = 0;
  1436. return hr;
  1437. }//WAM_DICTATOR::CleanUpDyingList
  1438. /*-----------------------------------------------------------------------------
  1439. WAM_DICTATOR::StopAplicationsByInstance
  1440. Unload out-proc applications for a particular w3 server instance.
  1441. Arguments:
  1442. W3_SERVER_INSTANCE *pInstance
  1443. pointer to W3 Server Instance object. Used to query the server instance
  1444. metabase path.
  1445. Return Value:
  1446. none
  1447. -----------------------------------------------------------------------------*/
  1448. VOID
  1449. WAM_DICTATOR::StopApplicationsByInstance
  1450. (
  1451. VOID *pContext //W3_SERVER_INSTANCE *pInstance
  1452. )
  1453. {
  1454. IF_DEBUG( WAM_ISA_CALLS )
  1455. DBGPRINTF((DBG_CONTEXT, "WAM_DICTATOR::StopApplicationsByInstance\n"));
  1456. BUFFER bufDataPaths;
  1457. MB mb( (IMDCOM*) g_pInetSvc->QueryMDObject() );
  1458. LPSTR pszCurrentPath;
  1459. STR strPath;
  1460. W3_SERVER_INSTANCE *pInstance;
  1461. DBG_ASSERT(pContext != NULL);
  1462. DBG_ASSERT(g_pWamDictator != NULL);
  1463. pInstance = (W3_SERVER_INSTANCE *)pContext;
  1464. g_pWamDictator->Reference();
  1465. if (!g_pWamDictator->FIsShutdown())
  1466. {
  1467. if ( mb.Open( pInstance->QueryMDPath(),
  1468. METADATA_PERMISSION_READ ) )
  1469. {
  1470. //
  1471. // First find the OOP Applications
  1472. //
  1473. if (mb.GetDataPaths(NULL,
  1474. MD_APP_WAM_CLSID,
  1475. STRING_METADATA,
  1476. &bufDataPaths))
  1477. {
  1478. //
  1479. // For each OOP Application
  1480. //
  1481. mb.Close();
  1482. for (pszCurrentPath = (LPSTR)bufDataPaths.QueryPtr();
  1483. *pszCurrentPath != '\0';
  1484. pszCurrentPath += (strlen(pszCurrentPath) + 1))
  1485. {
  1486. strPath.Copy(pInstance->QueryMDPath());
  1487. strPath.Append(pszCurrentPath);
  1488. strPath.SetLen(strlen(strPath.QueryStr()) - 1);
  1489. g_pWamDictator->UnLoadWamInfo(&strPath, FALSE);
  1490. }
  1491. }
  1492. else
  1493. {
  1494. mb.Close();
  1495. }
  1496. }
  1497. } // WamDictator is in shutdown
  1498. g_pWamDictator->Dereference();
  1499. }
  1500. BOOL
  1501. WAM_DICTATOR::DeleteWamInfoFromHashTable
  1502. (
  1503. CWamInfo * pWamInfo
  1504. )
  1505. {
  1506. LK_RETCODE lkReturn;
  1507. const CHAR * szKey;
  1508. DBG_ASSERT( pWamInfo );
  1509. szKey = m_HashTable.ExtractKey( pWamInfo );
  1510. if ( !szKey )
  1511. {
  1512. return FALSE;
  1513. }
  1514. lkReturn = m_HashTable.DeleteKey( szKey );
  1515. return ( lkReturn == LK_SUCCESS );
  1516. }
  1517. /*-----------------------------------------------------------------------------
  1518. WAM_DICTATOR::DumpWamDictatorInfo
  1519. Description:
  1520. This function dumps the stats on all WAMs for diagnostics
  1521. Arguments:
  1522. pchBuffer - pointer to buffer that will contain the html results
  1523. lpcchBuffer - pointer to DWORD containing the size of buffer on entry
  1524. On return this contains the # of bytes written out to buffer
  1525. Return:
  1526. TRUE for success and FALSE for failure
  1527. Look at GetLastError() for the error code.
  1528. -----------------------------------------------------------------------------*/
  1529. BOOL
  1530. WAM_DICTATOR::DumpWamDictatorInfo
  1531. (
  1532. OUT CHAR * pchBuffer,
  1533. IN OUT LPDWORD lpcchBuffer
  1534. )
  1535. {
  1536. LIST_ENTRY * pEntry;
  1537. DWORD iCount, cch;
  1538. BOOL fRet = TRUE;
  1539. if ( lpcchBuffer == NULL )
  1540. {
  1541. SetLastError( ERROR_INVALID_PARAMETER);
  1542. return ( FALSE);
  1543. }
  1544. if ( 200 < *lpcchBuffer )
  1545. {
  1546. // Print the header blob
  1547. cch = wsprintf( pchBuffer,
  1548. " Wam Director Table (%x)<br>"
  1549. "<TABLE BORDER> <TR> "
  1550. "<TH> Wam Instance </TH> "
  1551. "<TH> Total Reqs </TH> "
  1552. "<TH> Current Reqs </TH> "
  1553. "<TH> Max Reqs </TH> "
  1554. " </TR>"
  1555. ,
  1556. this
  1557. );
  1558. }
  1559. else
  1560. {
  1561. cch = 200;
  1562. }
  1563. //
  1564. // For now there is only ONE WAM. Later use a loop to iterate thru WAMs
  1565. //
  1566. iCount = 0;
  1567. CWamInfoHash::CIterator iter;
  1568. LK_RETCODE lkReturn = m_HashTable.InitializeIterator(&iter);
  1569. WAM_STATISTICS_INFO wsi;
  1570. CWamInfo* pWamInfo;
  1571. DWORD dwErr;
  1572. HRESULT hr;
  1573. ZeroMemory( (PVOID ) &wsi, sizeof(wsi));
  1574. while (LK_SUCCESS == lkReturn)
  1575. {
  1576. CWamInfoHash::Record *pRec = iter.Record();
  1577. pWamInfo = (CWamInfo *)pRec;
  1578. hr = pWamInfo->GetStatistics( 0, &wsi);
  1579. if (SUCCEEDED(hr))
  1580. {
  1581. DBGPRINTF(( DBG_CONTEXT, " Wam(%08x)::GetStatistics( %08x) => %08x\n",
  1582. pWamInfo->QueryIWam(), &wsi, hr));
  1583. if ( (cch + 150 ) < *lpcchBuffer)
  1584. {
  1585. cch += wsprintf( pchBuffer + cch,
  1586. " <TR> <TD> [%d] %s </TD> "
  1587. " <TD> %4d </TD>"
  1588. " <TD> %4d </TD>"
  1589. " <TD> %4d </TD>"
  1590. " </TR>"
  1591. ,
  1592. iCount,
  1593. pWamInfo->QueryKey(),
  1594. wsi.WamStats0.TotalWamRequests,
  1595. wsi.WamStats0.CurrentWamRequests,
  1596. wsi.WamStats0.MaxWamRequests
  1597. );
  1598. iCount++;
  1599. }
  1600. else
  1601. {
  1602. cch += 150;
  1603. }
  1604. }
  1605. lkReturn = m_HashTable.IncrementIterator(&iter);
  1606. } // while()
  1607. DBG_ASSERT(lkReturn == LK_NO_MORE_ELEMENTS);
  1608. lkReturn = m_HashTable.CloseIterator(&iter);
  1609. DBG_REQUIRE(lkReturn == LK_SUCCESS);
  1610. //
  1611. // dump the final summary
  1612. //
  1613. if ( (cch + 100 ) < *lpcchBuffer)
  1614. {
  1615. cch += wsprintf( pchBuffer + cch,
  1616. " </TABLE>"
  1617. );
  1618. }
  1619. else
  1620. {
  1621. cch += 100;
  1622. }
  1623. if ( *lpcchBuffer < cch )
  1624. {
  1625. SetLastError( ERROR_INSUFFICIENT_BUFFER);
  1626. fRet = FALSE;
  1627. }
  1628. *lpcchBuffer = cch;
  1629. return (fRet);
  1630. } // WAM_DICTATOR::DumpWamDictatorInfo()
  1631. /*-----------------------------------------------------------------------------
  1632. Description:
  1633. Thunk so that we can dll export DumpWamDictatorInfo.
  1634. -----------------------------------------------------------------------------*/
  1635. extern "C"
  1636. BOOL WamDictatorDumpInfo
  1637. (
  1638. OUT CHAR * pch,
  1639. IN OUT LPDWORD lpcchBuff
  1640. )
  1641. {
  1642. return ( g_pWamDictator->DumpWamDictatorInfo( pch, lpcchBuff));
  1643. } // WamDictatorDumpInfo()
  1644. HRESULT W3SVC_WamRegSink
  1645. (
  1646. LPCSTR szAppPath,
  1647. const DWORD dwCommand,
  1648. DWORD* pdwResult
  1649. )
  1650. {
  1651. return (g_pWamDictator->WamRegSink(szAppPath, dwCommand, pdwResult));
  1652. } // W3SVCDictatorDumpInfo()
  1653. /*-----------------------------------------------------------------------------
  1654. Thunks for Fake NT APIs
  1655. -----------------------------------------------------------------------------*/
  1656. CRITICAL_SECTION g_csNonNTAPIs;
  1657. LONG FakeInterlockedCompareExchange(
  1658. LONG *Destination,
  1659. LONG Exchange,
  1660. LONG Comperand
  1661. );
  1662. LONG
  1663. FakeInterlockedCompareExchange(
  1664. LONG *Destination,
  1665. LONG Exchange,
  1666. LONG Comperand
  1667. )
  1668. /*-----------------------------------------------------------------------------
  1669. Description:
  1670. This function fakes the interlocked compare exchange operation for non NT
  1671. platforms
  1672. See WAMLoadNTApis() for details
  1673. Returns:
  1674. returns the old value at Destination
  1675. -----------------------------------------------------------------------------*/
  1676. {
  1677. LONG oldValue;
  1678. EnterCriticalSection( &g_csNonNTAPIs);
  1679. oldValue = *Destination;
  1680. if ( oldValue == Comperand ) {
  1681. *Destination = Exchange;
  1682. }
  1683. LeaveCriticalSection( &g_csNonNTAPIs);
  1684. return( oldValue);
  1685. } // FakeInterlockedCompareExchange()
  1686. static VOID
  1687. LoadNTApis(VOID)
  1688. /*-----------------------------------------------------------------------------
  1689. Description:
  1690. This function loads the entry point for functions from
  1691. Kernel32.dll. If the entry point is missing, the function
  1692. pointer will point to a fake routine which does nothing. Otherwise,
  1693. it will point to the real function.
  1694. It dynamically loads the kernel32.dll to find the entry ponit and then
  1695. unloads it after getting the address. For the resulting function
  1696. pointer to work correctly one has to ensure that the kernel32.dll is
  1697. linked with the dll/exe which links to this file.
  1698. -----------------------------------------------------------------------------*/
  1699. {
  1700. // Initialize the critical section for non NT API support, in case if we need this
  1701. INITIALIZE_CRITICAL_SECTION( &g_csNonNTAPIs);
  1702. if ( g_pfnInterlockedCompareExchange == NULL )
  1703. {
  1704. HINSTANCE tmpInstance;
  1705. //
  1706. // load kernel32 and get NT specific entry points
  1707. //
  1708. tmpInstance = LoadLibrary("kernel32.dll");
  1709. if ( tmpInstance != NULL )
  1710. {
  1711. // For some reason the original function is _InterlockedCompareExchange!
  1712. g_pfnInterlockedCompareExchange = (PFN_INTERLOCKED_COMPARE_EXCHANGE )
  1713. GetProcAddress( tmpInstance, "InterlockedCompareExchange");
  1714. if ( g_pfnInterlockedCompareExchange == NULL )
  1715. {
  1716. // the function is not available
  1717. // Just thunk it.
  1718. g_pfnInterlockedCompareExchange = FakeInterlockedCompareExchange;
  1719. }
  1720. //
  1721. // We can free this because we are statically linked to it
  1722. //
  1723. FreeLibrary(tmpInstance);
  1724. }
  1725. }
  1726. return;
  1727. } // WAMLoadNTApis()
  1728. static void
  1729. UnloadNTApis(VOID)
  1730. {
  1731. DeleteCriticalSection( &g_csNonNTAPIs);
  1732. return;
  1733. } // WAMUnloadNTApis()
  1734. VOID
  1735. RecycleCallback(
  1736. VOID * pvContext
  1737. )
  1738. {
  1739. CWamInfo * pWamInfo = (CWamInfo*)pvContext;
  1740. DBG_ASSERT( pWamInfo );
  1741. pWamInfo->m_fRecycled = TRUE;
  1742. if (!g_pWamDictator->m_PTable.RecycleWamInfo( pWamInfo ) )
  1743. {
  1744. DBGPRINTF((
  1745. DBG_CONTEXT,
  1746. "[RecycleCallback] failed to recycle CWamInfo 0x%08x.\r\n"
  1747. ));
  1748. }
  1749. pWamInfo->Dereference();
  1750. return;
  1751. }
  1752. /************************ End of File ***********************/