/* * child.cpp * * Created on: Feb 11, 2019 * Author: hoo2 */ #include "sequencer.h" namespace snel { //! A type-safe memcpy template void memcpy (T* to, const T* from, size_t n) { for (size_t i=0 ; i void split(const std::basic_string& str, Container& cont, T delim) { std::basic_stringstream ss(str); std::basic_string 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& 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; // skip crap if (t == "&&") logic = LogicOp::AND; else if (t == "||") logic = LogicOp::OR; // one pass redirection parsing else if (!t.compare(0, 1, "<")) filenames[STDIN_] = t.substr(1); else if (!t.compare(0, 1, ">")) filenames[STDOUT_] = t.substr(1); else if (!t.compare(0, 2, "1>")) filenames[STDOUT_] = t.substr(2); else if (!t.compare(0, 2, "2>")) filenames[STDERR_] = t.substr(2); // two pass redirection parsing (if redirection came in 2 arguments) 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 commands; std::vector 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{}); // 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; } }