1 /*
2 * Copyright (c) 2006-2023, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2023-11-13 Shell init ver.
9 */
10 #include "../bsd_porting.h"
11 #include "../tty_config.h"
12 #include "../tty_internal.h"
13 #include "../terminal.h"
14
15 #include <rtdef.h>
16
17 /*-
18 * SPDX-License-Identifier: BSD-2-Clause
19 *
20 * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
21 * All rights reserved.
22 *
23 * Portions of this software were developed under sponsorship from Snow
24 * B.V., the Netherlands.
25 *
26 * Redistribution and use in source and binary forms, with or without
27 * modification, are permitted provided that the following conditions
28 * are met:
29 * 1. Redistributions of source code must retain the above copyright
30 * notice, this list of conditions and the following disclaimer.
31 * 2. Redistributions in binary form must reproduce the above copyright
32 * notice, this list of conditions and the following disclaimer in the
33 * documentation and/or other materials provided with the distribution.
34 *
35 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
36 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
39 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
40 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
41 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
43 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
44 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45 * SUCH DAMAGE.
46 */
47
48 /*
49 * Standard TTYDISC `termios' line discipline.
50 */
51
52 /* Statistics. */
53 static rt_atomic_t tty_nin = 0;
54 #ifdef USING_BSD_SYSCTL
55 SYSCTL_ULONG(_kern, OID_AUTO, tty_nin, CTLFLAG_RD, &tty_nin, 0,
56 "Total amount of bytes received");
57 #endif
58
59 static rt_atomic_t tty_nout = 0;
60 #ifdef USING_BSD_SYSCTL
61 SYSCTL_ULONG(_kern, OID_AUTO, tty_nout, CTLFLAG_RD, &tty_nout, 0,
62 "Total amount of bytes transmitted");
63 #endif
64
65 /* termios comparison macro's. */
66 #define CMP_CC(v, c) \
67 (tp->t_termios.c_cc[v] != _POSIX_VDISABLE && tp->t_termios.c_cc[v] == (c))
68 #define CMP_FLAG(field, opt) (tp->t_termios.c_##field##flag & (opt))
69
70 /* Characters that cannot be modified through c_cc. */
71 #define CTAB '\t'
72 #define CNL '\n'
73 #define CCR '\r'
74
75 /* Character is a control character. */
76 #define CTL_VALID(c) ((c) == 0x7f || (unsigned char)(c) < 0x20)
77 /* Control character should be processed on echo. */
78 #define CTL_ECHO(c, q) \
79 (!(q) && ((c) == CERASE2 || (c) == CTAB || (c) == CNL || (c) == CCR))
80 /* Control character should be printed using ^X notation. */
81 #define CTL_PRINT(c, q) \
82 ((c) == 0x7f || \
83 ((unsigned char)(c) < 0x20 && ((q) || ((c) != CTAB && (c) != CNL))))
84 /* Character is whitespace. */
85 #define CTL_WHITE(c) ((c) == ' ' || (c) == CTAB)
86 /* Character is alphanumeric. */
87 #define CTL_ALNUM(c) \
88 (((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'z') || \
89 ((c) >= 'A' && (c) <= 'Z'))
90 /* Character is UTF8-encoded. */
91 #define CTL_UTF8(c) (!!((c)&0x80))
92 /* Character is a UTF8 continuation byte. */
93 #define CTL_UTF8_CONT(c) (((c)&0xc0) == 0x80)
94
95 #define TTY_STACKBUF 256
96 #define UTF8_STACKBUF 4
97
ttydisc_open(struct lwp_tty * tp)98 void ttydisc_open(struct lwp_tty *tp)
99 {
100 ttydisc_optimize(tp);
101 }
102
ttydisc_close(struct lwp_tty * tp)103 void ttydisc_close(struct lwp_tty *tp)
104 {
105 /* Clean up our flags when leaving the discipline. */
106 tp->t_flags &= ~(TF_STOPPED | TF_HIWAT | TF_ZOMBIE);
107 tp->t_termios.c_lflag &= ~FLUSHO;
108
109 /*
110 * POSIX states that we must drain output and flush input on
111 * last close. Draining has already been done if possible.
112 */
113 tty_flush(tp, FREAD | FWRITE);
114
115 #ifdef USING_BSD_HOOK
116 if (ttyhook_hashook(tp, close))
117 ttyhook_close(tp);
118 #endif
119 }
120
ttydisc_read_canonical(struct lwp_tty * tp,struct uio * uio,int ioflag)121 static int ttydisc_read_canonical(struct lwp_tty *tp, struct uio *uio,
122 int ioflag)
123 {
124 char breakc[4] = {CNL}; /* enough to hold \n, VEOF and VEOL. */
125 int error;
126 size_t clen, flen = 0, n = 1;
127 unsigned char lastc = _POSIX_VDISABLE;
128
129 #define BREAK_ADD(c) \
130 do \
131 { \
132 if (tp->t_termios.c_cc[c] != _POSIX_VDISABLE) \
133 breakc[n++] = tp->t_termios.c_cc[c]; \
134 } while (0)
135 /* Determine which characters we should trigger on. */
136 BREAK_ADD(VEOF);
137 BREAK_ADD(VEOL);
138 #undef BREAK_ADD
139 breakc[n] = '\0';
140
141 do
142 {
143 error = tty_wait_background(tp, curthread, SIGTTIN);
144 if (error)
145 return error;
146
147 /*
148 * Quite a tricky case: unlike the old TTY
149 * implementation, this implementation copies data back
150 * to userspace in large chunks. Unfortunately, we can't
151 * calculate the line length on beforehand if it crosses
152 * ttyinq_block boundaries, because multiple reads could
153 * then make this code read beyond the newline.
154 *
155 * This is why we limit the read to:
156 * - The size the user has requested
157 * - The blocksize (done in tty_inq.c)
158 * - The amount of bytes until the newline
159 *
160 * This causes the line length to be recalculated after
161 * each block has been copied to userspace. This will
162 * cause the TTY layer to return data in chunks using
163 * the blocksize (except the first and last blocks).
164 */
165 clen =
166 ttyinq_findchar(&tp->t_inq, breakc, uio->uio_resid, (char *)&lastc);
167
168 /* No more data. */
169 if (clen == 0)
170 {
171 if (tp->t_flags & TF_ZOMBIE)
172 return 0;
173 else if (ioflag & IO_NDELAY)
174 return -EWOULDBLOCK;
175
176 error = tty_wait(tp, &tp->t_inwait);
177 if (error)
178 return error;
179 continue;
180 }
181
182 /* Don't send the EOF char back to userspace. */
183 if (CMP_CC(VEOF, lastc))
184 flen = 1;
185
186 MPASS(flen <= clen);
187
188 /* Read and throw away the EOF character. */
189 error = ttyinq_read_uio(&tp->t_inq, tp, uio, clen, flen);
190 if (error)
191 return error;
192
193 /** Notes: proceed when read buf frees and not reaching breaks */
194 } while (uio->uio_resid > 0 && lastc == _POSIX_VDISABLE);
195
196 return 0;
197 }
198
ttydisc_read_raw_no_timer(struct lwp_tty * tp,struct uio * uio,int ioflag)199 static int ttydisc_read_raw_no_timer(struct lwp_tty *tp, struct uio *uio,
200 int ioflag)
201 {
202 size_t vmin = tp->t_termios.c_cc[VMIN];
203 ssize_t oresid = uio->uio_resid;
204 int error;
205
206 MPASS(tp->t_termios.c_cc[VTIME] == 0);
207
208 /*
209 * This routine implements the easy cases of read()s while in
210 * non-canonical mode, namely case B and D, where we don't have
211 * any timers at all.
212 */
213
214 for (;;)
215 {
216 error = tty_wait_background(tp, curthread, SIGTTIN);
217 if (error)
218 return error;
219
220 error = ttyinq_read_uio(&tp->t_inq, tp, uio, uio->uio_resid, 0);
221 if (error)
222 return error;
223 if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin)
224 return 0;
225
226 /* We have to wait for more. */
227 if (tp->t_flags & TF_ZOMBIE)
228 return 0;
229 else if (ioflag & IO_NDELAY)
230 return -EWOULDBLOCK;
231
232 error = tty_wait(tp, &tp->t_inwait);
233 if (error)
234 return error;
235 }
236 }
237
ttydisc_read_raw_read_timer(struct lwp_tty * tp,struct uio * uio,int ioflag,int oresid)238 static int ttydisc_read_raw_read_timer(struct lwp_tty *tp, struct uio *uio,
239 int ioflag, int oresid)
240 {
241 size_t vmin = MAX(tp->t_termios.c_cc[VMIN], 1);
242 unsigned int vtime = tp->t_termios.c_cc[VTIME];
243 struct timeval end, now, left;
244 int error, hz;
245
246 MPASS(tp->t_termios.c_cc[VTIME] != 0);
247
248 /* Determine when the read should be expired. */
249 end.tv_sec = vtime / 10;
250 end.tv_usec = (vtime % 10) * 100000;
251 getmicrotime(&now);
252 timevaladd(&end, &now);
253
254 for (;;)
255 {
256 error = tty_wait_background(tp, curthread, SIGTTIN);
257 if (error)
258 return error;
259
260 error = ttyinq_read_uio(&tp->t_inq, tp, uio, uio->uio_resid, 0);
261 if (error)
262 return error;
263 if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin)
264 return 0;
265
266 /* Calculate how long we should wait. */
267 getmicrotime(&now);
268 if (timevalcmp(&now, &end, >))
269 return 0;
270 left = end;
271 timevalsub(&left, &now);
272 hz = tvtohz(&left);
273
274 /*
275 * We have to wait for more. If the timer expires, we
276 * should return a 0-byte read.
277 */
278 if (tp->t_flags & TF_ZOMBIE)
279 return 0;
280 else if (ioflag & IO_NDELAY)
281 return -EWOULDBLOCK;
282
283 error = tty_timedwait(tp, &tp->t_inwait, hz);
284 if (error)
285 return (error == -EWOULDBLOCK ? 0 : error);
286 }
287
288 return 0;
289 }
290
ttydisc_read_raw_interbyte_timer(struct lwp_tty * tp,struct uio * uio,int ioflag)291 static int ttydisc_read_raw_interbyte_timer(struct lwp_tty *tp, struct uio *uio,
292 int ioflag)
293 {
294 size_t vmin = tp->t_termios.c_cc[VMIN];
295 ssize_t oresid = uio->uio_resid;
296 int error;
297
298 MPASS(tp->t_termios.c_cc[VMIN] != 0);
299 MPASS(tp->t_termios.c_cc[VTIME] != 0);
300
301 /*
302 * When using the interbyte timer, the timer should be started
303 * after the first byte has been received. We just call into the
304 * generic read timer code after we've received the first byte.
305 */
306
307 for (;;)
308 {
309 error = tty_wait_background(tp, curthread, SIGTTIN);
310 if (error)
311 return error;
312
313 error = ttyinq_read_uio(&tp->t_inq, tp, uio, uio->uio_resid, 0);
314 if (error)
315 return error;
316 if (uio->uio_resid == 0 || (oresid - uio->uio_resid) >= vmin)
317 return 0;
318
319 /*
320 * Not enough data, but we did receive some, which means
321 * we'll now start using the interbyte timer.
322 */
323 if (oresid != uio->uio_resid)
324 break;
325
326 /* We have to wait for more. */
327 if (tp->t_flags & TF_ZOMBIE)
328 return 0;
329 else if (ioflag & IO_NDELAY)
330 return -EWOULDBLOCK;
331
332 error = tty_wait(tp, &tp->t_inwait);
333 if (error)
334 return error;
335 }
336
337 return ttydisc_read_raw_read_timer(tp, uio, ioflag, oresid);
338 }
339
ttydisc_read(struct lwp_tty * tp,struct uio * uio,int ioflag)340 int ttydisc_read(struct lwp_tty *tp, struct uio *uio, int ioflag)
341 {
342 int error;
343
344 tty_assert_locked(tp);
345
346 if (uio->uio_resid == 0)
347 return 0;
348
349 if (CMP_FLAG(l, ICANON))
350 error = ttydisc_read_canonical(tp, uio, ioflag);
351 else if (tp->t_termios.c_cc[VTIME] == 0)
352 error = ttydisc_read_raw_no_timer(tp, uio, ioflag);
353 else if (tp->t_termios.c_cc[VMIN] == 0)
354 error = ttydisc_read_raw_read_timer(tp, uio, ioflag, uio->uio_resid);
355 else
356 error = ttydisc_read_raw_interbyte_timer(tp, uio, ioflag);
357
358 if (ttyinq_bytesleft(&tp->t_inq) >= tp->t_inlow ||
359 ttyinq_bytescanonicalized(&tp->t_inq) == 0)
360 {
361 /* Unset the input watermark when we've got enough space. */
362 tty_hiwat_in_unblock(tp);
363 }
364
365 return error;
366 }
367
368 /* return the offset to special character from string start */
ttydisc_findchar(const char * obstart,unsigned int oblen)369 rt_inline unsigned int ttydisc_findchar(const char *obstart, unsigned int oblen)
370 {
371 const char *c = obstart;
372
373 while (oblen--)
374 {
375 if (CTL_VALID(*c))
376 break;
377 c++;
378 }
379
380 return (c - obstart);
381 }
382
ttydisc_write_oproc(struct lwp_tty * tp,char c)383 static int ttydisc_write_oproc(struct lwp_tty *tp, char c)
384 {
385 unsigned int scnt, error;
386
387 MPASS(CMP_FLAG(o, OPOST));
388 MPASS(CTL_VALID(c));
389
390 #define PRINT_NORMAL() ttyoutq_write_nofrag(&tp->t_outq, &c, 1)
391 switch (c)
392 {
393 case CEOF:
394 /* End-of-text dropping. */
395 if (CMP_FLAG(o, ONOEOT))
396 return 0;
397 return PRINT_NORMAL();
398
399 case CERASE2:
400 /* Handle backspace to fix tab expansion. */
401 if (PRINT_NORMAL() != 0)
402 return (-1);
403 if (tp->t_column > 0)
404 tp->t_column--;
405 return 0;
406
407 case CTAB:
408 /* Tab expansion. */
409 scnt = 8 - (tp->t_column & 7);
410 if (CMP_FLAG(o, TAB3))
411 {
412 error = ttyoutq_write_nofrag(&tp->t_outq, " ", scnt);
413 }
414 else
415 {
416 error = PRINT_NORMAL();
417 }
418 if (error)
419 return (-1);
420
421 tp->t_column += scnt;
422 MPASS((tp->t_column % 8) == 0);
423 return 0;
424
425 case CNL:
426 /* Newline conversion. */
427 if (CMP_FLAG(o, ONLCR))
428 {
429 /* Convert \n to \r\n. */
430 error = ttyoutq_write_nofrag(&tp->t_outq, "\r\n", 2);
431 }
432 else
433 {
434 error = PRINT_NORMAL();
435 }
436 if (error)
437 return (-1);
438
439 if (CMP_FLAG(o, ONLCR | ONLRET))
440 {
441 tp->t_column = tp->t_writepos = 0;
442 ttyinq_reprintpos_set(&tp->t_inq);
443 }
444 return 0;
445
446 case CCR:
447 /* Carriage return to newline conversion. */
448 if (CMP_FLAG(o, OCRNL))
449 c = CNL;
450 /* Omit carriage returns on column 0. */
451 if (CMP_FLAG(o, ONOCR) && tp->t_column == 0)
452 return 0;
453 if (PRINT_NORMAL() != 0)
454 return (-1);
455
456 tp->t_column = tp->t_writepos = 0;
457 ttyinq_reprintpos_set(&tp->t_inq);
458 return 0;
459 }
460
461 /*
462 * Invisible control character. Print it, but don't
463 * increase the column count.
464 */
465 return PRINT_NORMAL();
466 #undef PRINT_NORMAL
467 }
468
469 /*
470 * Just like the old TTY implementation, we need to copy data in chunks
471 * into a temporary buffer. One of the reasons why we need to do this,
472 * is because output processing (only TAB3 though) may allow the buffer
473 * to grow eight times.
474 */
ttydisc_write(struct lwp_tty * tp,struct uio * uio,int ioflag)475 int ttydisc_write(struct lwp_tty *tp, struct uio *uio, int ioflag)
476 {
477 char ob[TTY_STACKBUF];
478 char *obstart;
479 int error = 0;
480 unsigned int oblen = 0;
481
482 tty_assert_locked(tp);
483
484 if (tp->t_flags & TF_ZOMBIE)
485 return -EIO;
486
487 /*
488 * We don't need to check whether the process is the foreground
489 * process group or if we have a carrier. This is already done
490 * in ttydev_write().
491 */
492
493 while (uio->uio_resid > 0)
494 {
495 unsigned int nlen;
496
497 MPASS(oblen == 0);
498
499 if (CMP_FLAG(l, FLUSHO))
500 {
501 uio->uio_offset += uio->uio_resid;
502 uio->uio_resid = 0;
503 return 0;
504 }
505
506 /* Step 1: read data. */
507 obstart = ob;
508 nlen = MIN(uio->uio_resid, sizeof ob);
509 tty_unlock(tp);
510 error = uiomove(ob, nlen, uio);
511 tty_lock(tp);
512 if (error != 0)
513 break;
514 oblen = nlen;
515
516 if (tty_gone(tp))
517 {
518 error = -ENXIO;
519 break;
520 }
521
522 MPASS(oblen > 0);
523
524 /* Step 2: process data. */
525 do
526 {
527 unsigned int plen, wlen;
528
529 if (CMP_FLAG(l, FLUSHO))
530 {
531 uio->uio_offset += uio->uio_resid;
532 uio->uio_resid = 0;
533 return 0;
534 }
535
536 /* Search for special characters for post processing. */
537 if (CMP_FLAG(o, OPOST))
538 {
539 plen = ttydisc_findchar(obstart, oblen);
540 }
541 else
542 {
543 plen = oblen;
544 }
545
546 if (plen == 0)
547 {
548 /*
549 * We're going to process a character
550 * that needs processing
551 */
552 if (ttydisc_write_oproc(tp, *obstart) == 0)
553 {
554 obstart++;
555 oblen--;
556
557 tp->t_writepos = tp->t_column;
558 ttyinq_reprintpos_set(&tp->t_inq);
559 continue;
560 }
561 }
562 else
563 {
564 /* We're going to write regular data. */
565 wlen = ttyoutq_write(&tp->t_outq, obstart, plen);
566 obstart += wlen;
567 oblen -= wlen;
568 tp->t_column += wlen;
569
570 tp->t_writepos = tp->t_column;
571 ttyinq_reprintpos_set(&tp->t_inq);
572
573 if (wlen == plen)
574 continue;
575 }
576
577 /* Watermark reached. Try to sleep. */
578 tp->t_flags |= TF_HIWAT_OUT;
579
580 if (ioflag & IO_NDELAY)
581 {
582 error = EWOULDBLOCK;
583 goto done;
584 }
585
586 /*
587 * The driver may write back the data
588 * synchronously. Be sure to check the high
589 * water mark before going to sleep.
590 */
591 ttydevsw_outwakeup(tp);
592 if ((tp->t_flags & TF_HIWAT_OUT) == 0)
593 continue;
594
595 error = tty_wait(tp, &tp->t_outwait);
596 if (error)
597 goto done;
598
599 if (tp->t_flags & TF_ZOMBIE)
600 {
601 error = EIO;
602 goto done;
603 }
604 } while (oblen > 0);
605 }
606
607 done:
608 if (!tty_gone(tp))
609 ttydevsw_outwakeup(tp);
610
611 /*
612 * Add the amount of bytes that we didn't process back to the
613 * uio counters. We need to do this to make sure write() doesn't
614 * count the bytes we didn't store in the queue.
615 */
616 uio->uio_resid += oblen;
617 return error;
618 }
619
620 /* set optimize flags if possible */
ttydisc_optimize(struct lwp_tty * tp)621 void ttydisc_optimize(struct lwp_tty *tp)
622 {
623 tty_assert_locked(tp);
624
625 if (ttyhook_hashook(tp, rint_bypass))
626 {
627 tp->t_flags |= TF_BYPASS;
628 }
629 else if (ttyhook_hashook(tp, rint))
630 {
631 tp->t_flags &= ~TF_BYPASS;
632 }
633 else if (!CMP_FLAG(i, ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON) &&
634 (!CMP_FLAG(i, BRKINT) || CMP_FLAG(i, IGNBRK)) &&
635 (!CMP_FLAG(i, PARMRK) ||
636 CMP_FLAG(i, IGNPAR | IGNBRK) == (IGNPAR | IGNBRK)) &&
637 !CMP_FLAG(l, ECHO | ICANON | IEXTEN | ISIG | PENDIN))
638 {
639 tp->t_flags |= TF_BYPASS;
640 }
641 else
642 {
643 tp->t_flags &= ~TF_BYPASS;
644 }
645 }
646
ttydisc_modem(struct lwp_tty * tp,int open)647 void ttydisc_modem(struct lwp_tty *tp, int open)
648 {
649 tty_assert_locked(tp);
650
651 if (open)
652 cv_broadcast(&tp->t_dcdwait);
653
654 /*
655 * Ignore modem status lines when CLOCAL is turned on, but don't
656 * enter the zombie state when the TTY isn't opened, because
657 * that would cause the TTY to be in zombie state after being
658 * opened.
659 */
660 if (!tty_opened(tp) || CMP_FLAG(c, CLOCAL))
661 return;
662
663 if (open == 0)
664 {
665 /*
666 * Lost carrier.
667 */
668 tp->t_flags |= TF_ZOMBIE;
669
670 lwp_tty_signal_sessleader(tp, SIGHUP);
671 tty_flush(tp, FREAD | FWRITE);
672 }
673 else
674 {
675 /*
676 * Carrier is back again.
677 */
678
679 /* XXX: what should we do here? */
680 }
681 }
682
ttydisc_echo_force(struct lwp_tty * tp,char c,int quote)683 static int ttydisc_echo_force(struct lwp_tty *tp, char c, int quote)
684 {
685 if (CMP_FLAG(l, FLUSHO))
686 return 0;
687
688 if (CMP_FLAG(o, OPOST) && CTL_ECHO(c, quote))
689 {
690 /*
691 * Only perform postprocessing when OPOST is turned on
692 * and the character is an unquoted BS/TB/NL/CR.
693 */
694 return ttydisc_write_oproc(tp, c);
695 }
696 else if (CMP_FLAG(l, ECHOCTL) && CTL_PRINT(c, quote))
697 {
698 /*
699 * Only use ^X notation when ECHOCTL is turned on and
700 * we've got an quoted control character.
701 *
702 * Print backspaces when echoing an end-of-file.
703 */
704 char ob[4] = "^?\b\b";
705
706 /* Print ^X notation. */
707 if (c != 0x7f)
708 ob[1] = c + 'A' - 1;
709
710 if (!quote && CMP_CC(VEOF, c))
711 {
712 return ttyoutq_write_nofrag(&tp->t_outq, ob, 4);
713 }
714 else
715 {
716 tp->t_column += 2;
717 return ttyoutq_write_nofrag(&tp->t_outq, ob, 2);
718 }
719 }
720 else
721 {
722 /* Can just be printed. */
723 tp->t_column++;
724 return ttyoutq_write_nofrag(&tp->t_outq, &c, 1);
725 }
726 }
727
ttydisc_echo(struct lwp_tty * tp,char c,int quote)728 static int ttydisc_echo(struct lwp_tty *tp, char c, int quote)
729 {
730 /*
731 * Only echo characters when ECHO is turned on, or ECHONL when
732 * the character is an unquoted newline.
733 */
734 if (!CMP_FLAG(l, ECHO) && (!CMP_FLAG(l, ECHONL) || c != CNL || quote))
735 return 0;
736
737 return ttydisc_echo_force(tp, c, quote);
738 }
739
ttydisc_reprint_char(void * d,char c,int quote)740 static void ttydisc_reprint_char(void *d, char c, int quote)
741 {
742 struct lwp_tty *tp = d;
743
744 ttydisc_echo(tp, c, quote);
745 }
746
ttydisc_reprint(struct lwp_tty * tp)747 static void ttydisc_reprint(struct lwp_tty *tp)
748 {
749 cc_t c;
750
751 /* Print ^R\n, followed by the line. */
752 c = tp->t_termios.c_cc[VREPRINT];
753 if (c != _POSIX_VDISABLE)
754 ttydisc_echo(tp, c, 0);
755 ttydisc_echo(tp, CNL, 0);
756 ttyinq_reprintpos_reset(&tp->t_inq);
757
758 ttyinq_line_iterate_from_linestart(&tp->t_inq, ttydisc_reprint_char, tp);
759 }
760
761 struct ttydisc_recalc_length
762 {
763 struct lwp_tty *tp;
764 unsigned int curlen;
765 };
766
ttydisc_recalc_charlength(void * d,char c,int quote)767 static void ttydisc_recalc_charlength(void *d, char c, int quote)
768 {
769 struct ttydisc_recalc_length *data = d;
770 struct lwp_tty *tp = data->tp;
771
772 if (CTL_PRINT(c, quote))
773 {
774 if (CMP_FLAG(l, ECHOCTL))
775 data->curlen += 2;
776 }
777 else if (c == CTAB)
778 {
779 data->curlen += 8 - (data->curlen & 7);
780 }
781 else
782 {
783 data->curlen++;
784 }
785 }
786
ttydisc_recalc_linelength(struct lwp_tty * tp)787 static unsigned int ttydisc_recalc_linelength(struct lwp_tty *tp)
788 {
789 struct ttydisc_recalc_length data = {tp, tp->t_writepos};
790
791 ttyinq_line_iterate_from_reprintpos(&tp->t_inq, ttydisc_recalc_charlength,
792 &data);
793 return (data.curlen);
794 }
795
ttydisc_rubchar(struct lwp_tty * tp)796 static int ttydisc_rubchar(struct lwp_tty *tp)
797 {
798 char c;
799 int quote;
800 unsigned int prevpos, tablen;
801
802 if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0)
803 return (-1);
804 ttyinq_unputchar(&tp->t_inq);
805
806 if (CMP_FLAG(l, ECHO))
807 {
808 /*
809 * Remove the character from the screen. This is even
810 * safe for characters that span multiple characters
811 * (tabs, quoted, etc).
812 */
813 if (tp->t_writepos >= tp->t_column)
814 {
815 /* Retype the sentence. */
816 ttydisc_reprint(tp);
817 }
818 else if (CMP_FLAG(l, ECHOE))
819 {
820 if (CTL_PRINT(c, quote))
821 {
822 /* Remove ^X formatted chars. */
823 if (CMP_FLAG(l, ECHOCTL))
824 {
825 tp->t_column -= 2;
826 ttyoutq_write_nofrag(&tp->t_outq, "\b\b \b\b", 6);
827 }
828 }
829 else if (c == ' ')
830 {
831 /* Space character needs no rubbing. */
832 tp->t_column -= 1;
833 ttyoutq_write_nofrag(&tp->t_outq, "\b", 1);
834 }
835 else if (c == CTAB)
836 {
837 /*
838 * Making backspace work with tabs is
839 * quite hard. Recalculate the length of
840 * this character and remove it.
841 *
842 * Because terminal settings could be
843 * changed while the line is being
844 * inserted, the calculations don't have
845 * to be correct. Make sure we keep the
846 * tab length within proper bounds.
847 */
848 prevpos = ttydisc_recalc_linelength(tp);
849 if (prevpos >= tp->t_column)
850 tablen = 1;
851 else
852 tablen = tp->t_column - prevpos;
853 if (tablen > 8)
854 tablen = 8;
855
856 tp->t_column = prevpos;
857 ttyoutq_write_nofrag(&tp->t_outq, "\b\b\b\b\b\b\b\b", tablen);
858 return 0;
859 }
860 #ifdef USING_BSD_UTF8
861 else if ((tp->t_termios.c_iflag & IUTF8) != 0 && CTL_UTF8(c))
862 {
863 uint8_t bytes[UTF8_STACKBUF] = {0};
864 int curidx = UTF8_STACKBUF - 1, cwidth = 1, nb = 0;
865 teken_char_t codepoint;
866
867 /* Save current byte. */
868 bytes[curidx] = c;
869 curidx--;
870 nb++;
871 /* Loop back through inq until we hit the
872 * leading byte. */
873 while (CTL_UTF8_CONT(c) && nb < UTF8_STACKBUF)
874 {
875 /*
876 * Check if we've reached the beginning
877 * of the line.
878 */
879 if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0)
880 break;
881 ttyinq_unputchar(&tp->t_inq);
882 bytes[curidx] = c;
883 curidx--;
884 nb++;
885 }
886 /*
887 * Shift array so that the leading
888 * byte ends up at idx 0.
889 */
890 if (nb < UTF8_STACKBUF)
891 memmove(&bytes[0], &bytes[curidx + 1],
892 nb * sizeof(uint8_t));
893 /* Check for malformed UTF8 characters. */
894 if (nb == UTF8_STACKBUF && CTL_UTF8_CONT(bytes[0]))
895 {
896 /*
897 * Place all bytes back into the inq and
898 * delete the last byte only.
899 */
900 ttyinq_write(&tp->t_inq, bytes, UTF8_STACKBUF, 0);
901 ttyinq_unputchar(&tp->t_inq);
902 }
903 else
904 {
905 /* Find codepoint and width. */
906 codepoint = teken_utf8_bytes_to_codepoint(bytes, nb);
907 if (codepoint == TEKEN_UTF8_INVALID_CODEPOINT ||
908 (cwidth = teken_wcwidth(codepoint)) == -1)
909 {
910 /*
911 * Place all bytes back into the
912 * inq and fall back to
913 * default behaviour.
914 */
915 cwidth = 1;
916 ttyinq_write(&tp->t_inq, bytes, nb, 0);
917 ttyinq_unputchar(&tp->t_inq);
918 }
919 }
920 tp->t_column -= cwidth;
921 /*
922 * Delete character by punching
923 * 'cwidth' spaces over it.
924 */
925 if (cwidth == 1)
926 ttyoutq_write_nofrag(&tp->t_outq, "\b \b", 3);
927 else if (cwidth == 2)
928 ttyoutq_write_nofrag(&tp->t_outq, "\b\b \b\b", 6);
929 }
930 #endif
931 else
932 {
933 /*
934 * Remove a regular character by
935 * punching a space over it.
936 */
937 tp->t_column -= 1;
938 ttyoutq_write_nofrag(&tp->t_outq, "\b \b", 3);
939 }
940 }
941 else
942 {
943 /* Don't print spaces. */
944 ttydisc_echo(tp, tp->t_termios.c_cc[VERASE], 0);
945 }
946 }
947
948 return 0;
949 }
950
ttydisc_rubword(struct lwp_tty * tp)951 static void ttydisc_rubword(struct lwp_tty *tp)
952 {
953 char c;
954 int quote;
955 #ifdef ALTWERASE
956 int alnum;
957 #endif
958
959 /* Strip whitespace first. */
960 for (;;)
961 {
962 if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0)
963 return;
964 if (!CTL_WHITE(c))
965 break;
966 ttydisc_rubchar(tp);
967 }
968
969 /*
970 * Record whether the last character from the previous iteration
971 * was alphanumeric or not. We need this to implement ALTWERASE.
972 */
973 #ifdef ALTWERASE
974 alnum = CTL_ALNUM(c);
975 #endif
976 for (;;)
977 {
978 ttydisc_rubchar(tp);
979
980 if (ttyinq_peekchar(&tp->t_inq, &c, "e) != 0)
981 return;
982 if (CTL_WHITE(c))
983 return;
984 #ifdef ALTWERASE
985 if (CMP_FLAG(l, ALTWERASE) && CTL_ALNUM(c) != alnum)
986 return;
987 #endif
988 }
989 }
990
ttydisc_rint(struct lwp_tty * tp,char c,int flags)991 int ttydisc_rint(struct lwp_tty *tp, char c, int flags)
992 {
993 int signal, quote = 0;
994 char ob[3] = {0xff, 0x00};
995 size_t ol;
996
997 tty_assert_locked(tp);
998
999 rt_atomic_add(&tty_nin, 1);
1000
1001 #ifdef USING_BSD_HOOK
1002 if (ttyhook_hashook(tp, rint))
1003 return ttyhook_rint(tp, c, flags);
1004 #endif
1005
1006 if (tp->t_flags & TF_BYPASS)
1007 goto processed;
1008
1009 if (flags)
1010 {
1011 if (flags & TRE_BREAK)
1012 {
1013 if (CMP_FLAG(i, IGNBRK))
1014 {
1015 /* Ignore break characters. */
1016 return 0;
1017 }
1018 else if (CMP_FLAG(i, BRKINT))
1019 {
1020 /* Generate SIGINT on break. */
1021 tty_flush(tp, FREAD | FWRITE);
1022 lwp_tty_signal_pgrp(tp, SIGINT);
1023 return 0;
1024 }
1025 else
1026 {
1027 /* Just print it. */
1028 goto parmrk;
1029 }
1030 }
1031 else if (flags & TRE_FRAMING ||
1032 (flags & TRE_PARITY && CMP_FLAG(i, INPCK)))
1033 {
1034 if (CMP_FLAG(i, IGNPAR))
1035 {
1036 /* Ignore bad characters. */
1037 return 0;
1038 }
1039 else
1040 {
1041 /* Just print it. */
1042 goto parmrk;
1043 }
1044 }
1045 }
1046
1047 /* Allow any character to perform a wakeup. */
1048 if (CMP_FLAG(i, IXANY))
1049 {
1050 tp->t_flags &= ~TF_STOPPED;
1051 tp->t_termios.c_lflag &= ~FLUSHO; /* FLUSHO: Output is being flushed */
1052 }
1053
1054 /* Remove the top bit. */
1055 if (CMP_FLAG(i, ISTRIP))
1056 c &= ~0x80;
1057
1058 /* Skip input processing when we want to print it literally. */
1059 if (tp->t_flags & TF_LITERAL)
1060 {
1061 tp->t_flags &= ~TF_LITERAL;
1062 quote = 1;
1063 goto processed;
1064 }
1065
1066 /* Special control characters that are implementation dependent. */
1067 if (CMP_FLAG(l, IEXTEN))
1068 {
1069 /* Accept the next character as literal. */
1070 if (CMP_CC(VLNEXT, c))
1071 {
1072 if (CMP_FLAG(l, ECHO))
1073 {
1074 if (CMP_FLAG(l, ECHOE))
1075 ttyoutq_write_nofrag(&tp->t_outq, "^\b", 2);
1076 else
1077 ttydisc_echo(tp, c, 0);
1078 }
1079 tp->t_flags |= TF_LITERAL;
1080 return 0;
1081 }
1082 /* Discard processing */
1083 if (CMP_CC(VDISCARD, c))
1084 {
1085 if (CMP_FLAG(l, FLUSHO))
1086 {
1087 tp->t_termios.c_lflag &= ~FLUSHO;
1088 }
1089 else
1090 {
1091 tty_flush(tp, FWRITE);
1092 ttydisc_echo(tp, c, 0);
1093 if (tp->t_inq.ti_end > 0)
1094 ttydisc_reprint(tp);
1095 tp->t_termios.c_lflag |= FLUSHO;
1096 }
1097 }
1098 }
1099
1100 /*
1101 * Handle signal processing.
1102 */
1103 if (CMP_FLAG(l, ISIG))
1104 {
1105 #ifdef USING_BSD_SIGINFO
1106 if (CMP_FLAG(l, ICANON | IEXTEN) == (ICANON | IEXTEN))
1107 {
1108 if (CMP_CC(VSTATUS, c))
1109 {
1110 tty_signal_pgrp(tp, SIGINFO);
1111 return 0;
1112 }
1113 }
1114 #endif /* USING_BSD_SIGINFO */
1115
1116 /*
1117 * When compared to the old implementation, this
1118 * implementation also flushes the output queue. POSIX
1119 * is really brief about this, but does makes us assume
1120 * we have to do so.
1121 */
1122 signal = 0;
1123 if (CMP_CC(VINTR, c))
1124 {
1125 signal = SIGINT;
1126 }
1127 else if (CMP_CC(VQUIT, c))
1128 {
1129 signal = SIGQUIT;
1130 }
1131 else if (CMP_CC(VSUSP, c))
1132 {
1133 signal = SIGTSTP;
1134 }
1135
1136 if (signal != 0)
1137 {
1138 /*
1139 * Echo the character before signalling the
1140 * processes.
1141 */
1142 if (!CMP_FLAG(l, NOFLSH))
1143 tty_flush(tp, FREAD | FWRITE);
1144 ttydisc_echo(tp, c, 0);
1145 lwp_tty_signal_pgrp(tp, signal);
1146 return 0;
1147 }
1148 }
1149
1150 /*
1151 * Handle start/stop characters.
1152 */
1153 if (CMP_FLAG(i, IXON))
1154 {
1155 if (CMP_CC(VSTOP, c))
1156 {
1157 /* Stop it if we aren't stopped yet. */
1158 if ((tp->t_flags & TF_STOPPED) == 0)
1159 {
1160 tp->t_flags |= TF_STOPPED;
1161 return 0;
1162 }
1163 /*
1164 * Fallthrough:
1165 * When VSTART == VSTOP, we should make this key
1166 * toggle it.
1167 */
1168 if (!CMP_CC(VSTART, c))
1169 return 0;
1170 }
1171 if (CMP_CC(VSTART, c))
1172 {
1173 tp->t_flags &= ~TF_STOPPED;
1174 return 0;
1175 }
1176 }
1177
1178 /* Conversion of CR and NL. */
1179 switch (c)
1180 {
1181 case CCR:
1182 if (CMP_FLAG(i, IGNCR))
1183 return 0;
1184 if (CMP_FLAG(i, ICRNL))
1185 c = CNL;
1186 break;
1187 case CNL:
1188 if (CMP_FLAG(i, INLCR))
1189 c = CCR;
1190 break;
1191 }
1192
1193 /* Canonical line editing. */
1194 if (CMP_FLAG(l, ICANON))
1195 {
1196 if (CMP_CC(VERASE, c)
1197 #ifdef VERASE2
1198 || CMP_CC(VERASE2, c)
1199 #endif
1200 )
1201 {
1202 ttydisc_rubchar(tp);
1203 return 0;
1204 }
1205 else if (CMP_CC(VKILL, c))
1206 {
1207 while (ttydisc_rubchar(tp) == 0)
1208 ;
1209 return 0;
1210 }
1211 else if (CMP_FLAG(l, IEXTEN))
1212 {
1213 if (CMP_CC(VWERASE, c))
1214 {
1215 ttydisc_rubword(tp);
1216 return 0;
1217 }
1218 else if (CMP_CC(VREPRINT, c))
1219 {
1220 ttydisc_reprint(tp);
1221 return 0;
1222 }
1223 }
1224 }
1225
1226 processed:
1227 if (CMP_FLAG(i, PARMRK) && (unsigned char)c == 0xff)
1228 {
1229 /* Print 0xff 0xff. */
1230 ob[1] = 0xff;
1231 ol = 2;
1232 quote = 1;
1233 }
1234 else
1235 {
1236 ob[0] = c;
1237 ol = 1;
1238 }
1239
1240 goto print;
1241
1242 parmrk:
1243 if (CMP_FLAG(i, PARMRK))
1244 {
1245 /* Prepend 0xff 0x00 0x.. */
1246 ob[2] = c;
1247 ol = 3;
1248 quote = 1;
1249 }
1250 else
1251 {
1252 ob[0] = c;
1253 ol = 1;
1254 }
1255
1256 print:
1257 /* See if we can store this on the input queue. */
1258 if (ttyinq_write_nofrag(&tp->t_inq, ob, ol, quote) != 0)
1259 {
1260 if (CMP_FLAG(i, IMAXBEL))
1261 ttyoutq_write_nofrag(&tp->t_outq, "\a", 1);
1262
1263 /*
1264 * Prevent a deadlock here. It may be possible that a
1265 * user has entered so much data, there is no data
1266 * available to read(), but the buffers are full anyway.
1267 *
1268 * Only enter the high watermark if the device driver
1269 * can actually transmit something.
1270 */
1271 if (ttyinq_bytescanonicalized(&tp->t_inq) == 0)
1272 return 0;
1273
1274 tty_hiwat_in_block(tp);
1275 return (-1);
1276 }
1277
1278 /*
1279 * In raw mode, we canonicalize after receiving a single
1280 * character. Otherwise, we canonicalize when we receive a
1281 * newline, VEOL or VEOF, but only when it isn't quoted.
1282 */
1283 if (!CMP_FLAG(l, ICANON) ||
1284 (!quote && (c == CNL || CMP_CC(VEOL, c) || CMP_CC(VEOF, c))))
1285 {
1286 ttyinq_canonicalize(&tp->t_inq);
1287 }
1288
1289 ttydisc_echo(tp, c, quote);
1290
1291 return 0;
1292 }
1293
ttydisc_rint_simple(struct lwp_tty * tp,const void * buf,size_t len)1294 size_t ttydisc_rint_simple(struct lwp_tty *tp, const void *buf, size_t len)
1295 {
1296 const char *cbuf;
1297
1298 if (ttydisc_can_bypass(tp))
1299 return (ttydisc_rint_bypass(tp, buf, len));
1300
1301 for (cbuf = buf; len-- > 0; cbuf++)
1302 {
1303 if (ttydisc_rint(tp, *cbuf, 0) != 0)
1304 break;
1305 }
1306
1307 return (cbuf - (const char *)buf);
1308 }
1309
ttydisc_rint_bypass(struct lwp_tty * tp,const void * buf,size_t len)1310 size_t ttydisc_rint_bypass(struct lwp_tty *tp, const void *buf, size_t len)
1311 {
1312 size_t ret;
1313
1314 tty_assert_locked(tp);
1315
1316 MPASS(tp->t_flags & TF_BYPASS);
1317
1318 rt_atomic_add(&tty_nin, len);
1319
1320 #ifdef USING_BSD_HOOK
1321 if (ttyhook_hashook(tp, rint_bypass))
1322 return ttyhook_rint_bypass(tp, buf, len);
1323 #endif
1324
1325 ret = ttyinq_write(&tp->t_inq, buf, len, 0);
1326 ttyinq_canonicalize(&tp->t_inq);
1327 if (ret < len)
1328 tty_hiwat_in_block(tp);
1329
1330 return (ret);
1331 }
1332
ttydisc_rint_done(struct lwp_tty * tp)1333 void ttydisc_rint_done(struct lwp_tty *tp)
1334 {
1335 tty_assert_locked(tp);
1336
1337 #ifdef USING_BSD_HOOK
1338 if (ttyhook_hashook(tp, rint_done))
1339 ttyhook_rint_done(tp);
1340 #endif
1341
1342 /* Wake up readers. */
1343 tty_wakeup(tp, FREAD);
1344 /* Wake up driver for echo. */
1345 ttydevsw_outwakeup(tp);
1346 }
1347
ttydisc_rint_poll(struct lwp_tty * tp)1348 size_t ttydisc_rint_poll(struct lwp_tty *tp)
1349 {
1350 size_t l;
1351
1352 tty_assert_locked(tp);
1353
1354 #ifdef USING_BSD_HOOK
1355 if (ttyhook_hashook(tp, rint_poll))
1356 return ttyhook_rint_poll(tp);
1357 #endif
1358
1359 /*
1360 * XXX: Still allow character input when there's no space in the
1361 * buffers, but we haven't entered the high watermark. This is
1362 * to allow backspace characters to be inserted when in
1363 * canonical mode.
1364 */
1365 l = ttyinq_bytesleft(&tp->t_inq);
1366 if (l == 0 && (tp->t_flags & TF_HIWAT_IN) == 0)
1367 return (1);
1368
1369 return (l);
1370 }
1371
ttydisc_wakeup_watermark(struct lwp_tty * tp)1372 static void ttydisc_wakeup_watermark(struct lwp_tty *tp)
1373 {
1374 size_t c;
1375
1376 c = ttyoutq_bytesleft(&tp->t_outq);
1377 if (tp->t_flags & TF_HIWAT_OUT)
1378 {
1379 /* Only allow us to run when we're below the watermark. */
1380 if (c < tp->t_outlow)
1381 return;
1382
1383 /* Reset the watermark. */
1384 tp->t_flags &= ~TF_HIWAT_OUT;
1385 }
1386 else
1387 {
1388 /* Only run when we have data at all. */
1389 if (c == 0)
1390 return;
1391 }
1392 tty_wakeup(tp, FWRITE);
1393 }
1394
ttydisc_getc(struct lwp_tty * tp,void * buf,size_t len)1395 size_t ttydisc_getc(struct lwp_tty *tp, void *buf, size_t len)
1396 {
1397 tty_assert_locked(tp);
1398
1399 if (tp->t_flags & TF_STOPPED)
1400 return 0;
1401
1402 #ifdef USING_BSD_HOOK
1403 if (ttyhook_hashook(tp, getc_inject))
1404 return ttyhook_getc_inject(tp, buf, len);
1405 #endif
1406
1407 len = ttyoutq_read(&tp->t_outq, buf, len);
1408
1409 #ifdef USING_BSD_HOOK
1410 if (ttyhook_hashook(tp, getc_capture))
1411 ttyhook_getc_capture(tp, buf, len);
1412 #endif
1413
1414 ttydisc_wakeup_watermark(tp);
1415 rt_atomic_add(&tty_nout, len);
1416
1417 return (len);
1418 }
1419
ttydisc_getc_uio(struct lwp_tty * tp,struct uio * uio)1420 int ttydisc_getc_uio(struct lwp_tty *tp, struct uio *uio)
1421 {
1422 int error = 0;
1423 ssize_t obytes = uio->uio_resid;
1424 size_t len;
1425 char buf[TTY_STACKBUF];
1426
1427 tty_assert_locked(tp);
1428
1429 if (tp->t_flags & TF_STOPPED)
1430 return 0;
1431
1432 /*
1433 * When a TTY hook is attached, we cannot perform unbuffered
1434 * copying to userspace. Just call ttydisc_getc() and
1435 * temporarily store data in a shadow buffer.
1436 */
1437 if (ttyhook_hashook(tp, getc_capture) || ttyhook_hashook(tp, getc_inject))
1438 {
1439 while (uio->uio_resid > 0)
1440 {
1441 /* Read to shadow buffer. */
1442 len = ttydisc_getc(tp, buf, MIN(uio->uio_resid, sizeof buf));
1443 if (len == 0)
1444 break;
1445
1446 /* Copy to userspace. */
1447 tty_unlock(tp);
1448 error = uiomove(buf, len, uio);
1449 tty_lock(tp);
1450
1451 if (error != 0)
1452 break;
1453 }
1454 }
1455 else
1456 {
1457 error = ttyoutq_read_uio(&tp->t_outq, tp, uio);
1458
1459 ttydisc_wakeup_watermark(tp);
1460 rt_atomic_add(&tty_nout, obytes - uio->uio_resid);
1461 }
1462
1463 return error;
1464 }
1465
ttydisc_getc_poll(struct lwp_tty * tp)1466 size_t ttydisc_getc_poll(struct lwp_tty *tp)
1467 {
1468 tty_assert_locked(tp);
1469
1470 if (tp->t_flags & TF_STOPPED)
1471 return 0;
1472
1473 #if USING_BSD_HOOK
1474 if (ttyhook_hashook(tp, getc_poll))
1475 return ttyhook_getc_poll(tp);
1476 #endif
1477
1478 return ttyoutq_bytesused(&tp->t_outq);
1479 }
1480