Initial commit
This commit is contained in:
commit
ef7a4af571
3 changed files with 317 additions and 0 deletions
1
README.md
Normal file
1
README.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# A simple command class for C++
|
255
command.cpp
Normal file
255
command.cpp
Normal file
|
@ -0,0 +1,255 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#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<string> &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.size(); i++) {
|
||||||
|
argv[i] = const_cast<char*>(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;
|
||||||
|
}
|
61
command.h
Normal file
61
command.h
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#ifndef COMMAND_H
|
||||||
|
#define COMMAND_H
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#if __has_include(<boost/filesystem.hpp>)
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#define COMMAND_BOOST_FILESYSTEM
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class command {
|
||||||
|
public:
|
||||||
|
command();
|
||||||
|
command& operator <<(int n);
|
||||||
|
#ifdef COMMAND_BOOST_FILESYSTEM
|
||||||
|
command& operator <<(const boost::filesystem::path& p);
|
||||||
|
#else
|
||||||
|
command& operator <<(const std::string& s);
|
||||||
|
#endif
|
||||||
|
command& operator <<(const std::vector<std::string>& v);
|
||||||
|
command& operator <<(double d);
|
||||||
|
void clear();
|
||||||
|
int run();
|
||||||
|
void runbg();
|
||||||
|
bool isrunning();
|
||||||
|
int wait();
|
||||||
|
|
||||||
|
void silence_stdout();
|
||||||
|
void silence_stderr();
|
||||||
|
void silence();
|
||||||
|
void stdout(std::ostream& o);
|
||||||
|
void stderr(std::ostream& o);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::string> args;
|
||||||
|
|
||||||
|
pid_t childpid;
|
||||||
|
|
||||||
|
int stdout_pipe[2];
|
||||||
|
int stderr_pipe[2];
|
||||||
|
|
||||||
|
bool _silence_stdout = false;
|
||||||
|
bool _silence_stderr = false;
|
||||||
|
|
||||||
|
std::ostream* stdout_stream = nullptr;
|
||||||
|
std::ostream* stderr_stream = nullptr;
|
||||||
|
|
||||||
|
friend std::ostream& operator<<(std::ostream& o, const command& cmd);
|
||||||
|
|
||||||
|
char *readbuf;
|
||||||
|
int bufsize = 1024;
|
||||||
|
int async = false;
|
||||||
|
int read_from(int fd, std::ostream *stream);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& o, const command& cmd);
|
||||||
|
|
||||||
|
#endif // COMMAND_H
|
Loading…
Reference in a new issue