Main Page   Class Hierarchy   Compound List   File List   Compound Members   File Members  

wvpipe.cc

Go to the documentation of this file.
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  * 
00005  * Implementation of a WvPipe stream.  WvPipes allow you to create a new
00006  * process, attaching its stdin/stdout to a WvStream.
00007  * 
00008  * See wvpipe.h for more information.
00009  */
00010 #include "wvpipe.h"
00011 #include "wvsplitstream.h"
00012 #include <fcntl.h>
00013 #include <sys/types.h>
00014 #include <sys/socket.h>
00015 #include <signal.h>
00016 #include <sys/wait.h>
00017 #include <errno.h>
00018 #include <sys/ioctl.h>
00019 #include <assert.h>
00020 
00021 
00022 // The assorted WvPipe::WvPipe() constructors are described in wvpipe.h
00023 
00024 WvPipe::WvPipe(const char *program, const char * const *argv,
00025                bool writable, bool readable, bool catch_stderr,
00026                int stdin_fd, int stdout_fd, int stderr_fd)
00027 {
00028     setup(program, argv, writable, readable, catch_stderr,
00029           stdin_fd, stdout_fd, stderr_fd);
00030 }
00031 
00032 
00033 WvPipe::WvPipe(const char *program, const char * const *argv,
00034                bool writable, bool readable, bool catch_stderr,
00035                WvStream *stdin_str, WvStream *stdout_str,
00036                WvStream *stderr_str)
00037 {
00038     int fd0 = 0, fd1 = 1, fd2 = 2;
00039     if (stdin_str)
00040         fd0 = stdin_str->getrfd();
00041     if (stdout_str)
00042         fd1 = stdout_str->getwfd();
00043     if (stderr_str)
00044         fd2 = stderr_str->getwfd();
00045     setup(program, argv, writable, readable, catch_stderr, fd0, fd1, fd2);
00046 }
00047 
00048 
00049 WvPipe::WvPipe(const char *program, const char **argv,
00050                bool writable, bool readable, bool catch_stderr,
00051                WvSplitStream *stdio_str)
00052 {
00053     if (stdio_str)
00054     {
00055         int rfd = stdio_str->getrfd(), wfd = stdio_str->getwfd();
00056         setup(program, argv, writable, readable, catch_stderr,
00057               rfd, wfd, wfd);
00058     }
00059     else
00060         setup(program, argv, writable, readable, catch_stderr, 0, 1, 2);
00061 }
00062 
00063 
00064 void WvPipe::setup(const char *program, const char * const *argv,
00065               bool writable, bool readable, bool catch_stderr,
00066               int stdin_fd, int stdout_fd, int stderr_fd)
00067 {
00068     int socks[2];
00069 
00070     pid = 0;
00071     estatus = -1;
00072 
00073     if (!program || !argv)
00074     {
00075         errnum = EINVAL;
00076         return;
00077     }
00078     
00079     if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks))
00080     {
00081         errnum = errno;
00082         return;
00083     }
00084 
00085     // Fork.  The child process will run the program we're piping.
00086     // we don't need wvfork() because the child runs exec().
00087     pid = fork();
00088     if (pid < 0)
00089     {
00090         pid = 0;
00091         errnum = errno;
00092         ::close(socks[0]);
00093         ::close(socks[1]);
00094         return;
00095     }
00096     
00097     if (!pid)   // child process
00098     {
00099         ::close(socks[0]);
00100         
00101         if (writable)
00102             dup2(socks[1], 0); // writable means redirect child stdin
00103         else if (stdin_fd == -1)
00104             ::close(0);
00105         else
00106             dup2(stdin_fd, 0);
00107         if (readable)
00108             dup2(socks[1], 1); // readable means we redirect child stdout
00109         else if (stdout_fd == -1)
00110             ::close(1);
00111         else
00112             dup2(stdout_fd, 1);
00113         if (catch_stderr)
00114             dup2(socks[1], 2); // but catch_stderr does what you think
00115         else if (stderr_fd == -1)
00116             ::close(2);
00117         else
00118             dup2(stderr_fd, 2);
00119         
00120         fcntl(0, F_SETFD, 0);  // never close stdin
00121         fcntl(1, F_SETFD, 0);  // never close stdout
00122         fcntl(2, F_SETFD, 0);  // never close stderr
00123 
00124         /* If we're not capturing any of these through the socket, it
00125          * means that the child end of the socket will be closed right
00126          * at the execvp, which is bad. If we set the close-on-exec to
00127          * false, the child end of the socket will be closed when the
00128          * child (or sub-) process exits. */
00129         if(!writable && !readable && !catch_stderr)
00130             fcntl(socks[1], F_SETFD, 0);  // never close the socketpair
00131         
00132         // this will often fail, but when it does work it is probably
00133         // the Right Thing To Do (tm)
00134         if (!readable && stdout_fd != 1)
00135         {
00136             setsid();
00137             ioctl(1, TIOCSCTTY, 1);
00138         }
00139         
00140         // now run the program.  If it fails, use _exit() so no destructors
00141         // get called and make a mess.
00142         execvp(program, (char * const *)argv);
00143         _exit(242);
00144     }
00145 
00146     // otherwise, parent process
00147 
00148     // set non-blocking
00149     fcntl(socks[0], F_SETFL, O_RDWR|O_NONBLOCK);
00150 
00151     rwfd = socks[0];
00152     ::close(socks[1]);
00153 }
00154 
00155 
00156 // send the child process a signal
00157 void WvPipe::kill(int signum)
00158 {
00159     if (pid)
00160         ::kill(pid, signum);
00161 }
00162 
00163 
00164 // wait for the child to die
00165 int WvPipe::finish()
00166 {
00167     while (!child_exited())
00168         usleep(100*1000);
00169     
00170     return exit_status();
00171 }
00172 
00173 
00174 // determine if the child process has exited.
00175 // Note:  if the child forks off, this does not necessarily mean that
00176 //    the stream is invalid!  (use isok() for that as usual)
00177 bool WvPipe::child_exited()
00178 {
00179     int status;
00180     pid_t dead_pid;
00181     
00182     if (!pid) return true;
00183     
00184     dead_pid = waitpid(pid, &status, WNOHANG);
00185     if (dead_pid != pid)
00186         return false;
00187     estatus = status;
00188     pid = 0;
00189     return true;
00190 }
00191 
00192 
00193 // if child_exited(), return true if it died because of a signal, or
00194 // false if it died due to a call to exit().
00195 bool WvPipe::child_killed() const
00196 {
00197     int st = estatus;
00198     assert (WIFEXITED(st) || WIFSIGNALED(st));
00199     return WIFSIGNALED(st);
00200 }
00201 
00202 
00203 // return the numeric exit status of the child (if it exited) or the
00204 // signal that killed the child (if it was killed).
00205 int WvPipe::exit_status() const
00206 {
00207     int st = estatus;
00208     assert (WIFEXITED(st) || WIFSIGNALED(st));
00209     if (child_killed())
00210         return WTERMSIG(st);
00211     else
00212         return WEXITSTATUS(st);
00213 }
00214 
00215 
00216 // make sure our subtask ends up dead!
00217 WvPipe::~WvPipe()
00218 {
00219     int status, count;
00220     pid_t dead_pid;
00221     
00222     close();
00223     
00224     if (!pid) return;
00225 
00226     dead_pid = waitpid(pid, &status, WNOHANG);
00227     if (dead_pid == 0)
00228     {
00229         kill(SIGTERM);
00230         
00231         for (count = 20; count > 0; count--)
00232         {
00233             dead_pid = waitpid(pid, &status, WNOHANG);
00234             if (dead_pid == pid)
00235                 break;
00236             usleep(100 * 1000);
00237         }
00238         
00239         if (dead_pid == 0)
00240         {
00241             kill(SIGKILL);
00242             waitpid(pid, &status, 0);
00243         }
00244     }
00245 }

Generated on Sun Mar 16 01:01:11 2003 for WvStreams by doxygen1.3-rc3