summaryrefslogtreecommitdiff
path: root/src/burn/engine/engine.cpp
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2022-08-19 14:08:23 -0500
committerSean Hall <r.sean.hall@gmail.com>2022-08-19 17:02:27 -0500
commitb72f58abdf6dd5d0020f174358027158cb52cb72 (patch)
tree3070c1f1fa8a74125a2933f23a1be3c514ed1de7 /src/burn/engine/engine.cpp
parentbbc36178172e4198351f2f84a91faf56c65e1475 (diff)
downloadwix-b72f58abdf6dd5d0020f174358027158cb52cb72.tar.gz
wix-b72f58abdf6dd5d0020f174358027158cb52cb72.tar.bz2
wix-b72f58abdf6dd5d0020f174358027158cb52cb72.zip
Use a dedicated pipe for redirecting logging from the elevated process.
Fixes 6869
Diffstat (limited to 'src/burn/engine/engine.cpp')
-rw-r--r--src/burn/engine/engine.cpp264
1 files changed, 217 insertions, 47 deletions
diff --git a/src/burn/engine/engine.cpp b/src/burn/engine/engine.cpp
index 323d2c3a..daaf51dc 100644
--- a/src/burn/engine/engine.cpp
+++ b/src/burn/engine/engine.cpp
@@ -3,6 +3,15 @@
3#include "precomp.h" 3#include "precomp.h"
4 4
5 5
6typedef struct _REDIRECTED_LOGGING_CONTEXT
7{
8 CRITICAL_SECTION csBuffer;
9 LPSTR sczBuffer;
10 HANDLE hPipe;
11 HANDLE hLogEvent;
12 HANDLE hFinishedEvent;
13} REDIRECTED_LOGGING_CONTEXT;
14
6// constants 15// constants
7 16
8const DWORD RESTART_RETRIES = 10; 17const DWORD RESTART_RETRIES = 10;
@@ -49,6 +58,20 @@ static HRESULT DAPI RedirectLoggingOverPipe(
49 __in_z LPCSTR szString, 58 __in_z LPCSTR szString,
50 __in_opt LPVOID pvContext 59 __in_opt LPVOID pvContext
51 ); 60 );
61static HRESULT LogStringOverPipe(
62 __in_z LPCSTR szString,
63 __in HANDLE hPipe
64 );
65static DWORD WINAPI ElevatedLoggingThreadProc(
66 __in LPVOID lpThreadParameter
67 );
68static HRESULT WaitForElevatedLoggingThread(
69 __in REDIRECTED_LOGGING_CONTEXT* pContext,
70 __in HANDLE hLoggingThread
71 );
72static HRESULT WaitForUnelevatedLoggingThread(
73 __in HANDLE hUnelevatedLoggingThread
74 );
52static HRESULT Restart(); 75static HRESULT Restart();
53static void CALLBACK BurnTraceError( 76static void CALLBACK BurnTraceError(
54 __in_z LPCSTR szFile, 77 __in_z LPCSTR szFile,
@@ -361,7 +384,6 @@ static HRESULT InitializeEngineState(
361 HANDLE hSourceEngineFile = INVALID_HANDLE_VALUE; 384 HANDLE hSourceEngineFile = INVALID_HANDLE_VALUE;
362 385
363 pEngineState->internalCommand.automaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED; 386 pEngineState->internalCommand.automaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED;
364 pEngineState->dwElevatedLoggingTlsId = TLS_OUT_OF_INDEXES;
365 ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive); 387 ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive);
366 PipeConnectionInitialize(&pEngineState->companionConnection); 388 PipeConnectionInitialize(&pEngineState->companionConnection);
367 PipeConnectionInitialize(&pEngineState->embeddedConnection); 389 PipeConnectionInitialize(&pEngineState->embeddedConnection);
@@ -434,11 +456,6 @@ static void UninitializeEngineState(
434 ReleaseStr(pEngineState->log.sczPath); 456 ReleaseStr(pEngineState->log.sczPath);
435 ReleaseStr(pEngineState->log.sczPathVariable); 457 ReleaseStr(pEngineState->log.sczPathVariable);
436 458
437 if (TLS_OUT_OF_INDEXES != pEngineState->dwElevatedLoggingTlsId)
438 {
439 ::TlsFree(pEngineState->dwElevatedLoggingTlsId);
440 }
441
442 // clear struct 459 // clear struct
443 memset(pEngineState, 0, sizeof(BURN_ENGINE_STATE)); 460 memset(pEngineState, 0, sizeof(BURN_ENGINE_STATE));
444} 461}
@@ -624,6 +641,8 @@ LExit:
624 if (INVALID_HANDLE_VALUE != pEngineState->companionConnection.hPipe) 641 if (INVALID_HANDLE_VALUE != pEngineState->companionConnection.hPipe)
625 { 642 {
626 PipeTerminateChildProcess(&pEngineState->companionConnection, pEngineState->userExperience.dwExitCode, FALSE); 643 PipeTerminateChildProcess(&pEngineState->companionConnection, pEngineState->userExperience.dwExitCode, FALSE);
644
645 WaitForUnelevatedLoggingThread(pEngineState->hUnelevatedLoggingThread);
627 } 646 }
628 647
629 // If the splash screen is still around, close it. 648 // If the splash screen is still around, close it.
@@ -646,6 +665,9 @@ static HRESULT RunElevated(
646{ 665{
647 HRESULT hr = S_OK; 666 HRESULT hr = S_OK;
648 HANDLE hLock = NULL; 667 HANDLE hLock = NULL;
668 HANDLE hLoggingThread = NULL;
669 REDIRECTED_LOGGING_CONTEXT loggingContext = { };
670 BOOL fDeleteLoggingCs = FALSE;
649 671
650 // Initialize logging. 672 // Initialize logging.
651 hr = LoggingOpen(&pEngineState->log, &pEngineState->internalCommand, &pEngineState->command, &pEngineState->variables, pEngineState->registration.sczDisplayName); 673 hr = LoggingOpen(&pEngineState->log, &pEngineState->internalCommand, &pEngineState->command, &pEngineState->variables, pEngineState->registration.sczDisplayName);
@@ -655,20 +677,23 @@ static HRESULT RunElevated(
655 hr = PipeChildConnect(&pEngineState->companionConnection, TRUE); 677 hr = PipeChildConnect(&pEngineState->companionConnection, TRUE);
656 ExitOnFailure(hr, "Failed to connect to unelevated process."); 678 ExitOnFailure(hr, "Failed to connect to unelevated process.");
657 679
658 // Set up the thread local storage to store the correct pipe to communicate logging then 680 // Set up the context for the logging thread then
659 // override logging to write over the pipe. 681 // override logging to write over the pipe.
660 pEngineState->dwElevatedLoggingTlsId = ::TlsAlloc(); 682 ::InitializeCriticalSection(&loggingContext.csBuffer);
661 if (TLS_OUT_OF_INDEXES == pEngineState->dwElevatedLoggingTlsId) 683 fDeleteLoggingCs = TRUE;
662 {
663 ExitWithLastError(hr, "Failed to allocate thread local storage for logging.");
664 }
665 684
666 if (!::TlsSetValue(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe)) 685 loggingContext.hLogEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
667 { 686 ExitOnNullWithLastError(loggingContext.hLogEvent, hr, "Failed to create log event for logging thread.");
668 ExitWithLastError(hr, "Failed to set elevated pipe into thread local storage for logging."); 687
669 } 688 loggingContext.hFinishedEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
689 ExitOnNullWithLastError(loggingContext.hFinishedEvent, hr, "Failed to create finished event for logging thread.");
690
691 loggingContext.hPipe = pEngineState->companionConnection.hLoggingPipe;
692
693 hLoggingThread = ::CreateThread(NULL, 0, ElevatedLoggingThreadProc, &loggingContext, 0, NULL);
694 ExitOnNullWithLastError(hLoggingThread, hr, "Failed to create elevated logging thread.");
670 695
671 LogRedirect(RedirectLoggingOverPipe, pEngineState); 696 LogRedirect(RedirectLoggingOverPipe, &loggingContext);
672 697
673 // Create a top-level window to prevent shutting down the elevated process. 698 // Create a top-level window to prevent shutting down the elevated process.
674 hr = UiCreateMessageWindow(hInstance, pEngineState); 699 hr = UiCreateMessageWindow(hInstance, pEngineState);
@@ -677,16 +702,35 @@ static HRESULT RunElevated(
677 SrpInitialize(TRUE); 702 SrpInitialize(TRUE);
678 703
679 // Pump messages from parent process. 704 // Pump messages from parent process.
680 hr = ElevationChildPumpMessages(pEngineState->dwElevatedLoggingTlsId, pEngineState->companionConnection.hPipe, pEngineState->companionConnection.hCachePipe, &pEngineState->approvedExes, &pEngineState->cache, &pEngineState->containers, &pEngineState->packages, &pEngineState->payloads, &pEngineState->variables, &pEngineState->registration, &pEngineState->userExperience, &hLock, &pEngineState->userExperience.dwExitCode, &pEngineState->fRestart, &pEngineState->plan.fApplying); 705 hr = ElevationChildPumpMessages(pEngineState->companionConnection.hPipe, pEngineState->companionConnection.hCachePipe, &pEngineState->approvedExes, &pEngineState->cache, &pEngineState->containers, &pEngineState->packages, &pEngineState->payloads, &pEngineState->variables, &pEngineState->registration, &pEngineState->userExperience, &hLock, &pEngineState->userExperience.dwExitCode, &pEngineState->fRestart, &pEngineState->plan.fApplying);
681 LogRedirect(NULL, NULL); // reset logging so the next failure gets written to "log buffer" for the failure log.
682 ExitOnFailure(hr, "Failed to pump messages from parent process."); 706 ExitOnFailure(hr, "Failed to pump messages from parent process.");
683 707
708 WaitForElevatedLoggingThread(&loggingContext, hLoggingThread);
709
684LExit: 710LExit:
711 ReleaseHandle(hLoggingThread);
712
685 LogRedirect(NULL, NULL); // we're done talking to the child so always reset logging now. 713 LogRedirect(NULL, NULL); // we're done talking to the child so always reset logging now.
686 714
687 // If the message window is still around, close it. 715 // If the message window is still around, close it.
688 UiCloseMessageWindow(pEngineState); 716 UiCloseMessageWindow(pEngineState);
689 717
718 if (fDeleteLoggingCs)
719 {
720 ::DeleteCriticalSection(&loggingContext.csBuffer);
721 }
722
723 ReleaseHandle(loggingContext.hLogEvent);
724 ReleaseHandle(loggingContext.hFinishedEvent);
725
726 // If there was a log message left, try to log it locally.
727 if (loggingContext.sczBuffer)
728 {
729 LogStringWorkRaw(loggingContext.sczBuffer);
730
731 ReleaseStr(loggingContext.sczBuffer);
732 }
733
690 if (hLock) 734 if (hLock)
691 { 735 {
692 ::ReleaseMutex(hLock); 736 ::ReleaseMutex(hLock);
@@ -883,54 +927,180 @@ static HRESULT DAPI RedirectLoggingOverPipe(
883 __in_opt LPVOID pvContext 927 __in_opt LPVOID pvContext
884 ) 928 )
885{ 929{
886 static BOOL s_fCurrentlyLoggingToPipe = FALSE; 930 HRESULT hr = S_OK;
931 REDIRECTED_LOGGING_CONTEXT* pContext = static_cast<REDIRECTED_LOGGING_CONTEXT*>(pvContext);
932
933 ::EnterCriticalSection(&pContext->csBuffer);
934
935 hr = StrAnsiAllocConcat(&pContext->sczBuffer, szString, 0);
936
937 if (SUCCEEDED(hr) && !::SetEvent(pContext->hLogEvent))
938 {
939 HRESULT hrSet = HRESULT_FROM_WIN32(::GetLastError());
940 if (FAILED(hrSet))
941 {
942 TraceError(hrSet, "Failed to set log event.");
943 }
944 }
945
946 ::LeaveCriticalSection(&pContext->csBuffer);
887 947
948 return hr;
949}
950
951static HRESULT LogStringOverPipe(
952 __in_z LPCSTR szString,
953 __in HANDLE hPipe
954 )
955{
888 HRESULT hr = S_OK; 956 HRESULT hr = S_OK;
889 BURN_ENGINE_STATE* pEngineState = static_cast<BURN_ENGINE_STATE*>(pvContext);
890 BOOL fStartedLogging = FALSE;
891 HANDLE hPipe = INVALID_HANDLE_VALUE;
892 BYTE* pbData = NULL; 957 BYTE* pbData = NULL;
893 SIZE_T cbData = 0; 958 SIZE_T cbData = 0;
894 DWORD dwResult = 0; 959 DWORD dwResult = 0;
895 960
896 // Prevent this function from being called recursively. 961 hr = BuffWriteStringAnsi(&pbData, &cbData, szString);
897 if (s_fCurrentlyLoggingToPipe) 962 ExitOnFailure(hr, "Failed to prepare logging pipe message.");
898 { 963
899 ExitFunction(); 964 hr = PipeSendMessage(hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_LOG), pbData, cbData, NULL, NULL, &dwResult);
900 } 965 ExitOnFailure(hr, "Failed to send logging message over the pipe.");
966
967 hr = (HRESULT)dwResult;
968
969LExit:
970 ReleaseBuffer(pbData);
901 971
902 s_fCurrentlyLoggingToPipe = TRUE; 972 return hr;
903 fStartedLogging = TRUE; 973}
974
975static DWORD WINAPI ElevatedLoggingThreadProc(
976 __in LPVOID lpThreadParameter
977 )
978{
979 HRESULT hr = S_OK;
980 DWORD dwLastError = ERROR_SUCCESS;
981 REDIRECTED_LOGGING_CONTEXT* pContext = static_cast<REDIRECTED_LOGGING_CONTEXT*>(lpThreadParameter);
982 DWORD dwSignaledIndex = 0;
983 LPSTR sczBuffer = NULL;
984 BURN_PIPE_RESULT result = { };
985 HANDLE rghEvents[2] =
986 {
987 pContext->hLogEvent,
988 pContext->hFinishedEvent,
989 };
904 990
905 // Make sure the current thread set the pipe in TLS. 991 for (;;)
906 hPipe = ::TlsGetValue(pEngineState->dwElevatedLoggingTlsId);
907 if (!hPipe || INVALID_HANDLE_VALUE == hPipe)
908 { 992 {
909 hr = HRESULT_FROM_WIN32(ERROR_PIPE_NOT_CONNECTED); 993 hr = AppWaitForMultipleObjects(countof(rghEvents), rghEvents, FALSE, INFINITE, &dwSignaledIndex);
910 ExitFunction(); 994 if (FAILED(hr))
995 {
996 LogRedirect(NULL, NULL); // reset logging so the next failure gets written locally.
997 ExitOnFailure(hr, "Failed to wait for log thread events, signaled: %u.", dwSignaledIndex);
998 }
999
1000 if (1 == dwSignaledIndex)
1001 {
1002 LogRedirect(NULL, NULL); // No more messages will be logged over the pipe.
1003 }
1004
1005 dwLastError = ERROR_SUCCESS;
1006
1007 ::EnterCriticalSection(&pContext->csBuffer);
1008
1009 sczBuffer = pContext->sczBuffer;
1010 pContext->sczBuffer = NULL;
1011
1012 if (0 == dwSignaledIndex && !::ResetEvent(rghEvents[0]))
1013 {
1014 dwLastError = ::GetLastError();
1015 }
1016
1017 ::LeaveCriticalSection(&pContext->csBuffer);
1018
1019 if (ERROR_SUCCESS != dwLastError)
1020 {
1021 LogRedirect(NULL, NULL); // reset logging so the next failure gets written locally.
1022 ExitOnWin32Error(dwLastError, hr, "Failed to reset log event.");
1023 }
1024
1025 if (sczBuffer)
1026 {
1027 hr = LogStringOverPipe(sczBuffer, pContext->hPipe);
1028 if (FAILED(hr))
1029 {
1030 LogRedirect(NULL, NULL); // reset logging so the next failure gets written locally.
1031 ExitOnFailure(hr, "Failed to wait log message over pipe.");
1032 }
1033
1034 ReleaseStr(sczBuffer);
1035 }
1036
1037 if (1 == dwSignaledIndex)
1038 {
1039 break;
1040 }
911 } 1041 }
912 1042
913 // Do not log or use ExitOnFailure() macro here because they will be discarded 1043LExit:
914 // by the recursive block at the top of this function. 1044 LogRedirect(NULL, NULL); // No more messages will be logged over the pipe.
915 hr = BuffWriteStringAnsi(&pbData, &cbData, szString); 1045
916 if (SUCCEEDED(hr))
917 { 1046 {
918 hr = PipeSendMessage(hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_LOG), pbData, cbData, NULL, NULL, &dwResult); 1047 HRESULT hrTerminate = PipeTerminateLoggingPipe(pContext->hPipe, hr);
919 if (SUCCEEDED(hr)) 1048 if (FAILED(hrTerminate))
920 { 1049 {
921 hr = (HRESULT)dwResult; 1050 TraceError(hrTerminate, "Failed to terminate logging pipe.");
922 } 1051 }
923 } 1052 }
924 1053
925LExit: 1054 // Log the message locally if it failed to go over the pipe.
926 ReleaseBuffer(pbData); 1055 if (sczBuffer)
1056 {
1057 LogStringWorkRaw(sczBuffer);
1058
1059 ReleaseStr(sczBuffer);
1060 }
1061
1062 // Log any remaining message locally.
1063 if (pContext->sczBuffer)
1064 {
1065 AssertSz(FAILED(hr), "Exiting logging thread on success even though there was a leftover message");
1066 LogStringWorkRaw(pContext->sczBuffer);
1067
1068 ReleaseStr(pContext->sczBuffer);
1069 }
1070
1071 return (DWORD)hr;
1072}
1073
1074static HRESULT WaitForElevatedLoggingThread(
1075 __in REDIRECTED_LOGGING_CONTEXT* pContext,
1076 __in HANDLE hLoggingThread
1077 )
1078{
1079 HRESULT hr = S_OK;
927 1080
928 // We started logging so remember to say we are no longer logging. 1081 if (!::SetEvent(pContext->hFinishedEvent))
929 if (fStartedLogging)
930 { 1082 {
931 s_fCurrentlyLoggingToPipe = FALSE; 1083 ExitWithLastError(hr, "Failed to set log finished event.");
932 } 1084 }
933 1085
1086 hr = AppWaitForSingleObject(hLoggingThread, 5 * 60 * 1000); // TODO: is 5 minutes good?
1087 ExitOnFailure(hr, "Failed to wait for elevated logging thread.");
1088
1089LExit:
1090 return hr;
1091}
1092
1093static HRESULT WaitForUnelevatedLoggingThread(
1094 __in HANDLE hUnelevatedLoggingThread
1095 )
1096{
1097 HRESULT hr = S_OK;
1098
1099 // Give the thread 15 seconds to exit.
1100 hr = AppWaitForSingleObject(hUnelevatedLoggingThread, 15 * 1000);
1101 ExitOnFailure(hr, "Failed to wait for unelevated logging thread.");
1102
1103LExit:
934 return hr; 1104 return hr;
935} 1105}
936 1106