|
- /*
- * 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);
- }
- }
-
- std::string filter (const std::string in) {
- std::string out;
- char prev {0}, cur, next {0};
-
- for (auto i = in.begin(); i != in.end() ; ++i) {
- cur = *i;
- if (cur == '|') {
- if (prev != ' ') out += ' ';
- next = *++i;
- if (next == '|' || next == ' ') {
- out += cur; cur = next; // syntax ok
- }
- else {
- // syntax problem, insert a space
- out += cur; out += ' '; cur = next;
- }
- }
- prev = cur;
- out += cur;
- }
- return out;
- }
- /*
- * ========== ArgList ==========
- */
- ArgList::~ArgList() {
- for (vtype p : args_) {
- if (p != nullptr)
- delete[] p;
- }
- }
- ArgList& ArgList::push_back(const std::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);
- // close any leaked pipes
- // if (pipe_.fd[0] != -1) close(pipe_.fd[0]);
- // if (pipe_.fd[1] != -1) close(pipe_.fd[1]);
- }
-
- 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};
- bool CanRedirectIn = (!pipe_.from) ? true : false;
- bool CanRedirectOut = (!pipe_.to) ? true : false;
-
- for (auto& t: command) {
- if (t == "" || t== " " || t=="|") continue; // skip crap
-
- if (t == "&&") logic = LogicOp::AND;
- else if (t == "||") logic = LogicOp::OR;
- // one pass redirection parsing
- else if (CanRedirectIn && !t.compare(0, 1, "<"))
- filenames[STDIN_] = t.substr(1);
- else if (CanRedirectOut&& !t.compare(0, 1, ">"))
- filenames[STDOUT_] = t.substr(1);
- else if (CanRedirectOut&& !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) {
- if (CanRedirectIn)
- filenames[STDIN_] = t;
- in = false;
- }
- else if (out) {
- if (CanRedirectOut)
- filenames[STDOUT_] = t;
- out = false;
- }
- else if (err) {
- filenames[STDERR_] = t; err = false;
- }
- else
- arguments.push_back(t);
- }
- return *this;
- }
-
- bool Child::execute(std::vector<Child>::iterator it, bool first) {
- bool stop = {false};
- if (!first) --it;
- Child& previous = *it;
-
- // Check parent redirection
- redirect_std_if (filenames[STDIN_], STDIN_);
- redirect_std_if (filenames[STDOUT_], STDOUT_);
- redirect_std_if (filenames[STDERR_], STDERR_);
-
- // Check parent pipe control
- if (pipe_.to) {
- if((::pipe (pipe_.fd)) == -1)
- throw Error("Child: Can not create pipe");
- }
- pid_t pid = fork();
- if (pid < 0) {
- throw Error("Child: Can not create child process");
- }
- else if (pid == 0) { // child
- // Some extra pipe checking while we still have our data
- if (pipe_.to) { // transmitting child
- close(pipe_.fd[0]); // close the read end
- if ((dup2(pipe_.fd[1], STDOUT_)) == -1) // redirect output
- throw Error("Child pipe[to]: Can not redirect through pipe");
- }
- else if (!first && pipe_.from) { // receiveing child
- close (previous.pipe().fd[1]); // close the write end
- if ((dup2(previous.pipe().fd[0], STDIN_)) == -1) // redirect input
- throw Error("Child pipe[from]: Can not redirect through pipe");
- }
- execvp(arguments.front(), arguments.data());
- // error if we got here
- std::cout << "Can not invoke: " << arguments.front() << std::endl;
- throw Error("Child: Can not run child process");
- }
- else { // parent
- if (pipe_.to) close (pipe_.fd[1]);
- else if (!first && pipe_.from)
- close (previous.pipe().fd[0]);
-
- 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
- bool from = false;
- for (auto& t: tokens) {
- 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();
- if (from) {
- seq_.back().back().pipe().from = from;
- from = false;
- }
-
- if (is_pipe(t)) {
- seq_.back().back().pipe().to = true;
- from = true;
- }
- }
- }
- seq_.back().emplace_back(command); // construct the child with tokens
- if (from)
- seq_.back().back().pipe().from = from;
- }
- return *this;
- }
-
- Sequencer& Sequencer::execute() {
- for (auto& batch : seq_) {
- bool first;
- first = true;
- for (auto it = batch.begin() ; it != batch.end(); ++it) {
- Child& command = *it;
- if (command.make_arguments().execute(it, first))
- break;
- first = false;
- }
- }
- seq_.clear();
- return *this;
- }
-
- }
|