TTY.cpp 12 KB

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