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