* Copyright (c) 1982, 1986, 1990 The Regents of the University of California.
* Copyright (c) 1991 The Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. 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
* from: @(#)tty.c 7.44 (Berkeley) 5/28/91
static int proc_compare
__P((struct proc
*p1
, struct proc
*p2
));
/* symbolic sleep message strings */
char ttyout
[] = "ttyout";
char ttopen
[] = "ttyopn";
char ttclos
[] = "ttycls";
char ttybuf
[] = "ttybuf";
* Table giving parity for characters and indicating
* character classes to tty driver. The 8th bit
* indicates parity, the 7th bit indicates the character
* is an alphameric or underscore (for ALTWERASE), and the
* low 6 bits indicate delay type. If the low 6 bits are 0
* then the character needs no special processing on output;
* classes other than 0 might be translated or (not currently)
#define PARITY(c) (partab[c] & 0x80)
#define ISALPHA(c) (partab[(c)&TTY_CHARMASK] & 0x40)
#define CCLASS(c) (partab[c] & CCLASSMASK)
#define E 0x00 /* even parity */
#define O 0x80 /* odd parity */
#define ALPHA 0x40 /* alpha or underscore */
#define NA ORDINARY|ALPHA
E
|CC
, O
|CC
, O
|CC
, E
|CC
, O
|CC
, E
|CC
, E
|CC
, O
|CC
, /* nul - bel */
O
|BS
, E
|TB
, E
|NL
, O
|CC
, E
|VT
, O
|CR
, O
|CC
, E
|CC
, /* bs - si */
O
|CC
, E
|CC
, E
|CC
, O
|CC
, E
|CC
, O
|CC
, O
|CC
, E
|CC
, /* dle - etb */
E
|CC
, O
|CC
, O
|CC
, E
|CC
, O
|CC
, E
|CC
, E
|CC
, O
|CC
, /* can - us */
O
|NO
, E
|NO
, E
|NO
, O
|NO
, E
|NO
, O
|NO
, O
|NO
, E
|NO
, /* sp - ' */
E
|NO
, O
|NO
, O
|NO
, E
|NO
, O
|NO
, E
|NO
, E
|NO
, O
|NO
, /* ( - / */
E
|NA
, O
|NA
, O
|NA
, E
|NA
, O
|NA
, E
|NA
, E
|NA
, O
|NA
, /* 0 - 7 */
O
|NA
, E
|NA
, E
|NO
, O
|NO
, E
|NO
, O
|NO
, O
|NO
, E
|NO
, /* 8 - ? */
O
|NO
, E
|NA
, E
|NA
, O
|NA
, E
|NA
, O
|NA
, O
|NA
, E
|NA
, /* @ - G */
E
|NA
, O
|NA
, O
|NA
, E
|NA
, O
|NA
, E
|NA
, E
|NA
, O
|NA
, /* H - O */
E
|NA
, O
|NA
, O
|NA
, E
|NA
, O
|NA
, E
|NA
, E
|NA
, O
|NA
, /* P - W */
O
|NA
, E
|NA
, E
|NA
, O
|NO
, E
|NO
, O
|NO
, O
|NO
, O
|NA
, /* X - _ */
E
|NO
, O
|NA
, O
|NA
, E
|NA
, O
|NA
, E
|NA
, E
|NA
, O
|NA
, /* ` - g */
O
|NA
, E
|NA
, E
|NA
, O
|NA
, E
|NA
, O
|NA
, O
|NA
, E
|NA
, /* h - o */
O
|NA
, E
|NA
, E
|NA
, O
|NA
, E
|NA
, O
|NA
, O
|NA
, E
|NA
, /* p - w */
E
|NA
, O
|NA
, O
|NA
, E
|NO
, O
|NO
, E
|NO
, E
|NO
, O
|CC
, /* x - del */
* "meta" chars; should be settable per charset.
* For now, treat all as normal characters.
NA
, NA
, NA
, NA
, NA
, NA
, NA
, NA
,
NA
, NA
, NA
, NA
, NA
, NA
, NA
, NA
,
NA
, NA
, NA
, NA
, NA
, NA
, NA
, NA
,
NA
, NA
, NA
, NA
, NA
, NA
, NA
, NA
,
NA
, NA
, NA
, NA
, NA
, NA
, NA
, NA
,
NA
, NA
, NA
, NA
, NA
, NA
, NA
, NA
,
NA
, NA
, NA
, NA
, NA
, NA
, NA
, NA
,
NA
, NA
, NA
, NA
, NA
, NA
, NA
, NA
,
NA
, NA
, NA
, NA
, NA
, NA
, NA
, NA
,
NA
, NA
, NA
, NA
, NA
, NA
, NA
, NA
,
NA
, NA
, NA
, NA
, NA
, NA
, NA
, NA
,
NA
, NA
, NA
, NA
, NA
, NA
, NA
, NA
,
NA
, NA
, NA
, NA
, NA
, NA
, NA
, NA
,
NA
, NA
, NA
, NA
, NA
, NA
, NA
, NA
,
NA
, NA
, NA
, NA
, NA
, NA
, NA
, NA
,
NA
, NA
, NA
, NA
, NA
, NA
, NA
, NA
,
extern struct tty
*constty
; /* temporary virtual console */
* Is 'c' a line delimiter ("break" character)?
#define ttbreakc(c) ((c) == '\n' || ((c) == cc[VEOF] || \
(c) == cc[VEOL] || (c) == cc[VEOL2]) && (c) != _POSIX_VDISABLE)
bcopy(ttydefchars
, tp
->t_cc
, sizeof(ttydefchars
));
* Flush tty after output has drained.
if ((error
= ttywait(tp
)) == 0)
* Wait for output to drain.
int error
= 0, s
= spltty();
while ((RB_LEN(&tp
->t_out
) || tp
->t_state
&TS_BUSY
) &&
(tp
->t_state
&TS_CARR_ON
|| tp
->t_cflag
&CLOCAL
) &&
tp
->t_state
|= TS_ASLEEP
;
if (error
= ttysleep(tp
, (caddr_t
)&tp
->t_out
,
TTOPRI
| PCATCH
, ttyout
, 0))
register struct ringb *r = qq; \
* Flush TTY read and/or write queues,
* notifying anyone waiting.
tp
->t_state
&= ~(TS_LOCAL
|TS_TBLOCK
); /* XXX - should be TS_RTSBLOCK */
tp
->t_state
&= ~TS_TTSTOP
;
(*cdevsw
[major(tp
->t_dev
)].d_stop
)(tp
, rw
);
wakeup((caddr_t
)&tp
->t_out
);
selwakeup(tp
->t_wsel
, tp
->t_state
& TS_WCOLL
);
tp
->t_state
&= ~TS_WCOLL
;
* Send stop character on input overflow.
rawcc
= RB_LEN(&tp
->t_raw
);
cancc
= RB_LEN(&tp
->t_can
);
ttyflush(tp
, FREAD
|FWRITE
);
* Block further input iff:
* Current input > threshold AND input is available to user program
if (x
>= TTYHOG
/2 && (tp
->t_state
& TS_TBLOCK
) == 0 &&
((tp
->t_lflag
&ICANON
) == 0) || (cancc
> 0)) {
if (tp
->t_cc
[VSTOP
] != _POSIX_VDISABLE
) {
putc(tp
->t_cc
[VSTOP
], &tp
->t_out
);
tp
->t_state
|= TS_TBLOCK
; /* XXX - should be TS_RTSBLOCK? */
if (tp
->t_oproc
) /* kludge for pty */
tp
->t_state
&= ~TS_TIMEOUT
;
* Common code for ioctls on tty devices.
* Called after line-discipline-specific ioctl
* has been called to do discipline-specific functions
* and/or reject any of these ioctl commands.
ttioctl(tp
, com
, data
, flag
)
register struct proc
*p
= curproc
; /* XXX */
* If the ioctl involves modification,
* hang if in the background.
while (isbackground(curproc
, tp
) &&
p
->p_pgrp
->pg_jobc
&& (p
->p_flag
&SPPWAIT
) == 0 &&
(p
->p_sigignore
& sigmask(SIGTTOU
)) == 0 &&
(p
->p_sigmask
& sigmask(SIGTTOU
)) == 0) {
pgsignal(p
->p_pgrp
, SIGTTOU
, 1);
if (error
= ttysleep(tp
, (caddr_t
)&lbolt
,
TTOPRI
| PCATCH
, ttybg
, 0))
/* get discipline number */
*(int *)data
= tp
->t_line
;
/* set line discipline */
register int t
= *(int *)data
;
if ((unsigned)t
>= nldisp
)
(*linesw
[tp
->t_line
].l_close
)(tp
, flag
);
error
= (*linesw
[t
].l_open
)(dev
, tp
);
(void)(*linesw
[tp
->t_line
].l_open
)(dev
, tp
);
/* prevent more opens on channel */
tp
->t_state
|= TS_XCLUDE
;
tp
->t_state
&= ~TS_XCLUDE
;
register int flags
= *(int *)data
;
tp
->t_state
&= ~TS_ASYNC
;
/* return number of characters immediately available */
*(off_t
*)data
= ttnread(tp
);
*(int *)data
= RB_LEN(&tp
->t_out
);
if ((tp
->t_state
&TS_TTSTOP
) == 0) {
tp
->t_state
|= TS_TTSTOP
;
(*cdevsw
[major(tp
->t_dev
)].d_stop
)(tp
, 0);
if ((tp
->t_state
&TS_TTSTOP
) || (tp
->t_lflag
&FLUSHO
)) {
tp
->t_state
&= ~TS_TTSTOP
;
* Simulate typing of a character at the terminal.
if (p
->p_ucred
->cr_uid
&& (flag
& FREAD
) == 0)
if (p
->p_ucred
->cr_uid
&& !isctty(p
, tp
))
(*linesw
[tp
->t_line
].l_rint
)(*(u_char
*)data
, tp
);
struct termios
*t
= (struct termios
*)data
;
bcopy(&tp
->t_termios
, t
, sizeof(struct termios
));
register struct termios
*t
= (struct termios
*)data
;
if (com
== TIOCSETAW
|| com
== TIOCSETAF
) {
if (error
= ttywait(tp
)) {
if ((t
->c_cflag
&CIGNORE
) == 0) {
if (tp
->t_param
&& (error
= (*tp
->t_param
)(tp
, t
))) {
if ((tp
->t_state
&TS_CARR_ON
) == 0 &&
(t
->c_cflag
&CLOCAL
) == 0) {
tp
->t_state
&= ~TS_ISOPEN
;
tp
->t_cflag
= t
->c_cflag
;
tp
->t_ispeed
= t
->c_ispeed
;
tp
->t_ospeed
= t
->c_ospeed
;
if ((t
->c_lflag
&ICANON
) != (tp
->t_lflag
&ICANON
))
catb(&tp
->t_raw
, &tp
->t_can
);
catb(&tp
->t_can
, &tp
->t_raw
);
tp
->t_iflag
= t
->c_iflag
;
tp
->t_oflag
= t
->c_oflag
;
* Make the EXTPROC bit read only.
tp
->t_lflag
= t
->c_lflag
;
bcopy(t
->c_cc
, tp
->t_cc
, sizeof(t
->c_cc
));
* Give load average stats if requested (tcsh uses raw mode
* and directly sends the ioctl() to the tty driver)
* Set controlling terminal.
* Session ctty vnode pointer set in vnode layer.
(p
->p_session
->s_ttyvp
|| tp
->t_session
) &&
(tp
->t_session
!= p
->p_session
))
tp
->t_session
= p
->p_session
;
p
->p_session
->s_ttyp
= tp
;
* Set terminal process group.
register struct pgrp
*pgrp
= pgfind(*(int *)data
);
else if (pgrp
== NULL
|| pgrp
->pg_session
!= p
->p_session
)
*(int *)data
= tp
->t_pgrp
? tp
->t_pgrp
->pg_id
: NO_PID
;
if (bcmp((caddr_t
)&tp
->t_winsize
, data
,
sizeof (struct winsize
))) {
tp
->t_winsize
= *(struct winsize
*)data
;
pgsignal(tp
->t_pgrp
, SIGWINCH
, 1);
*(struct winsize
*)data
= tp
->t_winsize
;
if (constty
&& constty
!= tp
&&
(constty
->t_state
& (TS_CARR_ON
|TS_ISOPEN
)) ==
if (error
= suser(p
->p_ucred
, &p
->p_acflag
))
} else if (tp
== constty
)
return (ttcompat(tp
, com
, data
, flag
));
if (tp
->t_lflag
& PENDIN
)
nread
= RB_LEN(&tp
->t_can
);
if ((tp
->t_lflag
& ICANON
) == 0)
nread
+= RB_LEN(&tp
->t_raw
);
register struct tty
*tp
= &cdevsw
[major(dev
)].d_ttys
[minor(dev
)];
((tp
->t_cflag
&CLOCAL
) == 0 && (tp
->t_state
&TS_CARR_ON
) == 0))
if (tp
->t_rsel
&& (selp
= pfind(tp
->t_rsel
)) && selp
->p_wchan
== (caddr_t
)&selwait
)
if (RB_LEN(&tp
->t_out
) <= tp
->t_lowat
)
if (tp
->t_wsel
&& (selp
= pfind(tp
->t_wsel
)) && selp
->p_wchan
== (caddr_t
)&selwait
)
* Initial open of tty, or (re)entry to standard tty line discipline.
tp
->t_state
&= ~TS_WOPEN
;
if ((tp
->t_state
& TS_ISOPEN
) == 0) {
tp
->t_state
|= TS_ISOPEN
;
bzero((caddr_t
)&tp
->t_winsize
, sizeof(tp
->t_winsize
));
* "close" a line discipline
ttyflush(tp
, FREAD
|FWRITE
);
* Handle close() on a tty line: flush and set to initial state,
* bumping generation number so that pending read/write calls
* can detect recycling of the tty.
ttyflush(tp
, FREAD
|FWRITE
);
* XXX - do we need to send cc[VSTART] or do a ttstart() here in some cases?
* (TS_TBLOCK and TS_RTSBLOCK are being cleared.)
* Handle modem control transition on a tty.
* Flag indicates new state of carrier.
* Returns 0 if the line should be turned off, otherwise 1.
if ((tp
->t_state
&TS_WOPEN
) == 0 && (tp
->t_lflag
&MDMBUF
)) {
* MDMBUF: do flow control according to carrier flag
tp
->t_state
&= ~TS_TTSTOP
;
} else if ((tp
->t_state
&TS_TTSTOP
) == 0) {
tp
->t_state
|= TS_TTSTOP
;
(*cdevsw
[major(tp
->t_dev
)].d_stop
)(tp
, 0);
tp
->t_state
&= ~TS_CARR_ON
;
if (tp
->t_state
&TS_ISOPEN
&& (tp
->t_cflag
&CLOCAL
) == 0) {
if (tp
->t_session
&& tp
->t_session
->s_leader
)
psignal(tp
->t_session
->s_leader
, SIGHUP
);
ttyflush(tp
, FREAD
|FWRITE
);
tp
->t_state
|= TS_CARR_ON
;
* Default modem control routine (for other line disciplines).
* Return argument flag, to turn off device on carrier drop.
tp
->t_state
|= TS_CARR_ON
;
tp
->t_state
&= ~TS_CARR_ON
;
if ((tp
->t_cflag
&CLOCAL
) == 0) {
if (tp
->t_session
&& tp
->t_session
->s_leader
)
psignal(tp
->t_session
->s_leader
, SIGHUP
);
* reinput pending characters after state switch
hd
= RB_SUCC(&tp
->t_raw
, hd
);
tp
->t_state
&= ~TS_TYPEN
;
* Process input of a single character received on a tty.
register int iflag
= tp
->t_iflag
;
register int lflag
= tp
->t_lflag
;
register u_char
*cc
= tp
->t_cc
;
* If input is pending take it first.
* Handle exceptional conditions (break, parity, framing).
if (err
= (c
&TTY_ERRORMASK
)) {
if (err
&TTY_FE
&& !c
) { /* break */
else if (iflag
&BRKINT
&& lflag
&ISIG
&&
(cc
[VINTR
] != _POSIX_VDISABLE
))
} else if ((err
&TTY_PE
&& iflag
&INPCK
) || err
&TTY_FE
) {
putc(0377|TTY_QUOTE
, &tp
->t_raw
);
putc(0|TTY_QUOTE
, &tp
->t_raw
);
putc(c
|TTY_QUOTE
, &tp
->t_raw
);
* In tandem mode, check high water mark.
if ((tp
->t_state
&TS_TYPEN
) == 0 && (iflag
&ISTRIP
))
if ((tp
->t_lflag
&EXTPROC
) == 0) {
* Check for literal nexting very first
if (tp
->t_state
&TS_LNCH
) {
* Scan for special characters. This code
* is really just a big case statement with
* non-constant cases. The bottom of the
* case statement is labeled ``endcase'', so goto
* it after a case match, or similar.
* Control chars which aren't controlled
* by ICANON, ISIG, or IXON.
if (CCEQ(cc
[VLNEXT
], c
)) {
if (CCEQ(cc
[VDISCARD
], c
)) {
if (RB_LEN(&tp
->t_raw
) + RB_LEN(&tp
->t_can
))
if (CCEQ(cc
[VINTR
], c
) || CCEQ(cc
[VQUIT
], c
)) {
ttyflush(tp
, FREAD
|FWRITE
);
CCEQ(cc
[VINTR
], c
) ? SIGINT
: SIGQUIT
, 1);
if (CCEQ(cc
[VSUSP
], c
)) {
pgsignal(tp
->t_pgrp
, SIGTSTP
, 1);
* Handle start/stop characters.
if (CCEQ(cc
[VSTOP
], c
)) {
if ((tp
->t_state
&TS_TTSTOP
) == 0) {
tp
->t_state
|= TS_TTSTOP
;
(*cdevsw
[major(tp
->t_dev
)].d_stop
)(tp
,
if (!CCEQ(cc
[VSTART
], c
))
* if VSTART == VSTOP then toggle
} else if (c
== '\n' && iflag
&INLCR
)
if ((tp
->t_lflag
&EXTPROC
) == 0 && lflag
&ICANON
) {
* From here on down canonical mode character
* processing takes place.
if (CCEQ(cc
[VERASE
], c
)) {
ttyrub(unputc(&tp
->t_raw
), tp
);
if (CCEQ(cc
[VKILL
], c
)) {
if (lflag
&ECHOKE
&& RB_LEN(&tp
->t_raw
) == tp
->t_rocount
&&
while (RB_LEN(&tp
->t_raw
))
ttyrub(unputc(&tp
->t_raw
), tp
);
if (lflag
&ECHOK
|| lflag
&ECHOKE
)
while (getc(&tp
->t_raw
) > 0)
tp
->t_state
&= ~TS_LOCAL
;
if (CCEQ(cc
[VWERASE
], c
)) {
int alt
= lflag
&ALTWERASE
;
while ((c
= unputc(&tp
->t_raw
)) == ' ' || c
== '\t')
* erase last char of word and remember the
* next chars type (for ALTWERASE)
} while (c
!= ' ' && c
!= '\t' &&
(alt
== 0 || ISALPHA(c
) == ctype
));
(void) putc(c
, &tp
->t_raw
);
if (CCEQ(cc
[VREPRINT
], c
)) {
* ^T - kernel info and generate SIGINFO
if (CCEQ(cc
[VSTATUS
], c
)) {
pgsignal(tp
->t_pgrp
, SIGINFO
, 1);
if ((lflag
&NOKERNINFO
) == 0)
* Check for input buffer overflow
if (RB_LEN(&tp
->t_raw
)+RB_LEN(&tp
->t_can
) >= TTYHOG
) {
if (RB_LEN(&tp
->t_out
) < tp
->t_hiwat
)
(void) ttyoutput(CTRL('g'), tp
);
ttyflush(tp
, FREAD
| FWRITE
);
* Put data char in q for user and
* wakeup on seeing a line delimiter.
if (putc(c
, &tp
->t_raw
) >= 0) {
if ((lflag
&ICANON
) == 0) {
catb(&tp
->t_raw
, &tp
->t_can
);
} else if (tp
->t_rocount
++ == 0)
if (tp
->t_state
&TS_ERASE
) {
tp
->t_state
&= ~TS_ERASE
;
(void) ttyoutput('/', tp
);
if (CCEQ(cc
[VEOF
], c
) && lflag
&ECHO
) {
* Place the cursor over the '^' of the ^D.
i
= MIN(2, tp
->t_col
- i
);
(void) ttyoutput('\b', tp
);
* IXANY means allow any character to restart output.
if ((tp
->t_state
&TS_TTSTOP
) && (iflag
&IXANY
) == 0 &&
tp
->t_state
&= ~TS_TTSTOP
;
* Output a single character on a tty, doing output processing
* as needed (expanding tabs, newline processing, etc.).
* Returns < 0 if putc succeeds, otherwise returns char to resend.
register long oflag
= tp
->t_oflag
;
if ((oflag
&OPOST
) == 0) {
* Do tab expansion if OXTABS is set.
* Special case if we have external processing, we don't
* do the tab expansion because we'll probably get it
* wrong. If tab expansion needs to be done, let it
if (c
== '\t' && oflag
&OXTABS
&& (tp
->t_lflag
&EXTPROC
) == 0) {
if ((tp
->t_lflag
&FLUSHO
) == 0) {
s
= spltty(); /* don't interrupt tabs */
c
-= b_to_q(" ", c
, &tp
->t_outq
);
i
= min (c
, RB_CONTIGPUT(&tp
->t_out
));
bcopy(" ", tp
->t_out
.rb_tl
, i
);
RB_ROLLOVER(&tp
->t_out
, tp
->t_out
.rb_tl
+i
);
i
= min (c
-i
, RB_CONTIGPUT(&tp
->t_out
));
/* off end and still have space? */
bcopy(" ", tp
->t_out
.rb_tl
, i
);
RB_ROLLOVER(&tp
->t_out
, tp
->t_out
.rb_tl
+i
);
if (c
== CEOT
&& oflag
&ONOEOT
)
* Newline translation: if ONLCR is set,
* translate newline into "\r\n".
if (c
== '\n' && (tp
->t_oflag
&ONLCR
) && ttyoutput('\r', tp
) >= 0)
if ((tp
->t_lflag
&FLUSHO
) == 0 && putc(c
, &tp
->t_out
))
* Process a read call on a tty device.
register struct ringb
*qp
;
register u_char
*cc
= tp
->t_cc
;
register struct proc
*p
= curproc
;
* take pending input first
* Hang process if it's in the background.
if (isbackground(p
, tp
)) {
if ((p
->p_sigignore
& sigmask(SIGTTIN
)) ||
(p
->p_sigmask
& sigmask(SIGTTIN
)) ||
p
->p_flag
&SPPWAIT
|| p
->p_pgrp
->pg_jobc
== 0)
pgsignal(p
->p_pgrp
, SIGTTIN
, 1);
if (error
= ttysleep(tp
, (caddr_t
)&lbolt
, TTIPRI
| PCATCH
,
* If canonical, use the canonical queue,
* else use the raw queue.
qp
= lflag
&ICANON
? &tp
->t_can
: &tp
->t_raw
;
* If there is no input, sleep on rawq
* awaiting hardware receipt and notification.
* If we have data, we don't need to check for carrier.
carrier
= (tp
->t_state
&TS_CARR_ON
) || (tp
->t_cflag
&CLOCAL
);
if (!carrier
&& tp
->t_state
&TS_ISOPEN
) {
error
= ttysleep(tp
, (caddr_t
)&tp
->t_raw
, TTIPRI
| PCATCH
,
carrier
? ttyin
: ttopen
, 0);
* Input present, check for input mapping and processing.
while ((c
= getc(qp
)) >= 0) {
if (CCEQ(cc
[VDSUSP
], c
) && lflag
&ISIG
) {
pgsignal(tp
->t_pgrp
, SIGTSTP
, 1);
if (error
= ttysleep(tp
, (caddr_t
)&lbolt
,
TTIPRI
| PCATCH
, ttybg
, 0))
* Interpret EOF only in canonical mode.
if (CCEQ(cc
[VEOF
], c
) && lflag
&ICANON
)
* In canonical mode check for a "break character"
* marking the end of a "line of input".
if (lflag
&ICANON
&& ttbreakc(c
))
* Look to unblock output now that (presumably)
* the input queue has gone down.
if (tp
->t_state
&TS_TBLOCK
&& RB_LEN(&tp
->t_raw
) < TTYHOG
/5) {
if (cc
[VSTART
] != _POSIX_VDISABLE
&&
putc(cc
[VSTART
], &tp
->t_out
) == 0) {
tp
->t_state
&= ~TS_TBLOCK
;
#define TS_RTSBLOCK TS_TBLOCK /* XXX */
#define RB_I_LOW_WATER ((RBSZ - 2 * 256) * 7 / 8) /* XXX */
if (tp
->t_state
&TS_RTSBLOCK
&& RB_LEN(&tp
->t_raw
) <= RB_I_LOW_WATER
) {
tp
->t_state
&= ~TS_RTSBLOCK
;
* Check the output queue on tp for space for a kernel message
* (from uprintf/tprintf). Allow some space over the normal
* hiwater mark so we don't lose messages due to normal flow
* control, but don't let the tty run amok.
* Sleeps here are not interruptible, but we return prematurely
* if new signals come in.
if (RB_LEN(&tp
->t_out
) > hiwat
+ 200)
while (RB_LEN(&tp
->t_out
) > hiwat
) {
if (wait
== 0 || (curproc
&& curproc
->p_sig
!= oldsig
)) {
timeout(wakeup
, (caddr_t
)&tp
->t_out
, hz
);
tp
->t_state
|= TS_ASLEEP
;
sleep((caddr_t
)&tp
->t_out
, PZERO
- 1);
* Process a write call on a tty device.
register struct uio
*uio
;
register struct proc
*p
= curproc
;
int i
, hiwat
, cnt
, error
, s
;
if ((tp
->t_state
&TS_CARR_ON
) == 0 && (tp
->t_cflag
&CLOCAL
) == 0) {
if (tp
->t_state
&TS_ISOPEN
) {
} else if (flag
& IO_NDELAY
) {
error
= ttysleep(tp
, (caddr_t
)&tp
->t_raw
,
TTIPRI
| PCATCH
,ttopen
, 0);
* Hang the process if it's in the background.
if (isbackground(p
, tp
) &&
tp
->t_lflag
&TOSTOP
&& (p
->p_flag
&SPPWAIT
) == 0 &&
(p
->p_sigignore
& sigmask(SIGTTOU
)) == 0 &&
(p
->p_sigmask
& sigmask(SIGTTOU
)) == 0 &&
pgsignal(p
->p_pgrp
, SIGTTOU
, 1);
if (error
= ttysleep(tp
, (caddr_t
)&lbolt
, TTIPRI
| PCATCH
,
* Process the user's data in at most OBUFSIZ
* chunks. Perform any output translation.
* Keep track of high water mark, sleep on overflow
* awaiting device aid in acquiring new space.
while (uio
->uio_resid
> 0 || cc
> 0) {
if (tp
->t_lflag
&FLUSHO
) {
if (RB_LEN(&tp
->t_out
) > hiwat
)
* Grab a hunk of data from the user,
* unless we have some leftover from last time.
cc
= min(uio
->uio_resid
, OBUFSIZ
);
error
= uiomove(cp
, cc
, uio
);
* If nothing fancy need be done, grab those characters we
* can handle without any of ttyoutput's processing and
* just transfer them to the output q. For those chars
* which require special processing (as indicated by the
* bits in partab), call ttyoutput. After processing
* a hunk of data, look for FLUSHO so ^O's will take effect
if ((tp
->t_oflag
&OPOST
) == 0)
ce
= cc
- scanc((unsigned)cc
, (u_char
*)cp
,
(u_char
*)partab
, CCLASSMASK
);
* If ce is zero, then we're processing
* a special character through ttyoutput.
if (ttyoutput(*cp
, tp
) >= 0) {
/* no c-lists, wait a bit */
printf("\nttysleep - no c-lists\n"); /* XXX */
TTOPRI
| PCATCH
, ttybuf
, 0))
if ((tp
->t_lflag
&FLUSHO
) ||
RB_LEN(&tp
->t_out
) > hiwat
)
* A bunch of normal characters have been found,
* transfer them en masse to the output queue and
* continue processing at the top of the loop.
* If there are any further characters in this
* <= OBUFSIZ chunk, the first should be a character
* requiring special handling by ttyoutput.
i
= b_to_q(cp
, ce
, &tp
->t_outq
);
ce
= min (ce
, RB_CONTIGPUT(&tp
->t_out
));
bcopy(cp
, tp
->t_out
.rb_tl
, ce
);
tp
->t_out
.rb_tl
= RB_ROLLOVER(&tp
->t_out
,
ii
= min (i
, RB_CONTIGPUT(&tp
->t_out
));
bcopy(cp
+ ce
, tp
->t_out
.rb_tl
, ii
);
tp
->t_out
.rb_tl
= RB_ROLLOVER(&tp
->t_out
,
cp
+= ce
, cc
-= ce
, tk_nout
+= ce
;
if (RB_CONTIGPUT(&tp
->t_out
) > 0)
goto loop
; /* synchronous/fast */
/* out of space, wait a bit */
tp
->t_state
|= TS_ASLEEP
;
if (error
= ttysleep(tp
, (caddr_t
)&tp
->t_out
,
TTOPRI
| PCATCH
, ttybuf
, 0))
if (tp
->t_lflag
&FLUSHO
|| RB_LEN(&tp
->t_out
) > hiwat
)
* If cc is nonzero, we leave the uio structure inconsistent,
* as the offset and iov pointers have moved forward,
* but it doesn't matter (the call will either return short
* or restart with a new uio).
* This can only occur if FLUSHO is set in t_lflag,
* or if ttstart/oproc is synchronous (or very fast).
if (RB_LEN(&tp
->t_out
) <= hiwat
) {
if (uio
->uio_resid
== cnt
)
tp
->t_state
|= TS_ASLEEP
;
error
= ttysleep(tp
, (caddr_t
)&tp
->t_out
, TTOPRI
| PCATCH
, ttyout
, 0);
* Rubout one character from the rawq of tp
* as cleanly as possible.
if ((tp
->t_lflag
&ECHO
) == 0 || (tp
->t_lflag
&EXTPROC
))
if (tp
->t_rocount
== 0) {
* Screwed by ttwrite; retype
if (c
== ('\t'|TTY_QUOTE
) || c
== ('\n'|TTY_QUOTE
))
else switch (CCLASS(c
&= TTY_CHARMASK
)) {
if (tp
->t_rocount
< RB_LEN(&tp
->t_raw
)) {
for (c
= nextc(&cp
, &tp
->t_raw
); c
;
c
= nextc(&cp
, &tp
->t_raw
))
tp
->t_state
&= ~TS_CNTTB
;
* savecol will now be length of the tab
savecol
= 8; /* overflow screw */
(void) ttyoutput('\b', tp
);
printf("ttyrub: would panic c = %d, val = %d\n",
} else if (tp
->t_lflag
&ECHOPRT
) {
if ((tp
->t_state
&TS_ERASE
) == 0) {
(void) ttyoutput('\\', tp
);
ttyecho(tp
->t_cc
[VERASE
], tp
);
* Crt back over cnt chars perhaps
* We assume c_cc has already been checked.
if (tp
->t_cc
[VREPRINT
] != _POSIX_VDISABLE
)
ttyecho(tp
->t_cc
[VREPRINT
], tp
);
(void) ttyoutput('\n', tp
);
for (c
= nextc(&cp
, &tp
->t_can
); c
; c
= nextc(&cp
, &tp
->t_can
))
for (c
= nextc(&cp
, &tp
->t_raw
); c
; c
= nextc(&cp
, &tp
->t_raw
))
tp
->t_state
&= ~TS_ERASE
;
tp
->t_rocount
= RB_LEN(&tp
->t_raw
);
* Echo a typed character to the terminal.
if ((tp
->t_state
& TS_CNTTB
) == 0)
if (tp
->t_lflag
& EXTPROC
)
if ((tp
->t_lflag
& ECHO
) == 0) {
if ((tp
->t_lflag
& ECHONL
) == 0)
if (tp
->t_lflag
& ECHOCTL
) {
if ((c
& TTY_CHARMASK
) <= 037 && c
!= '\t' && c
!= '\n' ||
(void) ttyoutput('^', tp
);
* Wake up any readers on a tty.
selwakeup(tp
->t_rsel
, tp
->t_state
&TS_RCOLL
);
tp
->t_state
&= ~TS_RCOLL
;
if (tp
->t_state
& TS_ASYNC
)
pgsignal(tp
->t_pgrp
, SIGIO
, 1);
wakeup((caddr_t
)&tp
->t_raw
);
* Look up a code for a specified speed in a conversion table;
* used by drivers to map software speed values to hardware parameters.
register struct speedtab
*table
;
for ( ; table
->sp_speed
!= -1; table
++)
if (table
->sp_speed
== speed
)
* set tty hi and low water marks
* Try to arrange the dynamics so there's about one second
register cps
= tp
->t_ospeed
/ 10;
#define clamp(x, h, l) ((x)>h ? h : ((x)<l) ? l : (x))
tp
->t_lowat
= x
= clamp(cps
/2, TTMAXLOWAT
, TTMINLOWAT
);
x
= clamp(x
, TTMAXHIWAT
, TTMINHIWAT
);
tp
->t_hiwat
= roundup(x
, CBSIZE
);
* Report on state of foreground process group.
register struct proc
*p
, *pick
;
struct timeval utime
, stime
;
if (ttycheckoutq(tp
,0) == 0)
/* Print load average. */
tmp
= (averunnable
[0] * 100 + FSCALE
/ 2) >> FSHIFT
;
ttyprintf(tp
, "load: %d.%02d ", tmp
/ 100, tmp
% 100);
if (tp
->t_session
== NULL
)
ttyprintf(tp
, "not a controlling terminal\n");
else if (tp
->t_pgrp
== NULL
)
ttyprintf(tp
, "no foreground process group\n");
else if ((p
= tp
->t_pgrp
->pg_mem
) == NULL
)
ttyprintf(tp
, "empty foreground process group\n");
/* Pick interesting process. */
for (pick
= NULL
; p
!= NULL
; p
= p
->p_pgrpnxt
)
if (proc_compare(pick
, p
))
ttyprintf(tp
, " cmd: %s %d [%s] ", pick
->p_comm
, pick
->p_pid
,
pick
->p_stat
== SRUN
? "running" :
pick
->p_wmesg
? pick
->p_wmesg
: "iowait");
* Lock out clock if process is running; get user/system
ttyprintf(tp
, "%d.%02du ",
utime
.tv_sec
, (utime
.tv_usec
+ 5000) / 10000);
ttyprintf(tp
, "%d.%02ds ",
stime
.tv_sec
, (stime
.tv_usec
+ 5000) / 10000);
#define pgtok(a) (((a) * NBPG) / 1024)
/* Print percentage cpu, resident set size. */
tmp
= pick
->p_pctcpu
* 10000 + FSCALE
/ 2 >> FSHIFT
;
ttyprintf(tp
, "%d%% %dk\n",
tmp
/ 100, pgtok(pick
->p_vmspace
->vm_rssize
));
tp
->t_rocount
= 0; /* so pending input will be retyped if BS */
* Returns 1 if p2 is "better" than p1
* The algorithm for picking the "interesting" process is thus:
* 1) (Only foreground processes are eligable - implied)
* 2) Runnable processes are favored over anything
* else. The runner with the highest cpu
* utilization is picked (p_cpu). Ties are
* broken by picking the highest pid.
* 3 Next, the sleeper with the shortest sleep
* time is favored. With ties, we pick out
* just "short-term" sleepers (SSINTR == 0).
* Further ties are broken by picking the highest
#define isrun(p) (((p)->p_stat == SRUN) || ((p)->p_stat == SIDL))
#define TESTAB(a, b) ((a)<<1 | (b))
register struct proc
*p1
, *p2
;
* see if at least one of them is runnable
switch (TESTAB(isrun(p1
), isrun(p2
))) {
* tie - favor one with highest recent cpu utilization
if (p2
->p_cpu
> p1
->p_cpu
)
if (p1
->p_cpu
> p2
->p_cpu
)
return (p2
->p_pid
> p1
->p_pid
); /* tie - return highest pid */
switch (TESTAB(p1
->p_stat
== SZOMB
, p2
->p_stat
== SZOMB
)) {
return (p2
->p_pid
> p1
->p_pid
); /* tie - return highest pid */
* pick the one with the smallest sleep time
if (p2
->p_slptime
> p1
->p_slptime
)
if (p1
->p_slptime
> p2
->p_slptime
)
* favor one sleeping in a non-interruptible sleep
if (p1
->p_flag
&SSINTR
&& (p2
->p_flag
&SSINTR
) == 0)
if (p2
->p_flag
&SSINTR
&& (p1
->p_flag
&SSINTR
) == 0)
return (p2
->p_pid
> p1
->p_pid
); /* tie - return highest pid */
* Output char to tty; console putchar style.
if ((tp
->t_state
& (TS_CARR_ON
|TS_ISOPEN
)) == (TS_CARR_ON
|TS_ISOPEN
)) {
(void) ttyoutput('\r', tp
);
* Sleep on chan, returning ERESTART if tty changed
* while we napped and returning any errors (e.g. EINTR/ETIMEDOUT)
* reported by tsleep. If the tty is revoked, restarting a pending
* call will redo validation done at the start of the call.
ttysleep(tp
, chan
, pri
, wmesg
, timo
)
if (error
= tsleep(chan
, pri
, wmesg
, timo
))