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.
 
 
 

187 lines
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__ */