f/*++ Copyright (c) 2001 Microsoft Corporation Abstract: @doc @module evtlogwriter.cxx | Implementation of the Event Log Writer @end Author: Stefan Steiner [SSteiner] 07/26/2001 TBD: Revision History: Name Date Comments ssteiner 07/26/2001 created --*/ #include "stdafx.h" #include "vs_idl.hxx" #include "vs_hash.hxx" #include #include #include "vs_seh.hxx" #include "vs_trace.hxx" #include "vs_debug.hxx" #include "vs_reg.hxx" #include "evtlogwriter.hxx" #include "allerror.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 "WRTEVTRC" // //////////////////////////////////////////////////////////////////////// static GUID s_writerId = { 0xeee8c692, 0x67ed, 0x4250, 0x8d, 0x86, 0x39, 0x6, 0x3, 0x7, 0xd, 0x00 }; // The display name of the Event Log writer. static LPCWSTR s_wszWriterName = L"Event Log Writer"; // The file group component name for the Event Log static LPCWSTR s_wszComponentName = L"Event Logs"; // The Registry key that contains event-log information static LPCWSTR s_wszLogKey = L"SYSTEM\\CurrentControlSet\\Services\\Eventlog"; // The value field that contains the the event-log file static LPCWSTR s_wszLogFile = L"File"; // The location of where the Event Log will be copied. static CBsString s_cwsEventLogsTargetDir = ROOT_BACKUP_DIR SERVICE_STATE_SUBDIR L"\\EventLogs"; // The location of the Event Log static CBsString s_cwsSystemEventLogDir = L"%SystemRoot%\\system32\\config"; // The mask specifying which files to exclude from backup static CBsString s_cwsSystemEventLogFiles = L"*.evt"; // Class wide member variables CEventLogWriter *CEventLogWriter::sm_pWriter; HRESULT CEventLogWriter::sm_hrInitialize; HRESULT STDMETHODCALLTYPE CEventLogWriter::Initialize() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CEventLogWriter::Initialize"); try { ft.hr = CVssWriter::Initialize ( s_writerId, s_wszWriterName, VSS_UT_SYSTEMSERVICE, VSS_ST_OTHER, VSS_APP_SYSTEM, 60000 ); if (ft.HrFailed()) ft.Throw ( VSSDBG_WRITER, E_UNEXPECTED, L"Failed to initialize the Event Log writer. hr = 0x%08lx", ft.hr ); ft.hr = Subscribe(); if (ft.HrFailed()) ft.Throw ( VSSDBG_WRITER, E_UNEXPECTED, L"Subscribing the Event Log server writer failed. hr = %0x08lx", ft.hr ); } VSS_STANDARD_CATCH(ft) return ft.hr; } HRESULT STDMETHODCALLTYPE CEventLogWriter::Uninitialize() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CEventLogWriter::Uninitialize"); return Unsubscribe(); } // handle request for WRITER_METADATA // implements CVssWriter::OnIdentify bool STDMETHODCALLTYPE CEventLogWriter::OnIdentify(IVssCreateWriterMetadata *pMetadata) { CVssFunctionTracer ft(VSSDBG_WRITER, L"CEventLogWriter::OnIdentify"); try { // // Set the restore method for the writer // ft.hr = pMetadata->SetRestoreMethod ( VSS_RME_RESTORE_AT_REBOOT, NULL, NULL, VSS_WRE_NEVER, true ); ft.CheckForErrorInternal(VSSDBG_WRITER, L"IVssCreateWriterMetadata::SetRestoreMethod"); // // Add the one file component // ft.hr = pMetadata->AddComponent ( VSS_CT_FILEGROUP, NULL, s_wszComponentName, s_wszComponentName, NULL, // icon 0, false, false, true ); ft.CheckForErrorInternal(VSSDBG_WRITER, L"IVssCreateWriterMetadata::AddComponent"); AddExcludes(pMetadata); ScanLogs(); AddLogs(pMetadata); } VSS_STANDARD_CATCH(ft) TranslateWriterError(ft.hr); return ft.HrSucceeded(); } bool STDMETHODCALLTYPE CEventLogWriter::OnPrepareSnapshot() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CEventLogWriter::OnPrepareSnapshot"); try { CBsString cwsExpanded; if (!cwsExpanded.ExpandEnvironmentStrings(s_cwsEventLogsTargetDir)) { ft.TranslateError(VSSDBG_WRITER, HRESULT_FROM_WIN32(::GetLastError()), L"CBsString::ExpandEnvironmentStrings"); } // make sure that we don't backup up old stuff in the event log backup dir ft.hr = ::RemoveDirectoryTree(cwsExpanded); ft.CheckForErrorInternal(VSSDBG_WRITER, L"::RemoveDirectoryTree"); ft.hr = ::CreateTargetPath(cwsExpanded); ft.CheckForError(VSSDBG_WRITER, L"::CreateTargetPath"); if (IsPathAffected(s_cwsSystemEventLogDir)) BackupLogs(); } VSS_STANDARD_CATCH( ft ); TranslateWriterError(ft.hr); ft.Trace(VSSDBG_WRITER, L"CEventLogWriter returning with hresult 0x%08lx", ft.hr); return ft.HrSucceeded(); } bool STDMETHODCALLTYPE CEventLogWriter::OnFreeze() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CEventLogWriter::OnFreeze"); return ft.HrSucceeded(); } bool STDMETHODCALLTYPE CEventLogWriter::OnThaw() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CEventLogWriter::OnThaw"); return ft.HrSucceeded(); } bool STDMETHODCALLTYPE CEventLogWriter::OnAbort() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CEventLogWriter::OnAbort"); return ft.HrSucceeded(); } bool CEventLogWriter::IsPathInSnapshot(const WCHAR *wszPath) throw() { return IsPathAffected(wszPath); } // translate a sql writer error code into a writer error void CEventLogWriter::TranslateWriterError( HRESULT hr ) { switch(hr) { default: SetWriterFailure(VSS_E_WRITERERROR_NONRETRYABLE); break; case S_OK: break; case E_OUTOFMEMORY: case HRESULT_FROM_WIN32(ERROR_DISK_FULL): case HRESULT_FROM_WIN32(ERROR_TOO_MANY_OPEN_FILES): case HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY): case HRESULT_FROM_WIN32(ERROR_NO_MORE_USER_HANDLES): SetWriterFailure(VSS_E_WRITERERROR_OUTOFRESOURCES); break; } } // Creates one instance of the Event Log writer class. If one already exists, it simple returns. // static HRESULT CEventLogWriter::CreateWriter() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CEventLogWriter::CreateWriter"); if ( sm_pWriter != NULL ) return S_OK; // // OK, spin up a thread to do the subscription in. // try { sm_pWriter = new CEventLogWriter; if (sm_pWriter == NULL) ft.Throw(VSSDBG_WRITER, E_OUTOFMEMORY, L"Allocation of CEventLogWriter object failed."); DWORD tid; HANDLE hThread = ::CreateThread ( NULL, 256* 1024, InitializeThreadFunc, NULL, 0, &tid ); if (hThread == NULL) ft.Throw ( VSSDBG_WRITER, E_UNEXPECTED, L"CreateThread failed with error %d", ::GetLastError() ); // wait for thread to complete ::WaitForSingleObject( hThread, INFINITE ); ::CloseHandle( hThread ); ft.hr = sm_hrInitialize; } VSS_STANDARD_CATCH(ft) if (ft.HrFailed() && sm_pWriter) { delete sm_pWriter; sm_pWriter = NULL; } return ft.hr; } void CEventLogWriter::FreeLogs() { // free event-log information for (int x = 0; x < m_eventLogs.GetSize(); x++) { ::VssFreeString(m_eventLogs[x].m_pwszName); ::VssFreeString(m_eventLogs[x].m_pwszFileName); } m_eventLogs.RemoveAll(); } void CEventLogWriter::ScanLogs() { CVssFunctionTracer ft (VSSDBG_WRITER, L"CEventLogWriter::ScanLogs"); FreeLogs(); // open the main registry key CVssRegistryKey hkeyEventLogList (KEY_READ); if (!hkeyEventLogList.Open(HKEY_LOCAL_MACHINE, s_wszLogKey)) { ft.Trace (VSSDBG_WRITER, L"Failed to open registry key: %s", s_wszLogKey); return; } CVssRegistryKeyIterator iterator; iterator.Attach (hkeyEventLogList); // for each subkey corresponding to a log while (!iterator.IsEOF()) { EventLog currentLog; // --- get the file entry CVssRegistryKey hkeyEventLog(KEY_QUERY_VALUE); if (!hkeyEventLog.Open(hkeyEventLogList.GetHandle(), iterator.GetCurrentKeyName())) { ft.Throw(VSSDBG_WRITER, E_UNEXPECTED, L"Failed to open registry entry for event log %s", iterator.GetCurrentKeyName()); } CVssAutoPWSZ pwszFileName; hkeyEventLog.GetValue(s_wszLogFile, pwszFileName.GetRef(), false); if (pwszFileName.GetRef() == NULL) { ft.Trace(VSSDBG_WRITER, L"Error reading File value for event log %s", iterator.GetCurrentKeyName()); iterator.MoveNext(); continue; } // ensure that the name has at least a single \ in it if (wcschr(pwszFileName, DIR_SEP_CHAR) == NULL) { ft.Trace(VSSDBG_WRITER, L"Error in the File value for event log %s", iterator.GetCurrentKeyName()); iterator.MoveNext(); continue; } // --- get the name of the log CVssAutoPWSZ pwszName; if (!pwszName.CopyFrom(iterator.GetCurrentKeyName())) ft.Throw(VSSDBG_WRITER, E_OUTOFMEMORY, L"Failed to allocate memory for event log name"); // -- add the information to the list currentLog.m_pwszFileName = pwszFileName; currentLog.m_pwszName = pwszName; if (!m_eventLogs.Add(currentLog)) ft.Throw(VSSDBG_WRITER, E_OUTOFMEMORY, L"Failed to add entry to list"); pwszFileName.Detach(); pwszName.Detach(); iterator.MoveNext(); } } void CEventLogWriter::AddExcludes(IVssCreateWriterMetadata* pMetadata) { CVssFunctionTracer ft (VSSDBG_WRITER, L"CEventLogWriter::AddExcludes"); pMetadata->AddExcludeFiles(s_cwsSystemEventLogDir, s_cwsSystemEventLogFiles, false); } void CEventLogWriter::AddLogs(IVssCreateWriterMetadata* pMetadata) { CVssFunctionTracer ft (VSSDBG_WRITER, L"CEventLogWriter::AddLogs"); // add each log file to the component for (int x = 0; x < m_eventLogs.GetSize(); x++) { CVssAutoPWSZ pwszEventPath; if (!pwszEventPath.CopyFrom(m_eventLogs[x].m_pwszFileName)) ft.Throw(VSSDBG_WRITER, E_OUTOFMEMORY, L"Failed to allocate memory for event log name"); // --- seperate out the path, and add to the component VSS_PWSZ pwszEventFile = wcsrchr(pwszEventPath, DIR_SEP_CHAR); BS_ASSERT(pwszEventFile != NULL); *pwszEventFile = L'\0'; ++pwszEventFile; ft.hr = pMetadata->AddFilesToFileGroup (NULL, s_wszComponentName, pwszEventPath, pwszEventFile, false, s_cwsEventLogsTargetDir ); ft.CheckForErrorInternal(VSSDBG_WRITER, L"IVssCreateWriterMetadata::AddFilesToFileGroup"); } } void CEventLogWriter::BackupLogs() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CEventLogWriter::BackupLogs"); // back up each event log for (int x = 0; x < m_eventLogs.GetSize(); x++) { BS_ASSERT(m_eventLogs[x].m_pwszName != NULL); BS_ASSERT(m_eventLogs[x].m_pwszName != NULL); CVssAutoWin32EventLogHandle hLog = ::OpenEventLog(NULL, m_eventLogs[x].m_pwszName); if (hLog == NULL) { ft.Throw(VSSDBG_WRITER, E_UNEXPECTED, L"Failed to open event log %s. [0x%08lx]", m_eventLogs[x].m_pwszName, ::GetLastError()); } // --- build up the path that we will back up to VSS_PWSZ pwszBackupFile = ::wcsrchr(m_eventLogs[x].m_pwszFileName, DIR_SEP_CHAR); BS_ASSERT(pwszBackupFile != NULL); CBsString cwsBackupPath; cwsBackupPath.ExpandEnvironmentStrings(s_cwsEventLogsTargetDir); cwsBackupPath += pwszBackupFile; // --- NOTE: There's a potential failure scenario here. If two different event logs have the same filename // --- in different directories, they will overwrite each other in the spit directory. This will be fixed when the // --- writer is altered to be an in-place writer if (::BackupEventLog(hLog, cwsBackupPath) == FALSE) { ft.Throw(VSSDBG_WRITER, E_UNEXPECTED, L"Failed to backup event log %s to %s. [0x%08lx]", m_eventLogs[x].m_pwszName, cwsBackupPath, ::GetLastError()); } } } // static DWORD CEventLogWriter::InitializeThreadFunc(VOID *pv) { CVssFunctionTracer ft(VSSDBG_WRITER, L"CEventLogWriter::InitializeThreadFunc"); BOOL fCoinitializeSucceeded = false; try { // intialize MTA thread ft.hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); if (ft.HrFailed()) ft.Throw ( VSSDBG_WRITER, E_UNEXPECTED, L"CoInitializeEx failed 0x%08lx", ft.hr ); fCoinitializeSucceeded = true; // Now initialize the base class and subscribe ft.hr = sm_pWriter->Initialize(); } VSS_STANDARD_CATCH(ft) if (fCoinitializeSucceeded) CoUninitialize(); sm_hrInitialize = ft.hr; return 0; UNREFERENCED_PARAMETER( pv ); } // static void CEventLogWriter::DestroyWriter() { CVssFunctionTracer ft(VSSDBG_WRITER, L"CEventLogWriter::DestroyWriter"); if (sm_pWriter) { sm_pWriter->Uninitialize(); delete sm_pWriter; sm_pWriter = NULL; } }