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.

590 lines
12 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. wpipm.cxx
  5. Abstract:
  6. Contains the WPIPM class that handles communication with
  7. the admin service. WPIPM responds to pings, and tells
  8. the process when to shut down.
  9. Author:
  10. Michael Courage (MCourage) 22-Feb-1999
  11. Revision History:
  12. --*/
  13. #include <precomp.hxx>
  14. #include "dbgutil.h"
  15. #include "wpipm.hxx"
  16. #include "RwpFunctions.hxx"
  17. extern PFN_ULATQ_COLLECT_PERF_COUNTERS g_pfnCollectCounters;
  18. /**
  19. *
  20. * Routine Description:
  21. *
  22. * Initializes WPIPM.
  23. *
  24. * Arguments:
  25. *
  26. * pWpContext - pointer to the wp context (so we can tell it to shutdown)
  27. *
  28. * Return Value:
  29. *
  30. * HRESULT
  31. */
  32. HRESULT
  33. WP_IPM::Initialize(
  34. WP_CONTEXT * pWpContext
  35. )
  36. {
  37. HRESULT hr = S_OK;
  38. m_pWpContext = pWpContext;
  39. DWORD dwId = GetCurrentProcessId();
  40. //
  41. // create pipe
  42. //
  43. hr = IPM_MESSAGE_PIPE::CreateIpmMessagePipe(this,
  44. pWpContext->QueryConfig()->QueryNamedPipeId(),
  45. FALSE, // not server side
  46. NULL, // security descriptor
  47. &m_pPipe);
  48. if (FAILED(hr))
  49. {
  50. goto exit;
  51. }
  52. //
  53. // Send the real pid over the pipe
  54. //
  55. if(!RWP_IPM_BadParamTest(RWP_IPM_OP_GETPID, &hr, m_pPipe))
  56. {
  57. hr = m_pPipe->WriteMessage(IPM_OP_GETPID,
  58. sizeof(dwId),
  59. &dwId);
  60. }
  61. if (FAILED(hr))
  62. {
  63. goto exit;
  64. }
  65. hr = S_OK;
  66. exit:
  67. if (FAILED(hr))
  68. {
  69. Terminate();
  70. }
  71. return hr;
  72. }
  73. /**
  74. *
  75. * Routine Description:
  76. *
  77. * Terminates WPIPM.
  78. *
  79. * If the message pipe is open this function will disconnect it
  80. * and wait for the pipe's disconnection callback.
  81. *
  82. * Arguments:
  83. *
  84. * None.
  85. *
  86. * Return Value:
  87. *
  88. * HRESULT
  89. */
  90. HRESULT
  91. WP_IPM::Terminate(
  92. VOID
  93. )
  94. {
  95. if (m_pPipe)
  96. {
  97. m_pPipe->DestroyIpmMessagePipe();
  98. // pipe deletes itself
  99. m_pPipe = NULL;
  100. }
  101. m_pWpContext = NULL;
  102. return S_OK;
  103. }
  104. /**
  105. *
  106. *
  107. * Routine Description:
  108. *
  109. * This is a callback from the message pipe that means
  110. * the pipe has received a message.
  111. *
  112. * We decode the message and respond appropriately.
  113. *
  114. * Arguments:
  115. *
  116. * pPipeMessage - the message that we received
  117. *
  118. * Return Value:
  119. *
  120. * HRESULT
  121. *
  122. */
  123. VOID
  124. WP_IPM::AcceptMessage(
  125. IN const IPM_MESSAGE * pPipeMessage
  126. )
  127. {
  128. HRESULT hr = NO_ERROR;
  129. BOOL fRet = FALSE;
  130. switch (pPipeMessage->GetOpcode())
  131. {
  132. case IPM_OP_PING:
  133. //
  134. // Pings must go through the same mechanism that requests go through
  135. // to verify that requests are being picked off of the completion port
  136. //
  137. fRet = ThreadPoolPostCompletion(0, HandlePing, (LPOVERLAPPED)this);
  138. if (FALSE == fRet)
  139. {
  140. hr = HRESULT_FROM_WIN32(GetLastError());
  141. DBGPRINTF((DBG_CONTEXT, "Posting completion for ping handling failed"));
  142. break;
  143. }
  144. break;
  145. case IPM_OP_SHUTDOWN:
  146. hr = HandleShutdown(
  147. *( reinterpret_cast<const BOOL *>( pPipeMessage->GetData() ) )
  148. );
  149. break;
  150. case IPM_OP_REQUEST_COUNTERS:
  151. hr = HandleCounterRequest();
  152. break;
  153. case IPM_OP_PERIODIC_PROCESS_RESTART_PERIOD_IN_MINUTES:
  154. DBG_ASSERT( pPipeMessage->GetData() != NULL );
  155. hr = WP_RECYCLER::StartTimeBased(
  156. *( reinterpret_cast<const DWORD *>( pPipeMessage->GetData() ) )
  157. );
  158. hr = NO_ERROR;
  159. break;
  160. case IPM_OP_PERIODIC_PROCESS_RESTART_MEMORY_USAGE_IN_KB:
  161. {
  162. DBG_ASSERT( pPipeMessage->GetData() != NULL );
  163. // there are 2 DWORDS sent with memory based recycling
  164. // first is Max Virtual Memory, second is Max Private Bytes
  165. DWORD dwMaxVirtualMemoryKbUsage =
  166. *( reinterpret_cast<const DWORD *>( pPipeMessage->GetData() ) );
  167. DWORD dwMaxPrivateBytesKbUsage =
  168. *( reinterpret_cast<const DWORD *>( pPipeMessage->GetData() ) + 1 );
  169. hr = WP_RECYCLER::StartMemoryBased(
  170. dwMaxVirtualMemoryKbUsage,
  171. dwMaxPrivateBytesKbUsage );
  172. hr = NO_ERROR;
  173. break;
  174. }
  175. case IPM_OP_PERIODIC_PROCESS_RESTART_SCHEDULE:
  176. DBG_ASSERT( pPipeMessage->GetData() != NULL );
  177. hr = WP_RECYCLER::StartScheduleBased(
  178. ( reinterpret_cast<const WCHAR *>( pPipeMessage->GetData() ) )
  179. );
  180. hr = NO_ERROR;
  181. break;
  182. default:
  183. DBG_ASSERT(FALSE);
  184. hr = E_FAIL;
  185. break;
  186. }
  187. return;
  188. }
  189. /**
  190. *
  191. * Routine Description:
  192. *
  193. * This is a callback from the message pipe that means
  194. * the pipe has been connected and is ready for use.
  195. *
  196. * Arguments:
  197. *
  198. * None.
  199. *
  200. * Return Value:
  201. *
  202. * VOID
  203. */
  204. VOID
  205. WP_IPM::PipeConnected(
  206. VOID
  207. )
  208. {
  209. return;
  210. }
  211. /**
  212. *
  213. * Routine Description:
  214. *
  215. * This is a callback from the message pipe that means
  216. * the pipe has been disconnected and you won't be receiving
  217. * any more messages.
  218. *
  219. * Tells WPIPM::Terminate that it's ok to exit now.
  220. *
  221. * Arguments:
  222. *
  223. * hr - the error code associated with the pipe disconnection
  224. *
  225. * Return Value:
  226. *
  227. * VOID
  228. */
  229. VOID
  230. WP_IPM::PipeDisconnected(
  231. IN HRESULT hr
  232. )
  233. {
  234. if (FAILED(hr))
  235. {
  236. DBGPRINTF( (DBG_CONTEXT, "PipeDisconnected with hr ( %d).\n", hr));
  237. }
  238. //
  239. // All sorts of miscreant shutdown behaviors (for testing)
  240. //
  241. if (RwpBehaviorExhibited = RWP_Shutdown_Behavior(&hr))
  242. return;
  243. //
  244. // If the pipe disappears out from under us, WAS has probably orphaned
  245. // us, initiate fast shutdown of this worker process.
  246. //
  247. if (!m_pWpContext->IsInShutdown() &&
  248. hr != HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED) &&
  249. IsDebuggerPresent())
  250. {
  251. DBG_ASSERT( !"w3wp.exe is getting orphaned" );
  252. }
  253. m_pWpContext->IndicateShutdown( TRUE );
  254. return;
  255. }
  256. /**
  257. *
  258. * Routine Description:
  259. *
  260. * This is a callback from the message pipe that means
  261. * that the pipe received an invalid message.
  262. * Therefore, we signal to shutdown.
  263. *
  264. * Arguments:
  265. *
  266. * VOID
  267. *
  268. * Return Value:
  269. *
  270. * VOID
  271. */
  272. VOID
  273. WP_IPM::PipeMessageInvalid(
  274. VOID
  275. )
  276. {
  277. return PipeDisconnected(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
  278. }
  279. /**
  280. *
  281. * Routine Description:
  282. *
  283. * Handles the ping message. Sends the ping response message.
  284. *
  285. * Arguments:
  286. *
  287. * None.
  288. *
  289. * Return Value:
  290. *
  291. * HRESULT
  292. */
  293. //static
  294. VOID
  295. WP_IPM::HandlePing(
  296. DWORD,
  297. DWORD dwNumberOfBytesTransferred,
  298. LPOVERLAPPED lpOverlapped
  299. )
  300. {
  301. HRESULT hr = NO_ERROR;
  302. if (0 != dwNumberOfBytesTransferred)
  303. {
  304. DBG_ASSERT(0 == dwNumberOfBytesTransferred);
  305. return;
  306. }
  307. DBG_ASSERT(NULL != lpOverlapped);
  308. WP_IPM * pThis = (WP_IPM*) lpOverlapped;
  309. DBG_ASSERT(pThis->m_pPipe);
  310. DBGPRINTF( (DBG_CONTEXT, "Handle Ping\n\n"));
  311. // If we're supposed to do this test, just do it here. Don't care about result
  312. RWP_IPM_BadParamTest(RWP_IPM_OP_INVALID, &hr, pThis->m_pPipe);
  313. RWP_IPM_BadParamTest(RWP_IPM_OP_PING_REPLY, &hr, pThis->m_pPipe);
  314. // Even if we're testing this opcode, just do regular ping response so the app pool
  315. // doesn't get shut down by WAS
  316. if (RWP_NO_MISBEHAVE == RWP_Ping_Behavior(&hr, pThis->m_pPipe))
  317. {
  318. hr = pThis->m_pPipe->WriteMessage(
  319. IPM_OP_PING_REPLY, // ping reply opcode
  320. 0, // no data to send
  321. NULL // pointer to no data
  322. );
  323. if ( FAILED ( hr ) )
  324. {
  325. DBGPRINTF( (DBG_CONTEXT, "Failed to respond to ping\n\n"));
  326. goto exit;
  327. }
  328. }
  329. //
  330. // if we are not healthy then we need to to ask WAS to
  331. // shut us down.
  332. //
  333. if ( !( g_pwpContext->GetUnhealthy()))
  334. {
  335. DBGPRINTF( (DBG_CONTEXT, "Requesting shutdown due to isapi reporting unhealthiness\n\n"));
  336. hr = pThis->SendMsgToAdminProcess( IPM_WP_RESTART_ISAPI_REQUESTED_RECYCLE );
  337. if ( FAILED ( hr ) )
  338. {
  339. DBGPRINTF( (DBG_CONTEXT, "Failed telling WAS to shut us down\n\n"));
  340. goto exit;
  341. }
  342. }
  343. exit:
  344. return;
  345. }
  346. /**
  347. *
  348. * Routine Description:
  349. *
  350. * Handles the counter request message.
  351. *
  352. * Arguments:
  353. *
  354. * None.
  355. *
  356. * Return Value:
  357. *
  358. * HRESULT
  359. */
  360. HRESULT
  361. WP_IPM::HandleCounterRequest(
  362. VOID
  363. )
  364. {
  365. DBGPRINTF( (DBG_CONTEXT, "Handle Counter Request\n\n"));
  366. HRESULT hr;
  367. PBYTE pCounterData;
  368. DWORD dwCounterData;
  369. DBG_ASSERT ( m_pPipe );
  370. if (FAILED(hr = g_pfnCollectCounters(&pCounterData, &dwCounterData)))
  371. {
  372. DBGPRINTF( (DBG_CONTEXT, "Didn't collect counters\n\n"));
  373. return hr;
  374. }
  375. if(!RWP_IPM_BadParamTest(RWP_IPM_OP_SEND_COUNTERS, &hr, m_pPipe))
  376. {
  377. // If we're not testing this opcode, just do regular valid call
  378. hr = m_pPipe->WriteMessage(IPM_OP_SEND_COUNTERS, // ping reply opcode
  379. dwCounterData, // no data to send
  380. pCounterData); // pointer to no data
  381. }
  382. return hr;
  383. }
  384. /**
  385. *
  386. * Routine Description:
  387. *
  388. *
  389. * Handles the shutdown message. Shuts down the process
  390. *
  391. * Arguments:
  392. *
  393. * None.
  394. *
  395. * Return Value:
  396. *
  397. * HRESULT
  398. */
  399. HRESULT
  400. WP_IPM::HandleShutdown(
  401. BOOL fDoImmediate
  402. )
  403. {
  404. HRESULT hr = S_OK;
  405. DBGPRINTF( (DBG_CONTEXT, "Handle ******************** Shutdown\n\n"));
  406. //
  407. // All sorts of miscreant shutdown behaviors (for testing)
  408. //
  409. if (RwpBehaviorExhibited = RWP_Shutdown_Behavior(&hr))
  410. return (hr);
  411. m_pWpContext->IndicateShutdown( fDoImmediate );
  412. return hr;
  413. }
  414. /**
  415. *
  416. * Routine Description:
  417. *
  418. * Sends the message to indicate the worker process has either finished
  419. * initializing or has failed to initialize.
  420. *
  421. * Arguments:
  422. *
  423. * HRESULT indicating success/failure of initialization
  424. *
  425. * Return Value:
  426. *
  427. * HRESULT
  428. */
  429. HRESULT
  430. WP_IPM::SendInitCompleteMessage(
  431. HRESULT hrToSend
  432. )
  433. {
  434. HRESULT hr;
  435. if ( m_pPipe )
  436. {
  437. if(!RWP_IPM_BadParamTest(RWP_IPM_OP_HRESULT, &hr, m_pPipe))
  438. {
  439. // If we're not testing this opcode, just do regular valid call
  440. hr = m_pPipe->WriteMessage(
  441. IPM_OP_HRESULT, // opcode
  442. sizeof( hrToSend ), // data length
  443. reinterpret_cast<BYTE*>( &hrToSend ) // pointer to data
  444. );
  445. }
  446. return hr;
  447. }
  448. // if the pipe did not exist then we started up
  449. // without the IPM, probably we are attempting
  450. // to run without WAS support. ( from the cmd line )
  451. return S_OK;
  452. }
  453. /**
  454. *
  455. * Routine Description:
  456. *
  457. * Sends the message to indicate the worker process has reach certain state.
  458. * Main use is in shutdown. See IPM_WP_SHUTDOWN_MSG for reasons.
  459. *
  460. * Arguments:
  461. *
  462. * None.
  463. *
  464. * Return Value:
  465. *
  466. * HRESULT
  467. */
  468. HRESULT
  469. WP_IPM::SendMsgToAdminProcess(
  470. IPM_WP_SHUTDOWN_MSG reason
  471. )
  472. {
  473. HRESULT hr = S_OK;
  474. if (m_pPipe)
  475. {
  476. //
  477. // All sorts of miscreant process rotation behaviors (for testing)
  478. //
  479. if (RwpBehaviorExhibited = RWP_Rotation_Behavior(&hr, m_pPipe))
  480. return (hr);
  481. if(!RWP_IPM_BadParamTest(RWP_IPM_OP_WORKER_REQUESTS_SHUTDOWN, &hr, m_pPipe))
  482. {
  483. // If we're not testing this opcode, just do regular valid call
  484. hr = m_pPipe->WriteMessage(
  485. IPM_OP_WORKER_REQUESTS_SHUTDOWN, // sends message indicate shutdown
  486. sizeof(reason), // no data to send
  487. (BYTE *)&reason // pointer to no data
  488. );
  489. }
  490. }
  491. return S_OK;
  492. }