A quick and dirty shell implementation for A.U.TH. (Operating systems Lab)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

249 lines
7.7 KiB

  1. /*
  2. * child.cpp
  3. *
  4. * Created on: Feb 11, 2019
  5. * Author: hoo2
  6. */
  7. #include "sequencer.h"
  8. namespace snel {
  9. //! A type-safe memcpy
  10. template <typename T>
  11. void memcpy (T* to, const T* from, size_t n) {
  12. for (size_t i=0 ; i<n ; ++i)
  13. *to++ = *from++;
  14. }
  15. /*!
  16. * Split a string to tokens and store them in a container, using a delimiter
  17. * character.
  18. * \note
  19. * Requires: container with push_back functionality
  20. * @param str
  21. * @param cont
  22. * @param delim
  23. */
  24. template <typename T, typename Container>
  25. void split(const std::basic_string<T>& str, Container& cont, T delim) {
  26. std::basic_stringstream<T> ss(str);
  27. std::basic_string<T> token;
  28. while (std::getline(ss, token, delim)) {
  29. cont.push_back(token);
  30. }
  31. }
  32. /*
  33. * ========== ArgList ==========
  34. */
  35. ArgList::~ArgList() {
  36. for (vtype p : args_) {
  37. if (p != nullptr)
  38. delete[] p;
  39. }
  40. }
  41. ArgList& ArgList::push_back(const std::basic_string<type>& item) {
  42. vtype it = new type[item.length()+1];
  43. // get data
  44. memcpy (it, item.c_str(), item.length());
  45. it[item.length()] = 0;
  46. // update the argument vector
  47. args_.back() = it;
  48. args_.push_back(nullptr);
  49. return *this;
  50. }
  51. /*
  52. * ========== Child ==========
  53. */
  54. Child::~Child () {
  55. for (int i=0 ; i<3 ; ++i)
  56. restore_std_if(i);
  57. // close any leaked pipes
  58. // if (pipe_.fd[0] != -1) close(pipe_.fd[0]);
  59. // if (pipe_.fd[1] != -1) close(pipe_.fd[1]);
  60. }
  61. void Child::redirect_std_if(std::string fn, fd_t std_fd) {
  62. if (fn != "") {
  63. if ((files[std_fd] = openat(AT_FDCWD, fn.c_str(), O_RDWR | O_CREAT, 0640)) == -1)
  64. throw Error ("Child: Can not open file");
  65. if ((sstd[std_fd] = dup (std_fd)) == -1) // save STDxxx
  66. throw Error ("Child: Can not create file descriptor");
  67. if ((dup2(files[std_fd], std_fd)) == -1) // use input as STDIN_
  68. throw Error ("Child: Can not redirect file descriptor");
  69. }
  70. }
  71. void Child::restore_std_if(fd_t std_fd) {
  72. if (sstd[std_fd] != -1) {
  73. dup2(sstd[std_fd], std_fd);
  74. close (sstd[std_fd]);
  75. sstd[std_fd] = -1;
  76. }
  77. if (files[std_fd] != -1) {
  78. close (files[std_fd]);
  79. files[std_fd] = -1;
  80. }
  81. }
  82. Child& Child::make_arguments () {
  83. bool in{false}, out{false}, err{false};
  84. bool CanRedirectIn = (pipe_.from) ? true : false;
  85. bool CanRedirectOut = (!pipe_.to) ? true : false;
  86. for (auto& t: command) {
  87. if (t == "" || t== " " || t=="|") continue; // skip crap
  88. if (t == "&&") logic = LogicOp::AND;
  89. else if (t == "||") logic = LogicOp::OR;
  90. // one pass redirection parsing
  91. else if (CanRedirectIn && !t.compare(0, 1, "<"))
  92. filenames[STDIN_] = t.substr(1);
  93. else if (CanRedirectOut&& !t.compare(0, 1, ">"))
  94. filenames[STDOUT_] = t.substr(1);
  95. else if (CanRedirectOut&& !t.compare(0, 2, "1>"))
  96. filenames[STDOUT_] = t.substr(2);
  97. else if (!t.compare(0, 2, "2>"))
  98. filenames[STDERR_] = t.substr(2);
  99. // two pass redirection parsing (if redirection came in 2 arguments)
  100. else if (t == "<") in = true;
  101. else if (t == "1>" || t == ">") out = true;
  102. else if (t == "2>") err = true;
  103. else if (in) {
  104. if (CanRedirectIn)
  105. filenames[STDIN_] = t;
  106. in = false;
  107. }
  108. else if (out) {
  109. if (CanRedirectOut)
  110. filenames[STDOUT_] = t;
  111. out = false;
  112. }
  113. else if (err) {
  114. filenames[STDERR_] = t; err = false;
  115. }
  116. else
  117. arguments.push_back(t);
  118. }
  119. return *this;
  120. }
  121. bool Child::execute(const Child* previous) {
  122. bool stop = {false};
  123. // Check parent redirection
  124. redirect_std_if (filenames[STDIN_], STDIN_);
  125. redirect_std_if (filenames[STDOUT_], STDOUT_);
  126. redirect_std_if (filenames[STDERR_], STDERR_);
  127. // Check parent pipe control
  128. if (pipe_.to) {
  129. if((::pipe (pipe_.fd)) == -1)
  130. throw Error("Child: Can not create pipe");
  131. }
  132. pid_t pid = fork();
  133. if (pid < 0) {
  134. throw Error("Child: Can not create child process");
  135. }
  136. else if (pid == 0) { // child
  137. // Some extra pipe checking while we still have our data
  138. if (pipe_.to) { // transmitting child
  139. close(pipe_.fd[0]); // close the read end
  140. if ((dup2(pipe_.fd[1], STDOUT_)) == -1) // redirect output
  141. throw Error("Child pipe[to]: Can not redirect through pipe");
  142. }
  143. else if ((previous!= nullptr) && pipe_.from) { // receiveing child
  144. std::cout << "from[0]=" << previous->pipe().fd[0] << " from[1]=" << previous->pipe().fd[1]<< std::endl;
  145. close (previous->pipe().fd[1]); // close the write end
  146. if ((dup2(previous->pipe().fd[0], STDIN_)) == -1) // redirect input
  147. throw Error("Child pipe[from]: Can not redirect through pipe");
  148. }
  149. execvp(arguments.front(), arguments.data());
  150. // error if we got here
  151. std::cout << "Can not invoke: " << arguments.front() << std::endl;
  152. throw Error("Child: Can not run child process");
  153. }
  154. else { // parent
  155. if (pipe_.to) close (pipe_.fd[1]);
  156. else if (pipe_.from) close (previous->pipe().fd[0]);
  157. int exit_status;
  158. waitpid(pid, &exit_status, 0); // wait child process to finish
  159. restore_std_if (STDIN_);
  160. restore_std_if (STDOUT_);
  161. restore_std_if (STDERR_);
  162. switch (logic) {
  163. case LogicOp::AND:
  164. if (exit_status) stop = true;
  165. break;
  166. case LogicOp::OR:
  167. if (!exit_status) stop = true;
  168. break;
  169. default: break;
  170. }
  171. }
  172. return stop;
  173. }
  174. /*
  175. * ======== Sequencer ===========
  176. */
  177. Sequencer& Sequencer::parse (const std::string& input) {
  178. std::vector<std::string> commands;
  179. std::vector<std::string> tokens;
  180. Child::command_t command;
  181. split (input, commands, ';'); // split all commands
  182. for (auto& s : commands) {
  183. command.clear(); //clear child tokens
  184. tokens.clear(); // make a new token vector for command
  185. split (s, tokens, ' ');
  186. seq_.emplace_back(std::vector<Child>{}); // make a new child for command
  187. // check tokens inside command
  188. bool from = false;
  189. for (auto& t: tokens) {
  190. command.push_back(t); // get current token
  191. if (is_seperator(t)) {
  192. // construct a child with what we have so far and clear command
  193. seq_.back().emplace_back(command);
  194. command.clear();
  195. if (from) {
  196. seq_.back().back().pipe().from = from;
  197. from = false;
  198. }
  199. if (is_pipe(t)) {
  200. seq_.back().back().pipe().to = true;
  201. from = true;
  202. }
  203. }
  204. }
  205. seq_.back().emplace_back(command); // construct the child with tokens
  206. if (from)
  207. seq_.back().back().pipe().from = from;
  208. }
  209. return *this;
  210. }
  211. Sequencer& Sequencer::execute() {
  212. for (auto& batch : seq_)
  213. for (auto it = batch.begin() ; it != batch.end(); ++it) {
  214. Child& command = *it;
  215. Child p = (it != batch.begin()) ? std::prev(it) : Child{};
  216. if (command.make_arguments().execute(p))
  217. break;
  218. }
  219. seq_.clear();
  220. return *this;
  221. }
  222. }