Index: src/installer-ca/dutil/procutil.cpp |
=================================================================== |
new file mode 100644 |
--- /dev/null |
+++ b/src/installer-ca/dutil/procutil.cpp |
@@ -0,0 +1,499 @@ |
+//------------------------------------------------------------------------------------------------- |
+// <copyright file="procutil.cpp" company="Outercurve Foundation"> |
+// Copyright (c) 2004, Outercurve Foundation. |
+// This software is released under Microsoft Reciprocal License (MS-RL). |
+// The license and further copyright text can be found in the file |
+// LICENSE.TXT at the root directory of the distribution. |
+// </copyright> |
+// |
+// <summary> |
+// Process helper functions. |
+// </summary> |
+//------------------------------------------------------------------------------------------------- |
+ |
+#include "../precomp.h" |
+ |
+ |
+// private functions |
+static HRESULT CreatePipes( |
+ __out HANDLE *phOutRead, |
+ __out HANDLE *phOutWrite, |
+ __out HANDLE *phErrWrite, |
+ __out HANDLE *phInRead, |
+ __out HANDLE *phInWrite |
+ ); |
+ |
+static BOOL CALLBACK CloseWindowEnumCallback( |
+ __in HWND hWnd, |
+ __in LPARAM lParam |
+ ); |
+ |
+ |
+extern "C" HRESULT DAPI ProcElevated( |
+ __in HANDLE hProcess, |
+ __out BOOL* pfElevated |
+ ) |
+{ |
+ HRESULT hr = S_OK; |
+ HANDLE hToken = NULL; |
+ TOKEN_ELEVATION tokenElevated = { }; |
+ DWORD cbToken = 0; |
+ |
+ if (!::OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) |
+ { |
+ ExitWithLastError(hr, "Failed to open process token."); |
+ } |
+ |
+ if (::GetTokenInformation(hToken, TokenElevation, &tokenElevated, sizeof(TOKEN_ELEVATION), &cbToken)) |
+ { |
+ *pfElevated = (0 != tokenElevated.TokenIsElevated); |
+ } |
+ else |
+ { |
+ DWORD er = ::GetLastError(); |
+ hr = HRESULT_FROM_WIN32(er); |
+ |
+ // If it's invalid argument, this means the OS doesn't support TokenElevation, so we're not elevated. |
+ if (E_INVALIDARG == hr) |
+ { |
+ *pfElevated = FALSE; |
+ hr = S_OK; |
+ } |
+ else |
+ { |
+ ExitOnRootFailure(hr, "Failed to get elevation token from process."); |
+ } |
+ } |
+ |
+LExit: |
+ ReleaseHandle(hToken); |
+ |
+ return hr; |
+} |
+ |
+extern "C" HRESULT DAPI ProcWow64( |
+ __in HANDLE hProcess, |
+ __out BOOL* pfWow64 |
+ ) |
+{ |
+ HRESULT hr = S_OK; |
+ BOOL fIsWow64 = FALSE; |
+ |
+ typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); |
+ LPFN_ISWOW64PROCESS pfnIsWow64Process = (LPFN_ISWOW64PROCESS)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "IsWow64Process"); |
+ |
+ if (pfnIsWow64Process) |
+ { |
+ if (!pfnIsWow64Process(hProcess, &fIsWow64)) |
+ { |
+ ExitWithLastError(hr, "Failed to check WOW64 process."); |
+ } |
+ } |
+ |
+ *pfWow64 = fIsWow64; |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+extern "C" HRESULT DAPI ProcDisableWowFileSystemRedirection( |
+ __in PROC_FILESYSTEMREDIRECTION* pfsr |
+ ) |
+{ |
+ AssertSz(!pfsr->fDisabled, "File system redirection was already disabled."); |
+ HRESULT hr = S_OK; |
+ |
+ typedef BOOL (WINAPI *LPFN_Wow64DisableWow64FsRedirection)(PVOID *); |
+ LPFN_Wow64DisableWow64FsRedirection pfnWow64DisableWow64FsRedirection = (LPFN_Wow64DisableWow64FsRedirection)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "Wow64DisableWow64FsRedirection"); |
+ |
+ if (!pfnWow64DisableWow64FsRedirection) |
+ { |
+ ExitFunction1(hr = E_NOTIMPL); |
+ } |
+ |
+ if (!pfnWow64DisableWow64FsRedirection(&pfsr->pvRevertState)) |
+ { |
+ ExitWithLastError(hr, "Failed to disable file system redirection."); |
+ } |
+ |
+ pfsr->fDisabled = TRUE; |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+extern "C" HRESULT DAPI ProcRevertWowFileSystemRedirection( |
+ __in PROC_FILESYSTEMREDIRECTION* pfsr |
+ ) |
+{ |
+ HRESULT hr = S_OK; |
+ |
+ if (pfsr->fDisabled) |
+ { |
+ typedef BOOL (WINAPI *LPFN_Wow64RevertWow64FsRedirection)(PVOID); |
+ LPFN_Wow64RevertWow64FsRedirection pfnWow64RevertWow64FsRedirection = (LPFN_Wow64RevertWow64FsRedirection)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "Wow64RevertWow64FsRedirection"); |
+ |
+ if (!pfnWow64RevertWow64FsRedirection(pfsr->pvRevertState)) |
+ { |
+ ExitWithLastError(hr, "Failed to revert file system redirection."); |
+ } |
+ |
+ pfsr->fDisabled = FALSE; |
+ pfsr->pvRevertState = NULL; |
+ } |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+extern "C" HRESULT DAPI ProcExec( |
+ __in_z LPCWSTR wzExecutablePath, |
+ __in_z_opt LPCWSTR wzCommandLine, |
+ __in int nCmdShow, |
+ __out HANDLE *phProcess |
+ ) |
+{ |
+ HRESULT hr = S_OK; |
+ LPWSTR sczFullCommandLine = NULL; |
+ STARTUPINFOW si = { }; |
+ PROCESS_INFORMATION pi = { }; |
+ |
+ hr = StrAllocFormatted(&sczFullCommandLine, L"\"%ls\" %ls", wzExecutablePath, wzCommandLine ? wzCommandLine : L""); |
+ ExitOnFailure(hr, "Failed to allocate full command-line."); |
+ |
+ si.cb = sizeof(si); |
+ si.wShowWindow = static_cast<WORD>(nCmdShow); |
+ if (!::CreateProcessW(wzExecutablePath, sczFullCommandLine, NULL, NULL, FALSE, 0, 0, NULL, &si, &pi)) |
+ { |
+ ExitWithLastError1(hr, "Failed to create process: %ls", sczFullCommandLine); |
+ } |
+ |
+ *phProcess = pi.hProcess; |
+ pi.hProcess = NULL; |
+ |
+LExit: |
+ ReleaseHandle(pi.hThread); |
+ ReleaseHandle(pi.hProcess); |
+ ReleaseStr(sczFullCommandLine); |
+ |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+ ProcExecute() - executes a command-line. |
+ |
+*******************************************************************/ |
+extern "C" HRESULT DAPI ProcExecute( |
+ __in_z LPWSTR wzCommand, |
+ __out HANDLE *phProcess, |
+ __out_opt HANDLE *phChildStdIn, |
+ __out_opt HANDLE *phChildStdOutErr |
+ ) |
+{ |
+ HRESULT hr = S_OK; |
+ |
+ PROCESS_INFORMATION pi = { }; |
+ STARTUPINFOW si = { }; |
+ |
+ HANDLE hOutRead = INVALID_HANDLE_VALUE; |
+ HANDLE hOutWrite = INVALID_HANDLE_VALUE; |
+ HANDLE hErrWrite = INVALID_HANDLE_VALUE; |
+ HANDLE hInRead = INVALID_HANDLE_VALUE; |
+ HANDLE hInWrite = INVALID_HANDLE_VALUE; |
+ |
+ // Create redirect pipes. |
+ hr = CreatePipes(&hOutRead, &hOutWrite, &hErrWrite, &hInRead, &hInWrite); |
+ ExitOnFailure(hr, "failed to create output pipes"); |
+ |
+ // Set up startup structure. |
+ si.cb = sizeof(STARTUPINFOW); |
+ si.dwFlags = STARTF_USESTDHANDLES; |
+ si.hStdInput = hInRead; |
+ si.hStdOutput = hOutWrite; |
+ si.hStdError = hErrWrite; |
+ |
+#pragma prefast(push) |
+#pragma prefast(disable:25028) |
+ if (::CreateProcessW(NULL, |
+ wzCommand, // command line |
+ NULL, // security info |
+ NULL, // thread info |
+ TRUE, // inherit handles |
+ ::GetPriorityClass(::GetCurrentProcess()) | CREATE_NO_WINDOW, // creation flags |
+ NULL, // environment |
+ NULL, // cur dir |
+ &si, |
+ &pi)) |
+#pragma prefast(pop) |
+ { |
+ // Close child process output/input handles so child doesn't hang |
+ // while waiting for input from parent process. |
+ ::CloseHandle(hOutWrite); |
+ hOutWrite = INVALID_HANDLE_VALUE; |
+ |
+ ::CloseHandle(hErrWrite); |
+ hErrWrite = INVALID_HANDLE_VALUE; |
+ |
+ ::CloseHandle(hInRead); |
+ hInRead = INVALID_HANDLE_VALUE; |
+ } |
+ else |
+ { |
+ ExitWithLastError(hr, "Process failed to execute."); |
+ } |
+ |
+ *phProcess = pi.hProcess; |
+ pi.hProcess = 0; |
+ |
+ if (phChildStdIn) |
+ { |
+ *phChildStdIn = hInWrite; |
+ hInWrite = INVALID_HANDLE_VALUE; |
+ } |
+ |
+ if (phChildStdOutErr) |
+ { |
+ *phChildStdOutErr = hOutRead; |
+ hOutRead = INVALID_HANDLE_VALUE; |
+ } |
+ |
+LExit: |
+ if (pi.hThread) |
+ { |
+ ::CloseHandle(pi.hThread); |
+ } |
+ |
+ if (pi.hProcess) |
+ { |
+ ::CloseHandle(pi.hProcess); |
+ } |
+ |
+ ReleaseFileHandle(hOutRead); |
+ ReleaseFileHandle(hOutWrite); |
+ ReleaseFileHandle(hErrWrite); |
+ ReleaseFileHandle(hInRead); |
+ ReleaseFileHandle(hInWrite); |
+ |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+ ProcWaitForCompletion() - waits for process to complete and gets return code. |
+ |
+*******************************************************************/ |
+extern "C" HRESULT DAPI ProcWaitForCompletion( |
+ __in HANDLE hProcess, |
+ __in DWORD dwTimeout, |
+ __out DWORD *pReturnCode |
+ ) |
+{ |
+ HRESULT hr = S_OK; |
+ DWORD er = ERROR_SUCCESS; |
+ |
+ // Wait for everything to finish |
+ er = ::WaitForSingleObject(hProcess, dwTimeout); |
+ if (WAIT_FAILED == er) |
+ { |
+ ExitWithLastError(hr, "Failed to wait for process to complete."); |
+ } |
+ else if (WAIT_TIMEOUT == er) |
+ { |
+ ExitFunction1(hr = HRESULT_FROM_WIN32(er)); |
+ } |
+ |
+ if (!::GetExitCodeProcess(hProcess, &er)) |
+ { |
+ ExitWithLastError(hr, "Failed to get process return code."); |
+ } |
+ |
+ *pReturnCode = er; |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+/******************************************************************** |
+ ProcWaitForIds() - waits for multiple processes to complete. |
+ |
+*******************************************************************/ |
+extern "C" HRESULT DAPI ProcWaitForIds( |
+ __in_ecount(cProcessIds) const DWORD rgdwProcessIds[], |
+ __in DWORD cProcessIds, |
+ __in DWORD dwMilliseconds |
+ ) |
+{ |
+ HRESULT hr = S_OK; |
+ DWORD er = ERROR_SUCCESS; |
+ HANDLE hProcess = NULL; |
+ HANDLE * rghProcesses = NULL; |
+ DWORD cProcesses = 0; |
+ |
+ rghProcesses = static_cast<HANDLE*>(MemAlloc(sizeof(DWORD) * cProcessIds, TRUE)); |
+ ExitOnNull(rgdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for process ID Handles."); |
+ |
+ for (DWORD i = 0; i < cProcessIds; ++i) |
+ { |
+ hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, rgdwProcessIds[i]); |
+ if (hProcess != NULL) |
+ { |
+ rghProcesses[cProcesses++] = hProcess; |
+ } |
+ } |
+ |
+ er = ::WaitForMultipleObjects(cProcesses, rghProcesses, TRUE, dwMilliseconds); |
+ if (WAIT_FAILED == er) |
+ { |
+ ExitWithLastError(hr, "Failed to wait for process to complete."); |
+ } |
+ else if (WAIT_TIMEOUT == er) |
+ { |
+ ExitOnWin32Error(er, hr, "Timed out while waiting for process to complete."); |
+ } |
+ |
+LExit: |
+ if (rghProcesses) |
+ { |
+ for (DWORD i = 0; i < cProcesses; ++i) |
+ { |
+ if (NULL != rghProcesses[i]) |
+ { |
+ ::CloseHandle(rghProcesses[i]); |
+ } |
+ } |
+ |
+ MemFree(rghProcesses); |
+ } |
+ |
+ return hr; |
+} |
+ |
+/******************************************************************** |
+ ProcCloseIds() - sends WM_CLOSE messages to all process ids. |
+ |
+*******************************************************************/ |
+extern "C" HRESULT DAPI ProcCloseIds( |
+ __in_ecount(cProcessIds) const DWORD* pdwProcessIds, |
+ __in DWORD cProcessIds |
+ ) |
+{ |
+ HRESULT hr = S_OK; |
+ |
+ for (DWORD i = 0; i < cProcessIds; ++i) |
+ { |
+ if (!::EnumWindows(&CloseWindowEnumCallback, pdwProcessIds[i])) |
+ { |
+ ExitWithLastError(hr, "Failed to enumerate windows."); |
+ } |
+ } |
+ |
+LExit: |
+ return hr; |
+} |
+ |
+ |
+static HRESULT CreatePipes( |
+ __out HANDLE *phOutRead, |
+ __out HANDLE *phOutWrite, |
+ __out HANDLE *phErrWrite, |
+ __out HANDLE *phInRead, |
+ __out HANDLE *phInWrite |
+ ) |
+{ |
+ HRESULT hr = S_OK; |
+ SECURITY_ATTRIBUTES sa; |
+ HANDLE hOutTemp = INVALID_HANDLE_VALUE; |
+ HANDLE hInTemp = INVALID_HANDLE_VALUE; |
+ |
+ HANDLE hOutRead = INVALID_HANDLE_VALUE; |
+ HANDLE hOutWrite = INVALID_HANDLE_VALUE; |
+ HANDLE hErrWrite = INVALID_HANDLE_VALUE; |
+ HANDLE hInRead = INVALID_HANDLE_VALUE; |
+ HANDLE hInWrite = INVALID_HANDLE_VALUE; |
+ |
+ // Fill out security structure so we can inherit handles |
+ ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); |
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES); |
+ sa.bInheritHandle = TRUE; |
+ sa.lpSecurityDescriptor = NULL; |
+ |
+ // Create pipes |
+ if (!::CreatePipe(&hOutTemp, &hOutWrite, &sa, 0)) |
+ { |
+ ExitWithLastError(hr, "failed to create output pipe"); |
+ } |
+ |
+ if (!::CreatePipe(&hInRead, &hInTemp, &sa, 0)) |
+ { |
+ ExitWithLastError(hr, "failed to create input pipe"); |
+ } |
+ |
+ // Duplicate output pipe so standard error and standard output write to the same pipe. |
+ if (!::DuplicateHandle(::GetCurrentProcess(), hOutWrite, ::GetCurrentProcess(), &hErrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS)) |
+ { |
+ ExitWithLastError(hr, "failed to duplicate write handle"); |
+ } |
+ |
+ // We need to create new "output read" and "input write" handles that are non inheritable. Otherwise CreateProcess will creates handles in |
+ // the child process that can't be closed. |
+ if (!::DuplicateHandle(::GetCurrentProcess(), hOutTemp, ::GetCurrentProcess(), &hOutRead, 0, FALSE, DUPLICATE_SAME_ACCESS)) |
+ { |
+ ExitWithLastError(hr, "failed to duplicate output pipe"); |
+ } |
+ |
+ if (!::DuplicateHandle(::GetCurrentProcess(), hInTemp, ::GetCurrentProcess(), &hInWrite, 0, FALSE, DUPLICATE_SAME_ACCESS)) |
+ { |
+ ExitWithLastError(hr, "failed to duplicate input pipe"); |
+ } |
+ |
+ // now that everything has succeeded, assign to the outputs |
+ *phOutRead = hOutRead; |
+ hOutRead = INVALID_HANDLE_VALUE; |
+ |
+ *phOutWrite = hOutWrite; |
+ hOutWrite = INVALID_HANDLE_VALUE; |
+ |
+ *phErrWrite = hErrWrite; |
+ hErrWrite = INVALID_HANDLE_VALUE; |
+ |
+ *phInRead = hInRead; |
+ hInRead = INVALID_HANDLE_VALUE; |
+ |
+ *phInWrite = hInWrite; |
+ hInWrite = INVALID_HANDLE_VALUE; |
+ |
+LExit: |
+ ReleaseFileHandle(hOutRead); |
+ ReleaseFileHandle(hOutWrite); |
+ ReleaseFileHandle(hErrWrite); |
+ ReleaseFileHandle(hInRead); |
+ ReleaseFileHandle(hInWrite); |
+ ReleaseFileHandle(hOutTemp); |
+ ReleaseFileHandle(hInTemp); |
+ |
+ return hr; |
+} |
+ |
+ |
+/******************************************************************** |
+ CloseWindowEnumCallback() - outputs trace and log info |
+ |
+*******************************************************************/ |
+static BOOL CALLBACK CloseWindowEnumCallback( |
+ __in HWND hWnd, |
+ __in LPARAM lParam |
+ ) |
+{ |
+ DWORD dwPid = static_cast<DWORD>(lParam); |
+ DWORD dwProcessId = 0; |
+ |
+ ::GetWindowThreadProcessId(hWnd, &dwProcessId); |
+ if (dwPid == dwProcessId) |
+ { |
+ ::SendMessageW(hWnd, WM_CLOSE, 0, 0); |
+ } |
+ |
+ return TRUE; |
+} |