203 lines
5.6 KiB
C++
203 lines
5.6 KiB
C++
// Copyright (c) .NET Foundation. All rights reserved.
|
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
|
|
|
#include "stdafx.h"
|
|
#include "FileOutputManager.h"
|
|
#include "sttimer.h"
|
|
#include "exceptions.h"
|
|
#include "debugutil.h"
|
|
#include "SRWExclusiveLock.h"
|
|
#include "file_utility.h"
|
|
#include "StdWrapper.h"
|
|
|
|
extern HINSTANCE g_hModule;
|
|
|
|
FileOutputManager::FileOutputManager() :
|
|
FileOutputManager(/* fEnableNativeLogging */ true) { }
|
|
|
|
FileOutputManager::FileOutputManager(bool fEnableNativeLogging) :
|
|
m_hLogFileHandle(INVALID_HANDLE_VALUE),
|
|
m_disposed(false),
|
|
stdoutWrapper(nullptr),
|
|
stderrWrapper(nullptr),
|
|
m_fEnableNativeRedirection(fEnableNativeLogging)
|
|
{
|
|
InitializeSRWLock(&m_srwLock);
|
|
}
|
|
|
|
FileOutputManager::~FileOutputManager()
|
|
{
|
|
FileOutputManager::Stop();
|
|
}
|
|
|
|
HRESULT
|
|
FileOutputManager::Initialize(PCWSTR pwzStdOutLogFileName, PCWSTR pwzApplicationPath)
|
|
{
|
|
RETURN_IF_FAILED(m_wsApplicationPath.Copy(pwzApplicationPath));
|
|
RETURN_IF_FAILED(m_wsStdOutLogFileName.Copy(pwzStdOutLogFileName));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// Start redirecting stdout and stderr into the file handle.
|
|
// Uses sttimer to continuously flush output into the file.
|
|
HRESULT
|
|
FileOutputManager::Start()
|
|
{
|
|
SYSTEMTIME systemTime;
|
|
SECURITY_ATTRIBUTES saAttr = { 0 };
|
|
STRU struPath;
|
|
FILETIME processCreationTime;
|
|
FILETIME dummyFileTime;
|
|
|
|
// To make Console.* functions work, allocate a console
|
|
// in the current process.
|
|
if (!AllocConsole())
|
|
{
|
|
// ERROR_ACCESS_DENIED means there is a console already present.
|
|
if (GetLastError() != ERROR_ACCESS_DENIED)
|
|
{
|
|
RETURN_LAST_ERROR();
|
|
}
|
|
}
|
|
|
|
// Concatenate the log file name and application path
|
|
RETURN_IF_FAILED(FILE_UTILITY::ConvertPathToFullPath(
|
|
m_wsStdOutLogFileName.QueryStr(),
|
|
m_wsApplicationPath.QueryStr(),
|
|
&struPath));
|
|
|
|
RETURN_IF_FAILED(FILE_UTILITY::EnsureDirectoryPathExist(struPath.QueryStr()));
|
|
|
|
|
|
// TODO fix string as it is incorrect
|
|
RETURN_LAST_ERROR_IF(!GetProcessTimes(
|
|
GetCurrentProcess(),
|
|
&processCreationTime,
|
|
&dummyFileTime,
|
|
&dummyFileTime,
|
|
&dummyFileTime));
|
|
RETURN_LAST_ERROR_IF(!FileTimeToSystemTime(&processCreationTime, &systemTime));
|
|
|
|
RETURN_IF_FAILED(
|
|
m_struLogFilePath.SafeSnwprintf(L"%s_%d%02d%02d%02d%02d%02d_%d.log",
|
|
struPath.QueryStr(),
|
|
systemTime.wYear,
|
|
systemTime.wMonth,
|
|
systemTime.wDay,
|
|
systemTime.wHour,
|
|
systemTime.wMinute,
|
|
systemTime.wSecond,
|
|
GetCurrentProcessId()));
|
|
|
|
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
saAttr.bInheritHandle = TRUE;
|
|
saAttr.lpSecurityDescriptor = NULL;
|
|
|
|
// Create the file with both READ and WRITE.
|
|
m_hLogFileHandle = CreateFileW(m_struLogFilePath.QueryStr(),
|
|
FILE_READ_DATA | FILE_WRITE_DATA,
|
|
FILE_SHARE_READ,
|
|
&saAttr,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
nullptr);
|
|
|
|
RETURN_LAST_ERROR_IF(m_hLogFileHandle == INVALID_HANDLE_VALUE);
|
|
|
|
stdoutWrapper = std::make_unique<StdWrapper>(stdout, STD_OUTPUT_HANDLE, m_hLogFileHandle, m_fEnableNativeRedirection);
|
|
stderrWrapper = std::make_unique<StdWrapper>(stderr, STD_ERROR_HANDLE, m_hLogFileHandle, m_fEnableNativeRedirection);
|
|
|
|
stdoutWrapper->StartRedirection();
|
|
stderrWrapper->StartRedirection();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
FileOutputManager::Stop()
|
|
{
|
|
STRA straStdOutput;
|
|
CHAR pzFileContents[MAX_FILE_READ_SIZE] = { 0 };
|
|
DWORD dwNumBytesRead;
|
|
LARGE_INTEGER li = { 0 };
|
|
DWORD dwFilePointer = 0;
|
|
HANDLE handle = NULL;
|
|
WIN32_FIND_DATA fileData;
|
|
|
|
if (m_disposed)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
SRWExclusiveLock lock(m_srwLock);
|
|
|
|
if (m_disposed)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
m_disposed = true;
|
|
|
|
if (m_hLogFileHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
|
|
FlushFileBuffers(m_hLogFileHandle);
|
|
|
|
if (stdoutWrapper != nullptr)
|
|
{
|
|
RETURN_IF_FAILED(stdoutWrapper->StopRedirection());
|
|
}
|
|
|
|
if (stderrWrapper != nullptr)
|
|
{
|
|
RETURN_IF_FAILED(stderrWrapper->StopRedirection());
|
|
}
|
|
|
|
// delete empty log file
|
|
handle = FindFirstFile(m_struLogFilePath.QueryStr(), &fileData);
|
|
if (handle != INVALID_HANDLE_VALUE &&
|
|
handle != NULL &&
|
|
fileData.nFileSizeHigh == 0 &&
|
|
fileData.nFileSizeLow == 0) // skip check of nFileSizeHigh
|
|
{
|
|
FindClose(handle);
|
|
LOG_LAST_ERROR_IF(!DeleteFile(m_struLogFilePath.QueryStr()));
|
|
}
|
|
|
|
// Read the first 30Kb from the file and store it in a buffer.
|
|
// By doing this, we can close the handle to the file and be done with it.
|
|
RETURN_LAST_ERROR_IF(!GetFileSizeEx(m_hLogFileHandle, &li));
|
|
|
|
if (li.LowPart == 0 || li.HighPart > 0)
|
|
{
|
|
RETURN_HR(HRESULT_FROM_WIN32(ERROR_FILE_INVALID));
|
|
}
|
|
|
|
dwFilePointer = SetFilePointer(m_hLogFileHandle, 0, NULL, FILE_BEGIN);
|
|
|
|
RETURN_LAST_ERROR_IF(dwFilePointer == INVALID_SET_FILE_POINTER);
|
|
|
|
RETURN_LAST_ERROR_IF(!ReadFile(m_hLogFileHandle, pzFileContents, MAX_FILE_READ_SIZE, &dwNumBytesRead, NULL));
|
|
|
|
m_straFileContent.Copy(pzFileContents, dwNumBytesRead);
|
|
|
|
// printf will fail in in full IIS
|
|
if (printf(m_straFileContent.QueryStr()) != -1)
|
|
{
|
|
// Need to flush contents for the new stdout and stderr
|
|
_flushall();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
bool FileOutputManager::GetStdOutContent(STRA* struStdOutput)
|
|
{
|
|
struStdOutput->Copy(m_straFileContent);
|
|
return m_straFileContent.QueryCCH() > 0;
|
|
}
|