// *************************************************************************** // Copyright (C) 2000- Microsoft Corporation. // @File: sqlenum.cpp // // PURPOSE: // // Enumerate the sqlservers available on the local node. // // NOTES: // // // HISTORY: // // @Version: Whistler/Shiloh // 68067 srs 11/06/00 ntsnap fix // 67026 srs 10/05/00 Server enumeration bugs // // // @EndHeader@ // *************************************************************************** #ifdef HIDE_WARNINGS #pragma warning( disable : 4786) #endif #include #include //////////////////////////////////////////////////////////////////////// // Standard foo for file name aliasing. This code block must be after // all includes of VSS header files. // #ifdef VSS_FILE_ALIAS #undef VSS_FILE_ALIAS #endif #define VSS_FILE_ALIAS "SQLENUMC" // //////////////////////////////////////////////////////////////////////// //------------------------------------------------------------------- // ODBC error reporter // void PrintODBCError ( SQLSMALLINT HandleType, SQLHANDLE hHandle, CLogMsg &msg ) { CVssFunctionTracer ft(VSSDBG_SQLLIB, L"PrintODBCError"); INT i; INT j; SDWORD NativeErr; INT severity; INT msgstate; DBUSMALLINT LineNumber; WCHAR szSQLState[SQL_SQLSTATE_SIZE + 1]; WCHAR szMsg [1024 + 1]; WCHAR ServerName[SQL_MAX_SQLSERVERNAME + 1]; WCHAR ProcName [SQL_MAX_SQLSERVERNAME + 1]; if (SQLGetDiagField( HandleType, hHandle, 0, SQL_DIAG_NUMBER, &i, sizeof(i), NULL) == SQL_ERROR ) { ft.Trace(VSSDBG_SQLLIB, L"SQLGetDiagField failed"); } else { for (j = 1; j <= i; j++) { if (SQLGetDiagRecW ( HandleType, hHandle, (SQLSMALLINT)j, (SQLWCHAR*)szSQLState, &NativeErr, (SQLWCHAR*) szMsg, sizeof(szMsg) / sizeof(WCHAR), NULL) == SQL_ERROR ) { ft.Trace (VSSDBG_SQLLIB, L"SQLGetDiagRec failed"); } else { // Get driver specific diagnostic fields // if (SQLGetDiagField( HandleType, hHandle, (SQLSMALLINT)j, SQL_DIAG_SS_MSGSTATE, &msgstate, SQL_IS_INTEGER, NULL) == SQL_ERROR ) { msgstate = 0; } if (SQLGetDiagField( HandleType, hHandle, (SQLSMALLINT)j, SQL_DIAG_SS_SEVERITY, &severity, SQL_IS_INTEGER, NULL) == SQL_ERROR ) { severity = 0; } if (SQLGetDiagField( HandleType, hHandle, (SQLSMALLINT)j, SQL_DIAG_SS_SRVNAME, &ServerName, sizeof(ServerName), NULL) == SQL_ERROR ) { ServerName[0] = L'\0'; } if (SQLGetDiagField( HandleType, hHandle, (SQLSMALLINT)j, SQL_DIAG_SS_PROCNAME, &ProcName, sizeof(ProcName), NULL) == SQL_ERROR ) { ProcName[0] = L'\0'; } if (SQLGetDiagField( HandleType, hHandle, (SQLSMALLINT)j, SQL_DIAG_SS_LINE, &LineNumber, SQL_IS_SMALLINT, NULL) == SQL_ERROR ) { LineNumber = 0; } ft.Trace ( VSSDBG_SQLLIB, L"ODBC Error: Msg %d, SevLevel %d, State %d, SQLState %s\n%s\n", NativeErr, severity, msgstate, szSQLState, szMsg ); WCHAR buf[80]; swprintf(buf, L"Error: %d, Severity: %d, State: %d, SQLState: ", NativeErr, severity, msgstate); msg.Add(buf); msg.Add(szSQLState); msg.Add(L"\n ProcName: "); msg.Add(ProcName); swprintf(buf, L", Line Number: %d, ServerName: ", LineNumber); msg.Add(buf); msg.Add(L"\n"); msg.Add(szMsg); } } // for( j = 1; j <= i; j++ ) } } //------------------------------------------------------------ // Scanner to locate servernames in a "BrowseConnect" string. // class BrowseServers { public: const WCHAR* FindFirst (const WCHAR* source, unsigned* nameLen); const WCHAR* FindNext (unsigned* nameLen); private: const WCHAR* m_CurrChar; }; const WCHAR* BrowseServers::FindFirst (const WCHAR *source, unsigned* nameLen) { CVssFunctionTracer ft(VSSDBG_SQLLIB, L"BrowseServers::FindFirst"); const WCHAR* pChar; const WCHAR Prefix[] = L"Server={"; m_CurrChar = NULL; if (source == NULL) return NULL; pChar = wcsstr (source, Prefix); if (pChar == NULL) return NULL; m_CurrChar = pChar + wcslen (Prefix); if (*m_CurrChar == L'}') { // The server list is empty // m_CurrChar = NULL; return NULL; } if (nameLen) { *nameLen = wcscspn (m_CurrChar, L",;}"); } return m_CurrChar; } const WCHAR* BrowseServers::FindNext (unsigned* nameLen) { const WCHAR* pChar; if (m_CurrChar == NULL) return NULL; pChar = wcschr (m_CurrChar, L','); if (pChar == NULL) { m_CurrChar = NULL; return NULL; } m_CurrChar = pChar + 1; if (nameLen) { *nameLen = wcscspn (m_CurrChar, L",;}"); } return m_CurrChar; } //------------------------------------------------------------------------ // Return true if we could fetch the version of the SQLServer ODBC driver // bool GetSQLDriverVersion (int* major, int* minor) { CVssFunctionTracer ft(VSSDBG_SQLLIB, L"GetSQLDriverVersion"); DWORD status; HKEY hKey; WCHAR driver[MAX_PATH+1]; DWORD vType; DWORD pathLen = MAX_PATH; status = RegOpenKeyExW ( HKEY_LOCAL_MACHINE, L"Software\\ODBC\\ODBCINST.INI\\SQL Server", 0, KEY_QUERY_VALUE, &hKey); if (status != ERROR_SUCCESS) { ft.Trace (VSSDBG_SQLLIB, L"SQL not installed. Regkey status %d", status); return false; } status = (DWORD) RegQueryValueExW ( hKey, L"Driver", NULL, &vType, (BYTE*)driver, &pathLen); RegCloseKey (hKey); if (status != ERROR_SUCCESS) { ft.Trace(VSSDBG_SQLLIB, L"SQL Driver installed wrong: %d\n", status); return false; } char versionInfo [4096]; if (!GetFileVersionInfoW (driver, 0, 4096, versionInfo)) { ft.Trace(VSSDBG_SQLLIB, L"SQL Driver version not found: %d", GetLastError ()); return false; } VS_FIXEDFILEINFO* pInfo; UINT infoSize; if (!VerQueryValueW (versionInfo, L"\\", (LPVOID*)&pInfo, &infoSize)) { ft.Trace(VSSDBG_SQLLIB, L"version info resource not found: %d", GetLastError ()); return false; } *major = pInfo->dwFileVersionMS >> 16; *minor = pInfo->dwFileVersionMS & 0x0FFFF; return true; } //------------------------------------------------------------------------ // Build the list of servers on the current machine. // Throws exception if any errors occur. // StringVector* EnumerateServers () { CVssFunctionTracer ft(VSSDBG_SQLLIB, L"EnumerateServers"); SQLHENV henv = SQL_NULL_HANDLE; SQLHDBC hdbc = SQL_NULL_HANDLE; RETCODE rc; LPWSTR lpBuffer = NULL; StringVector* serverList = new StringVector; CLogMsg msg; int major, minor; if (!GetSQLDriverVersion (&major, &minor)) { // SQL isn't installed right, so just ignore it, return empty list. // return serverList; } if (major < 2000) { // Require the modern MDAC to give a proper enumeration. // ft.LogError(VSS_ERROR_SQLLIB_UNSUPPORTEDMDAC, VSSDBG_SQLLIB << major << minor); throw HRESULT (E_SQLLIB_NO_SUPPORT); // ORIGINAL CODE: // May have a 6.5 or 7.0 server. We aren't sure, but the // caller can determine if the server is up itself. // //serverList->push_back (L"(local)"); //return serverList; } // SQL2000 or better // try { if (SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv) == SQL_ERROR) { ft.LogError(VSS_ERROR_SQLLIB_SQLAllocHandle_FAILED, VSSDBG_SQLLIB); THROW_GENERIC; } rc = SQLSetEnvAttr( henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0); if (rc != SQL_SUCCESS) { PrintODBCError(SQL_HANDLE_ENV, henv, msg); if (rc == SQL_ERROR) { ft.LogError(VSS_ERROR_SQLLIB_ODBC_ERROR, VSSDBG_SQLLIB << L"SQLSetEnvAttr" << msg.GetMsg()); THROW_GENERIC; } } rc = SQLSetEnvAttr( henv, SQL_ATTR_CONNECTION_POOLING, (SQLPOINTER)SQL_CP_OFF, 0); if (rc != SQL_SUCCESS) { PrintODBCError(SQL_HANDLE_ENV, henv, msg); if (rc == SQL_ERROR) { ft.LogError(VSS_ERROR_SQLLIB_ODBC_ERROR, VSSDBG_SQLLIB << L"SQLSetEnvAttr" << msg.GetMsg()); THROW_GENERIC; } } rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc); if (rc != SQL_SUCCESS) { PrintODBCError(SQL_HANDLE_ENV, henv, msg); if (rc == SQL_ERROR) { ft.LogError(VSS_ERROR_SQLLIB_ODBC_ERROR, VSSDBG_SQLLIB << L"SQLSetAllocHandle" << msg.GetMsg()); THROW_GENERIC; } } // Note: older versions of sqlsvr32 don't support // these connect attributes, but the failure to // recognize them isn't detected until the actual // SQLBrowseConnect call. For old sqlsrv32, the // "list of servers" performs a domain search! // rc = SQLSetConnectAttr ( hdbc, SQL_COPT_SS_BROWSE_CONNECT, (SQLPOINTER)SQL_MORE_INFO_YES, SQL_IS_UINTEGER); if (rc != SQL_SUCCESS) { PrintODBCError(SQL_HANDLE_DBC, hdbc, msg); if (rc == SQL_ERROR) { ft.LogError(VSS_ERROR_SQLLIB_ODBC_ERROR, VSSDBG_SQLLIB << L"SQLSetConnectAttr" << msg.GetMsg()); THROW_GENERIC; } } rc = SQLSetConnectAttrW ( hdbc, SQL_COPT_SS_BROWSE_SERVER, (SQLPOINTER)L"(local)", SQL_NTS); if (rc != SQL_SUCCESS) { PrintODBCError(SQL_HANDLE_DBC, hdbc, msg); if (rc == SQL_ERROR) { ft.LogError(VSS_ERROR_SQLLIB_ODBC_ERROR, VSSDBG_SQLLIB << L"SQLSetConnectAttr" << msg.GetMsg()); THROW_GENERIC; } } // We use the maximum buffer supported in ODBC. // #define MAX_BUFFER 0x7ff0 lpBuffer = new WCHAR [MAX_BUFFER]; SQLSMALLINT browseLen; rc = SQLBrowseConnectW ( hdbc, L"Driver={SQL Server}", SQL_NTS, lpBuffer, MAX_BUFFER, &browseLen); //ft.Trace(VSSDBG_SQLLIB, L"browse connect rc: %d", rc); if (rc != SQL_NEED_DATA) { PrintODBCError(SQL_HANDLE_DBC, hdbc, msg); if (rc == SQL_ERROR) { ft.LogError(VSS_ERROR_SQLLIB_ODBC_ERROR, VSSDBG_SQLLIB << L"SQLSetConnectAttr" << msg.GetMsg()); THROW_GENERIC; } } // check for SQL6.5 in any of the servers // WCHAR *pVersion = lpBuffer; int srvVer; while (1) { pVersion = wcsstr (pVersion, L";Version:"); if (!pVersion) break; pVersion += 9; srvVer = 0; swscanf (pVersion, L"%u", &srvVer); if (srvVer < 7) { ft.LogError(VSS_ERROR_SQLLIB_UNSUPPORTEDSQLSERVER, VSSDBG_SQLLIB << srvVer); throw HRESULT (E_SQLLIB_NO_SUPPORT); } } //ft.Trace(VSSDBG_SQLLIB, L"BrowseResult:%s", lpBuffer); // Scan to count the servers // BrowseServers scanner; if (NULL == scanner.FindFirst (lpBuffer, NULL)) { ft.Trace(VSSDBG_SQLLIB, L"No servers found!\n"); } else { unsigned i,nameLen; const WCHAR* pServerName; unsigned int cServers = 1; while (scanner.FindNext (NULL) != NULL) { cServers++; } serverList->reserve (cServers); pServerName = scanner.FindFirst (lpBuffer, &nameLen); serverList->push_back (std::wstring (pServerName, nameLen)); i = 1; while (i < cServers) { pServerName = scanner.FindNext (&nameLen); serverList->push_back (std::wstring (pServerName, nameLen)); i++; } } if (lpBuffer) { delete [] lpBuffer; } if (hdbc) { SQLDisconnect(hdbc); SQLFreeHandle(SQL_HANDLE_DBC, hdbc); } if (henv) { SQLFreeHandle(SQL_HANDLE_ENV, henv); } } catch (...) { if (lpBuffer) { delete [] lpBuffer; } if (hdbc) { SQLDisconnect(hdbc); SQLFreeHandle(SQL_HANDLE_DBC, hdbc); } if (henv) { SQLFreeHandle(SQL_HANDLE_ENV, henv); } delete serverList; throw; } return serverList; }