TTY.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  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 <Kernel/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. namespace Kernel {
  33. TTY::TTY(unsigned major, unsigned minor)
  34. : CharacterDevice(major, minor)
  35. {
  36. set_default_termios();
  37. }
  38. TTY::~TTY()
  39. {
  40. }
  41. void TTY::set_default_termios()
  42. {
  43. memset(&m_termios, 0, sizeof(m_termios));
  44. m_termios.c_lflag |= ISIG | ECHO | ICANON;
  45. static const char default_cc[32] = "\003\034\010\025\004\0\1\0\021\023\032\0\022\017\027\026\0";
  46. memcpy(m_termios.c_cc, default_cc, sizeof(default_cc));
  47. }
  48. KResultOr<size_t> TTY::read(FileDescription&, size_t, u8* buffer, size_t size)
  49. {
  50. if (Process::current()->pgid() != pgid()) {
  51. // FIXME: Should we propigate this error path somehow?
  52. (void)Process::current()->send_signal(SIGTTIN, nullptr);
  53. return KResult(-EINTR);
  54. }
  55. if (m_input_buffer.size() < static_cast<size_t>(size))
  56. size = m_input_buffer.size();
  57. if (in_canonical_mode()) {
  58. size_t i = 0;
  59. for (; i < size; i++) {
  60. u8 ch = m_input_buffer.dequeue();
  61. if (ch == '\0') {
  62. //Here we handle a ^D line, so we don't add the
  63. //character to the output.
  64. m_available_lines--;
  65. break;
  66. } else if (ch == '\n' || is_eol(ch)) {
  67. buffer[i] = ch;
  68. i++;
  69. m_available_lines--;
  70. break;
  71. }
  72. buffer[i] = ch;
  73. }
  74. return i;
  75. }
  76. for (size_t i = 0; i < size; i++)
  77. buffer[i] = m_input_buffer.dequeue();
  78. return size;
  79. }
  80. KResultOr<size_t> TTY::write(FileDescription&, size_t, const u8* buffer, size_t size)
  81. {
  82. if (Process::current()->pgid() != pgid()) {
  83. // FIXME: Should we propigate this error path somehow?
  84. (void)Process::current()->send_signal(SIGTTOU, nullptr);
  85. return KResult(-EINTR);
  86. }
  87. on_tty_write(buffer, size);
  88. return size;
  89. }
  90. bool TTY::can_read(const FileDescription&, size_t) const
  91. {
  92. if (in_canonical_mode()) {
  93. return m_available_lines > 0;
  94. }
  95. return !m_input_buffer.is_empty();
  96. }
  97. bool TTY::can_write(const FileDescription&, size_t) const
  98. {
  99. return true;
  100. }
  101. bool TTY::is_eol(u8 ch) const
  102. {
  103. return ch == m_termios.c_cc[VEOL];
  104. }
  105. bool TTY::is_eof(u8 ch) const
  106. {
  107. return ch == m_termios.c_cc[VEOF];
  108. }
  109. bool TTY::is_kill(u8 ch) const
  110. {
  111. return ch == m_termios.c_cc[VKILL];
  112. }
  113. bool TTY::is_erase(u8 ch) const
  114. {
  115. return ch == m_termios.c_cc[VERASE];
  116. }
  117. bool TTY::is_werase(u8 ch) const
  118. {
  119. return ch == m_termios.c_cc[VWERASE];
  120. }
  121. void TTY::emit(u8 ch)
  122. {
  123. if (should_generate_signals()) {
  124. if (ch == m_termios.c_cc[VINTR]) {
  125. dbg() << tty_name() << ": VINTR pressed!";
  126. generate_signal(SIGINT);
  127. return;
  128. }
  129. if (ch == m_termios.c_cc[VQUIT]) {
  130. dbg() << tty_name() << ": VQUIT pressed!";
  131. generate_signal(SIGQUIT);
  132. return;
  133. }
  134. if (ch == m_termios.c_cc[VSUSP]) {
  135. dbg() << tty_name() << ": VSUSP pressed!";
  136. generate_signal(SIGTSTP);
  137. if (auto process = Process::from_pid(m_pgid)) {
  138. if (auto parent = Process::from_pid(process->ppid()))
  139. (void)parent->send_signal(SIGCHLD, process);
  140. }
  141. return;
  142. }
  143. }
  144. if (in_canonical_mode()) {
  145. if (is_eof(ch)) {
  146. m_available_lines++;
  147. //We use '\0' to delimit the end
  148. //of a line.
  149. m_input_buffer.enqueue('\0');
  150. return;
  151. }
  152. if (is_kill(ch)) {
  153. kill_line();
  154. return;
  155. }
  156. if (is_erase(ch)) {
  157. do_backspace();
  158. return;
  159. }
  160. if (is_werase(ch)) {
  161. erase_word();
  162. return;
  163. }
  164. if (ch == '\n' || is_eol(ch)) {
  165. m_available_lines++;
  166. }
  167. }
  168. m_input_buffer.enqueue(ch);
  169. echo(ch);
  170. }
  171. bool TTY::can_do_backspace() const
  172. {
  173. //can't do back space if we're empty. Plus, we don't want to
  174. //removing any lines "commited" by newlines or ^D.
  175. if (!m_input_buffer.is_empty() && !is_eol(m_input_buffer.last()) && m_input_buffer.last() != '\0') {
  176. return true;
  177. }
  178. return false;
  179. }
  180. void TTY::do_backspace()
  181. {
  182. if (can_do_backspace()) {
  183. m_input_buffer.dequeue_end();
  184. echo(8);
  185. echo(' ');
  186. echo(8);
  187. }
  188. }
  189. // TODO: Currently, both erase_word() and kill_line work by sending
  190. // a lot of VERASE characters; this is done because Terminal.cpp
  191. // doesn't currently support VWERASE and VKILL. When these are
  192. // implemented we could just send a VKILL or VWERASE.
  193. void TTY::erase_word()
  194. {
  195. //Note: if we have leading whitespace before the word
  196. //we want to delete we have to also delete that.
  197. bool first_char = false;
  198. while (can_do_backspace()) {
  199. u8 ch = m_input_buffer.last();
  200. if (ch == ' ' && first_char)
  201. break;
  202. if (ch != ' ')
  203. first_char = true;
  204. m_input_buffer.dequeue_end();
  205. erase_character();
  206. }
  207. }
  208. void TTY::kill_line()
  209. {
  210. while (can_do_backspace()) {
  211. m_input_buffer.dequeue_end();
  212. erase_character();
  213. }
  214. }
  215. void TTY::erase_character()
  216. {
  217. echo(m_termios.c_cc[VERASE]);
  218. echo(' ');
  219. echo(m_termios.c_cc[VERASE]);
  220. }
  221. void TTY::generate_signal(int signal)
  222. {
  223. if (!pgid())
  224. return;
  225. if (should_flush_on_signal())
  226. flush_input();
  227. dbg() << tty_name() << ": Send signal " << signal << " to everyone in pgrp " << pgid();
  228. InterruptDisabler disabler; // FIXME: Iterate over a set of process handles instead?
  229. Process::for_each_in_pgrp(pgid(), [&](auto& process) {
  230. dbg() << tty_name() << ": Send signal " << signal << " to " << process;
  231. // FIXME: Should this error be propagated somehow?
  232. (void)process.send_signal(signal, nullptr);
  233. return IterationDecision::Continue;
  234. });
  235. }
  236. void TTY::flush_input()
  237. {
  238. m_available_lines = 0;
  239. m_input_buffer.clear();
  240. }
  241. void TTY::set_termios(const termios& t)
  242. {
  243. m_termios = t;
  244. #ifdef TTY_DEBUG
  245. dbg() << tty_name() << " set_termios: "
  246. << "ECHO=" << should_echo_input()
  247. << ", ISIG=" << should_generate_signals()
  248. << ", ICANON=" << in_canonical_mode()
  249. << ", ECHOE=" << ((m_termios.c_lflag & ECHOE) != 0)
  250. << ", ECHOK=" << ((m_termios.c_lflag & ECHOK) != 0)
  251. << ", ECHONL=" << ((m_termios.c_lflag & ECHONL) != 0)
  252. << ", ISTRIP=" << ((m_termios.c_iflag & ISTRIP) != 0)
  253. << ", ICRNL=" << ((m_termios.c_iflag & ICRNL) != 0)
  254. << ", INLCR=" << ((m_termios.c_iflag & INLCR) != 0)
  255. << ", IGNCR=" << ((m_termios.c_iflag & IGNCR) != 0);
  256. #endif
  257. }
  258. int TTY::ioctl(FileDescription&, unsigned request, FlatPtr arg)
  259. {
  260. REQUIRE_PROMISE(tty);
  261. auto& current_process = *Process::current();
  262. pid_t pgid;
  263. termios* user_termios;
  264. winsize* user_winsize;
  265. #if 0
  266. // FIXME: When should we block things?
  267. // How do we make this work together with MasterPTY forwarding to us?
  268. if (current_process.tty() && current_process.tty() != this) {
  269. return -ENOTTY;
  270. }
  271. #endif
  272. switch (request) {
  273. case TIOCGPGRP:
  274. return m_pgid;
  275. case TIOCSPGRP:
  276. pgid = static_cast<pid_t>(arg);
  277. if (pgid <= 0)
  278. return -EINVAL;
  279. {
  280. InterruptDisabler disabler;
  281. auto process = Process::from_pid(pgid);
  282. if (!process)
  283. return -EPERM;
  284. if (pgid != process->pgid())
  285. return -EPERM;
  286. if (current_process.sid() != process->sid())
  287. return -EPERM;
  288. }
  289. m_pgid = pgid;
  290. return 0;
  291. case TCGETS: {
  292. user_termios = reinterpret_cast<termios*>(arg);
  293. if (!current_process.validate_write(user_termios, sizeof(termios)))
  294. return -EFAULT;
  295. copy_to_user(user_termios, &m_termios);
  296. return 0;
  297. }
  298. case TCSETS:
  299. case TCSETSF:
  300. case TCSETSW: {
  301. user_termios = reinterpret_cast<termios*>(arg);
  302. if (!current_process.validate_read(user_termios, sizeof(termios)))
  303. return -EFAULT;
  304. termios termios;
  305. copy_from_user(&termios, user_termios);
  306. set_termios(termios);
  307. if (request == TCSETSF)
  308. flush_input();
  309. return 0;
  310. }
  311. case TCFLSH:
  312. // Serenity's TTY implementation does not use an output buffer, so ignore TCOFLUSH.
  313. if (arg == TCIFLUSH || arg == TCIOFLUSH) {
  314. flush_input();
  315. } else if (arg != TCOFLUSH) {
  316. return -EINVAL;
  317. }
  318. return 0;
  319. case TIOCGWINSZ:
  320. user_winsize = reinterpret_cast<winsize*>(arg);
  321. if (!current_process.validate_write(user_winsize, sizeof(winsize)))
  322. return -EFAULT;
  323. winsize ws;
  324. ws.ws_row = m_rows;
  325. ws.ws_col = m_columns;
  326. ws.ws_xpixel = 0;
  327. ws.ws_ypixel = 0;
  328. copy_to_user(user_winsize, &ws);
  329. return 0;
  330. case TIOCSWINSZ: {
  331. user_winsize = reinterpret_cast<winsize*>(arg);
  332. if (!current_process.validate_read(user_winsize, sizeof(winsize)))
  333. return -EFAULT;
  334. winsize ws;
  335. copy_from_user(&ws, user_winsize);
  336. if (ws.ws_col == m_columns && ws.ws_row == m_rows)
  337. return 0;
  338. m_rows = ws.ws_row;
  339. m_columns = ws.ws_col;
  340. generate_signal(SIGWINCH);
  341. return 0;
  342. }
  343. case TIOCSCTTY:
  344. current_process.set_tty(this);
  345. return 0;
  346. case TIOCNOTTY:
  347. current_process.set_tty(nullptr);
  348. return 0;
  349. }
  350. ASSERT_NOT_REACHED();
  351. return -EINVAL;
  352. }
  353. void TTY::set_size(unsigned short columns, unsigned short rows)
  354. {
  355. m_rows = rows;
  356. m_columns = columns;
  357. }
  358. void TTY::hang_up()
  359. {
  360. generate_signal(SIGHUP);
  361. }
  362. }