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.

555 lines
17 KiB

  1. // --------------------------------------------------------------------------
  2. // Module Name: APIDispatcher.cpp
  3. //
  4. // Copyright (c) 1999-2000, Microsoft Corporation
  5. //
  6. // A class that handles API requests in the server on a separate thread. Each
  7. // thread is dedicated to respond to a single client. This is acceptable for
  8. // a lightweight server.
  9. //
  10. // History: 1999-11-07 vtan created
  11. // 2000-08-25 vtan moved from Neptune to Whistler
  12. // --------------------------------------------------------------------------
  13. #include "StandardHeader.h"
  14. #include "APIDispatcher.h"
  15. #include "APIRequest.h"
  16. #include "SingleThreadedExecution.h"
  17. #include "StatusCode.h"
  18. // --------------------------------------------------------------------------
  19. // CAPIDispatcher::CAPIDispatcher
  20. //
  21. // Arguments: hClientProcess = HANDLE to the client process.
  22. //
  23. // Returns: <none>
  24. //
  25. // Purpose: Constructor for CAPIDispatcher. The handle to the client
  26. // process is transferred to this object.
  27. //
  28. // History: 1999-11-07 vtan created
  29. // 2000-08-25 vtan moved from Neptune to Whistler
  30. // --------------------------------------------------------------------------
  31. CAPIDispatcher::CAPIDispatcher (HANDLE hClientProcess) :
  32. _hSection(NULL),
  33. _pSection(NULL),
  34. _hProcessClient(hClientProcess),
  35. _hPort(NULL),
  36. _fRequestsPending(false),
  37. _fConnectionClosed(false)
  38. {
  39. }
  40. // --------------------------------------------------------------------------
  41. // CAPIDispatcher::~CAPIDispatcher
  42. //
  43. // Arguments: <none>
  44. //
  45. // Returns: <none>
  46. //
  47. // Purpose: Destructor for CAPIDispatcher. Release the port handle if
  48. // present. Release the process handle.
  49. //
  50. // History: 1999-11-07 vtan created
  51. // 2000-08-25 vtan moved from Neptune to Whistler
  52. // --------------------------------------------------------------------------
  53. CAPIDispatcher::~CAPIDispatcher (void)
  54. {
  55. ASSERTMSG(_fConnectionClosed, "Destructor invoked without connection being closed in CAPIDispatcher::~CAPIDispatcher");
  56. if (_pSection != NULL)
  57. {
  58. TBOOL(UnmapViewOfFile(_pSection));
  59. _pSection = NULL;
  60. }
  61. ReleaseHandle(_hSection);
  62. ReleaseHandle(_hPort);
  63. ReleaseHandle(_hProcessClient);
  64. }
  65. // --------------------------------------------------------------------------
  66. // CAPIDispatcher::GetClientProcess
  67. //
  68. // Arguments: <none>
  69. //
  70. // Returns: HANDLE
  71. //
  72. // Purpose: Returns the handle to the client process. This is not
  73. // duplicated. DO NOT CLOSE THIS HANDLE.
  74. //
  75. // History: 1999-11-07 vtan created
  76. // 2000-08-25 vtan moved from Neptune to Whistler
  77. // --------------------------------------------------------------------------
  78. HANDLE CAPIDispatcher::GetClientProcess (void) const
  79. {
  80. return(_hProcessClient);
  81. }
  82. // --------------------------------------------------------------------------
  83. // CAPIDispatcher::GetClientSessionID
  84. //
  85. // Arguments: <none>
  86. //
  87. // Returns: DWORD
  88. //
  89. // Purpose: Returns the client session ID.
  90. //
  91. // History: 2000-11-11 vtan created
  92. // --------------------------------------------------------------------------
  93. DWORD CAPIDispatcher::GetClientSessionID (void) const
  94. {
  95. DWORD dwSessionID;
  96. ULONG ulReturnLength;
  97. PROCESS_SESSION_INFORMATION processSessionInformation;
  98. if (NT_SUCCESS(NtQueryInformationProcess(_hProcessClient,
  99. ProcessSessionInformation,
  100. &processSessionInformation,
  101. sizeof(processSessionInformation),
  102. &ulReturnLength)))
  103. {
  104. dwSessionID = processSessionInformation.SessionId;
  105. }
  106. else
  107. {
  108. dwSessionID = 0;
  109. }
  110. return(dwSessionID);
  111. }
  112. // --------------------------------------------------------------------------
  113. // CAPIDispatcher::SetPort
  114. //
  115. // Arguments: hPort = Reply port received from
  116. // ntdll!NtAcceptConnectionPort.
  117. //
  118. // Returns: <none>
  119. //
  120. // Purpose: Sets the given port handle into this object. The handle
  121. // ownership is transferred. Wait until the thread processing
  122. // requests is ready before returning.
  123. //
  124. // History: 1999-11-07 vtan created
  125. // 2000-08-25 vtan moved from Neptune to Whistler
  126. // --------------------------------------------------------------------------
  127. void CAPIDispatcher::SetPort (HANDLE hPort)
  128. {
  129. _hPort = hPort;
  130. }
  131. // --------------------------------------------------------------------------
  132. // CAPIDispatcher::GetSection
  133. //
  134. // Arguments: <none>
  135. //
  136. // Returns: HANDLE
  137. //
  138. // Purpose: Returns a handle to a section used to communicate large
  139. // quantities of data from client to server. If the section has
  140. // not been created then create it.
  141. //
  142. // History: 2000-10-10 vtan created
  143. // --------------------------------------------------------------------------
  144. HANDLE CAPIDispatcher::GetSection (void)
  145. {
  146. if (_hSection == NULL)
  147. {
  148. TSTATUS(CreateSection());
  149. }
  150. return(_hSection);
  151. }
  152. // --------------------------------------------------------------------------
  153. // CAPIDispatcher::GetSectionAddress
  154. //
  155. // Arguments: <none>
  156. //
  157. // Returns: void*
  158. //
  159. // Purpose: Returns the mapped address of the section.
  160. //
  161. // History: 2000-10-10 vtan created
  162. // --------------------------------------------------------------------------
  163. void* CAPIDispatcher::GetSectionAddress (void) const
  164. {
  165. return(_pSection);
  166. }
  167. // --------------------------------------------------------------------------
  168. // CAPIDispatcher::CloseConnection
  169. //
  170. // Arguments: <none>
  171. //
  172. // Returns: NTSTATUS
  173. //
  174. // Purpose: Sets the member variable indicating the dispatcher's port has
  175. // been closed and that any pending requests are now invalid.
  176. // The object is reference counted so if there are any pending
  177. // requests they will release their reference when they're done.
  178. // The caller of this function releases its reference.
  179. //
  180. // History: 1999-11-07 vtan created
  181. // 2000-08-25 vtan moved from Neptune to Whistler
  182. // 2000-11-08 vtan reference counted object
  183. // --------------------------------------------------------------------------
  184. NTSTATUS CAPIDispatcher::CloseConnection (void)
  185. {
  186. CSingleThreadedExecution requestsPendingLock(_lock);
  187. _fConnectionClosed = true;
  188. return(STATUS_SUCCESS);
  189. }
  190. // --------------------------------------------------------------------------
  191. // CAPIDispatcher::QueueRequest
  192. //
  193. // Arguments: portMessage = CPortMessage of request.
  194. //
  195. // Returns: NTSTATUS
  196. //
  197. // Purpose: Checks if the connection has been closed. If closed then it
  198. // rejects the request. Otherwise it queues it.
  199. //
  200. // History: 2000-12-02 vtan created
  201. // --------------------------------------------------------------------------
  202. NTSTATUS CAPIDispatcher::QueueRequest (const CPortMessage& portMessage)
  203. {
  204. NTSTATUS status;
  205. if (_fConnectionClosed)
  206. {
  207. status = RejectRequest(portMessage, STATUS_PORT_DISCONNECTED);
  208. }
  209. else
  210. {
  211. status = CreateAndQueueRequest(portMessage);
  212. }
  213. return(status);
  214. }
  215. // --------------------------------------------------------------------------
  216. // CAPIDispatcher::ExecuteRequest
  217. //
  218. // Arguments: portMessage = CPortMessage of request.
  219. //
  220. // Returns: NTSTATUS
  221. //
  222. // Purpose: Checks if the connection has been closed. If closed then it
  223. // rejects the request. Otherwise it executes it.
  224. //
  225. // History: 2000-12-02 vtan created
  226. // --------------------------------------------------------------------------
  227. NTSTATUS CAPIDispatcher::ExecuteRequest (const CPortMessage& portMessage)
  228. {
  229. NTSTATUS status;
  230. if (_fConnectionClosed)
  231. {
  232. status = RejectRequest(portMessage, STATUS_PORT_DISCONNECTED);
  233. }
  234. else
  235. {
  236. status = CreateAndExecuteRequest(portMessage);
  237. }
  238. return(status);
  239. }
  240. // --------------------------------------------------------------------------
  241. // CAPIDispatcher::RejectRequest
  242. //
  243. // Arguments: portMessage = CPortMessage of request.
  244. //
  245. // Returns: NTSTATUS
  246. //
  247. // Purpose: Sends back a reply to the caller STATUS_PORT_DISCONNECTED to
  248. // reject the request.
  249. //
  250. // History: 2000-12-02 vtan created
  251. // --------------------------------------------------------------------------
  252. NTSTATUS CAPIDispatcher::RejectRequest (const CPortMessage& portMessage, NTSTATUS status) const
  253. {
  254. CPortMessage portMessageOut(portMessage);
  255. // Send the message back to the client.
  256. portMessageOut.SetDataLength(sizeof(NTSTATUS));
  257. portMessageOut.SetReturnCode(status);
  258. return(SendReply(portMessageOut));
  259. }
  260. // --------------------------------------------------------------------------
  261. // CAPIDispatcher::Entry
  262. //
  263. // Arguments: <none>
  264. //
  265. // Returns: <none>
  266. //
  267. // Purpose: Main entry point for processing LPC requests. If there are
  268. // pending requests in the queue pick them off and process them.
  269. // While processing them more items can get queued. Keep
  270. // processing until there are no more queued items. There is a
  271. // possible overlap where a newly queued item can be missed. In
  272. // that case a new work item is queued to execute those requests.
  273. //
  274. // History: 1999-11-07 vtan created
  275. // 2000-08-25 vtan moved from Neptune to Whistler
  276. // --------------------------------------------------------------------------
  277. void CAPIDispatcher::Entry (void)
  278. {
  279. CAPIRequest *pAPIRequest;
  280. // Acquire the requests pending lock before fetching the first
  281. // request. This will ensure an accurate result.
  282. _lock.Acquire();
  283. pAPIRequest = static_cast<CAPIRequest*>(_queue.Get());
  284. // If there are more requests in the queue keep looping.
  285. while (pAPIRequest != NULL)
  286. {
  287. // Release the requests pending lock to allow more requests to
  288. // get queued to this dispatcher while the dispatch is executing.
  289. if (!_fConnectionClosed)
  290. {
  291. NTSTATUS status;
  292. // Before executing the API request release the lock to allow
  293. // more requests to get queued while executing this one.
  294. _lock.Release();
  295. // Execute the request.
  296. status = Execute(pAPIRequest);
  297. // Acquire the requests pending lock again before getting
  298. // the next available request. If the loop continues the
  299. // lock will be released at the top of the loop. If the loop
  300. // exits then the lock must be released outside.
  301. _lock.Acquire();
  302. // On debug builds ignore STATUS_REPLY_MESSAGE_MISMATCH.
  303. // This typically happens on stress machines where timing
  304. // causes the thread waiting on the reply to go away before
  305. // the service has a chance to reply to the LPC request.
  306. #ifdef DEBUG
  307. if (!_fConnectionClosed && !ExcludedStatusCodeForDebug(status))
  308. {
  309. TSTATUS(status);
  310. }
  311. #endif /* DEBUG */
  312. }
  313. // Remove this processed request.
  314. _queue.Remove();
  315. // Get the next request. A request may have been queued while
  316. // processing the request just processed. So keep looping until
  317. // there really are no requests left.
  318. pAPIRequest = static_cast<CAPIRequest*>(_queue.Get());
  319. }
  320. // Set the state to no longer processing requests so that any
  321. // further queued requests will cause the dispatcher to be
  322. // re-invoked in a new worker thread. Release the lock.
  323. _fRequestsPending = false;
  324. _lock.Release();
  325. }
  326. // --------------------------------------------------------------------------
  327. // CAPIDispatcher::Execute
  328. //
  329. // Arguments: pAPIRequest = API request to execute.
  330. //
  331. // Returns: NTSTATUS
  332. //
  333. // Purpose: Execute the API request. This can be done from a queued work
  334. // item executing on a different thread or execute in the server
  335. // port listen thread.
  336. //
  337. // History: 2000-10-19 vtan created
  338. // --------------------------------------------------------------------------
  339. NTSTATUS CAPIDispatcher::Execute (CAPIRequest *pAPIRequest) const
  340. {
  341. NTSTATUS status;
  342. // Set the return data size to NTSTATUS by default. Execute the
  343. // request. Store the result. If the executed function has more
  344. // data to return it will set the size itself.
  345. pAPIRequest->SetDataLength(sizeof(NTSTATUS));
  346. // Protect the execution with an exception block. If the code
  347. // throws an exception it would normally just kill the worker
  348. // thread. However, the CAPIDispatcher would be left in a state
  349. // where it was marked as still executing requests even though
  350. // the thread died. If an exception is thrown the function is
  351. // considered unsuccessful.
  352. __try
  353. {
  354. status = pAPIRequest->Execute();
  355. }
  356. __except (DispatcherExceptionFilter(GetExceptionInformation()))
  357. {
  358. status = STATUS_UNSUCCESSFUL;
  359. }
  360. pAPIRequest->SetReturnCode(status);
  361. // Reply to the client with the result.
  362. return(SendReply(*pAPIRequest));
  363. }
  364. // --------------------------------------------------------------------------
  365. // CAPIDispatcher::CreateSection
  366. //
  367. // Arguments: <none>
  368. //
  369. // Returns: NTSTATUS
  370. //
  371. // Purpose: Overridable function that creates a section object. Because
  372. // size is not determinable it can be inheritable.
  373. //
  374. // The default implementation does nothing.
  375. //
  376. // History: 2000-10-10 vtan created
  377. // --------------------------------------------------------------------------
  378. NTSTATUS CAPIDispatcher::CreateSection (void)
  379. {
  380. return(STATUS_NOT_IMPLEMENTED);
  381. }
  382. // --------------------------------------------------------------------------
  383. // CAPIDispatcher::SignalRequestPending
  384. //
  385. // Arguments: <none>
  386. //
  387. // Returns: NTSTATUS
  388. //
  389. // Purpose: Signals the event to wake up the thread processing requests.
  390. //
  391. // History: 1999-11-07 vtan created
  392. // 2000-08-25 vtan moved from Neptune to Whistler
  393. // --------------------------------------------------------------------------
  394. NTSTATUS CAPIDispatcher::SignalRequestPending (void)
  395. {
  396. NTSTATUS status;
  397. CSingleThreadedExecution requestsPendingLock(_lock);
  398. // Only check the validity of _fRequestsPending after acquiring the
  399. // lock. This will guarantee that the value of this variable is
  400. // 100% correct in a multi worker threaded environment.
  401. if (!_fRequestsPending)
  402. {
  403. _fRequestsPending = true;
  404. status = Queue();
  405. }
  406. else
  407. {
  408. status = STATUS_SUCCESS;
  409. }
  410. return(status);
  411. }
  412. // --------------------------------------------------------------------------
  413. // CAPIDispatcher::SendReply
  414. //
  415. // Arguments: portMessage = CPortMessage to send in the reply.
  416. //
  417. // Returns: NTSTATUS
  418. //
  419. // Purpose: Sends a reply to the LPC port so the caller can be unblocked.
  420. //
  421. // History: 2000-10-19 vtan created
  422. // --------------------------------------------------------------------------
  423. NTSTATUS CAPIDispatcher::SendReply (const CPortMessage& portMessage) const
  424. {
  425. return(NtReplyPort(_hPort, const_cast<PORT_MESSAGE*>(portMessage.GetPortMessage())));
  426. }
  427. #ifdef DEBUG
  428. // --------------------------------------------------------------------------
  429. // CAPIDispatcher::ExcludedStatusCodeForDebug
  430. //
  431. // Arguments: status = NTSTATUS code to check.
  432. //
  433. // Returns: bool
  434. //
  435. // Purpose: Returns whether this status code should be ignored on asserts.
  436. //
  437. // History: 2001-03-30 vtan created
  438. // --------------------------------------------------------------------------
  439. bool CAPIDispatcher::ExcludedStatusCodeForDebug (NTSTATUS status)
  440. {
  441. return((status == STATUS_REPLY_MESSAGE_MISMATCH) ||
  442. (status == STATUS_INVALID_CID));
  443. }
  444. #endif /* DEBUG */
  445. // --------------------------------------------------------------------------
  446. // CAPIDispatcher::DispatcherExceptionFilter
  447. //
  448. // Arguments: <none>
  449. //
  450. // Returns: LONG
  451. //
  452. // Purpose: Filters exceptions that occur when dispatching API requests.
  453. //
  454. // History: 2000-10-13 vtan created
  455. // --------------------------------------------------------------------------
  456. LONG WINAPI CAPIDispatcher::DispatcherExceptionFilter (struct _EXCEPTION_POINTERS *pExceptionInfo)
  457. {
  458. (LONG)RtlUnhandledExceptionFilter(pExceptionInfo);
  459. return(EXCEPTION_EXECUTE_HANDLER);
  460. }