aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil/pipeutil.cpp
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2024-01-10 23:53:18 -0800
committerRob Mensching <rob@firegiant.com>2024-01-11 18:24:06 -0800
commit4e7b7c0059d76498d1c24f348dbf6d5799203fe0 (patch)
tree1b00606cee7fd54ca4e07aa7b550fe63982220b3 /src/libs/dutil/WixToolset.DUtil/pipeutil.cpp
parent57b7689b2dc5e971911d1c6d48eebb5424ebca89 (diff)
downloadwix-4e7b7c0059d76498d1c24f348dbf6d5799203fe0.tar.gz
wix-4e7b7c0059d76498d1c24f348dbf6d5799203fe0.tar.bz2
wix-4e7b7c0059d76498d1c24f348dbf6d5799203fe0.zip
Add pipeutil to dutil
Plus a couple small clean-ups in a couple of dutil files.
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/pipeutil.cpp')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/pipeutil.cpp347
1 files changed, 347 insertions, 0 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/pipeutil.cpp b/src/libs/dutil/WixToolset.DUtil/pipeutil.cpp
new file mode 100644
index 00000000..4aa69d56
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/pipeutil.cpp
@@ -0,0 +1,347 @@
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
6static const DWORD PIPE_64KB = 64 * 1024;
7static const LPCWSTR PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls";
8
9
10// Exit macros
11#define PipeExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PIPEUTIL, x, s, __VA_ARGS__)
12#define PipeExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PIPEUTIL, x, s, __VA_ARGS__)
13#define PipeExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PIPEUTIL, x, s, __VA_ARGS__)
14#define PipeExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PIPEUTIL, x, s, __VA_ARGS__)
15#define PipeExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PIPEUTIL, x, s, __VA_ARGS__)
16#define PipeExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PIPEUTIL, x, s, __VA_ARGS__)
17#define PipeExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PIPEUTIL, p, x, e, s, __VA_ARGS__)
18#define PipeExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PIPEUTIL, p, x, s, __VA_ARGS__)
19#define PipeExitOnNullDebugTrace(p, x, e, s, ...) PipeExitOnNullDebugTraceSource(DUTIL_SOURCE_PIPEUTIL, p, x, e, s, __VA_ARGS__)
20#define PipeExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PIPEUTIL, p, x, s, __VA_ARGS__)
21#define PipeExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PIPEUTIL, e, x, s, __VA_ARGS__)
22#define PipeExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PIPEUTIL, g, x, s, __VA_ARGS__)
23
24
25static HRESULT AllocatePipeMessage(
26 __in DWORD dwMessageType,
27 __in_bcount_opt(cbData) LPVOID pvData,
28 __in SIZE_T cbData,
29 __out_bcount(cb) LPVOID* ppvMessage,
30 __out SIZE_T* pcbMessage
31);
32
33
34DAPI_(HRESULT) PipeClientConnect(
35 __in_z LPCWSTR wzPipeName,
36 __out HANDLE* phPipe
37)
38{
39 HRESULT hr = S_OK;
40 LPWSTR sczPipeName = NULL;
41 HANDLE hPipe = INVALID_HANDLE_VALUE;
42
43 // Try to connect to the parent.
44 hr = StrAllocFormatted(&sczPipeName, PIPE_NAME_FORMAT_STRING, wzPipeName);
45 PipeExitOnFailure(hr, "Failed to allocate name of pipe.");
46
47 hr = E_UNEXPECTED;
48 for (DWORD cRetry = 0; FAILED(hr) && cRetry < PIPE_RETRY_FOR_CONNECTION; ++cRetry)
49 {
50 hPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
51 if (INVALID_HANDLE_VALUE == hPipe)
52 {
53 hr = HRESULT_FROM_WIN32(::GetLastError());
54 if (E_FILENOTFOUND == hr) // if the pipe isn't created, call it a timeout waiting on the parent.
55 {
56 hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
57 }
58
59 ::Sleep(PIPE_WAIT_FOR_CONNECTION);
60 }
61 else // we have a connection, go with it.
62 {
63 hr = S_OK;
64 }
65 }
66 PipeExitOnRootFailure(hr, "Failed to open parent pipe: %ls", sczPipeName);
67
68 *phPipe = hPipe;
69 hPipe = INVALID_HANDLE_VALUE;
70
71LExit:
72 ReleaseFileHandle(hPipe);
73 return hr;
74}
75
76DAPI_(HRESULT) PipeCreate(
77 __in LPCWSTR wzName,
78 __in_opt LPSECURITY_ATTRIBUTES psa,
79 __out HANDLE* phPipe
80)
81{
82 HRESULT hr = S_OK;
83 LPWSTR sczFullPipeName = NULL;
84 HANDLE hPipe = INVALID_HANDLE_VALUE;
85
86 // Create the pipe.
87 hr = StrAllocFormatted(&sczFullPipeName, PIPE_NAME_FORMAT_STRING, wzName);
88 PipeExitOnFailure(hr, "Failed to allocate full name of pipe: %ls", wzName);
89
90 // TODO: consider using overlapped IO to do waits on the pipe and still be able to cancel and such.
91 hPipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, psa);
92 if (INVALID_HANDLE_VALUE == hPipe)
93 {
94 PipeExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName);
95 }
96
97 *phPipe = hPipe;
98 hPipe = INVALID_HANDLE_VALUE;
99
100LExit:
101 ReleaseFileHandle(hPipe);
102 ReleaseStr(sczFullPipeName);
103
104 return hr;
105}
106
107DAPI_(void) PipeFreeMessage(
108 __in PIPE_MESSAGE* pMsg
109)
110{
111 if (pMsg->fAllocatedData)
112 {
113 ReleaseNullMem(pMsg->pvData);
114 pMsg->fAllocatedData = FALSE;
115 }
116}
117
118
119DAPI_(HRESULT) PipeOpen(
120 __in_z LPCWSTR wzName,
121 __out HANDLE* phPipe
122)
123{
124 HRESULT hr = S_OK;
125 LPWSTR sczPipeName = NULL;
126 HANDLE hPipe = INVALID_HANDLE_VALUE;
127
128 // Try to connect to the parent.
129 hr = StrAllocFormatted(&sczPipeName, PIPE_NAME_FORMAT_STRING, wzName);
130 PipeExitOnFailure(hr, "Failed to allocate name of pipe.");
131
132 hr = E_UNEXPECTED;
133 for (DWORD cRetry = 0; FAILED(hr) && cRetry < PIPE_RETRY_FOR_CONNECTION; ++cRetry)
134 {
135 hPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
136 if (INVALID_HANDLE_VALUE == hPipe)
137 {
138 hr = HRESULT_FROM_WIN32(::GetLastError());
139 if (E_FILENOTFOUND == hr) // if the pipe isn't created, call it a timeout waiting on the parent.
140 {
141 hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
142 }
143
144 ::Sleep(PIPE_WAIT_FOR_CONNECTION);
145 }
146 else // we have a connection, go with it.
147 {
148 hr = S_OK;
149 }
150 }
151 PipeExitOnRootFailure(hr, "Failed to open parent pipe: %ls", sczPipeName);
152
153 *phPipe = hPipe;
154 hPipe = INVALID_HANDLE_VALUE;
155
156LExit:
157 ReleaseFileHandle(hPipe);
158 ReleaseStr(sczPipeName);
159
160 return hr;
161}
162
163DAPI_(HRESULT) PipeReadMessage(
164 __in HANDLE hPipe,
165 __in PIPE_MESSAGE* pMsg
166)
167{
168 HRESULT hr = S_OK;
169 BYTE pbMessageIdAndByteCount[sizeof(DWORD) + sizeof(DWORD)] = { };
170
171 hr = FileReadHandle(hPipe, pbMessageIdAndByteCount, sizeof(pbMessageIdAndByteCount));
172 if (HRESULT_FROM_WIN32(ERROR_BROKEN_PIPE) == hr)
173 {
174 memset(pbMessageIdAndByteCount, 0, sizeof(pbMessageIdAndByteCount));
175 hr = S_FALSE;
176 }
177 PipeExitOnFailure(hr, "Failed to read message from pipe.");
178
179 pMsg->dwMessageType = *(DWORD*)(pbMessageIdAndByteCount);
180 pMsg->cbData = *(DWORD*)(pbMessageIdAndByteCount + sizeof(DWORD));
181 if (pMsg->cbData)
182 {
183 pMsg->pvData = MemAlloc(pMsg->cbData, FALSE);
184 PipeExitOnNull(pMsg->pvData, hr, E_OUTOFMEMORY, "Failed to allocate data for message.");
185
186 hr = FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(pMsg->pvData), pMsg->cbData);
187 PipeExitOnFailure(hr, "Failed to read data for message.");
188
189 pMsg->fAllocatedData = TRUE;
190 }
191
192LExit:
193 if (!pMsg->fAllocatedData && pMsg->pvData)
194 {
195 MemFree(pMsg->pvData);
196 }
197
198 return hr;
199}
200
201DAPI_(HRESULT) PipeServerWaitForClientConnect(
202 __in HANDLE hPipe
203)
204{
205 HRESULT hr = S_OK;
206 DWORD dwPipeState = PIPE_READMODE_BYTE | PIPE_NOWAIT;
207
208 // Temporarily make the pipe non-blocking so we will not get stuck in ::ConnectNamedPipe() forever
209 // if the child decides not to show up.
210 if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL))
211 {
212 PipeExitWithLastError(hr, "Failed to set pipe to non-blocking.");
213 }
214
215 // Loop for a while waiting for a connection from child process.
216 DWORD cRetry = 0;
217 do
218 {
219 if (!::ConnectNamedPipe(hPipe, NULL))
220 {
221 DWORD er = ::GetLastError();
222 if (ERROR_PIPE_CONNECTED == er)
223 {
224 hr = S_OK;
225 break;
226 }
227 else if (ERROR_PIPE_LISTENING == er)
228 {
229 if (cRetry < PIPE_RETRY_FOR_CONNECTION)
230 {
231 hr = HRESULT_FROM_WIN32(er);
232 }
233 else
234 {
235 hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
236 break;
237 }
238
239 ++cRetry;
240 ::Sleep(PIPE_WAIT_FOR_CONNECTION);
241 }
242 else
243 {
244 hr = HRESULT_FROM_WIN32(er);
245 break;
246 }
247 }
248 } while (HRESULT_FROM_WIN32(ERROR_PIPE_LISTENING) == hr);
249 PipeExitOnRootFailure(hr, "Failed to wait for child to connect to pipe.");
250
251 // Put the pipe back in blocking mode.
252 dwPipeState = PIPE_READMODE_BYTE | PIPE_WAIT;
253 if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL))
254 {
255 PipeExitWithLastError(hr, "Failed to reset pipe to blocking.");
256 }
257
258LExit:
259 return hr;
260}
261
262DAPI_(HRESULT) PipeWriteMessage(
263 __in HANDLE hPipe,
264 __in DWORD dwMessageType,
265 __in_bcount_opt(cbData) LPVOID pvData,
266 __in SIZE_T cbData
267)
268{
269// HRESULT hr = S_OK;
270//
271// hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(&dwMessageType), sizeof(dwMessageType));
272// PipeExitOnFailure(hr, "Failed to write message id to pipe.");
273//
274// hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(&cbData), sizeof(cbData));
275// PipeExitOnFailure(hr, "Failed to write message data size to pipe.");
276//
277// if (pvData && cbData)
278// {
279// hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(pvData), cbData);
280// PipeExitOnFailure(hr, "Failed to write message data to pipe.");
281// }
282//
283//LExit:
284// return hr;
285 HRESULT hr = S_OK;
286 LPVOID pv = NULL;
287 SIZE_T cb = 0;
288
289 hr = AllocatePipeMessage(dwMessageType, pvData, cbData, &pv, &cb);
290 ExitOnFailure(hr, "Failed to allocate message to write.");
291
292 // Write the message.
293 hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(pv), cb);
294 ExitOnFailure(hr, "Failed to write message type to pipe.");
295
296LExit:
297 ReleaseMem(pv);
298 return hr;
299}
300
301static HRESULT AllocatePipeMessage(
302 __in DWORD dwMessageType,
303 __in_bcount_opt(cbData) LPVOID pvData,
304 __in SIZE_T cbData,
305 __out_bcount(cb) LPVOID* ppvMessage,
306 __out SIZE_T* pcbMessage
307)
308{
309 HRESULT hr = S_OK;
310 LPVOID pv = NULL;
311 size_t cb = 0;
312 DWORD dwcbData = 0;
313
314 // If no data was provided, ensure the count of bytes is zero.
315 if (!pvData)
316 {
317 cbData = 0;
318 }
319 else if (MAXDWORD < cbData)
320 {
321 ExitWithRootFailure(hr, E_INVALIDDATA, "Pipe message is too large.");
322 }
323
324 hr = ::SizeTAdd(sizeof(dwMessageType) + sizeof(dwcbData), cbData, &cb);
325 ExitOnRootFailure(hr, "Failed to calculate total pipe message size");
326
327 dwcbData = (DWORD)cbData;
328
329 // Allocate the message.
330 pv = MemAlloc(cb, FALSE);
331 ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for message.");
332
333 memcpy_s(pv, cb, &dwMessageType, sizeof(dwMessageType));
334 memcpy_s(static_cast<BYTE*>(pv) + sizeof(dwMessageType), cb - sizeof(dwMessageType), &dwcbData, sizeof(dwcbData));
335 if (dwcbData)
336 {
337 memcpy_s(static_cast<BYTE*>(pv) + sizeof(dwMessageType) + sizeof(dwcbData), cb - sizeof(dwMessageType) - sizeof(dwcbData), pvData, dwcbData);
338 }
339
340 *pcbMessage = cb;
341 *ppvMessage = pv;
342 pv = NULL;
343
344LExit:
345 ReleaseMem(pv);
346 return hr;
347}