TTY.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include "Process.h"
  27. #include <Kernel/TTY/TTY.h>
  28. #include <LibC/errno_numbers.h>
  29. #include <LibC/signal_numbers.h>
  30. #include <LibC/sys/ioctl_numbers.h>
  31. //#define TTY_DEBUG
  32. TTY::TTY(unsigned major, unsigned minor)
  33. : CharacterDevice(major, minor)
  34. {
  35. set_default_termios();
  36. }
  37. TTY::~TTY()
  38. {
  39. }
  40. void TTY::set_default_termios()
  41. {
  42. memset(&m_termios, 0, sizeof(m_termios));
  43. m_termios.c_lflag |= ISIG | ECHO | ICANON;
  44. static const char default_cc[32] = "\003\034\010\025\004\0\1\0\021\023\032\0\022\017\027\026\0";
  45. memcpy(m_termios.c_cc, default_cc, sizeof(default_cc));
  46. }
  47. ssize_t TTY::read(FileDescription&, u8* buffer, ssize_t size)
  48. {
  49. if (m_input_buffer.size() < size)
  50. size = m_input_buffer.size();
  51. if (in_canonical_mode()) {
  52. int i = 0;
  53. for (; i < size; i++) {
  54. u8 ch = m_input_buffer.dequeue();
  55. if (ch == '\0') {
  56. //Here we handle a ^D line, so we don't add the
  57. //character to the output.
  58. m_available_lines--;
  59. break;
  60. } else if (ch == '\n' || is_eol(ch)) {
  61. buffer[i] = ch;
  62. i++;
  63. m_available_lines--;
  64. break;
  65. }
  66. buffer[i] = ch;
  67. }
  68. return i;
  69. }
  70. for (int i = 0; i < size; i++)
  71. buffer[i] = m_input_buffer.dequeue();
  72. return size;
  73. }
  74. ssize_t TTY::write(FileDescription&, const u8* buffer, ssize_t size)
  75. {
  76. #ifdef TTY_DEBUG
  77. dbgprintf("TTY::write {%u} ", size);
  78. for (size_t i = 0; i < size; ++i) {
  79. dbgprintf("%b ", buffer[i]);
  80. }
  81. dbgprintf("\n");
  82. #endif
  83. on_tty_write(buffer, size);
  84. return size;
  85. }
  86. bool TTY::can_read(const FileDescription&) const
  87. {
  88. if (in_canonical_mode()) {
  89. return m_available_lines > 0;
  90. }
  91. return !m_input_buffer.is_empty();
  92. }
  93. bool TTY::can_write(const FileDescription&) const
  94. {
  95. return true;
  96. }
  97. bool TTY::is_eol(u8 ch) const
  98. {
  99. return ch == m_termios.c_cc[VEOL];
  100. }
  101. bool TTY::is_eof(u8 ch) const
  102. {
  103. return ch == m_termios.c_cc[VEOF];
  104. }
  105. bool TTY::is_kill(u8 ch) const
  106. {
  107. return ch == m_termios.c_cc[VKILL];
  108. }
  109. bool TTY::is_erase(u8 ch) const
  110. {
  111. return ch == m_termios.c_cc[VERASE];
  112. }
  113. bool TTY::is_werase(u8 ch) const
  114. {
  115. return ch == m_termios.c_cc[VWERASE];
  116. }
  117. void TTY::emit(u8 ch)
  118. {
  119. if (should_generate_signals()) {
  120. if (ch == m_termios.c_cc[VINTR]) {
  121. dbg() << tty_name() << ": VINTR pressed!";
  122. generate_signal(SIGINT);
  123. return;
  124. }
  125. if (ch == m_termios.c_cc[VQUIT]) {
  126. dbg() << tty_name() << ": VQUIT pressed!";
  127. generate_signal(SIGQUIT);
  128. return;
  129. }
  130. if (ch == m_termios.c_cc[VSUSP]) {
  131. dbg() << tty_name() << ": VSUSP pressed!";
  132. generate_signal(SIGTSTP);
  133. return;
  134. }
  135. }
  136. if (in_canonical_mode()) {
  137. if (is_eof(ch)) {
  138. m_available_lines++;
  139. //We use '\0' to delimit the end
  140. //of a line.
  141. m_input_buffer.enqueue('\0');
  142. return;
  143. }
  144. if (is_kill(ch)) {
  145. kill_line();
  146. return;
  147. }
  148. if (is_erase(ch)) {
  149. do_backspace();
  150. return;
  151. }
  152. if (is_werase(ch)) {
  153. erase_word();
  154. return;
  155. }
  156. if (ch == '\n' || is_eol(ch)) {
  157. m_available_lines++;
  158. }
  159. }
  160. m_input_buffer.enqueue(ch);
  161. echo(ch);
  162. }
  163. bool TTY::can_do_backspace() const
  164. {
  165. //can't do back space if we're empty. Plus, we don't want to
  166. //removing any lines "commited" by newlines or ^D.
  167. if (!m_input_buffer.is_empty() && !is_eol(m_input_buffer.last()) && m_input_buffer.last() != '\0') {
  168. return true;
  169. }
  170. return false;
  171. }
  172. void TTY::do_backspace()
  173. {
  174. if (can_do_backspace()) {
  175. m_input_buffer.dequeue_end();
  176. echo(8);
  177. echo(' ');
  178. echo(8);
  179. }
  180. }
  181. // TODO: Currently, both erase_word() and kill_line work by sending
  182. // a lot of VERASE characters; this is done because Terminal.cpp
  183. // doesn't currently support VWERASE and VKILL. When these are
  184. // implemented we could just send a VKILL or VWERASE.
  185. void TTY::erase_word()
  186. {
  187. //Note: if we have leading whitespace before the word
  188. //we want to delete we have to also delete that.
  189. bool first_char = false;
  190. while (can_do_backspace()) {
  191. u8 ch = m_input_buffer.last();
  192. if (ch == ' ' && first_char)
  193. break;
  194. if (ch != ' ')
  195. first_char = true;
  196. m_input_buffer.dequeue_end();
  197. echo(m_termios.c_cc[VERASE]);
  198. }
  199. }
  200. void TTY::kill_line()
  201. {
  202. while (can_do_backspace()) {
  203. m_input_buffer.dequeue_end();
  204. echo(m_termios.c_cc[VERASE]);
  205. }
  206. }
  207. void TTY::generate_signal(int signal)
  208. {
  209. if (!pgid())
  210. return;
  211. if (should_flush_on_signal())
  212. flush_input();
  213. dbg() << tty_name() << ": Send signal " << signal << " to everyone in pgrp " << pgid();
  214. InterruptDisabler disabler; // FIXME: Iterate over a set of process handles instead?
  215. Process::for_each_in_pgrp(pgid(), [&](auto& process) {
  216. dbg() << tty_name() << ": Send signal " << signal << " to " << process;
  217. process.send_signal(signal, nullptr);
  218. return IterationDecision::Continue;
  219. });
  220. }
  221. void TTY::flush_input()
  222. {
  223. m_available_lines = 0;
  224. m_input_buffer.clear();
  225. }
  226. void TTY::set_termios(const termios& t)
  227. {
  228. m_termios = t;
  229. #ifdef TTY_DEBUG
  230. dbg() << tty_name() << " set_termios: "
  231. << "ECHO=" << should_echo_input()
  232. << ", ISIG=" << should_generate_signals()
  233. << ", ICANON=" << in_canonical_mode()
  234. << ", ECHOE=" << ((m_termios.c_lflag & ECHOE) != 0)
  235. << ", ECHOK=" << ((m_termios.c_lflag & ECHOK) != 0)
  236. << ", ECHONL=" << ((m_termios.c_lflag & ECHONL) != 0)
  237. << ", ISTRIP=" << ((m_termios.c_iflag & ISTRIP) != 0)
  238. << ", ICRNL=" << ((m_termios.c_iflag & ICRNL) != 0)
  239. << ", INLCR=" << ((m_termios.c_iflag & INLCR) != 0)
  240. << ", IGNCR=" << ((m_termios.c_iflag & IGNCR) != 0);
  241. #endif
  242. }
  243. int TTY::ioctl(FileDescription&, unsigned request, unsigned arg)
  244. {
  245. REQUIRE_PROMISE(tty);
  246. auto& process = current->process();
  247. pid_t pgid;
  248. termios* tp;
  249. winsize* ws;
  250. #if 0
  251. // FIXME: When should we block things?
  252. // How do we make this work together with MasterPTY forwarding to us?
  253. if (process.tty() && process.tty() != this) {
  254. return -ENOTTY;
  255. }
  256. #endif
  257. switch (request) {
  258. case TIOCGPGRP:
  259. return m_pgid;
  260. case TIOCSPGRP:
  261. // FIXME: Validate pgid fully.
  262. pgid = static_cast<pid_t>(arg);
  263. if (pgid < 0)
  264. return -EINVAL;
  265. m_pgid = pgid;
  266. return 0;
  267. case TCGETS:
  268. tp = reinterpret_cast<termios*>(arg);
  269. if (!process.validate_write(tp, sizeof(termios)))
  270. return -EFAULT;
  271. *tp = m_termios;
  272. return 0;
  273. case TCSETS:
  274. case TCSETSF:
  275. case TCSETSW:
  276. tp = reinterpret_cast<termios*>(arg);
  277. if (!process.validate_read(tp, sizeof(termios)))
  278. return -EFAULT;
  279. set_termios(*tp);
  280. return 0;
  281. case TIOCGWINSZ:
  282. ws = reinterpret_cast<winsize*>(arg);
  283. if (!process.validate_write(ws, sizeof(winsize)))
  284. return -EFAULT;
  285. ws->ws_row = m_rows;
  286. ws->ws_col = m_columns;
  287. return 0;
  288. case TIOCSWINSZ:
  289. ws = reinterpret_cast<winsize*>(arg);
  290. if (!process.validate_read(ws, sizeof(winsize)))
  291. return -EFAULT;
  292. if (ws->ws_col == m_columns && ws->ws_row == m_rows)
  293. return 0;
  294. m_rows = ws->ws_row;
  295. m_columns = ws->ws_col;
  296. generate_signal(SIGWINCH);
  297. return 0;
  298. case TIOCSCTTY:
  299. process.set_tty(this);
  300. return 0;
  301. case TIOCNOTTY:
  302. process.set_tty(nullptr);
  303. return 0;
  304. }
  305. ASSERT_NOT_REACHED();
  306. return -EINVAL;
  307. }
  308. void TTY::set_size(unsigned short columns, unsigned short rows)
  309. {
  310. m_rows = rows;
  311. m_columns = columns;
  312. }
  313. void TTY::hang_up()
  314. {
  315. generate_signal(SIGHUP);
  316. }