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.
 
 
 

187 Zeilen
6.4 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; //!< Constant for stdin file descriptor
  27. constexpr fd_t STDOUT_ = STDOUT_FILENO; //!< Constant for stdout file descriptor
  28. constexpr fd_t STDERR_ = STDERR_FILENO; //!< Constant for stderr file descriptor
  29. std::string filter (const std::string in);
  30. /*!
  31. * A vector based wrapper above <tt>execvp()'s char* argv[]</tt> 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
  35. * [standard](www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf)
  36. * <pre>
  37. * The elements of a vector are stored contiguously, meaning that if v is
  38. * a vector<T, Allocator> where T is some type other than bool, then it obeys
  39. * the identity &v[n] == &v[0] + n for all 0 <= n < v.size().
  40. * </pre>
  41. *
  42. * See also [cppreference](https://en.cppreference.com/w/cpp/container/vector)
  43. */
  44. struct ArgList {
  45. using type = typename std::string::value_type; //!< Basic data type (aka char)
  46. using vtype = type*; //!< Vector type
  47. using vtype_ptr = vtype*; //!< Pointer to vector type
  48. ~ArgList();
  49. ArgList() = default;
  50. ArgList(const ArgList&) = default;
  51. ArgList(ArgList&&) = default;
  52. ArgList& push_back(const std::string& item); //!< A vector::push_back wrapper
  53. vtype front () { return args_.front(); } //!< A vector::front() wrapper
  54. size_t size () { return args_.size(); } //!< A vector::size() wrapper
  55. //! return a pointer to underling data for \c execvp()
  56. vtype_ptr data() noexcept { return args_.data(); }
  57. //! same as \c data()
  58. vtype_ptr operator*() noexcept { return data(); }
  59. private:
  60. //! underling data for the \c execvp() arguments
  61. std::vector<vtype> args_ {nullptr};
  62. };
  63. /*!
  64. * A pipe file descriptor container for each child.
  65. * \note
  66. * We store the pipe in the first child and use the flags \c from
  67. * and \c to to inform child's executer for where can find the data.
  68. */
  69. struct Pipe {
  70. ~Pipe();
  71. fd_t fd[2] {-1, -1};
  72. bool from {false};
  73. bool to {false};
  74. };
  75. /*!
  76. * An object to represent each process
  77. *
  78. * We create Child objects from \c command_t passed to constructor from \c Sequence::parse().
  79. * In child we store information about:
  80. * * The file descriptors for std redirection the process may use
  81. * * Pipes that may needed for communication with the \b next child
  82. * * Logic flags \c (&&, ||) the command may have, to control the execution flow.
  83. */
  84. class Child {
  85. public:
  86. //! Enumerator for the logic operators between commands
  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. bool execute(std::vector<Child>::iterator it, bool first);
  99. Pipe& pipe() { return pipe_; } //!< A pipe_ getter
  100. //! Private api
  101. //! @{
  102. private:
  103. void redirect_std_if(std::string fn, fd_t std_fd); //!< Tool to redirect std
  104. void restore_std_if(fd_t std_fd); //!< Tool to restore std redirection
  105. //! @}
  106. //! Data members
  107. //! \note
  108. //! \c arguments, \c files, \c sstd and \c pipe_ have linked resources so destructor(s) must be called.
  109. //! We use RAII by having all children to static allocated vectors. '}' will do the trick even
  110. //! if we throw ;)
  111. //! @{
  112. private:
  113. command_t command {}; //!< Each command is a vector of string as passed on from Sequence::parse()
  114. ArgList arguments {}; //!< The after parsing, actual null terminated arguments to pass to execvp()
  115. fd_t files[3] = {-1, -1, -1}; //!< file descriptors for the user requested redirections
  116. fd_t sstd [3] = {-1, -1, -1}; //!< file descriptors to store std fd's before redirection
  117. std::string filenames[3] = {"", "", ""}; //!< filenames of the user requested redirections
  118. LogicOp logic {LogicOp::NONE}; //!< Logic flags to store && and || command separators
  119. Pipe pipe_; //!< Pipe object for each child
  120. //! @}
  121. };
  122. /*!
  123. * The main object to represent and handle a command flow.
  124. *
  125. * We store commands as vector of vectors of commands. The inner vector
  126. * represent a command chain. For example the line:
  127. * \code ls | more && uname; cat lala \endcode
  128. * is an input with 2 command chains and result to a:
  129. * \code
  130. * vector<
  131. * vector<(ls |), (more &&), (uname)>,
  132. * vector<(cat)>
  133. * >
  134. * \endcode
  135. * By storing them in different vector we make the execution model simpler.
  136. * Each chain can stopped and the sequencer will continue with the next chain etc...
  137. * So in this example if \c more returns false(child::execute() returned true),
  138. * then \c uname will not parsed and executed and the flow will continue to \c cat
  139. */
  140. class Sequencer {
  141. public:
  142. Sequencer& parse (const std::string& input);
  143. Sequencer& execute ();
  144. //! private tools
  145. //! @{
  146. private:
  147. //! separator trait predicate
  148. bool is_seperator (std::string& s) {
  149. return (s == "&&" || s == "||" || s == "|") ? true : false;
  150. }
  151. //! pipe trait predicate
  152. bool is_pipe (std::string& s) {
  153. return (s == "|") ? true : false;
  154. }
  155. //! @}
  156. private:
  157. //! The sequencer data representation
  158. std::vector <
  159. std::vector <Child>
  160. > seq_ {};
  161. };
  162. }
  163. #endif /* __sequencer_h__ */