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.

743 lines
18 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1997.
  5. //
  6. // File: lpcsvr.c
  7. //
  8. // Contents:
  9. //
  10. // Classes:
  11. //
  12. // Functions:
  13. //
  14. // History: 12-12-97 RichardW Created
  15. //
  16. //----------------------------------------------------------------------------
  17. #include <ntos.h>
  18. #include <nt.h>
  19. #include <ntrtl.h>
  20. #include <nturtl.h>
  21. #include "lpcsvr.h"
  22. #define RtlpLpcLockServer( s ) RtlEnterCriticalSection( &s->Lock );
  23. #define RtlpLpcUnlockServer( s ) RtlLeaveCriticalSection( &s->Lock );
  24. #define RtlpLpcContextFromClient( p ) ( CONTAINING_RECORD( p, LPCSVR_CONTEXT, PrivateContext ) )
  25. //+---------------------------------------------------------------------------
  26. //
  27. // Function: RtlpLpcDerefContext
  28. //
  29. // Synopsis: Deref the context. If this context is being cleaned up after
  30. // the server has been deleted, then the message is freed directly,
  31. // rather than being released to the general queue.
  32. //
  33. // Arguments: [Context] --
  34. // [Message] --
  35. //
  36. // History: 2-06-98 RichardW Created
  37. //
  38. // Notes:
  39. //
  40. //----------------------------------------------------------------------------
  41. VOID
  42. RtlpLpcDerefContext(
  43. PLPCSVR_CONTEXT Context,
  44. PLPCSVR_MESSAGE Message
  45. )
  46. {
  47. PLPCSVR_SERVER Server ;
  48. Server = Context->Server ;
  49. if ( InterlockedDecrement( &Context->RefCount ) < 0 )
  50. {
  51. //
  52. // All gone, time to clean up:
  53. //
  54. RtlpLpcLockServer( Server );
  55. if ( Context->List.Flink )
  56. {
  57. RemoveEntryList( &Context->List );
  58. Server->ContextCount -- ;
  59. }
  60. else
  61. {
  62. if ( Message )
  63. {
  64. RtlFreeHeap( RtlProcessHeap(),
  65. 0,
  66. Message );
  67. }
  68. }
  69. RtlpLpcUnlockServer( Server );
  70. if ( Context->CommPort )
  71. {
  72. NtClose( Context->CommPort );
  73. }
  74. RtlFreeHeap( RtlProcessHeap(),
  75. 0,
  76. Context );
  77. }
  78. else
  79. {
  80. RtlpLpcLockServer( Server );
  81. Server->MessagePoolSize++ ;
  82. if ( Server->MessagePoolSize < Server->MessagePoolLimit )
  83. {
  84. Message->Header.Next = Server->MessagePool ;
  85. Server->MessagePool = Message ;
  86. }
  87. else
  88. {
  89. Server->MessagePoolSize-- ;
  90. RtlFreeHeap( RtlProcessHeap(),
  91. 0,
  92. Message );
  93. }
  94. RtlpLpcUnlockServer( Server );
  95. }
  96. }
  97. //+---------------------------------------------------------------------------
  98. //
  99. // Function: RtlpLpcWorkerThread
  100. //
  101. // Synopsis: General worker thread
  102. //
  103. // Arguments: [Parameter] --
  104. //
  105. // History: 2-06-98 RichardW Created
  106. //
  107. // Notes:
  108. //
  109. //----------------------------------------------------------------------------
  110. VOID
  111. RtlpLpcWorkerThread(
  112. PVOID Parameter
  113. )
  114. {
  115. PLPCSVR_MESSAGE Message ;
  116. PLPCSVR_CONTEXT Context ;
  117. NTSTATUS Status ;
  118. BOOLEAN Accept ;
  119. Message = (PLPCSVR_MESSAGE) Parameter ;
  120. Context = Message->Header.Context ;
  121. switch ( Message->Message.u2.s2.Type & 0xF )
  122. {
  123. case LPC_REQUEST:
  124. case LPC_DATAGRAM:
  125. DbgPrint("Calling Server's Request function\n");
  126. Status = Context->Server->Init.RequestFn(
  127. &Context->PrivateContext,
  128. &Message->Message,
  129. &Message->Message
  130. );
  131. if ( NT_SUCCESS( Status ) )
  132. {
  133. Status = NtReplyPort( Context->CommPort,
  134. &Message->Message );
  135. if ( !NT_SUCCESS( Status ) )
  136. {
  137. //
  138. // See what happened. The client may have gone away already.
  139. //
  140. break;
  141. }
  142. }
  143. break;
  144. case LPC_CONNECTION_REQUEST:
  145. DbgPrint("Calling Server's Connect function\n");
  146. Status = Context->Server->Init.ConnectFn(
  147. &Context->PrivateContext,
  148. &Message->Message,
  149. &Accept
  150. );
  151. //
  152. // If the comm port is still null, then do the accept. Otherwise, the
  153. // server called RtlAcceptConnectPort() explicitly, to set up a view.
  154. //
  155. if ( NT_SUCCESS( Status ) )
  156. {
  157. if ( Context->CommPort == NULL )
  158. {
  159. Status = NtAcceptConnectPort(
  160. &Context->CommPort,
  161. Context,
  162. &Message->Message,
  163. Accept,
  164. NULL,
  165. NULL );
  166. if ( !Accept )
  167. {
  168. //
  169. // Yank the context out of the list, since it is worthless
  170. //
  171. Context->RefCount = 0 ;
  172. }
  173. else
  174. {
  175. Status = NtCompleteConnectPort( Context->CommPort );
  176. }
  177. }
  178. }
  179. else
  180. {
  181. Status = NtAcceptConnectPort(
  182. &Context->CommPort,
  183. NULL,
  184. &Message->Message,
  185. FALSE,
  186. NULL,
  187. NULL );
  188. Context->RefCount = 0 ;
  189. }
  190. break;
  191. case LPC_CLIENT_DIED:
  192. DbgPrint( "Calling Server's Rundown function\n" );
  193. Status = Context->Server->Init.RundownFn(
  194. &Context->PrivateContext,
  195. &Message->Message
  196. );
  197. InterlockedDecrement( &Context->RefCount );
  198. break;
  199. default:
  200. //
  201. // An unexpected message came through. Normal LPC servers
  202. // don't handle the other types of messages. Drop it.
  203. //
  204. break;
  205. }
  206. RtlpLpcDerefContext( Context, Message );
  207. return ;
  208. }
  209. VOID
  210. RtlpLpcServerCallback(
  211. PVOID Parameter,
  212. BOOLEAN TimedOut
  213. )
  214. {
  215. PLPCSVR_SERVER Server ;
  216. NTSTATUS Status ;
  217. PLPCSVR_MESSAGE Message ;
  218. PLPCSVR_CONTEXT Context ;
  219. PLARGE_INTEGER RealTimeout ;
  220. LPCSVR_FILTER_RESULT FilterResult ;
  221. Server = (PLPCSVR_SERVER) Parameter ;
  222. if ( Server->WaitHandle )
  223. {
  224. Server->WaitHandle = NULL ;
  225. }
  226. while ( 1 )
  227. {
  228. DbgPrint("Entering LPC server\n" );
  229. RtlpLpcLockServer( Server );
  230. if ( Server->Flags & LPCSVR_SHUTDOWN_PENDING )
  231. {
  232. break;
  233. }
  234. if ( Server->MessagePool )
  235. {
  236. Message = Server->MessagePool ;
  237. Server->MessagePool = Message->Header.Next ;
  238. }
  239. else
  240. {
  241. Message = RtlAllocateHeap( RtlProcessHeap(),
  242. 0,
  243. Server->MessageSize );
  244. }
  245. RtlpLpcUnlockServer( Server );
  246. if ( !Message )
  247. {
  248. LARGE_INTEGER SleepInterval ;
  249. SleepInterval.QuadPart = 125 * 10000 ;
  250. NtDelayExecution( FALSE, &SleepInterval );
  251. continue;
  252. }
  253. if ( Server->Timeout.QuadPart )
  254. {
  255. RealTimeout = &Server->Timeout ;
  256. }
  257. else
  258. {
  259. RealTimeout = NULL ;
  260. }
  261. Status = NtReplyWaitReceivePortEx(
  262. Server->Port,
  263. &Context,
  264. NULL,
  265. &Message->Message,
  266. RealTimeout );
  267. DbgPrint("Server: NtReplyWaitReceivePort completed with %x\n", Status );
  268. if ( NT_SUCCESS( Status ) )
  269. {
  270. //
  271. // If we timed out, nobody was waiting for us:
  272. //
  273. if ( Status == STATUS_TIMEOUT )
  274. {
  275. //
  276. // Set up a general wait that will call back to this function
  277. // when ready.
  278. //
  279. RtlpLpcLockServer( Server );
  280. if ( ( Server->Flags & LPCSVR_SHUTDOWN_PENDING ) == 0 )
  281. {
  282. Status = RtlRegisterWait( &Server->WaitHandle,
  283. Server->Port,
  284. RtlpLpcServerCallback,
  285. Server,
  286. 0xFFFFFFFF,
  287. WT_EXECUTEONLYONCE );
  288. }
  289. RtlpLpcUnlockServer( Server );
  290. break;
  291. }
  292. if ( Status == STATUS_SUCCESS )
  293. {
  294. if ( Context )
  295. {
  296. InterlockedIncrement( &Context->RefCount );
  297. }
  298. else
  299. {
  300. //
  301. // New connection. Create a new context record
  302. //
  303. Context = RtlAllocateHeap( RtlProcessHeap(),
  304. 0,
  305. sizeof( LPCSVR_CONTEXT ) +
  306. Server->Init.ContextSize );
  307. if ( !Context )
  308. {
  309. HANDLE Bogus ;
  310. Status = NtAcceptConnectPort(
  311. &Bogus,
  312. NULL,
  313. &Message->Message,
  314. FALSE,
  315. NULL,
  316. NULL );
  317. RtlpLpcLockServer( Server );
  318. Message->Header.Next = Server->MessagePool ;
  319. Server->MessagePool = Message ;
  320. RtlpLpcUnlockServer( Server );
  321. continue;
  322. }
  323. Context->Server = Server ;
  324. Context->RefCount = 1 ;
  325. Context->CommPort = NULL ;
  326. RtlpLpcLockServer( Server );
  327. InsertTailList( &Server->ContextList, &Context->List );
  328. Server->ContextCount++ ;
  329. RtlpLpcUnlockServer( Server );
  330. }
  331. Message->Header.Context = Context ;
  332. FilterResult = LpcFilterAsync ;
  333. if ( Server->Init.FilterFn )
  334. {
  335. FilterResult = Server->Init.FilterFn( Context, &Message->Message );
  336. if (FilterResult == LpcFilterDrop )
  337. {
  338. RtlpLpcDerefContext( Context, Message );
  339. continue;
  340. }
  341. }
  342. if ( (Server->Flags & LPCSVR_SYNCHRONOUS) ||
  343. (FilterResult == LpcFilterSync) )
  344. {
  345. RtlpLpcWorkerThread( Message );
  346. }
  347. else
  348. {
  349. RtlQueueWorkItem( RtlpLpcWorkerThread,
  350. Message,
  351. 0 );
  352. }
  353. }
  354. }
  355. else
  356. {
  357. //
  358. // Error? Better shut down...
  359. //
  360. break;
  361. }
  362. }
  363. }
  364. NTSTATUS
  365. RtlCreateLpcServer(
  366. POBJECT_ATTRIBUTES PortName,
  367. PLPCSVR_INITIALIZE Init,
  368. PLARGE_INTEGER IdleTimeout,
  369. ULONG MessageSize,
  370. ULONG Options,
  371. PVOID * LpcServer
  372. )
  373. {
  374. PLPCSVR_SERVER Server ;
  375. NTSTATUS Status ;
  376. HANDLE Thread ;
  377. CLIENT_ID Id ;
  378. *LpcServer = NULL ;
  379. Server = RtlAllocateHeap( RtlProcessHeap(),
  380. 0,
  381. sizeof( LPCSVR_SERVER ) );
  382. if ( !Server ) {
  383. return STATUS_INSUFFICIENT_RESOURCES;
  384. }
  385. Status = RtlInitializeCriticalSectionAndSpinCount (&Server->Lock,
  386. 1000);
  387. if (!NT_SUCCESS (Status)) {
  388. RtlFreeHeap( RtlProcessHeap(), 0, Server );
  389. return Status;
  390. }
  391. InitializeListHead( &Server->ContextList );
  392. Server->ContextCount = 0;
  393. Server->Init = *Init;
  394. if ( !IdleTimeout ) {
  395. Server->Timeout.QuadPart = 0;
  396. } else {
  397. Server->Timeout = *IdleTimeout;
  398. }
  399. Server->MessageSize = MessageSize + sizeof( LPCSVR_MESSAGE ) -
  400. sizeof( PORT_MESSAGE );
  401. Server->MessagePool = 0;
  402. Server->MessagePoolSize = 0;
  403. Server->MessagePoolLimit = 4;
  404. Server->Flags = Options;
  405. //
  406. // Create the LPC port:
  407. //
  408. Status = NtCreateWaitablePort(
  409. &Server->Port,
  410. PortName,
  411. MessageSize,
  412. MessageSize,
  413. MessageSize * 4
  414. );
  415. if ( !NT_SUCCESS( Status ) )
  416. {
  417. RtlDeleteCriticalSection( &Server->Lock );
  418. RtlFreeHeap( RtlProcessHeap(), 0, Server );
  419. return Status;
  420. }
  421. //
  422. // Now, post the handle over to a wait queue
  423. //
  424. Status = RtlRegisterWait(
  425. &Server->WaitHandle,
  426. Server->Port,
  427. RtlpLpcServerCallback,
  428. Server,
  429. 0xFFFFFFFF,
  430. WT_EXECUTEONLYONCE
  431. );
  432. if (!NT_SUCCESS (Status)) {
  433. NtClose (Server->Port);
  434. RtlDeleteCriticalSection( &Server->Lock );
  435. RtlFreeHeap( RtlProcessHeap(), 0, Server );
  436. return Status;
  437. }
  438. *LpcServer = Server;
  439. return Status;
  440. }
  441. NTSTATUS
  442. RtlShutdownLpcServer(
  443. PVOID LpcServer
  444. )
  445. {
  446. PLPCSVR_SERVER Server ;
  447. OBJECT_ATTRIBUTES ObjA ;
  448. PLIST_ENTRY Scan ;
  449. PLPCSVR_CONTEXT Context ;
  450. PLPCSVR_MESSAGE Message ;
  451. NTSTATUS Status ;
  452. Server = (PLPCSVR_SERVER) LpcServer ;
  453. RtlpLpcLockServer( Server );
  454. if ( Server->Flags & LPCSVR_SHUTDOWN_PENDING )
  455. {
  456. RtlpLpcUnlockServer( Server );
  457. return STATUS_PENDING ;
  458. }
  459. if ( Server->WaitHandle )
  460. {
  461. RtlDeregisterWait( Server->WaitHandle );
  462. Server->WaitHandle = NULL ;
  463. }
  464. if ( Server->Timeout.QuadPart == 0 )
  465. {
  466. RtlpLpcUnlockServer( Server );
  467. return STATUS_NOT_IMPLEMENTED ;
  468. }
  469. //
  470. // If there are receives still pending, we have to sync
  471. // with those threads. To do so, we will tag the shutdown
  472. // flag, and then wait the timeout amount.
  473. //
  474. if ( Server->ReceiveThreads != 0 )
  475. {
  476. InitializeObjectAttributes( &ObjA,
  477. NULL,
  478. 0,
  479. 0,
  480. 0 );
  481. Status = NtCreateEvent( &Server->ShutdownEvent,
  482. EVENT_ALL_ACCESS,
  483. &ObjA,
  484. NotificationEvent,
  485. FALSE );
  486. if ( !NT_SUCCESS( Status ) )
  487. {
  488. RtlpLpcUnlockServer( Server );
  489. return Status ;
  490. }
  491. Server->Flags |= LPCSVR_SHUTDOWN_PENDING ;
  492. RtlpLpcUnlockServer( Server );
  493. Status = NtWaitForSingleObject(
  494. Server->ShutdownEvent,
  495. FALSE,
  496. &Server->Timeout );
  497. if ( Status == STATUS_TIMEOUT )
  498. {
  499. //
  500. // Hmm, the LPC server thread is hung somewhere,
  501. // press on
  502. //
  503. }
  504. RtlpLpcLockServer( Server );
  505. NtClose( Server->ShutdownEvent );
  506. Server->ShutdownEvent = NULL ;
  507. }
  508. else
  509. {
  510. Server->Flags |= LPCSVR_SHUTDOWN_PENDING ;
  511. }
  512. //
  513. // The server object is locked, and there are no receives
  514. // pending. Or, the receives appear hung. Skim through the
  515. // context list, calling the server code. The disconnect
  516. // message is NULL, indicating that this is a server initiated
  517. // shutdown.
  518. //
  519. while ( ! IsListEmpty( &Server->ContextList ) )
  520. {
  521. Scan = RemoveHeadList( &Server->ContextList );
  522. Context = CONTAINING_RECORD( Scan, LPCSVR_CONTEXT, List );
  523. Status = Server->Init.RundownFn(
  524. Context->PrivateContext,
  525. NULL );
  526. Context->List.Flink = NULL ;
  527. RtlpLpcDerefContext( Context, NULL );
  528. }
  529. //
  530. // All contexts have been deleted: clean up the messages
  531. //
  532. while ( Server->MessagePool )
  533. {
  534. Message = Server->MessagePool ;
  535. Server->MessagePool = Message ;
  536. RtlFreeHeap( RtlProcessHeap(),
  537. 0,
  538. Message );
  539. }
  540. //
  541. // Clean up server objects
  542. //
  543. return(STATUS_SUCCESS);
  544. }
  545. NTSTATUS
  546. RtlImpersonateLpcClient(
  547. PVOID Context,
  548. PPORT_MESSAGE Message
  549. )
  550. {
  551. PLPCSVR_CONTEXT LpcContext ;
  552. LpcContext = RtlpLpcContextFromClient( Context );
  553. return NtImpersonateClientOfPort(
  554. LpcContext->CommPort,
  555. Message );
  556. }
  557. NTSTATUS
  558. RtlCallbackLpcClient(
  559. PVOID Context,
  560. PPORT_MESSAGE Request,
  561. PPORT_MESSAGE Callback
  562. )
  563. {
  564. NTSTATUS Status ;
  565. PLPCSVR_CONTEXT LpcContext ;
  566. if ( Request != Callback )
  567. {
  568. Callback->ClientId = Request->ClientId ;
  569. Callback->MessageId = Request->MessageId ;
  570. }
  571. LpcContext = RtlpLpcContextFromClient( Context );
  572. Status = NtRequestWaitReplyPort(
  573. LpcContext->CommPort,
  574. Callback,
  575. Callback
  576. );
  577. return Status ;
  578. }