1 /*
2 * pipe.cxx
3 *
4 * Sub-process communicating with pipe I/O channel class
5 *
6 * Portable Windows Library
7 *
8 * Copyright (c) 1993-1998 Equivalence Pty. Ltd.
9 *
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
14 *
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * The Original Code is Portable Windows Library.
21 *
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
23 *
24 * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
25 * All Rights Reserved.
26 *
27 * Contributor(s): ______________________________________.
28 *
29 * $Revision: 24756 $
30 * $Author: rjongbloed $
31 * $Date: 2010-09-28 03:40:17 -0500 (Tue, 28 Sep 2010) $
32 */
33
34 #include <ptlib.h>
35 #include <ptlib/pipechan.h>
36
37 #include <ctype.h>
38
39 ///////////////////////////////////////////////////////////////////////////////
40 // PPipeChannel
41
PPipeChannel()42 PPipeChannel::PPipeChannel()
43 {
44 hToChild = hFromChild = hStandardError = INVALID_HANDLE_VALUE;
45 }
46
47
48 #ifdef _WIN32_WCE
PlatformOpen(const PString &,const PStringArray &,OpenMode,PBoolean,PBoolean,const PStringToString *)49 PBoolean PPipeChannel::PlatformOpen(const PString &, const PStringArray &, OpenMode, PBoolean, PBoolean, const PStringToString *)
50 {
51 return PFalse;
52 }
53 #else
PlatformOpen(const PString & subProgram,const PStringArray & argumentList,OpenMode mode,PBoolean searchPath,PBoolean stderrSeparate,const PStringToString * environment)54 PBoolean PPipeChannel::PlatformOpen(const PString & subProgram,
55 const PStringArray & argumentList,
56 OpenMode mode,
57 PBoolean searchPath,
58 PBoolean stderrSeparate,
59 const PStringToString * environment)
60 {
61 subProgName = subProgram;
62
63 const char * prog = NULL;
64 PStringStream cmdLine;
65 if (searchPath)
66 cmdLine << subProgram;
67 else
68 prog = subProgram;
69
70 for (PINDEX i = 0; i < argumentList.GetSize(); i++) {
71 cmdLine << ' ';
72 if (argumentList[i].Find(' ') == P_MAX_INDEX)
73 cmdLine << argumentList[i];
74 else if (argumentList[i].Find('"') == P_MAX_INDEX)
75 cmdLine << '"' << argumentList[i] << '"';
76 else
77 cmdLine << '\'' << argumentList[i] << '\'';
78 }
79
80 PCharArray envBuf;
81 char * envStr = NULL;
82 if (environment != NULL) {
83 PINDEX size = 0;
84 for (PINDEX e = 0; e < environment->GetSize(); e++) {
85 PString str = environment->GetKeyAt(e) + '=' + environment->GetDataAt(e);
86 PINDEX len = str.GetLength() + 1;
87 envBuf.SetSize(size + len);
88 memcpy(envBuf.GetPointer()+size, (const char *)str, len);
89 size += len;
90 }
91 envStr = envBuf.GetPointer();
92 }
93
94 //
95 // this code comes from http://msdn.microsoft.com/en-us/library/ms682499(VS.85).aspx
96 //
97 STARTUPINFO startup;
98 memset(&startup, 0, sizeof(startup));
99 startup.cb = sizeof(startup);
100 startup.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
101 startup.wShowWindow = SW_HIDE;
102 startup.hStdInput = INVALID_HANDLE_VALUE;
103 startup.hStdOutput = INVALID_HANDLE_VALUE;
104 startup.hStdError = INVALID_HANDLE_VALUE;
105
106 SECURITY_ATTRIBUTES security;
107 security.nLength = sizeof(security);
108 security.lpSecurityDescriptor = NULL;
109 security.bInheritHandle = TRUE;
110
111 // ReadOnly means child has no stdin
112 // otherwise create a pipe for us to send data to child
113 if (mode == ReadOnly)
114 hToChild = INVALID_HANDLE_VALUE;
115 else {
116 HANDLE writeEnd;
117 PAssertOS(CreatePipe(&startup.hStdInput, &writeEnd, &security, 0));
118 PAssertOS(SetHandleInformation(writeEnd, HANDLE_FLAG_INHERIT, 0));
119 PAssertOS(DuplicateHandle(GetCurrentProcess(), writeEnd,
120 GetCurrentProcess(), &hToChild, 0, PFalse,
121 DUPLICATE_CLOSE_SOURCE|DUPLICATE_SAME_ACCESS));
122 }
123
124 // WriteOnly means child has no stdout
125 // ReadWriteStd means child uses our stdout and stderr
126 // otherwise, create a pipe to read stdout from child, and perhaps a seperate one for stderr too
127 if (mode == WriteOnly)
128 hFromChild = INVALID_HANDLE_VALUE;
129 else if (mode == ReadWriteStd) {
130 hFromChild = INVALID_HANDLE_VALUE;
131 startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
132 startup.hStdError = GetStdHandle(STD_ERROR_HANDLE);
133 }
134 else {
135 PAssertOS(CreatePipe(&hFromChild, &startup.hStdOutput, &security, 1));
136 PAssertOS(SetHandleInformation(hFromChild, HANDLE_FLAG_INHERIT, 0));
137 if (stderrSeparate) {
138 PAssertOS(CreatePipe(&hStandardError, &startup.hStdError, &security, 1));
139 PAssertOS(SetHandleInformation(hStandardError, HANDLE_FLAG_INHERIT, 0));
140 }
141 else {
142 startup.hStdError = startup.hStdOutput;
143 hStandardError = INVALID_HANDLE_VALUE;
144 }
145 }
146
147 if (ConvertOSError(CreateProcess(prog, cmdLine.GetPointer(),
148 NULL, NULL, PTrue, 0, envStr,
149 NULL, &startup, &info) ? 0 : -2))
150 os_handle = info.dwProcessId;
151 else {
152 if (hToChild != INVALID_HANDLE_VALUE)
153 CloseHandle(hToChild);
154 if (hFromChild != INVALID_HANDLE_VALUE)
155 CloseHandle(hFromChild);
156 if (hStandardError != INVALID_HANDLE_VALUE)
157 CloseHandle(hStandardError);
158 }
159
160 if (startup.hStdInput != INVALID_HANDLE_VALUE)
161 CloseHandle(startup.hStdInput);
162
163 if (mode != ReadWriteStd) {
164 if (startup.hStdOutput != INVALID_HANDLE_VALUE)
165 CloseHandle(startup.hStdOutput);
166 if (startup.hStdOutput != startup.hStdError)
167 CloseHandle(startup.hStdError);
168 }
169
170 return IsOpen();
171 }
172 #endif // !_WIN32_WCE
173
174
175
~PPipeChannel()176 PPipeChannel::~PPipeChannel()
177 {
178 Close();
179 }
180
181
IsOpen() const182 PBoolean PPipeChannel::IsOpen() const
183 {
184 return os_handle != -1;
185 }
186
187
GetReturnCode() const188 int PPipeChannel::GetReturnCode() const
189 {
190 DWORD code;
191 if (GetExitCodeProcess(info.hProcess, &code))
192 return code != STILL_ACTIVE ? code : -2;
193
194 ((PPipeChannel*)this)->ConvertOSError(-2);
195 return -1;
196 }
197
198
CanReadAndWrite()199 PBoolean PPipeChannel::CanReadAndWrite()
200 {
201 return PTrue;
202 }
203
IsRunning() const204 PBoolean PPipeChannel::IsRunning() const
205 {
206 return GetReturnCode() == -2;
207 }
208
209
WaitForTermination()210 int PPipeChannel::WaitForTermination()
211 {
212 if (WaitForSingleObject(info.hProcess, INFINITE) == WAIT_OBJECT_0)
213 return GetReturnCode();
214
215 ConvertOSError(-2);
216 return -1;
217 }
218
219
WaitForTermination(const PTimeInterval & timeout)220 int PPipeChannel::WaitForTermination(const PTimeInterval & timeout)
221 {
222 if (WaitForSingleObject(info.hProcess, timeout.GetInterval()) == WAIT_OBJECT_0)
223 return GetReturnCode();
224
225 ConvertOSError(-2);
226 return -1;
227 }
228
229
Kill(int signal)230 PBoolean PPipeChannel::Kill(int signal)
231 {
232 return ConvertOSError(TerminateProcess(info.hProcess, signal) ? 0 : -2);
233 }
234
235
Read(void * buffer,PINDEX len)236 PBoolean PPipeChannel::Read(void * buffer, PINDEX len)
237 {
238 lastReadCount = 0;
239
240 DWORD count = 0;
241
242 #ifndef _WIN32_WCE
243 // Cannot use overlapped I/O with anonymous pipe.
244 // So have all this hideous code. :-(
245
246 if (readTimeout == PMaxTimeInterval) {
247 if (!ConvertOSError(ReadFile(hFromChild, buffer, 1, &count, NULL) ? 0 : -2, LastReadError))
248 return false;
249
250 lastReadCount = 1;
251 if (len == 1)
252 return true;
253
254 if (!PeekNamedPipe(hFromChild, NULL, 0, NULL, &count, NULL))
255 return ConvertOSError(-2, LastReadError);
256
257 if (count == 0)
258 return true;
259
260 ++((BYTE * &)buffer);
261 --len;
262 }
263 else {
264 PSimpleTimer timeout(readTimeout);
265 for (;;) {
266 if (!PeekNamedPipe(hFromChild, NULL, 0, NULL, &count, NULL))
267 return ConvertOSError(-2, LastReadError);
268
269 if (count > 0)
270 break;
271
272 if (timeout.HasExpired()) {
273 SetErrorValues(Timeout, EAGAIN, LastReadError);
274 return false;
275 }
276
277 Sleep(10);
278 }
279 }
280
281 if (len > (PINDEX)count)
282 len = count;
283 #endif
284
285 if (!ConvertOSError(ReadFile(hFromChild, buffer, len, &count, NULL) ? 0 : -2, LastReadError))
286 return false;
287
288 lastReadCount += count;
289 return lastReadCount > 0;
290 }
291
292
Write(const void * buffer,PINDEX len)293 PBoolean PPipeChannel::Write(const void * buffer, PINDEX len)
294 {
295 lastWriteCount = 0;
296 DWORD count;
297 if (!ConvertOSError(WriteFile(hToChild, buffer, len, &count, NULL) ? 0 : -2, LastWriteError))
298 return PFalse;
299 lastWriteCount = count;
300 return lastWriteCount >= len;
301 }
302
303
Close()304 PBoolean PPipeChannel::Close()
305 {
306 if (IsOpen()) {
307 os_handle = -1;
308 if (hToChild != INVALID_HANDLE_VALUE)
309 CloseHandle(hToChild);
310 if (hFromChild != INVALID_HANDLE_VALUE)
311 CloseHandle(hFromChild);
312 if (hStandardError != INVALID_HANDLE_VALUE)
313 CloseHandle(hStandardError);
314 if (!TerminateProcess(info.hProcess, 1))
315 return PFalse;
316 }
317 return PTrue;
318 }
319
320
Execute()321 PBoolean PPipeChannel::Execute()
322 {
323 flush();
324 clear();
325 if (hToChild != INVALID_HANDLE_VALUE)
326 CloseHandle(hToChild);
327 hToChild = INVALID_HANDLE_VALUE;
328 return IsRunning();
329 }
330
331
332 #ifdef _WIN32_WCE
ReadStandardError(PString &,PBoolean)333 PBoolean PPipeChannel::ReadStandardError(PString &, PBoolean)
334 {
335 return PFalse;
336 }
337 #else
ReadStandardError(PString & errors,PBoolean wait)338 PBoolean PPipeChannel::ReadStandardError(PString & errors, PBoolean wait)
339 {
340 DWORD available, bytesRead;
341 if (!PeekNamedPipe(hStandardError, NULL, 0, NULL, &available, NULL))
342 return ConvertOSError(-2, LastReadError);
343
344 if (available != 0)
345 return ConvertOSError(ReadFile(hStandardError,
346 errors.GetPointer(available+1), available,
347 &bytesRead, NULL) ? 0 : -2, LastReadError);
348
349 if (!wait)
350 return PFalse;
351
352 char firstByte;
353 if (!ReadFile(hStandardError, &firstByte, 1, &bytesRead, NULL))
354 return ConvertOSError(-2, LastReadError);
355
356 errors = firstByte;
357
358 if (!PeekNamedPipe(hStandardError, NULL, 0, NULL, &available, NULL))
359 return ConvertOSError(-2, LastReadError);
360
361 if (available == 0)
362 return PTrue;
363
364 return ConvertOSError(ReadFile(hStandardError,
365 errors.GetPointer(available+2)+1, available,
366 &bytesRead, NULL) ? 0 : -2, LastReadError);
367 }
368 #endif // !_WIN32_WCE
369
370
371 // End Of File ///////////////////////////////////////////////////////////////
372