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.

2864 lines
97 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. #include "resource.h"
  11. #include "tssdshrd.h"
  12. #include "sdrpc.h"
  13. #pragma warning (push, 4)
  14. #define SERVER_ADDRESS_LENGTH 64
  15. #define NUM_JETRPC_THREADS 10
  16. #define MAX_DRIVE_LETTER_LENGTH 24
  17. #define SD_QUERY_ENDPOINT_NAME L"TSSessionDirectoryQueryApi"
  18. // Number of 100-nanosecond periods in 1 second.
  19. #define FILETIME_INTERVAL_TO_SECONDS_MULTIPLIER 10000000
  20. #define DEBUG_LOG_FILENAME L"tssdis.log"
  21. #define REG_SESSION_DIRECTROY_CONTROL L"System\\CurrentControlSet\\Services\\Tssdis\\Parameters"
  22. #define MAX_TSSERVERS_TO_RECOVER 0xFFFF
  23. #define NO_RECOVER_WHEN_START 0
  24. //do we recover previous jet database when starting SD
  25. DWORD g_RecoverWhenStart = 1;
  26. // if asking TS to repopulate session when it rejoins
  27. // RepoluateSession is set to FALSE only when tssdis is
  28. // running on failover cluster and it's restarted within a time limit
  29. BOOL g_RepopulateSession = TRUE;
  30. // If tssdis is not restarted within this time (3 mins), we think the db is not consistent
  31. ULONGLONG g_TimeLimitToDeleteDB = 3 * 60 * FILETIME_INTERVAL_TO_SECONDS_MULTIPLIER;
  32. //
  33. // Global for fail-over cluster
  34. //
  35. DWORD g_dwClusterState;
  36. // Cluster network name
  37. WCHAR *g_ClusterNetworkName = NULL;
  38. // Cluster account token
  39. HANDLE g_hClusterToken = NULL;
  40. // File Handle for the database timestamp file
  41. HANDLE g_hTimeFile;
  42. #define SDLOCALGROUPNAMELENGTH 64
  43. #define SDLOCALGROUPDESLENGTH 128
  44. const DIRCOLUMNS SessionDirectoryColumns[NUM_SESSDIRCOLUMNS] = {
  45. { "UserName", JET_coltypLongText, 512 },
  46. { "Domain", JET_coltypLongText, 254 },
  47. { "ServerID", JET_coltypLong, 0 },
  48. { "SessionID", JET_coltypLong, 0 },
  49. { "TSProtocol", JET_coltypLong, 0 },
  50. { "CreateTimeLow", JET_coltypLong, 0 },
  51. { "CreateTimeHigh", JET_coltypLong, 0 },
  52. { "DisconnectTimeLow", JET_coltypLong, 0 },
  53. { "DisconnectTimeHigh", JET_coltypLong, 0 },
  54. { "ApplicationType", JET_coltypLongText, 512 },
  55. { "ResolutionWidth", JET_coltypLong, 0 },
  56. { "ResolutionHeight", JET_coltypLong, 0 },
  57. { "ColorDepth", JET_coltypLong, 0 },
  58. { "State", JET_coltypBit, 0 },
  59. };
  60. const DIRCOLUMNS ServerDirectoryColumns[NUM_SERVDIRCOLUMNS] = {
  61. { "ServerID", JET_coltypLong, 0 },
  62. { "ServerAddress", JET_coltypLongText, 128 },
  63. { "ClusterID", JET_coltypLong, 0 },
  64. { "AlmostInTimeLow", JET_coltypLong, 0 },
  65. { "AlmostInTimeHigh", JET_coltypLong, 0 },
  66. { "NumberFailedPings", JET_coltypLong, 0 },
  67. { "SingleSessionMode", JET_coltypBit, 0 },
  68. { "ServerDNSName", JET_coltypLongText, 128 },
  69. };
  70. const DIRCOLUMNS ClusterDirectoryColumns[NUM_CLUSDIRCOLUMNS] = {
  71. { "ClusterID", JET_coltypLong, 0 },
  72. { "ClusterName", JET_coltypLongText, 128 },
  73. { "SingleSessionMode", JET_coltypBit, 0 },
  74. };
  75. JET_COLUMNID sesdircolumnid[NUM_SESSDIRCOLUMNS];
  76. JET_COLUMNID servdircolumnid[NUM_SERVDIRCOLUMNS];
  77. JET_COLUMNID clusdircolumnid[NUM_CLUSDIRCOLUMNS];
  78. JET_INSTANCE g_instance = 0;
  79. ADOConnection *g_pConnection;
  80. HANDLE g_hStopServiceEvent;
  81. SERVICE_STATUS g_DISStatus;
  82. SERVICE_STATUS_HANDLE g_DISStatusHandle;
  83. BOOL g_bDebug = FALSE;
  84. PSID g_pSid = NULL; //Sid for SD local group
  85. PSID g_pAdminSid = NULL; //Sid for admin on the SD server
  86. // Registry settings follow
  87. #if 0
  88. DWORD g_bUseSQL = 0;
  89. #endif
  90. enum TraceOutputMode {
  91. NoTraceOutput,
  92. DebugPrintOutput,
  93. StdOutput,
  94. FileOutput
  95. };
  96. TraceOutputMode g_TraceOutputMode = NoTraceOutput;
  97. HANDLE g_hFileOutput = INVALID_HANDLE_VALUE;
  98. // For debugging purposes, we can set the ping mode to something other than
  99. // WinStationOpenServer using the registry.
  100. enum PingMode {
  101. NormalMode,
  102. AlwaysSucceed,
  103. AlwaysFail
  104. };
  105. PingMode g_PingMode = NormalMode;
  106. ULONGLONG g_TimeServerSilentBeforePing = 60 * FILETIME_INTERVAL_TO_SECONDS_MULTIPLIER;
  107. DWORD DISNumberSecondsBetweenPings = 10;
  108. DWORD g_NumberFailedPingsBeforePurge = 3;
  109. #ifdef DBG
  110. void OutputAllTables();
  111. #endif
  112. void DISDeleteLocalGroupSecDes();
  113. RPC_STATUS SDInitQueryRPC(VOID);
  114. void TSDISErrorOut(wchar_t *format_string, ...)
  115. {
  116. // Immediately bail out if we are in NoTraceOutput mode.
  117. if (g_TraceOutputMode == NoTraceOutput) {
  118. return;
  119. }
  120. else {
  121. // Otherwise, do the right thing.
  122. wchar_t TotalString[MAX_DEBUG_STRING_LENGTH + MAX_THREADIDSTR_LENGTH];
  123. wchar_t *ThreadIDString = TotalString;
  124. wchar_t *DebugOutString = NULL;
  125. va_list args;
  126. int ThreadStrLength;
  127. // Get the current thread ID
  128. ThreadStrLength = _snwprintf(ThreadIDString, MAX_THREADIDSTR_LENGTH,
  129. L"%d: ", GetCurrentThreadId());
  130. // Set the place for the out string to after the string, or after the whole
  131. // buffer if _snwprintf didn't have enough space.
  132. if (ThreadStrLength > 0)
  133. DebugOutString = &TotalString[ThreadStrLength];
  134. else
  135. DebugOutString = &TotalString[MAX_THREADIDSTR_LENGTH];
  136. va_start(args, format_string);
  137. // Create the debug output string.
  138. _vsnwprintf(DebugOutString, MAX_DEBUG_STRING_LENGTH, format_string, args);
  139. DebugOutString[MAX_DEBUG_STRING_LENGTH - 1] = '\0';
  140. // Output to the correct place.
  141. switch (g_TraceOutputMode) {
  142. case DebugPrintOutput:
  143. OutputDebugString(TotalString);
  144. break;
  145. case StdOutput:
  146. wprintf(TotalString);
  147. break;
  148. case FileOutput:
  149. {
  150. char TotalStringA[MAX_DEBUG_STRING_LENGTH +
  151. MAX_THREADIDSTR_LENGTH];
  152. DWORD dwBytes = 0;
  153. // Convert to ANSI.
  154. dwBytes = WideCharToMultiByte(CP_ACP, 0, TotalString,
  155. -1, TotalStringA, MAX_DEBUG_STRING_LENGTH +
  156. MAX_THREADIDSTR_LENGTH, 0, 0);
  157. // Don't write the terminating NULL (3rd argument)!
  158. // Ignore return value.
  159. WriteFile(g_hFileOutput, TotalStringA, dwBytes - 1,
  160. &dwBytes, NULL);
  161. break;
  162. }
  163. }
  164. va_end(args);
  165. }
  166. }
  167. // TSDISErrorTimeOut
  168. //
  169. // This function is used to output a single FILETIME low, high pair. The format
  170. // string, given as the first argument, MUST specify a %s format specifier for
  171. // where the date/time should go.
  172. //
  173. // Example:
  174. // TSDISErrorTimeOut(L"The date and time are %s\n", CurrTimeLow, CurrTimeHigh);
  175. void TSDISErrorTimeOut(wchar_t *format_string, DWORD TimeLow, DWORD TimeHigh)
  176. {
  177. if (g_TraceOutputMode == NoTraceOutput) {
  178. return;
  179. }
  180. else {
  181. // We just need to convert the FILETIME we have into a SYSTEMTIME,
  182. // and then output the SYSTEMTIME using GetDateFormat and GetTimeFormat.
  183. FILETIME ft;
  184. SYSTEMTIME st;
  185. SYSTEMTIME stloc;
  186. int offset = 0;
  187. wchar_t DateString[MAX_DATE_TIME_STRING_LENGTH];
  188. ft.dwLowDateTime = TimeLow;
  189. ft.dwHighDateTime = TimeHigh;
  190. if (FileTimeToSystemTime(&ft, &st) != 0) {
  191. // st is the system time.
  192. // UTC format?
  193. if (SystemTimeToTzSpecificLocalTime(NULL, &st, &stloc) != 0) {
  194. offset = GetDateFormat(LOCALE_SYSTEM_DEFAULT, DATE_SHORTDATE,
  195. &stloc, NULL, DateString, MAX_DATE_TIME_STRING_LENGTH);
  196. if (offset != 0) {
  197. // Turn the terminating NULL into a space.
  198. DateString[offset - 1] = ' ';
  199. // Write the time after the space.
  200. offset = GetTimeFormat(LOCALE_SYSTEM_DEFAULT, 0, &stloc,
  201. NULL, &DateString[offset],
  202. MAX_DATE_TIME_STRING_LENGTH - offset);
  203. if (offset != 0) {
  204. // Output the string.
  205. TSDISErrorOut(format_string, DateString);
  206. }
  207. }
  208. }
  209. }
  210. }
  211. }
  212. // This function is duplicated from \nt\termsrv\winsta\server\sessdir.cpp.
  213. //
  214. // PostSessDirErrorValueEvent
  215. //
  216. // Utility function used to create a system log wType event containing one
  217. // hex DWORD code value.
  218. void PostSessDirErrorValueEvent(unsigned EventCode, DWORD ErrVal, WORD wType)
  219. {
  220. HANDLE hLog;
  221. WCHAR hrString[128];
  222. PWSTR String = NULL;
  223. static DWORD numInstances = 0;
  224. //
  225. //count the numinstances of out of memory error, if this is more than
  226. //a specified number, we just won't log them
  227. //
  228. if( MY_STATUS_COMMITMENT_LIMIT == ErrVal )
  229. {
  230. if( numInstances > MAX_INSTANCE_MEMORYERR )
  231. return;
  232. //
  233. //if applicable, tell the user that we won't log any more of the out of memory errors
  234. //
  235. if( numInstances >= MAX_INSTANCE_MEMORYERR - 1 ) {
  236. wsprintfW(hrString, L"0x%X. This type of error will not be logged again to avoid eventlog fillup.", ErrVal);
  237. String = hrString;
  238. }
  239. numInstances++;
  240. }
  241. hLog = RegisterEventSource(NULL, L"TermServSessDir");
  242. if (hLog != NULL) {
  243. if( NULL == String ) {
  244. wsprintfW(hrString, L"0x%X", ErrVal);
  245. String = hrString;
  246. }
  247. ReportEvent(hLog, wType, 0, EventCode, NULL, 1, 0,
  248. (const WCHAR **)&String, NULL);
  249. DeregisterEventSource(hLog);
  250. }
  251. }
  252. // PostSessDirErrorMsgEvent
  253. //
  254. // Utility function used to create a system log wType event containing one
  255. // WCHAR msg.
  256. void PostSessDirErrorMsgEvent(unsigned EventCode, WCHAR *szMsg, WORD wType)
  257. {
  258. HANDLE hLog;
  259. hLog = RegisterEventSource(NULL, L"TermServSessDir");
  260. if (hLog != NULL) {
  261. ReportEvent(hLog, wType, 0, EventCode, NULL, 1, 0,
  262. (const WCHAR **)&szMsg, NULL);
  263. DeregisterEventSource(hLog);
  264. }
  265. }
  266. // DISJetGetServersPendingReconnects
  267. //
  268. // Returns arrays of max length 10 of servers pending reconnects, where the
  269. // reconnect is greater than g_TimeServerSilentBeforePing seconds.
  270. HRESULT STDMETHODCALLTYPE DISJetGetServersPendingReconnects(
  271. OUT long __RPC_FAR *pNumSessionsReturned,
  272. OUT WCHAR ServerAddressRows[10][SERVER_ADDRESS_LENGTH],
  273. OUT DWORD ServerIDs[10])
  274. {
  275. JET_ERR err;
  276. JET_SESID sesid = JET_sesidNil;
  277. JET_DBID dbid;
  278. JET_TABLEID servdirtableid;
  279. DWORD zero = 0;
  280. *pNumSessionsReturned = 0;
  281. unsigned i = 0;
  282. unsigned long cbActual;
  283. // These are really FILETIMEs, but we want to do 64-bit math on them,
  284. // and they're the same structure as FILETIMEs.
  285. ULARGE_INTEGER ulCurrentTime;
  286. ULARGE_INTEGER ulAITTime;
  287. //TSDISErrorOut(L"GetPendRec...");
  288. CALL(JetBeginSession(g_instance, &sesid, "user", ""));
  289. CALL(JetOpenDatabase(sesid, JETDBFILENAME, "", &dbid, 0));
  290. CALL(JetOpenTable(sesid, dbid, "ServerDirectory", NULL, 0, 0,
  291. &servdirtableid));
  292. // Get the current file time.
  293. SYSTEMTIME st;
  294. // Retrieve the time.
  295. GetSystemTime(&st);
  296. SystemTimeToFileTime(&st, (FILETIME *) &ulCurrentTime);
  297. // Set the current time to the sd timestamp file
  298. SetFileTime(g_hTimeFile, NULL, NULL, (FILETIME *)&ulCurrentTime);
  299. CALL(JetBeginTransaction(sesid));
  300. // Since Jet has no unsigned long type, go through the servers first
  301. // looking for keys greater than 0, 0, then looking for keys less than 0, 0
  302. // TODO: Consider how to do this with JET_coltypDateTime or using NULLs
  303. for (int j = 0; j < 2; j++) {
  304. CALL(JetSetCurrentIndex(sesid, servdirtableid, "ServerAlmostInTimes"));
  305. CALL(JetMakeKey(sesid, servdirtableid, &zero, sizeof(zero),
  306. JET_bitNewKey));
  307. CALL(JetMakeKey(sesid, servdirtableid, &zero, sizeof(zero), 0));
  308. if (0 == j)
  309. err = JetSeek(sesid, servdirtableid, JET_bitSeekGT);
  310. else
  311. err = JetSeek(sesid, servdirtableid, JET_bitSeekLT);
  312. while ((i < TSSD_MaxDisconnectedSessions) && (JET_errSuccess == err)) {
  313. // Get AlmostInTimeLow, AlmostInTimeHigh (3 + 4) for computation.
  314. CALL(JetRetrieveColumn(sesid, servdirtableid, servdircolumnid[
  315. SERVDIR_AITLOW_INTERNAL_INDEX], &(ulAITTime.LowPart),
  316. sizeof(ulAITTime.LowPart), &cbActual, 0, NULL));
  317. CALL(JetRetrieveColumn(sesid, servdirtableid, servdircolumnid[
  318. SERVDIR_AITHIGH_INTERNAL_INDEX], &(ulAITTime.HighPart),
  319. sizeof(ulAITTime.HighPart), &cbActual, 0, NULL));
  320. // If the difference between the current time and the time the
  321. // server was stamped is greater than the set
  322. // TimeServerSilentBeforePing, then put it in the return array,
  323. // else don't.
  324. if ((ulCurrentTime.QuadPart - ulAITTime.QuadPart) >
  325. g_TimeServerSilentBeforePing) {
  326. // Get ServerID
  327. CALL(JetRetrieveColumn(sesid, servdirtableid, servdircolumnid[
  328. SERVDIR_SERVID_INTERNAL_INDEX], &ServerIDs[i],
  329. sizeof(ServerIDs[i]), &cbActual, 0, NULL));
  330. // Get the ServerAddress for this record.
  331. CALL(JetRetrieveColumn(sesid, servdirtableid, servdircolumnid[
  332. SERVDIR_SERVADDR_INTERNAL_INDEX],
  333. &ServerAddressRows[i][0], sizeof(ServerAddressRows[i]),
  334. &cbActual, 0, NULL));
  335. i += 1;
  336. }
  337. // Move to the next matching record.
  338. if (0 == j)
  339. err = JetMove(sesid, servdirtableid, JET_MoveNext, 0);
  340. else
  341. err = JetMove(sesid, servdirtableid, JET_MovePrevious, 0);
  342. }
  343. }
  344. *pNumSessionsReturned = i;
  345. CALL(JetCommitTransaction(sesid, 0));
  346. CALL(JetCloseTable(sesid, servdirtableid));
  347. CALL(JetCloseDatabase(sesid, dbid, 0));
  348. CALL(JetEndSession(sesid, 0));
  349. return S_OK;
  350. HandleError:
  351. if (sesid != JET_sesidNil) {
  352. // Can't really recover. Just bail out.
  353. (VOID) JetRollback(sesid, JET_bitRollbackAll);
  354. // Force the session closed
  355. (VOID) JetEndSession(sesid, JET_bitForceSessionClosed);
  356. }
  357. return E_FAIL;
  358. }
  359. #if 0
  360. HRESULT STDMETHODCALLTYPE DISSQLGetServersPendingReconnects(
  361. OUT long __RPC_FAR *pNumSessionsReturned,
  362. OUT CVar *pVarRows)
  363. {
  364. long NumRecords = 0;
  365. HRESULT hr;
  366. ADOCommand *pCommand;
  367. ADOParameters *pParameters;
  368. ADORecordset *pResultRecordSet;
  369. CVar varFields;
  370. CVar varStart;
  371. TRC2((TB,"GetServersWithDisconnectedSessions"));
  372. ASSERT((pNumSessionsReturned != NULL),(TB,"NULL pNumSess"));
  373. hr = CreateADOStoredProcCommand(L"SP_TSDISGetServersPendingReconnects",
  374. &pCommand, &pParameters);
  375. if (SUCCEEDED(hr)) {
  376. // Execute the command.
  377. hr = pCommand->Execute(NULL, NULL, adCmdStoredProc,
  378. &pResultRecordSet);
  379. pParameters->Release();
  380. pCommand->Release();
  381. }
  382. else {
  383. ERR((TB,"GetServersWDiscSess: Failed create cmd, hr=0x%X", hr));
  384. }
  385. // At this point we have a result recordset containing the server rows
  386. // corresponding to all of the disconnected sessions.
  387. if (SUCCEEDED(hr)) {
  388. long State;
  389. NumRecords = 0;
  390. hr = pResultRecordSet->get_State(&State);
  391. if (SUCCEEDED(hr)) {
  392. if (!(State & adStateClosed)) {
  393. VARIANT_BOOL VB;
  394. // If EOF the recordset is empty.
  395. hr = pResultRecordSet->get_EOF(&VB);
  396. if (SUCCEEDED(hr)) {
  397. if (VB) {
  398. TRC1((TB,"GetServersWDiscSess: Result recordset EOF, "
  399. "0 rows"));
  400. goto PostUnpackResultSet;
  401. }
  402. }
  403. else {
  404. ERR((TB,"GetServersWDiscSess: Failed get_EOF, hr=0x%X",
  405. hr));
  406. goto PostUnpackResultSet;
  407. }
  408. }
  409. else {
  410. ERR((TB,"GetServersWDiscSess: Closed result recordset"));
  411. goto PostUnpackResultSet;
  412. }
  413. }
  414. else {
  415. ERR((TB,"GetServersWDiscSess: get_State failed, hr=0x%X", hr));
  416. goto PostUnpackResultSet;
  417. }
  418. // Grab the result data into a safearray, starting with the default
  419. // current row and all fields.
  420. varStart.InitNoParam();
  421. varFields.InitNoParam();
  422. hr = pResultRecordSet->GetRows(adGetRowsRest, varStart,
  423. varFields, pVarRows);
  424. if (SUCCEEDED(hr)) {
  425. hr = SafeArrayGetUBound(pVarRows->parray, 2, &NumRecords);
  426. if (SUCCEEDED(hr)) {
  427. // 0-based array bound was returned, num rows is that + 1.
  428. NumRecords++;
  429. TRC1((TB,"%d rows retrieved from safearray", NumRecords));
  430. }
  431. else {
  432. ERR((TB,"GetServersWithDisc: Failed safearray getubound, "
  433. "hr=0x%X", hr));
  434. goto PostUnpackResultSet;
  435. }
  436. }
  437. else {
  438. ERR((TB,"GetServersWDiscSess: Failed to get rows, hr=0x%X", hr));
  439. goto PostUnpackResultSet;
  440. }
  441. PostUnpackResultSet:
  442. pResultRecordSet->Release();
  443. }
  444. else {
  445. ERR((TB,"GetServersWDiscSess: Failed exec, hr=0x%X", hr));
  446. }
  447. *pNumSessionsReturned = NumRecords;
  448. return hr;
  449. }
  450. #endif
  451. /****************************************************************************/
  452. // DISDebugControlHandler
  453. //
  454. // Handle console control events for when service is in debug mode.
  455. /****************************************************************************/
  456. BOOL WINAPI DISDebugControlHandler(DWORD dwCtrlType) {
  457. switch(dwCtrlType)
  458. {
  459. case CTRL_BREAK_EVENT:
  460. case CTRL_C_EVENT:
  461. TSDISErrorOut(L"Stopping service\n");
  462. SetEvent(g_hStopServiceEvent);
  463. // Should I wait for that to complete?
  464. return TRUE;
  465. break;
  466. }
  467. return FALSE;
  468. }
  469. /****************************************************************************/
  470. // DISPingServer
  471. //
  472. // Given the IP address of a server, pings it. Returns TRUE on success, FALSE
  473. // on failure.
  474. /****************************************************************************/
  475. BOOLEAN DISPingServer(WCHAR *ServerAddress) {
  476. HANDLE hServer = NULL;
  477. hServer = WinStationOpenServer(ServerAddress);
  478. // The only case where we return false is where hServer is NULL and the
  479. // reason is not ERROR_ACCESS_DENIED.
  480. if (hServer == NULL) {
  481. if (GetLastError() != ERROR_ACCESS_DENIED)
  482. return FALSE;
  483. }
  484. else {
  485. // The hServer is valid, so clean up.
  486. WinStationCloseServer(hServer);
  487. }
  488. return TRUE;
  489. }
  490. /****************************************************************************/
  491. // DISGetServerStatus
  492. //
  493. // Given the IP address of a server, determines its state (Responding or
  494. // NotResponding).
  495. //
  496. // Currently implemented as a ping. See lengthy comment in main for one
  497. // possible future optimization.
  498. /****************************************************************************/
  499. SERVER_STATUS DISGetServerStatus(WCHAR *ServerAddress) {
  500. switch (g_PingMode) {
  501. case AlwaysFail:
  502. return NotResponding;
  503. case AlwaysSucceed:
  504. return Responding;
  505. case NormalMode:
  506. // NOTE INTENTIONAL FALLTHROUGH.
  507. default:
  508. if (DISPingServer(ServerAddress) == TRUE)
  509. return Responding;
  510. else
  511. return NotResponding;
  512. }
  513. }
  514. #if 0
  515. HRESULT DISSQLInitialize() {
  516. // Retrieve number of seconds to wait from the registry -- NOT IMPLEMENTED
  517. HRESULT hr = S_OK;
  518. BSTR ConnectString = NULL;
  519. LONG RegRetVal;
  520. HKEY hKey;
  521. BSTR ConnectStr = NULL;
  522. BSTR UserStr = NULL;
  523. BSTR PwdStr = NULL;
  524. RegRetVal = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  525. TEXT("Software\\Microsoft\\DIS"), 0, KEY_READ, &hKey);
  526. if (RegRetVal == ERROR_SUCCESS) {
  527. DWORD Type, DataSize;
  528. // Determine the needed size.
  529. DataSize = 0;
  530. RegRetVal = RegQueryValueExW(hKey, L"ConnectString", NULL,
  531. &Type, NULL, &DataSize);
  532. DataSize &= ~1;
  533. if (RegRetVal == ERROR_SUCCESS && Type == REG_SZ) {
  534. ConnectString = SysAllocStringLen(L"", DataSize /
  535. sizeof(WCHAR));
  536. if (ConnectString != NULL) {
  537. RegRetVal = RegQueryValueExW(hKey, L"ConnectString",
  538. NULL, &Type, (BYTE *)ConnectString,
  539. &DataSize);
  540. if (RegRetVal == ERROR_SUCCESS) {
  541. // Hold onto the connect string for use below.
  542. TRC1((TB,"Retrieved conn str %S", ConnectString));
  543. }
  544. else {
  545. ERR((TB,"Final RegQuery failed, err=%u", RegRetVal));
  546. hr = E_FAIL;
  547. goto Cleanup;
  548. }
  549. }
  550. else {
  551. ERR((TB,"Failed alloc connect string"));
  552. hr = E_OUTOFMEMORY;
  553. goto Cleanup;
  554. }
  555. }
  556. else {
  557. ERR((TB,"Failed RegQuery - err=%u, DataSize=%u, type=%u",
  558. RegRetVal, DataSize, Type));
  559. hr = E_FAIL;
  560. goto Cleanup;
  561. }
  562. RegCloseKey(hKey);
  563. }
  564. else {
  565. ERR((TB,"RegOpenKeyEx returned err %u", RegRetVal));
  566. hr = E_FAIL;
  567. goto Cleanup;
  568. }
  569. hr = CoInitialize(NULL);
  570. // Alloc the BSTRs for the connection.
  571. ConnectStr = SysAllocString(ConnectString);
  572. UserStr = SysAllocString(L"");
  573. PwdStr = SysAllocString(L"");
  574. if ((ConnectStr == NULL) || (UserStr == NULL) || (PwdStr == NULL)) {
  575. ERR((TB, "Failed alloc Connect, User, or PwdStr"));
  576. hr = E_OUTOFMEMORY;
  577. goto Cleanup;
  578. }
  579. // Create an ADO connection instance and connect.
  580. hr = CoCreateInstance(CLSID_CADOConnection, NULL,
  581. CLSCTX_INPROC_SERVER, IID_IADOConnection,
  582. (LPVOID *)&g_pConnection);
  583. if (SUCCEEDED(hr)) {
  584. // Do the open.
  585. hr = g_pConnection->Open(ConnectStr, UserStr, PwdStr,
  586. adOpenUnspecified);
  587. if (!SUCCEEDED(hr)) {
  588. ERR((TB,"Failed open DB, hr=0x%X", hr));
  589. g_pConnection->Release();
  590. g_pConnection = NULL;
  591. }
  592. }
  593. else {
  594. ERR((TB,"CoCreate(ADOConn) returned 0x%X", hr));
  595. }
  596. Cleanup:
  597. // SysFreeString(NULL) is ok.
  598. SysFreeString(ConnectString);
  599. SysFreeString(ConnectStr);
  600. SysFreeString(UserStr);
  601. SysFreeString(PwdStr);
  602. return hr;
  603. }
  604. #endif
  605. // Call each TS Server to ask them to rejoin SD
  606. void __cdecl DISOpenServer(void *Para)
  607. {
  608. HRESULT hr;
  609. WCHAR *pBindingString = NULL;
  610. RPC_BINDING_HANDLE hRPCBinding = NULL;
  611. WCHAR *szPrincipalName = NULL;
  612. WCHAR *ServerName = NULL;
  613. SDRecoverServerNames *SDRRecoverServerPara = (SDRecoverServerNames *)Para;
  614. unsigned int count = SDRRecoverServerPara->count;
  615. WCHAR ** ServerNameArray = SDRRecoverServerPara->ServerNameArray;
  616. unsigned int i;
  617. unsigned long RpcException;
  618. DWORD dwRejoinFlag = 0;
  619. dwRejoinFlag |= TSSD_FORCEREJOIN;
  620. // Impersonate the cluster account to make the rejoin RPC call
  621. if (g_dwClusterState == ClusterStateRunning) {
  622. if (g_hClusterToken) {
  623. if(!ImpersonateLoggedOnUser(g_hClusterToken)) {
  624. // If we failed to impersonate the cluster account, don't ask TS to rejoin, since it will
  625. // fail anyway due to access denied
  626. TSDISErrorOut(L"SD Recover: Error %d in ImpersonateLoggedOnUser\n", GetLastError());
  627. goto HandleError;
  628. }
  629. }
  630. else {
  631. // If g_hClusterToken is NULL, don't ask TS to rejoin, since it will
  632. // fail anyway due to access denied
  633. goto HandleError;
  634. }
  635. }
  636. // If it's on failover cluster, set the flag to tell server not to repopulate its sessions
  637. if (g_RepopulateSession == FALSE) {
  638. dwRejoinFlag |= TSSD_NOREPOPULATE;
  639. }
  640. for (i=0;i<count;i++) {
  641. if (NULL != hRPCBinding) {
  642. RpcBindingFree(&hRPCBinding);
  643. hRPCBinding = NULL;
  644. }
  645. ServerName = *(ServerNameArray + i);
  646. // Connect to the tssdjet RPC server according to the server name provided.
  647. // We first create an RPC binding handle from a composed binding string.
  648. hr = RpcStringBindingCompose(/*(WCHAR *)g_RPCUUID,*/
  649. 0,
  650. L"ncacn_ip_tcp", ServerName,
  651. 0,
  652. NULL, &pBindingString);
  653. if (hr == RPC_S_OK) {
  654. // Generate the RPC binding from the canonical RPC binding string.
  655. hr = RpcBindingFromStringBinding(pBindingString, &hRPCBinding);
  656. if (hr != RPC_S_OK) {
  657. ERR((TB,"SD Recover: Error %d in RpcBindingFromStringBinding\n", hr));
  658. goto LogError;
  659. }
  660. }
  661. else {
  662. ERR((TB,"SD Recover: Error %d in RpcStringBindingCompose\n", hr));
  663. goto LogError;
  664. }
  665. hr = RpcEpResolveBinding(hRPCBinding, TSSDTOJETRPC_ClientIfHandle);
  666. if (hr != RPC_S_OK) {
  667. ERR((TB, "SD Recover: Error %d in RpcEpResolveBinding", hr));
  668. goto LogError;
  669. }
  670. hr = RpcMgmtInqServerPrincName(hRPCBinding,
  671. RPC_C_AUTHN_GSS_NEGOTIATE,
  672. &szPrincipalName);
  673. if (hr != RPC_S_OK) {
  674. ERR((TB,"SD Recover: Error %d in RpcMgmtIngServerPrincName", hr));
  675. goto LogError;
  676. }
  677. hr = RpcBindingSetAuthInfo(hRPCBinding,
  678. szPrincipalName,
  679. RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
  680. RPC_C_AUTHN_GSS_NEGOTIATE,
  681. NULL, //CurrentIdentity
  682. NULL);
  683. RpcStringFree(&szPrincipalName);
  684. if (hr != RPC_S_OK) {
  685. ERR((TB,"SD Recover: Error %d in RpcBindingSetAuthInfo", hr));
  686. goto LogError;
  687. }
  688. RpcTryExcept {
  689. // Make the call to TS to ask it to rejoin
  690. hr = TSSDRPCRejoinSD(hRPCBinding, dwRejoinFlag);
  691. }
  692. RpcExcept(TSSDRpcExceptionFilter(RpcExceptionCode())) {
  693. RpcException = RpcExceptionCode();
  694. hr = RpcException;
  695. ERR((TB,"ForceRejoin: RPC Exception %d\n", RpcException));
  696. }
  697. RpcEndExcept
  698. LogError:
  699. if (hr != RPC_S_OK) {
  700. PostSessDirErrorMsgEvent(EVENT_FAIL_CALL_TS_REJOIN, ServerName, EVENTLOG_ERROR_TYPE);
  701. }
  702. }
  703. // Stop Impersonating
  704. if (g_dwClusterState == ClusterStateRunning) {
  705. RevertToSelf();
  706. }
  707. HandleError:
  708. if (NULL != hRPCBinding) {
  709. RpcBindingFree(&hRPCBinding);
  710. hRPCBinding = NULL;
  711. }
  712. // Free
  713. for (i=0;i<count;i++) {
  714. LocalFree(*(ServerNameArray + i));
  715. }
  716. LocalFree(ServerNameArray);
  717. LocalFree(SDRRecoverServerPara);
  718. return;
  719. }
  720. // When SD service is restarted, try to recover the Servers in the SD
  721. // Jet database and ask them to rejoin SD
  722. BOOL DISJetRecover()
  723. {
  724. JET_SESID sesid = JET_sesidNil;;
  725. JET_TABLEID servdirtableid;
  726. JET_DBID dbid = JET_dbidNil;
  727. JET_COLUMNDEF jcd;
  728. JET_COLUMNID ServerAddrCId;
  729. JET_ERR err = JET_errSuccess;
  730. unsigned long i, count = 0;
  731. WCHAR ServerName[SERVER_ADDRESS_LENGTH];
  732. WCHAR **ServerNameArray = NULL;
  733. unsigned long cbActual;
  734. SDRecoverServerNames *pSDRecoverServerPara;
  735. CALL(JetSetSystemParameter(&g_instance, 0, JET_paramSystemPath,
  736. 0, JETDISDBDIRECTORY));
  737. CALL(JetSetSystemParameter(&g_instance, 0, JET_paramTempPath,
  738. 0, JETDISDBDIRECTORY));
  739. CALL(JetSetSystemParameter(&g_instance, 0, JET_paramLogFilePath,
  740. 0, JETDISDBDIRECTORY));
  741. CALL(JetSetSystemParameter(&g_instance, 0, JET_paramCircularLog,
  742. 1, NULL));
  743. CALL(JetInit(&g_instance));
  744. CALL(JetBeginSession(g_instance, &sesid, "user", ""));
  745. CALL(JetAttachDatabase(sesid, JETDBFILENAME, 0));
  746. // Populate our columnid arrays
  747. CALL(JetOpenDatabase(sesid, JETDBFILENAME, "", &dbid, 0));
  748. CALL(JetOpenTable(sesid, dbid, "ServerDirectory", NULL, 0, 0,
  749. &servdirtableid));
  750. CALL(JetBeginTransaction(sesid));
  751. CALL(JetGetColumnInfo(sesid, dbid, "ServerDirectory",
  752. ServerDirectoryColumns[SERVDIR_SERVADDR_INTERNAL_INDEX].szColumnName, &jcd,
  753. sizeof(jcd), 0));
  754. ServerAddrCId = jcd.columnid;
  755. pSDRecoverServerPara = (SDRecoverServerNames *)LocalAlloc(LMEM_FIXED, sizeof(SDRecoverServerNames));
  756. if (NULL == pSDRecoverServerPara) {
  757. goto HandleError;
  758. }
  759. // Get number of TS Servers in the SD
  760. err = JetIndexRecordCount(sesid, servdirtableid, &count, MAX_TSSERVERS_TO_RECOVER);
  761. if (err != JET_errSuccess)
  762. goto HandleError;
  763. if (count) {
  764. CALL(JetMove(sesid, servdirtableid, JET_MoveFirst, 0));
  765. ServerNameArray = (WCHAR **)LocalAlloc(LMEM_FIXED, count * sizeof(WCHAR *));
  766. if (NULL == ServerNameArray) {
  767. goto HandleError;
  768. }
  769. }
  770. TSDISErrorOut(L"We have %d Servers to recover\n", count);
  771. for(i=0;i<count;i++)
  772. {
  773. CALL(JetRetrieveColumn(sesid, servdirtableid, ServerAddrCId,
  774. ServerName, SERVER_ADDRESS_LENGTH, &cbActual, 0, NULL));
  775. TSDISErrorOut(L"Server %d is %s\n", i+1, ServerName);
  776. *(ServerNameArray + i) = (WCHAR *)LocalAlloc(LMEM_FIXED, sizeof(ServerName));
  777. if (NULL == *(ServerNameArray+i)) {
  778. goto HandleError;
  779. }
  780. memcpy((BYTE *)(*(ServerNameArray + i)), (BYTE*)ServerName, sizeof(ServerName));
  781. if (i != (count-1))
  782. CALL(JetMove(sesid, servdirtableid, JET_MoveNext, 0));
  783. }
  784. // Spin a thread to call TS servers to rejoin SD
  785. pSDRecoverServerPara->count = count;
  786. pSDRecoverServerPara->ServerNameArray = ServerNameArray;
  787. if(-1 == _beginthread(DISOpenServer, 0, (PVOID)pSDRecoverServerPara)) {
  788. TSDISErrorOut(L"Unable to begin DISOpenServer thread\n");
  789. // Free mem
  790. for (i=0;i<count;i++) {
  791. LocalFree(*(pSDRecoverServerPara->ServerNameArray + i));
  792. }
  793. LocalFree(pSDRecoverServerPara->ServerNameArray);
  794. LocalFree(pSDRecoverServerPara);
  795. }
  796. CALL(JetCommitTransaction(sesid, 0));
  797. CALL(JetCloseTable(sesid, servdirtableid));
  798. CALL(JetCloseDatabase(sesid, dbid, 0));
  799. CALL(JetEndSession(sesid, 0));
  800. CALL(JetTerm(g_instance));
  801. g_instance = 0;
  802. return TRUE;
  803. HandleError:
  804. if (sesid != JET_sesidNil) {
  805. // Can't really recover. Just bail out.
  806. (VOID) JetRollback(sesid, JET_bitRollbackAll);
  807. // Force the session closed
  808. (VOID) JetEndSession(sesid, JET_bitForceSessionClosed);
  809. }
  810. JetTerm(g_instance);
  811. g_instance = 0;
  812. return FALSE;
  813. }
  814. // Delete the database and all other JET files (if present)
  815. void DeleteJetFiles()
  816. {
  817. HANDLE hFileFind;
  818. WIN32_FIND_DATA FindFileData;
  819. WCHAR filename[MAX_LOGFILE_LENGTH];
  820. DWORD dwError;
  821. // Delete the database and all other JET files (if present), and start anew.
  822. (void) DeleteFile(JETDBFILENAMEW);
  823. (void) DeleteFile(JETAUXFILENAME1W);
  824. (void) DeleteFile(JETAUXFILENAME2W);
  825. (void) DeleteFile(JETAUXFILENAME3W);
  826. (void) DeleteFile(JETAUXFILENAME4W);
  827. (void) DeleteFile(JETAUXFILENAME5W);
  828. (void) DeleteFile(JETAUXFILENAME6W);
  829. // Delete numbered log files. Jet can create a bunch of log files
  830. // of the form edb00001.log, edb00002.log, . . ., edb0000a.log,
  831. // edb0000b.log, . . ., edb0000f.log, edb00010.log, . . .
  832. hFileFind = FindFirstFile(JETLOGFILENAME, &FindFileData);
  833. if (hFileFind != INVALID_HANDLE_VALUE) {
  834. swprintf(filename, JETDISDBDIRECTORYW);
  835. wcsncat(filename, FindFileData.cFileName, MAX_LOGFILE_LENGTH - sizeof(JETDISDBDIRECTORYW) / sizeof(WCHAR) - 1);
  836. if (DeleteFile(filename) == 0) {
  837. dwError = GetLastError();
  838. if (dwError != ERROR_FILE_NOT_FOUND) {
  839. PostSessDirErrorValueEvent(EVENT_PROBLEM_DELETING_LOGS,
  840. dwError, EVENTLOG_ERROR_TYPE);
  841. }
  842. }
  843. while (FindNextFile(hFileFind, &FindFileData)) {
  844. swprintf(filename, JETDISDBDIRECTORYW);
  845. wcsncat(filename, FindFileData.cFileName, MAX_LOGFILE_LENGTH - sizeof(JETDISDBDIRECTORYW) / sizeof(WCHAR) - 1);
  846. if (DeleteFile(filename) == 0) {
  847. dwError = GetLastError();
  848. if (dwError != ERROR_FILE_NOT_FOUND) {
  849. PostSessDirErrorValueEvent(EVENT_PROBLEM_DELETING_LOGS,
  850. dwError, EVENTLOG_ERROR_TYPE);
  851. break;
  852. }
  853. }
  854. }
  855. FindClose(hFileFind);
  856. }
  857. }
  858. //
  859. // Session directory initialization on fail-over cluster
  860. //
  861. // Return True on success
  862. //
  863. BOOL DISJetInitInCluster()
  864. {
  865. BOOL fRet = FALSE;
  866. DWORD dwError;
  867. HCLUSTER hclus = NULL;
  868. HRESOURCE hrSD = NULL;
  869. WCHAR *pszDriveLetter = NULL;
  870. DWORD cchDriveLetter = MAX_DRIVE_LETTER_LENGTH;
  871. HCLUSENUM hClusEnum = NULL;
  872. DWORD dwIndex, dwCount;
  873. WCHAR ResourceName[256], *ServiceName;
  874. DWORD dwSize, dwType;
  875. LPVOID pPropertyList = NULL;
  876. DWORD rc;
  877. BOOL bFindSDService = FALSE;
  878. HRESOURCE hrNetworkName = NULL;
  879. struct CLUS_NETNAME_VS_TOKEN_INFO VsTokenInfo;
  880. DWORD dwReturnSize = 0;
  881. HANDLE hVSToken = NULL;
  882. // Change the current directory to the right place on the shared
  883. // drive.
  884. // Open the cluster.
  885. hclus = OpenCluster(NULL);
  886. if (hclus == NULL) {
  887. // TODO: Log event.
  888. TSDISErrorOut(L"Unable to open cluster, error %d\n",
  889. GetLastError());
  890. goto HandleError;
  891. }
  892. // Enuerate all the resources in the cluster to find the generic service
  893. // resource named "tssdis" i.e. the session directory service
  894. hClusEnum = ClusterOpenEnum(hclus, CLUSTER_ENUM_RESOURCE);
  895. if (hClusEnum == NULL) {
  896. // TODO: Log event.
  897. TSDISErrorOut(L"Unable to open cluster enum, error %d\n",
  898. GetLastError());
  899. goto HandleError;
  900. }
  901. dwCount = ClusterGetEnumCount(hClusEnum);
  902. for (dwIndex=0; dwIndex<dwCount; dwIndex++) {
  903. if (pPropertyList != NULL) {
  904. LocalFree(pPropertyList);
  905. pPropertyList = NULL;
  906. }
  907. if (hrSD != NULL) {
  908. CloseClusterResource(hrSD);
  909. hrSD = NULL;
  910. }
  911. dwSize = sizeof(ResourceName) / sizeof(WCHAR);
  912. if (ClusterEnum(hClusEnum, dwIndex, &dwType,
  913. ResourceName, &dwSize) != ERROR_SUCCESS) {
  914. TSDISErrorOut(L"ClusterEnum fails with error %d\n",
  915. GetLastError());
  916. continue;
  917. }
  918. hrSD = OpenClusterResource(hclus, ResourceName);
  919. if (hrSD == NULL) {
  920. TSDISErrorOut(L"OpenClusterResource fails with error %d\n",
  921. GetLastError());
  922. continue;
  923. }
  924. pPropertyList = NULL;
  925. dwSize = 0;
  926. rc = ClusterResourceControl(hrSD, // hResource
  927. NULL, // hHostNode
  928. CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES, // dwControlCode
  929. NULL, // lpInBuffer
  930. 0, // cbInBufferSize
  931. NULL, // lpOutBuffer
  932. 0, // cbOutBufferSiz
  933. &dwSize); // lpcbByteReturned
  934. if (rc != ERROR_SUCCESS) {
  935. TSDISErrorOut(L"ResourceControl fails with error %d\n", rc);
  936. continue;
  937. }
  938. dwSize += sizeof(WCHAR);
  939. pPropertyList = LocalAlloc(LMEM_FIXED, dwSize);
  940. if (pPropertyList == NULL) {
  941. TSDISErrorOut(L"Can't allocate memory for propertylist with size %d\n", dwSize);
  942. continue;
  943. }
  944. rc = ClusterResourceControl(hrSD,
  945. NULL,
  946. CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES,
  947. NULL,
  948. 0,
  949. pPropertyList,
  950. dwSize,
  951. NULL);
  952. if (rc != ERROR_SUCCESS) {
  953. TSDISErrorOut(L"ResourceControl fails with error %d\n", rc);
  954. continue;
  955. }
  956. rc = ResUtilFindSzProperty(pPropertyList, dwSize, L"ServiceName", &ServiceName);
  957. if (rc == ERROR_SUCCESS) {
  958. if (_wcsicmp(ServiceName, L"tssdis") == 0) {
  959. TSDISErrorOut(L"Find tssdis resource\n");
  960. bFindSDService = TRUE;
  961. LocalFree(ServiceName);
  962. break;
  963. }
  964. if (ServiceName != NULL) {
  965. LocalFree(ServiceName);
  966. ServiceName = NULL;
  967. }
  968. }
  969. CloseClusterResource(hrSD);
  970. hrSD = NULL;
  971. }
  972. if (pPropertyList != NULL) {
  973. LocalFree(pPropertyList);
  974. pPropertyList = NULL;
  975. }
  976. ClusterCloseEnum(hClusEnum);
  977. // Bail out if can't find tssdis resource
  978. if (!bFindSDService) {
  979. // TODO: Log event.
  980. TSDISErrorOut(L"Unable to find the resource with service name tssdis\n");
  981. goto HandleError;
  982. }
  983. // Find the network name resource
  984. hrNetworkName = ResUtilGetResourceDependency(hrSD, L"Network Name");
  985. if (hrNetworkName == NULL) {
  986. TSDISErrorOut(L"Unable to get the dependent NetworkName resource, error is %d\n", GetLastError());
  987. goto HandleError;
  988. }
  989. pPropertyList = NULL;
  990. dwSize = 0;
  991. // Get the property of the network name resource
  992. // This is the 1st call, just get the size of the porperty list
  993. rc = ClusterResourceControl(hrNetworkName, // hResource
  994. NULL, // hHostNode
  995. CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES, // dwControlCode
  996. NULL, // lpInBuffer
  997. 0, // cbInBufferSize
  998. NULL, // lpOutBuffer
  999. 0, // cbOutBufferSiz
  1000. &dwSize); // lpcbByteReturned
  1001. if (rc != ERROR_SUCCESS) {
  1002. TSDISErrorOut(L"ResourceControl fails with error %d\n", rc);
  1003. goto HandleError;
  1004. }
  1005. dwSize += sizeof(WCHAR);
  1006. pPropertyList = LocalAlloc(LMEM_FIXED, dwSize);
  1007. if (pPropertyList == NULL) {
  1008. TSDISErrorOut(L"Can't allocate memory for propertylist with size %d\n", dwSize);
  1009. goto HandleError;
  1010. }
  1011. // Get the property of the network name resource
  1012. rc = ClusterResourceControl(hrNetworkName,
  1013. NULL,
  1014. CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES,
  1015. NULL,
  1016. 0,
  1017. pPropertyList,
  1018. dwSize,
  1019. NULL);
  1020. if (rc != ERROR_SUCCESS) {
  1021. TSDISErrorOut(L"ResourceControl fails with error %d\n", rc);
  1022. goto HandleError;
  1023. }
  1024. // Find the "name" propery in the property list
  1025. rc = ResUtilFindSzProperty(pPropertyList, dwSize, L"Name", &g_ClusterNetworkName);
  1026. if (rc != ERROR_SUCCESS) {
  1027. g_ClusterNetworkName = NULL;
  1028. TSDISErrorOut(L"ResUtilFindSzProperty fails with error %d\n", rc);
  1029. goto HandleError;
  1030. }
  1031. if (pPropertyList != NULL) {
  1032. LocalFree(pPropertyList);
  1033. pPropertyList = NULL;
  1034. }
  1035. VsTokenInfo.ProcessID = GetCurrentProcessId();
  1036. VsTokenInfo.DesiredAccess = 0;
  1037. VsTokenInfo.InheritHandle = FALSE;
  1038. // Get the token of the virtual server
  1039. rc = ClusterResourceControl(
  1040. hrNetworkName,
  1041. 0,
  1042. CLUSCTL_RESOURCE_NETNAME_GET_VIRTUAL_SERVER_TOKEN,
  1043. &VsTokenInfo,
  1044. sizeof(CLUS_NETNAME_VS_TOKEN_INFO),
  1045. &hVSToken,
  1046. sizeof(HANDLE),
  1047. &dwReturnSize
  1048. );
  1049. if (rc != ERROR_SUCCESS) {
  1050. TSDISErrorOut(L"Get the virtual server token failed with error %d\n", rc);
  1051. hVSToken = NULL;
  1052. goto HandleError;
  1053. }
  1054. // Duplicate the virtual server token
  1055. if(!DuplicateTokenEx(
  1056. hVSToken,
  1057. MAXIMUM_ALLOWED,
  1058. NULL,
  1059. SecurityImpersonation,
  1060. TokenImpersonation,
  1061. &g_hClusterToken)) {
  1062. TSDISErrorOut(L"DuplicateTokenEx failed with error %d\n", GetLastError());
  1063. CloseHandle(hVSToken);
  1064. hVSToken = NULL;
  1065. g_hClusterToken = NULL;
  1066. goto HandleError;
  1067. }
  1068. if (hVSToken) {
  1069. CloseHandle(hVSToken);
  1070. hVSToken = NULL;
  1071. }
  1072. pszDriveLetter = new WCHAR[cchDriveLetter];
  1073. if (pszDriveLetter == NULL) {
  1074. TSDISErrorOut(L"Failed to allocate memory for drive letter.\n");
  1075. goto HandleError;
  1076. }
  1077. // Get the drive we're supposed to use.
  1078. dwError = ResUtilFindDependentDiskResourceDriveLetter(hclus, hrSD,
  1079. pszDriveLetter, &cchDriveLetter);
  1080. if (dwError == ERROR_MORE_DATA) {
  1081. // Wow, big drive letter!
  1082. delete [] pszDriveLetter;
  1083. pszDriveLetter = new WCHAR[cchDriveLetter];
  1084. if (pszDriveLetter == NULL) {
  1085. TSDISErrorOut(L"Failed to allocate memory for drive letter\n");
  1086. goto HandleError;
  1087. }
  1088. dwError = ResUtilFindDependentDiskResourceDriveLetter(hclus, hrSD,
  1089. pszDriveLetter, &cchDriveLetter);
  1090. }
  1091. if (dwError != ERROR_SUCCESS) {
  1092. TSDISErrorOut(L"Could not determine resource drive letter.\n");
  1093. delete [] pszDriveLetter;
  1094. pszDriveLetter = NULL;
  1095. goto HandleError;
  1096. }
  1097. // Switch the working directory to that drive.
  1098. if (SetCurrentDirectory(pszDriveLetter) == FALSE) {
  1099. TSDISErrorOut(L"Could not set current directory to that of "
  1100. L"shared disk %s. Error=%d\n", pszDriveLetter,
  1101. GetLastError());
  1102. delete [] pszDriveLetter;
  1103. pszDriveLetter = NULL;
  1104. goto HandleError;
  1105. }
  1106. fRet = TRUE;
  1107. HandleError:
  1108. if (pszDriveLetter != NULL) {
  1109. delete [] pszDriveLetter;
  1110. pszDriveLetter = NULL;
  1111. }
  1112. if (pPropertyList != NULL) {
  1113. LocalFree(pPropertyList);
  1114. pPropertyList = NULL;
  1115. }
  1116. if (hrSD != NULL) {
  1117. CloseClusterResource(hrSD);
  1118. hrSD = NULL;
  1119. }
  1120. if (hrNetworkName != NULL) {
  1121. CloseClusterResource(hrNetworkName);
  1122. hrNetworkName = NULL;
  1123. }
  1124. if (hclus != NULL) {
  1125. CloseCluster(hclus);
  1126. hclus = NULL;
  1127. }
  1128. return fRet;
  1129. }
  1130. HRESULT DISJetInitialize()
  1131. {
  1132. JET_SESID sesid = JET_sesidNil;;
  1133. JET_TABLEID sessdirtableid;
  1134. JET_TABLEID servdirtableid;
  1135. JET_TABLEID clusdirtableid;
  1136. JET_DBID dbid = JET_dbidNil;
  1137. JET_ERR err = JET_errSuccess;
  1138. JET_TABLECREATE tSess;
  1139. JET_COLUMNCREATE cSess[NUM_SESSDIRCOLUMNS];
  1140. JET_TABLECREATE tServ;
  1141. JET_COLUMNCREATE cServ[NUM_SERVDIRCOLUMNS];
  1142. JET_TABLECREATE tClus;
  1143. JET_COLUMNCREATE cClus[NUM_CLUSDIRCOLUMNS];
  1144. unsigned count;
  1145. DWORD dwError;
  1146. BOOL br;
  1147. SECURITY_ATTRIBUTES SA;
  1148. SYSTEMTIME SystemTime;
  1149. ULARGE_INTEGER ulCurrentTime;
  1150. ULARGE_INTEGER ulLastTime;
  1151. g_dwClusterState = ClusterStateNotInstalled;
  1152. //
  1153. // This is a string security descriptor. Look up "Security Descriptor
  1154. // Definition Language" in MSDN for more details.
  1155. //
  1156. // This one says:
  1157. //
  1158. // D: <we are creating a DACL>
  1159. // (A; <Allow ACE>
  1160. // OICI; <Perform object and container inheritance, i.e., let files and
  1161. // directories under this one have these attributes>
  1162. // GA <Generic All Access--Full Control>
  1163. // ;;;SY) <SYSTEM>
  1164. // (A;OICI;GA;;;BA) <same for Builtin Administrators group>
  1165. // (A;OICI;GA;;;CO) <same for creator/owner>
  1166. //
  1167. // We'll use it below to create our directory with the right permissions.
  1168. WCHAR *pwszSD = L"D:(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GA;;;CO)";
  1169. // Failover support--before reactivating, check logic versus reading curr directory from registry.
  1170. // First, determine whether we are running in a cluster. If so, files
  1171. // will have to go on the shared drive. If not, files will go in
  1172. // JETDISDBDIRECTORYW.
  1173. dwError = GetNodeClusterState(NULL, &g_dwClusterState);
  1174. if (dwError != ERROR_SUCCESS) {
  1175. g_dwClusterState = ClusterStateNotInstalled;
  1176. TSDISErrorOut(L"TSDIS: Unable to get cluster state, err = %d\n",
  1177. dwError);
  1178. }
  1179. // Do initialization if running on fail-over cluster
  1180. if (g_dwClusterState == ClusterStateRunning) {
  1181. if (!DISJetInitInCluster()) {
  1182. goto HandleError;
  1183. }
  1184. }
  1185. // Create security descriptor for database directory
  1186. SA.nLength = sizeof(SECURITY_ATTRIBUTES);
  1187. SA.bInheritHandle = FALSE;
  1188. SA.lpSecurityDescriptor = NULL;
  1189. br = ConvertStringSecurityDescriptorToSecurityDescriptor(pwszSD,
  1190. SDDL_REVISION_1, &(SA.lpSecurityDescriptor), NULL);
  1191. if (br == 0) {
  1192. PostSessDirErrorValueEvent(EVENT_COULDNOTSECUREDIR, GetLastError(), EVENTLOG_ERROR_TYPE);
  1193. goto HandleError;
  1194. }
  1195. // Create the system32\tssesdir directory.
  1196. if (CreateDirectory(JETDISDBDIRECTORYW, &SA) == 0) {
  1197. if (ERROR_ALREADY_EXISTS != (dwError = GetLastError())) {
  1198. PostSessDirErrorValueEvent(EVENT_COULDNOTCREATEDIR, dwError, EVENTLOG_ERROR_TYPE);
  1199. goto HandleError;
  1200. }
  1201. } else {
  1202. // We created it successfully, so set the directory attributes to not
  1203. // compress.
  1204. // Obtain a handle to the directory.
  1205. HANDLE hSDDirectory = CreateFile(JETDISDBDIRECTORYW, GENERIC_READ |
  1206. GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
  1207. OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
  1208. if (INVALID_HANDLE_VALUE != hSDDirectory) {
  1209. // We've succeeded opening the directory.
  1210. USHORT CompressionState = COMPRESSION_FORMAT_NONE;
  1211. USHORT OldCompressionState;
  1212. DWORD BytesReturned = 0;
  1213. // Get the current compression state.
  1214. if (DeviceIoControl(hSDDirectory, FSCTL_GET_COMPRESSION,
  1215. NULL, 0, &OldCompressionState, sizeof(USHORT),
  1216. &BytesReturned, NULL) != 0) {
  1217. // If the current compression state is compressed, uncompress.
  1218. if (OldCompressionState != COMPRESSION_FORMAT_NONE) {
  1219. if (DeviceIoControl(hSDDirectory, FSCTL_SET_COMPRESSION,
  1220. &CompressionState, sizeof(USHORT), NULL, 0,
  1221. &BytesReturned, NULL) == 0) {
  1222. // Set compression state failed--this should only be a trace,
  1223. // it may merely mean that the drive is FAT.
  1224. TSDISErrorOut(L"TSDIS: Set compression state off failed, "
  1225. L"lasterr=0x%X\n", GetLastError());
  1226. } else {
  1227. PostSessDirErrorValueEvent(EVENT_UNDID_COMPRESSION, 0, EVENTLOG_ERROR_TYPE);
  1228. }
  1229. }
  1230. }
  1231. CloseHandle(hSDDirectory);
  1232. } else {
  1233. // Nonfatal to have an error opening the directory
  1234. TSDISErrorOut(L"TSDIS: Open directory to change compression state "
  1235. L"failed, lasterr=0x%X\n", GetLastError());
  1236. }
  1237. }
  1238. // Open the timestamp file, compare with current time
  1239. // If the time difference is less than a limit, reuse the db
  1240. // otherwise delete db files
  1241. g_hTimeFile = CreateFile(JETTIMESTAMPFILEW,
  1242. GENERIC_WRITE,
  1243. FILE_SHARE_WRITE,
  1244. &SA,
  1245. OPEN_EXISTING,
  1246. FILE_ATTRIBUTE_NORMAL,
  1247. NULL);
  1248. if (g_hTimeFile == INVALID_HANDLE_VALUE) {
  1249. // This file doesn't exist, create new one
  1250. g_hTimeFile = CreateFile(JETTIMESTAMPFILEW,
  1251. GENERIC_WRITE,
  1252. FILE_SHARE_WRITE,
  1253. &SA,
  1254. CREATE_NEW,
  1255. FILE_ATTRIBUTE_NORMAL,
  1256. NULL);
  1257. }
  1258. else {
  1259. if (GetFileTime(g_hTimeFile, NULL, NULL, (FILETIME *)&ulLastTime)) {
  1260. GetSystemTime(&SystemTime);
  1261. if (SystemTimeToFileTime(&SystemTime, (FILETIME *) &ulCurrentTime)) {
  1262. if ((ulCurrentTime.QuadPart - ulLastTime.QuadPart) < g_TimeLimitToDeleteDB) {
  1263. if (g_dwClusterState == ClusterStateRunning) {
  1264. g_RepopulateSession = FALSE;
  1265. TSDISErrorOut(L"SD in restarted within a time limit, database can be reused\n");
  1266. }
  1267. }
  1268. }
  1269. }
  1270. else
  1271. TSDISErrorOut(L"SD is not restarted within a time limit, need to delete DB files \n");
  1272. }
  1273. // Recover Servers in Jet Database
  1274. if (g_RecoverWhenStart > NO_RECOVER_WHEN_START)
  1275. DISJetRecover();
  1276. // Delete database files if tssdis is not running on failover cluster
  1277. //if (g_dwClusterState != ClusterStateRunning)
  1278. if (g_RepopulateSession == TRUE)
  1279. DeleteJetFiles();
  1280. CALL(JetSetSystemParameter(&g_instance, 0, JET_paramSystemPath,
  1281. 0, JETDISDBDIRECTORY));
  1282. CALL(JetSetSystemParameter(&g_instance, 0, JET_paramTempPath,
  1283. 0, JETDISDBDIRECTORY));
  1284. //CALL(JetSetSystemParameter(&g_instance, 0, JET_paramMaxSessions,
  1285. // JETDISMAXSESSIONS, NULL));
  1286. CALL(JetSetSystemParameter(&g_instance, 0, JET_paramLogFilePath,
  1287. 0, JETDISDBDIRECTORY));
  1288. CALL(JetSetSystemParameter(&g_instance, 0, JET_paramCircularLog,
  1289. 1, NULL));
  1290. CALL(JetInit(&g_instance));
  1291. CALL(JetBeginSession(g_instance, &sesid, "user", ""));
  1292. err = JetCreateDatabase(sesid, JETDBFILENAME, "", &dbid, 0);
  1293. if (JET_errDatabaseDuplicate == err) {
  1294. JET_COLUMNDEF jcd;
  1295. err = JetAttachDatabase(sesid, JETDBFILENAME, 0);
  1296. // if we get a wrnDatabaseAttached, then we have recovered. Otherwise,
  1297. // check the return value as usual.
  1298. if (JET_wrnDatabaseAttached != err) {
  1299. CALL(err);
  1300. }
  1301. // Populate our columnid arrays
  1302. CALL(JetOpenDatabase(sesid, JETDBFILENAME, "", &dbid, 0));
  1303. CALL(JetOpenTable(sesid, dbid, "SessionDirectory", NULL, 0, 0,
  1304. &sessdirtableid));
  1305. CALL(JetOpenTable(sesid, dbid, "ServerDirectory", NULL, 0, 0,
  1306. &servdirtableid));
  1307. CALL(JetOpenTable(sesid, dbid, "ClusterDirectory", NULL, 0, 0,
  1308. &clusdirtableid));
  1309. CALL(JetBeginTransaction(sesid));
  1310. for (count = 0; count < NUM_SESSDIRCOLUMNS; count++) {
  1311. CALL(JetGetColumnInfo(sesid, dbid, "SessionDirectory",
  1312. SessionDirectoryColumns[count].szColumnName, &jcd,
  1313. sizeof(jcd), 0));
  1314. sesdircolumnid[count] = jcd.columnid;
  1315. }
  1316. for (count = 0; count < NUM_SERVDIRCOLUMNS; count++) {
  1317. CALL(JetGetColumnInfo(sesid, dbid, "ServerDirectory",
  1318. ServerDirectoryColumns[count].szColumnName, &jcd,
  1319. sizeof(jcd), 0));
  1320. servdircolumnid[count] = jcd.columnid;
  1321. }
  1322. for (count = 0; count < NUM_CLUSDIRCOLUMNS; count++) {
  1323. CALL(JetGetColumnInfo(sesid, dbid, "ClusterDirectory",
  1324. ClusterDirectoryColumns[count].szColumnName, &jcd,
  1325. sizeof(jcd), 0));
  1326. clusdircolumnid[count] = jcd.columnid;
  1327. }
  1328. CALL(JetCommitTransaction(sesid, 0));
  1329. goto NormalExit;
  1330. } else {
  1331. CALL(err);
  1332. }
  1333. CALL(JetBeginTransaction(sesid));
  1334. // Set up to create session directory schema
  1335. tSess.cbStruct = sizeof(tSess);
  1336. tSess.szTableName = "SessionDirectory";
  1337. tSess.szTemplateTableName = NULL;
  1338. tSess.ulPages = 0;
  1339. tSess.ulDensity = 100;
  1340. tSess.rgcolumncreate = &cSess[0];
  1341. tSess.cColumns = NUM_SESSDIRCOLUMNS;
  1342. tSess.rgindexcreate = NULL;
  1343. tSess.cIndexes = 0;
  1344. tSess.grbit = JET_bitTableCreateFixedDDL;
  1345. for (count = 0; count < NUM_SESSDIRCOLUMNS; count++) {
  1346. cSess[count].cbStruct = sizeof(JET_COLUMNCREATE);
  1347. cSess[count].szColumnName = SessionDirectoryColumns[count].szColumnName;
  1348. cSess[count].coltyp = SessionDirectoryColumns[count].coltyp;
  1349. cSess[count].cbMax = SessionDirectoryColumns[count].colMaxLen;
  1350. cSess[count].grbit = 0;
  1351. cSess[count].pvDefault = NULL;
  1352. cSess[count].cbDefault = 0;
  1353. cSess[count].cp = 1200;
  1354. cSess[count].columnid = 0;
  1355. cSess[count].err = JET_errSuccess;
  1356. }
  1357. // Actually create the session directory table.
  1358. CALL(JetCreateTableColumnIndex(sesid, dbid, &tSess));
  1359. // Store columnids, tableid for later reference.
  1360. for (count = 0; count < NUM_SESSDIRCOLUMNS; count++) {
  1361. sesdircolumnid[count] = cSess[count].columnid;
  1362. }
  1363. sessdirtableid = tSess.tableid;
  1364. // Create server, session index.
  1365. CALL(JetCreateIndex(sesid, sessdirtableid, "primaryIndex", 0,
  1366. "+ServerID\0+SessionID\0", sizeof("+ServerID\0+SessionID\0"),
  1367. 100));
  1368. // Create index by server for deletion.
  1369. CALL(JetCreateIndex(sesid, sessdirtableid, "ServerIndex", 0,
  1370. "+ServerID\0", sizeof("+ServerID\0"), 100));
  1371. // Create index for disconnected session retrieval.
  1372. CALL(JetCreateIndex(sesid, sessdirtableid, "DiscSessionIndex", 0,
  1373. "+UserName\0+Domain\0+State\0",
  1374. sizeof("+UserName\0+Domain\0+State\0"), 100));
  1375. // Create index for all session retrieval.
  1376. CALL(JetCreateIndex(sesid, sessdirtableid, "AllSessionIndex", 0,
  1377. "+UserName\0+Domain\0",
  1378. sizeof("+UserName\0+Domain\0"), 100));
  1379. // Create server directory.
  1380. tServ.cbStruct = sizeof(tServ);
  1381. tServ.szTableName = "ServerDirectory";
  1382. tServ.szTemplateTableName = NULL;
  1383. tServ.ulPages = 0;
  1384. tServ.ulDensity = 100;
  1385. tServ.rgcolumncreate = &cServ[0];
  1386. tServ.cColumns = NUM_SERVDIRCOLUMNS;
  1387. tServ.rgindexcreate = NULL;
  1388. tServ.cIndexes = 0;
  1389. tServ.grbit = JET_bitTableCreateFixedDDL;
  1390. for (count = 0; count < NUM_SERVDIRCOLUMNS; count++) {
  1391. cServ[count].cbStruct = sizeof(JET_COLUMNCREATE);
  1392. cServ[count].szColumnName = ServerDirectoryColumns[count].szColumnName;
  1393. cServ[count].coltyp = ServerDirectoryColumns[count].coltyp;
  1394. cServ[count].cbMax = ServerDirectoryColumns[count].colMaxLen;
  1395. cServ[count].grbit = 0;
  1396. cServ[count].pvDefault = NULL;
  1397. cServ[count].cbDefault = 0;
  1398. cServ[count].cp = 1200;
  1399. cServ[count].columnid = 0;
  1400. cServ[count].err = JET_errSuccess;
  1401. }
  1402. // Set the autoincrement column to autoincrement
  1403. cServ[0].grbit |= JET_bitColumnAutoincrement;
  1404. CALL(JetCreateTableColumnIndex(sesid, dbid, &tServ));
  1405. for (count = 0; count < NUM_SERVDIRCOLUMNS; count++) {
  1406. servdircolumnid[count] = cServ[count].columnid;
  1407. }
  1408. servdirtableid = tServ.tableid;
  1409. // Create Server Name (IP) index.
  1410. CALL(JetCreateIndex(sesid, servdirtableid, "ServNameIndex", 0,
  1411. "+ServerAddress\0", sizeof("+ServerAddress\0"), 100));
  1412. // Create Server DNS host Name index.
  1413. CALL(JetCreateIndex(sesid, servdirtableid, "ServDNSNameIndex", 0,
  1414. "+ServerDNSName\0", sizeof("+ServerDNSName\0"), 100));
  1415. // Create Server ID index.
  1416. CALL(JetCreateIndex(sesid, servdirtableid, "ServerIDIndex", 0,
  1417. "+ServerID\0", sizeof("+ServerID\0"), 100));
  1418. // Create Pending Reconnect index.
  1419. CALL(JetCreateIndex(sesid, servdirtableid, "ServerAlmostInTimes", 0,
  1420. "+AlmostInTimeLow\0+AlmostInTimeHigh\0",
  1421. sizeof("+AlmostInTimeLow\0+AlmostInTimeHigh\0"), 100));
  1422. // Create the single session index.
  1423. CALL(JetCreateIndex(sesid, servdirtableid, "SingleSessionIndex", 0,
  1424. "+ClusterID\0+SingleSessionMode\0",
  1425. sizeof("+ClusterID\0+SingleSessionMode\0"), 100));
  1426. // Create the ClusterID index.
  1427. CALL(JetCreateIndex(sesid, servdirtableid, "ClusterIDIndex", 0,
  1428. "+ClusterID\0", sizeof("+ClusterID\0"), 100));
  1429. // Create cluster directory.
  1430. tClus.cbStruct = sizeof(tClus);
  1431. tClus.szTableName = "ClusterDirectory";
  1432. tClus.szTemplateTableName = NULL;
  1433. tClus.ulPages = 0;
  1434. tClus.ulDensity = 100;
  1435. tClus.rgcolumncreate = &cClus[0];
  1436. tClus.cColumns = NUM_CLUSDIRCOLUMNS;
  1437. tClus.rgindexcreate = NULL;
  1438. tClus.cIndexes = 0;
  1439. tClus.grbit = JET_bitTableCreateFixedDDL;
  1440. for (count = 0; count < NUM_CLUSDIRCOLUMNS; count++) {
  1441. cClus[count].cbStruct = sizeof(JET_COLUMNCREATE);
  1442. cClus[count].szColumnName = ClusterDirectoryColumns[count].szColumnName;
  1443. cClus[count].coltyp = ClusterDirectoryColumns[count].coltyp;
  1444. cClus[count].cbMax = ClusterDirectoryColumns[count].colMaxLen;
  1445. cClus[count].grbit = 0;
  1446. cClus[count].pvDefault = NULL;
  1447. cClus[count].cbDefault = 0;
  1448. cClus[count].cp = 1200;
  1449. cClus[count].columnid = 0;
  1450. cClus[count].err = JET_errSuccess;
  1451. }
  1452. // Set the autoincrement column to autoincrement
  1453. cClus[0].grbit |= JET_bitColumnAutoincrement;
  1454. CALL(JetCreateTableColumnIndex(sesid, dbid, &tClus));
  1455. for (count = 0; count < NUM_CLUSDIRCOLUMNS; count++) {
  1456. clusdircolumnid[count] = cClus[count].columnid;
  1457. }
  1458. clusdirtableid = tClus.tableid;
  1459. // Create Cluster Name index.
  1460. CALL(JetCreateIndex(sesid, clusdirtableid, "ClusNameIndex",
  1461. JET_bitIndexUnique, "+ClusterName\0", sizeof("+ClusterName\0"),
  1462. 100));
  1463. // Create cluster ID index.
  1464. CALL(JetCreateIndex(sesid, clusdirtableid, "ClusIDIndex", 0,
  1465. "+ClusterID\0", sizeof("+ClusterID\0"), 100));
  1466. CALL(JetCommitTransaction(sesid, 0));
  1467. // Tables were opened with exclusive access from CreateTableColumnIndex.
  1468. // Close them now.
  1469. NormalExit:
  1470. CALL(JetCloseTable(sesid, sessdirtableid));
  1471. CALL(JetCloseTable(sesid, servdirtableid));
  1472. CALL(JetCloseTable(sesid, clusdirtableid));
  1473. CALL(JetCloseDatabase(sesid, dbid, 0));
  1474. CALL(JetEndSession(sesid, 0));
  1475. LocalFree(SA.lpSecurityDescriptor);
  1476. SA.lpSecurityDescriptor = NULL;
  1477. #ifdef DBG
  1478. OutputAllTables();
  1479. #endif // DBG
  1480. return 0;
  1481. HandleError:
  1482. if (sesid != JET_sesidNil) {
  1483. // Can't really recover. Just bail out.
  1484. (VOID) JetRollback(sesid, JET_bitRollbackAll);
  1485. // Force the session closed
  1486. (VOID) JetEndSession(sesid, JET_bitForceSessionClosed);
  1487. }
  1488. if (SA.lpSecurityDescriptor != NULL) {
  1489. LocalFree(SA.lpSecurityDescriptor);
  1490. SA.lpSecurityDescriptor = NULL;
  1491. }
  1492. PostSessDirErrorValueEvent(EVENT_JET_COULDNT_INIT, err, EVENTLOG_ERROR_TYPE);
  1493. exit(1);
  1494. }
  1495. /****************************************************************************/
  1496. // DISCleanupGlobals
  1497. //
  1498. // Common cleanup code for SQL and Jet code paths.
  1499. /****************************************************************************/
  1500. void DISCleanupGlobals()
  1501. {
  1502. if (g_hStopServiceEvent != NULL) {
  1503. CloseHandle(g_hStopServiceEvent);
  1504. g_hStopServiceEvent = NULL;
  1505. }
  1506. if (g_hFileOutput != INVALID_HANDLE_VALUE) {
  1507. if (CloseHandle(g_hFileOutput) == 0) {
  1508. ERR((TB, "CloseHandle on output file failed: lasterr=0x%X",
  1509. GetLastError()));
  1510. }
  1511. g_hFileOutput = INVALID_HANDLE_VALUE;
  1512. }
  1513. if (g_hTimeFile != INVALID_HANDLE_VALUE) {
  1514. CloseHandle(g_hTimeFile);
  1515. g_hTimeFile = INVALID_HANDLE_VALUE;
  1516. }
  1517. if (g_ClusterNetworkName != NULL) {
  1518. LocalFree(g_ClusterNetworkName);
  1519. g_ClusterNetworkName = NULL;
  1520. }
  1521. if (g_hClusterToken) {
  1522. CloseHandle(g_hClusterToken);
  1523. g_hClusterToken = NULL;
  1524. }
  1525. }
  1526. #if 0
  1527. /****************************************************************************/
  1528. // DISCallSPForServer
  1529. //
  1530. // Generic function to call a stored procedure that takes a ServerAddress as an
  1531. // argument.
  1532. /****************************************************************************/
  1533. void DISCallSPForServer(WCHAR *StoredProcName, WCHAR *ServerAddress) {
  1534. HRESULT hr;
  1535. ADOCommand *pCommand;
  1536. ADOParameters *pParameters;
  1537. ADORecordset *pResultRecordSet;
  1538. hr = CreateADOStoredProcCommand(StoredProcName, &pCommand, &pParameters);
  1539. if (SUCCEEDED(hr)) {
  1540. hr = AddADOInputStringParam(ServerAddress, L"ServerAddress",
  1541. pCommand, pParameters, FALSE);
  1542. if (SUCCEEDED(hr)) {
  1543. // Execute the command.
  1544. hr = pCommand->Execute(NULL, NULL, adCmdStoredProc,
  1545. &pResultRecordSet);
  1546. if (SUCCEEDED(hr)) {
  1547. pResultRecordSet->Release();
  1548. } else {
  1549. ERR((TB, "DISCallSPForServer: Failed Execute, hr = 0x%X",
  1550. hr));
  1551. }
  1552. }
  1553. else {
  1554. ERR((TB,"DISCallSPForServer: Failed add parameter, hr=0x%X", hr));
  1555. }
  1556. pParameters->Release();
  1557. pCommand->Release();
  1558. }
  1559. else {
  1560. ERR((TB,"DISCallSPForServer: Failed create cmd, hr=0x%X", hr));
  1561. }
  1562. }
  1563. #endif
  1564. /****************************************************************************/
  1565. // DISJetHandleDeadServer
  1566. //
  1567. // When a server is not responding, this function call sends the command to the
  1568. // Jet database to remove all entries pertaining to that server.
  1569. /****************************************************************************/
  1570. void DISJetHandleDeadServer(WCHAR *ServerAddress, DWORD ServerID) {
  1571. // FailureCount is initially set to 1, TRUE, to tell SetServerAITInternal
  1572. // to increment the failure count and return the resultant count.
  1573. DWORD FailureCount = 1;
  1574. TSSDSetServerAITInternal(ServerAddress, FALSE, &FailureCount);
  1575. TSDISErrorOut(L"Server %s (%d) not responding (Failure Count: %d).\n",
  1576. ServerAddress, ServerID, FailureCount);
  1577. if (FailureCount >= g_NumberFailedPingsBeforePurge)
  1578. TSSDPurgeServer(ServerID);
  1579. }
  1580. // TODO: Possible optimization: pass in ServerID
  1581. void DISJetSetServerPingSuccessful(WCHAR *ServerAddress) {
  1582. TSSDSetServerAITInternal(ServerAddress, TRUE, NULL);
  1583. }
  1584. #if 0
  1585. /****************************************************************************/
  1586. // DISSQLHandleDeadServer
  1587. //
  1588. // When a server is not responding, this function call sends the command to the
  1589. // database to execute SP_TSDISServerNotResponding.
  1590. /****************************************************************************/
  1591. void DISSQLHandleDeadServer(WCHAR *ServerAddress) {
  1592. DISCallSPForServer(L"SP_TSDISServerNotResponding", ServerAddress);
  1593. }
  1594. void DISSQLSetServerPingSuccessful(WCHAR *ServerAddress) {
  1595. DISCallSPForServer(L"SP_TSDISSetServerPingSuccessful", ServerAddress);
  1596. }
  1597. #endif
  1598. VOID DISCtrlHandler(DWORD opcode) {
  1599. switch(opcode)
  1600. {
  1601. //case SERVICE_CONTROL_PAUSE:
  1602. // pause
  1603. // g_DISStatus.dwCurrentState = SERVICE_PAUSED;
  1604. // break;
  1605. //case SERVICE_CONTROL_CONTINUE:
  1606. // continue
  1607. // g_DISStatus.dwCurrentState = SERVICE_RUNNING;
  1608. // break;
  1609. case SERVICE_CONTROL_STOP:
  1610. //stop
  1611. g_DISStatus.dwWin32ExitCode = 0;
  1612. g_DISStatus.dwCurrentState = SERVICE_STOP_PENDING;
  1613. g_DISStatus.dwCheckPoint = 0;
  1614. g_DISStatus.dwWaitHint = 0;
  1615. if (!SetServiceStatus(g_DISStatusHandle, &g_DISStatus)) {
  1616. ERR((TB, "SetServiceStatus failed"));
  1617. }
  1618. // Here is where to actually stop the service
  1619. SetEvent(g_hStopServiceEvent);
  1620. // Should I wait for that to complete?
  1621. return;
  1622. case SERVICE_CONTROL_INTERROGATE:
  1623. // fall through to return current status
  1624. break;
  1625. default:
  1626. ERR((TB, "Unrecognized opcode to DISCtrlHandler - 0x%08x", opcode));
  1627. }
  1628. // send current status
  1629. if (!SetServiceStatus(g_DISStatusHandle, &g_DISStatus)) {
  1630. ERR((TB, "SetServiceStatus failed"));
  1631. }
  1632. }
  1633. void DISDirectoryIntegrityLoop() {
  1634. CVar varRows;
  1635. WCHAR *ServerAddress;
  1636. #if 0
  1637. WCHAR ServerAddressBuf[SERVER_ADDRESS_LENGTH];
  1638. #endif
  1639. WCHAR ServerAddressRows[10][SERVER_ADDRESS_LENGTH];
  1640. DWORD ServerIDs[10];
  1641. long NumSessionsReturned = 0;
  1642. #if 0
  1643. HRESULT hr = S_OK;
  1644. #endif
  1645. SERVER_STATUS ServerStatus;
  1646. DWORD EventStatus;
  1647. #if 0
  1648. ServerAddress = ServerAddressBuf; // In SQL case, we need a static buffer
  1649. #endif
  1650. #if 0
  1651. TSDISErrorOut(L"%s active\n", g_bUseSQL ? L"Directory Integrity Service" :
  1652. L"Session Directory");
  1653. #endif
  1654. TSDISErrorOut(L"Session Directory Active\n");
  1655. // Loop forever
  1656. for ( ; ; ) {
  1657. // Retrieve set of servers that have disconnected sessions pending
  1658. // reconnects
  1659. #if 0
  1660. if (g_bUseSQL == FALSE)
  1661. #endif
  1662. DISJetGetServersPendingReconnects(&NumSessionsReturned,
  1663. ServerAddressRows, ServerIDs);
  1664. #if 0
  1665. else
  1666. DISSQLGetServersPendingReconnects(&NumSessionsReturned,
  1667. &varRows);
  1668. #endif
  1669. // For each server,
  1670. for (DWORD i = 0; i < (unsigned)NumSessionsReturned; i++) {
  1671. #if 0
  1672. if (g_bUseSQL == FALSE)
  1673. #endif
  1674. ServerAddress = ServerAddressRows[i];
  1675. #if 0
  1676. else
  1677. hr = GetRowArrayStringField(varRows.parray, i, 0,
  1678. ServerAddress, sizeof(ServerAddressBuf) /
  1679. sizeof(WCHAR) - 1);
  1680. if (FAILED(hr)) {
  1681. ERR((TB,"DISDirectoryIntegrityLoop: Row %u returned hr=0x%X",
  1682. i, hr));
  1683. }
  1684. #endif
  1685. ServerStatus = DISGetServerStatus(ServerAddress);
  1686. // if the server does not respond, handle dead server.
  1687. // The function we call will do the right thing, which may be
  1688. // to purge immediately, or may be to simply increment a failure
  1689. // count.
  1690. if (ServerStatus == NotResponding) {
  1691. #if 0
  1692. if (FALSE == g_bUseSQL)
  1693. #endif
  1694. DISJetHandleDeadServer(ServerAddress, ServerIDs[i]);
  1695. #if 0
  1696. else
  1697. DISSQLHandleDeadServer(ServerAddress);
  1698. #endif
  1699. #ifdef DBG
  1700. OutputAllTables();
  1701. #endif // DBG
  1702. }
  1703. // else stop pinging
  1704. else if (ServerStatus == Responding) {
  1705. #if 0
  1706. if (FALSE == g_bUseSQL)
  1707. #endif
  1708. DISJetSetServerPingSuccessful(ServerAddress);
  1709. #if 0
  1710. else
  1711. DISSQLSetServerPingSuccessful(ServerAddress);
  1712. #endif
  1713. }
  1714. else {
  1715. ERR((TB, "DISDirectoryIntegrityLoop: ServerStatus enum has bad "
  1716. "value %d", ServerStatus));
  1717. }
  1718. }
  1719. // Wait DISNumberSecondsBetweenPings
  1720. EventStatus = WaitForSingleObjectEx(g_hStopServiceEvent,
  1721. DISNumberSecondsBetweenPings * 1000, FALSE);
  1722. if (EventStatus == WAIT_TIMEOUT) {
  1723. // do normal stuff
  1724. continue;
  1725. } else if (EventStatus == WAIT_OBJECT_0) {
  1726. // the event was signaled -- clean up
  1727. DISDeleteLocalGroupSecDes();
  1728. break;
  1729. } else if (EventStatus == -1) {
  1730. // there is an error
  1731. } else {
  1732. // weird output from that function
  1733. }
  1734. }
  1735. }
  1736. #if 0
  1737. /****************************************************************************/
  1738. // DISSQLStart
  1739. //
  1740. // Service main entry point for when the service is configured to verify
  1741. // SQL tables.
  1742. /****************************************************************************/
  1743. VOID DISSQLStart(DWORD argc, LPTSTR *argv) {
  1744. HRESULT hr = S_OK;
  1745. // unreferenced parameters
  1746. argv;
  1747. argc;
  1748. g_DISStatus.dwServiceType = SERVICE_WIN32;
  1749. g_DISStatus.dwCurrentState = SERVICE_START_PENDING;
  1750. g_DISStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
  1751. g_DISStatus.dwWin32ExitCode = 0;
  1752. g_DISStatus.dwServiceSpecificExitCode = 0;
  1753. g_DISStatus.dwCheckPoint = 0;
  1754. g_DISStatus.dwWaitHint = 0;
  1755. g_DISStatusHandle = RegisterServiceCtrlHandler(
  1756. _T("Directory Integrity Service"), DISCtrlHandler);
  1757. if (g_DISStatusHandle == (SERVICE_STATUS_HANDLE)0) {
  1758. ERR((TB, "DISSQLStart: RegisterServiceCtrlHandler failed"));
  1759. goto ExitFunc;
  1760. }
  1761. // Initialization code goes here
  1762. hr = DISSQLInitialize();
  1763. if (FAILED(hr)) {
  1764. ERR((TB, "DISSQLStart: DISSQLInitialize failed"));
  1765. goto PostRegisterService;
  1766. }
  1767. g_DISStatus.dwCurrentState = SERVICE_RUNNING;
  1768. g_DISStatus.dwCheckPoint = 1;
  1769. if (!SetServiceStatus(g_DISStatusHandle, &g_DISStatus)) {
  1770. ERR((TB, "DISSQLStart: SetServiceHandler failed"));
  1771. goto PostRegisterService;
  1772. }
  1773. DISDirectoryIntegrityLoop();
  1774. PostRegisterService:
  1775. g_DISStatus.dwCurrentState = SERVICE_STOPPED;
  1776. g_DISStatus.dwCheckPoint = 2;
  1777. SetServiceStatus(g_DISStatusHandle, &g_DISStatus);
  1778. ExitFunc:
  1779. DISCleanupGlobals();
  1780. }
  1781. #endif
  1782. BOOL DISGetSDAdminSid()
  1783. {
  1784. DWORD cbSid = 0;
  1785. DWORD dwErr;
  1786. BOOL rc = FALSE;
  1787. if (!CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, NULL, &cbSid)) {
  1788. dwErr = GetLastError();
  1789. if (dwErr == ERROR_INSUFFICIENT_BUFFER) {
  1790. g_pAdminSid = LocalAlloc(LMEM_FIXED, cbSid);
  1791. }
  1792. else {
  1793. TSDISErrorOut(L"DISGetSDAdminSid: CreateWellKnownSid fails with %u\n", GetLastError());
  1794. goto HandleError;
  1795. }
  1796. }
  1797. else {
  1798. goto HandleError;
  1799. }
  1800. if (NULL == g_pAdminSid) {
  1801. TSDISErrorOut(L"DISGetSDAdminSid: Memory allocation fails with %u\n", GetLastError());
  1802. goto HandleError;
  1803. }
  1804. if (!CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, g_pAdminSid, &cbSid)) {
  1805. g_pAdminSid = NULL;
  1806. TSDISErrorOut(L"DISGetSDAdminSid: CreateWellKnownSid fails with %u\n", GetLastError());
  1807. goto HandleError;
  1808. }
  1809. rc = TRUE;
  1810. HandleError:
  1811. return rc;
  1812. }
  1813. /****************************************************************************/
  1814. // DISCreateLocalGroupSecDes
  1815. //
  1816. // Create Session Directory Computers local group if not exist
  1817. // and create the security descriptor of this local group
  1818. /****************************************************************************/
  1819. BOOL DISCreateLocalGroupSecDes()
  1820. {
  1821. DWORD Error;
  1822. ULONG SidSize, ReferencedDomainNameSize;
  1823. LPWSTR ReferencedDomainName = NULL;
  1824. SID_NAME_USE SidNameUse;
  1825. WCHAR SDLocalGroupName[SDLOCALGROUPNAMELENGTH];
  1826. WCHAR SDLocalGroupDes[SDLOCALGROUPDESLENGTH];
  1827. GROUP_INFO_1 SDGroupInfo = {SDLocalGroupName, SDLocalGroupDes};
  1828. HMODULE HModule = NULL;
  1829. LPBYTE pbBuffer = NULL;
  1830. DWORD dwEntriesRead = 0, dwTotalEntry = 0;
  1831. DWORD_PTR resumehandle = NULL;
  1832. NET_API_STATUS NetStatus;
  1833. BOOL rc = FALSE;
  1834. HModule = GetModuleHandle(NULL);
  1835. if (HModule == NULL) {
  1836. Error = GetLastError();
  1837. TSDISErrorOut(L"GetModuleHandle returns error : %u\n", Error);
  1838. goto HandleError;
  1839. }
  1840. if (!LoadString(HModule, IDS_SDLOCALGROUP_NAME, SDLocalGroupName, sizeof(SDLocalGroupName) / sizeof(WCHAR)) ||
  1841. !LoadString(HModule, IDS_SDLOCALGROUP_DES, SDLocalGroupDes, sizeof(SDLocalGroupDes) / sizeof(WCHAR)))
  1842. {
  1843. TSDISErrorOut(L"LoadString fails with %u\n", GetLastError());
  1844. goto HandleError;
  1845. }
  1846. // Create local group if not exist
  1847. NetStatus = NetLocalGroupAdd(
  1848. NULL,
  1849. 1,
  1850. (LPBYTE)&SDGroupInfo,
  1851. NULL
  1852. );
  1853. if(NERR_Success != NetStatus) {
  1854. if((NERR_GroupExists != NetStatus)
  1855. && (ERROR_ALIAS_EXISTS != NetStatus)) {
  1856. //
  1857. // Didn't create the group and group doesn't exist either.
  1858. //
  1859. TSDISErrorOut(L"NetLocalGroupAdd(%s) returns error: %u\n",
  1860. SDGroupInfo.grpi1_name, NetStatus);
  1861. goto HandleError;
  1862. }
  1863. }
  1864. //
  1865. // Group created. Now lookup the SID.
  1866. //
  1867. SidSize = ReferencedDomainNameSize = 0;
  1868. ReferencedDomainName = NULL;
  1869. NetStatus = LookupAccountName(
  1870. NULL,
  1871. SDGroupInfo.grpi1_name,
  1872. g_pSid,
  1873. &SidSize,
  1874. ReferencedDomainName,
  1875. &ReferencedDomainNameSize,
  1876. &SidNameUse);
  1877. if( NetStatus )
  1878. goto HandleError;
  1879. Error = GetLastError();
  1880. if( ERROR_INSUFFICIENT_BUFFER != Error )
  1881. goto HandleError;
  1882. g_pSid = (PSID)LocalAlloc(LMEM_FIXED, SidSize);
  1883. if (NULL == g_pSid) {
  1884. goto HandleError;
  1885. }
  1886. ReferencedDomainName = (LPWSTR)LocalAlloc(LMEM_FIXED,
  1887. sizeof(WCHAR)*(1+ReferencedDomainNameSize));
  1888. if (NULL == ReferencedDomainName) {
  1889. goto HandleError;
  1890. }
  1891. NetStatus = LookupAccountName(
  1892. NULL,
  1893. SDGroupInfo.grpi1_name,
  1894. g_pSid,
  1895. &SidSize,
  1896. ReferencedDomainName,
  1897. &ReferencedDomainNameSize,
  1898. &SidNameUse
  1899. );
  1900. if( 0 == NetStatus ) {
  1901. //
  1902. // Failed.
  1903. //
  1904. Error = GetLastError();
  1905. TSDISErrorOut(L"LookupAccountName failed with %u\n", Error);
  1906. goto HandleError;
  1907. }
  1908. // Get the members of the local group
  1909. NetStatus = NetLocalGroupGetMembers(
  1910. NULL,
  1911. SDGroupInfo.grpi1_name,
  1912. 0,
  1913. &pbBuffer,
  1914. MAX_PREFERRED_LENGTH,
  1915. &dwEntriesRead,
  1916. &dwTotalEntry,
  1917. &resumehandle
  1918. );
  1919. if (NERR_Success == NetStatus) {
  1920. if (dwEntriesRead == 0) {
  1921. // Th group is emptry, throw the event log
  1922. PostSessDirErrorMsgEvent(EVENT_SD_GROUP_EMPTY, SDGroupInfo.grpi1_name, EVENTLOG_WARNING_TYPE);
  1923. }
  1924. else {
  1925. if (pbBuffer) {
  1926. NetApiBufferFree(pbBuffer);
  1927. pbBuffer = NULL;
  1928. }
  1929. }
  1930. }
  1931. else {
  1932. TSDISErrorOut(L"NetLocalGroupGetMembersfailed with %d\n", NetStatus);
  1933. }
  1934. rc = TRUE;
  1935. return rc;
  1936. HandleError:
  1937. if (ReferencedDomainName)
  1938. LocalFree(ReferencedDomainName);
  1939. // Clean
  1940. DISDeleteLocalGroupSecDes();
  1941. return rc;
  1942. }
  1943. void DISDeleteLocalGroupSecDes()
  1944. {
  1945. if (g_pSid) {
  1946. LocalFree(g_pSid);
  1947. g_pSid = NULL;
  1948. }
  1949. if (g_pAdminSid) {
  1950. LocalFree(g_pAdminSid);
  1951. g_pAdminSid = NULL;
  1952. }
  1953. }
  1954. /****************************************************************************/
  1955. // DISJetStart
  1956. //
  1957. // Service main entry point for when the service is configured to act as
  1958. // an RPC server and use Jet for all session directory transactions.
  1959. /****************************************************************************/
  1960. VOID DISJetStart(DWORD argc, LPTSTR *argv) {
  1961. RPC_STATUS Status;
  1962. RPC_BINDING_VECTOR *pBindingVector = 0;
  1963. RPC_POLICY rpcpol = {sizeof(rpcpol), 0, 0};
  1964. WCHAR *szPrincipalName = NULL;
  1965. // unreferenced parameters
  1966. argv;
  1967. argc;
  1968. g_DISStatus.dwServiceType = SERVICE_WIN32;
  1969. g_DISStatus.dwCurrentState = SERVICE_START_PENDING;
  1970. g_DISStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
  1971. g_DISStatus.dwWin32ExitCode = 0;
  1972. g_DISStatus.dwServiceSpecificExitCode = 0;
  1973. g_DISStatus.dwCheckPoint = 0;
  1974. g_DISStatus.dwWaitHint = 0;
  1975. if (g_bDebug == FALSE) {
  1976. g_DISStatusHandle = RegisterServiceCtrlHandler(
  1977. _T("Directory Integrity Service"), DISCtrlHandler);
  1978. if (g_DISStatusHandle == (SERVICE_STATUS_HANDLE)0) {
  1979. ERR((TB, "DISJetStart: RegisterServiceCtrlHandler failed"));
  1980. goto ExitFunc;
  1981. }
  1982. }
  1983. // Init the RPC server interface.
  1984. // Register the named pipe. This uses NT domain authentication.
  1985. /*
  1986. Status = RpcServerUseProtseqEp(
  1987. L"ncacn_np", // Protocol Sequence
  1988. NUM_JETRPC_THREADS, // Maximum calls at one time
  1989. L"\\pipe\\TSSD_Jet_RPC_Service", // Endpoint
  1990. NULL); // Security
  1991. */
  1992. if (!DISCreateLocalGroupSecDes()) {
  1993. ERR((TB,"DISJetStart: Error in DISCreateLocalGroupSecDEs"));
  1994. goto PostRegisterService;
  1995. }
  1996. // Get the Sid of the Admin of SD machine
  1997. DISGetSDAdminSid();
  1998. Status = RpcServerUseProtseqEx(L"ncacn_ip_tcp", 3, 0, &rpcpol);
  1999. if (Status != RPC_S_OK) {
  2000. ERR((TB,"DISJetStart: Error %d RpcUseProtseqEp on ncacn_ip_tcp",
  2001. Status));
  2002. PostSessDirErrorValueEvent(EVENT_FAIL_RPC_INIT_USEPROTSEQ, Status, EVENTLOG_ERROR_TYPE);
  2003. goto PostRegisterService;
  2004. }
  2005. // Register our interface handle (found in jetrpc.h).
  2006. Status = RpcServerRegisterIfEx(TSSDJetRPC_ServerIfHandle, NULL, NULL,
  2007. 0, RPC_C_LISTEN_MAX_CALLS_DEFAULT, SDRPCAccessCheck);
  2008. if (Status != RPC_S_OK) {
  2009. ERR((TB,"DISJetStart: Error %d RegIf", Status));
  2010. PostSessDirErrorValueEvent(EVENT_FAIL_RPC_INIT_REGISTERIF, Status, EVENTLOG_ERROR_TYPE);
  2011. goto PostRegisterService;
  2012. }
  2013. Status = RpcServerInqBindings(&pBindingVector);
  2014. if (Status != RPC_S_OK) {
  2015. ERR((TB,"DISJetStart: Error %d InqBindings", Status));
  2016. PostSessDirErrorValueEvent(EVENT_FAIL_RPC_INIT_INQBINDINGS, Status, EVENTLOG_ERROR_TYPE);
  2017. goto PostRegisterService;
  2018. }
  2019. Status = RpcEpRegister(TSSDJetRPC_ServerIfHandle, pBindingVector, 0, 0);
  2020. // TODO: Probably need to unregister, maybe delete some binding vector.
  2021. if (Status != RPC_S_OK) {
  2022. ERR((TB,"DISJetStart: Error %d EpReg", Status));
  2023. PostSessDirErrorValueEvent(EVENT_FAIL_RPC_INIT_EPREGISTER, Status, EVENTLOG_ERROR_TYPE);
  2024. goto PostRegisterService;
  2025. }
  2026. Status = RpcServerInqDefaultPrincName(RPC_C_AUTHN_GSS_NEGOTIATE, &szPrincipalName);
  2027. if (Status != RPC_S_OK) {
  2028. ERR((TB,"DISJetStart: Error %d ServerIngDefaultPrincName", Status));
  2029. PostSessDirErrorValueEvent(EVENT_FAIL_RPC_INIT_INGPRINCNAME, Status, EVENTLOG_ERROR_TYPE);
  2030. goto PostRegisterService;
  2031. }
  2032. Status = RpcServerRegisterAuthInfo(szPrincipalName, RPC_C_AUTHN_GSS_NEGOTIATE, NULL, NULL);
  2033. RpcStringFree(&szPrincipalName);
  2034. if (Status != RPC_S_OK) {
  2035. ERR((TB,"DISJetStart: Error %d ServerRegisterAuthInfo", Status));
  2036. PostSessDirErrorValueEvent(EVENT_FAIL_RPC_INIT_REGAUTHINFO, Status, EVENTLOG_ERROR_TYPE);
  2037. goto PostRegisterService;
  2038. }
  2039. // Now initialize the JET database
  2040. DISJetInitialize();
  2041. // Init the RPC to support the query for SD
  2042. Status = SDInitQueryRPC();
  2043. if (Status != RPC_S_OK) {
  2044. TSDISErrorOut(L"SDInitQueryRPC fails with %d\n", Status);
  2045. }
  2046. // Now do the RPC listen to service calls
  2047. Status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
  2048. if (Status != RPC_S_OK) {
  2049. ERR((TB,"DISJetStart: Error %d ServerListen", Status));
  2050. PostSessDirErrorValueEvent(EVENT_FAIL_RPC_LISTEN, Status, EVENTLOG_ERROR_TYPE);
  2051. goto PostRegisterService;
  2052. }
  2053. // We are now up.
  2054. g_DISStatus.dwCurrentState = SERVICE_RUNNING;
  2055. g_DISStatus.dwCheckPoint = 1;
  2056. if (g_bDebug == FALSE)
  2057. SetServiceStatus(g_DISStatusHandle, &g_DISStatus);
  2058. // Now we have the RPC server running, we can just wait for the
  2059. // service-stop event to be fired to let us know we need to exit.
  2060. // We do this inside the Directory Integrity Loop.
  2061. DISDirectoryIntegrityLoop();
  2062. // Time to clean up.
  2063. // Kill the RPC listener.
  2064. RpcServerUnregisterIf(TSSDJetRPC_ServerIfHandle, NULL, NULL);
  2065. RpcServerUnregisterIf(TSSDQUERYRPC_ServerIfHandle, NULL, NULL);
  2066. TSDISErrorOut(L"Session Directory Stopped\n");
  2067. JetTerm(g_instance);
  2068. PostRegisterService:
  2069. g_DISStatus.dwCurrentState = SERVICE_STOPPED;
  2070. g_DISStatus.dwCheckPoint = 2;
  2071. if (g_bDebug == FALSE) {
  2072. if (!SetServiceStatus(g_DISStatusHandle, &g_DISStatus)) {
  2073. ERR((TB, "SetServiceStatus failed: %d", GetLastError()));
  2074. }
  2075. }
  2076. ExitFunc:
  2077. DISCleanupGlobals();
  2078. }
  2079. /****************************************************************************/
  2080. // DISInstallService
  2081. //
  2082. // Used to install the service, returns 0 on success, nonzero otherwise.
  2083. /****************************************************************************/
  2084. int DISInstallService() {
  2085. WCHAR wzModulePathname[MAX_PATH];
  2086. SC_HANDLE hSCM = NULL, hService = NULL;
  2087. if (0 != GetModuleFileNameW(NULL, wzModulePathname, MAX_PATH)) {
  2088. hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
  2089. if (hSCM != NULL) {
  2090. hService = CreateServiceW(hSCM, L"Directory Integrity Service",
  2091. L"Directory Integrity Service", 0,
  2092. SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START,
  2093. SERVICE_ERROR_NORMAL, wzModulePathname, NULL, NULL, NULL,
  2094. NULL, NULL);
  2095. if (hService != NULL) {
  2096. CloseServiceHandle(hService);
  2097. CloseServiceHandle(hSCM);
  2098. } else {
  2099. ERR((TB, "CreateService failed, error = 0x%X", GetLastError()));
  2100. CloseServiceHandle(hSCM);
  2101. return -1;
  2102. }
  2103. } else {
  2104. ERR((TB, "OpenSCManager failed, error = 0x%X", GetLastError()));
  2105. return -1;
  2106. }
  2107. } else {
  2108. ERR((TB, "GetModuleFileNameW failed, error = 0x%X", GetLastError()));
  2109. return -1;
  2110. }
  2111. return 0;
  2112. }
  2113. /****************************************************************************/
  2114. // DISRemoveService()
  2115. //
  2116. // Used to remove the service, returns 0 on success, nonzero otherwise.
  2117. /****************************************************************************/
  2118. int DISRemoveService() {
  2119. SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
  2120. if (hSCM != NULL) {
  2121. // Open this service for DELETE access
  2122. SC_HANDLE hService = OpenServiceW(hSCM, L"Directory Integrity Service",
  2123. DELETE);
  2124. if (hService != NULL) {
  2125. // Remove this service from the SCM's database.
  2126. DeleteService(hService);
  2127. CloseServiceHandle(hService);
  2128. CloseServiceHandle(hSCM);
  2129. return 0;
  2130. } else {
  2131. ERR((TB, "Failure opening service for delete, error = 0x%X",
  2132. GetLastError()));
  2133. }
  2134. CloseServiceHandle(hService);
  2135. } else {
  2136. ERR((TB, "Failure opening SC Manager, error = 0x%X", GetLastError()));
  2137. }
  2138. return -1;
  2139. }
  2140. // Reads a DWORD value out of the registry.
  2141. //
  2142. // In:
  2143. // hKey - an open HKEY
  2144. // RegValName - the name of the registry value
  2145. // pValue - pointer to the value. The value will be set to the registry value
  2146. // if the registry operation is a success, else it will remain untouched.
  2147. //
  2148. // Out:
  2149. // 0 if success, nonzero otherwise
  2150. int ReadRegVal(HKEY hKey, WCHAR *RegValName, DWORD *pValue)
  2151. {
  2152. DWORD RegRetVal;
  2153. DWORD Type, Temp, Size;
  2154. Size = sizeof(Temp);
  2155. RegRetVal = RegQueryValueExW(hKey, RegValName, NULL, &Type,
  2156. (BYTE *)&Temp, &Size);
  2157. if (RegRetVal == ERROR_SUCCESS) {
  2158. *pValue = Temp;
  2159. return 0;
  2160. }
  2161. else {
  2162. TRC1((TB, "TSSDIS: Failed RegQuery for %S - "
  2163. "err=%u, DataSize=%u, type=%u\n",
  2164. RegValName, RegRetVal, Size, Type));
  2165. return -1;
  2166. }
  2167. }
  2168. // Reads a Unicode text value out of the registry.
  2169. //
  2170. // hKey (IN) - an open HKEY
  2171. // RegValName (IN) - the name of the registry value
  2172. // pText (IN/OUT) - pointer to the buffer to which to write.
  2173. // cbData (IN) - size of buffer IN BYTES
  2174. //
  2175. // returns 0 if success, nonzero otherwise.
  2176. int ReadRegTextVal(HKEY hKey, WCHAR *RegValName, WCHAR *pText, DWORD cbData)
  2177. {
  2178. DWORD RegRetVal;
  2179. DWORD Type, Size;
  2180. Size = cbData;
  2181. RegRetVal = RegQueryValueExW(hKey, RegValName, NULL, &Type,
  2182. (BYTE *)pText, &Size);
  2183. if (RegRetVal == ERROR_SUCCESS) {
  2184. return 0;
  2185. }
  2186. else {
  2187. TRC1((TB, "TSSDIS: Failed RegQuery for %S - err=%u, DataSize=%u, "
  2188. "type=%u\n", RegValName, RegRetVal, Size, Type));
  2189. return -1;
  2190. }
  2191. }
  2192. // Reads configuration from the registry and sets global variables.
  2193. void ReadConfigAndSetGlobals()
  2194. {
  2195. DWORD RegRetVal;
  2196. HKEY hKey;
  2197. DWORD Temp;
  2198. WCHAR WorkingDirectory[MAX_PATH];
  2199. WCHAR *pwszSD = L"D:(A;OICI;GA;;;SY)(A;OICI;GA;;;BA)(A;OICI;GA;;;CO)";
  2200. SECURITY_ATTRIBUTES SA;
  2201. BOOL br;
  2202. // Open the service settings regkey and grab the UseJet flag.
  2203. // Absence of the key or the setting means no jet.
  2204. #if 0
  2205. g_bUseSQL = FALSE;
  2206. #endif
  2207. RegRetVal = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  2208. REG_SESSION_DIRECTROY_CONTROL, 0, KEY_READ, &hKey);
  2209. if (RegRetVal == ERROR_SUCCESS) {
  2210. // With each of these calls, an error is non-fatal.
  2211. #if 0
  2212. // Query UseSQL value.
  2213. ReadRegVal(hKey, L"UseSQL", &g_bUseSQL);
  2214. #endif
  2215. // Query PingMode value. Note this is an enum so sending the variable
  2216. // in directly is illegal.
  2217. if (ReadRegVal(hKey, L"PingMode", &Temp) == 0) {
  2218. // Make sure this is a legal value for the enum.
  2219. if (Temp > AlwaysFail)
  2220. Temp = NormalMode;
  2221. g_PingMode = (PingMode) Temp;
  2222. }
  2223. // Query TraceOutputMode value. As above, enum means don't set it
  2224. // directly.
  2225. if (ReadRegVal(hKey, L"TraceOutputMode", &Temp) == 0) {
  2226. // Make sure this is a legal value for the enum.
  2227. if (Temp > FileOutput)
  2228. Temp = NoTraceOutput;
  2229. g_TraceOutputMode = (TraceOutputMode) Temp;
  2230. }
  2231. // Query NumberFailedPingsBeforePurge.
  2232. ReadRegVal(hKey, L"NumberFailedPingsBeforePurge",
  2233. &g_NumberFailedPingsBeforePurge);
  2234. // Query TimeBetweenPings.
  2235. ReadRegVal(hKey, L"TimeBetweenPings", &DISNumberSecondsBetweenPings);
  2236. // Query TimeServerSilentBeforePing.
  2237. if (ReadRegVal(hKey, L"TimeServerSilentBeforePing", &Temp) == 0) {
  2238. g_TimeServerSilentBeforePing = (ULONGLONG) Temp *
  2239. FILETIME_INTERVAL_TO_SECONDS_MULTIPLIER;
  2240. }
  2241. // Query Working Directory
  2242. if (ReadRegTextVal(hKey, L"WorkingDirectory", WorkingDirectory,
  2243. sizeof(WorkingDirectory)) == 0) {
  2244. if (SetCurrentDirectory(WorkingDirectory) == 0) {
  2245. DWORD Err;
  2246. Err = GetLastError();
  2247. PostSessDirErrorValueEvent(EVENT_PROBLEM_SETTING_WORKDIR, Err, EVENTLOG_ERROR_TYPE);
  2248. ERR((TB, "TERMSRV: Unable to set directory to value read from "
  2249. "registry. LastErr=0x%X", Err));
  2250. }
  2251. }
  2252. // Query if we reover previous jet database when starting SD
  2253. ReadRegVal(hKey, L"RecoverWhenStart", &g_RecoverWhenStart);
  2254. RegCloseKey(hKey);
  2255. // Now, if in file output mode, open the file.
  2256. if (g_TraceOutputMode == FileOutput) {
  2257. // Create security descriptor for the log file
  2258. SA.nLength = sizeof(SECURITY_ATTRIBUTES);
  2259. SA.bInheritHandle = FALSE;
  2260. SA.lpSecurityDescriptor = NULL;
  2261. br = ConvertStringSecurityDescriptorToSecurityDescriptor(pwszSD,
  2262. SDDL_REVISION_1, &(SA.lpSecurityDescriptor), NULL);
  2263. if (br == TRUE) {
  2264. g_hFileOutput = CreateFile(DEBUG_LOG_FILENAME, GENERIC_WRITE,
  2265. FILE_SHARE_READ, &SA, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
  2266. NULL);
  2267. if (g_hFileOutput == INVALID_HANDLE_VALUE) {
  2268. ERR((TB, "Could not open debug log file, lasterror=0x%X",
  2269. GetLastError()));
  2270. g_TraceOutputMode = NoTraceOutput;
  2271. }
  2272. else {
  2273. DWORD dwRetVal = 0;
  2274. // Set the insertion point to the end of the file and output
  2275. // something.
  2276. dwRetVal = SetFilePointer(g_hFileOutput, 0, NULL, FILE_END);
  2277. if (dwRetVal == INVALID_SET_FILE_POINTER) {
  2278. ERR((TB, "Could not set to end of file, lasterror=0x%X",
  2279. GetLastError()));
  2280. g_TraceOutputMode = NoTraceOutput;
  2281. }
  2282. else {
  2283. DWORD dwBytesWritten = 0;
  2284. char *pszIntro = "\n\nNEW INSTANCE\n";
  2285. if (WriteFile(g_hFileOutput, pszIntro,
  2286. (DWORD) strlen(pszIntro), &dwBytesWritten,
  2287. NULL) == 0) {
  2288. ERR((TB, "WriteFile failed, lasterr=0x%X",
  2289. GetLastError()));
  2290. }
  2291. }
  2292. }
  2293. }
  2294. else {
  2295. ERR((TB, "ConvertStringSecurityDescriptorToSecurityDescriptor fails with 0x%X",
  2296. GetLastError()));
  2297. g_TraceOutputMode = NoTraceOutput;
  2298. }
  2299. }
  2300. }
  2301. else {
  2302. WRN((TB,"TERMSRV: Unable to open settings key in HKLM, "
  2303. "lasterr=0x%X", GetLastError()));
  2304. }
  2305. }
  2306. /*****************************************************************************
  2307. * SDInitQueryRPC
  2308. *
  2309. * Setup the RPC bindings, and listen for incoming requests.
  2310. ****************************************************************************/
  2311. RPC_STATUS
  2312. SDInitQueryRPC(VOID)
  2313. {
  2314. RPC_STATUS Status;
  2315. // register the LPC (local only) interface
  2316. Status = RpcServerUseProtseqEp(
  2317. L"ncalrpc", // Protocol Sequence (LPC)
  2318. NUM_JETRPC_THREADS, // Maximum calls at one time
  2319. SD_QUERY_ENDPOINT_NAME, // Endpoint
  2320. NULL // Security
  2321. );
  2322. if( Status != RPC_S_OK ) {
  2323. ERR((TB,"SDInitQueryRPC: Error %d RpcuseProtseqEp on ncalrpc", Status));
  2324. return( Status );
  2325. }
  2326. Status = RpcServerRegisterIfEx(TSSDQUERYRPC_ServerIfHandle, NULL, NULL,
  2327. 0, RPC_C_LISTEN_MAX_CALLS_DEFAULT, SDQueryRPCAccessCheck);
  2328. if( Status != RPC_S_OK ) {
  2329. ERR((TB,"SDInitQueryRPC: Error %d RpcServerRegisterIf", Status));
  2330. return( Status );
  2331. }
  2332. return RPC_S_OK;
  2333. }
  2334. int __cdecl main() {
  2335. int nArgc;
  2336. WCHAR **ppArgv = (WCHAR **) CommandLineToArgvW(GetCommandLineW(), &nArgc);
  2337. BOOL fStartService = (nArgc < 2);
  2338. int i;
  2339. HANDLE hMutex;
  2340. if ((fStartService == FALSE) && (ppArgv == NULL)) {
  2341. PostSessDirErrorValueEvent(EVENT_NO_COMMANDLINE, GetLastError(), EVENTLOG_ERROR_TYPE);
  2342. return -1;
  2343. }
  2344. SERVICE_TABLE_ENTRY DispatchTable[] =
  2345. {
  2346. { _T("Directory Integrity Service"), DISJetStart }, // Default to the
  2347. // Jet version.
  2348. { NULL, NULL }
  2349. };
  2350. for (i = 1; i < nArgc; i++) {
  2351. if ((ppArgv[i][0] == '-') || (ppArgv[i][0] == '/')) {
  2352. if (wcscmp(&ppArgv[i][1], L"install") == 0) {
  2353. if (DISInstallService()) {
  2354. ERR((TB, "Could not install service"));
  2355. }
  2356. }
  2357. if (wcscmp(&ppArgv[i][1], L"remove") == 0) {
  2358. if (DISRemoveService()) {
  2359. ERR((TB, "Could not remove service"));
  2360. }
  2361. }
  2362. if (wcscmp(&ppArgv[i][1], L"debug") == 0) {
  2363. TSDISErrorOut(L"Debugging Jet-based Session Directory\n");
  2364. g_hStopServiceEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  2365. g_bDebug = TRUE;
  2366. // Only allow one session directory at a time. System will close the
  2367. // handle automatically when the process terminates.
  2368. hMutex = CreateMutex(NULL, FALSE,
  2369. _T("Global\\Windows Terminal Server Session Directory"));
  2370. if (hMutex == NULL) {
  2371. // Handle creation failed, not because it already existed.
  2372. PostSessDirErrorValueEvent(EVENT_PROBLEM_CREATING_MUTEX,
  2373. GetLastError(), EVENTLOG_ERROR_TYPE);
  2374. return -1;
  2375. }
  2376. if (GetLastError() == ERROR_ALREADY_EXISTS) {
  2377. // Already a session directory out there.
  2378. PostSessDirErrorValueEvent(EVENT_TWO_SESSDIRS, 0, EVENTLOG_ERROR_TYPE);
  2379. return -1;
  2380. }
  2381. // Log to stdout by default in this mode, but can be
  2382. // overridden by the registry.
  2383. g_TraceOutputMode = StdOutput;
  2384. ReadConfigAndSetGlobals();
  2385. SetConsoleCtrlHandler(DISDebugControlHandler, TRUE);
  2386. DISJetStart(nArgc, ppArgv);
  2387. }
  2388. }
  2389. }
  2390. HeapFree(GetProcessHeap(), 0, (PVOID) ppArgv);
  2391. if (fStartService) {
  2392. // Stop event - signals for the ServiceMain thread to exit.
  2393. g_hStopServiceEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  2394. ReadConfigAndSetGlobals();
  2395. #if 0
  2396. if (g_bUseSQL) {
  2397. // Switch from the default to the SQL service start.
  2398. DispatchTable[0].lpServiceProc = DISSQLStart;
  2399. }
  2400. #endif
  2401. if (!StartServiceCtrlDispatcher(DispatchTable)) {
  2402. #ifdef DBG
  2403. DWORD dw = GetLastError();
  2404. #endif // DBG
  2405. ERR((TB, "Could not start service control dispatcher, error 0x%X",
  2406. dw));
  2407. }
  2408. }
  2409. return 0;
  2410. }
  2411. #pragma warning (pop)