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