aboutsummaryrefslogtreecommitdiff
path: root/src/libs/wcautil/WixToolset.WcaUtil/qtexec.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/wcautil/WixToolset.WcaUtil/qtexec.cpp')
-rw-r--r--src/libs/wcautil/WixToolset.WcaUtil/qtexec.cpp340
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
8static 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
86LExit:
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
98static 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
227LExit:
228 ReleaseMem(pBuffer);
229
230 ReleaseStr(szLog);
231 ReleaseStr(szTemp);
232 ReleaseStr(szWrite);
233 ReleaseStr(sczEscaped);
234
235 return hr;
236}
237
238static 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
316LExit:
317 return hr;
318}
319
320
321HRESULT 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
331HRESULT 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}