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.

1967 lines
63 KiB

  1. /****************************************************************************/
  2. // Directory Integrity Service
  3. //
  4. // Copyright (C) 2000, Microsoft Corporation
  5. /****************************************************************************/
  6. #include "dis.h"
  7. #include "jetrpc.h"
  8. #include "jetsdis.h"
  9. #include "sdevent.h"
  10. #pragma warning (push, 4)
  11. #define SERVER_ADDRESS_LENGTH 64
  12. #define NUM_JETRPC_THREADS 10
  13. #define MAX_DRIVE_LETTER_LENGTH 24
  14. // Number of 100-nanosecond periods in 1 second.
  15. #define FILETIME_INTERVAL_TO_SECONDS_MULTIPLIER 10000000
  16. #define DEBUG_LOG_FILENAME L"tssdis.log"
  17. #define MY_STATUS_COMMITMENT_LIMIT (0xC000012DL)
  18. #define MAX_INSTANCE_MEMORYERR 20
  19. const DIRCOLUMNS SessionDirectoryColumns[NUM_SESSDIRCOLUMNS] = {
  20. { "UserName", JET_coltypLongText, 512 },
  21. { "Domain", JET_coltypLongText, 254 },
  22. { "ServerID", JET_coltypLong, 0 },
  23. { "SessionID", JET_coltypLong, 0 },
  24. { "TSProtocol", JET_coltypLong, 0 },
  25. { "CreateTimeLow", JET_coltypLong, 0 },
  26. { "CreateTimeHigh", JET_coltypLong, 0 },
  27. { "DisconnectTimeLow", JET_coltypLong, 0 },
  28. { "DisconnectTimeHigh", JET_coltypLong, 0 },
  29. { "ApplicationType", JET_coltypLongText, 512 },
  30. { "ResolutionWidth", JET_coltypLong, 0 },
  31. { "ResolutionHeight", JET_coltypLong, 0 },
  32. { "ColorDepth", JET_coltypLong, 0 },
  33. { "State", JET_coltypBit, 0 },
  34. };
  35. const DIRCOLUMNS ServerDirectoryColumns[NUM_SERVDIRCOLUMNS] = {
  36. { "ServerID", JET_coltypLong, 0 },
  37. { "ServerAddress", JET_coltypLongText, 128 },
  38. { "ClusterID", JET_coltypLong, 0 },
  39. { "AlmostInTimeLow", JET_coltypLong, 0 },
  40. { "AlmostInTimeHigh", JET_coltypLong, 0 },
  41. { "NumberFailedPings", JET_coltypLong, 0 },
  42. { "SingleSessionMode", JET_coltypBit, 0 },
  43. };
  44. const DIRCOLUMNS ClusterDirectoryColumns[NUM_CLUSDIRCOLUMNS] = {
  45. { "ClusterID", JET_coltypLong, 0 },
  46. { "ClusterName", JET_coltypLongText, 128 },
  47. { "SingleSessionMode", JET_coltypBit, 0 },
  48. };
  49. JET_COLUMNID sesdircolumnid[NUM_SESSDIRCOLUMNS];
  50. JET_COLUMNID servdircolumnid[NUM_SERVDIRCOLUMNS];
  51. JET_COLUMNID clusdircolumnid[NUM_CLUSDIRCOLUMNS];
  52. JET_INSTANCE g_instance = 0;
  53. ADOConnection *g_pConnection;
  54. HANDLE g_hStopServiceEvent;
  55. SERVICE_STATUS g_DISStatus;
  56. SERVICE_STATUS_HANDLE g_DISStatusHandle;
  57. BOOL g_bDebug = FALSE;
  58. // Registry settings follow
  59. #if 0
  60. DWORD g_bUseSQL = 0;
  61. #endif
  62. enum TraceOutputMode {
  63. NoTraceOutput,
  64. DebugPrintOutput,
  65. StdOutput,
  66. FileOutput
  67. };
  68. TraceOutputMode g_TraceOutputMode = NoTraceOutput;
  69. HANDLE g_hFileOutput = INVALID_HANDLE_VALUE;
  70. // For debugging purposes, we can set the ping mode to something other than
  71. // WinStationOpenServer using the registry.
  72. enum PingMode {
  73. NormalMode,
  74. AlwaysSucceed,
  75. AlwaysFail
  76. };
  77. PingMode g_PingMode = NormalMode;
  78. ULONGLONG g_TimeServerSilentBeforePing = 60 * FILETIME_INTERVAL_TO_SECONDS_MULTIPLIER;
  79. DWORD DISNumberSecondsBetweenPings = 10;
  80. DWORD g_NumberFailedPingsBeforePurge = 3;
  81. #ifdef DBG
  82. void OutputAllTables();
  83. #endif
  84. void TSDISErrorOut(wchar_t *format_string, ...)
  85. {
  86. // Immediately bail out if we are in NoTraceOutput mode.
  87. if (g_TraceOutputMode == NoTraceOutput) {
  88. return;
  89. }
  90. else {
  91. // Otherwise, do the right thing.
  92. wchar_t TotalString[MAX_DEBUG_STRING_LENGTH + MAX_THREADIDSTR_LENGTH];
  93. wchar_t *ThreadIDString = TotalString;
  94. wchar_t *DebugOutString = NULL;
  95. va_list args;
  96. int ThreadStrLength;
  97. // Get the current thread ID
  98. ThreadStrLength = _snwprintf(ThreadIDString, MAX_THREADIDSTR_LENGTH,
  99. L"%d: ", GetCurrentThreadId());
  100. // Set the place for the out string to after the string, or after the whole
  101. // buffer if _snwprintf didn't have enough space.
  102. if (ThreadStrLength > 0)
  103. DebugOutString = &TotalString[ThreadStrLength];
  104. else
  105. DebugOutString = &TotalString[MAX_THREADIDSTR_LENGTH];
  106. va_start(args, format_string);
  107. // Create the debug output string.
  108. _vsnwprintf(DebugOutString, MAX_DEBUG_STRING_LENGTH, format_string, args);
  109. DebugOutString[MAX_DEBUG_STRING_LENGTH - 1] = '\0';
  110. // Output to the correct place.
  111. switch (g_TraceOutputMode) {
  112. case DebugPrintOutput:
  113. OutputDebugString(TotalString);
  114. break;
  115. case StdOutput:
  116. wprintf(TotalString);
  117. break;
  118. case FileOutput:
  119. {
  120. char TotalStringA[MAX_DEBUG_STRING_LENGTH +
  121. MAX_THREADIDSTR_LENGTH];
  122. DWORD dwBytes = 0;
  123. // Convert to ANSI.
  124. dwBytes = WideCharToMultiByte(CP_ACP, 0, TotalString,
  125. -1, TotalStringA, MAX_DEBUG_STRING_LENGTH +
  126. MAX_THREADIDSTR_LENGTH, 0, 0);
  127. // Don't write the terminating NULL (3rd argument)!
  128. // Ignore return value.
  129. WriteFile(g_hFileOutput, TotalStringA, dwBytes - 1,
  130. &dwBytes, NULL);
  131. break;
  132. }
  133. }
  134. va_end(args);
  135. }
  136. }
  137. // TSDISErrorTimeOut
  138. //
  139. // This function is used to output a single FILETIME low, high pair. The format
  140. // string, given as the first argument, MUST specify a %s format specifier for
  141. // where the date/time should go.
  142. //
  143. // Example:
  144. // TSDISErrorTimeOut(L"The date and time are %s\n", CurrTimeLow, CurrTimeHigh);
  145. void TSDISErrorTimeOut(wchar_t *format_string, DWORD TimeLow, DWORD TimeHigh)
  146. {
  147. if (g_TraceOutputMode == NoTraceOutput) {
  148. return;
  149. }
  150. else {
  151. // We just need to convert the FILETIME we have into a SYSTEMTIME,
  152. // and then output the SYSTEMTIME using GetDateFormat and GetTimeFormat.
  153. FILETIME ft;
  154. SYSTEMTIME st;
  155. SYSTEMTIME stloc;
  156. int offset = 0;
  157. wchar_t DateString[MAX_DATE_TIME_STRING_LENGTH];
  158. ft.dwLowDateTime = TimeLow;
  159. ft.dwHighDateTime = TimeHigh;
  160. if (FileTimeToSystemTime(&ft, &st) != 0) {
  161. // st is the system time.
  162. // UTC format?
  163. if (SystemTimeToTzSpecificLocalTime(NULL, &st, &stloc) != 0) {
  164. offset = GetDateFormat(LOCALE_SYSTEM_DEFAULT, DATE_SHORTDATE,
  165. &stloc, NULL, DateString, MAX_DATE_TIME_STRING_LENGTH);
  166. if (offset != 0) {
  167. // Turn the terminating NULL into a space.
  168. DateString[offset - 1] = ' ';
  169. // Write the time after the space.
  170. offset = GetTimeFormat(LOCALE_SYSTEM_DEFAULT, 0, &stloc,
  171. NULL, &DateString[offset],
  172. MAX_DATE_TIME_STRING_LENGTH - offset);
  173. if (offset != 0) {
  174. // Output the string.
  175. TSDISErrorOut(format_string, DateString);
  176. }
  177. }
  178. }
  179. }
  180. }
  181. }
  182. // This function is duplicated from \nt\termsrv\winsta\server\sessdir.cpp.
  183. //
  184. // PostSessDirErrorValueEvent
  185. //
  186. // Utility function used to create a system log error event containing one
  187. // hex DWORD error code value.
  188. void PostSessDirErrorValueEvent(unsigned EventCode, DWORD ErrVal)
  189. {
  190. HANDLE hLog;
  191. WCHAR hrString[128];
  192. PWSTR String = NULL;
  193. static DWORD numInstances = 0;
  194. //
  195. //count the numinstances of out of memory error, if this is more than
  196. //a specified number, we just won't log them
  197. //
  198. if( MY_STATUS_COMMITMENT_LIMIT == ErrVal )
  199. {
  200. if( numInstances > MAX_INSTANCE_MEMORYERR )
  201. return;
  202. //
  203. //if applicable, tell the user that we won't log any more of the out of memory errors
  204. //
  205. if( numInstances >= MAX_INSTANCE_MEMORYERR - 1 ) {
  206. wsprintfW(hrString, L"0x%X. This type of error will not be logged again to avoid eventlog fillup.", ErrVal);
  207. String = hrString;
  208. }
  209. numInstances++;
  210. }
  211. hLog = RegisterEventSource(NULL, L"TermServSessDir");
  212. if (hLog != NULL) {
  213. if( NULL == String ) {
  214. wsprintfW(hrString, L"0x%X", ErrVal);
  215. String = hrString;
  216. }
  217. ReportEvent(hLog, EVENTLOG_ERROR_TYPE, 0, EventCode, NULL, 1, 0,
  218. (const WCHAR **)&String, NULL);
  219. DeregisterEventSource(hLog);
  220. }
  221. }
  222. // DISJetGetServersPendingReconnects
  223. //
  224. // Returns arrays of max length 10 of servers pending reconnects, where the
  225. // reconnect is greater than g_TimeServerSilentBeforePing seconds.
  226. HRESULT STDMETHODCALLTYPE DISJetGetServersPendingReconnects(
  227. OUT long __RPC_FAR *pNumSessionsReturned,
  228. OUT WCHAR ServerAddressRows[10][SERVER_ADDRESS_LENGTH],
  229. OUT DWORD ServerIDs[10])
  230. {
  231. JET_ERR err;
  232. JET_SESID sesid = JET_sesidNil;
  233. JET_DBID dbid;
  234. JET_TABLEID servdirtableid;
  235. DWORD zero = 0;
  236. *pNumSessionsReturned = 0;
  237. unsigned i = 0;
  238. unsigned long cbActual;
  239. // These are really FILETIMEs, but we want to do 64-bit math on them,
  240. // and they're the same structure as FILETIMEs.
  241. ULARGE_INTEGER ulCurrentTime;
  242. ULARGE_INTEGER ulAITTime;
  243. //TSDISErrorOut(L"GetPendRec...");
  244. CALL(JetBeginSession(g_instance, &sesid, "user", ""));
  245. CALL(JetOpenDatabase(sesid, JETDBFILENAME, "", &dbid, 0));
  246. CALL(JetOpenTable(sesid, dbid, "ServerDirectory", NULL, 0, 0,
  247. &servdirtableid));
  248. // Get the current file time.
  249. SYSTEMTIME st;
  250. // Retrieve the time.
  251. GetSystemTime(&st);
  252. SystemTimeToFileTime(&st, (FILETIME *) &ulCurrentTime);
  253. CALL(JetBeginTransaction(sesid));
  254. // Since Jet has no unsigned long type, go through the servers first
  255. // looking for keys greater than 0, 0, then looking for keys less than 0, 0
  256. // TODO: Consider how to do this with JET_coltypDateTime or using NULLs
  257. for (int j = 0; j < 2; j++) {
  258. CALL(JetSetCurrentIndex(sesid, servdirtableid, "ServerAlmostInTimes"));
  259. CALL(JetMakeKey(sesid, servdirtableid, &zero, sizeof(zero),
  260. JET_bitNewKey));
  261. CALL(JetMakeKey(sesid, servdirtableid, &zero, sizeof(zero), 0));
  262. if (0 == j)
  263. err = JetSeek(sesid, servdirtableid, JET_bitSeekGT);
  264. else
  265. err = JetSeek(sesid, servdirtableid, JET_bitSeekLT);
  266. while ((i < TSSD_MaxDisconnectedSessions) && (JET_errSuccess == err)) {
  267. // Get AlmostInTimeLow, AlmostInTimeHigh (3 + 4) for computation.
  268. CALL(JetRetrieveColumn(sesid, servdirtableid, servdircolumnid[
  269. SERVDIR_AITLOW_INTERNAL_INDEX], &(ulAITTime.LowPart),
  270. sizeof(ulAITTime.LowPart), &cbActual, 0, NULL));
  271. CALL(JetRetrieveColumn(sesid, servdirtableid, servdircolumnid[
  272. SERVDIR_AITHIGH_INTERNAL_INDEX], &(ulAITTime.HighPart),
  273. sizeof(ulAITTime.HighPart), &cbActual, 0, NULL));
  274. // If the difference between the current time and the time the
  275. // server was stamped is greater than the set
  276. // TimeServerSilentBeforePing, then put it in the return array,
  277. // else don't.
  278. if ((ulCurrentTime.QuadPart - ulAITTime.QuadPart) >
  279. g_TimeServerSilentBeforePing) {
  280. // Get ServerID
  281. CALL(JetRetrieveColumn(sesid, servdirtableid, servdircolumnid[
  282. SERVDIR_SERVID_INTERNAL_INDEX], &ServerIDs[i],
  283. sizeof(ServerIDs[i]), &cbActual, 0, NULL));
  284. // Get the ServerAddress for this record.
  285. CALL(JetRetrieveColumn(sesid, servdirtableid, servdircolumnid[
  286. SERVDIR_SERVADDR_INTERNAL_INDEX],
  287. &ServerAddressRows[i][0], sizeof(ServerAddressRows[i]),
  288. &cbActual, 0, NULL));
  289. i += 1;
  290. }
  291. // Move to the next matching record.
  292. if (0 == j)
  293. err = JetMove(sesid, servdirtableid, JET_MoveNext, 0);
  294. else
  295. err = JetMove(sesid, servdirtableid, JET_MovePrevious, 0);
  296. }
  297. }
  298. *pNumSessionsReturned = i;
  299. CALL(JetCommitTransaction(sesid, 0));
  300. CALL(JetCloseTable(sesid, servdirtableid));
  301. CALL(JetCloseDatabase(sesid, dbid, 0));
  302. CALL(JetEndSession(sesid, 0));
  303. return S_OK;
  304. HandleError:
  305. if (sesid != JET_sesidNil) {
  306. // Can't really recover. Just bail out.
  307. (VOID) JetRollback(sesid, JET_bitRollbackAll);
  308. // Force the session closed
  309. (VOID) JetEndSession(sesid, JET_bitForceSessionClosed);
  310. }
  311. return E_FAIL;
  312. }
  313. #if 0
  314. HRESULT STDMETHODCALLTYPE DISSQLGetServersPendingReconnects(
  315. OUT long __RPC_FAR *pNumSessionsReturned,
  316. OUT CVar *pVarRows)
  317. {
  318. long NumRecords = 0;
  319. HRESULT hr;
  320. ADOCommand *pCommand;
  321. ADOParameters *pParameters;
  322. ADORecordset *pResultRecordSet;
  323. CVar varFields;
  324. CVar varStart;
  325. TRC2((TB,"GetServersWithDisconnectedSessions"));
  326. ASSERT((pNumSessionsReturned != NULL),(TB,"NULL pNumSess"));
  327. hr = CreateADOStoredProcCommand(L"SP_TSDISGetServersPendingReconnects",
  328. &pCommand, &pParameters);
  329. if (SUCCEEDED(hr)) {
  330. // Execute the command.
  331. hr = pCommand->Execute(NULL, NULL, adCmdStoredProc,
  332. &pResultRecordSet);
  333. pParameters->Release();
  334. pCommand->Release();
  335. }
  336. else {
  337. ERR((TB,"GetServersWDiscSess: Failed create cmd, hr=0x%X", hr));
  338. }
  339. // At this point we have a result recordset containing the server rows
  340. // corresponding to all of the disconnected sessions.
  341. if (SUCCEEDED(hr)) {
  342. long State;
  343. NumRecords = 0;
  344. hr = pResultRecordSet->get_State(&State);
  345. if (SUCCEEDED(hr)) {
  346. if (!(State & adStateClosed)) {
  347. VARIANT_BOOL VB;
  348. // If EOF the recordset is empty.
  349. hr = pResultRecordSet->get_EOF(&VB);
  350. if (SUCCEEDED(hr)) {
  351. if (VB) {
  352. TRC1((TB,"GetServersWDiscSess: Result recordset EOF, "
  353. "0 rows"));
  354. goto PostUnpackResultSet;
  355. }
  356. }
  357. else {
  358. ERR((TB,"GetServersWDiscSess: Failed get_EOF, hr=0x%X",
  359. hr));
  360. goto PostUnpackResultSet;
  361. }
  362. }
  363. else {
  364. ERR((TB,"GetServersWDiscSess: Closed result recordset"));
  365. goto PostUnpackResultSet;
  366. }
  367. }
  368. else {
  369. ERR((TB,"GetServersWDiscSess: get_State failed, hr=0x%X", hr));
  370. goto PostUnpackResultSet;
  371. }
  372. // Grab the result data into a safearray, starting with the default
  373. // current row and all fields.
  374. varStart.InitNoParam();
  375. varFields.InitNoParam();
  376. hr = pResultRecordSet->GetRows(adGetRowsRest, varStart,
  377. varFields, pVarRows);
  378. if (SUCCEEDED(hr)) {
  379. hr = SafeArrayGetUBound(pVarRows->parray, 2, &NumRecords);
  380. if (SUCCEEDED(hr)) {
  381. // 0-based array bound was returned, num rows is that + 1.
  382. NumRecords++;
  383. TRC1((TB,"%d rows retrieved from safearray", NumRecords));
  384. }
  385. else {
  386. ERR((TB,"GetServersWithDisc: Failed safearray getubound, "
  387. "hr=0x%X", hr));
  388. goto PostUnpackResultSet;
  389. }
  390. }
  391. else {
  392. ERR((TB,"GetServersWDiscSess: Failed to get rows, hr=0x%X", hr));
  393. goto PostUnpackResultSet;
  394. }
  395. PostUnpackResultSet:
  396. pResultRecordSet->Release();
  397. }
  398. else {
  399. ERR((TB,"GetServersWDiscSess: Failed exec, hr=0x%X", hr));
  400. }
  401. *pNumSessionsReturned = NumRecords;
  402. return hr;
  403. }
  404. #endif
  405. /****************************************************************************/
  406. // DISDebugControlHandler
  407. //
  408. // Handle console control events for when service is in debug mode.
  409. /****************************************************************************/
  410. BOOL WINAPI DISDebugControlHandler(DWORD dwCtrlType) {
  411. switch(dwCtrlType)
  412. {
  413. case CTRL_BREAK_EVENT:
  414. case CTRL_C_EVENT:
  415. TSDISErrorOut(L"Stopping service\n");
  416. SetEvent(g_hStopServiceEvent);
  417. // Should I wait for that to complete?
  418. return TRUE;
  419. break;
  420. }
  421. return FALSE;
  422. }
  423. /****************************************************************************/
  424. // DISPingServer
  425. //
  426. // Given the IP address of a server, pings it. Returns TRUE on success, FALSE
  427. // on failure.
  428. /****************************************************************************/
  429. BOOLEAN DISPingServer(WCHAR *ServerAddress) {
  430. HANDLE hServer = NULL;
  431. hServer = WinStationOpenServer(ServerAddress);
  432. // The only case where we return false is where hServer is NULL and the
  433. // reason is not ERROR_ACCESS_DENIED.
  434. if (hServer == NULL) {
  435. if (GetLastError() != ERROR_ACCESS_DENIED)
  436. return FALSE;
  437. }
  438. else {
  439. // The hServer is valid, so clean up.
  440. WinStationCloseServer(hServer);
  441. }
  442. return TRUE;
  443. }
  444. /****************************************************************************/
  445. // DISGetServerStatus
  446. //
  447. // Given the IP address of a server, determines its state (Responding or
  448. // NotResponding).
  449. //
  450. // Currently implemented as a ping. See lengthy comment in main for one
  451. // possible future optimization.
  452. /****************************************************************************/
  453. SERVER_STATUS DISGetServerStatus(WCHAR *ServerAddress) {
  454. switch (g_PingMode) {
  455. case AlwaysFail:
  456. return NotResponding;
  457. case AlwaysSucceed:
  458. return Responding;
  459. case NormalMode:
  460. // NOTE INTENTIONAL FALLTHROUGH.
  461. default:
  462. if (DISPingServer(ServerAddress) == TRUE)
  463. return Responding;
  464. else
  465. return NotResponding;
  466. }
  467. }
  468. #if 0
  469. HRESULT DISSQLInitialize() {
  470. // Retrieve number of seconds to wait from the registry -- NOT IMPLEMENTED
  471. HRESULT hr = S_OK;
  472. BSTR ConnectString = NULL;
  473. LONG RegRetVal;
  474. HKEY hKey;
  475. BSTR ConnectStr = NULL;
  476. BSTR UserStr = NULL;
  477. BSTR PwdStr = NULL;
  478. RegRetVal = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  479. TEXT("Software\\Microsoft\\DIS"), 0, KEY_READ, &hKey);
  480. if (RegRetVal == ERROR_SUCCESS) {
  481. DWORD Type, DataSize;
  482. // Determine the needed size.
  483. DataSize = 0;
  484. RegRetVal = RegQueryValueExW(hKey, L"ConnectString", NULL,
  485. &Type, NULL, &DataSize);
  486. DataSize &= ~1;
  487. if (RegRetVal == ERROR_SUCCESS && Type == REG_SZ) {
  488. ConnectString = SysAllocStringLen(L"", DataSize /
  489. sizeof(WCHAR));
  490. if (ConnectString != NULL) {
  491. RegRetVal = RegQueryValueExW(hKey, L"ConnectString",
  492. NULL, &Type, (BYTE *)ConnectString,
  493. &DataSize);
  494. if (RegRetVal == ERROR_SUCCESS) {
  495. // Hold onto the connect string for use below.
  496. TRC1((TB,"Retrieved conn str %S", ConnectString));
  497. }
  498. else {
  499. ERR((TB,"Final RegQuery failed, err=%u", RegRetVal));
  500. hr = E_FAIL;
  501. goto Cleanup;
  502. }
  503. }
  504. else {
  505. ERR((TB,"Failed alloc connect string"));
  506. hr = E_OUTOFMEMORY;
  507. goto Cleanup;
  508. }
  509. }
  510. else {
  511. ERR((TB,"Failed RegQuery - err=%u, DataSize=%u, type=%u",
  512. RegRetVal, DataSize, Type));
  513. hr = E_FAIL;
  514. goto Cleanup;
  515. }
  516. RegCloseKey(hKey);
  517. }
  518. else {
  519. ERR((TB,"RegOpenKeyEx returned err %u", RegRetVal));
  520. hr = E_FAIL;
  521. goto Cleanup;
  522. }
  523. hr = CoInitialize(NULL);
  524. // Alloc the BSTRs for the connection.
  525. ConnectStr = SysAllocString(ConnectString);
  526. UserStr = SysAllocString(L"");
  527. PwdStr = SysAllocString(L"");
  528. if ((ConnectStr == NULL) || (UserStr == NULL) || (PwdStr == NULL)) {
  529. ERR((TB, "Failed alloc Connect, User, or PwdStr"));
  530. hr = E_OUTOFMEMORY;
  531. goto Cleanup;
  532. }
  533. // Create an ADO connection instance and connect.
  534. hr = CoCreateInstance(CLSID_CADOConnection, NULL,
  535. CLSCTX_INPROC_SERVER, IID_IADOConnection,
  536. (LPVOID *)&g_pConnection);
  537. if (SUCCEEDED(hr)) {
  538. // Do the open.
  539. hr = g_pConnection->Open(ConnectStr, UserStr, PwdStr,
  540. adOpenUnspecified);
  541. if (!SUCCEEDED(hr)) {
  542. ERR((TB,"Failed open DB, hr=0x%X", hr));
  543. g_pConnection->Release();
  544. g_pConnection = NULL;
  545. }
  546. }
  547. else {
  548. ERR((TB,"CoCreate(ADOConn) returned 0x%X", hr));
  549. }
  550. Cleanup:
  551. // SysFreeString(NULL) is ok.
  552. SysFreeString(ConnectString);
  553. SysFreeString(ConnectStr);
  554. SysFreeString(UserStr);
  555. SysFreeString(PwdStr);
  556. return hr;
  557. }
  558. #endif
  559. HRESULT DISJetInitialize()
  560. {
  561. JET_SESID sesid = JET_sesidNil;;
  562. JET_TABLEID sessdirtableid;
  563. JET_TABLEID servdirtableid;
  564. JET_TABLEID clusdirtableid;
  565. JET_DBID dbid = JET_dbidNil;
  566. JET_ERR err = JET_errSuccess;
  567. JET_TABLECREATE tSess;
  568. JET_COLUMNCREATE cSess[NUM_SESSDIRCOLUMNS];
  569. JET_TABLECREATE tServ;
  570. JET_COLUMNCREATE cServ[NUM_SERVDIRCOLUMNS];
  571. JET_TABLECREATE tClus;
  572. JET_COLUMNCREATE cClus[NUM_CLUSDIRCOLUMNS];
  573. unsigned count;
  574. DWORD dwError;
  575. WCHAR filename[MAX_LOGFILE_LENGTH];
  576. BOOL br;
  577. SECURITY_ATTRIBUTES SA;
  578. //
  579. // This is a string security descriptor. Look up "Security Descriptor
  580. // Definition Language" in MSDN for more details.
  581. //
  582. // This one says:
  583. //
  584. // D: <we are creating a DACL>
  585. // (A; <Allow ACE>
  586. // OICI; <Perform object and container inheritance, i.e., let files and
  587. // directories under this one have these attributes>
  588. // GA <Generic All Access--Full Control>
  589. // ;;;SY) <SYSTEM>
  590. // (A;OICI;GA;;;BA) <same for Builtin Administrators group>
  591. // (A;OICI;GA;;;CO) <same for creator/owner>
  592. //
  593. // We'll use it below to create our directory with the right permissions.
  594. WCHAR *pwszSD = L"D:(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GA;;;CO)";
  595. #if 0 // Failover support--before reactivating, check logic versus reading curr directory from registry.
  596. DWORD dwClusterState;
  597. // First, determine whether we are running in a cluster. If so, files
  598. // will have to go on the shared drive. If not, files will go in
  599. // JETDISDBDIRECTORYW.
  600. dwError = GetNodeClusterState(NULL, &dwClusterState);
  601. if (dwError != ERROR_SUCCESS) {
  602. dwClusterState = ClusterStateNotInstalled;
  603. TSDISErrorOut(L"TSDIS: Unable to get cluster state, err = %d\n",
  604. dwError);
  605. }
  606. if (dwClusterState == ClusterStateRunning) {
  607. HCLUSTER hclus;
  608. HRESOURCE hrSD;
  609. WCHAR *pszDriveLetter = NULL;
  610. DWORD cchDriveLetter = MAX_DRIVE_LETTER_LENGTH;
  611. // Change the current directory to the right place on the shared
  612. // drive.
  613. // Open the cluster.
  614. hclus = OpenCluster(NULL);
  615. if (hclus == NULL) {
  616. // TODO: Log event.
  617. TSDISErrorOut(L"Unable to open cluster, error %d\n",
  618. GetLastError());
  619. goto HandleError;
  620. }
  621. // Open the session directory resource.
  622. // TODO: Don't want to hardcode this.
  623. hrSD = OpenClusterResource(hclus, L"DIS");
  624. if (hrSD == NULL) {
  625. // TODO: Log event.
  626. TSDISErrorOut(L"Unable to open cluster resource, error %d\n",
  627. GetLastError());
  628. goto HandleError;
  629. }
  630. pszDriveLetter = new WCHAR[cchDriveLetter];
  631. if (pszDriveLetter == NULL) {
  632. TSDISErrorOut(L"Failed to allocate memory for drive letter.\n");
  633. goto HandleError;
  634. }
  635. // Get the drive we're supposed to use.
  636. dwError = ResUtilFindDependentDiskResourceDriveLetter(hclus, hrSD,
  637. pszDriveLetter, &cchDriveLetter);
  638. if (dwError == ERROR_MORE_DATA) {
  639. // Wow, big drive letter!
  640. delete [] pszDriveLetter;
  641. pszDriveLetter = new WCHAR[cchDriveLetter];
  642. if (pszDriveLetter == NULL) {
  643. TSDISErrorOut(L"Failed to allocate memory for drive letter\n");
  644. goto HandleError;
  645. }
  646. dwError = ResUtilFindDependentDiskResourceDriveLetter(hclus, hrSD,
  647. pszDriveLetter, &cchDriveLetter);
  648. }
  649. if (dwError != ERROR_SUCCESS) {
  650. TSDISErrorOut(L"Could not determine resource drive letter.\n");
  651. delete [] pszDriveLetter;
  652. goto HandleError;
  653. }
  654. // Switch the working directory to that drive.
  655. if (SetCurrentDirectory(pszDriveLetter) == FALSE) {
  656. TSDISErrorOut(L"Could not set current directory to that of "
  657. L"shared disk %s. Error=%d\n", pszDriveLetter,
  658. GetLastError());
  659. delete [] pszDriveLetter;
  660. goto HandleError;
  661. }
  662. delete [] pszDriveLetter;
  663. }
  664. else {
  665. // This is where file deletion used to go.
  666. }
  667. #endif // Failover support
  668. // Create security descriptor for database directory
  669. SA.nLength = sizeof(SECURITY_ATTRIBUTES);
  670. SA.bInheritHandle = FALSE;
  671. br = ConvertStringSecurityDescriptorToSecurityDescriptor(pwszSD,
  672. SDDL_REVISION_1, &(SA.lpSecurityDescriptor), NULL);
  673. if (br == 0) {
  674. PostSessDirErrorValueEvent(EVENT_COULDNOTSECUREDIR, GetLastError());
  675. goto HandleError;
  676. }
  677. // Create the system32\tssesdir directory.
  678. if (CreateDirectory(JETDISDBDIRECTORYW, &SA) == 0) {
  679. if (ERROR_ALREADY_EXISTS != (dwError = GetLastError())) {
  680. PostSessDirErrorValueEvent(EVENT_COULDNOTCREATEDIR, dwError);
  681. goto HandleError;
  682. }
  683. } else {
  684. // We created it successfully, so set the directory attributes to not
  685. // compress.
  686. // Obtain a handle to the directory.
  687. HANDLE hSDDirectory = CreateFile(JETDISDBDIRECTORYW, GENERIC_READ |
  688. GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
  689. OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
  690. if (INVALID_HANDLE_VALUE != hSDDirectory) {
  691. // We've succeeded opening the directory.
  692. USHORT CompressionState = COMPRESSION_FORMAT_NONE;
  693. USHORT OldCompressionState;
  694. DWORD BytesReturned = 0;
  695. // Get the current compression state.
  696. if (DeviceIoControl(hSDDirectory, FSCTL_GET_COMPRESSION,
  697. NULL, 0, &OldCompressionState, sizeof(USHORT),
  698. &BytesReturned, NULL) != 0) {
  699. // If the current compression state is compressed, uncompress.
  700. if (OldCompressionState != COMPRESSION_FORMAT_NONE) {
  701. if (DeviceIoControl(hSDDirectory, FSCTL_SET_COMPRESSION,
  702. &CompressionState, sizeof(USHORT), NULL, 0,
  703. &BytesReturned, NULL) == 0) {
  704. // Set compression state failed--this should only be a trace,
  705. // it may merely mean that the drive is FAT.
  706. TSDISErrorOut(L"TSDIS: Set compression state off failed, "
  707. L"lasterr=0x%X\n", GetLastError());
  708. } else {
  709. PostSessDirErrorValueEvent(EVENT_UNDID_COMPRESSION, 0);
  710. }
  711. }
  712. }
  713. CloseHandle(hSDDirectory);
  714. } else {
  715. // Nonfatal to have an error opening the directory
  716. TSDISErrorOut(L"TSDIS: Open directory to change compression state "
  717. L"failed, lasterr=0x%X\n", GetLastError());
  718. }
  719. }
  720. // Delete the database and all other JET files (if present), and start anew.
  721. (void) DeleteFile(JETDBFILENAMEW);
  722. (void) DeleteFile(JETAUXFILENAME1W);
  723. (void) DeleteFile(JETAUXFILENAME2W);
  724. (void) DeleteFile(JETAUXFILENAME3W);
  725. (void) DeleteFile(JETAUXFILENAME4W);
  726. (void) DeleteFile(JETAUXFILENAME5W);
  727. (void) DeleteFile(JETAUXFILENAME6W);
  728. // Delete numbered log files. Jet can create a bunch of log files
  729. // of the form edb00001.log, edb00002.log, . . ., edb0000a.log,
  730. // edb0000b.log, . . ., edb0000f.log, edb00010.log, . . .
  731. for (int i = 1; i < 0xfffff; i++) {
  732. swprintf(filename, JETDISDBDIRECTORYW L"edb%05X.log", i);
  733. // If the delete fails for any reason, break out of the loop.
  734. // Most likely, the failure is because the file does not exist.
  735. // If it failed for some other reason,
  736. if (DeleteFile(filename) == 0) {
  737. dwError = GetLastError();
  738. if (dwError == ERROR_FILE_NOT_FOUND) {
  739. break;
  740. }
  741. else {
  742. PostSessDirErrorValueEvent(EVENT_PROBLEM_DELETING_LOGS,
  743. dwError);
  744. break;
  745. }
  746. }
  747. }
  748. CALL(JetSetSystemParameter(&g_instance, 0, JET_paramSystemPath,
  749. 0, JETDISDBDIRECTORY));
  750. CALL(JetSetSystemParameter(&g_instance, 0, JET_paramTempPath,
  751. 0, JETDISDBDIRECTORY));
  752. //CALL(JetSetSystemParameter(&g_instance, 0, JET_paramMaxSessions,
  753. // JETDISMAXSESSIONS, NULL));
  754. CALL(JetSetSystemParameter(&g_instance, 0, JET_paramLogFilePath,
  755. 0, JETDISDBDIRECTORY));
  756. CALL(JetSetSystemParameter(&g_instance, 0, JET_paramCircularLog,
  757. 1, NULL));
  758. CALL(JetInit(&g_instance));
  759. CALL(JetBeginSession(g_instance, &sesid, "user", ""));
  760. err = JetCreateDatabase(sesid, JETDBFILENAME, "", &dbid, 0);
  761. if (JET_errDatabaseDuplicate == err) {
  762. JET_COLUMNDEF jcd;
  763. err = JetAttachDatabase(sesid, JETDBFILENAME, 0);
  764. // if we get a wrnDatabaseAttached, then we have recovered. Otherwise,
  765. // check the return value as usual.
  766. if (JET_wrnDatabaseAttached != err) {
  767. CALL(err);
  768. }
  769. // Populate our columnid arrays
  770. CALL(JetOpenDatabase(sesid, JETDBFILENAME, "", &dbid, 0));
  771. CALL(JetOpenTable(sesid, dbid, "SessionDirectory", NULL, 0, 0,
  772. &sessdirtableid));
  773. CALL(JetOpenTable(sesid, dbid, "ServerDirectory", NULL, 0, 0,
  774. &servdirtableid));
  775. CALL(JetOpenTable(sesid, dbid, "ClusterDirectory", NULL, 0, 0,
  776. &clusdirtableid));
  777. CALL(JetBeginTransaction(sesid));
  778. for (count = 0; count < NUM_SESSDIRCOLUMNS; count++) {
  779. CALL(JetGetColumnInfo(sesid, dbid, "SessionDirectory",
  780. SessionDirectoryColumns[count].szColumnName, &jcd,
  781. sizeof(jcd), 0));
  782. sesdircolumnid[count] = jcd.columnid;
  783. }
  784. for (count = 0; count < NUM_SERVDIRCOLUMNS; count++) {
  785. CALL(JetGetColumnInfo(sesid, dbid, "ServerDirectory",
  786. ServerDirectoryColumns[count].szColumnName, &jcd,
  787. sizeof(jcd), 0));
  788. servdircolumnid[count] = jcd.columnid;
  789. }
  790. for (count = 0; count < NUM_CLUSDIRCOLUMNS; count++) {
  791. CALL(JetGetColumnInfo(sesid, dbid, "ClusterDirectory",
  792. ClusterDirectoryColumns[count].szColumnName, &jcd,
  793. sizeof(jcd), 0));
  794. clusdircolumnid[count] = jcd.columnid;
  795. }
  796. CALL(JetCommitTransaction(sesid, 0));
  797. goto NormalExit;
  798. } else {
  799. CALL(err);
  800. }
  801. CALL(JetBeginTransaction(sesid));
  802. // Set up to create session directory schema
  803. tSess.cbStruct = sizeof(tSess);
  804. tSess.szTableName = "SessionDirectory";
  805. tSess.szTemplateTableName = NULL;
  806. tSess.ulPages = 0;
  807. tSess.ulDensity = 100;
  808. tSess.rgcolumncreate = &cSess[0];
  809. tSess.cColumns = NUM_SESSDIRCOLUMNS;
  810. tSess.rgindexcreate = NULL;
  811. tSess.cIndexes = 0;
  812. tSess.grbit = JET_bitTableCreateFixedDDL;
  813. for (count = 0; count < NUM_SESSDIRCOLUMNS; count++) {
  814. cSess[count].cbStruct = sizeof(JET_COLUMNCREATE);
  815. cSess[count].szColumnName = SessionDirectoryColumns[count].szColumnName;
  816. cSess[count].coltyp = SessionDirectoryColumns[count].coltyp;
  817. cSess[count].cbMax = SessionDirectoryColumns[count].colMaxLen;
  818. cSess[count].grbit = 0;
  819. cSess[count].pvDefault = NULL;
  820. cSess[count].cbDefault = 0;
  821. cSess[count].cp = 1200;
  822. cSess[count].columnid = 0;
  823. cSess[count].err = JET_errSuccess;
  824. }
  825. // Actually create the session directory table.
  826. CALL(JetCreateTableColumnIndex(sesid, dbid, &tSess));
  827. // Store columnids, tableid for later reference.
  828. for (count = 0; count < NUM_SESSDIRCOLUMNS; count++) {
  829. sesdircolumnid[count] = cSess[count].columnid;
  830. }
  831. sessdirtableid = tSess.tableid;
  832. // Create server, session index.
  833. CALL(JetCreateIndex(sesid, sessdirtableid, "primaryIndex", 0,
  834. "+ServerID\0+SessionID\0", sizeof("+ServerID\0+SessionID\0"),
  835. 100));
  836. // Create index by server for deletion.
  837. CALL(JetCreateIndex(sesid, sessdirtableid, "ServerIndex", 0,
  838. "+ServerID\0", sizeof("+ServerID\0"), 100));
  839. // Create index for disconnected session retrieval.
  840. CALL(JetCreateIndex(sesid, sessdirtableid, "DiscSessionIndex", 0,
  841. "+UserName\0+Domain\0+State\0",
  842. sizeof("+UserName\0+Domain\0+State\0"), 100));
  843. // Create index for all session retrieval.
  844. CALL(JetCreateIndex(sesid, sessdirtableid, "AllSessionIndex", 0,
  845. "+UserName\0+Domain\0",
  846. sizeof("+UserName\0+Domain\0"), 100));
  847. // Create server directory.
  848. tServ.cbStruct = sizeof(tServ);
  849. tServ.szTableName = "ServerDirectory";
  850. tServ.szTemplateTableName = NULL;
  851. tServ.ulPages = 0;
  852. tServ.ulDensity = 100;
  853. tServ.rgcolumncreate = &cServ[0];
  854. tServ.cColumns = NUM_SERVDIRCOLUMNS;
  855. tServ.rgindexcreate = NULL;
  856. tServ.cIndexes = 0;
  857. tServ.grbit = JET_bitTableCreateFixedDDL;
  858. for (count = 0; count < NUM_SERVDIRCOLUMNS; count++) {
  859. cServ[count].cbStruct = sizeof(JET_COLUMNCREATE);
  860. cServ[count].szColumnName = ServerDirectoryColumns[count].szColumnName;
  861. cServ[count].coltyp = ServerDirectoryColumns[count].coltyp;
  862. cServ[count].cbMax = ServerDirectoryColumns[count].colMaxLen;
  863. cServ[count].grbit = 0;
  864. cServ[count].pvDefault = NULL;
  865. cServ[count].cbDefault = 0;
  866. cServ[count].cp = 1200;
  867. cServ[count].columnid = 0;
  868. cServ[count].err = JET_errSuccess;
  869. }
  870. // Set the autoincrement column to autoincrement
  871. cServ[0].grbit |= JET_bitColumnAutoincrement;
  872. CALL(JetCreateTableColumnIndex(sesid, dbid, &tServ));
  873. for (count = 0; count < NUM_SERVDIRCOLUMNS; count++) {
  874. servdircolumnid[count] = cServ[count].columnid;
  875. }
  876. servdirtableid = tServ.tableid;
  877. // Create Server Name index.
  878. CALL(JetCreateIndex(sesid, servdirtableid, "ServNameIndex", 0,
  879. "+ServerAddress\0", sizeof("+ServerAddress\0"), 100));
  880. // Create Server ID index.
  881. CALL(JetCreateIndex(sesid, servdirtableid, "ServerIDIndex", 0,
  882. "+ServerID\0", sizeof("+ServerID\0"), 100));
  883. // Create Pending Reconnect index.
  884. CALL(JetCreateIndex(sesid, servdirtableid, "ServerAlmostInTimes", 0,
  885. "+AlmostInTimeLow\0+AlmostInTimeHigh\0",
  886. sizeof("+AlmostInTimeLow\0+AlmostInTimeHigh\0"), 100));
  887. // Create the single session index.
  888. CALL(JetCreateIndex(sesid, servdirtableid, "SingleSessionIndex", 0,
  889. "+ClusterID\0+SingleSessionMode\0",
  890. sizeof("+ClusterID\0+SingleSessionMode\0"), 100));
  891. // Create cluster directory.
  892. tClus.cbStruct = sizeof(tClus);
  893. tClus.szTableName = "ClusterDirectory";
  894. tClus.szTemplateTableName = NULL;
  895. tClus.ulPages = 0;
  896. tClus.ulDensity = 100;
  897. tClus.rgcolumncreate = &cClus[0];
  898. tClus.cColumns = NUM_CLUSDIRCOLUMNS;
  899. tClus.rgindexcreate = NULL;
  900. tClus.cIndexes = 0;
  901. tClus.grbit = JET_bitTableCreateFixedDDL;
  902. for (count = 0; count < NUM_CLUSDIRCOLUMNS; count++) {
  903. cClus[count].cbStruct = sizeof(JET_COLUMNCREATE);
  904. cClus[count].szColumnName = ClusterDirectoryColumns[count].szColumnName;
  905. cClus[count].coltyp = ClusterDirectoryColumns[count].coltyp;
  906. cClus[count].cbMax = ClusterDirectoryColumns[count].colMaxLen;
  907. cClus[count].grbit = 0;
  908. cClus[count].pvDefault = NULL;
  909. cClus[count].cbDefault = 0;
  910. cClus[count].cp = 1200;
  911. cClus[count].columnid = 0;
  912. cClus[count].err = JET_errSuccess;
  913. }
  914. // Set the autoincrement column to autoincrement
  915. cClus[0].grbit |= JET_bitColumnAutoincrement;
  916. CALL(JetCreateTableColumnIndex(sesid, dbid, &tClus));
  917. for (count = 0; count < NUM_CLUSDIRCOLUMNS; count++) {
  918. clusdircolumnid[count] = cClus[count].columnid;
  919. }
  920. clusdirtableid = tClus.tableid;
  921. // Create Cluster Name index.
  922. CALL(JetCreateIndex(sesid, clusdirtableid, "ClusNameIndex",
  923. JET_bitIndexUnique, "+ClusterName\0", sizeof("+ClusterName\0"),
  924. 100));
  925. // Create cluster ID index.
  926. CALL(JetCreateIndex(sesid, clusdirtableid, "ClusIDIndex", 0,
  927. "+ClusterID\0", sizeof("+ClusterID\0"), 100));
  928. CALL(JetCommitTransaction(sesid, 0));
  929. // Tables were opened with exclusive access from CreateTableColumnIndex.
  930. // Close them now.
  931. NormalExit:
  932. CALL(JetCloseTable(sesid, sessdirtableid));
  933. CALL(JetCloseTable(sesid, servdirtableid));
  934. CALL(JetCloseTable(sesid, clusdirtableid));
  935. CALL(JetCloseDatabase(sesid, dbid, 0));
  936. CALL(JetEndSession(sesid, 0));
  937. LocalFree(SA.lpSecurityDescriptor);
  938. SA.lpSecurityDescriptor = NULL;
  939. #ifdef DBG
  940. OutputAllTables();
  941. #endif // DBG
  942. return 0;
  943. HandleError:
  944. if (sesid != JET_sesidNil) {
  945. // Can't really recover. Just bail out.
  946. (VOID) JetRollback(sesid, JET_bitRollbackAll);
  947. // Force the session closed
  948. (VOID) JetEndSession(sesid, JET_bitForceSessionClosed);
  949. }
  950. LocalFree(SA.lpSecurityDescriptor);
  951. SA.lpSecurityDescriptor = NULL;
  952. PostSessDirErrorValueEvent(EVENT_JET_COULDNT_INIT, err);
  953. exit(1);
  954. }
  955. /****************************************************************************/
  956. // DISCleanupGlobals
  957. //
  958. // Common cleanup code for SQL and Jet code paths.
  959. /****************************************************************************/
  960. void DISCleanupGlobals()
  961. {
  962. if (g_hStopServiceEvent != NULL) {
  963. CloseHandle(g_hStopServiceEvent);
  964. g_hStopServiceEvent = NULL;
  965. }
  966. if (g_hFileOutput != INVALID_HANDLE_VALUE) {
  967. if (CloseHandle(g_hFileOutput) == 0) {
  968. ERR((TB, "CloseHandle on output file failed: lasterr=0x%X",
  969. GetLastError()));
  970. }
  971. g_hFileOutput = INVALID_HANDLE_VALUE;
  972. }
  973. }
  974. #if 0
  975. /****************************************************************************/
  976. // DISCallSPForServer
  977. //
  978. // Generic function to call a stored procedure that takes a ServerAddress as an
  979. // argument.
  980. /****************************************************************************/
  981. void DISCallSPForServer(WCHAR *StoredProcName, WCHAR *ServerAddress) {
  982. HRESULT hr;
  983. ADOCommand *pCommand;
  984. ADOParameters *pParameters;
  985. ADORecordset *pResultRecordSet;
  986. hr = CreateADOStoredProcCommand(StoredProcName, &pCommand, &pParameters);
  987. if (SUCCEEDED(hr)) {
  988. hr = AddADOInputStringParam(ServerAddress, L"ServerAddress",
  989. pCommand, pParameters, FALSE);
  990. if (SUCCEEDED(hr)) {
  991. // Execute the command.
  992. hr = pCommand->Execute(NULL, NULL, adCmdStoredProc,
  993. &pResultRecordSet);
  994. if (SUCCEEDED(hr)) {
  995. pResultRecordSet->Release();
  996. } else {
  997. ERR((TB, "DISCallSPForServer: Failed Execute, hr = 0x%X",
  998. hr));
  999. }
  1000. }
  1001. else {
  1002. ERR((TB,"DISCallSPForServer: Failed add parameter, hr=0x%X", hr));
  1003. }
  1004. pParameters->Release();
  1005. pCommand->Release();
  1006. }
  1007. else {
  1008. ERR((TB,"DISCallSPForServer: Failed create cmd, hr=0x%X", hr));
  1009. }
  1010. }
  1011. #endif
  1012. /****************************************************************************/
  1013. // DISJetHandleDeadServer
  1014. //
  1015. // When a server is not responding, this function call sends the command to the
  1016. // Jet database to remove all entries pertaining to that server.
  1017. /****************************************************************************/
  1018. void DISJetHandleDeadServer(WCHAR *ServerAddress, DWORD ServerID) {
  1019. // FailureCount is initially set to 1, TRUE, to tell SetServerAITInternal
  1020. // to increment the failure count and return the resultant count.
  1021. DWORD FailureCount = 1;
  1022. TSSDSetServerAITInternal(ServerAddress, FALSE, &FailureCount);
  1023. TSDISErrorOut(L"Server %s (%d) not responding (Failure Count: %d).\n",
  1024. ServerAddress, ServerID, FailureCount);
  1025. if (FailureCount >= g_NumberFailedPingsBeforePurge)
  1026. TSSDPurgeServer(ServerID);
  1027. }
  1028. // TODO: Possible optimization: pass in ServerID
  1029. void DISJetSetServerPingSuccessful(WCHAR *ServerAddress) {
  1030. TSSDSetServerAITInternal(ServerAddress, TRUE, NULL);
  1031. }
  1032. #if 0
  1033. /****************************************************************************/
  1034. // DISSQLHandleDeadServer
  1035. //
  1036. // When a server is not responding, this function call sends the command to the
  1037. // database to execute SP_TSDISServerNotResponding.
  1038. /****************************************************************************/
  1039. void DISSQLHandleDeadServer(WCHAR *ServerAddress) {
  1040. DISCallSPForServer(L"SP_TSDISServerNotResponding", ServerAddress);
  1041. }
  1042. void DISSQLSetServerPingSuccessful(WCHAR *ServerAddress) {
  1043. DISCallSPForServer(L"SP_TSDISSetServerPingSuccessful", ServerAddress);
  1044. }
  1045. #endif
  1046. VOID DISCtrlHandler(DWORD opcode) {
  1047. switch(opcode)
  1048. {
  1049. //case SERVICE_CONTROL_PAUSE:
  1050. // pause
  1051. // g_DISStatus.dwCurrentState = SERVICE_PAUSED;
  1052. // break;
  1053. //case SERVICE_CONTROL_CONTINUE:
  1054. // continue
  1055. // g_DISStatus.dwCurrentState = SERVICE_RUNNING;
  1056. // break;
  1057. case SERVICE_CONTROL_STOP:
  1058. //stop
  1059. g_DISStatus.dwWin32ExitCode = 0;
  1060. g_DISStatus.dwCurrentState = SERVICE_STOP_PENDING;
  1061. g_DISStatus.dwCheckPoint = 0;
  1062. g_DISStatus.dwWaitHint = 0;
  1063. if (!SetServiceStatus(g_DISStatusHandle, &g_DISStatus)) {
  1064. ERR((TB, "SetServiceStatus failed"));
  1065. }
  1066. // Here is where to actually stop the service
  1067. SetEvent(g_hStopServiceEvent);
  1068. // Should I wait for that to complete?
  1069. g_DISStatus.dwCurrentState = SERVICE_STOPPED;
  1070. if (!SetServiceStatus(g_DISStatusHandle, &g_DISStatus)) {
  1071. ERR((TB, "SetServiceStatus failed"));
  1072. }
  1073. return;
  1074. case SERVICE_CONTROL_INTERROGATE:
  1075. // fall through to return current status
  1076. break;
  1077. default:
  1078. ERR((TB, "Unrecognized opcode to DISCtrlHandler - 0x%08x", opcode));
  1079. }
  1080. // send current status
  1081. if (!SetServiceStatus(g_DISStatusHandle, &g_DISStatus)) {
  1082. ERR((TB, "SetServiceStatus failed"));
  1083. }
  1084. }
  1085. void DISDirectoryIntegrityLoop() {
  1086. CVar varRows;
  1087. WCHAR *ServerAddress;
  1088. #if 0
  1089. WCHAR ServerAddressBuf[SERVER_ADDRESS_LENGTH];
  1090. #endif
  1091. WCHAR ServerAddressRows[10][SERVER_ADDRESS_LENGTH];
  1092. DWORD ServerIDs[10];
  1093. long NumSessionsReturned;
  1094. #if 0
  1095. HRESULT hr = S_OK;
  1096. #endif
  1097. SERVER_STATUS ServerStatus;
  1098. DWORD EventStatus;
  1099. #if 0
  1100. ServerAddress = ServerAddressBuf; // In SQL case, we need a static buffer
  1101. #endif
  1102. #if 0
  1103. TSDISErrorOut(L"%s active\n", g_bUseSQL ? L"Directory Integrity Service" :
  1104. L"Session Directory");
  1105. #endif
  1106. TSDISErrorOut(L"Session Directory Active\n");
  1107. // Loop forever
  1108. for ( ; ; ) {
  1109. // Retrieve set of servers that have disconnected sessions pending
  1110. // reconnects
  1111. #if 0
  1112. if (g_bUseSQL == FALSE)
  1113. #endif
  1114. DISJetGetServersPendingReconnects(&NumSessionsReturned,
  1115. ServerAddressRows, ServerIDs);
  1116. #if 0
  1117. else
  1118. DISSQLGetServersPendingReconnects(&NumSessionsReturned,
  1119. &varRows);
  1120. #endif
  1121. // For each server,
  1122. for (DWORD i = 0; i < (unsigned)NumSessionsReturned; i++) {
  1123. #if 0
  1124. if (g_bUseSQL == FALSE)
  1125. #endif
  1126. ServerAddress = ServerAddressRows[i];
  1127. #if 0
  1128. else
  1129. hr = GetRowArrayStringField(varRows.parray, i, 0,
  1130. ServerAddress, sizeof(ServerAddressBuf) /
  1131. sizeof(WCHAR) - 1);
  1132. if (FAILED(hr)) {
  1133. ERR((TB,"DISDirectoryIntegrityLoop: Row %u returned hr=0x%X",
  1134. i, hr));
  1135. }
  1136. #endif
  1137. ServerStatus = DISGetServerStatus(ServerAddress);
  1138. // if the server does not respond, handle dead server.
  1139. // The function we call will do the right thing, which may be
  1140. // to purge immediately, or may be to simply increment a failure
  1141. // count.
  1142. if (ServerStatus == NotResponding) {
  1143. #if 0
  1144. if (FALSE == g_bUseSQL)
  1145. #endif
  1146. DISJetHandleDeadServer(ServerAddress, ServerIDs[i]);
  1147. #if 0
  1148. else
  1149. DISSQLHandleDeadServer(ServerAddress);
  1150. #endif
  1151. #ifdef DBG
  1152. OutputAllTables();
  1153. #endif // DBG
  1154. }
  1155. // else stop pinging
  1156. else if (ServerStatus == Responding) {
  1157. #if 0
  1158. if (FALSE == g_bUseSQL)
  1159. #endif
  1160. DISJetSetServerPingSuccessful(ServerAddress);
  1161. #if 0
  1162. else
  1163. DISSQLSetServerPingSuccessful(ServerAddress);
  1164. #endif
  1165. }
  1166. else {
  1167. ERR((TB, "DISDirectoryIntegrityLoop: ServerStatus enum has bad "
  1168. "value %d", ServerStatus));
  1169. }
  1170. }
  1171. // Wait DISNumberSecondsBetweenPings
  1172. EventStatus = WaitForSingleObjectEx(g_hStopServiceEvent,
  1173. DISNumberSecondsBetweenPings * 1000, FALSE);
  1174. if (EventStatus == WAIT_TIMEOUT) {
  1175. // do normal stuff
  1176. continue;
  1177. } else if (EventStatus == WAIT_OBJECT_0) {
  1178. // the event was signaled -- clean up
  1179. break;
  1180. } else if (EventStatus == -1) {
  1181. // there is an error
  1182. } else {
  1183. // weird output from that function
  1184. }
  1185. }
  1186. }
  1187. #if 0
  1188. /****************************************************************************/
  1189. // DISSQLStart
  1190. //
  1191. // Service main entry point for when the service is configured to verify
  1192. // SQL tables.
  1193. /****************************************************************************/
  1194. VOID DISSQLStart(DWORD argc, LPTSTR *argv) {
  1195. HRESULT hr = S_OK;
  1196. // unreferenced parameters
  1197. argv;
  1198. argc;
  1199. g_DISStatus.dwServiceType = SERVICE_WIN32;
  1200. g_DISStatus.dwCurrentState = SERVICE_START_PENDING;
  1201. g_DISStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
  1202. g_DISStatus.dwWin32ExitCode = 0;
  1203. g_DISStatus.dwServiceSpecificExitCode = 0;
  1204. g_DISStatus.dwCheckPoint = 0;
  1205. g_DISStatus.dwWaitHint = 0;
  1206. g_DISStatusHandle = RegisterServiceCtrlHandler(
  1207. _T("Directory Integrity Service"), DISCtrlHandler);
  1208. if (g_DISStatusHandle == (SERVICE_STATUS_HANDLE)0) {
  1209. ERR((TB, "DISSQLStart: RegisterServiceCtrlHandler failed"));
  1210. goto ExitFunc;
  1211. }
  1212. // Initialization code goes here
  1213. hr = DISSQLInitialize();
  1214. if (FAILED(hr)) {
  1215. ERR((TB, "DISSQLStart: DISSQLInitialize failed"));
  1216. goto PostRegisterService;
  1217. }
  1218. g_DISStatus.dwCurrentState = SERVICE_RUNNING;
  1219. g_DISStatus.dwCheckPoint = 1;
  1220. if (!SetServiceStatus(g_DISStatusHandle, &g_DISStatus)) {
  1221. ERR((TB, "DISSQLStart: SetServiceHandler failed"));
  1222. goto PostRegisterService;
  1223. }
  1224. DISDirectoryIntegrityLoop();
  1225. PostRegisterService:
  1226. g_DISStatus.dwCurrentState = SERVICE_STOPPED;
  1227. g_DISStatus.dwCheckPoint = 2;
  1228. SetServiceStatus(g_DISStatusHandle, &g_DISStatus);
  1229. ExitFunc:
  1230. DISCleanupGlobals();
  1231. }
  1232. #endif
  1233. /****************************************************************************/
  1234. // DISJetStart
  1235. //
  1236. // Service main entry point for when the service is configured to act as
  1237. // an RPC server and use Jet for all session directory transactions.
  1238. /****************************************************************************/
  1239. VOID DISJetStart(DWORD argc, LPTSTR *argv) {
  1240. RPC_STATUS Status;
  1241. RPC_BINDING_VECTOR *pBindingVector = 0;
  1242. RPC_POLICY rpcpol = {sizeof(rpcpol), 0, 0};
  1243. // unreferenced parameters
  1244. argv;
  1245. argc;
  1246. g_DISStatus.dwServiceType = SERVICE_WIN32;
  1247. g_DISStatus.dwCurrentState = SERVICE_START_PENDING;
  1248. g_DISStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
  1249. g_DISStatus.dwWin32ExitCode = 0;
  1250. g_DISStatus.dwServiceSpecificExitCode = 0;
  1251. g_DISStatus.dwCheckPoint = 0;
  1252. g_DISStatus.dwWaitHint = 0;
  1253. if (g_bDebug == FALSE) {
  1254. g_DISStatusHandle = RegisterServiceCtrlHandler(
  1255. _T("Directory Integrity Service"), DISCtrlHandler);
  1256. if (g_DISStatusHandle == (SERVICE_STATUS_HANDLE)0) {
  1257. ERR((TB, "DISJetStart: RegisterServiceCtrlHandler failed"));
  1258. goto ExitFunc;
  1259. }
  1260. }
  1261. // Init the RPC server interface.
  1262. // Register the named pipe. This uses NT domain authentication.
  1263. /*
  1264. Status = RpcServerUseProtseqEp(
  1265. L"ncacn_np", // Protocol Sequence
  1266. NUM_JETRPC_THREADS, // Maximum calls at one time
  1267. L"\\pipe\\TSSD_Jet_RPC_Service", // Endpoint
  1268. NULL); // Security
  1269. */
  1270. Status = RpcServerUseProtseqEx(L"ncacn_ip_tcp", 3, 0, &rpcpol);
  1271. if (Status != RPC_S_OK) {
  1272. ERR((TB,"DISJetStart: Error %d RpcUseProtseqEp on ncacn_ip_tcp",
  1273. Status));
  1274. PostSessDirErrorValueEvent(EVENT_FAIL_RPC_INIT_USEPROTSEQ, Status);
  1275. goto PostRegisterService;
  1276. }
  1277. // Register our interface handle (found in jetrpc.h).
  1278. Status = RpcServerRegisterIf(TSSDJetRPC_ServerIfHandle, NULL, NULL);
  1279. if (Status != RPC_S_OK) {
  1280. ERR((TB,"DISJetStart: Error %d RegIf", Status));
  1281. PostSessDirErrorValueEvent(EVENT_FAIL_RPC_INIT_REGISTERIF, Status);
  1282. goto PostRegisterService;
  1283. }
  1284. Status = RpcServerInqBindings(&pBindingVector);
  1285. if (Status != RPC_S_OK) {
  1286. ERR((TB,"DISJetStart: Error %d InqBindings", Status));
  1287. PostSessDirErrorValueEvent(EVENT_FAIL_RPC_INIT_INQBINDINGS, Status);
  1288. goto PostRegisterService;
  1289. }
  1290. Status = RpcEpRegister(TSSDJetRPC_ServerIfHandle, pBindingVector, 0, 0);
  1291. // TODO: Probably need to unregister, maybe delete some binding vector.
  1292. if (Status != RPC_S_OK) {
  1293. ERR((TB,"DISJetStart: Error %d EpReg", Status));
  1294. PostSessDirErrorValueEvent(EVENT_FAIL_RPC_INIT_EPREGISTER, Status);
  1295. goto PostRegisterService;
  1296. }
  1297. Status = RpcServerRegisterAuthInfo(0, RPC_C_AUTHN_WINNT, 0, 0);
  1298. if (Status != RPC_S_OK) {
  1299. ERR((TB,"DISJetStart: Error %d RegAuthInfo", Status));
  1300. PostSessDirErrorValueEvent(EVENT_FAIL_RPC_INIT_REGAUTHINFO, Status);
  1301. goto PostRegisterService;
  1302. }
  1303. // Now initialize the JET database
  1304. DISJetInitialize();
  1305. // Now do the RPC listen to service calls
  1306. Status = RpcServerListen(1, NUM_JETRPC_THREADS, TRUE);
  1307. if (Status != RPC_S_OK) {
  1308. ERR((TB,"DISJetStart: Error %d ServerListen", Status));
  1309. PostSessDirErrorValueEvent(EVENT_FAIL_RPC_LISTEN, Status);
  1310. goto PostRegisterService;
  1311. }
  1312. // We are now up.
  1313. g_DISStatus.dwCurrentState = SERVICE_RUNNING;
  1314. g_DISStatus.dwCheckPoint = 1;
  1315. if (g_bDebug == FALSE)
  1316. SetServiceStatus(g_DISStatusHandle, &g_DISStatus);
  1317. // Now we have the RPC server running, we can just wait for the
  1318. // service-stop event to be fired to let us know we need to exit.
  1319. // We do this inside the Directory Integrity Loop.
  1320. DISDirectoryIntegrityLoop();
  1321. // Time to clean up.
  1322. // Kill the RPC listener.
  1323. RpcServerUnregisterIf(TSSDJetRPC_ServerIfHandle, NULL, NULL);
  1324. PostRegisterService:
  1325. g_DISStatus.dwCurrentState = SERVICE_STOPPED;
  1326. g_DISStatus.dwCheckPoint = 2;
  1327. if (g_bDebug == FALSE)
  1328. SetServiceStatus(g_DISStatusHandle, &g_DISStatus);
  1329. ExitFunc:
  1330. DISCleanupGlobals();
  1331. }
  1332. /****************************************************************************/
  1333. // DISInstallService
  1334. //
  1335. // Used to install the service, returns 0 on success, nonzero otherwise.
  1336. /****************************************************************************/
  1337. int DISInstallService() {
  1338. WCHAR wzModulePathname[MAX_PATH];
  1339. SC_HANDLE hSCM = NULL, hService = NULL;
  1340. if (0 != GetModuleFileNameW(NULL, wzModulePathname, MAX_PATH)) {
  1341. hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
  1342. if (hSCM != NULL) {
  1343. hService = CreateServiceW(hSCM, L"Directory Integrity Service",
  1344. L"Directory Integrity Service", 0,
  1345. SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START,
  1346. SERVICE_ERROR_NORMAL, wzModulePathname, NULL, NULL, NULL,
  1347. NULL, NULL);
  1348. if (hService != NULL) {
  1349. CloseServiceHandle(hService);
  1350. CloseServiceHandle(hSCM);
  1351. } else {
  1352. ERR((TB, "CreateService failed, error = 0x%X", GetLastError()));
  1353. CloseServiceHandle(hSCM);
  1354. return -1;
  1355. }
  1356. } else {
  1357. ERR((TB, "OpenSCManager failed, error = 0x%X", GetLastError()));
  1358. return -1;
  1359. }
  1360. } else {
  1361. ERR((TB, "GetModuleFileNameW failed, error = 0x%X", GetLastError()));
  1362. return -1;
  1363. }
  1364. return 0;
  1365. }
  1366. /****************************************************************************/
  1367. // DISRemoveService()
  1368. //
  1369. // Used to remove the service, returns 0 on success, nonzero otherwise.
  1370. /****************************************************************************/
  1371. int DISRemoveService() {
  1372. SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
  1373. if (hSCM != NULL) {
  1374. // Open this service for DELETE access
  1375. SC_HANDLE hService = OpenServiceW(hSCM, L"Directory Integrity Service",
  1376. DELETE);
  1377. if (hService != NULL) {
  1378. // Remove this service from the SCM's database.
  1379. DeleteService(hService);
  1380. CloseServiceHandle(hService);
  1381. CloseServiceHandle(hSCM);
  1382. return 0;
  1383. } else {
  1384. ERR((TB, "Failure opening service for delete, error = 0x%X",
  1385. GetLastError()));
  1386. }
  1387. CloseServiceHandle(hService);
  1388. } else {
  1389. ERR((TB, "Failure opening SC Manager, error = 0x%X", GetLastError()));
  1390. }
  1391. return -1;
  1392. }
  1393. // Reads a DWORD value out of the registry.
  1394. //
  1395. // In:
  1396. // hKey - an open HKEY
  1397. // RegValName - the name of the registry value
  1398. // pValue - pointer to the value. The value will be set to the registry value
  1399. // if the registry operation is a success, else it will remain untouched.
  1400. //
  1401. // Out:
  1402. // 0 if success, nonzero otherwise
  1403. int ReadRegVal(HKEY hKey, WCHAR *RegValName, DWORD *pValue)
  1404. {
  1405. DWORD RegRetVal;
  1406. DWORD Type, Temp, Size;
  1407. Size = sizeof(Temp);
  1408. RegRetVal = RegQueryValueExW(hKey, RegValName, NULL, &Type,
  1409. (BYTE *)&Temp, &Size);
  1410. if (RegRetVal == ERROR_SUCCESS) {
  1411. *pValue = Temp;
  1412. return 0;
  1413. }
  1414. else {
  1415. TRC1((TB, "TSSDIS: Failed RegQuery for %S - "
  1416. "err=%u, DataSize=%u, type=%u\n",
  1417. RegValName, RegRetVal, Size, Type));
  1418. return -1;
  1419. }
  1420. }
  1421. // Reads a Unicode text value out of the registry.
  1422. //
  1423. // hKey (IN) - an open HKEY
  1424. // RegValName (IN) - the name of the registry value
  1425. // pText (IN/OUT) - pointer to the buffer to which to write.
  1426. // cbData (IN) - size of buffer IN BYTES
  1427. //
  1428. // returns 0 if success, nonzero otherwise.
  1429. int ReadRegTextVal(HKEY hKey, WCHAR *RegValName, WCHAR *pText, DWORD cbData)
  1430. {
  1431. DWORD RegRetVal;
  1432. DWORD Type, Size;
  1433. Size = cbData;
  1434. RegRetVal = RegQueryValueExW(hKey, RegValName, NULL, &Type,
  1435. (BYTE *)pText, &Size);
  1436. if (RegRetVal == ERROR_SUCCESS) {
  1437. return 0;
  1438. }
  1439. else {
  1440. TRC1((TB, "TSSDIS: Failed RegQuery for %S - err=%u, DataSize=%u, "
  1441. "type=%u\n", RegValName, RegRetVal, Size, Type));
  1442. return -1;
  1443. }
  1444. }
  1445. // Reads configuration from the registry and sets global variables.
  1446. void ReadConfigAndSetGlobals()
  1447. {
  1448. DWORD RegRetVal;
  1449. HKEY hKey;
  1450. DWORD Temp;
  1451. WCHAR WorkingDirectory[MAX_PATH];
  1452. // Open the service settings regkey and grab the UseJet flag.
  1453. // Absence of the key or the setting means no jet.
  1454. #if 0
  1455. g_bUseSQL = FALSE;
  1456. #endif
  1457. RegRetVal = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  1458. L"Software\\Microsoft\\TermServSessDir", 0, KEY_READ, &hKey);
  1459. if (RegRetVal == ERROR_SUCCESS) {
  1460. // With each of these calls, an error is non-fatal.
  1461. #if 0
  1462. // Query UseSQL value.
  1463. ReadRegVal(hKey, L"UseSQL", &g_bUseSQL);
  1464. #endif
  1465. // Query PingMode value. Note this is an enum so sending the variable
  1466. // in directly is illegal.
  1467. if (ReadRegVal(hKey, L"PingMode", &Temp) == 0) {
  1468. // Make sure this is a legal value for the enum.
  1469. if (Temp > AlwaysFail)
  1470. Temp = NormalMode;
  1471. g_PingMode = (PingMode) Temp;
  1472. }
  1473. // Query TraceOutputMode value. As above, enum means don't set it
  1474. // directly.
  1475. if (ReadRegVal(hKey, L"TraceOutputMode", &Temp) == 0) {
  1476. // Make sure this is a legal value for the enum.
  1477. if (Temp > FileOutput)
  1478. Temp = NoTraceOutput;
  1479. g_TraceOutputMode = (TraceOutputMode) Temp;
  1480. }
  1481. // Query NumberFailedPingsBeforePurge.
  1482. ReadRegVal(hKey, L"NumberFailedPingsBeforePurge",
  1483. &g_NumberFailedPingsBeforePurge);
  1484. // Query TimeBetweenPings.
  1485. ReadRegVal(hKey, L"TimeBetweenPings", &DISNumberSecondsBetweenPings);
  1486. // Query TimeServerSilentBeforePing.
  1487. if (ReadRegVal(hKey, L"TimeServerSilentBeforePing", &Temp) == 0) {
  1488. g_TimeServerSilentBeforePing = (ULONGLONG) Temp *
  1489. FILETIME_INTERVAL_TO_SECONDS_MULTIPLIER;
  1490. }
  1491. // Query Working Directory
  1492. if (ReadRegTextVal(hKey, L"WorkingDirectory", WorkingDirectory,
  1493. sizeof(WorkingDirectory)) == 0) {
  1494. if (SetCurrentDirectory(WorkingDirectory) == 0) {
  1495. DWORD Err;
  1496. Err = GetLastError();
  1497. PostSessDirErrorValueEvent(EVENT_PROBLEM_SETTING_WORKDIR, Err);
  1498. ERR((TB, "TERMSRV: Unable to set directory to value read from "
  1499. "registry. LastErr=0x%X", Err));
  1500. }
  1501. }
  1502. RegCloseKey(hKey);
  1503. // Now, if in file output mode, open the file.
  1504. if (g_TraceOutputMode == FileOutput) {
  1505. g_hFileOutput = CreateFile(DEBUG_LOG_FILENAME, GENERIC_WRITE,
  1506. FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
  1507. NULL);
  1508. if (g_hFileOutput == INVALID_HANDLE_VALUE) {
  1509. ERR((TB, "Could not open debug log file, lasterror=0x%X",
  1510. GetLastError()));
  1511. g_TraceOutputMode = NoTraceOutput;
  1512. }
  1513. else {
  1514. DWORD dwRetVal = 0;
  1515. // Set the insertion point to the end of the file and output
  1516. // something.
  1517. dwRetVal = SetFilePointer(g_hFileOutput, 0, NULL, FILE_END);
  1518. if (dwRetVal == INVALID_SET_FILE_POINTER) {
  1519. ERR((TB, "Could not set to end of file, lasterror=0x%X",
  1520. GetLastError()));
  1521. g_TraceOutputMode = NoTraceOutput;
  1522. }
  1523. else {
  1524. DWORD dwBytesWritten = 0;
  1525. char *pszIntro = "\n\nNEW INSTANCE\n";
  1526. if (WriteFile(g_hFileOutput, pszIntro,
  1527. (DWORD) strlen(pszIntro), &dwBytesWritten,
  1528. NULL) == 0) {
  1529. ERR((TB, "WriteFile failed, lasterr=0x%X",
  1530. GetLastError()));
  1531. }
  1532. }
  1533. }
  1534. }
  1535. }
  1536. else {
  1537. WRN((TB,"TERMSRV: Unable to open settings key in HKLM, "
  1538. "lasterr=0x%X", GetLastError()));
  1539. }
  1540. }
  1541. int __cdecl main() {
  1542. int nArgc;
  1543. WCHAR **ppArgv = (WCHAR **) CommandLineToArgvW(GetCommandLineW(), &nArgc);
  1544. BOOL fStartService = (nArgc < 2);
  1545. int i;
  1546. HANDLE hMutex;
  1547. if ((fStartService == FALSE) && (ppArgv == NULL)) {
  1548. PostSessDirErrorValueEvent(EVENT_NO_COMMANDLINE, GetLastError());
  1549. return -1;
  1550. }
  1551. // Only allow one session directory at a time. System will close the
  1552. // handle automatically when the process terminates.
  1553. hMutex = CreateMutex(NULL, FALSE,
  1554. _T("Global\\Windows Terminal Server Session Directory"));
  1555. if (hMutex == NULL) {
  1556. // Handle creation failed, not because it already existed.
  1557. PostSessDirErrorValueEvent(EVENT_PROBLEM_CREATING_MUTEX,
  1558. GetLastError());
  1559. return -1;
  1560. }
  1561. if (GetLastError() == ERROR_ALREADY_EXISTS) {
  1562. // Already a session directory out there.
  1563. PostSessDirErrorValueEvent(EVENT_TWO_SESSDIRS, 0);
  1564. return -1;
  1565. }
  1566. SERVICE_TABLE_ENTRY DispatchTable[] =
  1567. {
  1568. { _T("Directory Integrity Service"), DISJetStart }, // Default to the
  1569. // Jet version.
  1570. { NULL, NULL }
  1571. };
  1572. for (i = 1; i < nArgc; i++) {
  1573. if ((ppArgv[i][0] == '-') || (ppArgv[i][0] == '/')) {
  1574. if (wcscmp(&ppArgv[i][1], L"install") == 0) {
  1575. if (DISInstallService()) {
  1576. ERR((TB, "Could not install service"));
  1577. }
  1578. }
  1579. if (wcscmp(&ppArgv[i][1], L"remove") == 0) {
  1580. if (DISRemoveService()) {
  1581. ERR((TB, "Could not remove service"));
  1582. }
  1583. }
  1584. if (wcscmp(&ppArgv[i][1], L"debug") == 0) {
  1585. TSDISErrorOut(L"Debugging Jet-based Session Directory\n");
  1586. g_hStopServiceEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  1587. g_bDebug = TRUE;
  1588. // Log to stdout by default in this mode, but can be
  1589. // overridden by the registry.
  1590. g_TraceOutputMode = StdOutput;
  1591. ReadConfigAndSetGlobals();
  1592. SetConsoleCtrlHandler(DISDebugControlHandler, TRUE);
  1593. DISJetStart(nArgc, ppArgv);
  1594. }
  1595. }
  1596. }
  1597. HeapFree(GetProcessHeap(), 0, (PVOID) ppArgv);
  1598. if (fStartService) {
  1599. // Stop event - signals for the ServiceMain thread to exit.
  1600. g_hStopServiceEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  1601. ReadConfigAndSetGlobals();
  1602. #if 0
  1603. if (g_bUseSQL) {
  1604. // Switch from the default to the SQL service start.
  1605. DispatchTable[0].lpServiceProc = DISSQLStart;
  1606. }
  1607. #endif
  1608. if (!StartServiceCtrlDispatcher(DispatchTable)) {
  1609. #ifdef DBG
  1610. DWORD dw = GetLastError();
  1611. #endif // DBG
  1612. ERR((TB, "Could not start service control dispatcher, error 0x%X",
  1613. dw));
  1614. }
  1615. }
  1616. return 0;
  1617. }
  1618. #pragma warning (pop)