00001 /** @file rutz/pipe.cc wrap posix inter-process pipes in a c++ 00002 iostreams interface */ 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 8249 2007-04-12 06:03:40Z rjpeters $ 00011 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/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 // 00032 /////////////////////////////////////////////////////////////////////// 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 8249 2007-04-12 06:03:40Z rjpeters $ $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/rutz/pipe.cc $"; 00462 #endif // !GROOVX_RUTZ_PIPE_CC_UTC20050626084019_DEFINED