/*++ Copyright (c) 1999 Microsoft Corporation Abstract: @doc @module vssadmin.cpp | Implementation of the Volume Snapshots demo @end Author: Adi Oltean [aoltean] 09/17/1999 TBD: Add comments. Revision History: Name Date Comments aoltean 09/17/1999 Created --*/ ///////////////////////////////////////////////////////////////////////////// // Includes // The rest of includes are specified here #include "vssadmin.h" #include #include // in public\internal\base\inc //////////////////////////////////////////////////////////////////////// // 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 "ADMVADMC" // //////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // Implementation CVssAdminCLI::CVssAdminCLI( IN HINSTANCE hInstance ) /*++ Description: Standard constructor. Initializes internal members --*/ { BS_ASSERT(hInstance != NULL); m_hInstance = hInstance; m_eCommandType = VSS_CMD_UNKNOWN; m_eListType = VSS_LIST_UNKNOWN; m_eFilterObjectType = VSS_OBJECT_UNKNOWN; m_eListedObjectType = VSS_OBJECT_UNKNOWN; m_FilterSnapshotSetId = GUID_NULL; m_FilterSnapshotId = GUID_NULL; m_pwszCmdLine = NULL; m_nReturnValue = VSS_CMDRET_ERROR; m_hConsoleOutput = INVALID_HANDLE_VALUE; } CVssAdminCLI::~CVssAdminCLI() /*++ Description: Standard destructor. Calls Finalize and eventually frees the memory allocated by internal members. --*/ { // Release the cached resource strings for( int nIndex = 0; nIndex < m_mapCachedResourceStrings.GetSize(); nIndex++) { LPCWSTR& pwszResString = m_mapCachedResourceStrings.GetValueAt(nIndex); ::VssFreeString(pwszResString); } // Release the cached provider names for( int nIndex = 0; nIndex < m_mapCachedProviderNames.GetSize(); nIndex++) { LPCWSTR& pwszProvName = m_mapCachedProviderNames.GetValueAt(nIndex); ::VssFreeString(pwszProvName); } // Release the cached command line ::VssFreeString(m_pwszCmdLine); // Uninitialize the COM library Finalize(); } ///////////////////////////////////////////////////////////////////////////// // Implementation LPCWSTR CVssAdminCLI::LoadString( IN CVssFunctionTracer& ft, IN UINT uStringId ) { LPCWSTR wszReturnedString = m_mapCachedResourceStrings.Lookup(uStringId); if (wszReturnedString) return wszReturnedString; // Load the string from resources. WCHAR wszBuffer[nStringBufferSize]; INT nReturnedCharacters = ::LoadStringW( GetInstance(), uStringId, wszBuffer, nStringBufferSize - 1 ); if (nReturnedCharacters == 0) ft.Throw( VSSDBG_COORD, E_UNEXPECTED, L"Error on loading the string %u. 0x%08lx", uStringId, ::GetLastError() ); // Duplicate the new string LPWSTR wszNewString = NULL; ::VssSafeDuplicateStr( ft, wszNewString, wszBuffer ); wszReturnedString = wszNewString; // Save the string in the cache if ( !m_mapCachedResourceStrings.Add( uStringId, wszReturnedString ) ) { ::VssFreeString( wszReturnedString ); ft.Throw( VSSDBG_COORD, E_OUTOFMEMORY, L"Memory allocation error"); } return wszReturnedString; } LPCWSTR CVssAdminCLI::GetNextCmdlineToken( IN CVssFunctionTracer& ft, IN bool bFirstToken /* = false */ ) throw(HRESULT) /*++ Description: This function returns the tokens in the command line. The function will skip any separators (space and tab). If bFirstCall == true then it will return the first token. Otherwise subsequent calls will return subsequent tokens. If the last token is NULL then there are no more tokens in the command line. --*/ { return ::wcstok( bFirstToken? m_pwszCmdLine: NULL, wszVssFmtSpaces ); UNREFERENCED_PARAMETER(ft); } bool CVssAdminCLI::Match( IN CVssFunctionTracer& ft, IN LPCWSTR wszString, IN LPCWSTR wszPatternString ) throw(HRESULT) /*++ Description: This function returns true iif the given string matches the pattern string. The comparison is case insensitive. --*/ { // If the string is NULL then the Match failed. if (wszString == NULL) return false; // Check for string equality (case insensitive) return (::_wcsicmp( wszString, wszPatternString ) == 0); UNREFERENCED_PARAMETER(ft); } bool CVssAdminCLI::ScanGuid( IN CVssFunctionTracer& /* ft */, IN LPCWSTR wszString, IN VSS_ID& Guid ) throw(HRESULT) /*++ Description: This function returns true iif the given string matches a guid. The guid is returned in the proper variable. The formatting is case insensitive. --*/ { return SUCCEEDED(::CLSIDFromString(W2OLE(const_cast(wszString)), &Guid)); } void CVssAdminCLI::Output( IN CVssFunctionTracer& ft, IN UINT uFormatStringId, ... ) throw(HRESULT) /*++ Description: This function returns true iif the given string matches the pattern strig from resources. The comparison is case insensitive. --*/ { WCHAR wszOutputBuffer[nStringBufferSize]; // Load the format string LPCWSTR wszFormat = LoadString( ft, uFormatStringId ); // Format the final string va_list marker; va_start( marker, uFormatStringId ); _vsnwprintf( wszOutputBuffer, nStringBufferSize - 1, wszFormat, marker ); va_end( marker ); // Print the final string to the output OutputOnConsole( wszOutputBuffer ); } void CVssAdminCLI::Output( IN CVssFunctionTracer& ft, IN LPCWSTR wszFormat, ... ) throw(HRESULT) /*++ Description: This function returns true iif the given string matches the pattern strig from resources. The comparison is case insensitive. --*/ { WCHAR wszOutputBuffer[nStringBufferSize]; // Format the final string va_list marker; va_start( marker, wszFormat ); _vsnwprintf( wszOutputBuffer, nStringBufferSize - 1, wszFormat, marker ); va_end( marker ); // Print the final string to the output OutputOnConsole( wszOutputBuffer ); UNREFERENCED_PARAMETER(ft); } void CVssAdminCLI::OutputOnConsole( IN LPCWSTR wszStr ) { DWORD dwCharsOutput; DWORD fdwMode; static BOOL bFirstTime = TRUE; static BOOL bIsTrueConsoleOutput; if ( m_hConsoleOutput == INVALID_HANDLE_VALUE ) { throw E_UNEXPECTED; } if ( bFirstTime ) { // // Stash away the results in static vars. bIsTrueConsoleOutput is TRUE when the // standard output handle is pointing to a console character device. // bIsTrueConsoleOutput = ( ::GetFileType( m_hConsoleOutput ) & FILE_TYPE_CHAR ) && ::GetConsoleMode( m_hConsoleOutput, &fdwMode ); bFirstTime = FALSE; } if ( bIsTrueConsoleOutput ) { // // Output to the console // if ( !::WriteConsoleW( m_hConsoleOutput, ( PVOID )wszStr, ( DWORD )::wcslen( wszStr ), &dwCharsOutput, NULL ) ) { throw HRESULT_FROM_WIN32( ::GetLastError() ); } } else { // // Output being redirected. WriteConsoleW doesn't work for redirected output. Convert // UNICODE to the current output CP multibyte charset. // // ---------------- To be removed later - Start ----------------- // // Translate \n to \r\n since the string might be directed to a disk file. // Remove this code if the .rc file can be updated to include // \r\n. This was needed since we are in UI lockdown. // LPWSTR pwszConversion; // Allocate a string twice the size of the original pwszConversion = ( LPWSTR )::malloc( ( ( ::wcslen( wszStr ) * 2 ) + 1 ) * sizeof( WCHAR ) ); if ( pwszConversion == NULL ) { throw E_OUTOFMEMORY; } // Copy the string to the new string and place a \r in front of any \n. Also // handle the case if \r\n is already in the string. DWORD dwIdx = 0; while ( wszStr[0] != L'\0' ) { if ( wszStr[0] == L'\r' && wszStr[1] == L'\n' ) { pwszConversion[dwIdx++] = L'\r'; pwszConversion[dwIdx++] = L'\n'; wszStr += 2; } else if ( wszStr[0] == L'\n' ) { pwszConversion[dwIdx++] = L'\r'; pwszConversion[dwIdx++] = L'\n'; wszStr += 1; } else { pwszConversion[dwIdx++] = wszStr[0]; wszStr += 1; } } pwszConversion[dwIdx] = L'\0'; // ---------------- To be removed later - End ----------------- LPSTR pszMultibyteBuffer; DWORD dwByteCount; // // Get size of temp buffer needed for the conversion. // dwByteCount = ::WideCharToMultiByte( ::GetConsoleOutputCP(), 0, pwszConversion, -1, NULL, 0, NULL, NULL ); if ( dwByteCount == 0 ) { ::free( pwszConversion ); throw HRESULT_FROM_WIN32( ::GetLastError() ); } pszMultibyteBuffer = ( LPSTR )::malloc( dwByteCount ); if ( pszMultibyteBuffer == NULL ) { ::free( pwszConversion ); throw E_OUTOFMEMORY; } // // Now convert it. // dwByteCount = ::WideCharToMultiByte( ::GetConsoleOutputCP(), 0, pwszConversion, -1, pszMultibyteBuffer, dwByteCount, NULL, NULL ); ::free( pwszConversion ); if ( dwByteCount == 0 ) { ::free( pszMultibyteBuffer ); throw HRESULT_FROM_WIN32( ::GetLastError() ); } // Finally output it. if ( !::WriteFile( m_hConsoleOutput, pszMultibyteBuffer, dwByteCount - 1, // Get rid of the trailing NULL char &dwCharsOutput, NULL ) ) { throw HRESULT_FROM_WIN32( ::GetLastError() ); } ::free( pszMultibyteBuffer ); } } LPCWSTR CVssAdminCLI::GetProviderName( IN CVssFunctionTracer& ft, IN VSS_ID& ProviderId ) throw(HRESULT) { LPCWSTR wszReturnedString = m_mapCachedProviderNames.Lookup(ProviderId); if (wszReturnedString) return wszReturnedString; CComPtr pICoord; ft.LogVssStartupAttempt(); ft.hr = pICoord.CoCreateInstance( CLSID_VSSCoordinator ); if ( ft.HrFailed() ) ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"Connection failed with hr = 0x%08lx", ft.hr); CComPtr pIEnumProvider; ft.hr = pICoord->Query( GUID_NULL, VSS_OBJECT_NONE, VSS_OBJECT_PROVIDER, &pIEnumProvider ); if ( ft.HrFailed() ) ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"Query failed with hr = 0x%08lx", ft.hr); VSS_OBJECT_PROP Prop; VSS_PROVIDER_PROP& Prov = Prop.Obj.Prov; // Go through the list of providers to find the one we are interested in. ULONG ulFetched; while( 1 ) { ft.hr = pIEnumProvider->Next( 1, &Prop, &ulFetched ); if ( ft.HrFailed() ) ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"Next failed with hr = 0x%08lx", ft.hr); if (ft.hr == S_FALSE) { // End of enumeration. // Provider not registered? Where did this snapshot come from? // It might be still possible if a snapshot provider was deleted // before querying the snapshot provider but after the snapshot attributes // were queried. BS_ASSERT(ulFetched == 0); return LoadString( ft, IDS_UNKNOWN_PROVIDER ); } if (Prov.m_ProviderId == ProviderId) break; } // Duplicate the new string LPWSTR wszNewString = NULL; BS_ASSERT(Prov.m_pwszProviderName); ::VssSafeDuplicateStr( ft, wszNewString, Prov.m_pwszProviderName ); wszReturnedString = wszNewString; // Save the string in the cache if (!m_mapCachedProviderNames.Add( ProviderId, wszReturnedString )) { ::VssFreeString( wszReturnedString ); ft.Throw( VSSDBG_COORD, E_OUTOFMEMORY, L"Memory allocation error"); } return wszReturnedString; } ///////////////////////////////////////////////////////////////////////////// // Implementation void CVssAdminCLI::Initialize( IN CVssFunctionTracer& ft ) throw(HRESULT) /*++ Description: Initializes the COM library. Called explicitely after instantiating the CVssAdminCLI object. --*/ { // Use the OEM code page ... ::setlocale(LC_ALL, ".OCP"); // Use the console UI language ::SetThreadUILanguage( 0 ); // // Use only the Console routines to output messages. To do so, need to open standard // output. // m_hConsoleOutput = ::GetStdHandle(STD_OUTPUT_HANDLE); if (m_hConsoleOutput == INVALID_HANDLE_VALUE) { ft.Throw( VSSDBG_VSSADMIN, HRESULT_FROM_WIN32( ::GetLastError() ), L"Initialize - Error from GetStdHandle(), rc: %d", ::GetLastError() ); } // Initialize COM library ft.hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if (ft.HrFailed()) ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"Failure in initializing the COM library 0x%08lx", ft.hr); // Initialize COM security ft.hr = CoInitializeSecurity( NULL, // IN PSECURITY_DESCRIPTOR pSecDesc, -1, // IN LONG cAuthSvc, NULL, // IN SOLE_AUTHENTICATION_SERVICE *asAuthSvc, NULL, // IN void *pReserved1, RPC_C_AUTHN_LEVEL_CONNECT, // IN DWORD dwAuthnLevel, RPC_C_IMP_LEVEL_IMPERSONATE, // IN DWORD dwImpLevel, NULL, // IN void *pAuthList, EOAC_NONE, // IN DWORD dwCapabilities, NULL // IN void *pReserved3 ); if (ft.HrFailed()) { ft.Throw( VSSDBG_VSSADMIN, ft.hr, L" Error: CoInitializeSecurity() returned 0x%08lx", ft.hr ); } // Print the header Output( ft, IDS_HEADER ); // Initialize the command line ::VssSafeDuplicateStr( ft, m_pwszCmdLine, ::GetCommandLineW() ); } void CVssAdminCLI::ParseCmdLine( IN CVssFunctionTracer& ft ) throw(HRESULT) /*++ Description: Parses the command line. --*/ { // Skip the executable name GetNextCmdlineToken( ft, true ); // Get the first token after the executable name LPCWSTR pwszArg1 = GetNextCmdlineToken( ft ); // Check if this is a list process if ( Match( ft, pwszArg1, wszVssOptList)) { m_eCommandType = VSS_CMD_LIST; // Get the next token after "list" LPCWSTR pwszArg2 = GetNextCmdlineToken( ft ); // Check if this is a list snapshots process if ( Match( ft, pwszArg2, wszVssOptSnapshots )) { m_eListType = VSS_LIST_SNAPSHOTS; // Get the next token after "snapshots" LPCWSTR pwszArg3 = GetNextCmdlineToken( ft ); if ( pwszArg3 == NULL ) { m_FilterSnapshotSetId = GUID_NULL; m_eFilterObjectType = VSS_OBJECT_NONE; return; } // Check if this is a snapshot set filter if ( ::_wcsnicmp( pwszArg3, wszVssOptSet, ::wcslen( wszVssOptSet ) ) == 0 ) { // Get the next token after "snapshots" LPCWSTR pwszArg4 = pwszArg3 + ::wcslen( wszVssOptSet ); // Get the snapshot set Id if ( (pwszArg4[0] != '\0' ) && ScanGuid( ft, pwszArg4, m_FilterSnapshotSetId ) && (GetNextCmdlineToken(ft) == NULL) ) { m_eFilterObjectType = VSS_OBJECT_SNAPSHOT_SET; return; } } } // Check if this is a list writers process if ( Match( ft, pwszArg2, wszVssOptWriters) && (GetNextCmdlineToken(ft) == NULL)) { m_eListType = VSS_LIST_WRITERS; m_FilterSnapshotSetId = GUID_NULL; m_eFilterObjectType = VSS_OBJECT_NONE; return; } // Check if this is a list providers process if ( Match( ft, pwszArg2, wszVssOptProviders)&& (GetNextCmdlineToken(ft) == NULL)) { m_eListType = VSS_LIST_PROVIDERS; m_FilterSnapshotSetId = GUID_NULL; m_eFilterObjectType = VSS_OBJECT_NONE; return; } } else if (pwszArg1 == NULL) m_nReturnValue = VSS_CMDRET_SUCCESS; m_eCommandType = VSS_CMD_USAGE; } void CVssAdminCLI::DoProcessing( IN CVssFunctionTracer& ft ) throw(HRESULT) { switch( m_eCommandType) { case VSS_CMD_USAGE: PrintUsage(ft); break; case VSS_CMD_LIST: switch (m_eListType) { case VSS_LIST_SNAPSHOTS: ListSnapshots(ft); break; case VSS_LIST_WRITERS: ListWriters(ft); break; case VSS_LIST_PROVIDERS: ListProviders(ft); break; default: ft.Throw( VSSDBG_COORD, E_UNEXPECTED, L"Invalid list type: %d %d", m_eListType, m_eCommandType); } break; default: ft.Throw( VSSDBG_COORD, E_UNEXPECTED, L"Invalid command type: %d", m_eCommandType); } } void CVssAdminCLI::Finalize() /*++ Description: Uninitialize the COM library. Called in CVssAdminCLI destructor. --*/ { // Uninitialize COM library CoUninitialize(); } HRESULT CVssAdminCLI::Main( IN HINSTANCE hInstance ) /*++ Function: CVssAdminCLI::Main Description: Static function used as the main entry point in the VSS CLI --*/ { CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdminCLI::Main" ); INT nReturnValue = VSS_CMDRET_ERROR; try { CVssAdminCLI program(hInstance); try { // Initialize the program. This calls CoInitialize() program.Initialize(ft); // Parse the command line program.ParseCmdLine(ft); // Do the work... program.DoProcessing(ft); } VSS_STANDARD_CATCH(ft) // Prints the error, if any if (ft.HrFailed()) program.Output( ft, IDS_ERROR, ft.hr ); nReturnValue = program.GetReturnValue(); // The destructor automatically calls CoUninitialize() } VSS_STANDARD_CATCH(ft) return nReturnValue; } ///////////////////////////////////////////////////////////////////////////// // WinMain extern "C" int WINAPI _tWinMain( IN HINSTANCE hInstance, IN HINSTANCE /*hPrevInstance*/, IN LPTSTR /*lpCmdLine*/, IN int /*nShowCmd*/) { return CVssAdminCLI::Main(hInstance); }