00001
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
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, 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;
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
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
00191 {
00192 if (m_parent_is_reader)
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
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
00277 if (WIFEXITED(child_status) == 0) return -1;
00278
00279
00280 if (WEXITSTATUS(child_status) != 0) return -1;
00281
00282
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
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
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
00452 if (WIFEXITED(child_status) == 0) return -1;
00453
00454
00455 if (WEXITSTATUS(child_status) != 0) return -1;
00456
00457
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