#include #include #include #include #include #include #include #include "command.h" using std::vector; using std::string; using std::ostringstream; using std::cout; using std::endl; command::command() { readbuf = (char *)malloc(bufsize); } command &command::operator <<(int n) { ostringstream s; s << n; args.push_back(s.str()); return *this; } #ifdef COMMAND_BOOST_FILESYSTEM command& command::operator<<(const boost::filesystem::path& p) { args.push_back(p.string()); return *this; } #else command& command::operator<<(const string& s) { args.push_back(s); return *this; } #endif command &command::operator <<(const vector &v) { std::copy(v.begin(), v.end(), std::back_inserter(args)); return *this; } command &command::operator <<(double d) { ostringstream s; s << d; args.push_back(s.str()); return *this; } void command::clear() { args.clear(); async = false; } int command::read_from(int fd, std::ostream *stream) { int r = read(fd, readbuf, bufsize); if (r <= 0) return r; stream->write(readbuf, r); if (r == bufsize) { bufsize *= 2; free(readbuf); readbuf = (char *)malloc(bufsize); } return r; } int command::run() { if (args.empty()) return -1; if (stdout_stream != nullptr) pipe(stdout_pipe); if (stderr_stream != nullptr) pipe(stderr_pipe); childpid = fork(); if (childpid == 0) { char **argv = (char**)malloc((args.size() + 1) * sizeof(char*)); for (uint i=0; i(args.at(i).c_str()); } argv[args.size()] = nullptr; if (_silence_stdout || _silence_stderr) { int f = open("/dev/null", O_WRONLY); if (_silence_stdout) dup2(f, STDOUT_FILENO); if (_silence_stderr) dup2(f, STDERR_FILENO); } if (stdout_stream != nullptr) { dup2(stdout_pipe[1], STDOUT_FILENO); close(stdout_pipe[0]); } if (stderr_stream != nullptr) { dup2(stderr_pipe[1], STDERR_FILENO); close(stderr_pipe[0]); } execvp(args.at(0).c_str(), argv); perror(args.at(0).c_str()); exit(-1); } int child_out = -1; int child_err = -1; // parent won't be writing anything to the pipes // and keeping the write ends open will prevent // EOF from happening on the read ends if (stdout_stream != nullptr) { close(stdout_pipe[1]); child_out = stdout_pipe[0]; } if (stderr_stream != nullptr) { child_err = stderr_pipe[0]; close(stderr_pipe[1]); } if (async) { return 0; } // now we'll wait on both ends, using select while (child_err >=0 || child_out >= 0) { fd_set read_fds; FD_ZERO(&read_fds); int nfds = 0; if (child_out >= 0) { FD_SET(child_out, &read_fds); nfds = std::max(nfds, child_out); } if (child_err >= 0) { FD_SET(child_err, &read_fds); nfds = std::max(nfds, child_err); } nfds++; int r = select(nfds, &read_fds, nullptr, nullptr, nullptr); if (r < 0) { perror("select failed"); break; } if (child_out >= 0 && FD_ISSET(child_out, &read_fds)) { int r = read_from(child_out, stdout_stream); if (r < 0) { perror("error reading from child stdout"); break; } if (r == 0) { close(child_out); child_out = -1; } } if (child_err >= 0 && FD_ISSET(child_err, &read_fds)) { read_from(child_err, stderr_stream); if (r < 0) { perror("error reading from child stderr"); break; } if (r == 0) { close(child_err); child_err = -1; } } } return this->wait(); } void command::runbg() { async = true; run(); } bool command::isrunning() { siginfo_t info; info.si_pid = 0; int r = waitid(P_PID, childpid, &info, WEXITED | WNOHANG | WNOWAIT); if (r == 0 && info.si_pid != 0) return true; switch (info.si_code) { case CLD_EXITED: return false; case CLD_KILLED: return false; case CLD_DUMPED: return false; } return true; } int command::wait() { int status; ::wait(&status); if (WIFEXITED(status)) return WEXITSTATUS(status); if (WIFSIGNALED(status)) return WTERMSIG(status) + 127; return -1; } void command::silence_stdout() { _silence_stdout = true; stdout_stream = nullptr; } void command::silence_stderr() { stderr_stream = nullptr; _silence_stderr = true; } void command::silence() { silence_stdout(); silence_stderr(); } void command::stdout(std::ostream &o) { stdout_stream = &o; _silence_stdout = false; } void command::stderr(std::ostream &o) { stderr_stream = &o; _silence_stderr = false; } std::ostream &operator<<(std::ostream &o, const command &cmd) { for (auto& s: cmd.args) { o << s << " "; } return o; }