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.
 
 
 

188 Zeilen
6.2 KiB

  1. /*!
  2. * \file
  3. * Sequencer.h
  4. * \brief A basic sequence interpreter for snel
  5. *
  6. * Created on: Feb, 2019
  7. * Author: Christos Choutouridis AEM: 8997
  8. * email : cchoutou@ece.auth.gr
  9. */
  10. #ifndef __sequencer_h__
  11. #define __sequencer_h__
  12. #include <exception>
  13. #include <string>
  14. #include <iostream>
  15. #include <sstream>
  16. #include <fstream>
  17. #include <vector>
  18. #include <utility>
  19. #include <algorithm>
  20. #include <unistd.h>
  21. #include <fcntl.h>
  22. #include <sys/wait.h>
  23. namespace snel {
  24. //! file descriptor type
  25. using fd_t = int;
  26. constexpr fd_t STDIN_ = STDIN_FILENO;
  27. constexpr fd_t STDOUT_ = STDOUT_FILENO;
  28. constexpr fd_t STDERR_ = STDERR_FILENO;
  29. std::string filter (const std::string in);
  30. /*!
  31. * A vector based wrapper above execvp()'s `char* argv[]` interface.
  32. * We use a vector for convenience and resizing capabilities. We can then
  33. * use a pointer to underling data to pass to execvp()
  34. * std::vector elements guaranteed to be contiguous. To quote the standard[1]
  35. * \code
  36. * The elements of a vector are stored contiguously, meaning that if v is
  37. * a vector<T, Allocator> where T is some type other than bool, then it obeys
  38. * the identity &v[n] == &v[0] + n for all 0 <= n < v.size().
  39. * \endcode
  40. *
  41. * See also cppreference[2].
  42. *
  43. * [1]: www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf
  44. * [2]: https://en.cppreference.com/w/cpp/container/vector
  45. */
  46. struct ArgList {
  47. using type = typename std::string::value_type; // Basic data type (aka char)
  48. using vtype = type*; // Vector type
  49. using vtype_ptr = vtype*; // Pointer to vector type
  50. ~ArgList();
  51. ArgList() = default;
  52. ArgList(const ArgList&) = default;
  53. ArgList(ArgList&&) = default;
  54. ArgList& push_back(const std::string& item); //!< A vector::push_back wrapper
  55. vtype front () { return args_.front(); } //!< A vector::front() wrapper
  56. size_t size () { return args_.size(); } //!< A vector::size() wrapper
  57. //! return a pointer to underling data for `execvp()`
  58. vtype_ptr data() noexcept { return args_.data(); }
  59. //! same as `data()`
  60. vtype_ptr operator*() noexcept { return data(); }
  61. private:
  62. // data
  63. std::vector<vtype> args_ {nullptr};
  64. };
  65. /*!
  66. * A pipe file descriptor container for each child.
  67. * \note
  68. * We store the pipe in the first child and use the flags `from`
  69. * and `to` to inform child's executer for where can find the data.
  70. */
  71. struct Pipe {
  72. ~Pipe();
  73. fd_t fd[2] {-1, -1};
  74. bool from {false};
  75. bool to {false};
  76. };
  77. /*!
  78. * An object to represent each process
  79. *
  80. * We create Child objects from command_t passed from Sequence::parse() and we
  81. * keep in each child informations about the file descriptors that may the process
  82. * will use, pipes that may need and logic flags(&&, ||) the command may have to control
  83. * the execution flow.
  84. */
  85. class Child {
  86. public:
  87. enum class LogicOp {
  88. NONE=0, OR, AND
  89. };
  90. using command_t = std::vector<std::string>; //!< A command type
  91. using Error = std::runtime_error; //!< An error type
  92. public:
  93. ~Child ();
  94. Child () = default;
  95. Child (const command_t& c) : command{c} { }
  96. Child (command_t&& c) : command{std::move(c)} { }
  97. Child& make_arguments (); //!< A per child parser before execution
  98. //! the actual execution routine
  99. bool execute(std::vector<Child>::iterator it, bool first);
  100. Pipe& pipe() { return pipe_; } //!< A pipe_ getter
  101. //! Private api
  102. //! @{
  103. private:
  104. void redirect_std_if(std::string fn, fd_t std_fd); //!< tool to redirect std
  105. void restore_std_if(fd_t std_fd); //!< tool to restor std redirection
  106. //! @}
  107. //! Data members
  108. //! \note
  109. //! `arguments`, `files`, `sstd` and `pipe_` have linked resources so destructor(s) must be called.
  110. //! We use RAII by having all children to static allocated vectors. '}' will do the trick even
  111. //! if we throw ;)
  112. //! @{
  113. private:
  114. command_t command {}; //!< Each command is a vector of string as passed on from Sequence::parse()
  115. ArgList arguments {}; //!< The after parsing, actual null terminated arguments to pass to execvp()
  116. fd_t files[3] = {-1, -1, -1}; //!< file descriptors for the user requested redirections
  117. fd_t sstd [3] = {-1, -1, -1}; //!< file descriptors to store std fd's before redirection
  118. std::string filenames[3] = {"", "", ""}; //!< filenames of the user requested redirections
  119. LogicOp logic {LogicOp::NONE}; //!< Logic flags to store && and || command separators
  120. Pipe pipe_; //!< Pipe object for each child
  121. //! @}
  122. };
  123. /*!
  124. * The main object to represent and handle a command flow.
  125. *
  126. * We store commands as vector of vectors of commands. The inner vector
  127. * represent a command chain. For example the line:
  128. * \code ls | more && uname; cat lala \endcode
  129. * is an input with 2 command chains and result to a:
  130. * \code
  131. * vector<
  132. * vector<(ls |), (more &&), (uname)>,
  133. * vector<(cat)>
  134. * >
  135. * \endcode
  136. * By storing them in different vector we make the execution model simpler.
  137. * Each chain can stopped and the sequencer will continue with the next chain etc...
  138. * So in this example if `more` returns false(child::execute() returned true),
  139. * then `uname` will not parsed and executed and the flow will continue to `cat`
  140. */
  141. class Sequencer {
  142. public:
  143. Sequencer& parse (const std::string& input); //!< Input line parser
  144. Sequencer& execute (); //!< Main sequencer executor
  145. //! private tools
  146. //! @{
  147. private:
  148. //! separator trait predicate
  149. bool is_seperator (std::string& s) {
  150. return (s == "&&" || s == "||" || s == "|") ? true : false;
  151. }
  152. //! pipe trait predicate
  153. bool is_pipe (std::string& s) {
  154. return (s == "|") ? true : false;
  155. }
  156. //! @}
  157. private:
  158. //! The sequencer data representation
  159. std::vector <
  160. std::vector <Child>
  161. > seq_ {};
  162. };
  163. }
  164. #endif /* __sequencer_h__ */