Send stderr logs to anonymous pipe instead of file when logging is disabled. (#549)
This commit is contained in:
parent
4ca45d951f
commit
87c3ca4752
|
|
@ -93,13 +93,13 @@ VOID
|
||||||
APPLICATION_INFO::UpdateAppOfflineFileHandle()
|
APPLICATION_INFO::UpdateAppOfflineFileHandle()
|
||||||
{
|
{
|
||||||
STRU strFilePath;
|
STRU strFilePath;
|
||||||
UTILITY::ConvertPathToFullPath(L".\\app_offline.htm",
|
UTILITY::ConvertPathToFullPath(L".\\app_offline.htm",
|
||||||
m_pConfiguration->QueryApplicationPhysicalPath()->QueryStr(),
|
m_pConfiguration->QueryApplicationPhysicalPath()->QueryStr(),
|
||||||
&strFilePath);
|
&strFilePath);
|
||||||
APP_OFFLINE_HTM *pOldAppOfflineHtm = NULL;
|
APP_OFFLINE_HTM *pOldAppOfflineHtm = NULL;
|
||||||
APP_OFFLINE_HTM *pNewAppOfflineHtm = NULL;
|
APP_OFFLINE_HTM *pNewAppOfflineHtm = NULL;
|
||||||
|
|
||||||
if (INVALID_FILE_ATTRIBUTES == GetFileAttributes(strFilePath.QueryStr()) &&
|
if (INVALID_FILE_ATTRIBUTES == GetFileAttributes(strFilePath.QueryStr()) &&
|
||||||
GetLastError() == ERROR_FILE_NOT_FOUND)
|
GetLastError() == ERROR_FILE_NOT_FOUND)
|
||||||
{
|
{
|
||||||
m_fAppOfflineFound = FALSE;
|
m_fAppOfflineFound = FALSE;
|
||||||
|
|
@ -141,9 +141,9 @@ APPLICATION_INFO::UpdateAppOfflineFileHandle()
|
||||||
m_pApplication->QueryConfig()->QueryApplicationPath()->QueryStr())))
|
m_pApplication->QueryConfig()->QueryApplicationPath()->QueryStr())))
|
||||||
{
|
{
|
||||||
UTILITY::LogEvent(g_hEventLog,
|
UTILITY::LogEvent(g_hEventLog,
|
||||||
EVENTLOG_INFORMATION_TYPE,
|
EVENTLOG_INFORMATION_TYPE,
|
||||||
ASPNETCORE_EVENT_RECYCLE_APPOFFLINE,
|
ASPNETCORE_EVENT_RECYCLE_APPOFFLINE,
|
||||||
strEventMsg.QueryStr());
|
strEventMsg.QueryStr());
|
||||||
}
|
}
|
||||||
|
|
||||||
m_pApplication->ShutDown();
|
m_pApplication->ShutDown();
|
||||||
|
|
@ -170,7 +170,7 @@ APPLICATION_INFO::EnsureApplicationCreated()
|
||||||
goto Finished;
|
goto Finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( m_pConfiguration->QueryHostingModel() == APP_HOSTING_MODEL::HOSTING_IN_PROCESS )
|
if (m_pConfiguration->QueryHostingModel() == APP_HOSTING_MODEL::HOSTING_IN_PROCESS)
|
||||||
{
|
{
|
||||||
if (FAILED(hr = HOSTFXR_UTILITY::GetHostFxrParameters(
|
if (FAILED(hr = HOSTFXR_UTILITY::GetHostFxrParameters(
|
||||||
g_hEventLog,
|
g_hEventLog,
|
||||||
|
|
@ -385,13 +385,13 @@ APPLICATION_INFO::FindNativeAssemblyFromHostfxr(
|
||||||
INT intIndex = -1;
|
INT intIndex = -1;
|
||||||
INT intPrevIndex = 0;
|
INT intPrevIndex = 0;
|
||||||
BOOL fFound = FALSE;
|
BOOL fFound = FALSE;
|
||||||
DWORD dwBufferSize = 1024;
|
DWORD dwBufferSize = 1024 * 10;
|
||||||
DWORD dwRequiredBufferSize = 0;
|
DWORD dwRequiredBufferSize = 0;
|
||||||
|
|
||||||
DBG_ASSERT(struFileName != NULL);
|
DBG_ASSERT(struFileName != NULL);
|
||||||
|
|
||||||
hmHostFxrDll = LoadLibraryW(m_pConfiguration->QueryHostFxrFullPath());
|
hmHostFxrDll = LoadLibraryW(m_pConfiguration->QueryHostFxrFullPath());
|
||||||
|
|
||||||
if (hmHostFxrDll == NULL)
|
if (hmHostFxrDll == NULL)
|
||||||
{
|
{
|
||||||
// Could not load hostfxr
|
// Could not load hostfxr
|
||||||
|
|
@ -429,9 +429,10 @@ APPLICATION_INFO::FindNativeAssemblyFromHostfxr(
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (dwRequiredBufferSize >= dwBufferSize)
|
else if (dwRequiredBufferSize > dwBufferSize)
|
||||||
{
|
{
|
||||||
dwBufferSize = dwRequiredBufferSize + 1; // for null terminator
|
dwBufferSize = dwRequiredBufferSize + 1; // for null terminator
|
||||||
|
|
||||||
if (FAILED(hr = struNativeSearchPaths.Resize(dwBufferSize)))
|
if (FAILED(hr = struNativeSearchPaths.Resize(dwBufferSize)))
|
||||||
{
|
{
|
||||||
goto Finished;
|
goto Finished;
|
||||||
|
|
@ -451,7 +452,7 @@ APPLICATION_INFO::FindNativeAssemblyFromHostfxr(
|
||||||
}
|
}
|
||||||
|
|
||||||
fFound = FALSE;
|
fFound = FALSE;
|
||||||
|
|
||||||
// The native search directories are semicolon delimited.
|
// The native search directories are semicolon delimited.
|
||||||
// Split on semicolons, append aspnetcorerh.dll, and check if the file exists.
|
// Split on semicolons, append aspnetcorerh.dll, and check if the file exists.
|
||||||
while ((intIndex = struNativeSearchPaths.IndexOf(L";", intPrevIndex)) != -1)
|
while ((intIndex = struNativeSearchPaths.IndexOf(L";", intPrevIndex)) != -1)
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@
|
||||||
#define ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP_MSG L"Only one inprocess application is allowed per IIS application pool. Please assign the application '%s' to a different IIS application pool."
|
#define ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP_MSG L"Only one inprocess application is allowed per IIS application pool. Please assign the application '%s' to a different IIS application pool."
|
||||||
#define ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR_MSG L"Mixed hosting model is not supported. Application '%s' configured with different hostingModel value '%d' other than the one of running application(s)."
|
#define ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR_MSG L"Mixed hosting model is not supported. Application '%s' configured with different hostingModel value '%d' other than the one of running application(s)."
|
||||||
#define ASPNETCORE_EVENT_ADD_APPLICATION_ERROR_MSG L"Failed to start application '%s', ErrorCode '0x%x'."
|
#define ASPNETCORE_EVENT_ADD_APPLICATION_ERROR_MSG L"Failed to start application '%s', ErrorCode '0x%x'."
|
||||||
|
#define ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDERR_MSG L"Application '%s' with physical root '%s' hit unexpected managed background thread exit, ErrorCode = '0x%x. First 4KB characters of captured stderr logs on startup:\r\n%s"
|
||||||
|
#define ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDOUT_MSG L"Application '%s' with physical root '%s' hit unexpected managed background thread exit, ErrorCode = '0x%x. Last 4KB characters of captured stdout and stderr logs:\r\n%s"
|
||||||
#define ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_MSG L"Application '%s' with physical root '%s' hit unexpected managed background thread exit, ErrorCode = '0x%x. Please check the stderr logs for more information."
|
#define ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_MSG L"Application '%s' with physical root '%s' hit unexpected managed background thread exit, ErrorCode = '0x%x. Please check the stderr logs for more information."
|
||||||
#define ASPNETCORE_EVENT_RECYCLE_APPOFFLINE_MSG L"Application '%s' is recycled due to app_offline file was detected."
|
#define ASPNETCORE_EVENT_RECYCLE_APPOFFLINE_MSG L"Application '%s' is recycled due to app_offline file was detected."
|
||||||
#define ASPNETCORE_EVENT_MODULE_DISABLED_MSG L"AspNetCore Module is disabled"
|
#define ASPNETCORE_EVENT_MODULE_DISABLED_MSG L"AspNetCore Module is disabled"
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
IN_PROCESS_APPLICATION* IN_PROCESS_APPLICATION::s_Application = NULL;
|
IN_PROCESS_APPLICATION* IN_PROCESS_APPLICATION::s_Application = NULL;
|
||||||
|
|
||||||
IN_PROCESS_APPLICATION::IN_PROCESS_APPLICATION(
|
IN_PROCESS_APPLICATION::IN_PROCESS_APPLICATION(
|
||||||
IHttpServer* pHttpServer,
|
IHttpServer* pHttpServer,
|
||||||
ASPNETCORE_CONFIG* pConfig) :
|
ASPNETCORE_CONFIG* pConfig) :
|
||||||
APPLICATION(pHttpServer, pConfig),
|
APPLICATION(pHttpServer, pConfig),
|
||||||
m_ProcessExitCode(0),
|
m_ProcessExitCode(0),
|
||||||
|
|
@ -12,7 +12,10 @@ IN_PROCESS_APPLICATION::IN_PROCESS_APPLICATION(
|
||||||
m_fInitialized(FALSE),
|
m_fInitialized(FALSE),
|
||||||
m_fRecycleProcessCalled(FALSE),
|
m_fRecycleProcessCalled(FALSE),
|
||||||
m_hLogFileHandle(INVALID_HANDLE_VALUE),
|
m_hLogFileHandle(INVALID_HANDLE_VALUE),
|
||||||
m_fDoneStdRedirect(FALSE)
|
m_hErrReadPipe(INVALID_HANDLE_VALUE),
|
||||||
|
m_hErrWritePipe(INVALID_HANDLE_VALUE),
|
||||||
|
m_fDoneStdRedirect(FALSE),
|
||||||
|
m_dwStdErrReadTotal(0)
|
||||||
{
|
{
|
||||||
// is it guaranteed that we have already checked app offline at this point?
|
// is it guaranteed that we have already checked app offline at this point?
|
||||||
// If so, I don't think there is much to do here.
|
// If so, I don't think there is much to do here.
|
||||||
|
|
@ -91,6 +94,8 @@ IN_PROCESS_APPLICATION::Recycle(
|
||||||
|
|
||||||
ReleaseSRWLockExclusive(&m_srwLock);
|
ReleaseSRWLockExclusive(&m_srwLock);
|
||||||
|
|
||||||
|
CloseStdErrHandles();
|
||||||
|
|
||||||
if (m_pStdFile != NULL)
|
if (m_pStdFile != NULL)
|
||||||
{
|
{
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
@ -105,7 +110,7 @@ IN_PROCESS_APPLICATION::Recycle(
|
||||||
m_hLogFileHandle = INVALID_HANDLE_VALUE;
|
m_hLogFileHandle = INVALID_HANDLE_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete empty log file, if logging is not enabled
|
// delete empty log file
|
||||||
handle = FindFirstFile(m_struLogFilePath.QueryStr(), &fileData);
|
handle = FindFirstFile(m_struLogFilePath.QueryStr(), &fileData);
|
||||||
if (handle != INVALID_HANDLE_VALUE &&
|
if (handle != INVALID_HANDLE_VALUE &&
|
||||||
fileData.nFileSizeHigh == 0 &&
|
fileData.nFileSizeHigh == 0 &&
|
||||||
|
|
@ -133,7 +138,6 @@ IN_PROCESS_APPLICATION::OnAsyncCompletion(
|
||||||
IN_PROCESS_HANDLER* pInProcessHandler
|
IN_PROCESS_HANDLER* pInProcessHandler
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
||||||
REQUEST_NOTIFICATION_STATUS dwRequestNotificationStatus = RQ_NOTIFICATION_CONTINUE;
|
REQUEST_NOTIFICATION_STATUS dwRequestNotificationStatus = RQ_NOTIFICATION_CONTINUE;
|
||||||
|
|
||||||
if (pInProcessHandler->QueryIsManagedRequestComplete())
|
if (pInProcessHandler->QueryIsManagedRequestComplete())
|
||||||
|
|
@ -171,10 +175,10 @@ IN_PROCESS_APPLICATION::OnExecuteRequest(
|
||||||
(ULONG)E_APPLICATION_ACTIVATION_EXEC_FAILURE);
|
(ULONG)E_APPLICATION_ACTIVATION_EXEC_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
pHttpContext->GetResponse()->SetStatus(500,
|
pHttpContext->GetResponse()->SetStatus(500,
|
||||||
"Internal Server Error",
|
"Internal Server Error",
|
||||||
0,
|
0,
|
||||||
(ULONG)E_APPLICATION_ACTIVATION_EXEC_FAILURE);
|
(ULONG)E_APPLICATION_ACTIVATION_EXEC_FAILURE);
|
||||||
|
|
||||||
return RQ_NOTIFICATION_FINISH_REQUEST;
|
return RQ_NOTIFICATION_FINISH_REQUEST;
|
||||||
}
|
}
|
||||||
|
|
@ -194,8 +198,12 @@ IN_PROCESS_APPLICATION::SetCallbackHandles(
|
||||||
m_ShutdownHandlerContext = pvShutdownHandlerContext;
|
m_ShutdownHandlerContext = pvShutdownHandlerContext;
|
||||||
m_AsyncCompletionHandler = async_completion_handler;
|
m_AsyncCompletionHandler = async_completion_handler;
|
||||||
|
|
||||||
|
CloseStdErrHandles();
|
||||||
|
// Can't check the std err handle as it isn't a critical error
|
||||||
|
SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE);
|
||||||
// Initialization complete
|
// Initialization complete
|
||||||
SetEvent(m_pInitalizeEvent);
|
SetEvent(m_pInitalizeEvent);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
|
|
@ -203,12 +211,13 @@ IN_PROCESS_APPLICATION::SetStdOut(
|
||||||
VOID
|
VOID
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
BOOL fLocked = FALSE;
|
BOOL fLocked = FALSE;
|
||||||
STRU struPath;
|
STRU struPath;
|
||||||
|
|
||||||
SYSTEMTIME systemTime;
|
SYSTEMTIME systemTime;
|
||||||
SECURITY_ATTRIBUTES saAttr = { 0 };
|
SECURITY_ATTRIBUTES saAttr = { 0 };
|
||||||
|
HANDLE hStdErrReadPipe;
|
||||||
|
HANDLE hStdErrWritePipe;
|
||||||
|
|
||||||
if (!m_fDoneStdRedirect)
|
if (!m_fDoneStdRedirect)
|
||||||
{
|
{
|
||||||
|
|
@ -217,54 +226,10 @@ IN_PROCESS_APPLICATION::SetStdOut(
|
||||||
fLocked = TRUE;
|
fLocked = TRUE;
|
||||||
if (!m_fDoneStdRedirect)
|
if (!m_fDoneStdRedirect)
|
||||||
{
|
{
|
||||||
hr = UTILITY::ConvertPathToFullPath(
|
|
||||||
m_pConfig->QueryStdoutLogFile()->QueryStr(),
|
|
||||||
m_pConfig->QueryApplicationPhysicalPath()->QueryStr(),
|
|
||||||
&struPath);
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = UTILITY::EnsureDirectoryPathExist(struPath.QueryStr());
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetSystemTime(&systemTime);
|
|
||||||
hr = 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());
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||||
saAttr.bInheritHandle = TRUE;
|
saAttr.bInheritHandle = TRUE;
|
||||||
saAttr.lpSecurityDescriptor = NULL;
|
saAttr.lpSecurityDescriptor = NULL;
|
||||||
|
|
||||||
m_hLogFileHandle = CreateFileW(m_struLogFilePath.QueryStr(),
|
|
||||||
FILE_WRITE_DATA,
|
|
||||||
FILE_SHARE_READ,
|
|
||||||
&saAttr,
|
|
||||||
CREATE_ALWAYS,
|
|
||||||
FILE_ATTRIBUTE_NORMAL,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
if (m_hLogFileHandle == INVALID_HANDLE_VALUE)
|
|
||||||
{
|
|
||||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// best effort
|
// best effort
|
||||||
// no need to capture the error code as nothing we can do here
|
// no need to capture the error code as nothing we can do here
|
||||||
|
|
@ -272,14 +237,70 @@ IN_PROCESS_APPLICATION::SetStdOut(
|
||||||
//
|
//
|
||||||
if (!GetConsoleWindow())
|
if (!GetConsoleWindow())
|
||||||
{
|
{
|
||||||
|
// Full IIS scenario.
|
||||||
|
|
||||||
//
|
//
|
||||||
// SetStdHandle works as w3wp does not have Console
|
// SetStdHandle works as w3wp does not have Console
|
||||||
// Current process does not have a console
|
// Current process does not have a console
|
||||||
//
|
//
|
||||||
SetStdHandle(STD_ERROR_HANDLE, m_hLogFileHandle);
|
|
||||||
if (m_pConfig->QueryStdoutLogEnabled())
|
if (m_pConfig->QueryStdoutLogEnabled())
|
||||||
{
|
{
|
||||||
SetStdHandle(STD_OUTPUT_HANDLE, m_hLogFileHandle);
|
hr = UTILITY::ConvertPathToFullPath(
|
||||||
|
m_pConfig->QueryStdoutLogFile()->QueryStr(),
|
||||||
|
m_pConfig->QueryApplicationPhysicalPath()->QueryStr(),
|
||||||
|
&struPath);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = UTILITY::EnsureDirectoryPathExist(struPath.QueryStr());
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetSystemTime(&systemTime);
|
||||||
|
hr = 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());
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_hLogFileHandle = CreateFileW(m_struLogFilePath.QueryStr(),
|
||||||
|
FILE_READ_DATA | FILE_WRITE_DATA,
|
||||||
|
FILE_SHARE_READ,
|
||||||
|
&saAttr,
|
||||||
|
CREATE_ALWAYS,
|
||||||
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (m_hLogFileHandle == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SetStdHandle(STD_OUTPUT_HANDLE, m_hLogFileHandle))
|
||||||
|
{
|
||||||
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SetStdHandle(STD_ERROR_HANDLE, m_hLogFileHandle))
|
||||||
|
{
|
||||||
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
// not work
|
// not work
|
||||||
// AllocConsole() does not help
|
// AllocConsole() does not help
|
||||||
// *stdout = *m_pStdFile;
|
// *stdout = *m_pStdFile;
|
||||||
|
|
@ -292,46 +313,60 @@ IN_PROCESS_APPLICATION::SetStdOut(
|
||||||
// Periodically flush the log content to file
|
// Periodically flush the log content to file
|
||||||
m_Timer.InitializeTimer(STTIMER::TimerCallback, &m_struLogFilePath, 3000, 3000);
|
m_Timer.InitializeTimer(STTIMER::TimerCallback, &m_struLogFilePath, 3000, 3000);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// CreatePipe for outputting stderr to the windows event log.
|
||||||
|
// Ignore failures
|
||||||
|
//
|
||||||
|
if (!CreatePipe(&hStdErrReadPipe, &hStdErrWritePipe, &saAttr, 0 /*nSize*/))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SetStdHandle(STD_ERROR_HANDLE, hStdErrWritePipe))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_hErrReadPipe = hStdErrReadPipe;
|
||||||
|
m_hErrWritePipe = hStdErrWritePipe;
|
||||||
|
|
||||||
|
// Read the stderr handle on a separate thread until we get 4096 bytes.
|
||||||
|
m_hErrThread = CreateThread(
|
||||||
|
NULL, // default security attributes
|
||||||
|
0, // default stack size
|
||||||
|
(LPTHREAD_START_ROUTINE)ReadStdErrHandle,
|
||||||
|
this, // thread function arguments
|
||||||
|
0, // default creation flags
|
||||||
|
NULL); // receive thread identifier
|
||||||
|
|
||||||
|
if (m_hErrThread == NULL)
|
||||||
|
{
|
||||||
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// The process has console, e.g., IIS Express scenario
|
// The process has console, e.g., IIS Express scenario
|
||||||
CloseHandle(m_hLogFileHandle);
|
|
||||||
m_hLogFileHandle = INVALID_HANDLE_VALUE;
|
|
||||||
|
|
||||||
if (m_pConfig->QueryStdoutLogEnabled())
|
if (_wfopen_s(&m_pStdFile, m_struLogFilePath.QueryStr(), L"w") == 0)
|
||||||
{
|
{
|
||||||
if (_wfopen_s(&m_pStdFile, m_struLogFilePath.QueryStr(), L"w") == 0)
|
// known issue: error info may not be capture when process crashes during buffering
|
||||||
{
|
// even we disabled FILE buffering
|
||||||
// known issue: error info may not be capture when process crashes during buffering
|
setvbuf(m_pStdFile, NULL, _IONBF, 0);
|
||||||
// even we disabled FILE buffering
|
_dup2(_fileno(m_pStdFile), _fileno(stdout));
|
||||||
setvbuf(m_pStdFile, NULL, _IONBF, 0);
|
_dup2(_fileno(m_pStdFile), _fileno(stderr));
|
||||||
_dup2(_fileno(m_pStdFile), _fileno(stdout));
|
|
||||||
_dup2(_fileno(m_pStdFile), _fileno(stderr));
|
|
||||||
}
|
|
||||||
// not work for console scenario
|
|
||||||
// close and AllocConsole does not help
|
|
||||||
//_wfreopen_s(&m_pStdFile, struLogFileName.QueryStr(), L"w", stdout);
|
|
||||||
// SetStdHandle(STD_ERROR_HANDLE, m_hLogFileHandle);
|
|
||||||
// SetStdHandle(STD_OUTPUT_HANDLE, m_hLogFileHandle);
|
|
||||||
//*stdout = *m_pStdFile;
|
|
||||||
//*stderr = *m_pStdFile;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// delete the file as log is disabled
|
|
||||||
WIN32_FIND_DATA fileData;
|
|
||||||
HANDLE handle = FindFirstFile(m_struLogFilePath.QueryStr(), &fileData);
|
|
||||||
if (handle != INVALID_HANDLE_VALUE &&
|
|
||||||
fileData.nFileSizeHigh == 0 &&
|
|
||||||
fileData.nFileSizeLow == 0)
|
|
||||||
{
|
|
||||||
FindClose(handle);
|
|
||||||
// no need to check whether the deletion succeeds
|
|
||||||
// as nothing can be done
|
|
||||||
DeleteFile(m_struLogFilePath.QueryStr());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// These don't work for console scenario
|
||||||
|
// close and AllocConsole does not help
|
||||||
|
//_wfreopen_s(&m_pStdFile, struLogFileName.QueryStr(), L"w", stdout);
|
||||||
|
// SetStdHandle(STD_ERROR_HANDLE, m_hLogFileHandle);
|
||||||
|
// SetStdHandle(STD_OUTPUT_HANDLE, m_hLogFileHandle);
|
||||||
|
//*stdout = *m_pStdFile;
|
||||||
|
//*stderr = *m_pStdFile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -358,6 +393,79 @@ Finished:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VOID
|
||||||
|
IN_PROCESS_APPLICATION::ReadStdErrHandle(
|
||||||
|
LPVOID pContext
|
||||||
|
)
|
||||||
|
{
|
||||||
|
IN_PROCESS_APPLICATION *pApplication = (IN_PROCESS_APPLICATION*)pContext;
|
||||||
|
DBG_ASSERT(pApplication != NULL);
|
||||||
|
pApplication->ReadStdErrHandleInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID
|
||||||
|
IN_PROCESS_APPLICATION::ReadStdErrHandleInternal(
|
||||||
|
VOID
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DWORD dwNumBytesRead = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (ReadFile(m_hErrReadPipe, &m_pzFileContents[m_dwStdErrReadTotal], 4096 - m_dwStdErrReadTotal, &dwNumBytesRead, NULL))
|
||||||
|
{
|
||||||
|
m_dwStdErrReadTotal += dwNumBytesRead;
|
||||||
|
if (m_dwStdErrReadTotal >= 4096)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (GetLastError() == ERROR_BROKEN_PIPE)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID
|
||||||
|
IN_PROCESS_APPLICATION::CloseStdErrHandles
|
||||||
|
(
|
||||||
|
VOID
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DWORD dwThreadStatus = 0;
|
||||||
|
DWORD dwTimeout = m_pConfig->QueryShutdownTimeLimitInMS();
|
||||||
|
// Close Handles for stderr as we only care about capturing startup errors
|
||||||
|
if (m_hErrWritePipe != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
CloseHandle(m_hErrWritePipe);
|
||||||
|
m_hErrWritePipe = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_hErrThread != NULL &&
|
||||||
|
GetExitCodeThread(m_hErrThread, &dwThreadStatus) != 0 &&
|
||||||
|
dwThreadStatus == STILL_ACTIVE)
|
||||||
|
{
|
||||||
|
// wait for gracefullshut down, i.e., the exit of the background thread or timeout
|
||||||
|
if (WaitForSingleObject(m_hErrThread, dwTimeout) != WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
// if the thread is still running, we need kill it first before exit to avoid AV
|
||||||
|
if (GetExitCodeThread(m_hErrThread, &dwThreadStatus) != 0 && dwThreadStatus == STILL_ACTIVE)
|
||||||
|
{
|
||||||
|
TerminateThread(m_hErrThread, STATUS_CONTROL_C_EXIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(m_hErrThread);
|
||||||
|
m_hErrThread = NULL;
|
||||||
|
|
||||||
|
if (m_hErrReadPipe != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
CloseHandle(m_hErrReadPipe);
|
||||||
|
m_hErrReadPipe = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Will be called by the inprocesshandler
|
// Will be called by the inprocesshandler
|
||||||
HRESULT
|
HRESULT
|
||||||
IN_PROCESS_APPLICATION::LoadManagedApplication
|
IN_PROCESS_APPLICATION::LoadManagedApplication
|
||||||
|
|
@ -544,35 +652,106 @@ IN_PROCESS_APPLICATION::ExecuteApplication(
|
||||||
// set the callbacks
|
// set the callbacks
|
||||||
s_Application = this;
|
s_Application = this;
|
||||||
|
|
||||||
RunDotnetApplication(m_pConfig->QueryHostFxrArgCount(), m_pConfig->QueryHostFxrArguments(), pProc);
|
hr = RunDotnetApplication(m_pConfig->QueryHostFxrArgCount(), m_pConfig->QueryHostFxrArguments(), pProc);
|
||||||
|
|
||||||
Finished:
|
Finished:
|
||||||
//
|
|
||||||
// this method is called by the background thread and should never exit unless shutdown
|
|
||||||
//
|
|
||||||
if (!m_fRecycleProcessCalled)
|
if (!m_fRecycleProcessCalled)
|
||||||
{
|
{
|
||||||
STRU strEventMsg;
|
//
|
||||||
if (SUCCEEDED(strEventMsg.SafeSnwprintf(
|
// Ungraceful shutdown, try to log an error message.
|
||||||
|
// This will be a common place for errors as it means the hostfxr_main returned
|
||||||
|
// or there was an exception.
|
||||||
|
//
|
||||||
|
|
||||||
|
CHAR pzFileContents[4096] = { 0 };
|
||||||
|
DWORD dwNumBytesRead;
|
||||||
|
STRU struStdErrLog;
|
||||||
|
LARGE_INTEGER li = { 0 };
|
||||||
|
STRU strEventMsg;
|
||||||
|
BOOL fLogged = FALSE;
|
||||||
|
DWORD dwFilePointer = 0;
|
||||||
|
|
||||||
|
if (m_pConfig->QueryStdoutLogEnabled())
|
||||||
|
{
|
||||||
|
// Put stdout/stderr logs into
|
||||||
|
if (m_hLogFileHandle != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
if (GetFileSizeEx(m_hLogFileHandle, &li) && li.LowPart > 0 && li.HighPart == 0)
|
||||||
|
{
|
||||||
|
if (li.LowPart > 4096)
|
||||||
|
{
|
||||||
|
dwFilePointer = SetFilePointer(m_hLogFileHandle, -4096, NULL, FILE_END);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dwFilePointer = SetFilePointer(m_hLogFileHandle, 0, NULL, FILE_BEGIN);
|
||||||
|
}
|
||||||
|
if (dwFilePointer != INVALID_SET_FILE_POINTER)
|
||||||
|
{
|
||||||
|
if (ReadFile(m_hLogFileHandle, pzFileContents, 4096, &dwNumBytesRead, NULL))
|
||||||
|
{
|
||||||
|
if (SUCCEEDED(struStdErrLog.CopyA(m_pzFileContents, m_dwStdErrReadTotal)) &&
|
||||||
|
SUCCEEDED(strEventMsg.SafeSnwprintf(
|
||||||
|
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDOUT_MSG,
|
||||||
|
m_pConfig->QueryApplicationPath()->QueryStr(),
|
||||||
|
m_pConfig->QueryApplicationPhysicalPath()->QueryStr(),
|
||||||
|
hr,
|
||||||
|
struStdErrLog.QueryStr())))
|
||||||
|
{
|
||||||
|
UTILITY::LogEvent(g_hEventLog,
|
||||||
|
EVENTLOG_ERROR_TYPE,
|
||||||
|
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT,
|
||||||
|
strEventMsg.QueryStr());
|
||||||
|
fLogged = TRUE;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (m_dwStdErrReadTotal > 0)
|
||||||
|
{
|
||||||
|
if (SUCCEEDED(struStdErrLog.CopyA(m_pzFileContents, m_dwStdErrReadTotal)) &&
|
||||||
|
SUCCEEDED(strEventMsg.SafeSnwprintf(
|
||||||
|
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDERR_MSG,
|
||||||
|
m_pConfig->QueryApplicationPath()->QueryStr(),
|
||||||
|
m_pConfig->QueryApplicationPhysicalPath()->QueryStr(),
|
||||||
|
hr,
|
||||||
|
struStdErrLog.QueryStr())))
|
||||||
|
{
|
||||||
|
UTILITY::LogEvent(g_hEventLog,
|
||||||
|
EVENTLOG_ERROR_TYPE,
|
||||||
|
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT,
|
||||||
|
strEventMsg.QueryStr());
|
||||||
|
fLogged = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fLogged)
|
||||||
|
{
|
||||||
|
// If we didn't log, log the generic message.
|
||||||
|
if (SUCCEEDED(strEventMsg.SafeSnwprintf(
|
||||||
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_MSG,
|
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_MSG,
|
||||||
m_pConfig->QueryApplicationPath()->QueryStr(),
|
m_pConfig->QueryApplicationPath()->QueryStr(),
|
||||||
m_pConfig->QueryApplicationPhysicalPath()->QueryStr(),
|
m_pConfig->QueryApplicationPhysicalPath()->QueryStr(),
|
||||||
m_ProcessExitCode)))
|
hr)))
|
||||||
{
|
{
|
||||||
UTILITY::LogEvent(g_hEventLog,
|
UTILITY::LogEvent(g_hEventLog,
|
||||||
EVENTLOG_ERROR_TYPE,
|
EVENTLOG_ERROR_TYPE,
|
||||||
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT,
|
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT,
|
||||||
strEventMsg.QueryStr());
|
strEventMsg.QueryStr());
|
||||||
}
|
fLogged = TRUE;
|
||||||
|
}
|
||||||
// error. the thread exits after application started
|
|
||||||
// Question: should we shutdown current worker process or keep the application in failure state?
|
|
||||||
// for now, we reccylce to keep the same behavior as that of out-of-process
|
|
||||||
if (m_fManagedAppLoaded)
|
|
||||||
{
|
|
||||||
Recycle();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The destructor of inprocessapplication will call recycle. No need to recycle here.
|
||||||
}
|
}
|
||||||
|
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -595,6 +774,7 @@ IN_PROCESS_APPLICATION::RunDotnetApplication(DWORD argc, CONST PCWSTR* argv, hos
|
||||||
// TODO Log error message here.
|
// TODO Log error message here.
|
||||||
hr = E_APPLICATION_ACTIVATION_EXEC_FAILURE;
|
hr = E_APPLICATION_ACTIVATION_EXEC_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
typedef REQUEST_NOTIFICATION_STATUS(WINAPI * PFN_REQUEST_HANDLER) (IN_PROCESS_HANDLER* pInProcessHandler, void* pvRequestHandlerContext);
|
typedef REQUEST_NOTIFICATION_STATUS(WINAPI * PFN_REQUEST_HANDLER) (IN_PROCESS_HANDLER* pInProcessHandler, void* pvRequestHandlerContext);
|
||||||
typedef BOOL( WINAPI * PFN_SHUTDOWN_HANDLER) (void* pvShutdownHandlerContext);
|
typedef BOOL(WINAPI * PFN_SHUTDOWN_HANDLER) (void* pvShutdownHandlerContext);
|
||||||
typedef REQUEST_NOTIFICATION_STATUS( WINAPI * PFN_MANAGED_CONTEXT_HANDLER)(void *pvManagedHttpContext, HRESULT hrCompletionStatus, DWORD cbCompletion);
|
typedef REQUEST_NOTIFICATION_STATUS(WINAPI * PFN_MANAGED_CONTEXT_HANDLER)(void *pvManagedHttpContext, HRESULT hrCompletionStatus, DWORD cbCompletion);
|
||||||
|
|
||||||
class IN_PROCESS_APPLICATION : public APPLICATION
|
class IN_PROCESS_APPLICATION : public APPLICATION
|
||||||
{
|
{
|
||||||
|
|
@ -15,53 +15,63 @@ public:
|
||||||
~IN_PROCESS_APPLICATION();
|
~IN_PROCESS_APPLICATION();
|
||||||
|
|
||||||
__override
|
__override
|
||||||
VOID
|
|
||||||
ShutDown();
|
|
||||||
|
|
||||||
VOID
|
|
||||||
SetCallbackHandles(
|
|
||||||
_In_ PFN_REQUEST_HANDLER request_callback,
|
|
||||||
_In_ PFN_SHUTDOWN_HANDLER shutdown_callback,
|
|
||||||
_In_ PFN_MANAGED_CONTEXT_HANDLER managed_context_callback,
|
|
||||||
_In_ VOID* pvRequstHandlerContext,
|
|
||||||
_In_ VOID* pvShutdownHandlerContext
|
|
||||||
);
|
|
||||||
|
|
||||||
VOID
|
|
||||||
Recycle(
|
|
||||||
VOID
|
VOID
|
||||||
);
|
ShutDown();
|
||||||
|
|
||||||
|
VOID
|
||||||
|
SetCallbackHandles(
|
||||||
|
_In_ PFN_REQUEST_HANDLER request_callback,
|
||||||
|
_In_ PFN_SHUTDOWN_HANDLER shutdown_callback,
|
||||||
|
_In_ PFN_MANAGED_CONTEXT_HANDLER managed_context_callback,
|
||||||
|
_In_ VOID* pvRequstHandlerContext,
|
||||||
|
_In_ VOID* pvShutdownHandlerContext
|
||||||
|
);
|
||||||
|
|
||||||
|
VOID
|
||||||
|
Recycle(
|
||||||
|
VOID
|
||||||
|
);
|
||||||
|
|
||||||
// Executes the .NET Core process
|
// Executes the .NET Core process
|
||||||
HRESULT
|
HRESULT
|
||||||
ExecuteApplication(
|
ExecuteApplication(
|
||||||
VOID
|
VOID
|
||||||
);
|
);
|
||||||
|
|
||||||
|
VOID
|
||||||
|
ReadStdErrHandleInternal(
|
||||||
|
VOID
|
||||||
|
);
|
||||||
|
|
||||||
|
VOID
|
||||||
|
CloseStdErrHandles(
|
||||||
|
VOID
|
||||||
|
);
|
||||||
|
|
||||||
HRESULT
|
HRESULT
|
||||||
LoadManagedApplication(
|
LoadManagedApplication(
|
||||||
VOID
|
VOID
|
||||||
);
|
);
|
||||||
|
|
||||||
REQUEST_NOTIFICATION_STATUS
|
REQUEST_NOTIFICATION_STATUS
|
||||||
OnAsyncCompletion(
|
OnAsyncCompletion(
|
||||||
DWORD cbCompletion,
|
DWORD cbCompletion,
|
||||||
HRESULT hrCompletionStatus,
|
HRESULT hrCompletionStatus,
|
||||||
IN_PROCESS_HANDLER* pInProcessHandler
|
IN_PROCESS_HANDLER* pInProcessHandler
|
||||||
);
|
);
|
||||||
|
|
||||||
REQUEST_NOTIFICATION_STATUS
|
REQUEST_NOTIFICATION_STATUS
|
||||||
OnExecuteRequest
|
OnExecuteRequest
|
||||||
(
|
(
|
||||||
IHttpContext* pHttpContext,
|
IHttpContext* pHttpContext,
|
||||||
IN_PROCESS_HANDLER* pInProcessHandler
|
IN_PROCESS_HANDLER* pInProcessHandler
|
||||||
);
|
);
|
||||||
|
|
||||||
static
|
static
|
||||||
IN_PROCESS_APPLICATION*
|
IN_PROCESS_APPLICATION*
|
||||||
GetInstance(
|
GetInstance(
|
||||||
VOID
|
VOID
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return s_Application;
|
return s_Application;
|
||||||
}
|
}
|
||||||
|
|
@ -85,6 +95,8 @@ private:
|
||||||
|
|
||||||
// The std log file handle
|
// The std log file handle
|
||||||
HANDLE m_hLogFileHandle;
|
HANDLE m_hLogFileHandle;
|
||||||
|
HANDLE m_hErrReadPipe;
|
||||||
|
HANDLE m_hErrWritePipe;
|
||||||
STRU m_struLogFilePath;
|
STRU m_struLogFilePath;
|
||||||
|
|
||||||
// The exit code of the .NET Core process
|
// The exit code of the .NET Core process
|
||||||
|
|
@ -101,27 +113,38 @@ private:
|
||||||
STTIMER m_Timer;
|
STTIMER m_Timer;
|
||||||
SRWLOCK m_srwLock;
|
SRWLOCK m_srwLock;
|
||||||
|
|
||||||
static IN_PROCESS_APPLICATION* s_Application;
|
// Thread for capturing startup stderr logs when logging is disabled
|
||||||
|
HANDLE m_hErrThread;
|
||||||
|
CHAR m_pzFileContents[4096] = { 0 };
|
||||||
|
DWORD m_dwStdErrReadTotal;
|
||||||
|
static IN_PROCESS_APPLICATION* s_Application;
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
SetStdOut(
|
SetStdOut(
|
||||||
|
VOID
|
||||||
|
);
|
||||||
|
|
||||||
|
static
|
||||||
VOID
|
VOID
|
||||||
);
|
ExecuteAspNetCoreProcess(
|
||||||
|
_In_ LPVOID pContext
|
||||||
|
);
|
||||||
|
|
||||||
static
|
static
|
||||||
VOID
|
VOID
|
||||||
ExecuteAspNetCoreProcess(
|
ReadStdErrHandle
|
||||||
_In_ LPVOID pContext
|
(
|
||||||
);
|
_In_ LPVOID pContext
|
||||||
|
);
|
||||||
|
|
||||||
static
|
static
|
||||||
INT
|
INT
|
||||||
FilterException(unsigned int code, struct _EXCEPTION_POINTERS *ep);
|
FilterException(unsigned int code, struct _EXCEPTION_POINTERS *ep);
|
||||||
|
|
||||||
HRESULT
|
HRESULT
|
||||||
RunDotnetApplication(
|
RunDotnetApplication(
|
||||||
DWORD argc,
|
DWORD argc,
|
||||||
CONST PCWSTR* argv,
|
CONST PCWSTR* argv,
|
||||||
hostfxr_main_fn pProc
|
hostfxr_main_fn pProc
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue