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.

1392 lines
37 KiB

  1. //============================================================================
  2. // Copyright (c) 1995, Microsoft Corporation
  3. //
  4. // File: init.c
  5. //
  6. // History:
  7. // Abolade Gbadegesin July-24-1995 Created
  8. //
  9. // Server routines for tracing dll.
  10. // All functions invoked by the server thread are code-page independent.
  11. //============================================================================
  12. #include <nt.h>
  13. #include <ntrtl.h>
  14. #include <nturtl.h>
  15. #include <windows.h>
  16. #include <rtutils.h>
  17. #include "trace.h"
  18. //#define STRSAFE_LIB
  19. #include <strsafe.h>
  20. // waits on
  21. // lpserver->hConsole
  22. // lpserver->hStopEvent
  23. // lpserver->hTableEvent
  24. // lpclient->hConfigEvent for each client
  25. #define POS_CONSOLE 0
  26. #define POS_STOP 1
  27. #define POS_TABLE 2
  28. #define POS_CLIENT_0 3
  29. #define POS_MAX MAXIMUM_WAIT_OBJECTS
  30. #define ADJUST_ARRAY(a) ((a) + posBase)
  31. #define ADJUST_INDEX(i) ((i) - posBase)
  32. #define OFFSET_CLIENT(i,d) (((i) + MAX_CLIENT_COUNT + d) % MAX_CLIENT_COUNT)
  33. extern VOID StopWorkers (VOID);
  34. LPTRACE_SERVER g_server = NULL;
  35. HINSTANCE g_module;
  36. HANDLE g_loadevent = NULL;
  37. HMODULE g_moduleRef;
  38. HANDLE g_serverThread;
  39. ULONG g_traceCount; //attempt server thread creation ?
  40. ULONG g_traceTime; //when last attempt to create server thread.
  41. DWORD g_posBase, g_posLast;//not used by serverthread.
  42. // only to decide if new serverthread to be created
  43. HANDLE g_hWaitHandles[POS_MAX];
  44. PTCHAR g_FormatBuffer;
  45. PTCHAR g_PrintBuffer;
  46. HINSTANCE
  47. IncrementModuleReference (
  48. VOID
  49. );
  50. BOOL WINAPI DLLMAIN(HINSTANCE hInstDLL, DWORD dwReason, LPVOID lpvReserved) {
  51. BOOL bSuccess;
  52. HANDLE c_loadevent;
  53. switch (dwReason) {
  54. case DLL_PROCESS_ATTACH:
  55. DisableThreadLibraryCalls(hInstDLL);
  56. g_module = hInstDLL;
  57. // If a server threade managed to start before we got
  58. // DLL_PROCESS_ATTACH call (possible because of NT Loader
  59. // bug), we need to release it
  60. c_loadevent = (HANDLE)InterlockedExchangePointer (
  61. &g_loadevent,
  62. INVALID_HANDLE_VALUE);
  63. if (c_loadevent!=NULL) {
  64. bSuccess = SetEvent (c_loadevent);
  65. ASSERTMSG ("Could not signal waiting trace servers ", bSuccess);
  66. }
  67. else
  68. bSuccess = TRUE;
  69. break;
  70. case DLL_PROCESS_DETACH:
  71. if (g_server) {
  72. bSuccess = TraceShutdownServer(g_server);
  73. g_server = NULL;
  74. }
  75. else
  76. bSuccess = TRUE;
  77. StopWorkers ();
  78. break;
  79. default:
  80. bSuccess = TRUE;
  81. }
  82. return bSuccess;
  83. }
  84. HINSTANCE
  85. IncrementModuleReference (
  86. VOID
  87. ) {
  88. HMODULE hmodule;
  89. TCHAR szmodule[MAX_PATH+1];
  90. HANDLE l_loadevent;
  91. HANDLE c_loadevent;
  92. DWORD rc;
  93. // Create an event in case we need to wait for DLL_PROCESS_ATTACH
  94. l_loadevent = CreateEvent (NULL, TRUE, FALSE, NULL);
  95. ASSERTMSG ("Could not create load event ", l_loadevent!=NULL);
  96. if (l_loadevent!=NULL) {
  97. // Make our event global if either no-one else
  98. // has done this yet
  99. c_loadevent = (HANDLE)InterlockedCompareExchangePointer (
  100. (PVOID *)&g_loadevent,
  101. l_loadevent,
  102. NULL);
  103. if (c_loadevent==NULL) {
  104. rc = WaitForSingleObject (l_loadevent, INFINITE);
  105. // Let other waiting threads run as we going to close
  106. // our event right after this
  107. Sleep (0);
  108. }
  109. else if (c_loadevent==INVALID_HANDLE_VALUE) {
  110. // DLL_PROCESS_ATTACH has already been called
  111. rc = WAIT_OBJECT_0;
  112. }
  113. else {
  114. // Somebody else managed to start before us
  115. // -> wait on that event
  116. #if DBG
  117. DbgPrint ("RTUTILS: %lx - trace server thread waiting for load on existing event.\n",
  118. GetCurrentThreadId ());
  119. #endif
  120. rc = WaitForSingleObject (c_loadevent, INFINITE);
  121. // Just in case the handle has been closed before we
  122. // managed to start the wait (unlikely because
  123. // of Sleep call above, but just in case ...)
  124. if ((rc!=WAIT_OBJECT_0)
  125. && (GetLastError ()==ERROR_INVALID_HANDLE)) {
  126. #if DBG
  127. DbgPrint ("RTUTILS: %lx - trace server thread load event was destroyed during wait.\n",
  128. GetCurrentThreadId ());
  129. #endif
  130. rc = WAIT_OBJECT_0;
  131. }
  132. }
  133. ASSERTMSG ("Wait on load event failed ", rc==WAIT_OBJECT_0);
  134. CloseHandle (l_loadevent);
  135. if (rc==WAIT_OBJECT_0) {
  136. //
  137. // we do a LoadLibrary to increment the reference count
  138. // on RTUTILS.DLL, so that when we're unloaded by the application,
  139. // our address space doesn't disappear.
  140. // instead, our event will be signalled and then we cleanup
  141. // and call FreeLibraryAndExitThread to unload ourselves
  142. //
  143. rc = GetModuleFileName(g_module, szmodule, sizeof(szmodule)/sizeof (szmodule[0]));
  144. ASSERTMSG ("Could not get dll path ", rc>0);
  145. if (rc>0) {
  146. // make sure the filename is null terminated.
  147. if (rc==sizeof(szmodule)/sizeof (szmodule[0]))
  148. return NULL;
  149. hmodule = LoadLibrary(szmodule);
  150. if (hmodule!=NULL)
  151. return hmodule;
  152. }
  153. }
  154. }
  155. return NULL;
  156. }
  157. //
  158. // sets up server struct in readiness for clients registering
  159. //
  160. LPTRACE_SERVER TraceCreateServer (
  161. LPTRACE_SERVER *lpserver
  162. ) {
  163. LPTRACE_SERVER l_lpserver, c_lpserver;
  164. DWORD rc;
  165. l_lpserver = (LPTRACE_SERVER)GlobalAlloc (GPTR, sizeof (TRACE_SERVER));
  166. if (l_lpserver!=NULL) {
  167. l_lpserver->TS_Flags = 0;
  168. l_lpserver->TS_Console = NULL;
  169. l_lpserver->TS_ConsoleCreated = 0;
  170. l_lpserver->TS_StopEvent = NULL;
  171. l_lpserver->TS_TableEvent = NULL;
  172. l_lpserver->TS_ClientCount = 0;
  173. l_lpserver->TS_ConsoleOwner = MAX_CLIENT_COUNT;
  174. InitializeListHead(&l_lpserver->TS_ClientEventsToClose);
  175. ZeroMemory(l_lpserver->TS_FlagsCache, MAX_CLIENT_COUNT * sizeof(DWORD));
  176. ZeroMemory(
  177. l_lpserver->TS_ClientTable, MAX_CLIENT_COUNT * sizeof(LPTRACE_CLIENT)
  178. );
  179. rc = TRACE_STARTUP_LOCKING(l_lpserver);
  180. ASSERTMSG ("Cound not initialize lock ", rc==NO_ERROR);
  181. if (rc==NO_ERROR) {
  182. c_lpserver = InterlockedCompareExchangePointer (
  183. (PVOID *)lpserver,
  184. l_lpserver,
  185. NULL
  186. );
  187. if (c_lpserver==NULL) {
  188. return l_lpserver;
  189. }
  190. else {
  191. TRACE_CLEANUP_LOCKING (l_lpserver);
  192. GlobalFree (l_lpserver);
  193. return c_lpserver;
  194. }
  195. }
  196. else {
  197. //
  198. // error. put null back into the global variable
  199. //
  200. InterlockedCompareExchangePointer (
  201. (PVOID *)lpserver,
  202. NULL,
  203. l_lpserver
  204. );
  205. GlobalFree (l_lpserver);
  206. }
  207. }
  208. #if DBG
  209. DbgPrint ("RTUTILS: %lx - trace server creation failed.\n",
  210. GetCurrentThreadId ());
  211. #endif
  212. return NULL;
  213. }
  214. //
  215. // cleans server struct and de-allocates memory used
  216. //
  217. BOOL
  218. TraceShutdownServer(
  219. LPTRACE_SERVER lpserver
  220. ) {
  221. if (lpserver->TS_StopEvent != NULL &&
  222. (g_serverThread)) {
  223. //
  224. // server thread is active, let it do cleanup
  225. //
  226. SetEvent(lpserver->TS_StopEvent);
  227. }
  228. else {
  229. //
  230. // we'll do the cleanup ourselves
  231. //
  232. TraceCleanUpServer(lpserver);
  233. }
  234. return TRUE;
  235. }
  236. DWORD
  237. TraceCleanUpServer(
  238. LPTRACE_SERVER lpserver
  239. ) {
  240. LPTRACE_CLIENT lpclient, *lplpc, *lplpcstart, *lplpcend;
  241. // TRACE_ACQUIRE_WRITELOCK(lpserver);
  242. TRACE_CLEANUP_LOCKING(lpserver);
  243. //
  244. // delete client structures
  245. //
  246. lplpcstart = lpserver->TS_ClientTable;
  247. lplpcend = lplpcstart + MAX_CLIENT_COUNT;
  248. for (lplpc = lplpcstart; lplpc < lplpcend; lplpc++) {
  249. if (*lplpc != NULL) {
  250. TraceDeleteClient(lpserver, lplpc);
  251. }
  252. }
  253. lpserver->TS_ConsoleOwner = MAX_CLIENT_COUNT;
  254. lpserver->TS_ClientCount = 0;
  255. if (lpserver->TS_TableEvent != NULL) {
  256. CloseHandle(lpserver->TS_TableEvent);
  257. lpserver->TS_TableEvent = NULL;
  258. }
  259. if (lpserver->TS_StopEvent != NULL) {
  260. CloseHandle(lpserver->TS_StopEvent);
  261. lpserver->TS_StopEvent = NULL;
  262. }
  263. if (lpserver->TS_Console!=NULL && lpserver->TS_Console!=INVALID_HANDLE_VALUE) {
  264. CloseHandle(lpserver->TS_Console);
  265. lpserver->TS_Console = NULL;
  266. }
  267. if (lpserver->TS_ConsoleCreated == TRUE)
  268. {
  269. FreeConsole();
  270. lpserver->TS_ConsoleCreated = FALSE;
  271. }
  272. lpserver->TS_Flags = 0;
  273. //
  274. // close any handles waiting to be closed
  275. //
  276. {
  277. PLIST_ENTRY pHead, ple;
  278. pHead = &lpserver->TS_ClientEventsToClose;
  279. for (ple=pHead->Flink; ple!=pHead; )
  280. {
  281. HANDLE hEvent = *(HANDLE*)(ple+1);
  282. PLIST_ENTRY pleOld = ple;
  283. ple = ple->Flink;
  284. RemoveEntryList(pleOld);
  285. CloseHandle(hEvent);
  286. HeapFree(GetProcessHeap(), 0, pleOld);
  287. }
  288. }
  289. //
  290. // free buffers if allocated
  291. //
  292. if (g_FormatBuffer)
  293. HeapFree(GetProcessHeap(), 0, g_FormatBuffer);
  294. if (g_PrintBuffer)
  295. HeapFree(GetProcessHeap(), 0, g_PrintBuffer);
  296. return TRUE;
  297. }
  298. //
  299. // assumes server is locked for writing
  300. //
  301. DWORD
  302. TraceCreateServerComplete(
  303. LPTRACE_SERVER lpserver
  304. ) {
  305. HKEY hkeyConfig;
  306. DWORD dwType, dwSize, dwValue;
  307. DWORD dwErr, dwThread, dwDisposition;
  308. do { // breakout loop
  309. //
  310. // create event signalled to stop server thread
  311. //
  312. lpserver->TS_StopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  313. if (lpserver->TS_StopEvent == NULL) {
  314. dwErr = GetLastError(); break;
  315. }
  316. //
  317. // create event signalled when client registers/deregisters
  318. //
  319. lpserver->TS_TableEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  320. if (lpserver->TS_TableEvent == NULL) {
  321. dwErr = GetLastError(); break;
  322. }
  323. //
  324. // open registry key containing server configuration
  325. //
  326. dwErr = RegCreateKeyEx(
  327. HKEY_LOCAL_MACHINE, REGKEY_TRACING, 0, NULL,
  328. REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
  329. &hkeyConfig, &dwDisposition
  330. );
  331. if (dwErr != ERROR_SUCCESS) { break; }
  332. //
  333. // read the server configuration from the config key
  334. //
  335. dwSize = sizeof(DWORD);
  336. dwErr = RegQueryValueEx(
  337. hkeyConfig, REGVAL_ENABLECONSOLETRACING, NULL,
  338. &dwType, (PBYTE)&dwValue, &dwSize
  339. );
  340. if (dwErr != ERROR_SUCCESS || dwType != REG_DWORD) {
  341. dwType = REG_DWORD;
  342. dwSize = sizeof(DWORD);
  343. dwValue = DEF_ENABLECONSOLETRACING;
  344. dwErr = RegSetValueEx(
  345. hkeyConfig, REGVAL_ENABLECONSOLETRACING, 0,
  346. dwType, (PBYTE)&dwValue, dwSize
  347. );
  348. if (dwErr != ERROR_SUCCESS)
  349. break;
  350. }
  351. if (dwValue != 0) { lpserver->TS_Flags |= TRACEFLAGS_USECONSOLE; }
  352. RegCloseKey(hkeyConfig); hkeyConfig = 0;
  353. //
  354. // set up array for client change notifications.
  355. // only used if server thread is not created
  356. //
  357. SetWaitArray(lpserver);
  358. return NO_ERROR;
  359. } while(FALSE);
  360. //
  361. // something went wrong, so clean up
  362. //
  363. if (lpserver->TS_TableEvent != NULL) {
  364. CloseHandle(lpserver->TS_TableEvent);
  365. lpserver->TS_TableEvent = NULL;
  366. }
  367. if (lpserver->TS_StopEvent != NULL) {
  368. CloseHandle(lpserver->TS_StopEvent);
  369. lpserver->TS_StopEvent = NULL;
  370. }
  371. return dwErr;
  372. }
  373. //
  374. // creates server thread if required
  375. //
  376. DWORD
  377. TraceCreateServerThread (
  378. DWORD dwFlags,
  379. BOOL bCallerLocked, //does caller have write lock
  380. BOOL bNewRegister //new client registered. so check
  381. )
  382. {
  383. DWORD dwErr=NO_ERROR;
  384. DWORD dwCurrentTime = GetTickCount();
  385. BOOL bCreate, bLocked=bCallerLocked;
  386. LPTRACE_SERVER lpserver;
  387. DWORD dwThread=0;
  388. lpserver = GET_TRACE_SERVER();
  389. if (!lpserver)
  390. return INVALID_TRACEID;
  391. //
  392. // check if serverthread should be created
  393. //
  394. bCreate = FALSE;
  395. do {
  396. if ((dwFlags & TRACE_USE_FILE) || (dwFlags & TRACE_USE_CONSOLE)) {
  397. bCreate = TRUE;
  398. break;
  399. }
  400. if (g_traceTime > dwCurrentTime)
  401. g_traceTime = dwCurrentTime;
  402. if (!bNewRegister) {
  403. if (dwCurrentTime - g_traceTime < 30000)
  404. break;
  405. }
  406. if (!bLocked){
  407. TRACE_ACQUIRE_WRITELOCK(lpserver);
  408. bLocked = TRUE;
  409. }
  410. // check again under lock if server thread has been created
  411. if (g_serverThread) {
  412. bCreate = FALSE;
  413. break;
  414. }
  415. //
  416. // enter the wait, passing the adjusted handle count
  417. // and the adjusted array base
  418. //
  419. {
  420. DWORD dwRetval;
  421. if (!bNewRegister) {
  422. // g_posLast points to the next empty event entry
  423. //
  424. // traceDeregister takes care to keep this list valid
  425. //
  426. dwRetval = WaitForMultipleObjects(
  427. g_posLast - g_posBase, g_hWaitHandles + g_posBase, FALSE, 0
  428. );
  429. g_traceTime = dwCurrentTime;
  430. if (dwRetval==WAIT_TIMEOUT)
  431. break;
  432. }
  433. }
  434. {
  435. LPTRACE_CLIENT *lplpc, *lplpcstart, *lplpcend;
  436. g_traceTime = dwCurrentTime;
  437. lplpcstart = lpserver->TS_ClientTable;
  438. lplpcend = lpserver->TS_ClientTable + MAX_CLIENT_COUNT;
  439. g_posLast = POS_CLIENT_0;
  440. for (lplpc = lplpcstart; lplpc < lplpcend; lplpc++) {
  441. if (*lplpc == NULL)
  442. continue;
  443. if (!TRACE_CLIENT_IS_DISABLED(*lplpc))
  444. TraceEnableClient(lpserver, *lplpc, FALSE);
  445. if (TRACE_CLIENT_USES_CONSOLE(*lplpc)
  446. || TRACE_CLIENT_USES_FILE(*lplpc))
  447. {
  448. bCreate = TRUE;
  449. break;
  450. }
  451. if (TRACE_CLIENT_USES_REGISTRY(*lplpc))
  452. g_hWaitHandles[g_posLast++] = (*lplpc)->TC_ConfigEvent;
  453. }
  454. }
  455. } while (FALSE);
  456. if (!bCreate) {
  457. if (bLocked && !bCallerLocked)
  458. TRACE_RELEASE_WRITELOCK(lpserver);
  459. return dwErr;
  460. }
  461. if (!bLocked) {
  462. TRACE_ACQUIRE_WRITELOCK(lpserver);
  463. bLocked = TRUE;
  464. }
  465. do {
  466. // final check under lock to see if thread created
  467. if (* ((ULONG_PTR volatile *)&g_serverThread) )
  468. break;
  469. g_moduleRef = IncrementModuleReference ();
  470. if (g_moduleRef==NULL) {
  471. dwErr = ERROR_CAN_NOT_COMPLETE;
  472. break;
  473. }
  474. g_serverThread = CreateThread(
  475. NULL, 0, TraceServerThread, lpserver, 0, &dwThread
  476. );
  477. if (g_serverThread == NULL) {
  478. dwErr = GetLastError();
  479. FreeLibrary(g_moduleRef);
  480. break;
  481. }
  482. CloseHandle(g_serverThread);
  483. } while (FALSE);
  484. if (bLocked && !bCallerLocked)
  485. TRACE_RELEASE_WRITELOCK(lpserver);
  486. return dwErr;
  487. }
  488. //----------------------------------------------------------------------------
  489. // Function: TraceServerThread
  490. //
  491. // Parameters:
  492. // LPVOID lpvParam
  493. //
  494. //----------------------------------------------------------------------------
  495. DWORD
  496. TraceServerThread(
  497. LPVOID lpvParam
  498. ) {
  499. DWORD dwErr;
  500. DWORD posBase, posLast;
  501. LPTRACE_SERVER lpserver;
  502. DWORD aWaitIndices[POS_MAX];
  503. HANDLE hWaitHandles[POS_MAX];
  504. LPTRACE_CLIENT lpclient, *lplpc, *lplpcstart, *lplpcend;
  505. //
  506. // get the server who owns this thread
  507. //
  508. lpserver = (LPTRACE_SERVER)lpvParam;
  509. //
  510. // set the flag to indicate we're running
  511. //
  512. InterlockedExchange(
  513. &lpserver->TS_Flags, lpserver->TS_Flags | TRACEFLAGS_SERVERTHREAD
  514. );
  515. //
  516. // allocate temp tracing buffers
  517. //
  518. {
  519. PTCHAR Tmp;
  520. Tmp = (PTCHAR) HeapAlloc(GetProcessHeap(), 0, DEF_PRINT_BUFSIZE);
  521. if (Tmp)
  522. InterlockedExchangePointer(&g_FormatBuffer, Tmp);
  523. Tmp = (PTCHAR) HeapAlloc(GetProcessHeap(), 0, DEF_PRINT_BUFSIZE);
  524. if (Tmp)
  525. {
  526. InterlockedExchangePointer(&g_PrintBuffer, Tmp);
  527. }
  528. }
  529. posBase = posLast = 0;
  530. lplpcstart = lpserver->TS_ClientTable;
  531. lplpcend = lpserver->TS_ClientTable + MAX_CLIENT_COUNT;
  532. //
  533. // make sure the latest config is loaded
  534. //
  535. TRACE_ACQUIRE_WRITELOCK(lpserver);
  536. for (lplpc = lplpcstart; lplpc < lplpcend; lplpc++) {
  537. if (*lplpc != NULL && !TRACE_CLIENT_IS_DISABLED(*lplpc))
  538. TraceEnableClient(lpserver, *lplpc, FALSE);
  539. }
  540. TRACE_RELEASE_WRITELOCK(lpserver);
  541. while (TRUE) {
  542. //
  543. // to figure out which handles will be waited on
  544. // first lock the server for reading
  545. //
  546. TRACE_ACQUIRE_READLOCK(lpserver);
  547. //
  548. // if a thread is using the console, wait on console input handle
  549. // otherwise, the base of the array of handles waited on
  550. // is adjusted upward (by setting posBase to 1); then, when the
  551. // wait returns the index of the signalled handle, the index is
  552. // compared against the POS_ constants adjusted downward
  553. // (by subtracting posBase from them);
  554. // thus if posBase is 1, we pass &hWaitHandles[1] and if we get
  555. // back 2, we compared it to (POS_CLIENT_0 - 1)==2 and then we
  556. // access position (2 - (POS_CLIENT_0 - 1))==0 in the actual
  557. // client table
  558. //
  559. if (lpserver->TS_Console != NULL
  560. && lpserver->TS_Console!=INVALID_HANDLE_VALUE)
  561. {
  562. posBase = 0;
  563. hWaitHandles[POS_CONSOLE] = lpserver->TS_Console;
  564. }
  565. else {
  566. posBase = 1;
  567. hWaitHandles[POS_CONSOLE] = NULL;
  568. }
  569. hWaitHandles[POS_STOP] = lpserver->TS_StopEvent;
  570. hWaitHandles[POS_TABLE] = lpserver->TS_TableEvent;
  571. posLast = POS_CLIENT_0;
  572. for (lplpc = lplpcstart; lplpc < lplpcend; lplpc++) {
  573. if (*lplpc != NULL && TRACE_CLIENT_USES_REGISTRY(*lplpc)) {
  574. aWaitIndices[posLast] = (ULONG) (lplpc - lplpcstart);
  575. hWaitHandles[posLast++] = (*lplpc)->TC_ConfigEvent;
  576. }
  577. }
  578. //
  579. // close any handles waiting to be closed
  580. // readlock is fine. writeLock held when inserting in list, and
  581. // only this thread can remove from list
  582. //
  583. {
  584. PLIST_ENTRY pHead, ple;
  585. pHead = &lpserver->TS_ClientEventsToClose;
  586. for (ple=pHead->Flink; ple!=pHead; )
  587. {
  588. HANDLE hEvent = *(HANDLE*)(ple+1);
  589. PLIST_ENTRY pleOld = ple;
  590. ple = ple->Flink;
  591. RemoveEntryList(pleOld);
  592. CloseHandle(hEvent);
  593. HeapFree(GetProcessHeap(), 0, pleOld);
  594. }
  595. }
  596. TRACE_RELEASE_READLOCK(lpserver);
  597. //
  598. // enter the wait, passing the adjusted handle count
  599. // and the adjusted array base
  600. //
  601. dwErr = WaitForMultipleObjects(
  602. posLast - posBase, hWaitHandles + posBase, FALSE, INFINITE
  603. );
  604. dwErr += (DWORD)posBase;
  605. if (dwErr == (WAIT_OBJECT_0 + POS_CONSOLE)) {
  606. //
  607. // must be a key pressed in the console, so
  608. // process it
  609. //
  610. // lock server for writing
  611. //
  612. TRACE_ACQUIRE_WRITELOCK(lpserver);
  613. if (lpserver->TS_Console != NULL
  614. && lpserver->TS_Console!=INVALID_HANDLE_VALUE)
  615. {
  616. TraceProcessConsoleInput(lpserver);
  617. }
  618. TRACE_RELEASE_WRITELOCK(lpserver);
  619. }
  620. else
  621. if (dwErr == (WAIT_OBJECT_0 + POS_STOP)) {
  622. //
  623. // time to break out of the loop
  624. //
  625. break;
  626. }
  627. else
  628. if (dwErr == (WAIT_OBJECT_0 + POS_TABLE)) {
  629. DWORD dwOwner;
  630. // a client registered or deregistered;
  631. // we pick up the new reg config change event
  632. // the next time through the loop
  633. }
  634. else
  635. if (dwErr >= (WAIT_OBJECT_0 + POS_CLIENT_0) &&
  636. dwErr <= (WAIT_OBJECT_0 + posLast)) {
  637. //
  638. // config changed for a client, lock server for writing
  639. // and lock client for writing, and reload the configuration
  640. // from the registry; take care in case the client has
  641. // already deregistered
  642. //
  643. TRACE_ACQUIRE_WRITELOCK(lpserver);
  644. lplpc = lpserver->TS_ClientTable +
  645. aWaitIndices[dwErr - WAIT_OBJECT_0];
  646. if (*lplpc == NULL) {
  647. TRACE_RELEASE_WRITELOCK(lpserver);
  648. continue;
  649. }
  650. //
  651. // load the client's configuration, unless it's disabled
  652. //
  653. if (!TRACE_CLIENT_IS_DISABLED(*lplpc)) {
  654. TraceEnableClient(lpserver, *lplpc, FALSE);
  655. }
  656. TRACE_RELEASE_WRITELOCK(lpserver);
  657. }
  658. }
  659. //
  660. // we've received the stop signal, so do cleanup and quit
  661. //
  662. TraceCleanUpServer(lpserver);
  663. //
  664. // unload the library and exit; this call never returns
  665. //
  666. FreeLibraryAndExitThread(g_moduleRef, 0);
  667. return dwErr;
  668. }
  669. //----------------------------------------------------------------------------
  670. // Function: TraceProcessConsoleInput
  671. //
  672. // Parameters:
  673. // LPTRACE_SERVER *lpserver
  674. //
  675. // Invoked when user presses a key in the console
  676. // Keypresses handle are
  677. // spacebar toggle the enabled/disabled flag for the client
  678. // whose screen buffer is active
  679. // pause same as <spacebar>
  680. // ctrl-tab set the active screen buffer to that of
  681. // the next client in the table
  682. // ctrl-shift-tab set the active screen buffer to that of
  683. // the previous client in the table
  684. // up, down, left, right scrolls the console window as expected
  685. // pageup, pagedown scrolls the console window as expected
  686. // assumes the server is locked for writing
  687. //----------------------------------------------------------------------------
  688. DWORD
  689. TraceProcessConsoleInput(
  690. LPTRACE_SERVER lpserver
  691. ) {
  692. INT dir;
  693. BOOL bSuccess;
  694. HANDLE hStdin;
  695. DWORD dwCount;
  696. WORD wRepCount;
  697. INPUT_RECORD inputRec;
  698. PKEY_EVENT_RECORD pkeyRec;
  699. DWORD dwConsoleOwner, dwNewOwner;
  700. LPTRACE_CLIENT lpclient, lpowner;
  701. //
  702. // see who owns the console
  703. //
  704. dwConsoleOwner = lpserver->TS_ConsoleOwner;
  705. if (dwConsoleOwner == MAX_CLIENT_COUNT) {
  706. //
  707. // no-one owns the console, so just return
  708. //
  709. return 0;
  710. }
  711. lpclient = lpserver->TS_ClientTable[dwConsoleOwner];
  712. //
  713. // get the console input handle
  714. //
  715. hStdin = lpserver->TS_Console;
  716. if (hStdin == NULL || hStdin==INVALID_HANDLE_VALUE) {
  717. //
  718. // no console, so quit
  719. //
  720. return 0;
  721. }
  722. //
  723. // read input record
  724. //
  725. bSuccess = ReadConsoleInput(hStdin, &inputRec, 1, &dwCount);
  726. if (!bSuccess || dwCount == 0) {
  727. return GetLastError();
  728. }
  729. //
  730. // return if its not a keyboard event
  731. //
  732. if (inputRec.EventType != KEY_EVENT) {
  733. return 0;
  734. }
  735. //
  736. // if its one we handle, handle it
  737. //
  738. pkeyRec = &inputRec.Event.KeyEvent;
  739. if (!pkeyRec->bKeyDown) {
  740. //
  741. // we process when the key is pressed, not released
  742. //
  743. return 0;
  744. }
  745. wRepCount = pkeyRec->wRepeatCount;
  746. switch(pkeyRec->wVirtualKeyCode) {
  747. //
  748. // space-bar and pause are handled identically
  749. //
  750. case VK_PAUSE:
  751. case VK_SPACE:
  752. if (lpclient == NULL) { break; }
  753. //
  754. // if space bar or pause pressed an even
  755. // number of times, do nothing
  756. //
  757. if ((wRepCount & 1) == 0) { break; }
  758. //
  759. // toggle the enabled flag for the client
  760. //
  761. if (TRACE_CLIENT_IS_DISABLED(lpclient)) {
  762. TraceEnableClient(lpserver, lpclient, FALSE);
  763. }
  764. else {
  765. TraceDisableClient(lpserver, lpclient);
  766. }
  767. TraceUpdateConsoleTitle(lpclient);
  768. break;
  769. //
  770. // arrow keys are handled here
  771. //
  772. case VK_LEFT:
  773. if (lpclient == NULL) { break; }
  774. TraceShiftConsoleWindow(lpclient, -wRepCount, 0, NULL);
  775. break;
  776. case VK_RIGHT:
  777. if (lpclient == NULL) { break; }
  778. TraceShiftConsoleWindow(lpclient, wRepCount, 0, NULL);
  779. break;
  780. case VK_UP:
  781. if (lpclient == NULL) { break; }
  782. TraceShiftConsoleWindow(lpclient, 0, -wRepCount, NULL);
  783. break;
  784. case VK_DOWN:
  785. if (lpclient == NULL) { break; }
  786. TraceShiftConsoleWindow(lpclient, 0, wRepCount, NULL);
  787. break;
  788. //
  789. // page-up and page-down are handled here
  790. //
  791. case VK_PRIOR:
  792. case VK_NEXT: {
  793. INT iHeight;
  794. CONSOLE_SCREEN_BUFFER_INFO csbi;
  795. if (lpclient == NULL) { break; }
  796. //
  797. // find the current height of the window
  798. //
  799. if (GetConsoleScreenBufferInfo(lpclient->TC_Console, &csbi)==0)
  800. return 0;
  801. iHeight = csbi.srWindow.Bottom - csbi.srWindow.Top;
  802. if (pkeyRec->wVirtualKeyCode == VK_PRIOR) {
  803. TraceShiftConsoleWindow(
  804. lpclient, 0, -(wRepCount * iHeight), &csbi
  805. );
  806. }
  807. else {
  808. TraceShiftConsoleWindow(
  809. lpclient, 0, (wRepCount * iHeight), &csbi
  810. );
  811. }
  812. break;
  813. }
  814. case VK_TAB:
  815. if ((pkeyRec->dwControlKeyState & LEFT_CTRL_PRESSED) ||
  816. (pkeyRec->dwControlKeyState & RIGHT_CTRL_PRESSED)) {
  817. //
  818. // ok, we can handle it.
  819. //
  820. // see if we are to move to
  821. // the previous screen buffer or to the next screen buffer
  822. //
  823. if (pkeyRec->dwControlKeyState & SHIFT_PRESSED) {
  824. // moving to previous screen buffer
  825. //
  826. dir = -1;
  827. }
  828. else {
  829. // moving to next screen buffer
  830. //
  831. dir = 1;
  832. }
  833. //
  834. // call the function which changes the console owner
  835. //
  836. TraceUpdateConsoleOwner(lpserver, dir);
  837. }
  838. break;
  839. }
  840. return 0;
  841. }
  842. //
  843. // assumes client is locked for reading or writing
  844. //
  845. DWORD
  846. TraceShiftConsoleWindow(
  847. LPTRACE_CLIENT lpclient,
  848. INT iXShift,
  849. INT iYShift,
  850. PCONSOLE_SCREEN_BUFFER_INFO pcsbi
  851. ) {
  852. PCOORD pc;
  853. PSMALL_RECT pr;
  854. CONSOLE_SCREEN_BUFFER_INFO csbi;
  855. //
  856. // if caller did not pass in current console info,
  857. // get the info before going any further
  858. //
  859. if (pcsbi == NULL) {
  860. pcsbi = &csbi;
  861. if (GetConsoleScreenBufferInfo(lpclient->TC_Console, pcsbi)==0)
  862. return 0;
  863. }
  864. //
  865. // shift the window from its current position
  866. //
  867. pc = &pcsbi->dwSize;
  868. pr = &pcsbi->srWindow;
  869. pr->Left += (USHORT)iXShift; pr->Right += (USHORT)iXShift;
  870. pr->Top += (USHORT)iYShift; pr->Bottom += (USHORT)iYShift;
  871. if (pr->Left < 0 || pr->Top < 0) { return 0; }
  872. if (pr->Right >= pc->X || pr->Bottom >= pc->Y) { return 0; }
  873. SetConsoleWindowInfo(lpclient->TC_Console, TRUE, pr);
  874. return 0;
  875. }
  876. //
  877. // searches for a new console owner in the specified direction
  878. // assumes server is locked for writing
  879. //
  880. DWORD
  881. TraceUpdateConsoleOwner(
  882. LPTRACE_SERVER lpserver,
  883. INT dir
  884. ) {
  885. INT i;
  886. DWORD dwOldOwner, dwNewOwner;
  887. LPTRACE_CLIENT lpNewOwner=NULL, lpOldOwner;
  888. //
  889. // if no-one owns the console, dwOldOwner is MAX_CLIENT_COUNT
  890. // in this case, the algorithm below ensures that the console
  891. // is assigned to someone else, if there is another console client
  892. //
  893. dwOldOwner = lpserver->TS_ConsoleOwner;
  894. if (dwOldOwner != MAX_CLIENT_COUNT) {
  895. lpOldOwner = lpserver->TS_ClientTable[dwOldOwner];
  896. }
  897. else {
  898. lpOldOwner = NULL;
  899. }
  900. //
  901. // find another owner; the macro OFFSET_CLIENT wraps
  902. // around both ends of the array, so we only need to take care
  903. // that the loop executes no more than MAX_CLIENT_COUNT times
  904. //
  905. for (i = 0, dwNewOwner = OFFSET_CLIENT(dwOldOwner, dir);
  906. i < MAX_CLIENT_COUNT && dwNewOwner != dwOldOwner;
  907. i++, dwNewOwner = OFFSET_CLIENT(dwNewOwner, dir)) {
  908. lpNewOwner = lpserver->TS_ClientTable[dwNewOwner];
  909. if (lpNewOwner != NULL) {
  910. if (TRACE_CLIENT_USES_CONSOLE(lpNewOwner)) {
  911. //
  912. // found a console client, so break out of the search
  913. //
  914. break;
  915. }
  916. }
  917. }
  918. if (lpNewOwner != NULL && TRACE_CLIENT_USES_CONSOLE(lpNewOwner)) {
  919. //
  920. // switch to the next buffer as follows:
  921. // call SetConsoleActiveScreenBuffer
  922. // update lpserver->dwConsoleOwner
  923. // update the console title since the new console owner
  924. // may be disabled
  925. //
  926. SetConsoleActiveScreenBuffer(lpNewOwner->TC_Console);
  927. lpserver->TS_ConsoleOwner = dwNewOwner;
  928. TraceUpdateConsoleTitle(lpNewOwner);
  929. }
  930. else
  931. if (lpOldOwner == NULL || !TRACE_CLIENT_USES_CONSOLE(lpOldOwner)) {
  932. //
  933. // no owner was found, and the current owner is gone
  934. // set the owner ID to MAX_CLIENT_COUNT, thereby
  935. // guaranteeing that the next console client
  936. // will become the console owner
  937. //
  938. lpserver->TS_ConsoleOwner = MAX_CLIENT_COUNT;
  939. }
  940. return 0;
  941. }
  942. //----------------------------------------------------------------------------
  943. // Function: CreateReadWriteLock
  944. //
  945. // Initializes a multiple-reader/single-writer lock object
  946. //----------------------------------------------------------------------------
  947. DWORD
  948. CreateReadWriteLock(
  949. PREAD_WRITE_LOCK pRWL
  950. ) {
  951. pRWL->RWL_ReaderCount = 0;
  952. try {
  953. InitializeCriticalSection(&(pRWL)->RWL_ReadWriteBlock);
  954. }
  955. except (EXCEPTION_EXECUTE_HANDLER) {
  956. return GetExceptionCode();
  957. }
  958. pRWL->RWL_ReaderDoneEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
  959. if (pRWL->RWL_ReaderDoneEvent != NULL) {
  960. return GetLastError();
  961. }
  962. return NO_ERROR;
  963. }
  964. //----------------------------------------------------------------------------
  965. // Function: DeleteReadWriteLock
  966. //
  967. // Frees resources used by a multiple-reader/single-writer lock object
  968. //----------------------------------------------------------------------------
  969. VOID
  970. DeleteReadWriteLock(
  971. PREAD_WRITE_LOCK pRWL
  972. ) {
  973. if (pRWL==NULL)
  974. return;
  975. if (pRWL->RWL_ReaderDoneEvent)
  976. {
  977. CloseHandle(pRWL->RWL_ReaderDoneEvent);
  978. DeleteCriticalSection(&pRWL->RWL_ReadWriteBlock);
  979. }
  980. pRWL->RWL_ReaderDoneEvent = NULL;
  981. pRWL->RWL_ReaderCount = 0;
  982. }
  983. //----------------------------------------------------------------------------
  984. // Function: AcquireReadLock
  985. //
  986. // Secures shared ownership of the lock object for the caller.
  987. //
  988. // readers enter the read-write critical section, increment the count,
  989. // and leave the critical section
  990. //----------------------------------------------------------------------------
  991. VOID
  992. AcquireReadLock(
  993. PREAD_WRITE_LOCK pRWL
  994. ) {
  995. EnterCriticalSection(&pRWL->RWL_ReadWriteBlock);
  996. InterlockedIncrement(&pRWL->RWL_ReaderCount);
  997. LeaveCriticalSection(&pRWL->RWL_ReadWriteBlock);
  998. }
  999. //----------------------------------------------------------------------------
  1000. // Function: ReleaseReadLock
  1001. //
  1002. // Relinquishes shared ownership of the lock object.
  1003. //
  1004. // the last reader sets the event to wake any waiting writers
  1005. //----------------------------------------------------------------------------
  1006. VOID
  1007. ReleaseReadLock (
  1008. PREAD_WRITE_LOCK pRWL
  1009. ) {
  1010. if (InterlockedDecrement(&pRWL->RWL_ReaderCount) < 0) {
  1011. SetEvent(pRWL->RWL_ReaderDoneEvent);
  1012. }
  1013. }
  1014. //----------------------------------------------------------------------------
  1015. // Function: AcquireWriteLock
  1016. //
  1017. // Secures exclusive ownership of the lock object.
  1018. //
  1019. // the writer blocks other threads by entering the ReadWriteBlock section,
  1020. // and then waits for any thread(s) owning the lock to finish
  1021. //----------------------------------------------------------------------------
  1022. VOID
  1023. AcquireWriteLock(
  1024. PREAD_WRITE_LOCK pRWL
  1025. ) {
  1026. EnterCriticalSection(&pRWL->RWL_ReadWriteBlock);
  1027. if (InterlockedDecrement(&pRWL->RWL_ReaderCount) >= 0) {
  1028. WaitForSingleObject(pRWL->RWL_ReaderDoneEvent, INFINITE);
  1029. }
  1030. }
  1031. //----------------------------------------------------------------------------
  1032. // Function: ReleaseWriteLock
  1033. //
  1034. // Relinquishes exclusive ownership of the lock object.
  1035. //
  1036. // the writer releases the lock by setting the count to zero
  1037. // and then leaving the ReadWriteBlock critical section
  1038. //----------------------------------------------------------------------------
  1039. VOID
  1040. ReleaseWriteLock(
  1041. PREAD_WRITE_LOCK pRWL
  1042. ) {
  1043. pRWL->RWL_ReaderCount = 0;
  1044. LeaveCriticalSection(&(pRWL)->RWL_ReadWriteBlock);
  1045. }
  1046. // assumes server lock
  1047. VOID
  1048. SetWaitArray(
  1049. LPTRACE_SERVER lpserver
  1050. )
  1051. {
  1052. //
  1053. // reset array for client change notifications.
  1054. // only used if server thread is not created
  1055. //
  1056. {
  1057. LPTRACE_CLIENT *lplpc, *lplpcstart, *lplpcend;
  1058. g_posBase = POS_TABLE;
  1059. g_hWaitHandles[POS_TABLE] = lpserver->TS_TableEvent;
  1060. g_posLast = POS_CLIENT_0;
  1061. lplpcstart = lpserver->TS_ClientTable;
  1062. lplpcend = lpserver->TS_ClientTable + MAX_CLIENT_COUNT;
  1063. for (lplpc = lplpcstart; lplpc < lplpcend; lplpc++) {
  1064. if (*lplpc != NULL && TRACE_CLIENT_USES_REGISTRY(*lplpc)) {
  1065. g_hWaitHandles[g_posLast++] = (*lplpc)->TC_ConfigEvent;
  1066. }
  1067. }
  1068. }
  1069. }