|
|
@@ -0,0 +1,197 @@ |
|
|
|
/* |
|
|
|
* child.cpp |
|
|
|
* |
|
|
|
* Created on: Feb 11, 2019 |
|
|
|
* Author: hoo2 |
|
|
|
*/ |
|
|
|
|
|
|
|
#include "sequencer.h" |
|
|
|
|
|
|
|
namespace snel { |
|
|
|
|
|
|
|
|
|
|
|
//! A type-safe memcpy |
|
|
|
template <typename T> |
|
|
|
void memcpy (T* to, const T* from, size_t n) { |
|
|
|
for (size_t i=0 ; i<n ; ++i) |
|
|
|
*to++ = *from++; |
|
|
|
} |
|
|
|
|
|
|
|
/*! |
|
|
|
* Split a string to tokens and store them in a container, using a delimiter |
|
|
|
* character. |
|
|
|
* \note |
|
|
|
* Requires: container with push_back functionality |
|
|
|
* @param str |
|
|
|
* @param cont |
|
|
|
* @param delim |
|
|
|
*/ |
|
|
|
template <typename T, typename Container> |
|
|
|
void split(const std::basic_string<T>& str, Container& cont, T delim) { |
|
|
|
std::basic_stringstream<T> ss(str); |
|
|
|
std::basic_string<T> token; |
|
|
|
while (std::getline(ss, token, delim)) { |
|
|
|
cont.push_back(token); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
* ========== ArgList ========== |
|
|
|
*/ |
|
|
|
ArgList::~ArgList() { |
|
|
|
for (vtype p : args_) { |
|
|
|
if (p != nullptr) |
|
|
|
delete[] p; |
|
|
|
} |
|
|
|
} |
|
|
|
ArgList& ArgList::push_back(const std::basic_string<type>& item) { |
|
|
|
vtype it = new type[item.length()+1]; |
|
|
|
|
|
|
|
// get data |
|
|
|
memcpy (it, item.c_str(), item.length()); |
|
|
|
it[item.length()] = 0; |
|
|
|
|
|
|
|
// update the argument vector |
|
|
|
args_.back() = it; |
|
|
|
args_.push_back(nullptr); |
|
|
|
return *this; |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
* ========== Child ========== |
|
|
|
*/ |
|
|
|
Child::~Child () { |
|
|
|
for (int i=0 ; i<3 ; ++i) |
|
|
|
restore_std_if(i); |
|
|
|
} |
|
|
|
|
|
|
|
void Child::redirect_std_if(std::string fn, fd_t std_fd) { |
|
|
|
if (fn != "") { |
|
|
|
if ((files[std_fd] = openat(AT_FDCWD, fn.c_str(), O_RDWR | O_CREAT, 0640)) == -1) |
|
|
|
throw Error ("Child: Can not open file"); |
|
|
|
if ((sstd[std_fd] = dup (std_fd)) == -1) // save STDxxx |
|
|
|
throw Error ("Child: Can not create file descriptor"); |
|
|
|
if ((dup2(files[std_fd], std_fd)) == -1) // use input as STDIN_ |
|
|
|
throw Error ("Child: Can not redirect file descriptor"); |
|
|
|
} |
|
|
|
} |
|
|
|
void Child::restore_std_if(fd_t std_fd) { |
|
|
|
if (sstd[std_fd] != -1) { |
|
|
|
dup2(sstd[std_fd], std_fd); |
|
|
|
close (sstd[std_fd]); |
|
|
|
sstd[std_fd] = -1; |
|
|
|
} |
|
|
|
if (files[std_fd] != -1) { |
|
|
|
close (files[std_fd]); |
|
|
|
files[std_fd] = -1; |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
Child& Child::make_arguments () { |
|
|
|
bool in{false}, out{false}, err{false}; |
|
|
|
|
|
|
|
for (auto& t: command) { |
|
|
|
if (t == "" || t== " ") |
|
|
|
continue; |
|
|
|
if (t == "&&") logic = LogicOp::AND; |
|
|
|
else if (t == "||") logic = LogicOp::OR; |
|
|
|
else if (t == "<") in = true; |
|
|
|
else if (t == "1>" || t == ">") out = true; |
|
|
|
else if (t == "2>") err = true; |
|
|
|
else if (in) { |
|
|
|
filenames[STDIN_] = t; in = false; |
|
|
|
} |
|
|
|
else if (out) { |
|
|
|
filenames[STDOUT_] = t; out = false; |
|
|
|
} |
|
|
|
else if (err) { |
|
|
|
filenames[STDERR_] = t; err = false; |
|
|
|
} |
|
|
|
else |
|
|
|
arguments.push_back(t); |
|
|
|
} |
|
|
|
return *this; |
|
|
|
} |
|
|
|
|
|
|
|
bool Child::execute() { |
|
|
|
bool stop = {false}; |
|
|
|
|
|
|
|
redirect_std_if (filenames[STDIN_], STDIN_); |
|
|
|
redirect_std_if (filenames[STDOUT_], STDOUT_); |
|
|
|
redirect_std_if (filenames[STDERR_], STDERR_); |
|
|
|
pid_t pid = fork(); |
|
|
|
if (pid < 0) { |
|
|
|
throw Error("Child::execute(): Can not create child process"); |
|
|
|
} |
|
|
|
else if (pid == 0) { // child |
|
|
|
|
|
|
|
execvp(arguments.front(), arguments.data()); |
|
|
|
// error if we got here |
|
|
|
std::cout << "Can not invoke: " << arguments.front() << std::endl; |
|
|
|
throw Error("Child::execute(): Can not run child process"); |
|
|
|
} |
|
|
|
else { // parent |
|
|
|
int exit_status; |
|
|
|
waitpid(pid, &exit_status, 0); // wait child process to finish |
|
|
|
restore_std_if (STDIN_); |
|
|
|
restore_std_if (STDOUT_); |
|
|
|
restore_std_if (STDERR_); |
|
|
|
|
|
|
|
switch (logic) { |
|
|
|
case LogicOp::AND: |
|
|
|
if (exit_status) stop = true; |
|
|
|
break; |
|
|
|
case LogicOp::OR: |
|
|
|
if (!exit_status) stop = true; |
|
|
|
break; |
|
|
|
default: break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return stop; |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
* ======== Sequencer =========== |
|
|
|
*/ |
|
|
|
Sequencer& Sequencer::parse (const std::string& input) { |
|
|
|
std::vector<std::string> commands; |
|
|
|
std::vector<std::string> tokens; |
|
|
|
Child::command_t command; |
|
|
|
|
|
|
|
split (input, commands, ';'); // split all commands |
|
|
|
for (auto& s : commands) { |
|
|
|
command.clear(); //clear child tokens |
|
|
|
tokens.clear(); // make a new token vector for command |
|
|
|
split (s, tokens, ' '); |
|
|
|
|
|
|
|
seq_.emplace_back(std::vector<Child>{}); // make a new child for command |
|
|
|
// check tokens inside command |
|
|
|
for (auto& t: tokens) { |
|
|
|
if (is_pipe(t)) { |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
command.push_back(t); // get current token |
|
|
|
if (is_seperator(t)) { |
|
|
|
// construct a child with what we have so far and clear command |
|
|
|
seq_.back().emplace_back(command); |
|
|
|
command.clear(); |
|
|
|
} |
|
|
|
} |
|
|
|
seq_.back().emplace_back(command); // construct the child with tokens |
|
|
|
} |
|
|
|
|
|
|
|
return *this; |
|
|
|
} |
|
|
|
|
|
|
|
Sequencer& Sequencer::execute() { |
|
|
|
for (auto& batch : seq_) |
|
|
|
for (auto& command: batch) |
|
|
|
if (command.make_arguments().execute()) |
|
|
|
break; |
|
|
|
seq_.clear(); |
|
|
|
return *this; |
|
|
|
} |
|
|
|
|
|
|
|
} |