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.
 
 
 

275 lines
8.3 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. std::string filter (const std::string in) {
  33. std::string out;
  34. char prev {0}, cur, next {0};
  35. for (auto i = in.begin(); i != in.end() ; ++i) {
  36. cur = *i;
  37. if (cur == '|') {
  38. if (prev != ' ') out += ' ';
  39. next = *++i;
  40. if (next == '|' || next == ' ') {
  41. out += cur; cur = next; // syntax ok
  42. }
  43. else {
  44. // syntax problem, insert a space
  45. out += cur; out += ' '; cur = next;
  46. }
  47. }
  48. prev = cur;
  49. out += cur;
  50. }
  51. return out;
  52. }
  53. /*
  54. * ========== ArgList ==========
  55. */
  56. ArgList::~ArgList() {
  57. for (vtype p : args_) {
  58. if (p != nullptr)
  59. delete[] p;
  60. }
  61. }
  62. ArgList& ArgList::push_back(const std::string& item) {
  63. vtype it = new type[item.length()+1];
  64. // get data
  65. memcpy (it, item.c_str(), item.length());
  66. it[item.length()] = 0;
  67. // update the argument vector
  68. args_.back() = it;
  69. args_.push_back(nullptr);
  70. return *this;
  71. }
  72. /*
  73. * ========== Child ==========
  74. */
  75. Child::~Child () {
  76. for (int i=0 ; i<3 ; ++i)
  77. restore_std_if(i);
  78. // close any leaked pipes
  79. // if (pipe_.fd[0] != -1) close(pipe_.fd[0]);
  80. // if (pipe_.fd[1] != -1) close(pipe_.fd[1]);
  81. }
  82. void Child::redirect_std_if(std::string fn, fd_t std_fd) {
  83. if (fn != "") {
  84. if ((files[std_fd] = openat(AT_FDCWD, fn.c_str(), O_RDWR | O_CREAT, 0640)) == -1)
  85. throw Error ("Child: Can not open file");
  86. if ((sstd[std_fd] = dup (std_fd)) == -1) // save STDxxx
  87. throw Error ("Child: Can not create file descriptor");
  88. if ((dup2(files[std_fd], std_fd)) == -1) // use input as STDIN_
  89. throw Error ("Child: Can not redirect file descriptor");
  90. }
  91. }
  92. void Child::restore_std_if(fd_t std_fd) {
  93. if (sstd[std_fd] != -1) {
  94. dup2(sstd[std_fd], std_fd);
  95. close (sstd[std_fd]);
  96. sstd[std_fd] = -1;
  97. }
  98. if (files[std_fd] != -1) {
  99. close (files[std_fd]);
  100. files[std_fd] = -1;
  101. }
  102. }
  103. Child& Child::make_arguments () {
  104. bool in{false}, out{false}, err{false};
  105. bool CanRedirectIn = (!pipe_.from) ? true : false;
  106. bool CanRedirectOut = (!pipe_.to) ? true : false;
  107. for (auto& t: command) {
  108. if (t == "" || t== " " || t=="|") continue; // skip crap
  109. if (t == "&&") logic = LogicOp::AND;
  110. else if (t == "||") logic = LogicOp::OR;
  111. // one pass redirection parsing
  112. else if (CanRedirectIn && !t.compare(0, 1, "<"))
  113. filenames[STDIN_] = t.substr(1);
  114. else if (CanRedirectOut&& !t.compare(0, 1, ">"))
  115. filenames[STDOUT_] = t.substr(1);
  116. else if (CanRedirectOut&& !t.compare(0, 2, "1>"))
  117. filenames[STDOUT_] = t.substr(2);
  118. else if (!t.compare(0, 2, "2>"))
  119. filenames[STDERR_] = t.substr(2);
  120. // two pass redirection parsing (if redirection came in 2 arguments)
  121. else if (t == "<") in = true;
  122. else if (t == "1>" || t == ">") out = true;
  123. else if (t == "2>") err = true;
  124. else if (in) {
  125. if (CanRedirectIn)
  126. filenames[STDIN_] = t;
  127. in = false;
  128. }
  129. else if (out) {
  130. if (CanRedirectOut)
  131. filenames[STDOUT_] = t;
  132. out = false;
  133. }
  134. else if (err) {
  135. filenames[STDERR_] = t; err = false;
  136. }
  137. else
  138. arguments.push_back(t);
  139. }
  140. return *this;
  141. }
  142. bool Child::execute(std::vector<Child>::iterator it, bool first) {
  143. bool stop = {false};
  144. if (!first) --it;
  145. Child& previous = *it;
  146. // Check parent redirection
  147. redirect_std_if (filenames[STDIN_], STDIN_);
  148. redirect_std_if (filenames[STDOUT_], STDOUT_);
  149. redirect_std_if (filenames[STDERR_], STDERR_);
  150. // Check parent pipe control
  151. if (pipe_.to) {
  152. if((::pipe (pipe_.fd)) == -1)
  153. throw Error("Child: Can not create pipe");
  154. }
  155. pid_t pid = fork();
  156. if (pid < 0) {
  157. throw Error("Child: Can not create child process");
  158. }
  159. else if (pid == 0) { // child
  160. // Some extra pipe checking while we still have our data
  161. if (pipe_.to) { // transmitting child
  162. close(pipe_.fd[0]); // close the read end
  163. if ((dup2(pipe_.fd[1], STDOUT_)) == -1) // redirect output
  164. throw Error("Child pipe[to]: Can not redirect through pipe");
  165. }
  166. else if (!first && pipe_.from) { // receiveing child
  167. close (previous.pipe().fd[1]); // close the write end
  168. if ((dup2(previous.pipe().fd[0], STDIN_)) == -1) // redirect input
  169. throw Error("Child pipe[from]: Can not redirect through pipe");
  170. }
  171. execvp(arguments.front(), arguments.data());
  172. // error if we got here
  173. std::cout << "Can not invoke: " << arguments.front() << std::endl;
  174. throw Error("Child: Can not run child process");
  175. }
  176. else { // parent
  177. if (pipe_.to) close (pipe_.fd[1]);
  178. else if (!first && pipe_.from)
  179. close (previous.pipe().fd[0]);
  180. int exit_status;
  181. waitpid(pid, &exit_status, 0); // wait child process to finish
  182. restore_std_if (STDIN_);
  183. restore_std_if (STDOUT_);
  184. restore_std_if (STDERR_);
  185. switch (logic) {
  186. case LogicOp::AND:
  187. if (exit_status) stop = true;
  188. break;
  189. case LogicOp::OR:
  190. if (!exit_status) stop = true;
  191. break;
  192. default: break;
  193. }
  194. }
  195. return stop;
  196. }
  197. /*
  198. * ======== Sequencer ===========
  199. */
  200. Sequencer& Sequencer::parse (const std::string& input) {
  201. std::vector<std::string> commands;
  202. std::vector<std::string> tokens;
  203. Child::command_t command;
  204. split (input, commands, ';'); // split all commands
  205. for (auto& s : commands) {
  206. command.clear(); //clear child tokens
  207. tokens.clear(); // make a new token vector for command
  208. split (s, tokens, ' ');
  209. seq_.emplace_back(std::vector<Child>{}); // make a new child for command
  210. // check tokens inside command
  211. bool from = false;
  212. for (auto& t: tokens) {
  213. command.push_back(t); // get current token
  214. if (is_seperator(t)) {
  215. // construct a child with what we have so far and clear command
  216. seq_.back().emplace_back(command);
  217. command.clear();
  218. if (from) {
  219. seq_.back().back().pipe().from = from;
  220. from = false;
  221. }
  222. if (is_pipe(t)) {
  223. seq_.back().back().pipe().to = true;
  224. from = true;
  225. }
  226. }
  227. }
  228. seq_.back().emplace_back(command); // construct the child with tokens
  229. if (from)
  230. seq_.back().back().pipe().from = from;
  231. }
  232. return *this;
  233. }
  234. Sequencer& Sequencer::execute() {
  235. for (auto& batch : seq_) {
  236. bool first;
  237. first = true;
  238. for (auto it = batch.begin() ; it != batch.end(); ++it) {
  239. Child& command = *it;
  240. if (command.make_arguments().execute(it, first))
  241. break;
  242. first = false;
  243. }
  244. }
  245. seq_.clear();
  246. return *this;
  247. }
  248. }