wvpipe.cc

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 <fcntl.h>
00011 #include <sys/types.h>
00012 #include <sys/socket.h>
00013 #include <signal.h>
00014 #include <sys/wait.h>
00015 #include <errno.h>
00016 #include <sys/ioctl.h>
00017 #include <assert.h>
00018 #include "wvpipe.h"
00019 
00020 // this code is pretty handy for debugging, since 'netstat -nap' can't tell
00021 // you the endpoints of a socketpair(), but it can tell you the name of a
00022 // "real" Unix domain socket.
00023 #if 0
00024 #include "wvaddr.h"
00025 static int socketpair(int d, int type, int protocol, int sv[2])
00026 {
00027     static int counter = 10;
00028     
00029     int f1 = socket(PF_UNIX, SOCK_STREAM, protocol);
00030     int f2 = socket(PF_UNIX, SOCK_STREAM, protocol);
00031     
00032     WvString s("/tmp/sock%s", ++counter);
00033     WvString s2("/tmp/sock%sb", counter);
00034     WvUnixAddr a(s), a2(s2);
00035     
00036     unlink(s);
00037     unlink(s2);
00038     
00039     bind(f1, a.sockaddr(), a.sockaddr_len());
00040     bind(f2, a2.sockaddr(), a2.sockaddr_len());
00041     listen(f1, 10);
00042     connect(f2, a.sockaddr(), a.sockaddr_len());
00043     
00044     socklen_t ll = a.sockaddr_len();
00045     int f3 = accept(f1, a.sockaddr(), &ll);
00046     close(f1);
00047     
00048     sv[0] = f3;
00049     sv[1] = f2;
00050     
00051     return 0;
00052 }
00053 #endif
00054 
00055 
00056 // The assorted WvPipe::WvPipe() constructors are described in wvpipe.h
00057 
00058 WvPipe::WvPipe(const char *program, const char * const *argv,
00059                bool writable, bool readable, bool catch_stderr,
00060                int stdin_fd, int stdout_fd, int stderr_fd)
00061 {
00062     setup(program, argv, writable, readable, catch_stderr,
00063           stdin_fd, stdout_fd, stderr_fd);
00064 }
00065 
00066 
00067 WvPipe::WvPipe(const char *program, const char * const *argv,
00068                bool writable, bool readable, bool catch_stderr,
00069                WvFDStream *stdin_str, WvFDStream *stdout_str,
00070                WvFDStream *stderr_str)
00071 {
00072     int fd0 = 0, fd1 = 1, fd2 = 2;
00073     if (stdin_str)
00074         fd0 = stdin_str->getrfd();
00075     if (stdout_str)
00076         fd1 = stdout_str->getwfd();
00077     if (stderr_str)
00078         fd2 = stderr_str->getwfd();
00079     setup(program, argv, writable, readable, catch_stderr, fd0, fd1, fd2);
00080 }
00081 
00082 
00083 WvPipe::WvPipe(const char *program, const char **argv,
00084                bool writable, bool readable, bool catch_stderr,
00085                WvFDStream *stdio_str)
00086 {
00087     if (stdio_str)
00088     {
00089         int rfd = stdio_str->getrfd(), wfd = stdio_str->getwfd();
00090         setup(program, argv, writable, readable, catch_stderr,
00091               rfd, wfd, wfd);
00092     }
00093     else
00094         setup(program, argv, writable, readable, catch_stderr, 0, 1, 2);
00095 }
00096 
00097 
00098 void WvPipe::setup(const char *program, const char * const *argv,
00099               bool writable, bool readable, bool catch_stderr,
00100               int stdin_fd, int stdout_fd, int stderr_fd)
00101 {
00102     int socks[2];
00103     int flags;
00104     int waitfd;
00105     int pid;
00106 
00107     if (!program || !argv)
00108     {
00109         seterr(EINVAL);
00110         return;
00111     }
00112 
00113     if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks))
00114     {
00115         seterr(errno);
00116         return;
00117     }
00118 
00119     fcntl(socks[0], F_SETFL, O_RDWR|O_NONBLOCK);
00120     setfd(socks[0]);
00121 
00122     pid = proc.fork(&waitfd);
00123 
00124     if (!pid)
00125     {
00126         // child process
00127 	::close(socks[0]);
00128 
00129         if (writable)
00130             dup2(socks[1], 0); // writable means redirect child stdin
00131         else if (stdin_fd == -1)
00132 	    ::close(0);
00133         else
00134             dup2(stdin_fd, 0);
00135         if (readable)
00136             dup2(socks[1], 1); // readable means we redirect child stdout
00137         else if (stdout_fd == -1)
00138 	    ::close(1);
00139         else
00140             dup2(stdout_fd, 1);
00141         if (catch_stderr)
00142             dup2(socks[1], 2); // but catch_stderr does what you think
00143         else if (stderr_fd == -1)
00144 	    ::close(2);
00145         else
00146             dup2(stderr_fd, 2);
00147 
00148         /* never close stdin/stdout/stderr */
00149         fcntl(0, F_SETFD, 0);
00150         fcntl(1, F_SETFD, 0);
00151         fcntl(2, F_SETFD, 0);
00152 
00153         /* drop the O_NONBLOCK from stdin/stdout/stderr, it confuses
00154          * some programs */
00155         flags = fcntl(0, F_GETFL);
00156         fcntl(0, F_SETFL, flags & ~O_NONBLOCK);
00157         flags = fcntl(1, F_GETFL);
00158         fcntl(1, F_SETFL, flags & ~O_NONBLOCK);
00159         flags = fcntl(2, F_GETFL);
00160         fcntl(2, F_SETFL, flags & ~O_NONBLOCK);
00161 
00162         /* If we're not capturing any of these through the socket, it
00163          * means that the child end of the socket will be closed right
00164          * at the execvp, which is bad. If we set the close-on-exec to
00165          * false, the child end of the socket will be closed when the
00166          * child (or sub-) process exits. */
00167         if (!writable && !readable && !catch_stderr)
00168             fcntl(socks[1], F_SETFD, 0);  // never close the socketpair
00169         else
00170             ::close(socks[1]); // has already been duplicated
00171         
00172         // this will often fail, but when it does work it is probably
00173         // the Right Thing To Do (tm)
00174         if (!readable && stdout_fd != 1)
00175         {
00176             setsid();
00177 // Only on some OSes will we find TIOCSCTTY to set the controlling tty.
00178 // On others, we need to use TCSETCTTY, but we are too lazy to implement that.
00179 #ifdef TIOCSCTTY
00180             ioctl(1, TIOCSCTTY, 1);
00181 #else
00182 # ifdef TCSETCTTY
00183 #  warning You should implement TCSETCTTY here.  Thanks!
00184 # endif
00185 #endif
00186         }
00187 
00188 	::close(waitfd);
00189         
00190         // now run the program.  If it fails, use _exit() so no destructors
00191         // get called and make a mess.
00192         execvp(program, (char * const *)argv);
00193         _exit(242);
00194     }
00195     else if (pid > 0)
00196     {
00197         // parent process.
00198         // now that we've forked, it's okay to close this fd if we fork again.
00199         fcntl(socks[0], F_SETFD, 1);
00200 	::close(socks[1]);
00201     }
00202     else
00203     {
00204 	::close(socks[0]);
00205 	::close(socks[1]);
00206         return;
00207     }
00208 }
00209 
00210 
00211 // send the child process a signal
00212 void WvPipe::kill(int signum)
00213 {
00214     if (proc.running)
00215         proc.kill(signum);
00216 }
00217 
00218 
00219 // wait for the child to die
00220 int WvPipe::finish(bool wait_children)
00221 {
00222     shutdown(getwfd(), SHUT_WR);
00223     close();
00224     while (proc.running)
00225         proc.wait(1000, wait_children);
00226     
00227     return proc.estatus;
00228 }
00229 
00230 
00231 bool WvPipe::child_exited()
00232 {
00233     /* FIXME: bug in WvSubProc? */
00234     proc.wait(0);
00235     proc.wait(0);
00236     return !proc.running;
00237 }
00238 
00239 
00240 // if child_exited(), return true if it died because of a signal, or
00241 // false if it died due to a call to exit().
00242 bool WvPipe::child_killed() const
00243 {
00244     int st = proc.estatus;
00245     assert (WIFEXITED(st) || WIFSIGNALED(st));
00246     return WIFSIGNALED(st);
00247 }
00248 
00249 
00250 // return the numeric exit status of the child (if it exited) or the
00251 // signal that killed the child (if it was killed).
00252 int WvPipe::exit_status()
00253 {
00254     /* FIXME: bug in WvSubProc? */
00255     proc.wait(0);
00256     proc.wait(0);
00257 
00258     int st = proc.estatus;
00259     assert (WIFEXITED(st) || WIFSIGNALED(st));
00260     if (child_killed())
00261         return WTERMSIG(st);
00262     else
00263         return WEXITSTATUS(st);
00264 }
00265 
00266 
00267 WvPipe::~WvPipe()
00268 {
00269     close();
00270 }
00271 
00272 
00273 // this is necessary when putting, say, sendmail through a WvPipe on the
00274 // globallist so we can forget about it.  We call nowrite() so that it'll
00275 // get the EOF and then go away when it's done, but we need to read from it
00276 // for it the WvPipe stop selecting true and get deleted.
00277 void WvPipe::ignore_read(WvStream& s, void *userdata)
00278 {
00279     char c;
00280     s.read(&c, 1);
00281 }

Generated on Fri Oct 5 18:20:28 2007 for WvStreams by  doxygen 1.5.3