diff options
Diffstat (limited to 'src/libs/wcautil/WixToolset.WcaUtil/qtexec.cpp')
-rw-r--r-- | src/libs/wcautil/WixToolset.WcaUtil/qtexec.cpp | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/qtexec.cpp b/src/libs/wcautil/WixToolset.WcaUtil/qtexec.cpp new file mode 100644 index 00000000..19abfaf8 --- /dev/null +++ b/src/libs/wcautil/WixToolset.WcaUtil/qtexec.cpp | |||
@@ -0,0 +1,340 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | #include "precomp.h" | ||
4 | |||
5 | #define OUTPUT_BUFFER 1024 | ||
6 | #define ONEMINUTE 60000 | ||
7 | |||
8 | static HRESULT CreatePipes( | ||
9 | __out HANDLE *phOutRead, | ||
10 | __out HANDLE *phOutWrite, | ||
11 | __out HANDLE *phErrWrite, | ||
12 | __out HANDLE *phInRead, | ||
13 | __out HANDLE *phInWrite | ||
14 | ) | ||
15 | { | ||
16 | Assert(phOutRead); | ||
17 | Assert(phOutWrite); | ||
18 | Assert(phErrWrite); | ||
19 | Assert(phInRead); | ||
20 | Assert(phInWrite); | ||
21 | |||
22 | HRESULT hr = S_OK; | ||
23 | SECURITY_ATTRIBUTES sa; | ||
24 | HANDLE hOutTemp = INVALID_HANDLE_VALUE; | ||
25 | HANDLE hInTemp = INVALID_HANDLE_VALUE; | ||
26 | |||
27 | HANDLE hOutRead = INVALID_HANDLE_VALUE; | ||
28 | HANDLE hOutWrite = INVALID_HANDLE_VALUE; | ||
29 | HANDLE hErrWrite = INVALID_HANDLE_VALUE; | ||
30 | HANDLE hInRead = INVALID_HANDLE_VALUE; | ||
31 | HANDLE hInWrite = INVALID_HANDLE_VALUE; | ||
32 | |||
33 | // Fill out security structure so we can inherit handles | ||
34 | ::ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); | ||
35 | sa.nLength = sizeof(SECURITY_ATTRIBUTES); | ||
36 | sa.bInheritHandle = TRUE; | ||
37 | sa.lpSecurityDescriptor = NULL; | ||
38 | |||
39 | // Create pipes | ||
40 | if (!::CreatePipe(&hOutTemp, &hOutWrite, &sa, 0)) | ||
41 | { | ||
42 | ExitOnLastError(hr, "Failed to create output pipe"); | ||
43 | } | ||
44 | |||
45 | if (!::CreatePipe(&hInRead, &hInTemp, &sa, 0)) | ||
46 | { | ||
47 | ExitOnLastError(hr, "Failed to create input pipe"); | ||
48 | } | ||
49 | |||
50 | |||
51 | // Duplicate output pipe so standard error and standard output write to | ||
52 | // the same pipe | ||
53 | if (!::DuplicateHandle(::GetCurrentProcess(), hOutWrite, ::GetCurrentProcess(), &hErrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS)) | ||
54 | { | ||
55 | ExitOnLastError(hr, "Failed to duplicate write handle"); | ||
56 | } | ||
57 | |||
58 | // We need to create new output read and input write handles that are | ||
59 | // non inheritable. Otherwise it creates handles that can't be closed. | ||
60 | if (!::DuplicateHandle(::GetCurrentProcess(), hOutTemp, ::GetCurrentProcess(), &hOutRead, 0, FALSE, DUPLICATE_SAME_ACCESS)) | ||
61 | { | ||
62 | ExitOnLastError(hr, "Failed to duplicate output pipe"); | ||
63 | } | ||
64 | |||
65 | if (!::DuplicateHandle(::GetCurrentProcess(), hInTemp, ::GetCurrentProcess(), &hInWrite, 0, FALSE, DUPLICATE_SAME_ACCESS)) | ||
66 | { | ||
67 | ExitOnLastError(hr, "Failed to duplicate input pipe"); | ||
68 | } | ||
69 | |||
70 | // now that everything has succeeded, assign to the outputs | ||
71 | *phOutRead = hOutRead; | ||
72 | hOutRead = INVALID_HANDLE_VALUE; | ||
73 | |||
74 | *phOutWrite = hOutWrite; | ||
75 | hOutWrite = INVALID_HANDLE_VALUE; | ||
76 | |||
77 | *phErrWrite = hErrWrite; | ||
78 | hErrWrite = INVALID_HANDLE_VALUE; | ||
79 | |||
80 | *phInRead = hInRead; | ||
81 | hInRead = INVALID_HANDLE_VALUE; | ||
82 | |||
83 | *phInWrite = hInWrite; | ||
84 | hInWrite = INVALID_HANDLE_VALUE; | ||
85 | |||
86 | LExit: | ||
87 | ReleaseFile(hOutRead); | ||
88 | ReleaseFile(hOutWrite); | ||
89 | ReleaseFile(hErrWrite); | ||
90 | ReleaseFile(hInRead); | ||
91 | ReleaseFile(hInWrite); | ||
92 | ReleaseFile(hOutTemp); | ||
93 | ReleaseFile(hInTemp); | ||
94 | |||
95 | return hr; | ||
96 | } | ||
97 | |||
98 | static HRESULT HandleOutput( | ||
99 | __in BOOL fLogOutput, | ||
100 | __in HANDLE hRead, | ||
101 | __out_z_opt LPWSTR* psczOutput | ||
102 | ) | ||
103 | { | ||
104 | BYTE* pBuffer = NULL; | ||
105 | LPWSTR szLog = NULL; | ||
106 | LPWSTR szTemp = NULL; | ||
107 | LPWSTR pEnd = NULL; | ||
108 | LPWSTR pNext = NULL; | ||
109 | LPWSTR sczEscaped = NULL; | ||
110 | LPSTR szWrite = NULL; | ||
111 | DWORD dwBytes = OUTPUT_BUFFER; | ||
112 | BOOL bFirst = TRUE; | ||
113 | BOOL bUnicode = TRUE; | ||
114 | HRESULT hr = S_OK; | ||
115 | |||
116 | // Get buffer for output | ||
117 | pBuffer = static_cast<BYTE *>(MemAlloc(OUTPUT_BUFFER, FALSE)); | ||
118 | ExitOnNull(pBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for output."); | ||
119 | |||
120 | while (0 != dwBytes) | ||
121 | { | ||
122 | ::ZeroMemory(pBuffer, OUTPUT_BUFFER); | ||
123 | if (!::ReadFile(hRead, pBuffer, OUTPUT_BUFFER - 1, &dwBytes, NULL) && GetLastError() != ERROR_BROKEN_PIPE) | ||
124 | { | ||
125 | ExitOnLastError(hr, "Failed to read from handle."); | ||
126 | } | ||
127 | |||
128 | if (fLogOutput) | ||
129 | { | ||
130 | // Check for UNICODE or ANSI output | ||
131 | if (bFirst) | ||
132 | { | ||
133 | if ((isgraph(pBuffer[0]) && isgraph(pBuffer[1])) || | ||
134 | (isgraph(pBuffer[0]) && isspace(pBuffer[1])) || | ||
135 | (isspace(pBuffer[0]) && isgraph(pBuffer[1])) || | ||
136 | (isspace(pBuffer[0]) && isspace(pBuffer[1]))) | ||
137 | { | ||
138 | bUnicode = FALSE; | ||
139 | } | ||
140 | |||
141 | bFirst = FALSE; | ||
142 | } | ||
143 | |||
144 | // Keep track of output | ||
145 | if (bUnicode) | ||
146 | { | ||
147 | hr = StrAllocConcat(&szLog, (LPCWSTR)pBuffer, 0); | ||
148 | ExitOnFailure(hr, "Failed to concatenate output strings."); | ||
149 | |||
150 | if (psczOutput) | ||
151 | { | ||
152 | hr = StrAllocConcat(psczOutput, (LPCWSTR)pBuffer, 0); | ||
153 | ExitOnFailure(hr, "Failed to concatenate output to return string."); | ||
154 | } | ||
155 | } | ||
156 | else | ||
157 | { | ||
158 | hr = StrAllocStringAnsi(&szTemp, (LPCSTR)pBuffer, 0, CP_OEMCP); | ||
159 | ExitOnFailure(hr, "Failed to allocate output string."); | ||
160 | hr = StrAllocConcat(&szLog, szTemp, 0); | ||
161 | ExitOnFailure(hr, "Failed to concatenate output strings."); | ||
162 | |||
163 | if (psczOutput) | ||
164 | { | ||
165 | hr = StrAllocConcat(psczOutput, szTemp, 0); | ||
166 | ExitOnFailure(hr, "Failed to concatenate output to return string."); | ||
167 | } | ||
168 | } | ||
169 | |||
170 | // Log each line of the output | ||
171 | pNext = szLog; | ||
172 | pEnd = wcschr(szLog, L'\r'); | ||
173 | if (NULL == pEnd) | ||
174 | { | ||
175 | pEnd = wcschr(szLog, L'\n'); | ||
176 | } | ||
177 | while (pEnd && *pEnd) | ||
178 | { | ||
179 | // Find beginning of next line | ||
180 | pEnd[0] = 0; | ||
181 | ++pEnd; | ||
182 | if ((pEnd[0] == L'\r') || (pEnd[0] == L'\n')) | ||
183 | { | ||
184 | ++pEnd; | ||
185 | } | ||
186 | |||
187 | // Log output | ||
188 | hr = StrAllocString(&sczEscaped, pNext, 0); | ||
189 | ExitOnFailure(hr, "Failed to allocate copy of string"); | ||
190 | |||
191 | hr = StrReplaceStringAll(&sczEscaped, L"%", L"%%"); | ||
192 | ExitOnFailure(hr, "Failed to escape percent signs in string"); | ||
193 | |||
194 | hr = StrAnsiAllocString(&szWrite, sczEscaped, 0, CP_OEMCP); | ||
195 | ExitOnFailure(hr, "Failed to convert output to ANSI"); | ||
196 | WcaLog(LOGMSG_STANDARD, szWrite); | ||
197 | |||
198 | // Next line | ||
199 | pNext = pEnd; | ||
200 | pEnd = wcschr(pNext, L'\r'); | ||
201 | if (NULL == pEnd) | ||
202 | { | ||
203 | pEnd = wcschr(pNext, L'\n'); | ||
204 | } | ||
205 | } | ||
206 | |||
207 | hr = StrAllocString(&szTemp, pNext, 0); | ||
208 | ExitOnFailure(hr, "Failed to allocate string"); | ||
209 | |||
210 | hr = StrAllocString(&szLog, szTemp, 0); | ||
211 | ExitOnFailure(hr, "Failed to allocate string"); | ||
212 | } | ||
213 | } | ||
214 | |||
215 | // Print any text that didn't end with a new line | ||
216 | if (szLog && *szLog) | ||
217 | { | ||
218 | hr = StrReplaceStringAll(&szLog, L"%", L"%%"); | ||
219 | ExitOnFailure(hr, "Failed to escape percent signs in string"); | ||
220 | |||
221 | hr = StrAnsiAllocString(&szWrite, szLog, 0, CP_OEMCP); | ||
222 | ExitOnFailure(hr, "Failed to convert output to ANSI"); | ||
223 | |||
224 | WcaLog(LOGMSG_VERBOSE, szWrite); | ||
225 | } | ||
226 | |||
227 | LExit: | ||
228 | ReleaseMem(pBuffer); | ||
229 | |||
230 | ReleaseStr(szLog); | ||
231 | ReleaseStr(szTemp); | ||
232 | ReleaseStr(szWrite); | ||
233 | ReleaseStr(sczEscaped); | ||
234 | |||
235 | return hr; | ||
236 | } | ||
237 | |||
238 | static HRESULT QuietExecImpl( | ||
239 | __inout_z LPWSTR wzCommand, | ||
240 | __in DWORD dwTimeout, | ||
241 | __in BOOL fLogCommand, | ||
242 | __in BOOL fLogOutput, | ||
243 | __out_z_opt LPWSTR* psczOutput | ||
244 | ) | ||
245 | { | ||
246 | HRESULT hr = S_OK; | ||
247 | PROCESS_INFORMATION oProcInfo; | ||
248 | STARTUPINFOW oStartInfo; | ||
249 | DWORD dwExitCode = ERROR_SUCCESS; | ||
250 | HANDLE hOutRead = INVALID_HANDLE_VALUE; | ||
251 | HANDLE hOutWrite = INVALID_HANDLE_VALUE; | ||
252 | HANDLE hErrWrite = INVALID_HANDLE_VALUE; | ||
253 | HANDLE hInRead = INVALID_HANDLE_VALUE; | ||
254 | HANDLE hInWrite = INVALID_HANDLE_VALUE; | ||
255 | |||
256 | memset(&oProcInfo, 0, sizeof(oProcInfo)); | ||
257 | memset(&oStartInfo, 0, sizeof(oStartInfo)); | ||
258 | |||
259 | // Create output redirect pipes | ||
260 | hr = CreatePipes(&hOutRead, &hOutWrite, &hErrWrite, &hInRead, &hInWrite); | ||
261 | ExitOnFailure(hr, "Failed to create output pipes"); | ||
262 | |||
263 | // Set up startup structure | ||
264 | oStartInfo.cb = sizeof(STARTUPINFOW); | ||
265 | oStartInfo.dwFlags = STARTF_USESTDHANDLES; | ||
266 | oStartInfo.hStdInput = hInRead; | ||
267 | oStartInfo.hStdOutput = hOutWrite; | ||
268 | oStartInfo.hStdError = hErrWrite; | ||
269 | |||
270 | // Log command if we were asked to do so | ||
271 | if (fLogCommand) | ||
272 | { | ||
273 | WcaLog(LOGMSG_VERBOSE, "%ls", wzCommand); | ||
274 | } | ||
275 | |||
276 | #pragma prefast(suppress:25028) | ||
277 | if (::CreateProcessW(NULL, | ||
278 | wzCommand, // command line | ||
279 | NULL, // security info | ||
280 | NULL, // thread info | ||
281 | TRUE, // inherit handles | ||
282 | ::GetPriorityClass(::GetCurrentProcess()) | CREATE_NO_WINDOW, // creation flags | ||
283 | NULL, // environment | ||
284 | NULL, // cur dir | ||
285 | &oStartInfo, | ||
286 | &oProcInfo)) | ||
287 | { | ||
288 | ReleaseFile(oProcInfo.hThread); | ||
289 | |||
290 | // Close child output/input handles so it doesn't hang | ||
291 | ReleaseFile(hOutWrite); | ||
292 | ReleaseFile(hErrWrite); | ||
293 | ReleaseFile(hInRead); | ||
294 | |||
295 | // Log output if we were asked to do so; otherwise just read the output handle | ||
296 | HandleOutput(fLogOutput, hOutRead, psczOutput); | ||
297 | |||
298 | // Wait for everything to finish | ||
299 | ::WaitForSingleObject(oProcInfo.hProcess, dwTimeout); | ||
300 | if (!::GetExitCodeProcess(oProcInfo.hProcess, &dwExitCode)) | ||
301 | { | ||
302 | dwExitCode = ERROR_SEM_IS_SET; | ||
303 | } | ||
304 | |||
305 | ReleaseFile(hOutRead); | ||
306 | ReleaseFile(hInWrite); | ||
307 | ReleaseFile(oProcInfo.hProcess); | ||
308 | } | ||
309 | else | ||
310 | { | ||
311 | ExitOnLastError(hr, "Command failed to execute."); | ||
312 | } | ||
313 | |||
314 | ExitOnWin32Error(dwExitCode, hr, "Command line returned an error."); | ||
315 | |||
316 | LExit: | ||
317 | return hr; | ||
318 | } | ||
319 | |||
320 | |||
321 | HRESULT WIXAPI QuietExec( | ||
322 | __inout_z LPWSTR wzCommand, | ||
323 | __in DWORD dwTimeout, | ||
324 | __in BOOL fLogCommand, | ||
325 | __in BOOL fLogOutput | ||
326 | ) | ||
327 | { | ||
328 | return QuietExecImpl(wzCommand, dwTimeout, fLogCommand, fLogOutput, NULL); | ||
329 | } | ||
330 | |||
331 | HRESULT WIXAPI QuietExecCapture( | ||
332 | __inout_z LPWSTR wzCommand, | ||
333 | __in DWORD dwTimeout, | ||
334 | __in BOOL fLogCommand, | ||
335 | __in BOOL fLogOutput, | ||
336 | __out_z_opt LPWSTR* psczOutput | ||
337 | ) | ||
338 | { | ||
339 | return QuietExecImpl(wzCommand, dwTimeout, fLogCommand, fLogOutput, psczOutput); | ||
340 | } | ||