A quick and dirty shell implementation for A.U.TH. (Operating systems Lab)
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 

204 Zeilen
5.8 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. }
  58. void Child::redirect_std_if(std::string fn, fd_t std_fd) {
  59. if (fn != "") {
  60. if ((files[std_fd] = openat(AT_FDCWD, fn.c_str(), O_RDWR | O_CREAT, 0640)) == -1)
  61. throw Error ("Child: Can not open file");
  62. if ((sstd[std_fd] = dup (std_fd)) == -1) // save STDxxx
  63. throw Error ("Child: Can not create file descriptor");
  64. if ((dup2(files[std_fd], std_fd)) == -1) // use input as STDIN_
  65. throw Error ("Child: Can not redirect file descriptor");
  66. }
  67. }
  68. void Child::restore_std_if(fd_t std_fd) {
  69. if (sstd[std_fd] != -1) {
  70. dup2(sstd[std_fd], std_fd);
  71. close (sstd[std_fd]);
  72. sstd[std_fd] = -1;
  73. }
  74. if (files[std_fd] != -1) {
  75. close (files[std_fd]);
  76. files[std_fd] = -1;
  77. }
  78. }
  79. Child& Child::make_arguments () {
  80. bool in{false}, out{false}, err{false};
  81. for (auto& t: command) {
  82. if (t == "" || t== " ") continue; // skip crap
  83. if (t == "&&") logic = LogicOp::AND;
  84. else if (t == "||") logic = LogicOp::OR;
  85. // one pass redirection parsing
  86. else if (!t.compare(0, 1, "<")) filenames[STDIN_] = t.substr(1);
  87. else if (!t.compare(0, 1, ">")) filenames[STDOUT_] = t.substr(1);
  88. else if (!t.compare(0, 2, "1>")) filenames[STDOUT_] = t.substr(2);
  89. else if (!t.compare(0, 2, "2>")) filenames[STDERR_] = t.substr(2);
  90. // two pass redirection parsing (if redirection came in 2 arguments)
  91. else if (t == "<") in = true;
  92. else if (t == "1>" || t == ">") out = true;
  93. else if (t == "2>") err = true;
  94. else if (in) {
  95. filenames[STDIN_] = t; in = false;
  96. }
  97. else if (out) {
  98. filenames[STDOUT_] = t; out = false;
  99. }
  100. else if (err) {
  101. filenames[STDERR_] = t; err = false;
  102. }
  103. else
  104. arguments.push_back(t);
  105. }
  106. return *this;
  107. }
  108. bool Child::execute() {
  109. bool stop = {false};
  110. redirect_std_if (filenames[STDIN_], STDIN_);
  111. redirect_std_if (filenames[STDOUT_], STDOUT_);
  112. redirect_std_if (filenames[STDERR_], STDERR_);
  113. pid_t pid = fork();
  114. if (pid < 0) {
  115. throw Error("Child::execute(): Can not create child process");
  116. }
  117. else if (pid == 0) { // child
  118. execvp(arguments.front(), arguments.data());
  119. // error if we got here
  120. std::cout << "Can not invoke: " << arguments.front() << std::endl;
  121. throw Error("Child::execute(): Can not run child process");
  122. }
  123. else { // parent
  124. int exit_status;
  125. waitpid(pid, &exit_status, 0); // wait child process to finish
  126. restore_std_if (STDIN_);
  127. restore_std_if (STDOUT_);
  128. restore_std_if (STDERR_);
  129. switch (logic) {
  130. case LogicOp::AND:
  131. if (exit_status) stop = true;
  132. break;
  133. case LogicOp::OR:
  134. if (!exit_status) stop = true;
  135. break;
  136. default: break;
  137. }
  138. }
  139. return stop;
  140. }
  141. /*
  142. * ======== Sequencer ===========
  143. */
  144. Sequencer& Sequencer::parse (const std::string& input) {
  145. std::vector<std::string> commands;
  146. std::vector<std::string> tokens;
  147. Child::command_t command;
  148. split (input, commands, ';'); // split all commands
  149. for (auto& s : commands) {
  150. command.clear(); //clear child tokens
  151. tokens.clear(); // make a new token vector for command
  152. split (s, tokens, ' ');
  153. seq_.emplace_back(std::vector<Child>{}); // make a new child for command
  154. // check tokens inside command
  155. for (auto& t: tokens) {
  156. if (is_pipe(t)) {
  157. }
  158. command.push_back(t); // get current token
  159. if (is_seperator(t)) {
  160. // construct a child with what we have so far and clear command
  161. seq_.back().emplace_back(command);
  162. command.clear();
  163. }
  164. }
  165. seq_.back().emplace_back(command); // construct the child with tokens
  166. }
  167. return *this;
  168. }
  169. Sequencer& Sequencer::execute() {
  170. for (auto& batch : seq_)
  171. for (auto& command: batch)
  172. if (command.make_arguments().execute())
  173. break;
  174. seq_.clear();
  175. return *this;
  176. }
  177. }