diff options
Diffstat (limited to 'src/burn/engine/engine.cpp')
-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 | ||