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