A quick and dirty shell implementation for A.U.TH. (Operating systems Lab)
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

sequencer.h 6.2 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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__ */