diff --git a/bin/stty/modes.c b/bin/stty/modes.c index ce39083d7d63..3818677ddc4e 100644 --- a/bin/stty/modes.c +++ b/bin/stty/modes.c @@ -1,245 +1,247 @@ /*- * Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)modes.c 8.3 (Berkeley) 4/2/94"; #endif #endif /* not lint */ #include #include #include #include "stty.h" int msearch(char ***, struct info *); struct modes { const char *name; long set; long unset; }; /* * The code in optlist() depends on minus options following regular * options, i.e. "foo" must immediately precede "-foo". */ static const struct modes cmodes[] = { { "cs5", CS5, CSIZE }, { "cs6", CS6, CSIZE }, { "cs7", CS7, CSIZE }, { "cs8", CS8, CSIZE }, { "cstopb", CSTOPB, 0 }, { "-cstopb", 0, CSTOPB }, { "cread", CREAD, 0 }, { "-cread", 0, CREAD }, { "parenb", PARENB, 0 }, { "-parenb", 0, PARENB }, { "parodd", PARODD, 0 }, { "-parodd", 0, PARODD }, { "parity", PARENB | CS7, PARODD | CSIZE }, { "-parity", CS8, PARODD | PARENB | CSIZE }, { "evenp", PARENB | CS7, PARODD | CSIZE }, { "-evenp", CS8, PARODD | PARENB | CSIZE }, { "oddp", PARENB | CS7 | PARODD, CSIZE }, { "-oddp", CS8, PARODD | PARENB | CSIZE }, { "pass8", CS8, PARODD | PARENB | CSIZE }, { "-pass8", PARENB | CS7, PARODD | CSIZE }, { "hupcl", HUPCL, 0 }, { "-hupcl", 0, HUPCL }, { "hup", HUPCL, 0 }, { "-hup", 0, HUPCL }, { "clocal", CLOCAL, 0 }, { "-clocal", 0, CLOCAL }, { "crtscts", CRTSCTS, 0 }, { "-crtscts", 0, CRTSCTS }, { "ctsflow", CCTS_OFLOW, 0 }, { "-ctsflow", 0, CCTS_OFLOW }, { "dsrflow", CDSR_OFLOW, 0 }, { "-dsrflow", 0, CDSR_OFLOW }, { "dtrflow", CDTR_IFLOW, 0 }, { "-dtrflow", 0, CDTR_IFLOW }, { "rtsflow", CRTS_IFLOW, 0 }, { "-rtsflow", 0, CRTS_IFLOW }, { "mdmbuf", MDMBUF, 0 }, { "-mdmbuf", 0, MDMBUF }, { "rtsdtr", 0, CNO_RTSDTR }, { "-rtsdtr", CNO_RTSDTR, 0 }, { NULL, 0, 0 }, }; static const struct modes imodes[] = { { "ignbrk", IGNBRK, 0 }, { "-ignbrk", 0, IGNBRK }, { "brkint", BRKINT, 0 }, { "-brkint", 0, BRKINT }, { "ignpar", IGNPAR, 0 }, { "-ignpar", 0, IGNPAR }, { "parmrk", PARMRK, 0 }, { "-parmrk", 0, PARMRK }, { "inpck", INPCK, 0 }, { "-inpck", 0, INPCK }, { "istrip", ISTRIP, 0 }, { "-istrip", 0, ISTRIP }, { "inlcr", INLCR, 0 }, { "-inlcr", 0, INLCR }, { "igncr", IGNCR, 0 }, { "-igncr", 0, IGNCR }, { "icrnl", ICRNL, 0 }, { "-icrnl", 0, ICRNL }, { "ixon", IXON, 0 }, { "-ixon", 0, IXON }, { "flow", IXON, 0 }, { "-flow", 0, IXON }, { "ixoff", IXOFF, 0 }, { "-ixoff", 0, IXOFF }, { "tandem", IXOFF, 0 }, { "-tandem", 0, IXOFF }, { "ixany", IXANY, 0 }, { "-ixany", 0, IXANY }, { "decctlq", 0, IXANY }, { "-decctlq", IXANY, 0 }, { "imaxbel", IMAXBEL, 0 }, { "-imaxbel", 0, IMAXBEL }, + { "iutf8", IUTF8, 0 }, + { "-iutf8", 0, IUTF8 }, { NULL, 0, 0 }, }; static const struct modes lmodes[] = { { "echo", ECHO, 0 }, { "-echo", 0, ECHO }, { "echoe", ECHOE, 0 }, { "-echoe", 0, ECHOE }, { "crterase", ECHOE, 0 }, { "-crterase", 0, ECHOE }, { "crtbs", ECHOE, 0 }, /* crtbs not supported, close enough */ { "-crtbs", 0, ECHOE }, { "echok", ECHOK, 0 }, { "-echok", 0, ECHOK }, { "echoke", ECHOKE, 0 }, { "-echoke", 0, ECHOKE }, { "crtkill", ECHOKE, 0 }, { "-crtkill", 0, ECHOKE }, { "altwerase", ALTWERASE, 0 }, { "-altwerase", 0, ALTWERASE }, { "iexten", IEXTEN, 0 }, { "-iexten", 0, IEXTEN }, { "echonl", ECHONL, 0 }, { "-echonl", 0, ECHONL }, { "echoctl", ECHOCTL, 0 }, { "-echoctl", 0, ECHOCTL }, { "ctlecho", ECHOCTL, 0 }, { "-ctlecho", 0, ECHOCTL }, { "echoprt", ECHOPRT, 0 }, { "-echoprt", 0, ECHOPRT }, { "prterase", ECHOPRT, 0 }, { "-prterase", 0, ECHOPRT }, { "isig", ISIG, 0 }, { "-isig", 0, ISIG }, { "icanon", ICANON, 0 }, { "-icanon", 0, ICANON }, { "noflsh", NOFLSH, 0 }, { "-noflsh", 0, NOFLSH }, { "tostop", TOSTOP, 0 }, { "-tostop", 0, TOSTOP }, { "flusho", FLUSHO, 0 }, { "-flusho", 0, FLUSHO }, { "pendin", PENDIN, 0 }, { "-pendin", 0, PENDIN }, { "crt", ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT }, { "-crt", ECHOK, ECHOE|ECHOKE|ECHOCTL }, { "newcrt", ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT }, { "-newcrt", ECHOK, ECHOE|ECHOKE|ECHOCTL }, { "nokerninfo", NOKERNINFO, 0 }, { "-nokerninfo",0, NOKERNINFO }, { "kerninfo", 0, NOKERNINFO }, { "-kerninfo", NOKERNINFO, 0 }, { NULL, 0, 0 }, }; static const struct modes omodes[] = { { "opost", OPOST, 0 }, { "-opost", 0, OPOST }, { "litout", 0, OPOST }, { "-litout", OPOST, 0 }, { "onlcr", ONLCR, 0 }, { "-onlcr", 0, ONLCR }, { "ocrnl", OCRNL, 0 }, { "-ocrnl", 0, OCRNL }, { "tabs", TAB0, TABDLY }, /* "preserve" tabs */ { "-tabs", TAB3, TABDLY }, { "oxtabs", TAB3, TABDLY }, { "-oxtabs", TAB0, TABDLY }, { "tab0", TAB0, TABDLY }, { "tab3", TAB3, TABDLY }, { "onocr", ONOCR, 0 }, { "-onocr", 0, ONOCR }, { "onlret", ONLRET, 0 }, { "-onlret", 0, ONLRET }, { NULL, 0, 0 }, }; #define CHK(s) (*name == s[0] && !strcmp(name, s)) int msearch(char ***argvp, struct info *ip) { const struct modes *mp; char *name; name = **argvp; for (mp = cmodes; mp->name; ++mp) if (CHK(mp->name)) { ip->t.c_cflag &= ~mp->unset; ip->t.c_cflag |= mp->set; ip->set = 1; return (1); } for (mp = imodes; mp->name; ++mp) if (CHK(mp->name)) { ip->t.c_iflag &= ~mp->unset; ip->t.c_iflag |= mp->set; ip->set = 1; return (1); } for (mp = lmodes; mp->name; ++mp) if (CHK(mp->name)) { ip->t.c_lflag &= ~mp->unset; ip->t.c_lflag |= mp->set; ip->set = 1; return (1); } for (mp = omodes; mp->name; ++mp) if (CHK(mp->name)) { ip->t.c_oflag &= ~mp->unset; ip->t.c_oflag |= mp->set; ip->set = 1; return (1); } return (0); } diff --git a/bin/stty/print.c b/bin/stty/print.c index 07a3c2058474..33472bbccea1 100644 --- a/bin/stty/print.c +++ b/bin/stty/print.c @@ -1,285 +1,286 @@ /*- * Copyright (c) 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)print.c 8.6 (Berkeley) 4/16/94"; #endif #endif /* not lint */ #include #include #include #include #include #include "stty.h" #include "extern.h" static void binit(const char *); static void bput(const char *); static const char *ccval(struct cchar *, int); void print(struct termios *tp, struct winsize *wp, int ldisc, enum FMT fmt) { struct cchar *p; long tmp; u_char *cc; int cnt, ispeed, ospeed; char buf1[100], buf2[100]; cnt = 0; /* Line discipline. */ if (ldisc != TTYDISC) { switch(ldisc) { case SLIPDISC: cnt += printf("slip disc; "); break; case PPPDISC: cnt += printf("ppp disc; "); break; default: cnt += printf("#%d disc; ", ldisc); break; } } /* Line speed. */ ispeed = cfgetispeed(tp); ospeed = cfgetospeed(tp); if (ispeed != ospeed) cnt += printf("ispeed %d baud; ospeed %d baud;", ispeed, ospeed); else cnt += printf("speed %d baud;", ispeed); if (fmt >= BSD) cnt += printf(" %d rows; %d columns;", wp->ws_row, wp->ws_col); if (cnt) (void)printf("\n"); #define on(f) ((tmp & (f)) != 0) #define put(n, f, d) \ if (fmt >= BSD || on(f) != (d)) \ bput((n) + on(f)); /* "local" flags */ tmp = tp->c_lflag; binit("lflags"); put("-icanon", ICANON, 1); put("-isig", ISIG, 1); put("-iexten", IEXTEN, 1); put("-echo", ECHO, 1); put("-echoe", ECHOE, 0); put("-echok", ECHOK, 0); put("-echoke", ECHOKE, 0); put("-echonl", ECHONL, 0); put("-echoctl", ECHOCTL, 0); put("-echoprt", ECHOPRT, 0); put("-altwerase", ALTWERASE, 0); put("-noflsh", NOFLSH, 0); put("-tostop", TOSTOP, 0); put("-flusho", FLUSHO, 0); put("-pendin", PENDIN, 0); put("-nokerninfo", NOKERNINFO, 0); put("-extproc", EXTPROC, 0); /* input flags */ tmp = tp->c_iflag; binit("iflags"); put("-istrip", ISTRIP, 0); put("-icrnl", ICRNL, 1); put("-inlcr", INLCR, 0); put("-igncr", IGNCR, 0); put("-ixon", IXON, 1); put("-ixoff", IXOFF, 0); put("-ixany", IXANY, 1); put("-imaxbel", IMAXBEL, 1); put("-ignbrk", IGNBRK, 0); put("-brkint", BRKINT, 1); put("-inpck", INPCK, 0); put("-ignpar", IGNPAR, 0); put("-parmrk", PARMRK, 0); + put("-iutf8", IUTF8, 1); /* output flags */ tmp = tp->c_oflag; binit("oflags"); put("-opost", OPOST, 1); put("-onlcr", ONLCR, 1); put("-ocrnl", OCRNL, 0); switch(tmp&TABDLY) { case TAB0: bput("tab0"); break; case TAB3: bput("tab3"); break; } put("-onocr", ONOCR, 0); put("-onlret", ONLRET, 0); /* control flags (hardware state) */ tmp = tp->c_cflag; binit("cflags"); put("-cread", CREAD, 1); switch(tmp&CSIZE) { case CS5: bput("cs5"); break; case CS6: bput("cs6"); break; case CS7: bput("cs7"); break; case CS8: bput("cs8"); break; } bput("-parenb" + on(PARENB)); put("-parodd", PARODD, 0); put("-hupcl", HUPCL, 1); put("-clocal", CLOCAL, 0); put("-cstopb", CSTOPB, 0); switch(tmp & (CCTS_OFLOW | CRTS_IFLOW)) { case CCTS_OFLOW: bput("ctsflow"); break; case CRTS_IFLOW: bput("rtsflow"); break; default: put("-crtscts", CCTS_OFLOW | CRTS_IFLOW, 0); break; } put("-dsrflow", CDSR_OFLOW, 0); put("-dtrflow", CDTR_IFLOW, 0); put("-mdmbuf", MDMBUF, 0); /* XXX mdmbuf == dtrflow */ if (on(CNO_RTSDTR)) bput("-rtsdtr"); else { if (fmt >= BSD) bput("rtsdtr"); } /* special control characters */ cc = tp->c_cc; if (fmt == POSIX) { binit("cchars"); for (p = cchars1; p->name; ++p) { (void)snprintf(buf1, sizeof(buf1), "%s = %s;", p->name, ccval(p, cc[p->sub])); bput(buf1); } binit(NULL); } else { binit(NULL); for (p = cchars1, cnt = 0; p->name; ++p) { if (fmt != BSD && cc[p->sub] == p->def) continue; #define WD "%-8s" (void)snprintf(buf1 + cnt * 8, sizeof(buf1) - cnt * 8, WD, p->name); (void)snprintf(buf2 + cnt * 8, sizeof(buf2) - cnt * 8, WD, ccval(p, cc[p->sub])); if (++cnt == LINELENGTH / 8) { cnt = 0; (void)printf("%s\n", buf1); (void)printf("%s\n", buf2); } } if (cnt) { (void)printf("%s\n", buf1); (void)printf("%s\n", buf2); } } } static int col; static const char *label; static void binit(const char *lb) { if (col) { (void)printf("\n"); col = 0; } label = lb; } static void bput(const char *s) { if (col == 0) { col = printf("%s: %s", label, s); return; } if ((col + strlen(s)) > LINELENGTH) { (void)printf("\n\t"); col = printf("%s", s) + 8; return; } col += printf(" %s", s); } static const char * ccval(struct cchar *p, int c) { static char buf[5]; char *bp; if (p->sub == VMIN || p->sub == VTIME) { (void)snprintf(buf, sizeof(buf), "%d", c); return (buf); } if (c == _POSIX_VDISABLE) return (""); bp = buf; if (c & 0200) { *bp++ = 'M'; *bp++ = '-'; c &= 0177; } if (c == 0177) { *bp++ = '^'; *bp++ = '?'; } else if (c < 040) { *bp++ = '^'; *bp++ = c + '@'; } else *bp++ = c; *bp = '\0'; return (buf); } diff --git a/bin/stty/stty.1 b/bin/stty/stty.1 index 9e9405928c67..c572a657a997 100644 --- a/bin/stty/stty.1 +++ b/bin/stty/stty.1 @@ -1,644 +1,647 @@ .\"- .\" Copyright (c) 1990, 1993, 1994 .\" The Regents of the University of California. All rights reserved. .\" .\" This code is derived from software contributed to Berkeley by .\" the Institute of Electrical and Electronics Engineers, Inc. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" @(#)stty.1 8.4 (Berkeley) 4/18/94 .\" .Dd September 27, 2022 .Dt STTY 1 .Os .Sh NAME .Nm stty .Nd set the options for a terminal device interface .Sh SYNOPSIS .Nm .Op Fl a | e | g .Op Fl f Ar file .Op Ar arguments .Sh DESCRIPTION The .Nm utility sets or reports on terminal characteristics for the device that is its standard input. If no options or arguments are specified, it reports the settings of a subset of characteristics as well as additional ones if they differ from their default values. Otherwise it modifies the terminal state according to the specified arguments. Some combinations of arguments are mutually exclusive on some terminal types. .Pp The following options are available: .Bl -tag -width indent .It Fl a Display all the current settings for the terminal to standard output as per .St -p1003.2 . .It Fl e Display all the current settings for the terminal to standard output in the traditional .Bx ``all'' and ``everything'' formats. .It Fl f Open and use the terminal named by .Ar file rather than using standard input. The file is opened using the .Dv O_NONBLOCK flag of .Fn open , making it possible to set or display settings on a terminal that might otherwise block on the open. .It Fl g Display all the current settings for the terminal to standard output in a form that may be used as an argument to a subsequent invocation of .Nm to restore the current terminal state as per .St -p1003.2 . .El .Pp The following arguments are available to set the terminal characteristics: .Ss Control Modes: Control mode flags affect hardware characteristics associated with the terminal. This corresponds to the c_cflag in the termios structure. .Bl -tag -width Fl .It Cm parenb Pq Fl parenb Enable (disable) parity generation and detection. .It Cm parodd Pq Fl parodd Select odd (even) parity. .It Cm cs5 cs6 cs7 cs8 Select character size, if possible. .It Ar number Set terminal baud rate to the number given, if possible. If the baud rate is set to zero, modem control is no longer asserted. .It Cm ispeed Ar number Set terminal input baud rate to the number given, if possible. If the input baud rate is set to zero, the input baud rate is set to the value of the output baud rate. .It Cm ospeed Ar number Set terminal output baud rate to the number given, if possible. If the output baud rate is set to zero, modem control is no longer asserted. .It Cm speed Ar number This sets both .Cm ispeed and .Cm ospeed to .Ar number . .It Cm hupcl Pq Fl hupcl Stop asserting modem control (do not stop asserting modem control) on last close. .It Cm hup Pq Fl hup Same as hupcl .Pq Fl hupcl . .It Cm cstopb Pq Fl cstopb Use two (one) stop bits per character. .It Cm cread Pq Fl cread Enable (disable) the receiver. .It Cm clocal Pq Fl clocal Assume a line without (with) modem control. .It Cm crtscts Pq Fl crtscts Enable (disable) RTS/CTS flow control. .It Cm rtsdtr Pq Fl rtsdtr Enable (disable) asserting RTS/DTR on open. .El .Ss Input Modes: This corresponds to the c_iflag in the termios structure. .Bl -tag -width Fl .It Cm ignbrk Pq Fl ignbrk Ignore (do not ignore) break on input. .It Cm brkint Pq Fl brkint Signal (do not signal) .Dv INTR on break. .It Cm ignpar Pq Fl ignpar Ignore (do not ignore) characters with parity errors. .It Cm parmrk Pq Fl parmrk Mark (do not mark) characters with parity errors. .It Cm inpck Pq Fl inpck Enable (disable) input parity checking. .It Cm istrip Pq Fl istrip Strip (do not strip) input characters to seven bits. .It Cm inlcr Pq Fl inlcr Map (do not map) .Dv NL to .Dv CR on input. .It Cm igncr Pq Fl igncr Ignore (do not ignore) .Dv CR on input. .It Cm icrnl Pq Fl icrnl Map (do not map) .Dv CR to .Dv NL on input. .It Cm ixon Pq Fl ixon Enable (disable) .Dv START/STOP output control. Output from the system is stopped when the system receives .Dv STOP and started when the system receives .Dv START , or if .Cm ixany is set, any character restarts output. .It Cm ixoff Pq Fl ixoff Request that the system send (not send) .Dv START/STOP characters when the input queue is nearly empty/full. .It Cm ixany Pq Fl ixany Allow any character (allow only .Dv START ) to restart output. .It Cm imaxbel Pq Fl imaxbel The system imposes a limit of .Dv MAX_INPUT (currently 255) characters in the input queue. If .Cm imaxbel is set and the input queue limit has been reached, subsequent input causes the system to send an ASCII BEL character to the output queue (the terminal beeps at you). Otherwise, if .Cm imaxbel is unset and the input queue is full, the next input character causes the entire input and output queues to be discarded. +.It Cm iutf8 Pq Fl iutf8 +Assume that input characters are UTF-8 encoded. Setting this flag +causes backspace to properly delete multibyte characters in canonical mode. .El .Ss Output Modes: This corresponds to the c_oflag of the termios structure. .Bl -tag -width Fl .It Cm opost Pq Fl opost Post-process output (do not post-process output; ignore all other output modes). .It Cm onlcr Pq Fl onlcr Map (do not map) .Dv NL to .Dv CR-NL on output. .It Cm ocrnl Pq Fl ocrnl Map (do not map) .Dv CR to .Dv NL on output. .It Cm tab0 tab3 Select tab expansion policy. .Cm tab0 disables tab expansion, while .Cm tab3 enables it. .It Cm onocr Pq Fl onocr Do not (do) output CRs at column zero. .It Cm onlret Pq Fl onlret On the terminal NL performs (does not perform) the CR function. .El .Ss Local Modes: Local mode flags (lflags) affect various and sundry characteristics of terminal processing. Historically the term "local" pertained to new job control features implemented by Jim Kulp on a .Tn Pdp 11/70 at .Tn IIASA . Later the driver ran on the first .Tn VAX at Evans Hall, UC Berkeley, where the job control details were greatly modified but the structure definitions and names remained essentially unchanged. The second interpretation of the 'l' in lflag is ``line discipline flag'' which corresponds to the .Ar c_lflag of the .Ar termios structure. .Bl -tag -width Fl .It Cm isig Pq Fl isig Enable (disable) the checking of characters against the special control characters .Dv INTR , QUIT , and .Dv SUSP . .It Cm icanon Pq Fl icanon Enable (disable) canonical input .Pf ( Dv ERASE and .Dv KILL processing). .It Cm iexten Pq Fl iexten Enable (disable) any implementation defined special control characters not currently controlled by icanon, isig, or ixon. .It Cm echo Pq Fl echo Echo back (do not echo back) every character typed. .It Cm echoe Pq Fl echoe The .Dv ERASE character shall (shall not) visually erase the last character in the current line from the display, if possible. .It Cm echok Pq Fl echok Echo (do not echo) .Dv NL after .Dv KILL character. .It Cm echoke Pq Fl echoke The .Dv KILL character shall (shall not) visually erase the current line from the display, if possible. .It Cm echonl Pq Fl echonl Echo (do not echo) .Dv NL , even if echo is disabled. .It Cm echoctl Pq Fl echoctl If .Cm echoctl is set, echo control characters as ^X. Otherwise control characters echo as themselves. .It Cm echoprt Pq Fl echoprt For printing terminals. If set, echo erased characters backwards within ``\\'' and ``/''. Otherwise, disable this feature. .It Cm noflsh Pq Fl noflsh Disable (enable) flush after .Dv INTR , QUIT , SUSP . .It Cm tostop Pq Fl tostop Send (do not send) .Dv SIGTTOU for background output. This causes background jobs to stop if they attempt terminal output. .It Cm altwerase Pq Fl altwerase Use (do not use) an alternate word erase algorithm when processing .Dv WERASE characters. This alternate algorithm considers sequences of alphanumeric/underscores as words. It also skips the first preceding character in its classification (as a convenience since the one preceding character could have been erased with simply an .Dv ERASE character.) .It Cm mdmbuf Pq Fl mdmbuf If set, flow control output based on condition of Carrier Detect. Otherwise writes return an error if Carrier Detect is low (and Carrier is not being ignored with the .Dv CLOCAL flag.) .It Cm flusho Pq Fl flusho Indicates output is (is not) being discarded. .It Cm pendin Pq Fl pendin Indicates input is (is not) pending after a switch from non-canonical to canonical mode and will be re-input when a read becomes pending or more input arrives. .El .Ss Control Characters: .Bl -tag -width Fl .It Ar control-character Ar string Set .Ar control-character to .Ar string . If string is a single character, the control character is set to that character. If string is the two character sequence "^-" or the string "undef" the control character is disabled (i.e., set to .Pf { Dv _POSIX_VDISABLE Ns } . ) .Pp Recognized control-characters: .Bd -ragged -offset indent .Bl -column character Subscript .It control- Ta \& Ta \& .It character Ta Subscript Ta Description .It _________ Ta _________ Ta _______________ .It eof Ta Tn VEOF Ta EOF No character .It eol Ta Tn VEOL Ta EOL No character .It eol2 Ta Tn VEOL2 Ta EOL2 No character .It erase Ta Tn VERASE Ta ERASE No character .It erase2 Ta Tn VERASE2 Ta ERASE2 No character .It werase Ta Tn VWERASE Ta WERASE No character .It intr Ta Tn VINTR Ta INTR No character .It kill Ta Tn VKILL Ta KILL No character .It quit Ta Tn VQUIT Ta QUIT No character .It susp Ta Tn VSUSP Ta SUSP No character .It start Ta Tn VSTART Ta START No character .It stop Ta Tn VSTOP Ta STOP No character .It dsusp Ta Tn VDSUSP Ta DSUSP No character .It lnext Ta Tn VLNEXT Ta LNEXT No character .It reprint Ta Tn VREPRINT Ta REPRINT No character .It status Ta Tn VSTATUS Ta STATUS No character .El .Ed .It Cm min Ar number .It Cm time Ar number Set the value of min or time to number. .Dv MIN and .Dv TIME are used in Non-Canonical mode input processing (-icanon). .El .Ss Combination Modes: .Bl -tag -width Fl .It Ar saved settings Set the current terminal characteristics to the saved settings produced by the .Fl g option. .It Cm evenp No or Cm parity Enable parenb and cs7; disable parodd. .It Cm oddp Enable parenb, cs7, and parodd. .It Fl parity , evenp , oddp Disable parenb, and set cs8. .It Cm \&nl Pq Fl \&nl Enable (disable) icrnl. In addition -nl unsets inlcr and igncr. .It Cm ek Reset .Dv ERASE , .Dv ERASE2 , and .Dv KILL characters back to system defaults. .It Cm sane Resets all modes to reasonable values for interactive terminal use. .It Cm tty Set the line discipline to the standard terminal line discipline .Dv TTYDISC . .It Cm crt Pq Fl crt Set (disable) all modes suitable for a CRT display device. .It Cm kerninfo Pq Fl kerninfo Enable (disable) the system generated status line associated with processing a .Dv STATUS character (usually set to ^T). The status line consists of the system load average, the current command name, its process ID, the event the process is waiting on (or the status of the process), the user and system times, percent cpu, and current memory usage. .Pp If the .Xr sysctl 8 variable .Va kern.tty_info_kstacks is set to a non-zero value, the status message also includes the kernel program stack of the foreground thread. .It Cm columns Ar number The terminal size is recorded as having .Ar number columns. .It Cm cols Ar number is an alias for .Cm columns . .It Cm rows Ar number The terminal size is recorded as having .Ar number rows. .It Cm dec Set modes suitable for users of Digital Equipment Corporation systems .Dv ( ERASE , .Dv KILL , and .Dv INTR characters are set to ^?, ^U, and ^C; .Dv ixany is disabled, and .Dv crt is enabled.) .It Cm extproc Pq Fl extproc If set, this flag indicates that some amount of terminal processing is being performed by either the terminal hardware or by the remote side connected to a pty. .It Cm raw Pq Fl raw If set, change the modes of the terminal so that no input or output processing is performed. If unset, change the modes of the terminal to some reasonable state that performs input and output processing. Note that since the terminal driver no longer has a single .Dv RAW bit, it is not possible to intuit what flags were set prior to setting .Cm raw . This means that unsetting .Cm raw may not put back all the setting that were previously in effect. To set the terminal into a raw state and then accurately restore it, the following shell code is recommended: .Bd -literal save_state=$(stty -g) stty raw \&... stty "$save_state" .Ed .It Cm size The size of the terminal is printed as two numbers on a single line, first rows, then columns. .El .Ss Compatibility Modes: These modes remain for compatibility with the previous version of the .Nm command. .Bl -tag -width Fl .It Cm all Reports all the terminal modes as with .Cm stty Fl a except that the control characters are printed in a columnar format. .It Cm everything Same as .Cm all . .It Cm cooked Same as .Cm sane . .It Cm cbreak If set, enables .Cm brkint , ixon , imaxbel , opost , .Cm isig , iexten , and .Fl icanon . If unset, same as .Cm sane . .It Cm new Same as .Cm tty . .It Cm old Same as .Cm tty . .It Cm newcrt Pq Fl newcrt Same as .Cm crt . .It Cm pass8 The converse of .Cm parity . .It Cm tandem Pq Fl tandem Same as .Cm ixoff . .It Cm decctlq Pq Fl decctlq The converse of .Cm ixany . .It Cm crterase Pq Fl crterase Same as .Cm echoe . .It Cm crtbs Pq Fl crtbs Same as .Cm echoe . .It Cm crtkill Pq Fl crtkill Same as .Cm echoke . .It Cm ctlecho Pq Fl ctlecho Same as .Cm echoctl . .It Cm prterase Pq Fl prterase Same as .Cm echoprt . .It Cm litout Pq Fl litout The converse of .Cm opost . .It Cm oxtabs Pq Fl oxtabs Expand (do not expand) tabs to spaces on output. .It Cm tabs Pq Fl tabs The converse of .Cm oxtabs . .It Cm brk Ar value Same as the control character .Cm eol . .It Cm flush Ar value Same as the control character .Cm discard . .It Cm rprnt Ar value Same as the control character .Cm reprint . .El .Sh INTERACTION WITH JOB CONTROL Modifications to the terminal settings are treated by job control (see .Xr termios 4 ) same as writes. When the .Nm utility is executing in a background process group, such attempts result in the kernel sending the .Dv SIGTTOU signal and stopping the process until its group is returned to foreground. The non-blocking open of the terminal device with the .Fl f option to .Nm does not affect the behavior. If it is desirable to modify the settings from the background, .Xr sh 1 users might utilize the following idiom: .Bd -literal (trap '' TTOU; stty -f /dev/tty sane) .Ed .Pp Note that changing terminal settings for a running foreground job that is not prepared for it might cause inconsistencies. .Sh EXIT STATUS .Ex -std .Sh SEE ALSO .Xr resizewin 1 , .Xr termios 4 , .Xr pstat 8 .Sh STANDARDS The .Nm utility is expected to be .St -p1003.2 compatible. The flags .Fl e and .Fl f are extensions to the standard. .Sh HISTORY A .Nm command appeared in .At v2 . diff --git a/share/man/man4/termios.4 b/share/man/man4/termios.4 index d3a5c8fa582d..80015025345e 100644 --- a/share/man/man4/termios.4 +++ b/share/man/man4/termios.4 @@ -1,1597 +1,1599 @@ .\" Copyright (c) 1991, 1992, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" @(#)termios.4 8.4 (Berkeley) 4/19/94 .\" .Dd June 28, 2020 .Dt TERMIOS 4 .Os .Sh NAME .Nm termios .Nd general terminal line discipline .Sh SYNOPSIS .In termios.h .Sh DESCRIPTION This describes a general terminal line discipline that is supported on tty asynchronous communication ports. .Ss Opening a Terminal Device File When a terminal file is opened, it normally causes the process to wait until a connection is established. For most hardware, the presence of a connection is indicated by the assertion of the hardware .Dv CARRIER line. If the termios structure associated with the terminal file has the .Dv CLOCAL flag set in the cflag, or if the .Dv O_NONBLOCK flag is set in the .Xr open 2 call, then the open will succeed even without a connection being present. In practice, applications seldom open these files; they are opened by special programs, such as .Xr getty 8 , and become an application's standard input, output, and error files. .Ss Job Control in a Nutshell Every process is associated with a particular process group and session. The grouping is hierarchical: every member of a particular process group is a member of the same session. This structuring is used in managing groups of related processes for purposes of .\" .Gw "job control" ; .Em "job control" ; that is, the ability from the keyboard (or from program control) to simultaneously stop or restart a complex command (a command composed of one or more related processes). The grouping into process groups allows delivering of signals that stop or start the group as a whole, along with arbitrating which process group has access to the single controlling terminal. The grouping at a higher layer into sessions is to restrict the job control related signals and system calls to within processes resulting from a particular instance of a .Dq login . Typically, a session is created when a user logs in, and the login terminal is setup to be the controlling terminal; all processes spawned from that login shell are in the same session, and inherit the controlling terminal. .Pp A job control shell operating interactively (that is, reading commands from a terminal) normally groups related processes together by placing them into the same process group. A set of processes in the same process group is collectively referred to as a .Dq job . When the foreground process group of the terminal is the same as the process group of a particular job, that job is said to be in the .Dq foreground . When the process group of the terminal is different from the process group of a job (but is still the controlling terminal), that job is said to be in the .Dq background . Normally the shell reads a command and starts the job that implements that command. If the command is to be started in the foreground (typical), it sets the process group of the terminal to the process group of the started job, waits for the job to complete, and then sets the process group of the terminal back to its own process group (it puts itself into the foreground). If the job is to be started in the background (as denoted by the shell operator "&"), it never changes the process group of the terminal and does not wait for the job to complete (that is, it immediately attempts to read the next command). If the job is started in the foreground, the user may type a key (usually .Ql \&^Z ) which generates the terminal stop signal .Pq Dv SIGTSTP and has the effect of stopping the entire job. The shell will notice that the job stopped, and will resume running after placing itself in the foreground. The shell also has commands for placing stopped jobs in the background, and for placing stopped or background jobs into the foreground. .Ss Orphaned Process Groups An orphaned process group is a process group that has no process whose parent is in a different process group, yet is in the same session. Conceptually it means a process group that does not have a parent that could do anything if it were to be stopped. For example, the initial login shell is typically in an orphaned process group. Orphaned process groups are immune to keyboard generated stop signals and job control signals resulting from reads or writes to the controlling terminal. .Ss The Controlling Terminal A terminal may belong to a process as its controlling terminal. Each process of a session that has a controlling terminal has the same controlling terminal. A terminal may be the controlling terminal for at most one session. The controlling terminal for a session is allocated by the session leader by issuing the .Dv TIOCSCTTY ioctl. A controlling terminal is never acquired by merely opening a terminal device file. When a controlling terminal becomes associated with a session, its foreground process group is set to the process group of the session leader. .Pp The controlling terminal is inherited by a child process during a .Xr fork 2 function call. A process relinquishes its controlling terminal when it creates a new session with the .Xr setsid 2 function; other processes remaining in the old session that had this terminal as their controlling terminal continue to have it. A process does not relinquish its controlling terminal simply by closing all of its file descriptors associated with the controlling terminal if other processes continue to have it open. .Pp When a controlling process terminates, the controlling terminal is disassociated from the current session, allowing it to be acquired by a new session leader. Subsequent access to the terminal by other processes in the earlier session will be denied, with attempts to access the terminal treated as if modem disconnect had been sensed. .Ss Terminal Access Control If a process is in the foreground process group of its controlling terminal, read operations are allowed. Any attempts by a process in a background process group to read from its controlling terminal causes a .Dv SIGTTIN signal to be sent to the process's group unless one of the following special cases apply: if the reading process is ignoring or blocking the .Dv SIGTTIN signal, or if the process group of the reading process is orphaned, the .Xr read 2 returns -1 with .Va errno set to .Er EIO and no signal is sent. The default action of the .Dv SIGTTIN signal is to stop the process to which it is sent. .Pp If a process is in the foreground process group of its controlling terminal, write operations are allowed. Attempts by a process in a background process group to write to its controlling terminal will cause the process group to be sent a .Dv SIGTTOU signal unless one of the following special cases apply: if .Dv TOSTOP is not set, or if .Dv TOSTOP is set and the process is ignoring or blocking the .Dv SIGTTOU signal, the process is allowed to write to the terminal and the .Dv SIGTTOU signal is not sent. If .Dv TOSTOP is set, and the process group of the writing process is orphaned, and the writing process is not ignoring or blocking .Dv SIGTTOU , the .Xr write 2 returns -1 with errno set to .Er EIO and no signal is sent. .Pp Certain calls that set terminal parameters are treated in the same fashion as write, except that .Dv TOSTOP is ignored; that is, the effect is identical to that of terminal writes when .Dv TOSTOP is set. .Ss Input Processing and Reading Data A terminal device associated with a terminal device file may operate in full-duplex mode, so that data may arrive even while output is occurring. Each terminal device file has associated with it an input queue, into which incoming data is stored by the system before being read by a process. The system imposes a limit, .Pf \&{ Dv MAX_INPUT Ns \&} , on the number of bytes that may be stored in the input queue. The behavior of the system when this limit is exceeded depends on the setting of the .Dv IMAXBEL flag in the termios .Fa c_iflag . If this flag is set, the terminal is sent an .Tn ASCII .Dv BEL character each time a character is received while the input queue is full. Otherwise, the input queue is flushed upon receiving the character. .Pp Two general kinds of input processing are available, determined by whether the terminal device file is in canonical mode or noncanonical mode. Additionally, input characters are processed according to the .Fa c_iflag and .Fa c_lflag fields. Such processing can include echoing, which in general means transmitting input characters immediately back to the terminal when they are received from the terminal. This is useful for terminals that can operate in full-duplex mode. .Pp The manner in which data is provided to a process reading from a terminal device file is dependent on whether the terminal device file is in canonical or noncanonical mode. .Pp Another dependency is whether the .Dv O_NONBLOCK flag is set by .Xr open 2 or .Xr fcntl 2 . If the .Dv O_NONBLOCK flag is clear, then the read request is blocked until data is available or a signal has been received. If the .Dv O_NONBLOCK flag is set, then the read request is completed, without blocking, in one of three ways: .Bl -enum -offset indent .It If there is enough data available to satisfy the entire request, and the read completes successfully the number of bytes read is returned. .It If there is not enough data available to satisfy the entire request, and the read completes successfully, having read as much data as possible, the number of bytes read is returned. .It If there is no data available, the read returns -1, with errno set to .Er EAGAIN . .El .Pp When data is available depends on whether the input processing mode is canonical or noncanonical. .Ss Canonical Mode Input Processing In canonical mode input processing, terminal input is processed in units of lines. A line is delimited by a newline .Ql \&\en character, an end-of-file .Pq Dv EOF character, or an end-of-line .Pq Dv EOL character. See the .Sx "Special Characters" section for more information on .Dv EOF and .Dv EOL . This means that a read request will not return until an entire line has been typed, or a signal has been received. Also, no matter how many bytes are requested in the read call, at most one line is returned. It is not, however, necessary to read a whole line at once; any number of bytes, even one, may be requested in a read without losing information. .Pp .Pf \&{ Dv MAX_CANON Ns \&} is a limit on the number of bytes in a line. The behavior of the system when this limit is exceeded is the same as when the input queue limit .Pf \&{ Dv MAX_INPUT Ns \&} , is exceeded. .Pp Erase and kill processing occur when either of two special characters, the .Dv ERASE and .Dv KILL characters (see the .Sx "Special Characters" section), is received. This processing affects data in the input queue that has not yet been delimited by a newline .Dv NL , .Dv EOF , or .Dv EOL character. This un-delimited data makes up the current line. The .Dv ERASE character deletes the last character in the current line, if there is any. The .Dv KILL character deletes all data in the current line, if there is any. The .Dv ERASE and .Dv KILL characters have no effect if there is no data in the current line. The .Dv ERASE and .Dv KILL characters themselves are not placed in the input queue. .Ss Noncanonical Mode Input Processing In noncanonical mode input processing, input bytes are not assembled into lines, and erase and kill processing does not occur. The values of the .Dv VMIN and .Dv VTIME members of the .Fa c_cc array are used to determine how to process the bytes received. .Pp .Dv MIN represents the minimum number of bytes that should be received when the .Xr read 2 function successfully returns. .Dv TIME is a timer of 0.1 second granularity that is used to time out bursty and short term data transmissions. If .Dv MIN is greater than .Dv \&{ Dv MAX_INPUT Ns \&} , the response to the request is undefined. The four possible values for .Dv MIN and .Dv TIME and their interactions are described below. .Ss "Case A: MIN > 0, TIME > 0" In this case .Dv TIME serves as an inter-byte timer and is activated after the first byte is received. Since it is an inter-byte timer, it is reset after a byte is received. The interaction between .Dv MIN and .Dv TIME is as follows: as soon as one byte is received, the inter-byte timer is started. If .Dv MIN bytes are received before the inter-byte timer expires (remember that the timer is reset upon receipt of each byte), the read is satisfied. If the timer expires before .Dv MIN bytes are received, the characters received to that point are returned to the user. Note that if .Dv TIME expires at least one byte is returned because the timer would not have been enabled unless a byte was received. In this case .Pf \&( Dv MIN > 0, .Dv TIME > 0) the read blocks until the .Dv MIN and .Dv TIME mechanisms are activated by the receipt of the first byte, or a signal is received. If data is in the buffer at the time of the .Fn read , the result is as if data had been received immediately after the .Fn read . .Ss "Case B: MIN > 0, TIME = 0" In this case, since the value of .Dv TIME is zero, the timer plays no role and only .Dv MIN is significant. A pending read is not satisfied until .Dv MIN bytes are received (i.e., the pending read blocks until .Dv MIN bytes are received), or a signal is received. A program that uses this case to read record-based terminal .Dv I/O may block indefinitely in the read operation. .Ss "Case C: MIN = 0, TIME > 0" In this case, since .Dv MIN = 0, .Dv TIME no longer represents an inter-byte timer. It now serves as a read timer that is activated as soon as the read function is processed. A read is satisfied as soon as a single byte is received or the read timer expires. Note that in this case if the timer expires, no bytes are returned. If the timer does not expire, the only way the read can be satisfied is if a byte is received. In this case the read will not block indefinitely waiting for a byte; if no byte is received within .Dv TIME Ns *0.1 seconds after the read is initiated, the read returns a value of zero, having read no data. If data is in the buffer at the time of the read, the timer is started as if data had been received immediately after the read. .Ss Case D: MIN = 0, TIME = 0 The minimum of either the number of bytes requested or the number of bytes currently available is returned without waiting for more bytes to be input. If no characters are available, read returns a value of zero, having read no data. .Ss Writing Data and Output Processing When a process writes one or more bytes to a terminal device file, they are processed according to the .Fa c_oflag field (see the .Sx "Output Modes" section). The implementation may provide a buffering mechanism; as such, when a call to .Fn write completes, all of the bytes written have been scheduled for transmission to the device, but the transmission will not necessarily have been completed. .\" See also .Sx "6.4.2" for the effects of .\" .Dv O_NONBLOCK .\" on write. .Ss Special Characters Certain characters have special functions on input or output or both. These functions are summarized as follows: .Bl -tag -width indent .It Dv INTR Special character on input and is recognized if the .Dv ISIG flag (see the .Sx "Local Modes" section) is enabled. Generates a .Dv SIGINT signal which is sent to all processes in the foreground process group for which the terminal is the controlling terminal. If .Dv ISIG is set, the .Dv INTR character is discarded when processed. .It Dv QUIT Special character on input and is recognized if the .Dv ISIG flag is enabled. Generates a .Dv SIGQUIT signal which is sent to all processes in the foreground process group for which the terminal is the controlling terminal. If .Dv ISIG is set, the .Dv QUIT character is discarded when processed. .It Dv ERASE Special character on input and is recognized if the .Dv ICANON flag is set. Erases the last character in the current line; see .Sx "Canonical Mode Input Processing" . It does not erase beyond the start of a line, as delimited by an .Dv NL , .Dv EOF , or .Dv EOL character. If .Dv ICANON is set, the .Dv ERASE character is discarded when processed. .It Dv KILL Special character on input and is recognized if the .Dv ICANON flag is set. Deletes the entire line, as delimited by a .Dv NL , .Dv EOF , or .Dv EOL character. If .Dv ICANON is set, the .Dv KILL character is discarded when processed. .It Dv EOF Special character on input and is recognized if the .Dv ICANON flag is set. When received, all the bytes waiting to be read are immediately passed to the process, without waiting for a newline, and the .Dv EOF is discarded. Thus, if there are no bytes waiting (that is, the .Dv EOF occurred at the beginning of a line), a byte count of zero is returned from the .Fn read , representing an end-of-file indication. If .Dv ICANON is set, the .Dv EOF character is discarded when processed. .It Dv NL Special character on input and is recognized if the .Dv ICANON flag is set. It is the line delimiter .Ql \&\en . .It Dv EOL Special character on input and is recognized if the .Dv ICANON flag is set. Is an additional line delimiter, like .Dv NL . .It Dv SUSP If the .Dv ISIG flag is enabled, receipt of the .Dv SUSP character causes a .Dv SIGTSTP signal to be sent to all processes in the foreground process group for which the terminal is the controlling terminal, and the .Dv SUSP character is discarded when processed. .It Dv STOP Special character on both input and output and is recognized if the .Dv IXON (output control) or .Dv IXOFF (input control) flag is set. Can be used to temporarily suspend output. It is useful with fast terminals to prevent output from disappearing before it can be read. If .Dv IXON is set, the .Dv STOP character is discarded when processed. .It Dv START Special character on both input and output and is recognized if the .Dv IXON (output control) or .Dv IXOFF (input control) flag is set. Can be used to resume output that has been suspended by a .Dv STOP character. If .Dv IXON is set, the .Dv START character is discarded when processed. .It Dv CR Special character on input and is recognized if the .Dv ICANON flag is set; it is the .Ql \&\er , as denoted in the .Tn \&C Standard {2}. When .Dv ICANON and .Dv ICRNL are set and .Dv IGNCR is not set, this character is translated into a .Dv NL , and has the same effect as a .Dv NL character. .El .Pp The following special characters are extensions defined by this system and are not a part of .St -p1003.1 termios. .Bl -tag -width indent .It Dv EOL2 Secondary .Dv EOL character. Same function as .Dv EOL . .It Dv WERASE Special character on input and is recognized if the .Dv ICANON flag is set. Erases the last word in the current line according to one of two algorithms. If the .Dv ALTWERASE flag is not set, first any preceding whitespace is erased, and then the maximal sequence of non-whitespace characters. If .Dv ALTWERASE is set, first any preceding whitespace is erased, and then the maximal sequence of alphabetic/underscores or non alphabetic/underscores. As a special case in this second algorithm, the first previous non-whitespace character is skipped in determining whether the preceding word is a sequence of alphabetic/underscores. This sounds confusing but turns out to be quite practical. .It Dv REPRINT Special character on input and is recognized if the .Dv ICANON flag is set. Causes the current input edit line to be retyped. .It Dv DSUSP Has similar actions to the .Dv SUSP character, except that the .Dv SIGTSTP signal is delivered when one of the processes in the foreground process group issues a .Fn read to the controlling terminal. .It Dv LNEXT Special character on input and is recognized if the .Dv IEXTEN flag is set. Receipt of this character causes the next character to be taken literally. .It Dv DISCARD Special character on input and is recognized if the .Dv IEXTEN flag is set. Receipt of this character toggles the flushing of terminal output. .It Dv STATUS Special character on input and is recognized if the .Dv ICANON flag is set. Receipt of this character causes a .Dv SIGINFO signal to be sent to the foreground process group of the terminal. Also, if the .Dv NOKERNINFO flag is not set, it causes the kernel to write a status message to the terminal that displays the current load average, the name of the command in the foreground, its process ID, the symbolic wait channel, the number of user and system seconds used, the percentage of cpu the process is getting, and the resident set size of the process. .Pp In case the .Xr sysctl 8 variable .Va kern.tty_info_kstacks is set to a non-zero value, the running thread's kernel stack is written to the terminal (e.g., for debugging purposes). .El .Pp The .Dv NL and .Dv CR characters cannot be changed. The values for all the remaining characters can be set and are described later in the document under Special Control Characters. .Pp Special character functions associated with changeable special control characters can be disabled individually by setting their value to .Dv {_POSIX_VDISABLE} ; see .Sx "Special Control Characters" . .Pp If two or more special characters have the same value, the function performed when that character is received is undefined. .Ss Modem Disconnect If a modem disconnect is detected by the terminal interface for a controlling terminal, and if .Dv CLOCAL is not set in the .Fa c_cflag field for the terminal, the .Dv SIGHUP signal is sent to the controlling process associated with the terminal. Unless other arrangements have been made, this causes the controlling process to terminate. Any subsequent call to the .Fn read function returns the value zero, indicating end of file. Thus, processes that read a terminal file and test for end-of-file can terminate appropriately after a disconnect. .\" If the .\" .Er EIO .\" condition specified in 6.1.1.4 that applies .\" when the implementation supports job control also exists, it is .\" unspecified whether the .\" .Dv EOF .\" condition or the .\" .Pf [ Dv EIO .\" ] is returned. Any subsequent .Fn write to the terminal device returns -1, with .Va errno set to .Er EIO , until the device is closed. .Sh General Terminal Interface .Ss Closing a Terminal Device File The last process to close a terminal device file causes any output to be sent to the device and any input to be discarded. Then, if .Dv HUPCL is set in the control modes, and the communications port supports a disconnect function, the terminal device performs a disconnect. .Ss Parameters That Can Be Set Routines that need to control certain terminal .Tn I/O characteristics do so by using the termios structure as defined in the header .In termios.h . This structure contains minimally four scalar elements of bit flags and one array of special characters. The scalar flag elements are named: .Fa c_iflag , .Fa c_oflag , .Fa c_cflag , and .Fa c_lflag . The character array is named .Fa c_cc , and its maximum index is .Dv NCCS . .Ss Input Modes Values of the .Fa c_iflag field describe the basic terminal input control, and are composed of following masks: .Pp .Bl -tag -width IMAXBEL -offset indent -compact .It Dv IGNBRK /* ignore BREAK condition */ .It Dv BRKINT /* map BREAK to SIGINTR */ .It Dv IGNPAR /* ignore (discard) parity errors */ .It Dv PARMRK /* mark parity and framing errors */ .It Dv INPCK /* enable checking of parity errors */ .It Dv ISTRIP /* strip 8th bit off chars */ .It Dv INLCR /* map NL into CR */ .It Dv IGNCR /* ignore CR */ .It Dv ICRNL /* map CR to NL (ala CRMOD) */ .It Dv IXON /* enable output flow control */ .It Dv IXOFF /* enable input flow control */ .It Dv IXANY /* any char will restart after stop */ .It Dv IMAXBEL /* ring bell on input queue full */ +.It Dv IUTF8 +/* assume input is UTF-8 encoded */ .El .Pp In the context of asynchronous serial data transmission, a break condition is defined as a sequence of zero-valued bits that continues for more than the time to send one byte. The entire sequence of zero-valued bits is interpreted as a single break condition, even if it continues for a time equivalent to more than one byte. In contexts other than asynchronous serial data transmission the definition of a break condition is implementation defined. .Pp If .Dv IGNBRK is set, a break condition detected on input is ignored, that is, not put on the input queue and therefore not read by any process. If .Dv IGNBRK is not set and .Dv BRKINT is set, the break condition flushes the input and output queues and if the terminal is the controlling terminal of a foreground process group, the break condition generates a single .Dv SIGINT signal to that foreground process group. If neither .Dv IGNBRK nor .Dv BRKINT is set, a break condition is read as a single .Ql \&\e0 , or if .Dv PARMRK is set, as .Ql \&\e377 , .Ql \&\e0 , .Ql \&\e0 . .Pp If .Dv IGNPAR is set, a byte with a framing or parity error (other than break) is ignored. .Pp If .Dv PARMRK is set, and .Dv IGNPAR is not set, a byte with a framing or parity error (other than break) is given to the application as the three-character sequence .Ql \&\e377 , .Ql \&\e0 , X, where .Ql \&\e377 , .Ql \&\e0 is a two-character flag preceding each sequence and X is the data of the character received in error. To avoid ambiguity in this case, if .Dv ISTRIP is not set, a valid character of .Ql \&\e377 is given to the application as .Ql \&\e377 , .Ql \&\e377 . If neither .Dv PARMRK nor .Dv IGNPAR is set, a framing or parity error (other than break) is given to the application as a single character .Ql \&\e0 . .Pp If .Dv INPCK is set, input parity checking is enabled. If .Dv INPCK is not set, input parity checking is disabled, allowing output parity generation without input parity errors. Note that whether input parity checking is enabled or disabled is independent of whether parity detection is enabled or disabled (see .Sx "Control Modes" ) . If parity detection is enabled but input parity checking is disabled, the hardware to which the terminal is connected recognizes the parity bit, but the terminal special file does not check whether this bit is set correctly or not. .Pp If .Dv ISTRIP is set, valid input bytes are first stripped to seven bits, otherwise all eight bits are processed. .Pp If .Dv INLCR is set, a received .Dv NL character is translated into a .Dv CR character. If .Dv IGNCR is set, a received .Dv CR character is ignored (not read). If .Dv IGNCR is not set and .Dv ICRNL is set, a received .Dv CR character is translated into a .Dv NL character. .Pp If .Dv IXON is set, start/stop output control is enabled. A received .Dv STOP character suspends output and a received .Dv START character restarts output. If .Dv IXANY is also set, then any character may restart output. When .Dv IXON is set, .Dv START and .Dv STOP characters are not read, but merely perform flow control functions. When .Dv IXON is not set, the .Dv START and .Dv STOP characters are read. .Pp If .Dv IXOFF is set, start/stop input control is enabled. The system shall transmit one or more .Dv STOP characters, which are intended to cause the terminal device to stop transmitting data, as needed to prevent the input queue from overflowing and causing the undefined behavior described in .Sx "Input Processing and Reading Data" , and shall transmit one or more .Dv START characters, which are intended to cause the terminal device to resume transmitting data, as soon as the device can continue transmitting data without risk of overflowing the input queue. The precise conditions under which .Dv STOP and .Dv START characters are transmitted are implementation defined. .Pp If .Dv IMAXBEL is set and the input queue is full, subsequent input shall cause an .Tn ASCII .Dv BEL character to be transmitted to the output queue. .Pp The initial input control value after .Fn open is implementation defined. .Ss Output Modes Values of the .Fa c_oflag field describe the basic terminal output control, and are composed of the following masks: .Pp .Bl -tag -width ONOEOT -offset indent -compact .It Dv OPOST /* enable following output processing */ .It Dv ONLCR /* map NL to CR-NL (ala .Dv CRMOD ) */ .It Dv OCRNL /* map CR to NL */ .It Dv TABDLY /* tab delay mask */ .It Dv TAB0 /* no tab delay and expansion */ .It Dv TAB3 /* expand tabs to spaces */ .It Dv ONOEOT /* discard .Dv EOT Ns 's .Ql \&^D on output) */ .It Dv ONOCR /* do not transmit CRs on column 0 */ .It Dv ONLRET /* on the terminal NL performs the CR function */ .El .Pp If .Dv OPOST is set, the remaining flag masks are interpreted as follows; otherwise characters are transmitted without change. .Pp If .Dv ONLCR is set, newlines are translated to carriage return, linefeeds. .Pp If .Dv OCRNL is set, carriage returns are translated to newlines. .Pp The .Dv TABDLY bits specify the tab delay. The .Fa c_oflag is masked with .Dv TABDLY and compared with the values .Dv TAB0 or .Dv TAB3 . If .Dv TAB3 is set, tabs are expanded to the appropriate number of spaces (assuming 8 column tab stops). .Pp If .Dv ONOEOT is set, .Tn ASCII .Dv EOT Ns 's are discarded on output. .Pp If .Dv ONOCR is set, no CR character is transmitted when at column 0 (first position). .Pp If .Dv ONLRET is set, the NL character is assumed to do the carriage-return function; the column pointer will be set to 0. .Ss Control Modes Values of the .Fa c_cflag field describe the basic terminal hardware control, and are composed of the following masks. Not all values specified are supported by all hardware. .Pp .Bl -tag -width CRTSXIFLOW -offset indent -compact .It Dv CSIZE /* character size mask */ .It Dv CS5 /* 5 bits (pseudo) */ .It Dv CS6 /* 6 bits */ .It Dv CS7 /* 7 bits */ .It Dv CS8 /* 8 bits */ .It Dv CSTOPB /* send 2 stop bits */ .It Dv CREAD /* enable receiver */ .It Dv PARENB /* parity enable */ .It Dv PARODD /* odd parity, else even */ .It Dv HUPCL /* hang up on last close */ .It Dv CLOCAL /* ignore modem status lines */ .It Dv CCTS_OFLOW /* .Dv CTS flow control of output */ .It Dv CRTSCTS /* same as .Dv CCTS_OFLOW */ .It Dv CRTS_IFLOW /* RTS flow control of input */ .It Dv MDMBUF /* flow control output via Carrier */ .It Dv CNO_RTSDTR /* Do not assert RTS or DTR automatically */ .El .Pp The .Dv CSIZE bits specify the byte size in bits for both transmission and reception. The .Fa c_cflag is masked with .Dv CSIZE and compared with the values .Dv CS5 , .Dv CS6 , .Dv CS7 , or .Dv CS8 . This size does not include the parity bit, if any. If .Dv CSTOPB is set, two stop bits are used, otherwise one stop bit. For example, at 110 baud, two stop bits are normally used. .Pp If .Dv CREAD is set, the receiver is enabled. Otherwise, no character is received. Not all hardware supports this bit. In fact, this flag is pretty silly and if it were not part of the .Nm specification it would be omitted. .Pp If .Dv PARENB is set, parity generation and detection are enabled and a parity bit is added to each character. If parity is enabled, .Dv PARODD specifies odd parity if set, otherwise even parity is used. .Pp If .Dv HUPCL is set, the modem control lines for the port are lowered when the last process with the port open closes the port or the process terminates. The modem connection is broken. .Pp If .Dv CLOCAL is set, a connection does not depend on the state of the modem status lines. If .Dv CLOCAL is clear, the modem status lines are monitored. .Pp Under normal circumstances, a call to the .Fn open function waits for the modem connection to complete. However, if the .Dv O_NONBLOCK flag is set or if .Dv CLOCAL has been set, the .Fn open function returns immediately without waiting for the connection. .Pp The .Dv CCTS_OFLOW .Pf ( Dv CRTSCTS ) flag is currently unused. .Pp If .Dv MDMBUF is set then output flow control is controlled by the state of Carrier Detect. .Pp If .Dv CNO_RTSDTR is set then the RTS and DTR lines will not be asserted when the device is opened. As a result, this flag is only useful on initial-state devices. .Pp If the object for which the control modes are set is not an asynchronous serial connection, some of the modes may be ignored; for example, if an attempt is made to set the baud rate on a network connection to a terminal on another host, the baud rate may or may not be set on the connection between that terminal and the machine it is directly connected to. .Ss Local Modes Values of the .Fa c_lflag field describe the control of various functions, and are composed of the following masks. .Pp .Bl -tag -width NOKERNINFO -offset indent -compact .It Dv ECHOKE /* visual erase for line kill */ .It Dv ECHOE /* visually erase chars */ .It Dv ECHO /* enable echoing */ .It Dv ECHONL /* echo .Dv NL even if .Dv ECHO is off */ .It Dv ECHOPRT /* visual erase mode for hardcopy */ .It Dv ECHOCTL /* echo control chars as ^(Char) */ .It Dv ISIG /* enable signals .Dv INTR , .Dv QUIT , .Dv [D]SUSP */ .It Dv ICANON /* canonicalize input lines */ .It Dv ALTWERASE /* use alternate .Dv WERASE algorithm */ .It Dv IEXTEN /* enable .Dv DISCARD and .Dv LNEXT */ .It Dv EXTPROC /* external processing */ .It Dv TOSTOP /* stop background jobs from output */ .It Dv FLUSHO /* output being flushed (state) */ .It Dv NOKERNINFO /* no kernel output from .Dv VSTATUS */ .It Dv PENDIN /* XXX retype pending input (state) */ .It Dv NOFLSH /* don't flush after interrupt */ .El .Pp If .Dv ECHO is set, input characters are echoed back to the terminal. If .Dv ECHO is not set, input characters are not echoed. .Pp If .Dv ECHOE and .Dv ICANON are set, the .Dv ERASE character causes the terminal to erase the last character in the current line from the display, if possible. If there is no character to erase, an implementation may echo an indication that this was the case or do nothing. .Pp If .Dv ECHOK and .Dv ICANON are set, the .Dv KILL character causes the current line to be discarded and the system echoes the .Ql \&\en character after the .Dv KILL character. .Pp If .Dv ECHOKE and .Dv ICANON are set, the .Dv KILL character causes the current line to be discarded and the system causes the terminal to erase the line from the display. .Pp If .Dv ECHOPRT and .Dv ICANON are set, the system assumes that the display is a printing device and prints a backslash and the erased characters when processing .Dv ERASE characters, followed by a forward slash. .Pp If .Dv ECHOCTL is set, the system echoes control characters in a visible fashion using a caret followed by the control character. .Pp If .Dv ALTWERASE is set, the system uses an alternative algorithm for determining what constitutes a word when processing .Dv WERASE characters (see .Dv WERASE ) . .Pp If .Dv ECHONL and .Dv ICANON are set, the .Ql \&\en character echoes even if .Dv ECHO is not set. .Pp If .Dv ICANON is set, canonical processing is enabled. This enables the erase and kill edit functions, and the assembly of input characters into lines delimited by .Dv NL , .Dv EOF , and .Dv EOL , as described in .Sx "Canonical Mode Input Processing" . .Pp If .Dv ICANON is not set, read requests are satisfied directly from the input queue. A read is not satisfied until at least .Dv MIN bytes have been received or the timeout value .Dv TIME expired between bytes. The time value represents tenths of seconds. See .Sx "Noncanonical Mode Input Processing" for more details. .Pp If .Dv ISIG is set, each input character is checked against the special control characters .Dv INTR , .Dv QUIT , and .Dv SUSP (job control only). If an input character matches one of these control characters, the function associated with that character is performed. If .Dv ISIG is not set, no checking is done. Thus these special input functions are possible only if .Dv ISIG is set. .Pp If .Dv IEXTEN is set, implementation-defined functions are recognized from the input data. How .Dv IEXTEN being set interacts with .Dv ICANON , .Dv ISIG , .Dv IXON , or .Dv IXOFF is implementation defined. If .Dv IEXTEN is not set, then implementation-defined functions are not recognized, and the corresponding input characters are not processed as described for .Dv ICANON , .Dv ISIG , .Dv IXON , and .Dv IXOFF . .Pp If .Dv NOFLSH is set, the normal flush of the input and output queues associated with the .Dv INTR , .Dv QUIT , and .Dv SUSP characters are not be done. .Pp If .Dv TOSTOP is set, the signal .Dv SIGTTOU is sent to the process group of a process that tries to write to its controlling terminal if it is not in the foreground process group for that terminal. This signal, by default, stops the members of the process group. Otherwise, the output generated by that process is output to the current output stream. Processes that are blocking or ignoring .Dv SIGTTOU signals are excepted and allowed to produce output and the .Dv SIGTTOU signal is not sent. .Pp If .Dv NOKERNINFO is set, the kernel does not produce a status message when processing .Dv STATUS characters (see .Dv STATUS ) . .Ss Special Control Characters The special control characters values are defined by the array .Fa c_cc . This table lists the array index, the corresponding special character, and the system default value. For an accurate list of the system defaults, consult the header file .In sys/ttydefaults.h . .Pp .Bl -column "Index Name" "Special Character" -offset indent -compact .It Em "Index Name Special Character Default Value" .It Dv VEOF Ta EOF Ta \&^D .It Dv VEOL Ta EOL Ta _POSIX_VDISABLE .It Dv VEOL2 Ta EOL2 Ta _POSIX_VDISABLE .It Dv VERASE Ta ERASE Ta \&^? Ql \&\e177 .It Dv VWERASE Ta WERASE Ta \&^W .It Dv VKILL Ta KILL Ta \&^U .It Dv VREPRINT Ta REPRINT Ta \&^R .It Dv VINTR Ta INTR Ta \&^C .It Dv VQUIT Ta QUIT Ta \&^\e\e Ql \&\e34 .It Dv VSUSP Ta SUSP Ta \&^Z .It Dv VDSUSP Ta DSUSP Ta \&^Y .It Dv VSTART Ta START Ta \&^Q .It Dv VSTOP Ta STOP Ta \&^S .It Dv VLNEXT Ta LNEXT Ta \&^V .It Dv VDISCARD Ta DISCARD Ta \&^O .It Dv VMIN Ta --- Ta \&1 .It Dv VTIME Ta --- Ta \&0 .It Dv VSTATUS Ta STATUS Ta \&^T .El .Pp If the value of one of the changeable special control characters (see .Sx "Special Characters" ) is .Dv {_POSIX_VDISABLE} , that function is disabled; that is, no input data is recognized as the disabled special character. If .Dv ICANON is not set, the value of .Dv {_POSIX_VDISABLE} has no special meaning for the .Dv VMIN and .Dv VTIME entries of the .Fa c_cc array. .Pp The initial values of the flags and control characters after .Fn open is set according to the values in the header .In sys/ttydefaults.h . .Sh SEE ALSO .Xr stty 1 , .Xr tcgetsid 3 , .Xr tcgetwinsize 3 , .Xr tcsendbreak 3 , .Xr tcsetattr 3 , .Xr tcsetsid 3 , .Xr tty 4 , .Xr stack 9 diff --git a/sys/kern/tty.c b/sys/kern/tty.c index 134c1dfba98b..620233947410 100644 --- a/sys/kern/tty.c +++ b/sys/kern/tty.c @@ -1,2449 +1,2449 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2008 Ed Schouten * All rights reserved. * * Portions of this software were developed under sponsorship from Snow * B.V., the Netherlands. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include "opt_capsicum.h" #include "opt_printf.h" #include #include #include #include #include #include #include #include #ifdef COMPAT_43TTY #include #endif /* COMPAT_43TTY */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define TTYDEFCHARS #include #undef TTYDEFCHARS #include #include #include #include static MALLOC_DEFINE(M_TTY, "tty", "tty device"); static void tty_rel_free(struct tty *tp); static TAILQ_HEAD(, tty) tty_list = TAILQ_HEAD_INITIALIZER(tty_list); static struct sx tty_list_sx; SX_SYSINIT(tty_list, &tty_list_sx, "tty list"); static unsigned int tty_list_count = 0; /* Character device of /dev/console. */ static struct cdev *dev_console; static const char *dev_console_filename; /* * Flags that are supported and stored by this implementation. */ -#define TTYSUP_IFLAG (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|\ - INLCR|IGNCR|ICRNL|IXON|IXOFF|IXANY|IMAXBEL) +#define TTYSUP_IFLAG (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|INLCR|\ + IGNCR|ICRNL|IXON|IXOFF|IXANY|IMAXBEL|IUTF8) #define TTYSUP_OFLAG (OPOST|ONLCR|TAB3|ONOEOT|OCRNL|ONOCR|ONLRET) #define TTYSUP_LFLAG (ECHOKE|ECHOE|ECHOK|ECHO|ECHONL|ECHOPRT|\ ECHOCTL|ISIG|ICANON|ALTWERASE|IEXTEN|TOSTOP|\ FLUSHO|NOKERNINFO|NOFLSH) #define TTYSUP_CFLAG (CIGNORE|CSIZE|CSTOPB|CREAD|PARENB|PARODD|\ HUPCL|CLOCAL|CCTS_OFLOW|CRTS_IFLOW|CDTR_IFLOW|\ CDSR_OFLOW|CCAR_OFLOW|CNO_RTSDTR) #define TTY_CALLOUT(tp,d) (dev2unit(d) & TTYUNIT_CALLOUT) static int tty_drainwait = 5 * 60; SYSCTL_INT(_kern, OID_AUTO, tty_drainwait, CTLFLAG_RWTUN, &tty_drainwait, 0, "Default output drain timeout in seconds"); /* * Set TTY buffer sizes. */ #define TTYBUF_MAX 65536 #ifdef PRINTF_BUFR_SIZE #define TTY_PRBUF_SIZE PRINTF_BUFR_SIZE #else #define TTY_PRBUF_SIZE 256 #endif /* * Allocate buffer space if necessary, and set low watermarks, based on speed. * Note that the ttyxxxq_setsize() functions may drop and then reacquire the tty * lock during memory allocation. They will return ENXIO if the tty disappears * while unlocked. */ static int tty_watermarks(struct tty *tp) { size_t bs = 0; int error; /* Provide an input buffer for 2 seconds of data. */ if (tp->t_termios.c_cflag & CREAD) bs = MIN(tp->t_termios.c_ispeed / 5, TTYBUF_MAX); error = ttyinq_setsize(&tp->t_inq, tp, bs); if (error != 0) return (error); /* Set low watermark at 10% (when 90% is available). */ tp->t_inlow = (ttyinq_getallocatedsize(&tp->t_inq) * 9) / 10; /* Provide an output buffer for 2 seconds of data. */ bs = MIN(tp->t_termios.c_ospeed / 5, TTYBUF_MAX); error = ttyoutq_setsize(&tp->t_outq, tp, bs); if (error != 0) return (error); /* Set low watermark at 10% (when 90% is available). */ tp->t_outlow = (ttyoutq_getallocatedsize(&tp->t_outq) * 9) / 10; return (0); } static int tty_drain(struct tty *tp, int leaving) { sbintime_t timeout_at; size_t bytes; int error; if (ttyhook_hashook(tp, getc_inject)) /* buffer is inaccessible */ return (0); /* * For close(), use the recent historic timeout of "1 second without * making progress". For tcdrain(), use t_drainwait as the timeout, * with zero meaning "no timeout" which gives POSIX behavior. */ if (leaving) timeout_at = getsbinuptime() + SBT_1S; else if (tp->t_drainwait != 0) timeout_at = getsbinuptime() + SBT_1S * tp->t_drainwait; else timeout_at = 0; /* * Poll the output buffer and the hardware for completion, at 10 Hz. * Polling is required for devices which are not able to signal an * interrupt when the transmitter becomes idle (most USB serial devs). * The unusual structure of this loop ensures we check for busy one more * time after tty_timedwait() returns EWOULDBLOCK, so that success has * higher priority than timeout if the IO completed in the last 100mS. */ error = 0; bytes = ttyoutq_bytesused(&tp->t_outq); for (;;) { if (ttyoutq_bytesused(&tp->t_outq) == 0 && !ttydevsw_busy(tp)) return (0); if (error != 0) return (error); ttydevsw_outwakeup(tp); error = tty_timedwait(tp, &tp->t_outwait, hz / 10); if (error != 0 && error != EWOULDBLOCK) return (error); else if (timeout_at == 0 || getsbinuptime() < timeout_at) error = 0; else if (leaving && ttyoutq_bytesused(&tp->t_outq) < bytes) { /* In close, making progress, grant an extra second. */ error = 0; timeout_at += SBT_1S; bytes = ttyoutq_bytesused(&tp->t_outq); } } } /* * Though ttydev_enter() and ttydev_leave() seem to be related, they * don't have to be used together. ttydev_enter() is used by the cdev * operations to prevent an actual operation from being processed when * the TTY has been abandoned. ttydev_leave() is used by ttydev_open() * and ttydev_close() to determine whether per-TTY data should be * deallocated. */ static __inline int ttydev_enter(struct tty *tp) { tty_lock(tp); if (tty_gone(tp) || !tty_opened(tp)) { /* Device is already gone. */ tty_unlock(tp); return (ENXIO); } return (0); } static void ttydev_leave(struct tty *tp) { tty_assert_locked(tp); if (tty_opened(tp) || tp->t_flags & TF_OPENCLOSE) { /* Device is still opened somewhere. */ tty_unlock(tp); return; } tp->t_flags |= TF_OPENCLOSE; /* Remove console TTY. */ constty_clear(tp); /* Drain any output. */ if (!tty_gone(tp)) tty_drain(tp, 1); ttydisc_close(tp); /* Free i/o queues now since they might be large. */ ttyinq_free(&tp->t_inq); tp->t_inlow = 0; ttyoutq_free(&tp->t_outq); tp->t_outlow = 0; knlist_clear(&tp->t_inpoll.si_note, 1); knlist_clear(&tp->t_outpoll.si_note, 1); if (!tty_gone(tp)) ttydevsw_close(tp); tp->t_flags &= ~TF_OPENCLOSE; cv_broadcast(&tp->t_dcdwait); tty_rel_free(tp); } /* * Operations that are exposed through the character device in /dev. */ static int ttydev_open(struct cdev *dev, int oflags, int devtype __unused, struct thread *td) { struct tty *tp; int error; tp = dev->si_drv1; error = 0; tty_lock(tp); if (tty_gone(tp)) { /* Device is already gone. */ tty_unlock(tp); return (ENXIO); } /* * Block when other processes are currently opening or closing * the TTY. */ while (tp->t_flags & TF_OPENCLOSE) { error = tty_wait(tp, &tp->t_dcdwait); if (error != 0) { tty_unlock(tp); return (error); } } tp->t_flags |= TF_OPENCLOSE; /* * Make sure the "tty" and "cua" device cannot be opened at the * same time. The console is a "tty" device. */ if (TTY_CALLOUT(tp, dev)) { if (tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) { error = EBUSY; goto done; } } else { if (tp->t_flags & TF_OPENED_OUT) { error = EBUSY; goto done; } } if (tp->t_flags & TF_EXCLUDE && priv_check(td, PRIV_TTY_EXCLUSIVE)) { error = EBUSY; goto done; } if (!tty_opened(tp)) { /* Set proper termios flags. */ if (TTY_CALLOUT(tp, dev)) tp->t_termios = tp->t_termios_init_out; else tp->t_termios = tp->t_termios_init_in; ttydevsw_param(tp, &tp->t_termios); /* Prevent modem control on callout devices and /dev/console. */ if (TTY_CALLOUT(tp, dev) || dev == dev_console) tp->t_termios.c_cflag |= CLOCAL; if ((tp->t_termios.c_cflag & CNO_RTSDTR) == 0) ttydevsw_modem(tp, SER_DTR|SER_RTS, 0); error = ttydevsw_open(tp); if (error != 0) goto done; ttydisc_open(tp); error = tty_watermarks(tp); if (error != 0) goto done; } /* Wait for Carrier Detect. */ if ((oflags & O_NONBLOCK) == 0 && (tp->t_termios.c_cflag & CLOCAL) == 0) { while ((ttydevsw_modem(tp, 0, 0) & SER_DCD) == 0) { error = tty_wait(tp, &tp->t_dcdwait); if (error != 0) goto done; } } if (dev == dev_console) tp->t_flags |= TF_OPENED_CONS; else if (TTY_CALLOUT(tp, dev)) tp->t_flags |= TF_OPENED_OUT; else tp->t_flags |= TF_OPENED_IN; MPASS((tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) == 0 || (tp->t_flags & TF_OPENED_OUT) == 0); done: tp->t_flags &= ~TF_OPENCLOSE; cv_broadcast(&tp->t_dcdwait); ttydev_leave(tp); return (error); } static int ttydev_close(struct cdev *dev, int fflag, int devtype __unused, struct thread *td __unused) { struct tty *tp = dev->si_drv1; tty_lock(tp); /* * Don't actually close the device if it is being used as the * console. */ MPASS((tp->t_flags & (TF_OPENED_CONS | TF_OPENED_IN)) == 0 || (tp->t_flags & TF_OPENED_OUT) == 0); if (dev == dev_console) tp->t_flags &= ~TF_OPENED_CONS; else tp->t_flags &= ~(TF_OPENED_IN|TF_OPENED_OUT); if (tp->t_flags & TF_OPENED) { tty_unlock(tp); return (0); } /* If revoking, flush output now to avoid draining it later. */ if (fflag & FREVOKE) tty_flush(tp, FWRITE); tp->t_flags &= ~TF_EXCLUDE; /* Properly wake up threads that are stuck - revoke(). */ tp->t_revokecnt++; tty_wakeup(tp, FREAD|FWRITE); cv_broadcast(&tp->t_bgwait); cv_broadcast(&tp->t_dcdwait); ttydev_leave(tp); return (0); } static __inline int tty_is_ctty(struct tty *tp, struct proc *p) { tty_assert_locked(tp); return (p->p_session == tp->t_session && p->p_flag & P_CONTROLT); } int tty_wait_background(struct tty *tp, struct thread *td, int sig) { struct proc *p; struct pgrp *pg; ksiginfo_t ksi; int error; MPASS(sig == SIGTTIN || sig == SIGTTOU); tty_assert_locked(tp); p = td->td_proc; for (;;) { pg = p->p_pgrp; PGRP_LOCK(pg); PROC_LOCK(p); /* * pg may no longer be our process group. * Re-check after locking. */ if (p->p_pgrp != pg) { PROC_UNLOCK(p); PGRP_UNLOCK(pg); continue; } /* * The process should only sleep, when: * - This terminal is the controlling terminal * - Its process group is not the foreground process * group * - The parent process isn't waiting for the child to * exit * - the signal to send to the process isn't masked */ if (!tty_is_ctty(tp, p) || p->p_pgrp == tp->t_pgrp) { /* Allow the action to happen. */ PROC_UNLOCK(p); PGRP_UNLOCK(pg); return (0); } if (SIGISMEMBER(p->p_sigacts->ps_sigignore, sig) || SIGISMEMBER(td->td_sigmask, sig)) { /* Only allow them in write()/ioctl(). */ PROC_UNLOCK(p); PGRP_UNLOCK(pg); return (sig == SIGTTOU ? 0 : EIO); } if ((p->p_flag & P_PPWAIT) != 0 || (pg->pg_flags & PGRP_ORPHANED) != 0) { /* Don't allow the action to happen. */ PROC_UNLOCK(p); PGRP_UNLOCK(pg); return (EIO); } PROC_UNLOCK(p); /* * Send the signal and sleep until we're the new * foreground process group. */ if (sig != 0) { ksiginfo_init(&ksi); ksi.ksi_code = SI_KERNEL; ksi.ksi_signo = sig; sig = 0; } pgsignal(pg, ksi.ksi_signo, 1, &ksi); PGRP_UNLOCK(pg); error = tty_wait(tp, &tp->t_bgwait); if (error) return (error); } } static int ttydev_read(struct cdev *dev, struct uio *uio, int ioflag) { struct tty *tp = dev->si_drv1; int error; error = ttydev_enter(tp); if (error) goto done; error = ttydisc_read(tp, uio, ioflag); tty_unlock(tp); /* * The read() call should not throw an error when the device is * being destroyed. Silently convert it to an EOF. */ done: if (error == ENXIO) error = 0; return (error); } static int ttydev_write(struct cdev *dev, struct uio *uio, int ioflag) { struct tty *tp = dev->si_drv1; int defer, error; error = ttydev_enter(tp); if (error) return (error); if (tp->t_termios.c_lflag & TOSTOP) { error = tty_wait_background(tp, curthread, SIGTTOU); if (error) goto done; } if (ioflag & IO_NDELAY && tp->t_flags & TF_BUSY_OUT) { /* Allow non-blocking writes to bypass serialization. */ error = ttydisc_write(tp, uio, ioflag); } else { /* Serialize write() calls. */ while (tp->t_flags & TF_BUSY_OUT) { error = tty_wait(tp, &tp->t_outserwait); if (error) goto done; } tp->t_flags |= TF_BUSY_OUT; defer = sigdeferstop(SIGDEFERSTOP_ERESTART); error = ttydisc_write(tp, uio, ioflag); sigallowstop(defer); tp->t_flags &= ~TF_BUSY_OUT; cv_signal(&tp->t_outserwait); } done: tty_unlock(tp); return (error); } static int ttydev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { struct tty *tp = dev->si_drv1; int error; error = ttydev_enter(tp); if (error) return (error); switch (cmd) { case TIOCCBRK: case TIOCCONS: case TIOCDRAIN: case TIOCEXCL: case TIOCFLUSH: case TIOCNXCL: case TIOCSBRK: case TIOCSCTTY: case TIOCSETA: case TIOCSETAF: case TIOCSETAW: case TIOCSPGRP: case TIOCSTART: case TIOCSTAT: case TIOCSTI: case TIOCSTOP: case TIOCSWINSZ: #if 0 case TIOCSDRAINWAIT: case TIOCSETD: #endif #ifdef COMPAT_43TTY case TIOCLBIC: case TIOCLBIS: case TIOCLSET: case TIOCSETC: case OTIOCSETD: case TIOCSETN: case TIOCSETP: case TIOCSLTC: #endif /* COMPAT_43TTY */ /* * If the ioctl() causes the TTY to be modified, let it * wait in the background. */ error = tty_wait_background(tp, curthread, SIGTTOU); if (error) goto done; } if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { struct termios *old = &tp->t_termios; struct termios *new = (struct termios *)data; struct termios *lock = TTY_CALLOUT(tp, dev) ? &tp->t_termios_lock_out : &tp->t_termios_lock_in; int cc; /* * Lock state devices. Just overwrite the values of the * commands that are currently in use. */ new->c_iflag = (old->c_iflag & lock->c_iflag) | (new->c_iflag & ~lock->c_iflag); new->c_oflag = (old->c_oflag & lock->c_oflag) | (new->c_oflag & ~lock->c_oflag); new->c_cflag = (old->c_cflag & lock->c_cflag) | (new->c_cflag & ~lock->c_cflag); new->c_lflag = (old->c_lflag & lock->c_lflag) | (new->c_lflag & ~lock->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lock->c_cc[cc]) new->c_cc[cc] = old->c_cc[cc]; if (lock->c_ispeed) new->c_ispeed = old->c_ispeed; if (lock->c_ospeed) new->c_ospeed = old->c_ospeed; } error = tty_ioctl(tp, cmd, data, fflag, td); done: tty_unlock(tp); return (error); } static int ttydev_poll(struct cdev *dev, int events, struct thread *td) { struct tty *tp = dev->si_drv1; int error, revents = 0; error = ttydev_enter(tp); if (error) return ((events & (POLLIN|POLLRDNORM)) | POLLHUP); if (events & (POLLIN|POLLRDNORM)) { /* See if we can read something. */ if (ttydisc_read_poll(tp) > 0) revents |= events & (POLLIN|POLLRDNORM); } if (tp->t_flags & TF_ZOMBIE) { /* Hangup flag on zombie state. */ revents |= POLLHUP; } else if (events & (POLLOUT|POLLWRNORM)) { /* See if we can write something. */ if (ttydisc_write_poll(tp) > 0) revents |= events & (POLLOUT|POLLWRNORM); } if (revents == 0) { if (events & (POLLIN|POLLRDNORM)) selrecord(td, &tp->t_inpoll); if (events & (POLLOUT|POLLWRNORM)) selrecord(td, &tp->t_outpoll); } tty_unlock(tp); return (revents); } static int ttydev_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr) { struct tty *tp = dev->si_drv1; int error; /* Handle mmap() through the driver. */ error = ttydev_enter(tp); if (error) return (-1); error = ttydevsw_mmap(tp, offset, paddr, nprot, memattr); tty_unlock(tp); return (error); } /* * kqueue support. */ static void tty_kqops_read_detach(struct knote *kn) { struct tty *tp = kn->kn_hook; knlist_remove(&tp->t_inpoll.si_note, kn, 0); } static int tty_kqops_read_event(struct knote *kn, long hint __unused) { struct tty *tp = kn->kn_hook; tty_assert_locked(tp); if (tty_gone(tp) || tp->t_flags & TF_ZOMBIE) { kn->kn_flags |= EV_EOF; return (1); } else { kn->kn_data = ttydisc_read_poll(tp); return (kn->kn_data > 0); } } static void tty_kqops_write_detach(struct knote *kn) { struct tty *tp = kn->kn_hook; knlist_remove(&tp->t_outpoll.si_note, kn, 0); } static int tty_kqops_write_event(struct knote *kn, long hint __unused) { struct tty *tp = kn->kn_hook; tty_assert_locked(tp); if (tty_gone(tp)) { kn->kn_flags |= EV_EOF; return (1); } else { kn->kn_data = ttydisc_write_poll(tp); return (kn->kn_data > 0); } } static struct filterops tty_kqops_read = { .f_isfd = 1, .f_detach = tty_kqops_read_detach, .f_event = tty_kqops_read_event, }; static struct filterops tty_kqops_write = { .f_isfd = 1, .f_detach = tty_kqops_write_detach, .f_event = tty_kqops_write_event, }; static int ttydev_kqfilter(struct cdev *dev, struct knote *kn) { struct tty *tp = dev->si_drv1; int error; error = ttydev_enter(tp); if (error) return (error); switch (kn->kn_filter) { case EVFILT_READ: kn->kn_hook = tp; kn->kn_fop = &tty_kqops_read; knlist_add(&tp->t_inpoll.si_note, kn, 1); break; case EVFILT_WRITE: kn->kn_hook = tp; kn->kn_fop = &tty_kqops_write; knlist_add(&tp->t_outpoll.si_note, kn, 1); break; default: error = EINVAL; break; } tty_unlock(tp); return (error); } static struct cdevsw ttydev_cdevsw = { .d_version = D_VERSION, .d_open = ttydev_open, .d_close = ttydev_close, .d_read = ttydev_read, .d_write = ttydev_write, .d_ioctl = ttydev_ioctl, .d_kqfilter = ttydev_kqfilter, .d_poll = ttydev_poll, .d_mmap = ttydev_mmap, .d_name = "ttydev", .d_flags = D_TTY, }; /* * Init/lock-state devices */ static int ttyil_open(struct cdev *dev, int oflags __unused, int devtype __unused, struct thread *td) { struct tty *tp; int error; tp = dev->si_drv1; error = 0; tty_lock(tp); if (tty_gone(tp)) error = ENODEV; tty_unlock(tp); return (error); } static int ttyil_close(struct cdev *dev __unused, int flag __unused, int mode __unused, struct thread *td __unused) { return (0); } static int ttyil_rdwr(struct cdev *dev __unused, struct uio *uio __unused, int ioflag __unused) { return (ENODEV); } static int ttyil_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { struct tty *tp = dev->si_drv1; int error; tty_lock(tp); if (tty_gone(tp)) { error = ENODEV; goto done; } error = ttydevsw_cioctl(tp, dev2unit(dev), cmd, data, td); if (error != ENOIOCTL) goto done; error = 0; switch (cmd) { case TIOCGETA: /* Obtain terminal flags through tcgetattr(). */ *(struct termios*)data = *(struct termios*)dev->si_drv2; break; case TIOCSETA: /* Set terminal flags through tcsetattr(). */ error = priv_check(td, PRIV_TTY_SETA); if (error) break; *(struct termios*)dev->si_drv2 = *(struct termios*)data; break; case TIOCGETD: *(int *)data = TTYDISC; break; case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); break; default: error = ENOTTY; } done: tty_unlock(tp); return (error); } static struct cdevsw ttyil_cdevsw = { .d_version = D_VERSION, .d_open = ttyil_open, .d_close = ttyil_close, .d_read = ttyil_rdwr, .d_write = ttyil_rdwr, .d_ioctl = ttyil_ioctl, .d_name = "ttyil", .d_flags = D_TTY, }; static void tty_init_termios(struct tty *tp) { struct termios *t = &tp->t_termios_init_in; t->c_cflag = TTYDEF_CFLAG; t->c_iflag = TTYDEF_IFLAG; t->c_lflag = TTYDEF_LFLAG; t->c_oflag = TTYDEF_OFLAG; t->c_ispeed = TTYDEF_SPEED; t->c_ospeed = TTYDEF_SPEED; memcpy(&t->c_cc, ttydefchars, sizeof ttydefchars); tp->t_termios_init_out = *t; } void tty_init_console(struct tty *tp, speed_t s) { struct termios *ti = &tp->t_termios_init_in; struct termios *to = &tp->t_termios_init_out; if (s != 0) { ti->c_ispeed = ti->c_ospeed = s; to->c_ispeed = to->c_ospeed = s; } ti->c_cflag |= CLOCAL; to->c_cflag |= CLOCAL; } /* * Standard device routine implementations, mostly meant for * pseudo-terminal device drivers. When a driver creates a new terminal * device class, missing routines are patched. */ static int ttydevsw_defopen(struct tty *tp __unused) { return (0); } static void ttydevsw_defclose(struct tty *tp __unused) { } static void ttydevsw_defoutwakeup(struct tty *tp __unused) { panic("Terminal device has output, while not implemented"); } static void ttydevsw_definwakeup(struct tty *tp __unused) { } static int ttydevsw_defioctl(struct tty *tp __unused, u_long cmd __unused, caddr_t data __unused, struct thread *td __unused) { return (ENOIOCTL); } static int ttydevsw_defcioctl(struct tty *tp __unused, int unit __unused, u_long cmd __unused, caddr_t data __unused, struct thread *td __unused) { return (ENOIOCTL); } static int ttydevsw_defparam(struct tty *tp __unused, struct termios *t) { /* * Allow the baud rate to be adjusted for pseudo-devices, but at * least restrict it to 115200 to prevent excessive buffer * usage. Also disallow 0, to prevent foot shooting. */ if (t->c_ispeed < B50) t->c_ispeed = B50; else if (t->c_ispeed > B115200) t->c_ispeed = B115200; if (t->c_ospeed < B50) t->c_ospeed = B50; else if (t->c_ospeed > B115200) t->c_ospeed = B115200; t->c_cflag |= CREAD; return (0); } static int ttydevsw_defmodem(struct tty *tp __unused, int sigon __unused, int sigoff __unused) { /* Simulate a carrier to make the TTY layer happy. */ return (SER_DCD); } static int ttydevsw_defmmap(struct tty *tp __unused, vm_ooffset_t offset __unused, vm_paddr_t *paddr __unused, int nprot __unused, vm_memattr_t *memattr __unused) { return (-1); } static void ttydevsw_defpktnotify(struct tty *tp __unused, char event __unused) { } static void ttydevsw_deffree(void *softc __unused) { panic("Terminal device freed without a free-handler"); } static bool ttydevsw_defbusy(struct tty *tp __unused) { return (FALSE); } /* * TTY allocation and deallocation. TTY devices can be deallocated when * the driver doesn't use it anymore, when the TTY isn't a session's * controlling TTY and when the device node isn't opened through devfs. */ struct tty * tty_alloc(struct ttydevsw *tsw, void *sc) { return (tty_alloc_mutex(tsw, sc, NULL)); } struct tty * tty_alloc_mutex(struct ttydevsw *tsw, void *sc, struct mtx *mutex) { struct tty *tp; /* Make sure the driver defines all routines. */ #define PATCH_FUNC(x) do { \ if (tsw->tsw_ ## x == NULL) \ tsw->tsw_ ## x = ttydevsw_def ## x; \ } while (0) PATCH_FUNC(open); PATCH_FUNC(close); PATCH_FUNC(outwakeup); PATCH_FUNC(inwakeup); PATCH_FUNC(ioctl); PATCH_FUNC(cioctl); PATCH_FUNC(param); PATCH_FUNC(modem); PATCH_FUNC(mmap); PATCH_FUNC(pktnotify); PATCH_FUNC(free); PATCH_FUNC(busy); #undef PATCH_FUNC tp = malloc(sizeof(struct tty) + TTY_PRBUF_SIZE, M_TTY, M_WAITOK | M_ZERO); tp->t_prbufsz = TTY_PRBUF_SIZE; tp->t_devsw = tsw; tp->t_devswsoftc = sc; tp->t_flags = tsw->tsw_flags; tp->t_drainwait = tty_drainwait; tty_init_termios(tp); cv_init(&tp->t_inwait, "ttyin"); cv_init(&tp->t_outwait, "ttyout"); cv_init(&tp->t_outserwait, "ttyosr"); cv_init(&tp->t_bgwait, "ttybg"); cv_init(&tp->t_dcdwait, "ttydcd"); /* Allow drivers to use a custom mutex to lock the TTY. */ if (mutex != NULL) { tp->t_mtx = mutex; } else { tp->t_mtx = &tp->t_mtxobj; mtx_init(&tp->t_mtxobj, "ttymtx", NULL, MTX_DEF); } knlist_init_mtx(&tp->t_inpoll.si_note, tp->t_mtx); knlist_init_mtx(&tp->t_outpoll.si_note, tp->t_mtx); return (tp); } static void tty_dealloc(void *arg) { struct tty *tp = arg; /* * ttyydev_leave() usually frees the i/o queues earlier, but it is * not always called between queue allocation and here. The queues * may be allocated by ioctls on a pty control device without the * corresponding pty slave device ever being open, or after it is * closed. */ ttyinq_free(&tp->t_inq); ttyoutq_free(&tp->t_outq); seldrain(&tp->t_inpoll); seldrain(&tp->t_outpoll); knlist_destroy(&tp->t_inpoll.si_note); knlist_destroy(&tp->t_outpoll.si_note); cv_destroy(&tp->t_inwait); cv_destroy(&tp->t_outwait); cv_destroy(&tp->t_bgwait); cv_destroy(&tp->t_dcdwait); cv_destroy(&tp->t_outserwait); if (tp->t_mtx == &tp->t_mtxobj) mtx_destroy(&tp->t_mtxobj); ttydevsw_free(tp); free(tp, M_TTY); } static void tty_rel_free(struct tty *tp) { struct cdev *dev; tty_assert_locked(tp); #define TF_ACTIVITY (TF_GONE|TF_OPENED|TF_HOOK|TF_OPENCLOSE) if (tp->t_sessioncnt != 0 || (tp->t_flags & TF_ACTIVITY) != TF_GONE) { /* TTY is still in use. */ tty_unlock(tp); return; } /* Stop asynchronous I/O. */ funsetown(&tp->t_sigio); /* TTY can be deallocated. */ dev = tp->t_dev; tp->t_dev = NULL; tty_unlock(tp); if (dev != NULL) { sx_xlock(&tty_list_sx); TAILQ_REMOVE(&tty_list, tp, t_list); tty_list_count--; sx_xunlock(&tty_list_sx); destroy_dev_sched_cb(dev, tty_dealloc, tp); } } void tty_rel_pgrp(struct tty *tp, struct pgrp *pg) { MPASS(tp->t_sessioncnt > 0); tty_assert_locked(tp); if (tp->t_pgrp == pg) tp->t_pgrp = NULL; tty_unlock(tp); } void tty_rel_sess(struct tty *tp, struct session *sess) { MPASS(tp->t_sessioncnt > 0); /* Current session has left. */ if (tp->t_session == sess) { tp->t_session = NULL; MPASS(tp->t_pgrp == NULL); } tp->t_sessioncnt--; tty_rel_free(tp); } void tty_rel_gone(struct tty *tp) { tty_assert_locked(tp); MPASS(!tty_gone(tp)); /* Simulate carrier removal. */ ttydisc_modem(tp, 0); /* Wake up all blocked threads. */ tty_wakeup(tp, FREAD|FWRITE); cv_broadcast(&tp->t_bgwait); cv_broadcast(&tp->t_dcdwait); tp->t_flags |= TF_GONE; tty_rel_free(tp); } static int tty_drop_ctty(struct tty *tp, struct proc *p) { struct session *session; struct vnode *vp; /* * This looks terrible, but it's generally safe as long as the tty * hasn't gone away while we had the lock dropped. All of our sanity * checking that this operation is OK happens after we've picked it back * up, so other state changes are generally not fatal and the potential * for this particular operation to happen out-of-order in a * multithreaded scenario is likely a non-issue. */ tty_unlock(tp); sx_xlock(&proctree_lock); tty_lock(tp); if (tty_gone(tp)) { sx_xunlock(&proctree_lock); return (ENODEV); } /* * If the session doesn't have a controlling TTY, or if we weren't * invoked on the controlling TTY, we'll return ENOIOCTL as we've * historically done. */ session = p->p_session; if (session->s_ttyp == NULL || session->s_ttyp != tp) { sx_xunlock(&proctree_lock); return (ENOTTY); } if (!SESS_LEADER(p)) { sx_xunlock(&proctree_lock); return (EPERM); } PROC_LOCK(p); SESS_LOCK(session); vp = session->s_ttyvp; session->s_ttyp = NULL; session->s_ttyvp = NULL; session->s_ttydp = NULL; SESS_UNLOCK(session); tp->t_sessioncnt--; p->p_flag &= ~P_CONTROLT; PROC_UNLOCK(p); sx_xunlock(&proctree_lock); /* * If we did have a vnode, release our reference. Ordinarily we manage * these at the devfs layer, but we can't necessarily know that we were * invoked on the vnode referenced in the session (i.e. the vnode we * hold a reference to). We explicitly don't check VBAD/VIRF_DOOMED here * to avoid a vnode leak -- in circumstances elsewhere where we'd hit a * VIRF_DOOMED vnode, release has been deferred until the controlling TTY * is either changed or released. */ if (vp != NULL) devfs_ctty_unref(vp); return (0); } /* * Exposing information about current TTY's through sysctl */ static void tty_to_xtty(struct tty *tp, struct xtty *xt) { tty_assert_locked(tp); xt->xt_size = sizeof(struct xtty); xt->xt_insize = ttyinq_getsize(&tp->t_inq); xt->xt_incc = ttyinq_bytescanonicalized(&tp->t_inq); xt->xt_inlc = ttyinq_bytesline(&tp->t_inq); xt->xt_inlow = tp->t_inlow; xt->xt_outsize = ttyoutq_getsize(&tp->t_outq); xt->xt_outcc = ttyoutq_bytesused(&tp->t_outq); xt->xt_outlow = tp->t_outlow; xt->xt_column = tp->t_column; xt->xt_pgid = tp->t_pgrp ? tp->t_pgrp->pg_id : 0; xt->xt_sid = tp->t_session ? tp->t_session->s_sid : 0; xt->xt_flags = tp->t_flags; xt->xt_dev = tp->t_dev ? dev2udev(tp->t_dev) : (uint32_t)NODEV; } static int sysctl_kern_ttys(SYSCTL_HANDLER_ARGS) { unsigned long lsize; struct xtty *xtlist, *xt; struct tty *tp; int error; sx_slock(&tty_list_sx); lsize = tty_list_count * sizeof(struct xtty); if (lsize == 0) { sx_sunlock(&tty_list_sx); return (0); } xtlist = xt = malloc(lsize, M_TTY, M_WAITOK); TAILQ_FOREACH(tp, &tty_list, t_list) { tty_lock(tp); tty_to_xtty(tp, xt); tty_unlock(tp); xt++; } sx_sunlock(&tty_list_sx); error = SYSCTL_OUT(req, xtlist, lsize); free(xtlist, M_TTY); return (error); } SYSCTL_PROC(_kern, OID_AUTO, ttys, CTLTYPE_OPAQUE|CTLFLAG_RD|CTLFLAG_MPSAFE, 0, 0, sysctl_kern_ttys, "S,xtty", "List of TTYs"); /* * Device node creation. Device has been set up, now we can expose it to * the user. */ int tty_makedevf(struct tty *tp, struct ucred *cred, int flags, const char *fmt, ...) { va_list ap; struct make_dev_args args; struct cdev *dev, *init, *lock, *cua, *cinit, *clock; const char *prefix = "tty"; char name[SPECNAMELEN - 3]; /* for "tty" and "cua". */ uid_t uid; gid_t gid; mode_t mode; int error; /* Remove "tty" prefix from devices like PTY's. */ if (tp->t_flags & TF_NOPREFIX) prefix = ""; va_start(ap, fmt); vsnrprintf(name, sizeof name, 32, fmt, ap); va_end(ap); if (cred == NULL) { /* System device. */ uid = UID_ROOT; gid = GID_WHEEL; mode = S_IRUSR|S_IWUSR; } else { /* User device. */ uid = cred->cr_ruid; gid = GID_TTY; mode = S_IRUSR|S_IWUSR|S_IWGRP; } flags = flags & TTYMK_CLONING ? MAKEDEV_REF : 0; flags |= MAKEDEV_CHECKNAME; /* Master call-in device. */ make_dev_args_init(&args); args.mda_flags = flags; args.mda_devsw = &ttydev_cdevsw; args.mda_cr = cred; args.mda_uid = uid; args.mda_gid = gid; args.mda_mode = mode; args.mda_si_drv1 = tp; error = make_dev_s(&args, &dev, "%s%s", prefix, name); if (error != 0) return (error); tp->t_dev = dev; init = lock = cua = cinit = clock = NULL; /* Slave call-in devices. */ if (tp->t_flags & TF_INITLOCK) { args.mda_devsw = &ttyil_cdevsw; args.mda_unit = TTYUNIT_INIT; args.mda_si_drv1 = tp; args.mda_si_drv2 = &tp->t_termios_init_in; error = make_dev_s(&args, &init, "%s%s.init", prefix, name); if (error != 0) goto fail; dev_depends(dev, init); args.mda_unit = TTYUNIT_LOCK; args.mda_si_drv2 = &tp->t_termios_lock_in; error = make_dev_s(&args, &lock, "%s%s.lock", prefix, name); if (error != 0) goto fail; dev_depends(dev, lock); } /* Call-out devices. */ if (tp->t_flags & TF_CALLOUT) { make_dev_args_init(&args); args.mda_flags = flags; args.mda_devsw = &ttydev_cdevsw; args.mda_cr = cred; args.mda_uid = UID_UUCP; args.mda_gid = GID_DIALER; args.mda_mode = 0660; args.mda_unit = TTYUNIT_CALLOUT; args.mda_si_drv1 = tp; error = make_dev_s(&args, &cua, "cua%s", name); if (error != 0) goto fail; dev_depends(dev, cua); /* Slave call-out devices. */ if (tp->t_flags & TF_INITLOCK) { args.mda_devsw = &ttyil_cdevsw; args.mda_unit = TTYUNIT_CALLOUT | TTYUNIT_INIT; args.mda_si_drv2 = &tp->t_termios_init_out; error = make_dev_s(&args, &cinit, "cua%s.init", name); if (error != 0) goto fail; dev_depends(dev, cinit); args.mda_unit = TTYUNIT_CALLOUT | TTYUNIT_LOCK; args.mda_si_drv2 = &tp->t_termios_lock_out; error = make_dev_s(&args, &clock, "cua%s.lock", name); if (error != 0) goto fail; dev_depends(dev, clock); } } sx_xlock(&tty_list_sx); TAILQ_INSERT_TAIL(&tty_list, tp, t_list); tty_list_count++; sx_xunlock(&tty_list_sx); return (0); fail: destroy_dev(dev); if (init) destroy_dev(init); if (lock) destroy_dev(lock); if (cinit) destroy_dev(cinit); if (clock) destroy_dev(clock); return (error); } /* * Signalling processes. */ void tty_signal_sessleader(struct tty *tp, int sig) { struct proc *p; struct session *s; tty_assert_locked(tp); MPASS(sig >= 1 && sig < NSIG); /* Make signals start output again. */ tp->t_flags &= ~TF_STOPPED; tp->t_termios.c_lflag &= ~FLUSHO; /* * Load s_leader exactly once to avoid race where s_leader is * set to NULL by a concurrent invocation of killjobc() by the * session leader. Note that we are not holding t_session's * lock for the read. */ if ((s = tp->t_session) != NULL && (p = atomic_load_ptr(&s->s_leader)) != NULL) { PROC_LOCK(p); kern_psignal(p, sig); PROC_UNLOCK(p); } } void tty_signal_pgrp(struct tty *tp, int sig) { ksiginfo_t ksi; tty_assert_locked(tp); MPASS(sig >= 1 && sig < NSIG); /* Make signals start output again. */ tp->t_flags &= ~TF_STOPPED; tp->t_termios.c_lflag &= ~FLUSHO; if (sig == SIGINFO && !(tp->t_termios.c_lflag & NOKERNINFO)) tty_info(tp); if (tp->t_pgrp != NULL) { ksiginfo_init(&ksi); ksi.ksi_signo = sig; ksi.ksi_code = SI_KERNEL; PGRP_LOCK(tp->t_pgrp); pgsignal(tp->t_pgrp, sig, 1, &ksi); PGRP_UNLOCK(tp->t_pgrp); } } void tty_wakeup(struct tty *tp, int flags) { if (tp->t_flags & TF_ASYNC && tp->t_sigio != NULL) pgsigio(&tp->t_sigio, SIGIO, (tp->t_session != NULL)); if (flags & FWRITE) { cv_broadcast(&tp->t_outwait); selwakeup(&tp->t_outpoll); KNOTE_LOCKED(&tp->t_outpoll.si_note, 0); } if (flags & FREAD) { cv_broadcast(&tp->t_inwait); selwakeup(&tp->t_inpoll); KNOTE_LOCKED(&tp->t_inpoll.si_note, 0); } } int tty_wait(struct tty *tp, struct cv *cv) { int error; int revokecnt = tp->t_revokecnt; tty_lock_assert(tp, MA_OWNED|MA_NOTRECURSED); MPASS(!tty_gone(tp)); error = cv_wait_sig(cv, tp->t_mtx); /* Bail out when the device slipped away. */ if (tty_gone(tp)) return (ENXIO); /* Restart the system call when we may have been revoked. */ if (tp->t_revokecnt != revokecnt) return (ERESTART); return (error); } int tty_timedwait(struct tty *tp, struct cv *cv, int hz) { int error; int revokecnt = tp->t_revokecnt; tty_lock_assert(tp, MA_OWNED|MA_NOTRECURSED); MPASS(!tty_gone(tp)); error = cv_timedwait_sig(cv, tp->t_mtx, hz); /* Bail out when the device slipped away. */ if (tty_gone(tp)) return (ENXIO); /* Restart the system call when we may have been revoked. */ if (tp->t_revokecnt != revokecnt) return (ERESTART); return (error); } void tty_flush(struct tty *tp, int flags) { if (flags & FWRITE) { tp->t_flags &= ~TF_HIWAT_OUT; ttyoutq_flush(&tp->t_outq); tty_wakeup(tp, FWRITE); if (!tty_gone(tp)) { ttydevsw_outwakeup(tp); ttydevsw_pktnotify(tp, TIOCPKT_FLUSHWRITE); } } if (flags & FREAD) { tty_hiwat_in_unblock(tp); ttyinq_flush(&tp->t_inq); tty_wakeup(tp, FREAD); if (!tty_gone(tp)) { ttydevsw_inwakeup(tp); ttydevsw_pktnotify(tp, TIOCPKT_FLUSHREAD); } } } void tty_set_winsize(struct tty *tp, const struct winsize *wsz) { if (memcmp(&tp->t_winsize, wsz, sizeof(*wsz)) == 0) return; tp->t_winsize = *wsz; tty_signal_pgrp(tp, SIGWINCH); } static int tty_generic_ioctl(struct tty *tp, u_long cmd, void *data, int fflag, struct thread *td) { int error; switch (cmd) { /* * Modem commands. * The SER_* and TIOCM_* flags are the same, but one bit * shifted. I don't know why. */ case TIOCSDTR: ttydevsw_modem(tp, SER_DTR, 0); return (0); case TIOCCDTR: ttydevsw_modem(tp, 0, SER_DTR); return (0); case TIOCMSET: { int bits = *(int *)data; ttydevsw_modem(tp, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, ((~bits) & (TIOCM_DTR | TIOCM_RTS)) >> 1); return (0); } case TIOCMBIS: { int bits = *(int *)data; ttydevsw_modem(tp, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1, 0); return (0); } case TIOCMBIC: { int bits = *(int *)data; ttydevsw_modem(tp, 0, (bits & (TIOCM_DTR | TIOCM_RTS)) >> 1); return (0); } case TIOCMGET: *(int *)data = TIOCM_LE + (ttydevsw_modem(tp, 0, 0) << 1); return (0); case FIOASYNC: if (*(int *)data) tp->t_flags |= TF_ASYNC; else tp->t_flags &= ~TF_ASYNC; return (0); case FIONBIO: /* This device supports non-blocking operation. */ return (0); case FIONREAD: *(int *)data = ttyinq_bytescanonicalized(&tp->t_inq); return (0); case FIONWRITE: case TIOCOUTQ: *(int *)data = ttyoutq_bytesused(&tp->t_outq); return (0); case FIOSETOWN: if (tp->t_session != NULL && !tty_is_ctty(tp, td->td_proc)) /* Not allowed to set ownership. */ return (ENOTTY); /* Temporarily unlock the TTY to set ownership. */ tty_unlock(tp); error = fsetown(*(int *)data, &tp->t_sigio); tty_lock(tp); return (error); case FIOGETOWN: if (tp->t_session != NULL && !tty_is_ctty(tp, td->td_proc)) /* Not allowed to set ownership. */ return (ENOTTY); /* Get ownership. */ *(int *)data = fgetown(&tp->t_sigio); return (0); case TIOCGETA: /* Obtain terminal flags through tcgetattr(). */ *(struct termios*)data = tp->t_termios; return (0); case TIOCSETA: case TIOCSETAW: case TIOCSETAF: { struct termios *t = data; /* * Who makes up these funny rules? According to POSIX, * input baud rate is set equal to the output baud rate * when zero. */ if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; /* Discard any unsupported bits. */ t->c_iflag &= TTYSUP_IFLAG; t->c_oflag &= TTYSUP_OFLAG; t->c_lflag &= TTYSUP_LFLAG; t->c_cflag &= TTYSUP_CFLAG; /* Set terminal flags through tcsetattr(). */ if (cmd == TIOCSETAW || cmd == TIOCSETAF) { error = tty_drain(tp, 0); if (error) return (error); if (cmd == TIOCSETAF) tty_flush(tp, FREAD); } /* * Only call param() when the flags really change. */ if ((t->c_cflag & CIGNORE) == 0 && (tp->t_termios.c_cflag != t->c_cflag || ((tp->t_termios.c_iflag ^ t->c_iflag) & (IXON|IXOFF|IXANY)) || tp->t_termios.c_ispeed != t->c_ispeed || tp->t_termios.c_ospeed != t->c_ospeed)) { error = ttydevsw_param(tp, t); if (error) return (error); /* XXX: CLOCAL? */ tp->t_termios.c_cflag = t->c_cflag & ~CIGNORE; tp->t_termios.c_ispeed = t->c_ispeed; tp->t_termios.c_ospeed = t->c_ospeed; /* Baud rate has changed - update watermarks. */ error = tty_watermarks(tp); if (error) return (error); } /* Copy new non-device driver parameters. */ tp->t_termios.c_iflag = t->c_iflag; tp->t_termios.c_oflag = t->c_oflag; tp->t_termios.c_lflag = t->c_lflag; memcpy(&tp->t_termios.c_cc, t->c_cc, sizeof t->c_cc); ttydisc_optimize(tp); if ((t->c_lflag & ICANON) == 0) { /* * When in non-canonical mode, wake up all * readers. Canonicalize any partial input. VMIN * and VTIME could also be adjusted. */ ttyinq_canonicalize(&tp->t_inq); tty_wakeup(tp, FREAD); } /* * For packet mode: notify the PTY consumer that VSTOP * and VSTART may have been changed. */ if (tp->t_termios.c_iflag & IXON && tp->t_termios.c_cc[VSTOP] == CTRL('S') && tp->t_termios.c_cc[VSTART] == CTRL('Q')) ttydevsw_pktnotify(tp, TIOCPKT_DOSTOP); else ttydevsw_pktnotify(tp, TIOCPKT_NOSTOP); return (0); } case TIOCGETD: /* For compatibility - we only support TTYDISC. */ *(int *)data = TTYDISC; return (0); case TIOCGPGRP: if (!tty_is_ctty(tp, td->td_proc)) return (ENOTTY); if (tp->t_pgrp != NULL) *(int *)data = tp->t_pgrp->pg_id; else *(int *)data = NO_PID; return (0); case TIOCGSID: if (!tty_is_ctty(tp, td->td_proc)) return (ENOTTY); MPASS(tp->t_session); *(int *)data = tp->t_session->s_sid; return (0); case TIOCNOTTY: return (tty_drop_ctty(tp, td->td_proc)); case TIOCSCTTY: { struct proc *p = td->td_proc; /* XXX: This looks awful. */ tty_unlock(tp); sx_xlock(&proctree_lock); tty_lock(tp); if (!SESS_LEADER(p)) { /* Only the session leader may do this. */ sx_xunlock(&proctree_lock); return (EPERM); } if (tp->t_session != NULL && tp->t_session == p->p_session) { /* This is already our controlling TTY. */ sx_xunlock(&proctree_lock); return (0); } if (p->p_session->s_ttyp != NULL || (tp->t_session != NULL && tp->t_session->s_ttyvp != NULL && tp->t_session->s_ttyvp->v_type != VBAD)) { /* * There is already a relation between a TTY and * a session, or the caller is not the session * leader. * * Allow the TTY to be stolen when the vnode is * invalid, but the reference to the TTY is * still active. This allows immediate reuse of * TTYs of which the session leader has been * killed or the TTY revoked. */ sx_xunlock(&proctree_lock); return (EPERM); } /* Connect the session to the TTY. */ tp->t_session = p->p_session; tp->t_session->s_ttyp = tp; tp->t_sessioncnt++; /* Assign foreground process group. */ tp->t_pgrp = p->p_pgrp; PROC_LOCK(p); p->p_flag |= P_CONTROLT; PROC_UNLOCK(p); sx_xunlock(&proctree_lock); return (0); } case TIOCSPGRP: { struct pgrp *pg; /* * XXX: Temporarily unlock the TTY to locate the process * group. This code would be lot nicer if we would ever * decompose proctree_lock. */ tty_unlock(tp); sx_slock(&proctree_lock); pg = pgfind(*(int *)data); if (pg != NULL) PGRP_UNLOCK(pg); if (pg == NULL || pg->pg_session != td->td_proc->p_session) { sx_sunlock(&proctree_lock); tty_lock(tp); return (EPERM); } tty_lock(tp); /* * Determine if this TTY is the controlling TTY after * relocking the TTY. */ if (!tty_is_ctty(tp, td->td_proc)) { sx_sunlock(&proctree_lock); return (ENOTTY); } tp->t_pgrp = pg; sx_sunlock(&proctree_lock); /* Wake up the background process groups. */ cv_broadcast(&tp->t_bgwait); return (0); } case TIOCFLUSH: { int flags = *(int *)data; if (flags == 0) flags = (FREAD|FWRITE); else flags &= (FREAD|FWRITE); tty_flush(tp, flags); return (0); } case TIOCDRAIN: /* Drain TTY output. */ return tty_drain(tp, 0); case TIOCGDRAINWAIT: *(int *)data = tp->t_drainwait; return (0); case TIOCSDRAINWAIT: error = priv_check(td, PRIV_TTY_DRAINWAIT); if (error == 0) tp->t_drainwait = *(int *)data; return (error); case TIOCCONS: /* Set terminal as console TTY. */ if (*(int *)data) { error = priv_check(td, PRIV_TTY_CONSOLE); if (error) return (error); error = constty_set(tp); } else { error = constty_clear(tp); } return (error); case TIOCGWINSZ: /* Obtain window size. */ *(struct winsize*)data = tp->t_winsize; return (0); case TIOCSWINSZ: /* Set window size. */ tty_set_winsize(tp, data); return (0); case TIOCEXCL: tp->t_flags |= TF_EXCLUDE; return (0); case TIOCNXCL: tp->t_flags &= ~TF_EXCLUDE; return (0); case TIOCSTOP: tp->t_flags |= TF_STOPPED; ttydevsw_pktnotify(tp, TIOCPKT_STOP); return (0); case TIOCSTART: tp->t_flags &= ~TF_STOPPED; tp->t_termios.c_lflag &= ~FLUSHO; ttydevsw_outwakeup(tp); ttydevsw_pktnotify(tp, TIOCPKT_START); return (0); case TIOCSTAT: tty_info(tp); return (0); case TIOCSTI: if ((fflag & FREAD) == 0 && priv_check(td, PRIV_TTY_STI)) return (EPERM); if (!tty_is_ctty(tp, td->td_proc) && priv_check(td, PRIV_TTY_STI)) return (EACCES); ttydisc_rint(tp, *(char *)data, 0); ttydisc_rint_done(tp); return (0); } #ifdef COMPAT_43TTY return tty_ioctl_compat(tp, cmd, data, fflag, td); #else /* !COMPAT_43TTY */ return (ENOIOCTL); #endif /* COMPAT_43TTY */ } int tty_ioctl(struct tty *tp, u_long cmd, void *data, int fflag, struct thread *td) { int error; tty_assert_locked(tp); if (tty_gone(tp)) return (ENXIO); error = ttydevsw_ioctl(tp, cmd, data, td); if (error == ENOIOCTL) error = tty_generic_ioctl(tp, cmd, data, fflag, td); return (error); } dev_t tty_udev(struct tty *tp) { if (tp->t_dev) return (dev2udev(tp->t_dev)); else return (NODEV); } int tty_checkoutq(struct tty *tp) { /* 256 bytes should be enough to print a log message. */ return (ttyoutq_bytesleft(&tp->t_outq) >= 256); } void tty_hiwat_in_block(struct tty *tp) { if ((tp->t_flags & TF_HIWAT_IN) == 0 && tp->t_termios.c_iflag & IXOFF && tp->t_termios.c_cc[VSTOP] != _POSIX_VDISABLE) { /* * Input flow control. Only enter the high watermark when we * can successfully store the VSTOP character. */ if (ttyoutq_write_nofrag(&tp->t_outq, &tp->t_termios.c_cc[VSTOP], 1) == 0) tp->t_flags |= TF_HIWAT_IN; } else { /* No input flow control. */ tp->t_flags |= TF_HIWAT_IN; } } void tty_hiwat_in_unblock(struct tty *tp) { if (tp->t_flags & TF_HIWAT_IN && tp->t_termios.c_iflag & IXOFF && tp->t_termios.c_cc[VSTART] != _POSIX_VDISABLE) { /* * Input flow control. Only leave the high watermark when we * can successfully store the VSTART character. */ if (ttyoutq_write_nofrag(&tp->t_outq, &tp->t_termios.c_cc[VSTART], 1) == 0) tp->t_flags &= ~TF_HIWAT_IN; } else { /* No input flow control. */ tp->t_flags &= ~TF_HIWAT_IN; } if (!tty_gone(tp)) ttydevsw_inwakeup(tp); } /* * TTY hooks interface. */ static int ttyhook_defrint(struct tty *tp, char c, int flags) { if (ttyhook_rint_bypass(tp, &c, 1) != 1) return (-1); return (0); } int ttyhook_register(struct tty **rtp, struct proc *p, int fd, struct ttyhook *th, void *softc) { struct tty *tp; struct file *fp; struct cdev *dev; struct cdevsw *cdp; struct filedesc *fdp; cap_rights_t rights; int error, ref; /* Validate the file descriptor. */ /* * XXX this code inspects a file descriptor from a different process, * but there is no dedicated routine to do it in fd code, making the * ordeal highly questionable. */ fdp = p->p_fd; FILEDESC_SLOCK(fdp); error = fget_cap_noref(fdp, fd, cap_rights_init_one(&rights, CAP_TTYHOOK), &fp, NULL); if (error == 0 && !fhold(fp)) error = EBADF; FILEDESC_SUNLOCK(fdp); if (error != 0) return (error); if (fp->f_ops == &badfileops) { error = EBADF; goto done1; } /* * Make sure the vnode is bound to a character device. * Unlocked check for the vnode type is ok there, because we * only shall prevent calling devvn_refthread on the file that * never has been opened over a character device. */ if (fp->f_type != DTYPE_VNODE || fp->f_vnode->v_type != VCHR) { error = EINVAL; goto done1; } /* Make sure it is a TTY. */ cdp = devvn_refthread(fp->f_vnode, &dev, &ref); if (cdp == NULL) { error = ENXIO; goto done1; } if (dev != fp->f_data) { error = ENXIO; goto done2; } if (cdp != &ttydev_cdevsw) { error = ENOTTY; goto done2; } tp = dev->si_drv1; /* Try to attach the hook to the TTY. */ error = EBUSY; tty_lock(tp); MPASS((tp->t_hook == NULL) == ((tp->t_flags & TF_HOOK) == 0)); if (tp->t_flags & TF_HOOK) goto done3; tp->t_flags |= TF_HOOK; tp->t_hook = th; tp->t_hooksoftc = softc; *rtp = tp; error = 0; /* Maybe we can switch into bypass mode now. */ ttydisc_optimize(tp); /* Silently convert rint() calls to rint_bypass() when possible. */ if (!ttyhook_hashook(tp, rint) && ttyhook_hashook(tp, rint_bypass)) th->th_rint = ttyhook_defrint; done3: tty_unlock(tp); done2: dev_relthread(dev, ref); done1: fdrop(fp, curthread); return (error); } void ttyhook_unregister(struct tty *tp) { tty_assert_locked(tp); MPASS(tp->t_flags & TF_HOOK); /* Disconnect the hook. */ tp->t_flags &= ~TF_HOOK; tp->t_hook = NULL; /* Maybe we need to leave bypass mode. */ ttydisc_optimize(tp); /* Maybe deallocate the TTY as well. */ tty_rel_free(tp); } /* * /dev/console handling. */ static int ttyconsdev_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { struct tty *tp; /* System has no console device. */ if (dev_console_filename == NULL) return (ENXIO); /* Look up corresponding TTY by device name. */ sx_slock(&tty_list_sx); TAILQ_FOREACH(tp, &tty_list, t_list) { if (strcmp(dev_console_filename, tty_devname(tp)) == 0) { dev_console->si_drv1 = tp; break; } } sx_sunlock(&tty_list_sx); /* System console has no TTY associated. */ if (dev_console->si_drv1 == NULL) return (ENXIO); return (ttydev_open(dev, oflags, devtype, td)); } static int ttyconsdev_write(struct cdev *dev, struct uio *uio, int ioflag) { log_console(uio); return (ttydev_write(dev, uio, ioflag)); } /* * /dev/console is a little different than normal TTY's. When opened, * it determines which TTY to use. When data gets written to it, it * will be logged in the kernel message buffer. */ static struct cdevsw ttyconsdev_cdevsw = { .d_version = D_VERSION, .d_open = ttyconsdev_open, .d_close = ttydev_close, .d_read = ttydev_read, .d_write = ttyconsdev_write, .d_ioctl = ttydev_ioctl, .d_kqfilter = ttydev_kqfilter, .d_poll = ttydev_poll, .d_mmap = ttydev_mmap, .d_name = "ttyconsdev", .d_flags = D_TTY, }; static void ttyconsdev_init(void *unused __unused) { dev_console = make_dev_credf(MAKEDEV_ETERNAL, &ttyconsdev_cdevsw, 0, NULL, UID_ROOT, GID_WHEEL, 0600, "console"); } SYSINIT(tty, SI_SUB_DRIVERS, SI_ORDER_FIRST, ttyconsdev_init, NULL); void ttyconsdev_select(const char *name) { dev_console_filename = name; } /* * Debugging routines. */ #include "opt_ddb.h" #ifdef DDB #include #include static const struct { int flag; char val; } ttystates[] = { #if 0 { TF_NOPREFIX, 'N' }, #endif { TF_INITLOCK, 'I' }, { TF_CALLOUT, 'C' }, /* Keep these together -> 'Oi' and 'Oo'. */ { TF_OPENED, 'O' }, { TF_OPENED_IN, 'i' }, { TF_OPENED_OUT, 'o' }, { TF_OPENED_CONS, 'c' }, { TF_GONE, 'G' }, { TF_OPENCLOSE, 'B' }, { TF_ASYNC, 'Y' }, { TF_LITERAL, 'L' }, /* Keep these together -> 'Hi' and 'Ho'. */ { TF_HIWAT, 'H' }, { TF_HIWAT_IN, 'i' }, { TF_HIWAT_OUT, 'o' }, { TF_STOPPED, 'S' }, { TF_EXCLUDE, 'X' }, { TF_BYPASS, 'l' }, { TF_ZOMBIE, 'Z' }, { TF_HOOK, 's' }, /* Keep these together -> 'bi' and 'bo'. */ { TF_BUSY, 'b' }, { TF_BUSY_IN, 'i' }, { TF_BUSY_OUT, 'o' }, { 0, '\0'}, }; #define TTY_FLAG_BITS \ "\20\1NOPREFIX\2INITLOCK\3CALLOUT\4OPENED_IN" \ "\5OPENED_OUT\6OPENED_CONS\7GONE\10OPENCLOSE" \ "\11ASYNC\12LITERAL\13HIWAT_IN\14HIWAT_OUT" \ "\15STOPPED\16EXCLUDE\17BYPASS\20ZOMBIE" \ "\21HOOK\22BUSY_IN\23BUSY_OUT" #define DB_PRINTSYM(name, addr) \ db_printf("%s " #name ": ", sep); \ db_printsym((db_addr_t) addr, DB_STGY_ANY); \ db_printf("\n"); static void _db_show_devsw(const char *sep, const struct ttydevsw *tsw) { db_printf("%sdevsw: ", sep); db_printsym((db_addr_t)tsw, DB_STGY_ANY); db_printf(" (%p)\n", tsw); DB_PRINTSYM(open, tsw->tsw_open); DB_PRINTSYM(close, tsw->tsw_close); DB_PRINTSYM(outwakeup, tsw->tsw_outwakeup); DB_PRINTSYM(inwakeup, tsw->tsw_inwakeup); DB_PRINTSYM(ioctl, tsw->tsw_ioctl); DB_PRINTSYM(param, tsw->tsw_param); DB_PRINTSYM(modem, tsw->tsw_modem); DB_PRINTSYM(mmap, tsw->tsw_mmap); DB_PRINTSYM(pktnotify, tsw->tsw_pktnotify); DB_PRINTSYM(free, tsw->tsw_free); } static void _db_show_hooks(const char *sep, const struct ttyhook *th) { db_printf("%shook: ", sep); db_printsym((db_addr_t)th, DB_STGY_ANY); db_printf(" (%p)\n", th); if (th == NULL) return; DB_PRINTSYM(rint, th->th_rint); DB_PRINTSYM(rint_bypass, th->th_rint_bypass); DB_PRINTSYM(rint_done, th->th_rint_done); DB_PRINTSYM(rint_poll, th->th_rint_poll); DB_PRINTSYM(getc_inject, th->th_getc_inject); DB_PRINTSYM(getc_capture, th->th_getc_capture); DB_PRINTSYM(getc_poll, th->th_getc_poll); DB_PRINTSYM(close, th->th_close); } static void _db_show_termios(const char *name, const struct termios *t) { db_printf("%s: iflag 0x%x oflag 0x%x cflag 0x%x " "lflag 0x%x ispeed %u ospeed %u\n", name, t->c_iflag, t->c_oflag, t->c_cflag, t->c_lflag, t->c_ispeed, t->c_ospeed); } /* DDB command to show TTY statistics. */ DB_SHOW_COMMAND(tty, db_show_tty) { struct tty *tp; if (!have_addr) { db_printf("usage: show tty \n"); return; } tp = (struct tty *)addr; db_printf("%p: %s\n", tp, tty_devname(tp)); db_printf("\tmtx: %p\n", tp->t_mtx); db_printf("\tflags: 0x%b\n", tp->t_flags, TTY_FLAG_BITS); db_printf("\trevokecnt: %u\n", tp->t_revokecnt); /* Buffering mechanisms. */ db_printf("\tinq: %p begin %u linestart %u reprint %u end %u " "nblocks %u quota %u\n", &tp->t_inq, tp->t_inq.ti_begin, tp->t_inq.ti_linestart, tp->t_inq.ti_reprint, tp->t_inq.ti_end, tp->t_inq.ti_nblocks, tp->t_inq.ti_quota); db_printf("\toutq: %p begin %u end %u nblocks %u quota %u\n", &tp->t_outq, tp->t_outq.to_begin, tp->t_outq.to_end, tp->t_outq.to_nblocks, tp->t_outq.to_quota); db_printf("\tinlow: %zu\n", tp->t_inlow); db_printf("\toutlow: %zu\n", tp->t_outlow); _db_show_termios("\ttermios", &tp->t_termios); db_printf("\twinsize: row %u col %u xpixel %u ypixel %u\n", tp->t_winsize.ws_row, tp->t_winsize.ws_col, tp->t_winsize.ws_xpixel, tp->t_winsize.ws_ypixel); db_printf("\tcolumn: %u\n", tp->t_column); db_printf("\twritepos: %u\n", tp->t_writepos); db_printf("\tcompatflags: 0x%x\n", tp->t_compatflags); /* Init/lock-state devices. */ _db_show_termios("\ttermios_init_in", &tp->t_termios_init_in); _db_show_termios("\ttermios_init_out", &tp->t_termios_init_out); _db_show_termios("\ttermios_lock_in", &tp->t_termios_lock_in); _db_show_termios("\ttermios_lock_out", &tp->t_termios_lock_out); /* Hooks */ _db_show_devsw("\t", tp->t_devsw); _db_show_hooks("\t", tp->t_hook); /* Process info. */ db_printf("\tpgrp: %p gid %d\n", tp->t_pgrp, tp->t_pgrp ? tp->t_pgrp->pg_id : 0); db_printf("\tsession: %p", tp->t_session); if (tp->t_session != NULL) db_printf(" count %u leader %p tty %p sid %d login %s", tp->t_session->s_count, tp->t_session->s_leader, tp->t_session->s_ttyp, tp->t_session->s_sid, tp->t_session->s_login); db_printf("\n"); db_printf("\tsessioncnt: %u\n", tp->t_sessioncnt); db_printf("\tdevswsoftc: %p\n", tp->t_devswsoftc); db_printf("\thooksoftc: %p\n", tp->t_hooksoftc); db_printf("\tdev: %p\n", tp->t_dev); } /* DDB command to list TTYs. */ DB_SHOW_ALL_COMMAND(ttys, db_show_all_ttys) { struct tty *tp; size_t isiz, osiz; int i, j; /* Make the output look like `pstat -t'. */ db_printf("PTR "); #if defined(__LP64__) db_printf(" "); #endif db_printf(" LINE INQ CAN LIN LOW OUTQ USE LOW " "COL SESS PGID STATE\n"); TAILQ_FOREACH(tp, &tty_list, t_list) { isiz = tp->t_inq.ti_nblocks * TTYINQ_DATASIZE; osiz = tp->t_outq.to_nblocks * TTYOUTQ_DATASIZE; db_printf("%p %10s %5zu %4u %4u %4zu %5zu %4u %4zu %5u %5d " "%5d ", tp, tty_devname(tp), isiz, tp->t_inq.ti_linestart - tp->t_inq.ti_begin, tp->t_inq.ti_end - tp->t_inq.ti_linestart, isiz - tp->t_inlow, osiz, tp->t_outq.to_end - tp->t_outq.to_begin, osiz - tp->t_outlow, MIN(tp->t_column, 99999), tp->t_session ? tp->t_session->s_sid : 0, tp->t_pgrp ? tp->t_pgrp->pg_id : 0); /* Flag bits. */ for (i = j = 0; ttystates[i].flag; i++) if (tp->t_flags & ttystates[i].flag) { db_printf("%c", ttystates[i].val); j++; } if (j == 0) db_printf("-"); db_printf("\n"); } } #endif /* DDB */ diff --git a/sys/sys/_termios.h b/sys/sys/_termios.h index 327ffcb2f98b..823752732ee2 100644 --- a/sys/sys/_termios.h +++ b/sys/sys/_termios.h @@ -1,237 +1,238 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988, 1989, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)termios.h 8.3 (Berkeley) 3/28/94 */ #ifndef _SYS__TERMIOS_H_ #define _SYS__TERMIOS_H_ /* * Special Control Characters * * Index into c_cc[] character array. * * Name Subscript Enabled by */ #define VEOF 0 /* ICANON */ #define VEOL 1 /* ICANON */ #if __BSD_VISIBLE #define VEOL2 2 /* ICANON together with IEXTEN */ #endif #define VERASE 3 /* ICANON */ #if __BSD_VISIBLE #define VWERASE 4 /* ICANON together with IEXTEN */ #endif #define VKILL 5 /* ICANON */ #if __BSD_VISIBLE #define VREPRINT 6 /* ICANON together with IEXTEN */ #define VERASE2 7 /* ICANON */ #endif /* 7 ex-spare 1 */ #define VINTR 8 /* ISIG */ #define VQUIT 9 /* ISIG */ #define VSUSP 10 /* ISIG */ #if __BSD_VISIBLE #define VDSUSP 11 /* ISIG together with IEXTEN */ #endif #define VSTART 12 /* IXON, IXOFF */ #define VSTOP 13 /* IXON, IXOFF */ #if __BSD_VISIBLE #define VLNEXT 14 /* IEXTEN */ #define VDISCARD 15 /* IEXTEN */ #endif #define VMIN 16 /* !ICANON */ #define VTIME 17 /* !ICANON */ #if __BSD_VISIBLE #define VSTATUS 18 /* ICANON together with IEXTEN */ /* 19 spare 2 */ #endif #define NCCS 20 #define _POSIX_VDISABLE 0xff /* * Input flags - software input processing */ #define IGNBRK 0x00000001 /* ignore BREAK condition */ #define BRKINT 0x00000002 /* map BREAK to SIGINTR */ #define IGNPAR 0x00000004 /* ignore (discard) parity errors */ #define PARMRK 0x00000008 /* mark parity and framing errors */ #define INPCK 0x00000010 /* enable checking of parity errors */ #define ISTRIP 0x00000020 /* strip 8th bit off chars */ #define INLCR 0x00000040 /* map NL into CR */ #define IGNCR 0x00000080 /* ignore CR */ #define ICRNL 0x00000100 /* map CR to NL (ala CRMOD) */ #define IXON 0x00000200 /* enable output flow control */ #define IXOFF 0x00000400 /* enable input flow control */ #if __XSI_VISIBLE || __POSIX_VISIBLE >= 200809 #define IXANY 0x00000800 /* any char will restart after stop */ #endif #if __BSD_VISIBLE #define IMAXBEL 0x00002000 /* ring bell on input queue full */ +#define IUTF8 0x00004000 /* assume input is utf-8 encoded */ #endif /* * Output flags - software output processing */ #define OPOST 0x00000001 /* enable following output processing */ #if __XSI_VISIBLE #define ONLCR 0x00000002 /* map NL to CR-NL (ala CRMOD) */ #endif #if __BSD_VISIBLE #define TABDLY 0x00000004 /* tab delay mask */ #define TAB0 0x00000000 /* no tab delay and expansion */ #define TAB3 0x00000004 /* expand tabs to spaces */ #define ONOEOT 0x00000008 /* discard EOT's (^D) on output) */ #endif #if __XSI_VISIBLE #define OCRNL 0x00000010 /* map CR to NL on output */ #define ONOCR 0x00000020 /* no CR output at column 0 */ #define ONLRET 0x00000040 /* NL performs CR function */ #endif /* * Control flags - hardware control of terminal */ #if __BSD_VISIBLE #define CIGNORE 0x00000001 /* ignore control flags */ #endif #define CSIZE 0x00000300 /* character size mask */ #define CS5 0x00000000 /* 5 bits (pseudo) */ #define CS6 0x00000100 /* 6 bits */ #define CS7 0x00000200 /* 7 bits */ #define CS8 0x00000300 /* 8 bits */ #define CSTOPB 0x00000400 /* send 2 stop bits */ #define CREAD 0x00000800 /* enable receiver */ #define PARENB 0x00001000 /* parity enable */ #define PARODD 0x00002000 /* odd parity, else even */ #define HUPCL 0x00004000 /* hang up on last close */ #define CLOCAL 0x00008000 /* ignore modem status lines */ #if __BSD_VISIBLE #define CCTS_OFLOW 0x00010000 /* CTS flow control of output */ #define CRTSCTS (CCTS_OFLOW | CRTS_IFLOW) #define CRTS_IFLOW 0x00020000 /* RTS flow control of input */ #define CDTR_IFLOW 0x00040000 /* DTR flow control of input */ #define CDSR_OFLOW 0x00080000 /* DSR flow control of output */ #define CCAR_OFLOW 0x00100000 /* DCD flow control of output */ #define CNO_RTSDTR 0x00200000 /* Do not assert RTS or DTR automatically */ #endif /* * "Local" flags - dumping ground for other state * * Warning: some flags in this structure begin with * the letter "I" and look like they belong in the * input flag. */ #if __BSD_VISIBLE #define ECHOKE 0x00000001 /* visual erase for line kill */ #endif #define ECHOE 0x00000002 /* visually erase chars */ #define ECHOK 0x00000004 /* echo NL after line kill */ #define ECHO 0x00000008 /* enable echoing */ #define ECHONL 0x00000010 /* echo NL even if ECHO is off */ #if __BSD_VISIBLE #define ECHOPRT 0x00000020 /* visual erase mode for hardcopy */ #define ECHOCTL 0x00000040 /* echo control chars as ^(Char) */ #endif #define ISIG 0x00000080 /* enable signals INTR, QUIT, [D]SUSP */ #define ICANON 0x00000100 /* canonicalize input lines */ #if __BSD_VISIBLE #define ALTWERASE 0x00000200 /* use alternate WERASE algorithm */ #endif #define IEXTEN 0x00000400 /* enable DISCARD and LNEXT */ #define EXTPROC 0x00000800 /* external processing */ #define TOSTOP 0x00400000 /* stop background jobs from output */ #if __BSD_VISIBLE #define FLUSHO 0x00800000 /* output being flushed (state) */ #define NOKERNINFO 0x02000000 /* no kernel output from VSTATUS */ #define PENDIN 0x20000000 /* XXX retype pending input (state) */ #endif #define NOFLSH 0x80000000 /* don't flush after interrupt */ /* * Standard speeds */ #define B0 0 #define B50 50 #define B75 75 #define B110 110 #define B134 134 #define B150 150 #define B200 200 #define B300 300 #define B600 600 #define B1200 1200 #define B1800 1800 #define B2400 2400 #define B4800 4800 #define B9600 9600 #define B19200 19200 #define B38400 38400 #if __BSD_VISIBLE #define B7200 7200 #define B14400 14400 #define B28800 28800 #define B57600 57600 #define B76800 76800 #define B115200 115200 #define B230400 230400 #define B460800 460800 #define B500000 500000 #define B921600 921600 #define B1000000 1000000U #define B1500000 1500000U #define B2000000 2000000U #define B2500000 2500000U #define B3000000 3000000U #define B3500000 3500000U #define B4000000 4000000U #define EXTA 19200 #define EXTB 38400 #endif typedef unsigned int tcflag_t; typedef unsigned char cc_t; typedef unsigned int speed_t; struct termios { tcflag_t c_iflag; /* input flags */ tcflag_t c_oflag; /* output flags */ tcflag_t c_cflag; /* control flags */ tcflag_t c_lflag; /* local flags */ cc_t c_cc[NCCS]; /* control chars */ speed_t c_ispeed; /* input speed */ speed_t c_ospeed; /* output speed */ }; #endif /* !_SYS__TERMIOS_H_ */