pipe.cc

Go to the documentation of this file.
00001 
00003 
00004 //
00005 // Copyright (c) 2003-2004 California Institute of Technology
00006 // Copyright (c) 2004-2007 University of Southern California
00007 // Rob Peters <rjpeters at usc dot edu>
00008 //
00009 // created: Fri May  2 16:38:30 2003
00010 // commit: $Id: pipe.cc 10065 2007-04-12 05:54:56Z rjpeters $
00011 // $HeadURL: file:///lab/rjpeters/svnrepo/code/trunk/groovx/src/rutz/pipe.cc $
00012 //
00013 // --------------------------------------------------------------------
00014 //
00015 // This file is part of GroovX.
00016 //   [http://ilab.usc.edu/rjpeters/groovx/]
00017 //
00018 // GroovX is free software; you can redistribute it and/or modify it
00019 // under the terms of the GNU General Public License as published by
00020 // the Free Software Foundation; either version 2 of the License, or
00021 // (at your option) any later version.
00022 //
00023 // GroovX is distributed in the hope that it will be useful, but
00024 // WITHOUT ANY WARRANTY; without even the implied warranty of
00025 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00026 // General Public License for more details.
00027 //
00028 // You should have received a copy of the GNU General Public License
00029 // along with GroovX; if not, write to the Free Software Foundation,
00030 // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00031 //
00033 
00034 #ifndef GROOVX_RUTZ_PIPE_CC_UTC20050626084019_DEFINED
00035 #define GROOVX_RUTZ_PIPE_CC_UTC20050626084019_DEFINED
00036 
00037 #include "rutz/pipe.h"
00038 
00039 #include "rutz/error.h"
00040 #include "rutz/fstring.h"
00041 #include "rutz/sfmt.h"
00042 
00043 #include <cerrno>
00044 #include <cstdarg>
00045 #include <vector>
00046 
00047 #include "rutz/debug.h"
00048 GVX_DBG_REGISTER
00049 
00050 namespace
00051 {
00052   char** make_argv(const char* argv0, va_list a)
00053   {
00054     std::vector<const char*> args;
00055     std::vector<size_t> offsets;
00056     size_t totalchars = 0;
00057     for (const char* arg = argv0;
00058          arg != 0;
00059          arg = va_arg(a, char*))
00060       {
00061         args.push_back(arg);
00062         offsets.push_back(totalchars);
00063 
00064         totalchars += strlen(arg) + 1;
00065       }
00066 
00067     void* mem = malloc(totalchars + (args.size() + 1) * sizeof(char*));
00068 
00069     if (mem == 0)
00070       throw rutz::error("memory allocation failed", SRC_POS);
00071 
00072     char** const ptrs = static_cast<char**>(mem);
00073     char* const chars = static_cast<char*>(mem) + (args.size() + 1) * sizeof(char*);
00074 
00075     char* q = chars;
00076 
00077     for (uint i = 0; i < args.size(); ++i)
00078       {
00079         ptrs[i] = &chars[0] + offsets[i];
00080 
00081         for (const char* p = args[i]; *p != '\0'; ++p)
00082           *q++ = *p;
00083 
00084         *q++ = '\0';
00085       }
00086 
00087     ptrs[args.size()] = 0;
00088 
00089     return ptrs;
00090   }
00091 }
00092 
00093 rutz::shell_pipe::shell_pipe(const char* command, const char* mode) :
00094   m_file(popen(command, mode)),
00095   m_stream(m_file, std::ios::in|std::ios::out),
00096   m_exit_status(0)
00097 {}
00098 
00099 rutz::shell_pipe::~shell_pipe()
00100 { close(); }
00101 
00102 int rutz::shell_pipe::close()
00103 {
00104   if ( !is_closed() )
00105     {
00106       m_stream.close();
00107       m_exit_status = pclose(m_file);
00108       m_file = 0;
00109     }
00110   return m_exit_status;
00111 }
00112 
00113 rutz::pipe_fds::pipe_fds()
00114 {
00115   if (pipe(m_fds) != 0)
00116     throw rutz::error("couldn't create pipe", SRC_POS);
00117 }
00118 
00119 rutz::pipe_fds::~pipe_fds() throw()
00120 {
00121   close_reader();
00122   close_writer();
00123 }
00124 
00125 rutz::child_process::child_process() :
00126   m_child_status(0),
00127   m_pid(fork())
00128 {
00129   if (m_pid == -1)
00130     throw rutz::error("couldn't fork child process", SRC_POS);
00131 }
00132 
00133 rutz::child_process::~child_process() throw()
00134 {
00135   wait();
00136 }
00137 
00138 int rutz::child_process::wait() throw()
00139 {
00140   if (m_pid != 0)
00141     {
00142       waitpid(m_pid, &m_child_status, /*options*/ 0);
00143       m_pid = 0;
00144     }
00145 
00146   return m_child_status;
00147 }
00148 
00149 namespace
00150 {
00151   bool is_read_mode(const char* m)
00152   {
00153     if (m == 0) throw rutz::error("invalid read/write mode", SRC_POS);
00154     switch (m[0])
00155       {
00156       case 'r': return true;
00157       case 'w': return false;
00158       }
00159 
00160     throw rutz::error(rutz::sfmt("invalid read/write mode '%s'", m),
00161                       SRC_POS);
00162     return false; // "can't happen"
00163   }
00164 }
00165 
00166 void rutz::exec_pipe::init(char* const* argv)
00167 {
00168   if (m_child.in_parent())
00169     {
00170       if (m_parent_is_reader)
00171         {
00172           m_fds.close_writer();
00173 
00174           m_stream =
00175             new rutz::stdiostream(m_fds.reader(),
00176                                   std::ios::in|std::ios::binary);
00177         }
00178       else // parent is writer
00179         {
00180           m_fds.close_reader();
00181 
00182           m_stream =
00183             new rutz::stdiostream(m_fds.writer(),
00184                                   std::ios::out|std::ios::binary);
00185         }
00186 
00187       if (m_stream == 0)
00188         throw rutz::error("couldn't open stream in parent process", SRC_POS);
00189     }
00190   else // in child
00191     {
00192       if (m_parent_is_reader) // ==> child is writer
00193         {
00194           m_fds.close_reader();
00195 
00196           if (dup2(m_fds.writer(), STDOUT_FILENO) == -1)
00197             {
00198               fprintf(stderr, "dup2 failed in child process (%s):\n",
00199                       argv[0]);
00200               fprintf(stderr, "%s\n", strerror(errno));
00201               exit(-1);
00202             }
00203         }
00204       else // parent is writer, child is reader
00205         {
00206           m_fds.close_writer();
00207 
00208           if (dup2(m_fds.reader(), STDIN_FILENO) == -1)
00209             {
00210               fprintf(stderr, "dup2 failed in child process (%s):\n",
00211                       argv[0]);
00212               fprintf(stderr, "%s\n", strerror(errno));
00213               exit(-1);
00214             }
00215         }
00216 
00217       execv(argv[0], argv);
00218 
00219       fprintf(stderr, "execv failed in child process (%s)\n", argv[0]);
00220       fprintf(stderr, "%s\n", strerror(errno));
00221       exit(-1);
00222     }
00223 }
00224 
00225 rutz::exec_pipe::exec_pipe(const char* m, char* const* argv) :
00226   m_parent_is_reader(is_read_mode(m)),
00227   m_fds(),
00228   m_child(),
00229   m_stream(0)
00230 {
00231   this->init(argv);
00232 }
00233 
00234 rutz::exec_pipe::exec_pipe(const char* m, const char* argv0, ...) :
00235   m_parent_is_reader(is_read_mode(m)),
00236   m_fds(),
00237   m_child(),
00238   m_stream(0)
00239 {
00240   va_list a;
00241   va_start(a, argv0);
00242   char** argv = make_argv(argv0, a);
00243   va_end(a);
00244 
00245   try { this->init(argv); }
00246   catch (...) { free(argv); throw; }
00247 
00248   free(argv);
00249 }
00250 
00251 rutz::exec_pipe::~exec_pipe() throw()
00252 {
00253   delete m_stream;
00254 }
00255 
00256 std::iostream& rutz::exec_pipe::stream() throw()
00257 {
00258   GVX_ASSERT(m_stream != 0);
00259   return *m_stream;
00260 }
00261 
00262 void rutz::exec_pipe::close()
00263 {
00264   if (m_stream != 0)
00265     {
00266       m_stream->close();
00267       m_fds.close_reader();
00268       m_fds.close_writer();
00269     }
00270 }
00271 
00272 int rutz::exec_pipe::exit_status() throw()
00273 {
00274   const int child_status = m_child.wait();
00275 
00276   // Check if the child process exited abnormally
00277   if (WIFEXITED(child_status) == 0) return -1;
00278 
00279   // Check if the child process gave an error exit code
00280   if (WEXITSTATUS(child_status) != 0) return -1;
00281 
00282   // OK, everything looks fine
00283   return 0;
00284 }
00285 
00286 rutz::bidir_pipe::bidir_pipe() :
00287   m_in_pipe(),
00288   m_out_pipe(),
00289   m_child(),
00290   m_in_stream(0),
00291   m_out_stream(0),
00292   m_block_child_sigint(true)
00293 {
00294   // user must call init() before using the bidir_pipe's streams
00295 }
00296 
00297 rutz::bidir_pipe::bidir_pipe(char* const* argv) :
00298   m_in_pipe(),
00299   m_out_pipe(),
00300   m_child(),
00301   m_in_stream(0),
00302   m_out_stream(0),
00303   m_block_child_sigint(true)
00304 {
00305   this->init(argv);
00306 }
00307 
00308 rutz::bidir_pipe::bidir_pipe(const char* argv0, ...) :
00309   m_in_pipe(),
00310   m_out_pipe(),
00311   m_child(),
00312   m_in_stream(0),
00313   m_out_stream(0),
00314   m_block_child_sigint(true)
00315 {
00316   va_list a;
00317   va_start(a, argv0);
00318   char** argv = make_argv(argv0, a);
00319   va_end(a);
00320 
00321   try { this->init(argv); }
00322   catch (...) { free(argv); throw; }
00323 
00324   free(argv);
00325 }
00326 
00327 rutz::bidir_pipe::~bidir_pipe() throw()
00328 {
00329   close_in();
00330   close_out();
00331 
00332   delete m_out_stream;
00333   delete m_in_stream;
00334 }
00335 
00336 void rutz::bidir_pipe::block_child_sigint()
00337 {
00338   m_block_child_sigint = true;
00339 }
00340 
00341 void rutz::bidir_pipe::init(char* const* argv)
00342 {
00343   if (m_child.in_parent())
00344     {
00345       m_in_pipe.close_writer();
00346 
00347       m_in_stream =
00348         new rutz::stdiostream(m_in_pipe.reader(),
00349                               std::ios::in|std::ios::binary);
00350 
00351       if (m_in_stream == 0)
00352         throw rutz::error("couldn't open input stream in parent process",
00353                           SRC_POS);
00354 
00355       m_out_pipe.close_reader();
00356 
00357       m_out_stream =
00358         new rutz::stdiostream(m_out_pipe.writer(),
00359                               std::ios::out|std::ios::binary);
00360 
00361       if (m_out_stream == 0)
00362         throw rutz::error("couldn't open input stream in parent process",
00363                           SRC_POS);
00364     }
00365   else // in child
00366     {
00367       m_in_pipe.close_reader();
00368 
00369       if (dup2(m_in_pipe.writer(), STDOUT_FILENO) == -1)
00370         {
00371           fprintf(stderr, "dup2 failed in child process (%s):\n",
00372                   argv[0]);
00373           fprintf(stderr, "%s\n", strerror(errno));
00374           exit(-1);
00375         }
00376 
00377       m_out_pipe.close_writer();
00378 
00379       if (dup2(m_out_pipe.reader(), STDIN_FILENO) == -1)
00380         {
00381           fprintf(stderr, "dup2 failed in child process (%s):\n",
00382                   argv[0]);
00383           fprintf(stderr, "%s\n", strerror(errno));
00384           exit(-1);
00385         }
00386 
00387       if (m_block_child_sigint)
00388         signal(SIGINT, SIG_IGN);
00389 
00390       errno = 0;
00391 
00392       execv(argv[0], argv);
00393 
00394       fprintf(stderr, "execv failed in child process (%s):\n", argv[0]);
00395       fprintf(stderr, "%s\n", strerror(errno));
00396       exit(-1);
00397     }
00398 }
00399 
00400 void rutz::bidir_pipe::init(const char* argv0, ...)
00401 {
00402   va_list a;
00403   va_start(a, argv0);
00404   char** argv = make_argv(argv0, a);
00405   va_end(a);
00406 
00407   try { this->init(argv); }
00408   catch (...) { free(argv); throw; }
00409 
00410   free(argv);
00411 }
00412 
00413 std::iostream& rutz::bidir_pipe::in_stream() throw()
00414 {
00415   GVX_ASSERT(m_in_stream != 0);
00416   return *m_in_stream;
00417 }
00418 
00419 std::iostream& rutz::bidir_pipe::out_stream() throw()
00420 {
00421   GVX_ASSERT(m_out_stream != 0);
00422   return *m_out_stream;
00423 }
00424 
00425 void rutz::bidir_pipe::close_in()
00426 {
00427   if (m_in_stream != 0)
00428     {
00429       m_in_stream->close();
00430     }
00431 
00432   m_in_pipe.close_reader();
00433   m_in_pipe.close_writer();
00434 }
00435 
00436 void rutz::bidir_pipe::close_out()
00437 {
00438   if (m_out_stream != 0)
00439     {
00440       m_out_stream->close();
00441     }
00442 
00443   m_out_pipe.close_reader();
00444   m_out_pipe.close_writer();
00445 }
00446 
00447 int rutz::bidir_pipe::exit_status() throw()
00448 {
00449   const int child_status = m_child.wait();
00450 
00451   // Check if the child process exited abnormally
00452   if (WIFEXITED(child_status) == 0) return -1;
00453 
00454   // Check if the child process gave an error exit code
00455   if (WEXITSTATUS(child_status) != 0) return -1;
00456 
00457   // OK, everything looks fine
00458   return 0;
00459 }
00460 
00461 static const char __attribute__((used)) vcid_groovx_rutz_pipe_cc_utc20050626084019[] = "$Id: pipe.cc 10065 2007-04-12 05:54:56Z rjpeters $ $HeadURL: file:
00462 #endif // !GROOVX_RUTZ_PIPE_CC_UTC20050626084019_DEFINED

The software described here is Copyright (c) 1998-2005, Rob Peters.
This page was generated Wed Dec 3 06:49:40 2008 by Doxygen version 1.5.5.