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.

564 lines
12 KiB

  1. // ***************************************************************************
  2. // Copyright (C) 2000- Microsoft Corporation.
  3. // @File: sqlenum.cpp
  4. //
  5. // PURPOSE:
  6. //
  7. // Enumerate the sqlservers available on the local node.
  8. //
  9. // NOTES:
  10. //
  11. //
  12. // HISTORY:
  13. //
  14. // @Version: Whistler/Shiloh
  15. // 68067 srs 11/06/00 ntsnap fix
  16. // 67026 srs 10/05/00 Server enumeration bugs
  17. //
  18. //
  19. // @EndHeader@
  20. // ***************************************************************************
  21. #ifdef HIDE_WARNINGS
  22. #pragma warning( disable : 4786)
  23. #endif
  24. #include <stdafx.h>
  25. #include <clogmsg.h>
  26. ////////////////////////////////////////////////////////////////////////
  27. // Standard foo for file name aliasing. This code block must be after
  28. // all includes of VSS header files.
  29. //
  30. #ifdef VSS_FILE_ALIAS
  31. #undef VSS_FILE_ALIAS
  32. #endif
  33. #define VSS_FILE_ALIAS "SQLENUMC"
  34. //
  35. ////////////////////////////////////////////////////////////////////////
  36. //-------------------------------------------------------------------
  37. // ODBC error reporter
  38. //
  39. void PrintODBCError
  40. (
  41. SQLSMALLINT HandleType,
  42. SQLHANDLE hHandle,
  43. CLogMsg &msg
  44. )
  45. {
  46. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"PrintODBCError");
  47. INT i;
  48. INT j;
  49. SDWORD NativeErr;
  50. INT severity;
  51. INT msgstate;
  52. DBUSMALLINT LineNumber;
  53. WCHAR szSQLState[SQL_SQLSTATE_SIZE + 1];
  54. WCHAR szMsg [1024 + 1];
  55. WCHAR ServerName[SQL_MAX_SQLSERVERNAME + 1];
  56. WCHAR ProcName [SQL_MAX_SQLSERVERNAME + 1];
  57. if (SQLGetDiagField(
  58. HandleType,
  59. hHandle,
  60. 0,
  61. SQL_DIAG_NUMBER,
  62. &i,
  63. sizeof(i),
  64. NULL) == SQL_ERROR )
  65. {
  66. ft.Trace(VSSDBG_SQLLIB, L"SQLGetDiagField failed");
  67. }
  68. else
  69. {
  70. for (j = 1; j <= i; j++)
  71. {
  72. if (SQLGetDiagRecW (
  73. HandleType,
  74. hHandle,
  75. (SQLSMALLINT)j,
  76. (SQLWCHAR*)szSQLState,
  77. &NativeErr,
  78. (SQLWCHAR*) szMsg,
  79. sizeof(szMsg) / sizeof(WCHAR),
  80. NULL) == SQL_ERROR )
  81. {
  82. ft.Trace (VSSDBG_SQLLIB, L"SQLGetDiagRec failed");
  83. }
  84. else
  85. {
  86. // Get driver specific diagnostic fields
  87. //
  88. if (SQLGetDiagField(
  89. HandleType,
  90. hHandle,
  91. (SQLSMALLINT)j,
  92. SQL_DIAG_SS_MSGSTATE,
  93. &msgstate,
  94. SQL_IS_INTEGER,
  95. NULL) == SQL_ERROR )
  96. {
  97. msgstate = 0;
  98. }
  99. if (SQLGetDiagField(
  100. HandleType,
  101. hHandle,
  102. (SQLSMALLINT)j,
  103. SQL_DIAG_SS_SEVERITY,
  104. &severity,
  105. SQL_IS_INTEGER,
  106. NULL) == SQL_ERROR )
  107. {
  108. severity = 0;
  109. }
  110. if (SQLGetDiagField(
  111. HandleType,
  112. hHandle,
  113. (SQLSMALLINT)j,
  114. SQL_DIAG_SS_SRVNAME,
  115. &ServerName,
  116. sizeof(ServerName),
  117. NULL) == SQL_ERROR )
  118. {
  119. ServerName[0] = L'\0';
  120. }
  121. if (SQLGetDiagField(
  122. HandleType,
  123. hHandle,
  124. (SQLSMALLINT)j,
  125. SQL_DIAG_SS_PROCNAME,
  126. &ProcName,
  127. sizeof(ProcName),
  128. NULL) == SQL_ERROR )
  129. {
  130. ProcName[0] = L'\0';
  131. }
  132. if (SQLGetDiagField(
  133. HandleType,
  134. hHandle,
  135. (SQLSMALLINT)j,
  136. SQL_DIAG_SS_LINE,
  137. &LineNumber,
  138. SQL_IS_SMALLINT,
  139. NULL) == SQL_ERROR )
  140. {
  141. LineNumber = 0;
  142. }
  143. ft.Trace
  144. (
  145. VSSDBG_SQLLIB,
  146. L"ODBC Error: Msg %d, SevLevel %d, State %d, SQLState %s\n%s\n",
  147. NativeErr,
  148. severity,
  149. msgstate,
  150. szSQLState,
  151. szMsg
  152. );
  153. WCHAR buf[80];
  154. swprintf(buf, L"Error: %d, Severity: %d, State: %d, SQLState: ", NativeErr, severity, msgstate);
  155. msg.Add(buf);
  156. msg.Add(szSQLState);
  157. msg.Add(L"\n ProcName: ");
  158. msg.Add(ProcName);
  159. swprintf(buf, L", Line Number: %d, ServerName: ", LineNumber);
  160. msg.Add(buf);
  161. msg.Add(L"\n");
  162. msg.Add(szMsg);
  163. }
  164. } // for( j = 1; j <= i; j++ )
  165. }
  166. }
  167. //------------------------------------------------------------
  168. // Scanner to locate servernames in a "BrowseConnect" string.
  169. //
  170. class BrowseServers
  171. {
  172. public:
  173. const WCHAR* FindFirst (const WCHAR* source, unsigned* nameLen);
  174. const WCHAR* FindNext (unsigned* nameLen);
  175. private:
  176. const WCHAR* m_CurrChar;
  177. };
  178. const WCHAR*
  179. BrowseServers::FindFirst (const WCHAR *source, unsigned* nameLen)
  180. {
  181. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"BrowseServers::FindFirst");
  182. const WCHAR* pChar;
  183. const WCHAR Prefix[] = L"Server={";
  184. m_CurrChar = NULL;
  185. if (source == NULL)
  186. return NULL;
  187. pChar = wcsstr (source, Prefix);
  188. if (pChar == NULL)
  189. return NULL;
  190. m_CurrChar = pChar + wcslen (Prefix);
  191. if (*m_CurrChar == L'}')
  192. {
  193. // The server list is empty
  194. //
  195. m_CurrChar = NULL;
  196. return NULL;
  197. }
  198. if (nameLen)
  199. {
  200. *nameLen = wcscspn (m_CurrChar, L",;}");
  201. }
  202. return m_CurrChar;
  203. }
  204. const WCHAR*
  205. BrowseServers::FindNext (unsigned* nameLen)
  206. {
  207. const WCHAR* pChar;
  208. if (m_CurrChar == NULL)
  209. return NULL;
  210. pChar = wcschr (m_CurrChar, L',');
  211. if (pChar == NULL)
  212. {
  213. m_CurrChar = NULL;
  214. return NULL;
  215. }
  216. m_CurrChar = pChar + 1;
  217. if (nameLen)
  218. {
  219. *nameLen = wcscspn (m_CurrChar, L",;}");
  220. }
  221. return m_CurrChar;
  222. }
  223. //------------------------------------------------------------------------
  224. // Return true if we could fetch the version of the SQLServer ODBC driver
  225. //
  226. bool
  227. GetSQLDriverVersion (int* major, int* minor)
  228. {
  229. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"GetSQLDriverVersion");
  230. DWORD status;
  231. HKEY hKey;
  232. WCHAR driver[MAX_PATH+1];
  233. DWORD vType;
  234. DWORD pathLen = MAX_PATH;
  235. status = RegOpenKeyExW (
  236. HKEY_LOCAL_MACHINE,
  237. L"Software\\ODBC\\ODBCINST.INI\\SQL Server",
  238. 0,
  239. KEY_QUERY_VALUE,
  240. &hKey);
  241. if (status != ERROR_SUCCESS)
  242. {
  243. ft.Trace (VSSDBG_SQLLIB, L"SQL not installed. Regkey status %d", status);
  244. return false;
  245. }
  246. status = (DWORD) RegQueryValueExW (
  247. hKey,
  248. L"Driver",
  249. NULL,
  250. &vType,
  251. (BYTE*)driver,
  252. &pathLen);
  253. RegCloseKey (hKey);
  254. if (status != ERROR_SUCCESS)
  255. {
  256. ft.Trace(VSSDBG_SQLLIB, L"SQL Driver installed wrong: %d\n", status);
  257. return false;
  258. }
  259. char versionInfo [4096];
  260. if (!GetFileVersionInfoW (driver, 0, 4096, versionInfo))
  261. {
  262. ft.Trace(VSSDBG_SQLLIB, L"SQL Driver version not found: %d", GetLastError ());
  263. return false;
  264. }
  265. VS_FIXEDFILEINFO* pInfo;
  266. UINT infoSize;
  267. if (!VerQueryValueW (versionInfo, L"\\", (LPVOID*)&pInfo, &infoSize))
  268. {
  269. ft.Trace(VSSDBG_SQLLIB, L"version info resource not found: %d", GetLastError ());
  270. return false;
  271. }
  272. *major = pInfo->dwFileVersionMS >> 16;
  273. *minor = pInfo->dwFileVersionMS & 0x0FFFF;
  274. return true;
  275. }
  276. //------------------------------------------------------------------------
  277. // Build the list of servers on the current machine.
  278. // Throws exception if any errors occur.
  279. //
  280. StringVector*
  281. EnumerateServers ()
  282. {
  283. CVssFunctionTracer ft(VSSDBG_SQLLIB, L"EnumerateServers");
  284. SQLHENV henv = SQL_NULL_HANDLE;
  285. SQLHDBC hdbc = SQL_NULL_HANDLE;
  286. RETCODE rc;
  287. LPWSTR lpBuffer = NULL;
  288. StringVector* serverList = new StringVector;
  289. CLogMsg msg;
  290. int major, minor;
  291. if (!GetSQLDriverVersion (&major, &minor))
  292. {
  293. // SQL isn't installed right, so just ignore it, return empty list.
  294. //
  295. return serverList;
  296. }
  297. if (major < 2000)
  298. {
  299. // Require the modern MDAC to give a proper enumeration.
  300. //
  301. ft.LogError(VSS_ERROR_SQLLIB_UNSUPPORTEDMDAC, VSSDBG_SQLLIB << major << minor);
  302. throw HRESULT (E_SQLLIB_NO_SUPPORT);
  303. // ORIGINAL CODE:
  304. // May have a 6.5 or 7.0 server. We aren't sure, but the
  305. // caller can determine if the server is up itself.
  306. //
  307. //serverList->push_back (L"(local)");
  308. //return serverList;
  309. }
  310. // SQL2000 or better
  311. //
  312. try
  313. {
  314. if (SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv) == SQL_ERROR)
  315. {
  316. ft.LogError(VSS_ERROR_SQLLIB_SQLAllocHandle_FAILED, VSSDBG_SQLLIB);
  317. THROW_GENERIC;
  318. }
  319. rc = SQLSetEnvAttr(
  320. henv,
  321. SQL_ATTR_ODBC_VERSION,
  322. (SQLPOINTER)SQL_OV_ODBC3,
  323. 0);
  324. if (rc != SQL_SUCCESS)
  325. {
  326. PrintODBCError(SQL_HANDLE_ENV, henv, msg);
  327. if (rc == SQL_ERROR)
  328. {
  329. ft.LogError(VSS_ERROR_SQLLIB_ODBC_ERROR, VSSDBG_SQLLIB << L"SQLSetEnvAttr" << msg.GetMsg());
  330. THROW_GENERIC;
  331. }
  332. }
  333. rc = SQLSetEnvAttr(
  334. henv,
  335. SQL_ATTR_CONNECTION_POOLING,
  336. (SQLPOINTER)SQL_CP_OFF,
  337. 0);
  338. if (rc != SQL_SUCCESS)
  339. {
  340. PrintODBCError(SQL_HANDLE_ENV, henv, msg);
  341. if (rc == SQL_ERROR)
  342. {
  343. ft.LogError(VSS_ERROR_SQLLIB_ODBC_ERROR, VSSDBG_SQLLIB << L"SQLSetEnvAttr" << msg.GetMsg());
  344. THROW_GENERIC;
  345. }
  346. }
  347. rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
  348. if (rc != SQL_SUCCESS)
  349. {
  350. PrintODBCError(SQL_HANDLE_ENV, henv, msg);
  351. if (rc == SQL_ERROR)
  352. {
  353. ft.LogError(VSS_ERROR_SQLLIB_ODBC_ERROR, VSSDBG_SQLLIB << L"SQLSetAllocHandle" << msg.GetMsg());
  354. THROW_GENERIC;
  355. }
  356. }
  357. // Note: older versions of sqlsvr32 don't support
  358. // these connect attributes, but the failure to
  359. // recognize them isn't detected until the actual
  360. // SQLBrowseConnect call. For old sqlsrv32, the
  361. // "list of servers" performs a domain search!
  362. //
  363. rc = SQLSetConnectAttr (
  364. hdbc,
  365. SQL_COPT_SS_BROWSE_CONNECT,
  366. (SQLPOINTER)SQL_MORE_INFO_YES,
  367. SQL_IS_UINTEGER);
  368. if (rc != SQL_SUCCESS)
  369. {
  370. PrintODBCError(SQL_HANDLE_DBC, hdbc, msg);
  371. if (rc == SQL_ERROR)
  372. {
  373. ft.LogError(VSS_ERROR_SQLLIB_ODBC_ERROR, VSSDBG_SQLLIB << L"SQLSetConnectAttr" << msg.GetMsg());
  374. THROW_GENERIC;
  375. }
  376. }
  377. rc = SQLSetConnectAttrW (
  378. hdbc,
  379. SQL_COPT_SS_BROWSE_SERVER,
  380. (SQLPOINTER)L"(local)",
  381. SQL_NTS);
  382. if (rc != SQL_SUCCESS)
  383. {
  384. PrintODBCError(SQL_HANDLE_DBC, hdbc, msg);
  385. if (rc == SQL_ERROR)
  386. {
  387. ft.LogError(VSS_ERROR_SQLLIB_ODBC_ERROR, VSSDBG_SQLLIB << L"SQLSetConnectAttr" << msg.GetMsg());
  388. THROW_GENERIC;
  389. }
  390. }
  391. // We use the maximum buffer supported in ODBC.
  392. //
  393. #define MAX_BUFFER 0x7ff0
  394. lpBuffer = new WCHAR [MAX_BUFFER];
  395. SQLSMALLINT browseLen;
  396. rc = SQLBrowseConnectW (
  397. hdbc,
  398. L"Driver={SQL Server}",
  399. SQL_NTS,
  400. lpBuffer,
  401. MAX_BUFFER,
  402. &browseLen);
  403. //ft.Trace(VSSDBG_SQLLIB, L"browse connect rc: %d", rc);
  404. if (rc != SQL_NEED_DATA)
  405. {
  406. PrintODBCError(SQL_HANDLE_DBC, hdbc, msg);
  407. if (rc == SQL_ERROR)
  408. {
  409. ft.LogError(VSS_ERROR_SQLLIB_ODBC_ERROR, VSSDBG_SQLLIB << L"SQLSetConnectAttr" << msg.GetMsg());
  410. THROW_GENERIC;
  411. }
  412. }
  413. // check for SQL6.5 in any of the servers
  414. //
  415. WCHAR *pVersion = lpBuffer;
  416. int srvVer;
  417. while (1)
  418. {
  419. pVersion = wcsstr (pVersion, L";Version:");
  420. if (!pVersion)
  421. break;
  422. pVersion += 9;
  423. srvVer = 0;
  424. swscanf (pVersion, L"%u", &srvVer);
  425. if (srvVer < 7)
  426. {
  427. ft.LogError(VSS_ERROR_SQLLIB_UNSUPPORTEDSQLSERVER, VSSDBG_SQLLIB << srvVer);
  428. throw HRESULT (E_SQLLIB_NO_SUPPORT);
  429. }
  430. }
  431. //ft.Trace(VSSDBG_SQLLIB, L"BrowseResult:%s", lpBuffer);
  432. // Scan to count the servers
  433. //
  434. BrowseServers scanner;
  435. if (NULL == scanner.FindFirst (lpBuffer, NULL))
  436. {
  437. ft.Trace(VSSDBG_SQLLIB, L"No servers found!\n");
  438. }
  439. else
  440. {
  441. unsigned i,nameLen;
  442. const WCHAR* pServerName;
  443. unsigned int cServers = 1;
  444. while (scanner.FindNext (NULL) != NULL)
  445. {
  446. cServers++;
  447. }
  448. serverList->reserve (cServers);
  449. pServerName = scanner.FindFirst (lpBuffer, &nameLen);
  450. serverList->push_back (std::wstring (pServerName, nameLen));
  451. i = 1;
  452. while (i < cServers)
  453. {
  454. pServerName = scanner.FindNext (&nameLen);
  455. serverList->push_back (std::wstring (pServerName, nameLen));
  456. i++;
  457. }
  458. }
  459. if (lpBuffer)
  460. {
  461. delete [] lpBuffer;
  462. }
  463. if (hdbc)
  464. {
  465. SQLDisconnect(hdbc);
  466. SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
  467. }
  468. if (henv)
  469. {
  470. SQLFreeHandle(SQL_HANDLE_ENV, henv);
  471. }
  472. }
  473. catch (...)
  474. {
  475. if (lpBuffer)
  476. {
  477. delete [] lpBuffer;
  478. }
  479. if (hdbc)
  480. {
  481. SQLDisconnect(hdbc);
  482. SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
  483. }
  484. if (henv)
  485. {
  486. SQLFreeHandle(SQL_HANDLE_ENV, henv);
  487. }
  488. delete serverList;
  489. throw;
  490. }
  491. return serverList;
  492. }