TTY.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. #include "Process.h"
  2. #include <Kernel/TTY/TTY.h>
  3. #include <LibC/errno_numbers.h>
  4. #include <LibC/signal_numbers.h>
  5. #include <LibC/sys/ioctl_numbers.h>
  6. //#define TTY_DEBUG
  7. TTY::TTY(unsigned major, unsigned minor)
  8. : CharacterDevice(major, minor)
  9. {
  10. set_default_termios();
  11. }
  12. TTY::~TTY()
  13. {
  14. }
  15. void TTY::set_default_termios()
  16. {
  17. memset(&m_termios, 0, sizeof(m_termios));
  18. m_termios.c_lflag |= ISIG | ECHO | ICANON;
  19. static const char default_cc[32] = "\003\034\010\025\004\0\1\0\021\023\032\0\022\017\027\026\0";
  20. memcpy(m_termios.c_cc, default_cc, sizeof(default_cc));
  21. }
  22. ssize_t TTY::read(FileDescription&, u8* buffer, ssize_t size)
  23. {
  24. if (m_input_buffer.size() < size)
  25. size = m_input_buffer.size();
  26. if (in_canonical_mode()) {
  27. int i = 0;
  28. for (; i < size; i++) {
  29. u8 ch = m_input_buffer.dequeue();
  30. if (ch == '\0') {
  31. //Here we handle a ^D line, so we don't add the
  32. //character to the output.
  33. m_available_lines--;
  34. break;
  35. } else if (ch == '\n' || is_eol(ch)) {
  36. buffer[i] = ch;
  37. i++;
  38. m_available_lines--;
  39. break;
  40. }
  41. buffer[i] = ch;
  42. }
  43. return i;
  44. }
  45. for (int i = 0; i < size; i++)
  46. buffer[i] = m_input_buffer.dequeue();
  47. return size;
  48. }
  49. ssize_t TTY::write(FileDescription&, const u8* buffer, ssize_t size)
  50. {
  51. #ifdef TTY_DEBUG
  52. dbgprintf("TTY::write {%u} ", size);
  53. for (size_t i = 0; i < size; ++i) {
  54. dbgprintf("%b ", buffer[i]);
  55. }
  56. dbgprintf("\n");
  57. #endif
  58. on_tty_write(buffer, size);
  59. return size;
  60. }
  61. bool TTY::can_read(FileDescription&) const
  62. {
  63. if (in_canonical_mode()) {
  64. return m_available_lines > 0;
  65. }
  66. return !m_input_buffer.is_empty();
  67. }
  68. bool TTY::can_write(FileDescription&) const
  69. {
  70. return true;
  71. }
  72. bool TTY::is_eol(u8 ch) const
  73. {
  74. return ch == m_termios.c_cc[VEOL];
  75. }
  76. bool TTY::is_eof(u8 ch) const
  77. {
  78. return ch == m_termios.c_cc[VEOF];
  79. }
  80. bool TTY::is_kill(u8 ch) const
  81. {
  82. return ch == m_termios.c_cc[VKILL];
  83. }
  84. bool TTY::is_erase(u8 ch) const
  85. {
  86. return ch == m_termios.c_cc[VERASE];
  87. }
  88. bool TTY::is_werase(u8 ch) const
  89. {
  90. return ch == m_termios.c_cc[VWERASE];
  91. }
  92. void TTY::emit(u8 ch)
  93. {
  94. if (should_generate_signals()) {
  95. if (ch == m_termios.c_cc[VINTR]) {
  96. dbg() << tty_name() << ": VINTR pressed!";
  97. generate_signal(SIGINT);
  98. return;
  99. }
  100. if (ch == m_termios.c_cc[VQUIT]) {
  101. dbg() << tty_name() << ": VQUIT pressed!";
  102. generate_signal(SIGQUIT);
  103. return;
  104. }
  105. if (ch == m_termios.c_cc[VSUSP]) {
  106. dbg() << tty_name() << ": VSUSP pressed!";
  107. generate_signal(SIGTSTP);
  108. return;
  109. }
  110. }
  111. if (in_canonical_mode()) {
  112. if (is_eof(ch)) {
  113. m_available_lines++;
  114. //We use '\0' to delimit the end
  115. //of a line.
  116. m_input_buffer.enqueue('\0');
  117. return;
  118. }
  119. if (is_kill(ch)) {
  120. kill_line();
  121. return;
  122. }
  123. if (is_erase(ch)) {
  124. do_backspace();
  125. return;
  126. }
  127. if (is_werase(ch)) {
  128. erase_word();
  129. return;
  130. }
  131. if (ch == '\n' || is_eol(ch)) {
  132. m_available_lines++;
  133. }
  134. }
  135. m_input_buffer.enqueue(ch);
  136. echo(ch);
  137. }
  138. bool TTY::can_do_backspace() const
  139. {
  140. //can't do back space if we're empty. Plus, we don't want to
  141. //removing any lines "commited" by newlines or ^D.
  142. if (!m_input_buffer.is_empty() && !is_eol(m_input_buffer.last()) && m_input_buffer.last() != '\0') {
  143. return true;
  144. }
  145. return false;
  146. }
  147. void TTY::do_backspace()
  148. {
  149. if (can_do_backspace()) {
  150. m_input_buffer.dequeue_end();
  151. echo(m_termios.c_cc[VERASE]);
  152. }
  153. }
  154. // TODO: Currently, both erase_word() and kill_line work by sending
  155. // a lot of VERASE characters; this is done because Terminal.cpp
  156. // doesn't currently support VWERASE and VKILL. When these are
  157. // implemented we could just send a VKILL or VWERASE.
  158. void TTY::erase_word()
  159. {
  160. //Note: if we have leading whitespace before the word
  161. //we want to delete we have to also delete that.
  162. bool first_char = false;
  163. while (can_do_backspace()) {
  164. u8 ch = m_input_buffer.last();
  165. if (ch == ' ' && first_char)
  166. break;
  167. if (ch != ' ')
  168. first_char = true;
  169. m_input_buffer.dequeue_end();
  170. echo(m_termios.c_cc[VERASE]);
  171. }
  172. }
  173. void TTY::kill_line()
  174. {
  175. while (can_do_backspace()) {
  176. m_input_buffer.dequeue_end();
  177. echo(m_termios.c_cc[VERASE]);
  178. }
  179. }
  180. void TTY::generate_signal(int signal)
  181. {
  182. if (!pgid())
  183. return;
  184. if (should_flush_on_signal())
  185. flush_input();
  186. dbg() << tty_name() << ": Send signal " << signal << " to everyone in pgrp " << pgid();
  187. InterruptDisabler disabler; // FIXME: Iterate over a set of process handles instead?
  188. Process::for_each_in_pgrp(pgid(), [&](auto& process) {
  189. dbg() << tty_name() << ": Send signal " << signal << " to " << process;
  190. process.send_signal(signal, nullptr);
  191. return IterationDecision::Continue;
  192. });
  193. }
  194. void TTY::flush_input()
  195. {
  196. m_available_lines = 0;
  197. m_input_buffer.clear();
  198. }
  199. void TTY::set_termios(const termios& t)
  200. {
  201. m_termios = t;
  202. dbg() << tty_name() << " set_termios: "
  203. << "ECHO=" << should_echo_input()
  204. << ", ISIG=" << should_generate_signals()
  205. << ", ICANON=" << in_canonical_mode()
  206. << ", ECHOE=" << ((m_termios.c_lflag & ECHOE) != 0)
  207. << ", ECHOK=" << ((m_termios.c_lflag & ECHOK) != 0)
  208. << ", ECHONL=" << ((m_termios.c_lflag & ECHONL) != 0)
  209. << ", ISTRIP=" << ((m_termios.c_iflag & ISTRIP) != 0)
  210. << ", ICRNL=" << ((m_termios.c_iflag & ICRNL) != 0)
  211. << ", INLCR=" << ((m_termios.c_iflag & INLCR) != 0)
  212. << ", IGNCR=" << ((m_termios.c_iflag & IGNCR) != 0);
  213. }
  214. int TTY::ioctl(FileDescription&, unsigned request, unsigned arg)
  215. {
  216. auto& process = current->process();
  217. pid_t pgid;
  218. termios* tp;
  219. winsize* ws;
  220. #if 0
  221. // FIXME: When should we block things?
  222. // How do we make this work together with MasterPTY forwarding to us?
  223. if (process.tty() && process.tty() != this) {
  224. return -ENOTTY;
  225. }
  226. #endif
  227. switch (request) {
  228. case TIOCGPGRP:
  229. return m_pgid;
  230. case TIOCSPGRP:
  231. // FIXME: Validate pgid fully.
  232. pgid = static_cast<pid_t>(arg);
  233. if (pgid < 0)
  234. return -EINVAL;
  235. m_pgid = pgid;
  236. return 0;
  237. case TCGETS:
  238. tp = reinterpret_cast<termios*>(arg);
  239. if (!process.validate_write(tp, sizeof(termios)))
  240. return -EFAULT;
  241. *tp = m_termios;
  242. return 0;
  243. case TCSETS:
  244. case TCSETSF:
  245. case TCSETSW:
  246. tp = reinterpret_cast<termios*>(arg);
  247. if (!process.validate_read(tp, sizeof(termios)))
  248. return -EFAULT;
  249. set_termios(*tp);
  250. return 0;
  251. case TIOCGWINSZ:
  252. ws = reinterpret_cast<winsize*>(arg);
  253. if (!process.validate_write(ws, sizeof(winsize)))
  254. return -EFAULT;
  255. ws->ws_row = m_rows;
  256. ws->ws_col = m_columns;
  257. return 0;
  258. case TIOCSWINSZ:
  259. ws = reinterpret_cast<winsize*>(arg);
  260. if (!process.validate_read(ws, sizeof(winsize)))
  261. return -EFAULT;
  262. if (ws->ws_col == m_columns && ws->ws_row == m_rows)
  263. return 0;
  264. m_rows = ws->ws_row;
  265. m_columns = ws->ws_col;
  266. generate_signal(SIGWINCH);
  267. return 0;
  268. case TIOCSCTTY:
  269. process.set_tty(this);
  270. return 0;
  271. case TIOCNOTTY:
  272. process.set_tty(nullptr);
  273. return 0;
  274. }
  275. ASSERT_NOT_REACHED();
  276. return -EINVAL;
  277. }
  278. void TTY::set_size(unsigned short columns, unsigned short rows)
  279. {
  280. m_rows = rows;
  281. m_columns = columns;
  282. }
  283. void TTY::hang_up()
  284. {
  285. generate_signal(SIGHUP);
  286. }