github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/tty/line_discipline.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package tty 16 17 import ( 18 "bytes" 19 "unicode/utf8" 20 21 "github.com/SagerNet/gvisor/pkg/abi/linux" 22 "github.com/SagerNet/gvisor/pkg/context" 23 "github.com/SagerNet/gvisor/pkg/sentry/arch" 24 "github.com/SagerNet/gvisor/pkg/sentry/kernel" 25 "github.com/SagerNet/gvisor/pkg/sync" 26 "github.com/SagerNet/gvisor/pkg/syserror" 27 "github.com/SagerNet/gvisor/pkg/usermem" 28 "github.com/SagerNet/gvisor/pkg/waiter" 29 ) 30 31 // LINT.IfChange 32 33 const ( 34 // canonMaxBytes is the number of bytes that fit into a single line of 35 // terminal input in canonical mode. This corresponds to N_TTY_BUF_SIZE 36 // in include/linux/tty.h. 37 canonMaxBytes = 4096 38 39 // nonCanonMaxBytes is the maximum number of bytes that can be read at 40 // a time in noncanonical mode. 41 nonCanonMaxBytes = canonMaxBytes - 1 42 43 spacesPerTab = 8 44 ) 45 46 // lineDiscipline dictates how input and output are handled between the 47 // pseudoterminal (pty) master and replica. It can be configured to alter I/O, 48 // modify control characters (e.g. Ctrl-C for SIGINT), etc. The following man 49 // pages are good resources for how to affect the line discipline: 50 // 51 // * termios(3) 52 // * tty_ioctl(4) 53 // 54 // This file corresponds most closely to drivers/tty/n_tty.c. 55 // 56 // lineDiscipline has a simple structure but supports a multitude of options 57 // (see the above man pages). It consists of two queues of bytes: one from the 58 // terminal master to replica (the input queue) and one from replica to master 59 // (the output queue). When bytes are written to one end of the pty, the line 60 // discipline reads the bytes, modifies them or takes special action if 61 // required, and enqueues them to be read by the other end of the pty: 62 // 63 // input from terminal +-------------+ input to process (e.g. bash) 64 // +------------------------>| input queue |---------------------------+ 65 // | (inputQueueWrite) +-------------+ (inputQueueRead) | 66 // | | 67 // | v 68 // masterFD replicaFD 69 // ^ | 70 // | | 71 // | output to terminal +--------------+ output from process | 72 // +------------------------| output queue |<--------------------------+ 73 // (outputQueueRead) +--------------+ (outputQueueWrite) 74 // 75 // Lock order: 76 // termiosMu 77 // inQueue.mu 78 // outQueue.mu 79 // 80 // +stateify savable 81 type lineDiscipline struct { 82 // sizeMu protects size. 83 sizeMu sync.Mutex `state:"nosave"` 84 85 // size is the terminal size (width and height). 86 size linux.WindowSize 87 88 // inQueue is the input queue of the terminal. 89 inQueue queue 90 91 // outQueue is the output queue of the terminal. 92 outQueue queue 93 94 // termiosMu protects termios. 95 termiosMu sync.RWMutex `state:"nosave"` 96 97 // termios is the terminal configuration used by the lineDiscipline. 98 termios linux.KernelTermios 99 100 // column is the location in a row of the cursor. This is important for 101 // handling certain special characters like backspace. 102 column int 103 104 // masterWaiter is used to wait on the master end of the TTY. 105 masterWaiter waiter.Queue `state:"zerovalue"` 106 107 // replicaWaiter is used to wait on the replica end of the TTY. 108 replicaWaiter waiter.Queue `state:"zerovalue"` 109 } 110 111 func newLineDiscipline(termios linux.KernelTermios) *lineDiscipline { 112 ld := lineDiscipline{termios: termios} 113 ld.inQueue.transformer = &inputQueueTransformer{} 114 ld.outQueue.transformer = &outputQueueTransformer{} 115 return &ld 116 } 117 118 // getTermios gets the linux.Termios for the tty. 119 func (l *lineDiscipline) getTermios(task *kernel.Task, args arch.SyscallArguments) (uintptr, error) { 120 l.termiosMu.RLock() 121 defer l.termiosMu.RUnlock() 122 // We must copy a Termios struct, not KernelTermios. 123 t := l.termios.ToTermios() 124 _, err := t.CopyOut(task, args[2].Pointer()) 125 return 0, err 126 } 127 128 // setTermios sets a linux.Termios for the tty. 129 func (l *lineDiscipline) setTermios(task *kernel.Task, args arch.SyscallArguments) (uintptr, error) { 130 l.termiosMu.Lock() 131 defer l.termiosMu.Unlock() 132 oldCanonEnabled := l.termios.LEnabled(linux.ICANON) 133 // We must copy a Termios struct, not KernelTermios. 134 var t linux.Termios 135 _, err := t.CopyIn(task, args[2].Pointer()) 136 l.termios.FromTermios(t) 137 138 // If canonical mode is turned off, move bytes from inQueue's wait 139 // buffer to its read buffer. Anything already in the read buffer is 140 // now readable. 141 if oldCanonEnabled && !l.termios.LEnabled(linux.ICANON) { 142 l.inQueue.mu.Lock() 143 l.inQueue.pushWaitBufLocked(l) 144 l.inQueue.readable = true 145 l.inQueue.mu.Unlock() 146 l.replicaWaiter.Notify(waiter.ReadableEvents) 147 } 148 149 return 0, err 150 } 151 152 func (l *lineDiscipline) windowSize(t *kernel.Task, args arch.SyscallArguments) error { 153 l.sizeMu.Lock() 154 defer l.sizeMu.Unlock() 155 _, err := l.size.CopyOut(t, args[2].Pointer()) 156 return err 157 } 158 159 func (l *lineDiscipline) setWindowSize(t *kernel.Task, args arch.SyscallArguments) error { 160 l.sizeMu.Lock() 161 defer l.sizeMu.Unlock() 162 _, err := l.size.CopyIn(t, args[2].Pointer()) 163 return err 164 } 165 166 func (l *lineDiscipline) masterReadiness() waiter.EventMask { 167 // We don't have to lock a termios because the default master termios 168 // is immutable. 169 return l.inQueue.writeReadiness(&linux.MasterTermios) | l.outQueue.readReadiness(&linux.MasterTermios) 170 } 171 172 func (l *lineDiscipline) replicaReadiness() waiter.EventMask { 173 l.termiosMu.RLock() 174 defer l.termiosMu.RUnlock() 175 return l.outQueue.writeReadiness(&l.termios) | l.inQueue.readReadiness(&l.termios) 176 } 177 178 func (l *lineDiscipline) inputQueueReadSize(t *kernel.Task, args arch.SyscallArguments) error { 179 return l.inQueue.readableSize(t, args) 180 } 181 182 func (l *lineDiscipline) inputQueueRead(ctx context.Context, dst usermem.IOSequence) (int64, error) { 183 l.termiosMu.RLock() 184 defer l.termiosMu.RUnlock() 185 n, pushed, err := l.inQueue.read(ctx, dst, l) 186 if err != nil { 187 return 0, err 188 } 189 if n > 0 { 190 l.masterWaiter.Notify(waiter.WritableEvents) 191 if pushed { 192 l.replicaWaiter.Notify(waiter.ReadableEvents) 193 } 194 return n, nil 195 } 196 return 0, syserror.ErrWouldBlock 197 } 198 199 func (l *lineDiscipline) inputQueueWrite(ctx context.Context, src usermem.IOSequence) (int64, error) { 200 l.termiosMu.RLock() 201 defer l.termiosMu.RUnlock() 202 n, err := l.inQueue.write(ctx, src, l) 203 if err != nil { 204 return 0, err 205 } 206 if n > 0 { 207 l.replicaWaiter.Notify(waiter.ReadableEvents) 208 return n, nil 209 } 210 return 0, syserror.ErrWouldBlock 211 } 212 213 func (l *lineDiscipline) outputQueueReadSize(t *kernel.Task, args arch.SyscallArguments) error { 214 return l.outQueue.readableSize(t, args) 215 } 216 217 func (l *lineDiscipline) outputQueueRead(ctx context.Context, dst usermem.IOSequence) (int64, error) { 218 l.termiosMu.RLock() 219 defer l.termiosMu.RUnlock() 220 n, pushed, err := l.outQueue.read(ctx, dst, l) 221 if err != nil { 222 return 0, err 223 } 224 if n > 0 { 225 l.replicaWaiter.Notify(waiter.WritableEvents) 226 if pushed { 227 l.masterWaiter.Notify(waiter.ReadableEvents) 228 } 229 return n, nil 230 } 231 return 0, syserror.ErrWouldBlock 232 } 233 234 func (l *lineDiscipline) outputQueueWrite(ctx context.Context, src usermem.IOSequence) (int64, error) { 235 l.termiosMu.RLock() 236 defer l.termiosMu.RUnlock() 237 n, err := l.outQueue.write(ctx, src, l) 238 if err != nil { 239 return 0, err 240 } 241 if n > 0 { 242 l.masterWaiter.Notify(waiter.ReadableEvents) 243 return n, nil 244 } 245 return 0, syserror.ErrWouldBlock 246 } 247 248 // transformer is a helper interface to make it easier to stateify queue. 249 type transformer interface { 250 // transform functions require queue's mutex to be held. 251 transform(*lineDiscipline, *queue, []byte) int 252 } 253 254 // outputQueueTransformer implements transformer. It performs line discipline 255 // transformations on the output queue. 256 // 257 // +stateify savable 258 type outputQueueTransformer struct{} 259 260 // transform does output processing for one end of the pty. See 261 // drivers/tty/n_tty.c:do_output_char for an analogous kernel function. 262 // 263 // Preconditions: 264 // * l.termiosMu must be held for reading. 265 // * q.mu must be held. 266 func (*outputQueueTransformer) transform(l *lineDiscipline, q *queue, buf []byte) int { 267 // transformOutput is effectively always in noncanonical mode, as the 268 // master termios never has ICANON set. 269 270 if !l.termios.OEnabled(linux.OPOST) { 271 q.readBuf = append(q.readBuf, buf...) 272 if len(q.readBuf) > 0 { 273 q.readable = true 274 } 275 return len(buf) 276 } 277 278 var ret int 279 for len(buf) > 0 { 280 size := l.peek(buf) 281 cBytes := append([]byte{}, buf[:size]...) 282 ret += size 283 buf = buf[size:] 284 // We're guaranteed that cBytes has at least one element. 285 switch cBytes[0] { 286 case '\n': 287 if l.termios.OEnabled(linux.ONLRET) { 288 l.column = 0 289 } 290 if l.termios.OEnabled(linux.ONLCR) { 291 q.readBuf = append(q.readBuf, '\r', '\n') 292 continue 293 } 294 case '\r': 295 if l.termios.OEnabled(linux.ONOCR) && l.column == 0 { 296 continue 297 } 298 if l.termios.OEnabled(linux.OCRNL) { 299 cBytes[0] = '\n' 300 if l.termios.OEnabled(linux.ONLRET) { 301 l.column = 0 302 } 303 break 304 } 305 l.column = 0 306 case '\t': 307 spaces := spacesPerTab - l.column%spacesPerTab 308 if l.termios.OutputFlags&linux.TABDLY == linux.XTABS { 309 l.column += spaces 310 q.readBuf = append(q.readBuf, bytes.Repeat([]byte{' '}, spacesPerTab)...) 311 continue 312 } 313 l.column += spaces 314 case '\b': 315 if l.column > 0 { 316 l.column-- 317 } 318 default: 319 l.column++ 320 } 321 q.readBuf = append(q.readBuf, cBytes...) 322 } 323 if len(q.readBuf) > 0 { 324 q.readable = true 325 } 326 return ret 327 } 328 329 // inputQueueTransformer implements transformer. It performs line discipline 330 // transformations on the input queue. 331 // 332 // +stateify savable 333 type inputQueueTransformer struct{} 334 335 // transform does input processing for one end of the pty. Characters read are 336 // transformed according to flags set in the termios struct. See 337 // drivers/tty/n_tty.c:n_tty_receive_char_special for an analogous kernel 338 // function. 339 // 340 // Preconditions: 341 // * l.termiosMu must be held for reading. 342 // * q.mu must be held. 343 func (*inputQueueTransformer) transform(l *lineDiscipline, q *queue, buf []byte) int { 344 // If there's a line waiting to be read in canonical mode, don't write 345 // anything else to the read buffer. 346 if l.termios.LEnabled(linux.ICANON) && q.readable { 347 return 0 348 } 349 350 maxBytes := nonCanonMaxBytes 351 if l.termios.LEnabled(linux.ICANON) { 352 maxBytes = canonMaxBytes 353 } 354 355 var ret int 356 for len(buf) > 0 && len(q.readBuf) < canonMaxBytes { 357 size := l.peek(buf) 358 cBytes := append([]byte{}, buf[:size]...) 359 // We're guaranteed that cBytes has at least one element. 360 switch cBytes[0] { 361 case '\r': 362 if l.termios.IEnabled(linux.IGNCR) { 363 buf = buf[size:] 364 ret += size 365 continue 366 } 367 if l.termios.IEnabled(linux.ICRNL) { 368 cBytes[0] = '\n' 369 } 370 case '\n': 371 if l.termios.IEnabled(linux.INLCR) { 372 cBytes[0] = '\r' 373 } 374 } 375 376 // In canonical mode, we discard non-terminating characters 377 // after the first 4095. 378 if l.shouldDiscard(q, cBytes) { 379 buf = buf[size:] 380 ret += size 381 continue 382 } 383 384 // Stop if the buffer would be overfilled. 385 if len(q.readBuf)+size > maxBytes { 386 break 387 } 388 buf = buf[size:] 389 ret += size 390 391 // If we get EOF, make the buffer available for reading. 392 if l.termios.LEnabled(linux.ICANON) && l.termios.IsEOF(cBytes[0]) { 393 q.readable = true 394 break 395 } 396 397 q.readBuf = append(q.readBuf, cBytes...) 398 399 // Anything written to the readBuf will have to be echoed. 400 if l.termios.LEnabled(linux.ECHO) { 401 l.outQueue.writeBytes(cBytes, l) 402 l.masterWaiter.Notify(waiter.ReadableEvents) 403 } 404 405 // If we finish a line, make it available for reading. 406 if l.termios.LEnabled(linux.ICANON) && l.termios.IsTerminating(cBytes) { 407 q.readable = true 408 break 409 } 410 } 411 412 // In noncanonical mode, everything is readable. 413 if !l.termios.LEnabled(linux.ICANON) && len(q.readBuf) > 0 { 414 q.readable = true 415 } 416 417 return ret 418 } 419 420 // shouldDiscard returns whether c should be discarded. In canonical mode, if 421 // too many bytes are enqueued, we keep reading input and discarding it until 422 // we find a terminating character. Signal/echo processing still occurs. 423 // 424 // Precondition: 425 // * l.termiosMu must be held for reading. 426 // * q.mu must be held. 427 func (l *lineDiscipline) shouldDiscard(q *queue, cBytes []byte) bool { 428 return l.termios.LEnabled(linux.ICANON) && len(q.readBuf)+len(cBytes) >= canonMaxBytes && !l.termios.IsTerminating(cBytes) 429 } 430 431 // peek returns the size in bytes of the next character to process. As long as 432 // b isn't empty, peek returns a value of at least 1. 433 func (l *lineDiscipline) peek(b []byte) int { 434 size := 1 435 // If UTF-8 support is enabled, runes might be multiple bytes. 436 if l.termios.IEnabled(linux.IUTF8) { 437 _, size = utf8.DecodeRune(b) 438 } 439 return size 440 } 441 442 // LINT.ThenChange(../../fsimpl/devpts/line_discipline.go)