// *************************************************************************** // Copyright (C) 2000- Microsoft Corporation. // @File: sqlenum.cpp // // PURPOSE: // // Enumerate the sqlservers available on the local node. // // NOTES: // // // HISTORY: // // @Version: Whistler/Shiloh // 76910 SRS 08/08/01 Rollforward from VSS snapshot // 68228 12/05/00 ntsnap work // 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" // //////////////////////////////////////////////////////////////////////// //------------------------------------------------------------------------ // Determine if the given service name is for a sql server instance. // If so, return TRUE, the version (7,8,9) and the name of the server // The servername is the name used to connect to the server. // This will always be of the form: [\] // On a cluster, the ComputerName is a virtual server name. // BOOL // TRUE if the service is a sqlserver instance IsSQL ( PCWSTR pServiceName, // in: name of a service UINT* pVersion, // out: version of the sql instance WString& serverName) // out: servername to use to connect to instance { BOOL isDefault = FALSE; PCWSTR pInstanceName = NULL; if (_wcsicmp (pServiceName, L"MSSQLSERVER") != 0) { if (_wcsnicmp (pServiceName, L"MSSQL$", 6) != 0) { return FALSE; } // we have a named instance // pInstanceName = pServiceName+6; isDefault = FALSE; } else { // default instance.... pInstanceName remains null... isDefault = TRUE; } WString rootKey = L"Software\\Microsoft\\"; if (isDefault) { rootKey += L"MSSQLServer"; } else { rootKey += L"Microsoft SQL Server\\" + WString (pInstanceName); } // First determine the "machinename". // when clustered, we pull the the virtual server name from the registry. // BOOL isClustered = FALSE; WString keyName = rootKey + L"\\Cluster"; HKEY regHandle; if (RegOpenKeyExW ( HKEY_LOCAL_MACHINE, keyName.c_str (), 0, KEY_QUERY_VALUE, ®Handle) == ERROR_SUCCESS) { #define MAX_CLUSTER_NAME 256 DWORD keytype; WCHAR clusterName [MAX_CLUSTER_NAME+1]; DWORD valueLen = sizeof (clusterName)- sizeof(WCHAR); clusterName[MAX_CLUSTER_NAME] = L'\0'; if (RegQueryValueExW ( regHandle, L"ClusterName", NULL, &keytype, (LPBYTE) clusterName, &valueLen) == ERROR_SUCCESS && keytype == REG_SZ) { isClustered = TRUE; serverName = WString(clusterName); } RegCloseKey (regHandle); } if (!isClustered) { WCHAR compName [MAX_COMPUTERNAME_LENGTH + 2]; DWORD nameLen = MAX_COMPUTERNAME_LENGTH + 1; if (!GetComputerNameW (compName, &nameLen)) { // In the unlikely event that this fails, // let's just use '.' // compName [0] = L'.'; compName [1] = 0; } serverName = compName; } // For named instances, append the instance name to the "machine" name. // if (!isDefault) { serverName += L"\\" + WString (pInstanceName); } *pVersion = 9; // assume post sql2000 if we can't tell keyName = rootKey + L"\\MSSQLServer\\CurrentVersion"; if (RegOpenKeyExW ( HKEY_LOCAL_MACHINE, keyName.c_str (), 0, KEY_QUERY_VALUE, ®Handle) == ERROR_SUCCESS) { DWORD keytype; const bufferSize = 20; WCHAR versionString [bufferSize+1]; DWORD valueLen = sizeof (versionString) - sizeof(WCHAR); versionString[bufferSize] = L'\0'; if (RegQueryValueExW ( regHandle, L"CurrentVersion", NULL, &keytype, (LPBYTE) versionString, &valueLen) == ERROR_SUCCESS && keytype == REG_SZ) { swscanf (versionString, L"%d", pVersion); } RegCloseKey (regHandle); } 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"); RETCODE rc; BYTE* pBuf = NULL; std::auto_ptr serverList (new StringVector); SC_HANDLE hSCManager = NULL; BOOL restrict2000 = FALSE; // Read a registry key to see if we should avoid sql versions // beyond SQL2000. // { CVssRegistryKey restrictKey (KEY_QUERY_VALUE); if (restrictKey.Open (HKEY_LOCAL_MACHINE, x_wszVssCASettingsPath)) { DWORD val; if (restrictKey.GetValue (L"MSDEVersionChecking", val, FALSE)) { if (val != 0) { restrict2000 = TRUE; ft.Trace(VSSDBG_SQLLIB, L"Restricting Enumeration - MSDE writer will skip every SQL version newer than 2000"); } } restrictKey.Close (); } } try { // open SCM // hSCManager = OpenSCManagerW (NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT); if (hSCManager == NULL ) ft.TranslateWin32Error(VSSDBG_SQLLIB, L"OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT)"); LPENUM_SERVICE_STATUSW pServStat; DWORD bytesNeeded; DWORD sizeOfBuffer; DWORD entriesReturned; DWORD resumeHandle = 0; DWORD status; EnumServicesStatusW (hSCManager, SERVICE_WIN32, SERVICE_ACTIVE, NULL, 0, &bytesNeeded, &entriesReturned, &resumeHandle); status = GetLastError (); if (status != ERROR_MORE_DATA) ft.TranslateWin32Error(VSSDBG_SQLLIB, L"EnumServicesStatus(SERVICE_WIN32, SERVICE_STATE_ALL, ...)"); sizeOfBuffer = bytesNeeded; pBuf = new BYTE [sizeOfBuffer]; // "new" will throw on err BOOL moreExpected = FALSE; do { pServStat = (LPENUM_SERVICE_STATUSW)pBuf; moreExpected = FALSE; if (!EnumServicesStatusW (hSCManager, SERVICE_WIN32, SERVICE_ACTIVE, pServStat, sizeOfBuffer, &bytesNeeded, &entriesReturned, &resumeHandle)) { status = GetLastError (); if (status != ERROR_MORE_DATA) ft.TranslateWin32Error(VSSDBG_SQLLIB, L"EnumServicesStatus(SERVICE_WIN32, SERVICE_STATE_ALL, ...)"); moreExpected = TRUE; } while (entriesReturned-- > 0) { UINT version = 0; WString serverName; // We only need the running servers. // if (pServStat->ServiceStatus.dwCurrentState == SERVICE_RUNNING) { if (IsSQL (pServStat->lpServiceName, &version, serverName)) { ft.Trace(VSSDBG_SQLLIB, L"Service: %s Server: %s. Version=%d\n", pServStat->lpServiceName, serverName.c_str (), version); if (version >= 7) { if (version < 9 || !restrict2000) { serverList->push_back (serverName); } } } } pServStat++; } } while (moreExpected); if (pBuf) { delete [] pBuf; } if (hSCManager) { CloseServiceHandle (hSCManager); } } catch (HRESULT) { if (pBuf) { delete [] pBuf; } if (hSCManager) { CloseServiceHandle (hSCManager); } throw; } catch (std::exception) { if (pBuf) { delete [] pBuf; } if (hSCManager) { CloseServiceHandle (hSCManager); } throw; } return serverList.release(); }