TTY.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2022, the SerenityOS developers.
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/ScopeGuard.h>
  8. #include <AK/StringView.h>
  9. #include <Kernel/API/POSIX/errno.h>
  10. #include <Kernel/Arch/x86/InterruptDisabler.h>
  11. #include <Kernel/Debug.h>
  12. #include <Kernel/Process.h>
  13. #include <Kernel/TTY/TTY.h>
  14. #include <LibC/signal_numbers.h>
  15. #include <LibC/sys/ioctl_numbers.h>
  16. #define TTYDEFCHARS
  17. #include <LibC/sys/ttydefaults.h>
  18. #undef TTYDEFCHARS
  19. namespace Kernel {
  20. TTY::TTY(MajorNumber major, MinorNumber minor)
  21. : CharacterDevice(major, minor)
  22. {
  23. set_default_termios();
  24. }
  25. TTY::~TTY()
  26. {
  27. }
  28. void TTY::set_default_termios()
  29. {
  30. memset(&m_termios, 0, sizeof(m_termios));
  31. m_termios.c_iflag = TTYDEF_IFLAG;
  32. m_termios.c_oflag = TTYDEF_OFLAG;
  33. m_termios.c_cflag = TTYDEF_CFLAG;
  34. m_termios.c_lflag = TTYDEF_LFLAG;
  35. m_termios.c_ispeed = TTYDEF_SPEED;
  36. m_termios.c_ospeed = TTYDEF_SPEED;
  37. memcpy(m_termios.c_cc, ttydefchars, sizeof(ttydefchars));
  38. }
  39. ErrorOr<size_t> TTY::read(OpenFileDescription&, u64, UserOrKernelBuffer& buffer, size_t size)
  40. {
  41. if (Process::current().pgid() != pgid()) {
  42. // FIXME: Should we propagate this error path somehow?
  43. [[maybe_unused]] auto rc = Process::current().send_signal(SIGTTIN, nullptr);
  44. return EINTR;
  45. }
  46. if (m_input_buffer.size() < static_cast<size_t>(size))
  47. size = m_input_buffer.size();
  48. bool need_evaluate_block_conditions = false;
  49. auto result = buffer.write_buffered<512>(size, [&](Bytes data) {
  50. size_t bytes_written = 0;
  51. for (; bytes_written < data.size(); ++bytes_written) {
  52. auto bit_index = m_input_buffer.head_index();
  53. bool is_special_character = m_special_character_bitmask[bit_index / 8] & (1 << (bit_index % 8));
  54. if (in_canonical_mode() && is_special_character) {
  55. u8 ch = m_input_buffer.dequeue();
  56. if (ch == '\0') {
  57. // EOF
  58. m_available_lines--;
  59. need_evaluate_block_conditions = true;
  60. break;
  61. } else {
  62. // '\n' or EOL
  63. data[bytes_written++] = ch;
  64. m_available_lines--;
  65. break;
  66. }
  67. }
  68. data[bytes_written] = m_input_buffer.dequeue();
  69. }
  70. return bytes_written;
  71. });
  72. if ((!result.is_error() && result.value() > 0) || need_evaluate_block_conditions)
  73. evaluate_block_conditions();
  74. return result;
  75. }
  76. ErrorOr<size_t> TTY::write(OpenFileDescription&, u64, const UserOrKernelBuffer& buffer, size_t size)
  77. {
  78. if (m_termios.c_lflag & TOSTOP && Process::current().pgid() != pgid()) {
  79. [[maybe_unused]] auto rc = Process::current().send_signal(SIGTTOU, nullptr);
  80. return EINTR;
  81. }
  82. constexpr size_t num_chars = 256;
  83. return buffer.read_buffered<num_chars>(size, [&](ReadonlyBytes bytes) -> ErrorOr<size_t> {
  84. u8 modified_data[num_chars * 2];
  85. size_t modified_data_size = 0;
  86. for (const auto& byte : bytes) {
  87. process_output(byte, [&modified_data, &modified_data_size](u8 out_ch) {
  88. modified_data[modified_data_size++] = out_ch;
  89. });
  90. }
  91. auto bytes_written_or_error = on_tty_write(UserOrKernelBuffer::for_kernel_buffer(modified_data), modified_data_size);
  92. if (bytes_written_or_error.is_error() || !(m_termios.c_oflag & OPOST) || !(m_termios.c_oflag & ONLCR))
  93. return bytes_written_or_error;
  94. auto bytes_written = bytes_written_or_error.value();
  95. if (bytes_written == modified_data_size)
  96. return bytes.size();
  97. // Degenerate case where we converted some newlines and encountered a partial write
  98. // Calculate where in the input buffer the last character would have been
  99. size_t pos_data = 0;
  100. for (size_t pos_modified_data = 0; pos_modified_data < bytes_written; ++pos_data) {
  101. if (bytes[pos_data] == '\n')
  102. pos_modified_data += 2;
  103. else
  104. pos_modified_data += 1;
  105. // Handle case where the '\r' got written but not the '\n'
  106. // FIXME: Our strategy is to retry writing both. We should really be queuing a write for the corresponding '\n'
  107. if (pos_modified_data > bytes_written)
  108. --pos_data;
  109. }
  110. return pos_data;
  111. });
  112. }
  113. void TTY::echo_with_processing(u8 ch)
  114. {
  115. process_output(ch, [this](u8 out_ch) { echo(out_ch); });
  116. }
  117. template<typename Functor>
  118. void TTY::process_output(u8 ch, Functor put_char)
  119. {
  120. if (m_termios.c_oflag & OPOST) {
  121. if (ch == '\n' && (m_termios.c_oflag & ONLCR))
  122. put_char('\r');
  123. put_char(ch);
  124. } else {
  125. put_char(ch);
  126. }
  127. }
  128. bool TTY::can_read(const OpenFileDescription&, u64) const
  129. {
  130. if (in_canonical_mode()) {
  131. return m_available_lines > 0;
  132. }
  133. return !m_input_buffer.is_empty();
  134. }
  135. bool TTY::can_write(const OpenFileDescription&, u64) const
  136. {
  137. return true;
  138. }
  139. bool TTY::is_eol(u8 ch) const
  140. {
  141. return ch == m_termios.c_cc[VEOL];
  142. }
  143. bool TTY::is_eof(u8 ch) const
  144. {
  145. return ch == m_termios.c_cc[VEOF];
  146. }
  147. bool TTY::is_kill(u8 ch) const
  148. {
  149. return ch == m_termios.c_cc[VKILL];
  150. }
  151. bool TTY::is_erase(u8 ch) const
  152. {
  153. return ch == m_termios.c_cc[VERASE];
  154. }
  155. bool TTY::is_werase(u8 ch) const
  156. {
  157. return ch == m_termios.c_cc[VWERASE];
  158. }
  159. void TTY::emit(u8 ch, bool do_evaluate_block_conditions)
  160. {
  161. if (m_termios.c_iflag & ISTRIP)
  162. ch &= 0x7F;
  163. if (should_generate_signals()) {
  164. if (ch == m_termios.c_cc[VINFO]) {
  165. dbgln("{}: VINFO pressed!", tty_name());
  166. generate_signal(SIGINFO);
  167. return;
  168. }
  169. if (ch == m_termios.c_cc[VINTR]) {
  170. dbgln("{}: VINTR pressed!", tty_name());
  171. generate_signal(SIGINT);
  172. return;
  173. }
  174. if (ch == m_termios.c_cc[VQUIT]) {
  175. dbgln("{}: VQUIT pressed!", tty_name());
  176. generate_signal(SIGQUIT);
  177. return;
  178. }
  179. if (ch == m_termios.c_cc[VSUSP]) {
  180. dbgln("{}: VSUSP pressed!", tty_name());
  181. generate_signal(SIGTSTP);
  182. if (auto original_process_parent = m_original_process_parent.strong_ref()) {
  183. [[maybe_unused]] auto rc = original_process_parent->send_signal(SIGCHLD, nullptr);
  184. }
  185. // TODO: Else send it to the session leader maybe?
  186. return;
  187. }
  188. }
  189. ScopeGuard guard([&]() {
  190. if (do_evaluate_block_conditions)
  191. evaluate_block_conditions();
  192. });
  193. if (ch == '\r' && (m_termios.c_iflag & ICRNL))
  194. ch = '\n';
  195. else if (ch == '\n' && (m_termios.c_iflag & INLCR))
  196. ch = '\r';
  197. auto current_char_head_index = (m_input_buffer.head_index() + m_input_buffer.size()) % TTY_BUFFER_SIZE;
  198. m_special_character_bitmask[current_char_head_index / 8] &= ~(1u << (current_char_head_index % 8));
  199. auto set_special_bit = [&] {
  200. m_special_character_bitmask[current_char_head_index / 8] |= (1u << (current_char_head_index % 8));
  201. };
  202. if (in_canonical_mode()) {
  203. if (is_eof(ch)) {
  204. // Since EOF might change between when the data came in and when it is read,
  205. // we use '\0' along with the bitmask to signal EOF. Any non-zero byte with
  206. // the special bit set signals an end-of-line.
  207. set_special_bit();
  208. m_available_lines++;
  209. m_input_buffer.enqueue('\0');
  210. return;
  211. }
  212. if (is_kill(ch) && m_termios.c_lflag & ECHOK) {
  213. kill_line();
  214. return;
  215. }
  216. if (is_erase(ch) && m_termios.c_lflag & ECHOE) {
  217. do_backspace();
  218. return;
  219. }
  220. if (is_werase(ch)) {
  221. erase_word();
  222. return;
  223. }
  224. if (ch == '\n') {
  225. if (m_termios.c_lflag & ECHO || m_termios.c_lflag & ECHONL)
  226. echo_with_processing('\n');
  227. set_special_bit();
  228. m_input_buffer.enqueue('\n');
  229. m_available_lines++;
  230. return;
  231. }
  232. if (is_eol(ch)) {
  233. set_special_bit();
  234. m_available_lines++;
  235. }
  236. }
  237. m_input_buffer.enqueue(ch);
  238. if (m_termios.c_lflag & ECHO)
  239. echo_with_processing(ch);
  240. }
  241. bool TTY::can_do_backspace() const
  242. {
  243. // can't do back space if we're empty. Plus, we don't want to
  244. // remove any lines "committed" by newlines or ^D.
  245. if (!m_input_buffer.is_empty() && !is_eol(m_input_buffer.last()) && m_input_buffer.last() != '\0') {
  246. return true;
  247. }
  248. return false;
  249. }
  250. static size_t length_with_tabs(CircularDeque<u8, TTY_BUFFER_SIZE> const& line)
  251. {
  252. size_t length = 0;
  253. for (auto& ch : line) {
  254. length += (ch == '\t') ? 8 - (length % 8) : 1;
  255. }
  256. return length;
  257. }
  258. void TTY::do_backspace()
  259. {
  260. if (can_do_backspace()) {
  261. auto ch = m_input_buffer.dequeue_end();
  262. size_t to_delete = 1;
  263. if (ch == '\t') {
  264. auto length = length_with_tabs(m_input_buffer);
  265. to_delete = 8 - (length % 8);
  266. }
  267. for (size_t i = 0; i < to_delete; ++i) {
  268. // We deliberately don't process the output here.
  269. echo('\b');
  270. echo(' ');
  271. echo('\b');
  272. }
  273. evaluate_block_conditions();
  274. }
  275. }
  276. // TODO: Currently, both erase_word() and kill_line work by sending
  277. // a lot of VERASE characters; this is done because Terminal.cpp
  278. // doesn't currently support VWERASE and VKILL. When these are
  279. // implemented we could just send a VKILL or VWERASE.
  280. void TTY::erase_word()
  281. {
  282. //Note: if we have leading whitespace before the word
  283. //we want to delete we have to also delete that.
  284. bool first_char = false;
  285. bool did_dequeue = false;
  286. while (can_do_backspace()) {
  287. u8 ch = m_input_buffer.last();
  288. if (ch == ' ' && first_char)
  289. break;
  290. if (ch != ' ')
  291. first_char = true;
  292. m_input_buffer.dequeue_end();
  293. did_dequeue = true;
  294. erase_character();
  295. }
  296. if (did_dequeue)
  297. evaluate_block_conditions();
  298. }
  299. void TTY::kill_line()
  300. {
  301. bool did_dequeue = false;
  302. while (can_do_backspace()) {
  303. m_input_buffer.dequeue_end();
  304. did_dequeue = true;
  305. erase_character();
  306. }
  307. if (did_dequeue)
  308. evaluate_block_conditions();
  309. }
  310. void TTY::erase_character()
  311. {
  312. // We deliberately don't process the output here.
  313. echo(m_termios.c_cc[VERASE]);
  314. echo(' ');
  315. echo(m_termios.c_cc[VERASE]);
  316. }
  317. void TTY::generate_signal(int signal)
  318. {
  319. if (!pgid())
  320. return;
  321. if (should_flush_on_signal())
  322. flush_input();
  323. dbgln_if(TTY_DEBUG, "{}: Send signal {} to everyone in pgrp {}", tty_name(), signal, pgid().value());
  324. InterruptDisabler disabler; // FIXME: Iterate over a set of process handles instead?
  325. Process::for_each_in_pgrp(pgid(), [&](auto& process) {
  326. dbgln_if(TTY_DEBUG, "{}: Send signal {} to {}", tty_name(), signal, process);
  327. // FIXME: Should this error be propagated somehow?
  328. [[maybe_unused]] auto rc = process.send_signal(signal, nullptr);
  329. });
  330. }
  331. void TTY::flush_input()
  332. {
  333. m_available_lines = 0;
  334. m_input_buffer.clear();
  335. evaluate_block_conditions();
  336. }
  337. ErrorOr<void> TTY::set_termios(const termios& t)
  338. {
  339. ErrorOr<void> rc;
  340. m_termios = t;
  341. dbgln_if(TTY_DEBUG, "{} set_termios: ECHO={}, ISIG={}, ICANON={}, ECHOE={}, ECHOK={}, ECHONL={}, ISTRIP={}, ICRNL={}, INLCR={}, IGNCR={}, OPOST={}, ONLCR={}",
  342. tty_name(),
  343. should_echo_input(),
  344. should_generate_signals(),
  345. in_canonical_mode(),
  346. ((m_termios.c_lflag & ECHOE) != 0),
  347. ((m_termios.c_lflag & ECHOK) != 0),
  348. ((m_termios.c_lflag & ECHONL) != 0),
  349. ((m_termios.c_iflag & ISTRIP) != 0),
  350. ((m_termios.c_iflag & ICRNL) != 0),
  351. ((m_termios.c_iflag & INLCR) != 0),
  352. ((m_termios.c_iflag & IGNCR) != 0),
  353. ((m_termios.c_oflag & OPOST) != 0),
  354. ((m_termios.c_oflag & ONLCR) != 0));
  355. struct FlagDescription {
  356. tcflag_t value;
  357. StringView name;
  358. };
  359. constexpr FlagDescription unimplemented_iflags[] = {
  360. { IGNBRK, "IGNBRK" },
  361. { BRKINT, "BRKINT" },
  362. { IGNPAR, "IGNPAR" },
  363. { PARMRK, "PARMRK" },
  364. { INPCK, "INPCK" },
  365. { IGNCR, "IGNCR" },
  366. { IUCLC, "IUCLC" },
  367. { IXON, "IXON" },
  368. { IXANY, "IXANY" },
  369. { IXOFF, "IXOFF" },
  370. { IMAXBEL, "IMAXBEL" },
  371. { IUTF8, "IUTF8" }
  372. };
  373. for (auto flag : unimplemented_iflags) {
  374. if (m_termios.c_iflag & flag.value) {
  375. dbgln("FIXME: iflag {} unimplemented", flag.name);
  376. rc = ENOTIMPL;
  377. }
  378. }
  379. constexpr FlagDescription unimplemented_oflags[] = {
  380. { OLCUC, "OLCUC" },
  381. { ONOCR, "ONOCR" },
  382. { ONLRET, "ONLRET" },
  383. { OFILL, "OFILL" },
  384. { OFDEL, "OFDEL" }
  385. };
  386. for (auto flag : unimplemented_oflags) {
  387. if (m_termios.c_oflag & flag.value) {
  388. dbgln("FIXME: oflag {} unimplemented", flag.name);
  389. rc = ENOTIMPL;
  390. }
  391. }
  392. if ((m_termios.c_cflag & CSIZE) != CS8) {
  393. dbgln("FIXME: Character sizes other than 8 bits are not supported");
  394. rc = ENOTIMPL;
  395. }
  396. constexpr FlagDescription unimplemented_cflags[] = {
  397. { CSTOPB, "CSTOPB" },
  398. { CREAD, "CREAD" },
  399. { PARENB, "PARENB" },
  400. { PARODD, "PARODD" },
  401. { HUPCL, "HUPCL" },
  402. { CLOCAL, "CLOCAL" }
  403. };
  404. for (auto flag : unimplemented_cflags) {
  405. if (m_termios.c_cflag & flag.value) {
  406. dbgln("FIXME: cflag {} unimplemented", flag.name);
  407. rc = ENOTIMPL;
  408. }
  409. }
  410. constexpr FlagDescription unimplemented_lflags[] = {
  411. { TOSTOP, "TOSTOP" },
  412. { IEXTEN, "IEXTEN" }
  413. };
  414. for (auto flag : unimplemented_lflags) {
  415. if (m_termios.c_lflag & flag.value) {
  416. dbgln("FIXME: lflag {} unimplemented", flag.name);
  417. rc = ENOTIMPL;
  418. }
  419. }
  420. return rc;
  421. }
  422. ErrorOr<void> TTY::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg)
  423. {
  424. auto& current_process = Process::current();
  425. TRY(current_process.require_promise(Pledge::tty));
  426. #if 0
  427. // FIXME: When should we block things?
  428. // How do we make this work together with MasterPTY forwarding to us?
  429. if (current_process.tty() && current_process.tty() != this) {
  430. return ENOTTY;
  431. }
  432. #endif
  433. switch (request) {
  434. case TIOCGPGRP: {
  435. auto user_pgid = static_ptr_cast<pid_t*>(arg);
  436. auto pgid = this->pgid().value();
  437. return copy_to_user(user_pgid, &pgid);
  438. }
  439. case TIOCSPGRP: {
  440. ProcessGroupID pgid = static_cast<pid_t>(arg.ptr());
  441. if (pgid <= 0)
  442. return EINVAL;
  443. InterruptDisabler disabler;
  444. auto process_group = ProcessGroup::from_pgid(pgid);
  445. // Disallow setting a nonexistent PGID.
  446. if (!process_group)
  447. return EINVAL;
  448. auto process = Process::from_pid(ProcessID(pgid.value()));
  449. SessionID new_sid = process ? process->sid() : Process::get_sid_from_pgid(pgid);
  450. if (!new_sid || new_sid != current_process.sid())
  451. return EPERM;
  452. if (process && pgid != process->pgid())
  453. return EPERM;
  454. m_pg = process_group;
  455. if (process) {
  456. if (auto parent = Process::from_pid(process->ppid())) {
  457. m_original_process_parent = *parent;
  458. return {};
  459. }
  460. }
  461. m_original_process_parent = nullptr;
  462. return {};
  463. }
  464. case TCGETS: {
  465. auto user_termios = static_ptr_cast<termios*>(arg);
  466. return copy_to_user(user_termios, &m_termios);
  467. }
  468. case TCSETS:
  469. case TCSETSF:
  470. case TCSETSW: {
  471. auto user_termios = static_ptr_cast<termios const*>(arg);
  472. auto termios = TRY(copy_typed_from_user(user_termios));
  473. auto rc = set_termios(termios);
  474. if (request == TCSETSF)
  475. flush_input();
  476. return rc;
  477. }
  478. case TCFLSH: {
  479. // Serenity's TTY implementation does not use an output buffer, so ignore TCOFLUSH.
  480. auto operation = static_cast<u8>(arg.ptr());
  481. if (operation == TCIFLUSH || operation == TCIOFLUSH) {
  482. flush_input();
  483. } else if (operation != TCOFLUSH) {
  484. return EINVAL;
  485. }
  486. return {};
  487. }
  488. case TIOCGWINSZ: {
  489. auto user_winsize = static_ptr_cast<winsize*>(arg);
  490. winsize ws {};
  491. ws.ws_row = m_rows;
  492. ws.ws_col = m_columns;
  493. ws.ws_xpixel = 0;
  494. ws.ws_ypixel = 0;
  495. return copy_to_user(user_winsize, &ws);
  496. }
  497. case TIOCSWINSZ: {
  498. auto user_winsize = static_ptr_cast<winsize const*>(arg);
  499. auto ws = TRY(copy_typed_from_user(user_winsize));
  500. if (ws.ws_col == m_columns && ws.ws_row == m_rows)
  501. return {};
  502. m_rows = ws.ws_row;
  503. m_columns = ws.ws_col;
  504. generate_signal(SIGWINCH);
  505. return {};
  506. }
  507. case TIOCSCTTY:
  508. current_process.set_tty(this);
  509. return {};
  510. case TIOCSTI:
  511. return EIO;
  512. case TIOCNOTTY:
  513. current_process.set_tty(nullptr);
  514. return {};
  515. }
  516. return EINVAL;
  517. }
  518. ErrorOr<NonnullOwnPtr<KString>> TTY::pseudo_path(const OpenFileDescription&) const
  519. {
  520. return tty_name().try_clone();
  521. }
  522. void TTY::set_size(unsigned short columns, unsigned short rows)
  523. {
  524. m_rows = rows;
  525. m_columns = columns;
  526. }
  527. void TTY::hang_up()
  528. {
  529. generate_signal(SIGHUP);
  530. }
  531. }