Uncommented beaudio code
[pwlib.git] / src / ptlib / unix / pipechan.cxx
blobc766df5ac1211c76fb52035b50790436cce83c5d
1 /*
2 * pipechan.cxx
4 * Sub-process commuicating with pip I/O channel implementation
6 * Portable Windows Library
8 * Copyright (c) 1993-1998 Equivalence Pty. Ltd.
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/
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.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
25 * All Rights Reserved.
27 * Contributor(s): ______________________________________.
29 * $Log$
30 * Revision 1.42 2003/04/22 23:43:51 craigs
31 * MacOSX changes as per Hugo Santos
33 * Revision 1.41 2003/01/09 08:21:47 robertj
34 * Fixed possibly handle leak if fork() fails.
35 * Also added belt and braces checks for making sure no handle can leak in
36 * other unknown logic. Plus do handle high water mark logging.
38 * Revision 1.40 2003/01/08 01:33:52 craigs
39 * Fixed problem with not checking errno on return from waitpid
41 * Revision 1.39 2003/01/08 01:29:22 craigs
42 * More changes for return code to waitpid
44 * Revision 1.38 2002/12/18 01:12:09 craigs
45 * Remove erroneous WUNTRACED and added support for EINTR from waitpid
47 * Revision 1.37 2002/12/05 05:11:16 craigs
48 * Fixed IsRunning and WaitForTermination to provide the correct return
49 * codes from subprograms
51 * Revision 1.36 2002/12/02 03:57:18 robertj
52 * More RTEMS support patches, thank you Vladimir Nesic.
54 * Revision 1.35 2002/11/22 10:14:07 robertj
55 * QNX port, thanks Xiaodan Tang
57 * Revision 1.34 2002/10/17 13:44:27 robertj
58 * Port to RTEMS, thanks Vladimir Nesic.
60 * Revision 1.33 2002/10/10 04:43:44 robertj
61 * VxWorks port, thanks Martijn Roest
63 * Revision 1.32 2002/07/31 07:30:11 craigs
64 * WaitForTermination now returns exit code of program, as required
66 * Revision 1.31 2001/10/11 02:20:54 robertj
67 * Added IRIX support (no audio/video), thanks Andre Schulze.
69 * Revision 1.30 2001/08/16 11:58:22 rogerh
70 * Add more Mac OS X changes from John Woods <jfw@jfwhome.funhouse.com>
72 * Revision 1.29 2001/08/12 06:32:04 rogerh
73 * Add Mac OS Carbon changes from John Woods <jfw@jfwhome.funhouse.com>
75 * Revision 1.28 2001/06/30 06:59:07 yurik
76 * Jac Goudsmit from Be submit these changes 6/28. Implemented by Yuri Kiryanov
78 * Revision 1.27 2000/06/21 01:01:22 robertj
79 * AIX port, thanks Wolfgang Platzer (wolfgang.platzer@infonova.at).
81 * Revision 1.26 2000/04/09 18:19:23 rogerh
82 * Add my changes for NetBSD support.
84 * Revision 1.25 2000/04/06 12:11:32 rogerh
85 * MacOS X support submitted by Kevin Packard
87 * Revision 1.24 2000/03/08 12:17:09 rogerh
88 * Add OpenBSD support
90 * Revision 1.23 1999/06/28 09:28:02 robertj
91 * Portability issues, especially n BeOS (thanks Yuri!)
93 * Revision 1.22 1999/02/22 13:26:54 robertj
94 * BeOS port changes.
96 * Revision 1.21 1998/11/30 21:51:46 robertj
97 * New directory structure.
99 * Revision 1.20 1998/11/24 10:25:19 robertj
100 * Fixed environment variable on FreeBSD
102 * Revision 1.19 1998/11/24 09:39:11 robertj
103 * FreeBSD port.
105 * Revision 1.18 1998/11/06 01:06:05 robertj
106 * Solaris environment variable name.
108 * Revision 1.17 1998/11/05 09:42:01 robertj
109 * Fixed bug in direct stdout mode opening redirected stdout.
110 * Solaris support, missing environ declaration.
111 * Added assert for unsupported timeout in WaitForTermination() under solaris.
113 * Revision 1.16 1998/11/02 11:11:19 robertj
114 * Added pipe output to stdout/stderr.
116 * Revision 1.15 1998/11/02 10:30:40 robertj
117 * GNU v6 compatibility.
119 * Revision 1.14 1998/11/02 10:07:34 robertj
120 * Added ReadStandardError implementation
122 * Revision 1.13 1998/10/30 13:02:50 robertj
123 * New pipe channel enhancements.
125 * Revision 1.12 1998/10/26 11:09:56 robertj
126 * added separation of stdout and stderr.
128 * Revision 1.11 1998/09/24 04:12:14 robertj
129 * Added open software license.
133 #pragma implementation "pipechan.h"
135 #include <ptlib.h>
136 #include <ptlib/pipechan.h>
138 #include <unistd.h>
139 #include <sys/ioctl.h>
140 #include <sys/types.h>
141 #include <sys/wait.h>
142 #include <fcntl.h>
143 #include <signal.h>
145 #if defined(P_LINUX) || defined(P_SOLARIS)
146 #include <termio.h>
147 #endif
149 #if defined(P_MACOSX)
150 #include <crt_externs.h>
151 #endif
153 #include "../common/pipechan.cxx"
156 int PX_NewHandle(const char *, int);
159 ////////////////////////////////////////////////////////////////
161 // PPipeChannel
164 PPipeChannel::PPipeChannel()
166 toChildPipe[0] = toChildPipe[1] = -1;
167 fromChildPipe[0] = fromChildPipe[1] = -1;
168 stderrChildPipe[0] = stderrChildPipe[1] = -1;
172 BOOL PPipeChannel::PlatformOpen(const PString & subProgram,
173 const PStringArray & argumentList,
174 OpenMode mode,
175 BOOL searchPath,
176 BOOL stderrSeparate,
177 const PStringToString * environment)
179 #if defined(P_VXWORKS) || defined(P_RTEMS)
180 PAssertAlways("PPipeChannel::PlatformOpen");
181 return FALSE;
182 #else
183 subProgName = subProgram;
185 // setup the pipe to the child
186 if (mode == ReadOnly)
187 toChildPipe[0] = toChildPipe[1] = -1;
188 else {
189 PAssert(pipe(toChildPipe) == 0, POperatingSystemError);
190 PX_NewHandle("PPipeChannel toChildPipe", PMAX(toChildPipe[0], toChildPipe[1]));
193 // setup the pipe from the child
194 if (mode == WriteOnly || mode == ReadWriteStd)
195 fromChildPipe[0] = fromChildPipe[1] = -1;
196 else {
197 PAssert(pipe(fromChildPipe) == 0, POperatingSystemError);
198 PX_NewHandle("PPipeChannel fromChildPipe", PMAX(fromChildPipe[0], fromChildPipe[1]));
201 if (stderrSeparate)
202 PAssert(pipe(stderrChildPipe) == 0, POperatingSystemError);
203 else {
204 stderrChildPipe[0] = stderrChildPipe[1] = -1;
205 PX_NewHandle("PPipeChannel stderrChildPipe", PMAX(stderrChildPipe[0], stderrChildPipe[1]));
208 // fork to allow us to execute the child
209 #if defined(__BEOS__) || defined(P_IRIX)
210 childPid = fork();
211 #else
212 childPid = vfork();
213 #endif
214 if (childPid < 0)
215 return FALSE;
217 if (childPid > 0) {
218 // setup the pipe to the child
219 if (toChildPipe[0] != -1) {
220 ::close(toChildPipe[0]);
221 toChildPipe[0] = -1;
224 if (fromChildPipe[1] != -1) {
225 ::close(fromChildPipe[1]);
226 fromChildPipe[1] = -1;
229 if (stderrChildPipe[1] != -1) {
230 ::close(stderrChildPipe[1]);
231 stderrChildPipe[1] = -1;
234 os_handle = 0;
235 return TRUE;
238 // the following code is in the child process
240 // if we need to write to the child, make sure the child's stdin
241 // is redirected
242 if (toChildPipe[0] != -1) {
243 ::close(STDIN_FILENO);
244 ::dup(toChildPipe[0]);
245 ::close(toChildPipe[0]);
246 ::close(toChildPipe[1]);
247 } else {
248 int fd = open("/dev/null", O_RDONLY);
249 PAssertOS(fd >= 0);
250 ::close(STDIN_FILENO);
251 ::dup(fd);
252 ::close(fd);
255 // if we need to read from the child, make sure the child's stdout
256 // and stderr is redirected
257 if (fromChildPipe[1] != -1) {
258 ::close(STDOUT_FILENO);
259 ::dup(fromChildPipe[1]);
260 ::close(STDERR_FILENO);
261 if (!stderrSeparate)
262 ::dup(fromChildPipe[1]);
263 ::close(fromChildPipe[1]);
264 ::close(fromChildPipe[0]);
265 } else if (mode != ReadWriteStd) {
266 int fd = ::open("/dev/null", O_WRONLY);
267 PAssertOS(fd >= 0);
268 ::close(STDOUT_FILENO);
269 ::dup(fd);
270 ::close(STDERR_FILENO);
271 if (!stderrSeparate)
272 ::dup(fd);
273 ::close(fd);
276 if (stderrSeparate) {
277 ::dup(stderrChildPipe[1]);
278 ::close(stderrChildPipe[1]);
279 ::close(stderrChildPipe[0]);
282 // set the SIGINT and SIGQUIT to ignore so the child process doesn't
283 // inherit them from the parent
284 signal(SIGINT, SIG_IGN);
285 signal(SIGQUIT, SIG_IGN);
287 // and set ourselves as out own process group so we don't get signals
288 // from our parent's terminal (hopefully!)
289 PSETPGRP();
291 // setup the arguments, not as we are about to execl or exit, we don't
292 // care about memory leaks, they are not real!
293 char ** args = (char **)calloc(argumentList.GetSize()+2, sizeof(char *));
294 args[0] = strdup(subProgName.GetTitle());
295 PINDEX i;
296 for (i = 0; i < argumentList.GetSize(); i++)
297 args[i+1] = argumentList[i].GetPointer();
299 // Set up new environment if one specified.
300 if (environment != NULL) {
301 #if defined(P_SOLARIS) || defined(P_FREEBSD) || defined(P_OPENBSD) || defined (P_NETBSD) || defined(__BEOS__) || defined(P_MACOSX) || defined(P_MACOS) || defined (P_AIX) || defined(P_IRIX) || defined(P_QNX)
302 extern char ** environ;
303 # if defined(P_MACOSX)
304 # define environ (*_NSGetEnviron())
305 # endif
306 # define __environ environ
307 #endif
308 __environ = (char **)calloc(environment->GetSize()+1, sizeof(char*));
309 for (i = 0; i < environment->GetSize(); i++) {
310 PString str = environment->GetKeyAt(i) + '=' + environment->GetDataAt(i);
311 __environ[i] = strdup(str);
315 // execute the child as required
316 if (searchPath)
317 execvp(subProgram, args);
318 else
319 execv(subProgram, args);
321 exit(2);
322 return FALSE;
323 #endif // P_VXWORKS || P_RTEMS
327 BOOL PPipeChannel::Close()
329 // close pipe from child
330 if (fromChildPipe[0] != -1) {
331 ::close(fromChildPipe[0]);
332 fromChildPipe[0] = -1;
335 if (fromChildPipe[1] != -1) {
336 ::close(fromChildPipe[1]);
337 fromChildPipe[1] = -1;
340 // close pipe to child
341 if (toChildPipe[0] != -1) {
342 ::close(toChildPipe[0]);
343 toChildPipe[0] = -1;
346 if (toChildPipe[1] != -1) {
347 ::close(toChildPipe[1]);
348 toChildPipe[1] = -1;
351 // close pipe to child
352 if (stderrChildPipe[0] != -1) {
353 ::close(stderrChildPipe[0]);
354 stderrChildPipe[0] = -1;
357 if (stderrChildPipe[1] != -1) {
358 ::close(stderrChildPipe[1]);
359 stderrChildPipe[1] = -1;
362 // kill the child process
363 if (IsRunning()) {
364 kill (childPid, SIGKILL);
365 WaitForTermination();
368 // ensure this channel looks like it is closed
369 os_handle = -1;
370 childPid = 0;
372 return TRUE;
375 BOOL PPipeChannel::Read(void * buffer, PINDEX len)
377 PAssert(IsOpen(), "Attempt to read from closed pipe");
378 PAssert(fromChildPipe[0] != -1, "Attempt to read from write-only pipe");
380 os_handle = fromChildPipe[0];
381 BOOL status = PChannel::Read(buffer, len);
382 os_handle = 0;
383 return status;
386 BOOL PPipeChannel::Write(const void * buffer, PINDEX len)
388 PAssert(IsOpen(), "Attempt to write to closed pipe");
389 PAssert(toChildPipe[1] != -1, "Attempt to write to read-only pipe");
391 os_handle = toChildPipe[1];
392 BOOL status = PChannel::Write(buffer, len);
393 os_handle = 0;
394 return status;
397 BOOL PPipeChannel::Execute()
399 flush();
400 clear();
401 if (toChildPipe[1] != -1) {
402 ::close(toChildPipe[1]);
403 toChildPipe[1] = -1;
405 return TRUE;
409 PPipeChannel::~PPipeChannel()
411 Close();
414 int PPipeChannel::GetReturnCode() const
416 return retVal;
419 BOOL PPipeChannel::IsRunning() const
421 if (childPid == 0)
422 return FALSE;
424 #if defined(P_PTHREADS) || defined(P_MAC_MPTHREADS)
426 int err;
427 int status;
428 if ((err = waitpid(childPid, &status, WNOHANG)) == 0)
429 return TRUE;
431 if (err != childPid)
432 return FALSE;
434 PPipeChannel * thisW = (PPipeChannel *)this;
435 thisW->childPid = 0;
437 if (WIFEXITED(status)) {
438 thisW->retVal = WEXITSTATUS(status);
439 PTRACE(2, "PipeChannel\tChild exited with code " << retVal);
440 } else if (WIFSIGNALED(status)) {
441 PTRACE(2, "PipeChannel\tChild was signalled with " << WTERMSIG(status));
442 thisW->retVal = -1;
443 } else if (WIFSTOPPED(status)) {
444 PTRACE(2, "PipeChannel\tChild was stopped with " << WSTOPSIG(status));
445 thisW->retVal = -1;
446 } else {
447 PTRACE(2, "PipeChannel\tChild was stopped with unknown status" << status);
448 thisW->retVal = -1;
451 return FALSE;
453 #else
454 return kill(childPid, 0) == 0;
455 #endif
458 int PPipeChannel::WaitForTermination()
460 if (childPid == 0)
461 return retVal;
463 int err;
465 #if defined(P_PTHREADS) || defined(P_MAC_MPTHREADS)
466 int status;
467 do {
468 err = waitpid(childPid, &status, 0);
469 if (err == childPid) {
470 childPid = 0;
471 if (WIFEXITED(status)) {
472 retVal = WEXITSTATUS(status);
473 PTRACE(2, "PipeChannel\tChild exited with code " << retVal);
474 } else if (WIFSIGNALED(status)) {
475 PTRACE(2, "PipeChannel\tChild was signalled with " << WTERMSIG(status));
476 retVal = -1;
477 } else if (WIFSTOPPED(status)) {
478 PTRACE(2, "PipeChannel\tChild was stopped with " << WSTOPSIG(status));
479 retVal = -1;
480 } else {
481 PTRACE(2, "PipeChannel\tChild was stopped with unknown status" << status);
482 retVal = -1;
484 return retVal;
486 } while (errno == EINTR);
487 #else
488 if ((err = kill (childPid, 0)) == 0)
489 return retVal = PThread::Current()->PXBlockOnChildTerminate(childPid, PMaxTimeInterval);
490 #endif
492 ConvertOSError(err);
493 return -1;
496 int PPipeChannel::WaitForTermination(const PTimeInterval & timeout)
498 if (childPid == 0)
499 return retVal;
501 int err;
503 #if defined(P_PTHREADS) || defined(P_MAC_MPTHREADS)
504 PAssert(timeout == PMaxTimeInterval, PUnimplementedFunction);
505 int status;
506 do {
507 err = waitpid(childPid, &status, 0);
508 if (err == childPid) {
509 childPid = 0;
510 if (WIFEXITED(status)) {
511 retVal = WEXITSTATUS(status);
512 PTRACE(2, "PipeChannel\tChild exited with code " << retVal);
513 } else if (WIFSIGNALED(status)) {
514 PTRACE(2, "PipeChannel\tChild was signalled with " << WTERMSIG(status));
515 retVal = -1;
516 } else if (WIFSTOPPED(status)) {
517 PTRACE(2, "PipeChannel\tChild was stopped with " << WSTOPSIG(status));
518 retVal = -1;
519 } else {
520 PTRACE(2, "PipeChannel\tChild was stopped with unknown status" << status);
521 retVal = -1;
523 return retVal;
525 } while (errno == EINTR);
526 #else
527 if ((err = kill (childPid, 0)) == 0)
528 return retVal = PThread::Current()->PXBlockOnChildTerminate(childPid, timeout);
529 #endif
531 ConvertOSError(err);
532 return -1;
535 BOOL PPipeChannel::Kill(int killType)
537 return ConvertOSError(kill (childPid, killType));
540 BOOL PPipeChannel::CanReadAndWrite()
542 return TRUE;
546 BOOL PPipeChannel::ReadStandardError(PString & errors, BOOL wait)
548 PAssert(IsOpen(), "Attempt to read from closed pipe");
549 PAssert(stderrChildPipe[0] != -1, "Attempt to read from write-only pipe");
551 os_handle = stderrChildPipe[0];
553 BOOL status = FALSE;
554 #ifndef BE_BONELESS
555 int available;
556 if (ConvertOSError(ioctl(stderrChildPipe[0], FIONREAD, &available))) {
557 if (available != 0)
558 status = PChannel::Read(errors.GetPointer(available+1), available);
559 else if (wait) {
560 char firstByte;
561 status = PChannel::Read(&firstByte, 1);
562 if (status) {
563 errors = firstByte;
564 if (ConvertOSError(ioctl(stderrChildPipe[0], FIONREAD, &available))) {
565 if (available != 0)
566 status = PChannel::Read(errors.GetPointer(available+2)+1, available);
571 #endif
573 os_handle = 0;
574 return status;