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.
 
 
 

198 lines
5.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. /*
  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== " ")
  83. continue;
  84. if (t == "&&") logic = LogicOp::AND;
  85. else if (t == "||") logic = LogicOp::OR;
  86. else if (t == "<") in = true;
  87. else if (t == "1>" || t == ">") out = true;
  88. else if (t == "2>") err = true;
  89. else if (in) {
  90. filenames[STDIN_] = t; in = false;
  91. }
  92. else if (out) {
  93. filenames[STDOUT_] = t; out = false;
  94. }
  95. else if (err) {
  96. filenames[STDERR_] = t; err = false;
  97. }
  98. else
  99. arguments.push_back(t);
  100. }
  101. return *this;
  102. }
  103. bool Child::execute() {
  104. bool stop = {false};
  105. redirect_std_if (filenames[STDIN_], STDIN_);
  106. redirect_std_if (filenames[STDOUT_], STDOUT_);
  107. redirect_std_if (filenames[STDERR_], STDERR_);
  108. pid_t pid = fork();
  109. if (pid < 0) {
  110. throw Error("Child::execute(): Can not create child process");
  111. }
  112. else if (pid == 0) { // child
  113. execvp(arguments.front(), arguments.data());
  114. // error if we got here
  115. std::cout << "Can not invoke: " << arguments.front() << std::endl;
  116. throw Error("Child::execute(): Can not run child process");
  117. }
  118. else { // parent
  119. int exit_status;
  120. waitpid(pid, &exit_status, 0); // wait child process to finish
  121. restore_std_if (STDIN_);
  122. restore_std_if (STDOUT_);
  123. restore_std_if (STDERR_);
  124. switch (logic) {
  125. case LogicOp::AND:
  126. if (exit_status) stop = true;
  127. break;
  128. case LogicOp::OR:
  129. if (!exit_status) stop = true;
  130. break;
  131. default: break;
  132. }
  133. }
  134. return stop;
  135. }
  136. /*
  137. * ======== Sequencer ===========
  138. */
  139. Sequencer& Sequencer::parse (const std::string& input) {
  140. std::vector<std::string> commands;
  141. std::vector<std::string> tokens;
  142. Child::command_t command;
  143. split (input, commands, ';'); // split all commands
  144. for (auto& s : commands) {
  145. command.clear(); //clear child tokens
  146. tokens.clear(); // make a new token vector for command
  147. split (s, tokens, ' ');
  148. seq_.emplace_back(std::vector<Child>{}); // make a new child for command
  149. // check tokens inside command
  150. for (auto& t: tokens) {
  151. if (is_pipe(t)) {
  152. }
  153. command.push_back(t); // get current token
  154. if (is_seperator(t)) {
  155. // construct a child with what we have so far and clear command
  156. seq_.back().emplace_back(command);
  157. command.clear();
  158. }
  159. }
  160. seq_.back().emplace_back(command); // construct the child with tokens
  161. }
  162. return *this;
  163. }
  164. Sequencer& Sequencer::execute() {
  165. for (auto& batch : seq_)
  166. for (auto& command: batch)
  167. if (command.make_arguments().execute())
  168. break;
  169. seq_.clear();
  170. return *this;
  171. }
  172. }