WvStreams
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, WvStringList *env)
00061 {
00062     setup(program, argv, writable, readable, catch_stderr,
00063           stdin_fd, stdout_fd, stderr_fd, env);
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, WvStringList *env)
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, env);
00080 }
00081 
00082 
00083 WvPipe::WvPipe(const char *program, const char **argv,
00084                bool writable, bool readable, bool catch_stderr,
00085                WvFDStream *stdio_str, WvStringList *env)
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, env);
00092     }
00093     else
00094         setup(program, argv, writable, readable, catch_stderr, 0, 1, 2, env);
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                    WvStringList *env)
00102 {
00103     int socks[2];
00104     int flags;
00105     int waitfd;
00106     int pid;
00107 
00108     if (!program || !argv)
00109     {
00110         seterr(EINVAL);
00111         return;
00112     }
00113 
00114     if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks))
00115     {
00116         seterr(errno);
00117         return;
00118     }
00119 
00120     fcntl(socks[0], F_SETFL, O_RDWR|O_NONBLOCK);
00121     setfd(socks[0]);
00122     
00123     if (env) 
00124     {
00125         WvStringList::Iter it(*env);
00126         for (it.rewind(); it.next(); ) 
00127         {
00128             proc.env.append(*it);
00129         }
00130     }
00131     pid = proc.fork(&waitfd);
00132 
00133     if (!pid)
00134     {
00135         // child process
00136         ::close(socks[0]);
00137 
00138         if (writable)
00139             dup2(socks[1], 0); // writable means redirect child stdin
00140         else if (stdin_fd == -1)
00141             ::close(0);
00142         else
00143             dup2(stdin_fd, 0);
00144         if (readable)
00145             dup2(socks[1], 1); // readable means we redirect child stdout
00146         else if (stdout_fd == -1)
00147             ::close(1);
00148         else
00149             dup2(stdout_fd, 1);
00150         if (catch_stderr)
00151             dup2(socks[1], 2); // but catch_stderr does what you think
00152         else if (stderr_fd == -1)
00153             ::close(2);
00154         else
00155             dup2(stderr_fd, 2);
00156 
00157         /* never close stdin/stdout/stderr */
00158         fcntl(0, F_SETFD, 0);
00159         fcntl(1, F_SETFD, 0);
00160         fcntl(2, F_SETFD, 0);
00161 
00162         /* drop the O_NONBLOCK from stdin/stdout/stderr, it confuses
00163          * some programs */
00164         flags = fcntl(0, F_GETFL);
00165         fcntl(0, F_SETFL, flags & ~O_NONBLOCK);
00166         flags = fcntl(1, F_GETFL);
00167         fcntl(1, F_SETFL, flags & ~O_NONBLOCK);
00168         flags = fcntl(2, F_GETFL);
00169         fcntl(2, F_SETFL, flags & ~O_NONBLOCK);
00170 
00171         /* If we're not capturing any of these through the socket, it
00172          * means that the child end of the socket will be closed right
00173          * at the execvp, which is bad. If we set the close-on-exec to
00174          * false, the child end of the socket will be closed when the
00175          * child (or sub-) process exits. */
00176         if (!writable && !readable && !catch_stderr)
00177             fcntl(socks[1], F_SETFD, 0);  // never close the socketpair
00178         else
00179 	    ::close(socks[1]); // has already been duplicated
00180         
00181         // this will often fail, but when it does work it is probably
00182         // the Right Thing To Do (tm)
00183         if (!readable && stdout_fd != 1)
00184         {
00185             setsid();
00186 // Only on some OSes will we find TIOCSCTTY to set the controlling tty.
00187 // On others, we need to use TCSETCTTY, but we are too lazy to implement that.
00188 #ifdef TIOCSCTTY
00189             ioctl(1, TIOCSCTTY, 1);
00190 #else
00191 # ifdef TCSETCTTY
00192 #  warning You should implement TCSETCTTY here.  Thanks!
00193 # endif
00194 #endif
00195         }
00196 
00197         ::close(waitfd);
00198         
00199         // now run the program.  If it fails, use _exit() so no destructors
00200         // get called and make a mess.
00201         execvp(program, (char * const *)argv);
00202         _exit(242);
00203     }
00204     else if (pid > 0)
00205     {
00206         // parent process.
00207         // now that we've forked, it's okay to close this fd if we fork again.
00208         fcntl(socks[0], F_SETFD, 1);
00209         ::close(socks[1]);
00210     }
00211     else
00212     {
00213         ::close(socks[0]);
00214         ::close(socks[1]);
00215         return;
00216     }
00217 }
00218 
00219 
00220 // send the child process a signal
00221 void WvPipe::kill(int signum)
00222 {
00223     if (proc.running)
00224         proc.kill(signum);
00225 }
00226 
00227 
00228 // wait for the child to die
00229 int WvPipe::finish(bool wait_children)
00230 {
00231     shutdown(getwfd(), SHUT_WR);
00232     close();
00233     while (proc.running)
00234         proc.wait(1000, wait_children);
00235     
00236     return proc.estatus;
00237 }
00238 
00239 
00240 bool WvPipe::child_exited()
00241 {
00242     /* FIXME: bug in WvSubProc? */
00243     proc.wait(0);
00244     proc.wait(0);
00245     return !proc.running;
00246 }
00247 
00248 
00249 // if child_exited(), return true if it died because of a signal, or
00250 // false if it died due to a call to exit().
00251 bool WvPipe::child_killed() const
00252 {
00253     int st = proc.estatus;
00254     assert (WIFEXITED(st) || WIFSIGNALED(st));
00255     return WIFSIGNALED(st);
00256 }
00257 
00258 
00259 // return the numeric exit status of the child (if it exited) or the
00260 // signal that killed the child (if it was killed).
00261 int WvPipe::exit_status()
00262 {
00263     /* FIXME: bug in WvSubProc? */
00264     proc.wait(0);
00265     proc.wait(0);
00266 
00267     int st = proc.estatus;
00268     assert (WIFEXITED(st) || WIFSIGNALED(st));
00269     if (child_killed())
00270         return WTERMSIG(st);
00271     else
00272         return WEXITSTATUS(st);
00273 }
00274 
00275 
00276 WvPipe::~WvPipe()
00277 {
00278     close();
00279 }
00280 
00281 
00282 // this is necessary when putting, say, sendmail through a WvPipe on the
00283 // globallist so we can forget about it.  We call nowrite() so that it'll
00284 // get the EOF and then go away when it's done, but we need to read from it
00285 // for it the WvPipe stop selecting true and get deleted.
00286 void WvPipe::ignore_read(WvStream& s)
00287 {
00288     char buf[512];
00289     s.read(&buf, sizeof(buf));
00290 }