diff options
| author | Sean Hall <r.sean.hall@gmail.com> | 2022-08-19 14:08:23 -0500 |
|---|---|---|
| committer | Sean Hall <r.sean.hall@gmail.com> | 2022-08-19 17:02:27 -0500 |
| commit | b72f58abdf6dd5d0020f174358027158cb52cb72 (patch) | |
| tree | 3070c1f1fa8a74125a2933f23a1be3c514ed1de7 /src/burn/engine/engine.cpp | |
| parent | bbc36178172e4198351f2f84a91faf56c65e1475 (diff) | |
| download | wix-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 '')
| -rw-r--r-- | src/burn/engine/engine.cpp | 264 |
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 | ||
| 6 | typedef 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 | ||
| 8 | const DWORD RESTART_RETRIES = 10; | 17 | const 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 | ); |
| 61 | static HRESULT LogStringOverPipe( | ||
| 62 | __in_z LPCSTR szString, | ||
| 63 | __in HANDLE hPipe | ||
| 64 | ); | ||
| 65 | static DWORD WINAPI ElevatedLoggingThreadProc( | ||
| 66 | __in LPVOID lpThreadParameter | ||
| 67 | ); | ||
| 68 | static HRESULT WaitForElevatedLoggingThread( | ||
| 69 | __in REDIRECTED_LOGGING_CONTEXT* pContext, | ||
| 70 | __in HANDLE hLoggingThread | ||
| 71 | ); | ||
| 72 | static HRESULT WaitForUnelevatedLoggingThread( | ||
| 73 | __in HANDLE hUnelevatedLoggingThread | ||
| 74 | ); | ||
| 52 | static HRESULT Restart(); | 75 | static HRESULT Restart(); |
| 53 | static void CALLBACK BurnTraceError( | 76 | static 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 | |||
| 684 | LExit: | 710 | LExit: |
| 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 | |||
| 951 | static 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 | |||
| 969 | LExit: | ||
| 970 | ReleaseBuffer(pbData); | ||
| 901 | 971 | ||
| 902 | s_fCurrentlyLoggingToPipe = TRUE; | 972 | return hr; |
| 903 | fStartedLogging = TRUE; | 973 | } |
| 974 | |||
| 975 | static 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 | 1043 | LExit: |
| 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 | ||
| 925 | LExit: | 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 | |||
| 1074 | static 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 | |||
| 1089 | LExit: | ||
| 1090 | return hr; | ||
| 1091 | } | ||
| 1092 | |||
| 1093 | static 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 | |||
| 1103 | LExit: | ||
| 934 | return hr; | 1104 | return hr; |
| 935 | } | 1105 | } |
| 936 | 1106 | ||
