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
564 lines
12 KiB
// ***************************************************************************
|
|
// 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 <stdafx.h>
|
|
#include <clogmsg.h>
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// 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;
|
|
}
|