exec.cc

Go to the documentation of this file.
00001 #include "config.h"
00002 
00003 #include <iostream>
00004 
00005 #ifdef HAVE_STDLIB_H
00006 #include <stdlib.h>
00007 #endif
00008 #ifdef HAVE_SYS_TYPES_H
00009 #include <sys/types.h>
00010 #endif
00011 #ifdef HAVE_SYS_WAIT_H
00012 #include <sys/wait.h>
00013 #endif
00014 #ifdef HAVE_UNISTD_H
00015 #include <unistd.h>
00016 #endif
00017 #ifdef HAVE_SIGNAL_H
00018 #include <signal.h>
00019 #endif
00020 #ifdef HAVE_SYS_SELECT_H
00021 #include <sys/select.h>
00022 #endif
00023 #include <sys/time.h>
00024 
00025 #include <cerrno>
00026 
00027 #include "asserts.h"
00028 #include "error.h"
00029 #include "fs.h"
00030 #include "exec.h"
00031 
00032 /** C'tor */
00033 execute::execute()
00034 {
00035         clear();
00036 }
00037 
00038 /** D'tor */
00039 execute::~execute()
00040 {
00041         clear();
00042 }
00043 
00044 /** Reset the execute class to default values, kill the child processif one is
00045  * running */
00046 void execute::clear(void)
00047 {
00048         if (child_running() && is_parent())
00049                 signal_child(SIGKILL);
00050 
00051         m_fd1[0] = 0;
00052         m_fd1[1] = 0;
00053         m_fd2[0] = 0;
00054         m_fd2[1] = 0;
00055         m_fd3[0] = 0;
00056         m_fd3[1] = 0;
00057         m_pid = 0;
00058         m_status = 0;
00059         m_in_eof = false;
00060         m_out_eof = false;
00061         m_err_eof = false;
00062         m_child_started = false;
00063 }
00064 
00065 /** Generic signal handler */
00066 static void _signal_handler(int signo)
00067 {
00068         throw(ERROR(0,"Tried to write to a read-only pipe"));
00069 }
00070 
00071 /** Fork a child process */
00072 void execute::fork(void)
00073 {
00074         clear();
00075         if (signal(SIGPIPE, _signal_handler) == SIG_ERR)
00076                 throw(ERROR(errno,"Could not catch SIGPIPIE signal"));
00077         if (pipe(m_fd1) < 0)
00078                 throw(ERROR(errno,"Could not create pipe for in"));
00079         if (pipe(m_fd2) < 0)
00080                 throw(ERROR(errno,"Could not create pipe for out"));
00081         if (pipe(m_fd3) < 0)
00082                 throw(ERROR(errno,"Could not create pipe for err"));
00083         if ((m_pid = ::fork()) < 0)
00084                 throw(ERROR(errno,"Could not fork"));
00085         if (m_pid > 0) {
00086                 // Parent process
00087                 close(m_fd1[0]);
00088                 close(m_fd2[1]);
00089                 close(m_fd3[1]);
00090                 m_child_started = true;
00091         }
00092         else {
00093                 // Child process
00094                 close(m_fd1[1]);
00095                 close(m_fd2[0]);
00096                 close(m_fd3[0]);
00097         }
00098 }
00099 
00100 /** Returns true if called by the child */
00101 bool execute::is_child(void)
00102 {
00103         if (m_pid == 0) {
00104                 return(true);
00105         }
00106         return(false);
00107 }
00108 
00109 /** Returns true if called by the parent */
00110 bool execute::is_parent(void)
00111 {
00112         bool value;
00113 
00114         value = !is_child();
00115 
00116         return(value);
00117 }
00118 
00119 /** Returns the PID */
00120 pid_t execute::my_pid(void)
00121 {
00122         pid_t value;
00123 
00124         value = pid();
00125 
00126         return(value);
00127 }
00128 
00129 /** Called by the child to exit with a particular code */
00130 void execute::exit(int code)
00131 {
00132         if (is_child())
00133 		::exit(code);
00134 }
00135 
00136 /** Called by the child to reroute the child's stdin, stdout, and stderr to
00137  * the parent */
00138 void execute::reroute_stdio(void)
00139 {
00140         if (!is_child())
00141                 return;
00142 
00143         if (m_fd1[0] != STDIN_FILENO) {
00144                 if (dup2(m_fd1[0], STDIN_FILENO) != STDIN_FILENO) {
00145                         error e(errno,ERROR_INSTANCE("dup2() failed for stdin"));
00146                         std::cerr << e << std::endl;
00147                         exit(127);
00148                 }
00149                 close(m_fd1[0]);
00150         }
00151         if (m_fd2[1] != STDOUT_FILENO) {
00152                 if (dup2(m_fd2[1], STDOUT_FILENO) != STDOUT_FILENO) {
00153                         error e(errno,ERROR_INSTANCE("dup2() failed for stdout"));
00154                         std::cerr << e << std::endl;
00155                         exit(127);
00156                 }
00157                 close(m_fd2[1]);
00158         }
00159         if (m_fd3[1] != STDERR_FILENO) {
00160                 if (dup2(m_fd3[1], STDERR_FILENO) != STDERR_FILENO) {
00161                         error e(errno,ERROR_INSTANCE("dup2() failed for stderr"));
00162                         std::cerr << e << std::endl;
00163                         exit(127);
00164                 }
00165                 close(m_fd3[1]);
00166         }
00167 }
00168 
00169 /** Returns the child's PID */
00170 pid_t execute::child_pid(void)
00171 {
00172         return(m_pid);
00173 }
00174 
00175 /** Send a signal to the child */
00176 void execute::signal_child(int signal_no)
00177 {
00178         kill(m_pid, signal_no);
00179 }
00180 
00181 /** Send a HUP signal to the child */
00182 void execute::hup_child(void)
00183 {
00184         signal_child(SIGHUP);
00185 }
00186 
00187 /** Send a KILL signal to the child */
00188 void execute::kill_child(void)
00189 {
00190         signal_child(SIGKILL);
00191 }
00192 
00193 /** Wait for the child to exit */
00194 void execute::wait(void)
00195 {
00196         if (is_parent())
00197 		::wait(&m_status);
00198 }
00199 
00200 /** Check the child's status */
00201 pid_t execute::check_child_(void)
00202 {
00203         pid_t value;
00204         
00205         value = waitpid(m_pid, &m_status, WNOHANG|WUNTRACED);
00206 
00207         return(value);
00208 }
00209 
00210 /** Returns true if the child has been started */
00211 bool execute::child_started(void) const
00212 {
00213         return(m_child_started);
00214 }
00215 
00216 /** Returns true if the child is running */
00217 bool execute::child_running(void)
00218 {
00219         bool value;
00220 
00221         // value = (check_child_() == 0); // Should this be != -1 instead?
00222         value = (check_child_() != -1);
00223 
00224         return(value);
00225 }
00226 
00227 /** Returns true of the child has existed */
00228 bool execute::child_exited(void)
00229 {
00230         bool value;
00231 
00232         value = (check_child_() == -1);
00233 
00234         return(value);
00235 }
00236 
00237 /** Returns true if the child has exited normally */
00238 bool execute::child_exited_normally(void)
00239 {
00240         bool value;
00241 
00242         // TODO: Something's screwy here...
00243         // value = (child_exited() && WIFEXITED(m_status));
00244         value = (child_exited() && (child_exit_code() == 0));
00245 
00246         return(value);
00247 }
00248 
00249 /** Returns true if the child was signaled */
00250 bool execute::child_signaled(void)
00251 {
00252         bool value;
00253 
00254         check_child_();
00255         value = WIFSIGNALED(m_status);
00256 
00257         return(value);
00258 }
00259 
00260 /** Return the child's exit code */
00261 int execute::child_exit_code(void)
00262 {
00263         int value;
00264 
00265         // TODO: Something's screwy here...
00266         check_child_();
00267         value = WEXITSTATUS(m_status);
00268 
00269         return(value);
00270 }
00271 
00272 /** If the child was signaled, return the signal number */
00273 int execute::child_signal_no(void)
00274 {
00275         int value;
00276 
00277         check_child_();
00278         if (child_exited()) {
00279                 value = WTERMSIG(m_status);
00280                 return(value);
00281         }
00282         else {
00283                 value = WSTOPSIG(m_status);
00284                 return(value);
00285         }
00286 }
00287 
00288 /** Return a file descriptor for I/O between parent and child
00289 
00290         If called by the parent, a writeable file descriptor is returned.
00291         If called by the child, a readable file descriptor is returned.
00292 
00293         If retroute_stdio() was called by the child, then the returned file
00294         descriptor is the same as that used by the child for stdin.
00295  */
00296 int execute::in_fd(void)
00297 {
00298         if (is_parent()) {
00299                 // Return write end of input pipe
00300                 return(m_fd1[1]);
00301         }
00302         else {
00303                 // Return read end of input pipe
00304                 return(m_fd1[0]); 
00305         }
00306 }
00307 
00308 /** Return a file descriptor for I/O between parent a child.
00309 
00310         If called by the parent, a readable file descriptor is returned.
00311         If called by the child, a writable file descriptor is returned.
00312 
00313         If reroute_stdio() was called by the child, then the returned file
00314         descriptor is the same as that used by the child for stdout.
00315  */
00316 int execute::out_fd(void)
00317 {
00318         if (is_parent()) {
00319                 // Return read end of output pipe
00320                 return(m_fd2[0]);
00321         }
00322         else {
00323                 // Return write end of output pipe
00324                 return(m_fd2[1]);
00325         }
00326 }
00327 
00328 /** Return a file descriptor for I/O between parent and child.
00329 
00330         If called by the parent, a readable file descriptor is returned.
00331         If called by the child, a writeable file descriptor is returned.
00332 
00333         If reroute_stdio() was called by the child, then the returned file
00334         descriptior is the same as that used by the child for stderr.
00335  */
00336 int execute::err_fd(void)
00337 {
00338         if (is_parent()) {
00339                 // return read end of error pipe
00340                 return(m_fd3[0]); 
00341         }
00342         else {
00343                 // return write end of error pipe
00344                 return(m_fd3[1]); 
00345         }
00346 }
00347 
00348 /** Return true if the file descriptor is ready to be written to */
00349 bool execute::check_write_ready_(int fd)
00350 {
00351         struct timeval timeout = { 0, 0 };
00352         fd_set wset;
00353 
00354         FD_ZERO(&wset);
00355         FD_SET(fd, &wset);
00356         select(fd+1, 0, &wset, 0, &timeout);
00357         if (FD_ISSET(fd, &wset)) {
00358                 return(true);
00359         }
00360         return(false);
00361 }
00362 
00363 /** Return true if the file descriptor has input ready to be read */
00364 bool execute::check_read_ready_(int fd)
00365 {
00366         struct timeval timeout = { 0, 0 };
00367         fd_set rset;
00368 
00369         FD_ZERO(&rset);
00370         FD_SET(fd, &rset);
00371         select(fd+1, &rset, 0, 0, &timeout);
00372         if (FD_ISSET(fd, &rset)) {
00373                 return(true);
00374         }
00375         return(false);
00376 }
00377 
00378 /** Execute a command, rerouting stdin, stdout, and stderr to parent */
00379 void execute::exec(const std::string command)
00380 {
00381         fork();
00382         if (is_parent())
00383                 return;
00384 
00385         reroute_stdio();
00386         
00387         execl("/bin/sh", "sh", "-c", command.c_str(), (char *)0);
00388         exit(127);
00389 }
00390 
00391 /** Check I/O for input
00392 
00393         If called by the parent, check if ready to write to child's input.
00394         If called by the child, check if input is ready to be read.
00395 
00396         If reroute_stdio() was called by the child, then this pipe is the same as
00397         used by the child for stdin.
00398  */
00399 bool execute::in_ready(void)
00400 {
00401         bool value;
00402 
00403         if (is_parent())
00404                 value = check_write_ready_(in_fd());
00405         else
00406                 value = check_read_ready_(in_fd());
00407         
00408         return(value);
00409 }
00410 
00411 /** Check I/O for output
00412 
00413         If called by the parent, check if output from child is ready to be read.
00414         If called by the child, check if output to parent is ready to be written to.
00415 
00416         If reroute_stdio() was called by the child, then this pipe is the same as
00417         used by the child for stdout.
00418  */
00419 bool execute::out_ready(void)
00420 {
00421         bool value;
00422 
00423         if (is_parent())
00424                 value = check_read_ready_(out_fd());
00425         else
00426                 value = check_write_ready_(out_fd());
00427         
00428         return(value);
00429 }
00430 
00431 /** Check I/O for output
00432 
00433         If called by the parent, check if output from child is ready to be read.
00434         If called by the child, check if output to parent is ready to be written to.
00435 
00436         If reroute_stdio() was called by the child, then this pipe is the same as
00437         used by the child for stderr.
00438  */
00439 bool execute::err_ready(void)
00440 {
00441         bool value;
00442 
00443         if (is_parent())
00444                 value = check_read_ready_(err_fd());
00445         else
00446                 value = check_write_ready_(err_fd());
00447         
00448         return(value);
00449 }
00450 
00451 /** Check for input EOF */
00452 bool execute::in_eof(void)
00453 {
00454         return(m_in_eof);
00455 }
00456 
00457 /** Check for output EOF */
00458 bool execute::out_eof(void)
00459 {
00460         return(m_out_eof);
00461 }
00462 
00463 /** Check for err EOF */
00464 bool execute::err_eof(void)
00465 {
00466         return(m_err_eof);
00467 }
00468 
00469 /** Allow child to read input from in_fd() */
00470 int execute::in_read(char* buf, const int len)
00471 {
00472         int n;
00473         int err;
00474 
00475         n = read(in_fd(), buf, len);
00476         err = errno;
00477         errno = 0;
00478         if (n > 0) {
00479                 return(n);
00480         }
00481         if (child_started() && !child_running() && (err != EAGAIN))
00482                 m_in_eof = true;
00483         return(0);
00484 }
00485 
00486 /** Allow parent to write output to in_fd() */
00487 int execute::in_write(const char* buf, const int len)
00488 {
00489         int n;
00490         int err;
00491 
00492         n = write(in_fd(), buf, len);
00493         err = errno;
00494         errno = 0;
00495         if (n > 0) {
00496                 return(n);
00497         }
00498         if (child_started() && !child_running() && (err != EAGAIN))
00499                 m_in_eof = true;
00500         return(0);
00501 }
00502 
00503 /** Allow parent to read out_fd() */
00504 int execute::out_read(char* buf, const int len)
00505 {
00506         int n;
00507         int err;
00508 
00509         n = read(out_fd(), buf, len);
00510         err = errno;
00511         errno = 0;
00512         if (n > 0) {
00513                 return(n);
00514         }
00515         if (child_started() && !child_running() && (err != EAGAIN))
00516                 m_out_eof = true;
00517         return(0);
00518 }
00519 
00520 /** Allow child to write to out_fd() */
00521 int execute::out_write(const char* buf, const int len)
00522 {
00523         int n;
00524         int err;
00525 
00526         n = write(out_fd(), buf, len);
00527         err = errno;
00528         errno = 0;
00529         if (n > 0) {
00530                 return(n);
00531         }
00532         if (child_started() && !child_running() && (err != EAGAIN))
00533                 m_out_eof = true;
00534         return(0);
00535 }
00536 
00537 /** Allow parent to read from err_fd() */
00538 int execute::err_read(char* buf, const int len)
00539 {
00540         int n;
00541         int err;
00542 
00543         n = read(err_fd(), buf, len);
00544         err = errno;
00545         errno = 0;
00546         if (n > 0) {
00547                 return(n);
00548         }
00549         if (child_started() && !child_running() && (err != EAGAIN))
00550                 m_err_eof = true;
00551         return(0);
00552 }
00553 
00554 /** Allow child to write to err_fd() */
00555 int execute::err_write(const char* buf, const int len)
00556 {
00557         int n;
00558         int err;
00559 
00560         n = write(err_fd(), buf, len);
00561         err = errno;
00562         errno = 0;
00563         if (n > 0) {
00564                 return(n);
00565         }
00566         if (child_started() && !child_running() && (err != EAGAIN))
00567                 m_err_eof = true;
00568         return(0);
00569 }
00570 
00571 /** Dump execute object information -- used for debugging */
00572 void execute::print(std::ostream& out)
00573 {
00574         out << "execute::is_child() = " << is_child() << std::endl;
00575         out << "execute::is_parent() = " << is_parent() << std::endl;
00576         out << "execute::my_pid() = " << my_pid() << std::endl;
00577         out << "execute::child_running() = " << child_running() << std::endl;
00578         out << "execute::child_exited() = " << child_exited() << std::endl;
00579         out << "execute::child_exited_normally() = " << child_exited_normally() 
00580                 << std::endl;
00581         out << "execute::child_signaled() = " << child_signaled() << std::endl;
00582         out << "execute::child_exit_code() = " << child_exit_code() << std::endl;
00583         out << "execute::child_signal_no() = " << child_signal_no() << std::endl;
00584         out << "execute::in_fd() = " << in_fd() << std::endl;
00585         out << "execute::out_fd() = " << out_fd() << std::endl;
00586         out << "execute::err_fd() = " << err_fd() << std::endl;
00587         out << "execute::in_ready() = " << in_ready() << std::endl;
00588         out << "execute::out_ready() = " << out_ready() << std::endl;
00589         out << "execute::err_ready() = " << err_ready() << std::endl;
00590 }
00591 
00592 /** Convenience function to call execute::print() */
00593 std::ostream& operator << (std::ostream& out, execute& exe)
00594 {
00595         exe.print(out);
00596 
00597         return(out);
00598 }
00599 

Generated on Fri Jun 23 16:46:30 2006 for rvm by  doxygen 1.4.2