* Copyright (c) 1982, 1986, 1990, 1991, 1993
* The Regents of the University of California. All rights reserved.
* %sccs.include.redist.c%
* @(#)tty.c 8.2 (Berkeley) %G%
static int proc_compare
__P((struct proc
*p1
, struct proc
*p2
));
static int ttnread
__P((struct tty
*));
static void ttyblock
__P((struct tty
*tp
));
static void ttyecho
__P((int, struct tty
*tp
));
static void ttyrubo
__P((struct tty
*, int));
/* Symbolic sleep message strings. */
char ttclos
[] = "ttycls";
char ttopen
[] = "ttyopn";
char ttybuf
[] = "ttybuf";
char ttyout
[] = "ttyout";
* Table with character classes and parity. 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) require delays.
#define E 0x00 /* Even parity. */
#define O 0x80 /* Odd parity. */
#define PARITY(c) (char_type[c] & O)
#define ALPHA 0x40 /* Alpha or underscore. */
#define ISALPHA(c) (char_type[(c) & TTY_CHARMASK] & ALPHA)
#define CCLASS(c) (char_type[c] & CCLASSMASK)
#define NA ORDINARY | ALPHA
char const char_type
[] = {
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 character set;
* for now, treat them 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
,
/* Macros to clear/set/test flags. */
#define SET(t, f) (t) |= (f)
#define CLR(t, f) (t) &= ~(f)
#define ISSET(t, f) ((t) & (f))
* Initial open of tty, or (re)entry to standard tty line discipline.
if (!ISSET(tp
->t_state
, TS_ISOPEN
)) {
SET(tp
->t_state
, TS_ISOPEN
);
bzero(&tp
->t_winsize
, sizeof(tp
->t_winsize
));
CLR(tp
->t_state
, TS_WOPEN
);
* 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.
extern struct tty
*constty
; /* Temporary virtual console. */
ttyflush(tp
, FREAD
| FWRITE
);
/* Is 'c' a line delimiter ("break" character)? */
((c) == '\n' || ((c) == cc[VEOF] || \
(c) == cc[VEOL] || (c) == cc[VEOL2]) && (c) != _POSIX_VDISABLE)
* 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.
if (ISSET(lflag
, ICANON
)) {
if (err
= (c
&TTY_ERRORMASK
)) {
if (err
&TTY_FE
&& !c
) { /* break */
else if (iflag
&BRKINT
&& lflag
&ISIG
&&
(cc
[VINTR
] != POSIX_V_DISABLE
))
} else if ((err
&TTY_PE
&& iflag
&INPCK
) || err
&TTY_FE
) {
* In tandem mode, check high water mark.
* In tandem mode, check high water mark.
if ((tp
->t_state
&TS_TYPEN
) == 0 && (iflag
&ISTRIP
))
* Extensions to POSIX input modes which aren't controlled
* by ICANON, ISIG, or IXON.
if (CCEQ(cc
[VLNEXT
],c
) && (iflag
&ISTRIP
)) {
ttyout("^\b", tp
); /*XXX - presumes too much */
if (ISSET(lflag
, ISIG
)) {
if (CCEQ(cc
[VINTR
], c
) || CCEQ(cc
[VQUIT
], c
)) {
if (!ISSET(lflag
, NOFLSH
))
ttyflush(tp
, FREAD
| FWRITE
);
if (CCEQ(cc
[VSUSP
], c
)) {
if (!ISSET(lflag
, NOFLSH
))
pgsignal(tp
->t_pgrp
, SIGTSTP
, 1);
if ((tp
->t_state
&TS_TTSTOP
) == 0) {
tp
->t_state
|= TS_TTSTOP
;
(*cdevsw
[major(tp
->t_dev
)].d_stop
)(tp
, 0);
if (!CCEQ(cc
[VSTART
], c
))
* if VSTART == VSTOP we toggle
} while (c
!= ' ' && c
!= '\t');
(void) putc(c
, &tp
->t_rawq
);
if (CCEQ(cc
[VREPRINT
], c
)) {
* Check for input buffer overflow
if (tp
->t_rawq
.c_cc
+ tp
->t_canq
.c_cc
>= TTYHOG
) {
if (tp
->t_outq
.c_cc
< TTHIWAT(tp
))
(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_rawq
) >= 0) {
if (!ISSET(lflag
, ICANON
)) {
catq(&tp
->t_rawq
, &tp
->t_canq
);
} else if (tp
->t_rocount
++ == 0)
if (CCEQ(cc
[VQUOTE
], c
) && (iflag
&ISTRIP
))
tp
->t_state
|= TS_QUOT
; /* '\' escape */
if (ISSET(tp
->t_state
, TS_ERASE
)) {
CLR(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 (ISSET(tp
->t_state
, TS_TTSTOP
) &&
!ISSET(iflag
, IXANY
) && cc
[VSTART
] != cc
[VSTOP
])
* Output a single character on a tty, doing output processing
* as needed (expanding tabs, newline processing, etc.).
* Returns < 0 if succeeds, otherwise returns char to resend.
if (!(tp
->t_oflag
&OPOST
)) {
if (putc(c
, &tp
->t_outq
))
* Do tab expansion if OXTABS is set. Special case if we 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 happen
ISSET(oflag
, OXTABS
) && !ISSET(tp
->t_lflag
, EXTPROC
)) {
if (!ISSET(tp
->t_lflag
, FLUSHO
)) {
s
= spltty(); /* Don't interrupt tabs. */
c
-= b_to_q(" ", c
, &tp
->t_outq
);
if (c
== CEOT
&& ISSET(oflag
, ONOEOT
))
* Newline translation: if ONLCR is set,
* translate newline into "\r\n".
if ((tp
->t_lflag
&FLUSHO
) == 0 && putc(c
, &tp
->t_outq
))
if (!ISSET(tp
->t_lflag
, FLUSHO
) && putc(c
, &tp
->t_outq
))
* Common code for ioctls on all 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
)
extern struct tty
*constty
; /* Temporary virtual console. */
/* If the ioctl involves modification, hang if in the background. */
#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
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);
&lbolt
, TTOPRI
| PCATCH
, ttybg
, 0))
switch (com
) { /* Process the ioctl. */
case FIOASYNC
: /* set/clear async i/o */
SET(tp
->t_state
, TS_ASYNC
);
CLR(tp
->t_state
, TS_ASYNC
);
case FIONBIO
: /* set/clear non-blocking i/o */
break; /* XXX: delete. */
case FIONREAD
: /* get # bytes to read */
*(int *)data
= ttnread(tp
);
case TIOCEXCL
: /* set exclusive use of tty */
SET(tp
->t_state
, TS_XCLUDE
);
case TIOCFLUSH
: { /* flush buffers */
register int flags
= *(int *)data
;
case TIOCCONS
: /* become virtual console */
if (constty
&& constty
!= tp
&&
ISSET(constty
->t_state
, TS_CARR_ON
| TS_ISOPEN
) ==
(TS_CARR_ON
| TS_ISOPEN
))
if (error
= suser(p
->p_ucred
, &p
->p_acflag
))
} else if (tp
== constty
)
case TIOCDRAIN
: /* wait till output drained */
case TIOCGETA
: { /* get termios struct */
struct termios
*t
= (struct termios
*)data
;
bcopy(&tp
->t_termios
, t
, sizeof(struct termios
));
case TIOCGETD
: /* get line discipline */
*(int *)data
= tp
->t_line
;
case TIOCGWINSZ
: /* get window size */
*(struct winsize
*)data
= tp
->t_winsize
;
case TIOCGPGRP
: /* get pgrp of tty */
*(int *)data
= tp
->t_pgrp
? tp
->t_pgrp
->pg_id
: NO_PID
;
case TIOCHPCL
: /* hang up on last close */
case TIOCNXCL
: /* reset exclusive use of tty */
CLR(tp
->t_state
, TS_XCLUDE
);
case TIOCOUTQ
: /* output queue size */
*(int *)data
= tp
->t_outq
.c_cc
;
case TIOCSETA
: /* set termios struct */
case TIOCSETAW
: /* drain output, set */
case TIOCSETAF
: { /* drn out, fls in, set */
register struct termios
*t
= (struct termios
*)data
;
if (com
== TIOCSETAW
|| com
== TIOCSETAF
) {
if (error
= ttywait(tp
)) {
if (!ISSET(t
->c_cflag
, CIGNORE
)) {
if (tp
->t_param
&& (error
= (*tp
->t_param
)(tp
, t
))) {
if (!ISSET(tp
->t_state
, TS_CARR_ON
) &&
ISSET(tp
->t_cflag
, CLOCAL
) &&
!ISSET(t
->c_cflag
, CLOCAL
)) {
CLR(tp
->t_state
, TS_ISOPEN
);
SET(tp
->t_state
, TS_WOPEN
);
tp
->t_cflag
= t
->c_cflag
;
tp
->t_ispeed
= t
->c_ispeed
;
tp
->t_ospeed
= t
->c_ospeed
;
if (ISSET(t
->c_lflag
, ICANON
) !=
ISSET(tp
->t_lflag
, ICANON
))
if (ISSET(t
->c_lflag
, ICANON
)) {
catq(&tp
->t_rawq
, &tp
->t_canq
);
tp
->t_iflag
= t
->c_iflag
;
tp
->t_oflag
= t
->c_oflag
;
* Make the EXTPROC bit read only.
if (ISSET(tp
->t_lflag
, EXTPROC
))
SET(t
->c_lflag
, EXTPROC
);
CLR(t
->c_lflag
, EXTPROC
);
tp
->t_lflag
= t
->c_lflag
;
bcopy(t
->c_cc
, tp
->t_cc
, sizeof(t
->c_cc
));
case TIOCSETD
: { /* set line discipline */
register int t
= *(int *)data
;
dev_t device
= tp
->t_dev
;
(*linesw
[tp
->t_line
].l_close
)(tp
, flag
);
error
= (*linesw
[t
].l_open
)(device
, tp
);
(void)(*linesw
[tp
->t_line
].l_open
)(device
, tp
);
case TIOCSTART
: /* start output, like ^Q */
if (ISSET(tp
->t_state
, TS_TTSTOP
) ||
ISSET(tp
->t_lflag
, FLUSHO
)) {
CLR(tp
->t_lflag
, FLUSHO
);
CLR(tp
->t_state
, TS_TTSTOP
);
case TIOCSTI
: /* simulate terminal input */
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
);
case TIOCSTOP
: /* stop output, like ^S */
if (!ISSET(tp
->t_state
, TS_TTSTOP
)) {
SET(tp
->t_state
, TS_TTSTOP
);
(*cdevsw
[major(tp
->t_dev
)].d_stop
)(tp
, 0);
case TIOCSCTTY
: /* become controlling tty */
/* 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
;
case TIOCSPGRP
: { /* set pgrp of tty */
register struct pgrp
*pgrp
= pgfind(*(int *)data
);
else if (pgrp
== NULL
|| pgrp
->pg_session
!= p
->p_session
)
case TIOCSWINSZ
: /* set window size */
if (bcmp((caddr_t
)&tp
->t_winsize
, data
,
sizeof (struct winsize
))) {
tp
->t_winsize
= *(struct winsize
*)data
;
pgsignal(tp
->t_pgrp
, SIGWINCH
, 1);
#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
return (ttcompat(tp
, com
, data
, flag
));
tp
= &cdevsw
[major(device
)].d_ttys
[minor(device
)];
if (nread
> 0 || !ISSET(tp
->t_cflag
, CLOCAL
) &&
!ISSET(tp
->t_state
, TS_CARR_ON
))
selrecord(p
, &tp
->t_rsel
);
if (tp
->t_outq
.c_cc
<= tp
->t_lowat
) {
selrecord(p
, &tp
->t_wsel
);
if (ISSET(tp
->t_lflag
, PENDIN
))
if (!ISSET(tp
->t_lflag
, ICANON
))
nread
+= tp
->t_rawq
.c_cc
;
* Wait for output to drain.
while ((tp
->t_outq
.c_cc
|| ISSET(tp
->t_state
, TS_BUSY
)) &&
(ISSET(tp
->t_state
, TS_CARR_ON
) || ISSET(tp
->t_cflag
, CLOCAL
))
SET(tp
->t_state
, TS_ASLEEP
);
&tp
->t_outq
, TTOPRI
| PCATCH
, ttyout
, 0))
* Flush if successfully wait.
if ((error
= ttywait(tp
)) == 0)
* Flush tty read and/or write queues, notifying anyone waiting.
CLR(tp
->t_state
, TS_LOCAL
);
CLR(tp
->t_state
, TS_TTSTOP
);
(*cdevsw
[major(tp
->t_dev
)].d_stop
)(tp
, rw
);
wakeup((caddr_t
)&tp
->t_outq
);
* Copy in the default termios characters.
bcopy(ttydefchars
, tp
->t_cc
, sizeof(ttydefchars
));
* Send stop character on input overflow.
total
= tp
->t_rawq
.c_cc
+ tp
->t_canq
.c_cc
;
if (tp
->t_rawq
.c_cc
> TTYHOG
) {
ttyflush(tp
, FREAD
| FWRITE
);
CLR(tp
->t_state
, TS_TBLOCK
);
* Block further input iff: current input > threshold
* AND input is available to user program.
if (total
>= TTYHOG
/ 2 &&
!ISSET(tp
->t_state
, TS_TBLOCK
) &&
!ISSET(tp
->t_lflag
, ICANON
) || tp
->t_canq
.c_cc
> 0 &&
tp
->t_cc
[VSTOP
] != _POSIX_VDISABLE
) {
if (putc(tp
->t_cc
[VSTOP
], &tp
->t_outq
) == 0) {
SET(tp
->t_state
, TS_TBLOCK
);
CLR(tp
->t_state
, TS_TIMEOUT
);
if (tp
->t_oproc
!= NULL
) /* XXX: Kludge for pty. */
* "close" a line discipline
ttyflush(tp
, FREAD
| FWRITE
);
* 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 (!ISSET(tp
->t_state
, TS_WOPEN
) && ISSET(tp
->t_cflag
, MDMBUF
)) {
* MDMBUF: do flow control according to carrier flag
CLR(tp
->t_state
, TS_TTSTOP
);
} else if (!ISSET(tp
->t_state
, TS_TTSTOP
)) {
SET(tp
->t_state
, TS_TTSTOP
);
(*cdevsw
[major(tp
->t_dev
)].d_stop
)(tp
, 0);
CLR(tp
->t_state
, TS_CARR_ON
);
if (ISSET(tp
->t_state
, TS_ISOPEN
) &&
!ISSET(tp
->t_cflag
, CLOCAL
)) {
if (tp
->t_session
&& tp
->t_session
->s_leader
)
psignal(tp
->t_session
->s_leader
, SIGHUP
);
ttyflush(tp
, FREAD
| FWRITE
);
SET(tp
->t_state
, TS_CARR_ON
);
* Default modem control routine (for other line disciplines).
* Return argument flag, to turn off device on carrier drop.
SET(tp
->t_state
, TS_CARR_ON
);
CLR(tp
->t_state
, TS_CARR_ON
);
if (!ISSET(tp
->t_cflag
, CLOCAL
)) {
if (tp
->t_session
&& tp
->t_session
->s_leader
)
psignal(tp
->t_session
->s_leader
, SIGHUP
);
* Reinput pending characters after state switch
CLR(tp
->t_lflag
, PENDIN
);
SET(tp
->t_state
, TS_TYPEN
);
tp
->t_rawq
.c_cf
= tp
->t_rawq
.c_cl
= 0;
while ((c
= getc(&tq
)) >= 0)
CLR(tp
->t_state
, TS_TYPEN
);
* Process a read call on a tty device.
register struct clist
*qp
;
register long lflag
= tp
->t_lflag
;
register long iflag
= tp
->t_iflag
;
register u_char
*cc
= tp
->t_cc
;
loop
: lflag
= tp
->t_lflag
;
* 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
, &lbolt
, TTIPRI
| PCATCH
, ttybg
, 0))
* If canonical, use the canonical queue,
* else use the raw queue.
qp
= ISSET(lflag
, ICANON
) ? &tp
->t_canq
: &tp
->t_rawq
;
* 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
= ISSET(tp
->t_state
, TS_CARR_ON
) ||
ISSET(tp
->t_cflag
, CLOCAL
);
if (!carrier
&& ISSET(tp
->t_state
, TS_ISOPEN
)) {
error
= ttysleep(tp
, &tp
->t_rawq
, TTIPRI
| PCATCH
,
carrier
? ttyin
: ttopen
, 0);
* Input present, check for input mapping and processing.
while ((c
= getc(qp
)) >= 0) {
if (CCEQ(cc
[VDSUSP
], c
) && ISSET(lflag
, ISIG
)) {
pgsignal(tp
->t_pgrp
, SIGTSTP
, 1);
&lbolt
, TTIPRI
| PCATCH
, ttybg
, 0))
* Interpret EOF only in canonical mode.
if (CCEQ(cc
[VEOF
], c
) && lflag
&ICANON
)
error
= ureadc(iflag
&ISTRIP
? c
& 0177 : c
, uio
);
* 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
&& tp
->t_rawq
.c_cc
< TTYHOG
/5) {
if (cc
[VSTART
] != POSIX_V_DISABLE
&& putc(cc
[VSTART
], &tp
->t_outq
) == 0) {
CLR(tp
->t_state
, TS_TBLOCK
);
* Check the output queue on tp for space for a kernel message (from uprintf
* or 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
oldsig
= wait
? curproc
->p_sig
: 0;
if (tp
->t_outq
.c_cc
> hiwat
+ 200)
while (tp
->t_outq
.c_cc
> hiwat
) {
if (wait
== 0 || curproc
->p_sig
!= oldsig
) {
timeout((void (*)__P((void *)))wakeup
,
(void *)&tp
->t_outq
, hz
);
SET(tp
->t_state
, TS_ASLEEP
);
sleep((caddr_t
)&tp
->t_outq
, PZERO
- 1);
* Process a write call on a tty device.
register struct uio
*uio
;
int i
, hiwat
, cnt
, error
, s
;
if (!ISSET(tp
->t_state
, TS_CARR_ON
) &&
!ISSET(tp
->t_cflag
, CLOCAL
)) {
if (ISSET(tp
->t_state
, TS_ISOPEN
)) {
} else if (flag
& IO_NDELAY
) {
/* Sleep awaiting carrier. */
&tp
->t_rawq
, TTIPRI
| PCATCH
,ttopen
, 0);
* Hang the process if it's in the background.
if (isbackground(p
, tp
) &&
ISSET(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
, &lbolt
, TTIPRI
| PCATCH
, ttybg
, 0))
* 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.
for (cc
= 0; uio
->uio_resid
> 0 || cc
> 0;) {
if (ISSET(tp
->t_lflag
, FLUSHO
)) {
if (tp
->t_outq
.c_cc
> 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 char_type), call ttyoutput. After processing
* a hunk of data, look for FLUSHO so ^O's will take effect
if (!(tp
->t_oflag
&OPOST
))
ce
= cc
- scanc((u_int
)cc
, (u_char
*)cp
,
(u_char
*)char_type
, CCLASSMASK
);
* If ce is zero, then we're processing
* a special character through ttyoutput.
if (ttyoutput(*cp
, tp
) >= 0) {
/* No Clists, wait a bit. */
if (error
= ttysleep(tp
, &lbolt
,
TTOPRI
| PCATCH
, ttybuf
, 0))
if (tp
->t_lflag
&FLUSHO
||
* 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
);
cp
+= ce
, cc
-= ce
, tk_nout
+= ce
;
/* No Clists, wait a bit. */
&lbolt
, TTOPRI
| PCATCH
, ttybuf
, 0))
if (tp
->t_lflag
&FLUSHO
|| tp
->t_outq
.c_cc
> 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 (tp
->t_outq
.c_cc
<= hiwat
) {
return (uio
->uio_resid
== cnt
? EWOULDBLOCK
: 0);
SET(tp
->t_state
, TS_ASLEEP
);
error
= ttysleep(tp
, &tp
->t_outq
, TTOPRI
| PCATCH
, ttyout
, 0);
* Rubout one character from the rawq of tp
* as cleanly as possible.
if ((tp
->t_lflag
&ECHO
) == 0)
if (tp
->t_rocount
== 0) {
* Screwed by ttwrite; retype
/* if tab or newline was escaped - XXX - not 8bit */
if (c
== ('\t' | TTY_QUOTE
) || c
== ('\n' | TTY_QUOTE
))
else switch (CCLASS(c
&= TTY_CHARMASK
)) {
if (tp
->t_rocount
< tp
->t_rawq
.c_cc
) {
tabc
= *cp
; /* XXX FIX NEXTC */
for (; cp
; cp
= nextc(&tp
->t_rawq
, cp
, &tabc
))
CLR(tp
->t_lflag
, FLUSHO
);
CLR(tp
->t_state
, TS_CNTTB
);
/* savecol will now be length of the tab. */
savecol
= 8; /* overflow screw */
(void)ttyoutput('\b', tp
);
#define PANICSTR "ttyrub: would panic c = %d, val = %d\n"
(void)printf(PANICSTR
, c
, CCLASS(c
));
panic(PANICSTR
, c
, CCLASS(c
));
} else if (ISSET(tp
->t_lflag
, ECHOPRT
)) {
if (!ISSET(tp
->t_state
, TS_ERASE
)) {
SET(tp
->t_state
, TS_ERASE
);
(void)ttyoutput('\\', tp
);
ttyecho(tp
->t_cc
[VERASE
], tp
);
* Back over cnt characters, erasing them.
register char *rubostring
= tp
->t_lflag
&ECHOE
? "\b \b" : "\b";
(void)ttyoutput('\b', tp
);
(void)ttyoutput(' ', tp
);
(void)ttyoutput('\b', tp
);
* Reprint the rawq line. Note, it is assumed that c_cc has already
/* Echo the reprint character. */
if (tp
->t_cc
[VREPRINT
] != POSIX_V_DISABLE
)
ttyecho(tp
->t_cc
[VREPRINT
], tp
);
(void)ttyoutput('\n', tp
);
* FIX: NEXTC IS BROKEN - DOESN'T CHECK QUOTE
for (cp
= tp
->t_canq
.c_cf
, c
= (cp
!= NULL
? *cp
: 0);
cp
!= NULL
; cp
= nextc(&tp
->t_canq
, cp
, &c
))
for (cp
= tp
->t_rawq
.c_cf
, c
= (cp
!= NULL
? *cp
: 0);
cp
!= NULL
; cp
= nextc(&tp
->t_rawq
, cp
, &c
))
CLR(tp
->t_state
, TS_ERASE
);
tp
->t_rocount
= tp
->t_rawq
.c_cc
;
* Echo a typed character to the terminal.
if ((tp
->t_lflag
&ECHO
) == 0 && !(tp
->t_lflag
&ECHONL
&& c
== '\n'))
if (ISSET(tp
->t_lflag
, ECHOCTL
) &&
((c
& TTY_CHARMASK
) <= 037 && c
!= '\t' && c
!= '\n' ||
(c
& TTY_CHARMASK
) == 0177)) {
(void)ttyoutput('^', tp
);
* Wake up any readers on a tty
.
if (ISSET(tp
->t_state
, TS_ASYNC
))
pgsignal(tp
->t_pgrp
, SIGIO
, 1);
wakeup((caddr_t
)&tp
->t_rawq
);
* 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
#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
.ldavg
[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");
calcru(pick
, &utime
, &stime
, NULL
);
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",
pick
->p_stat
== SIDL
|| pick
->p_stat
== SZOMB
? 0 :
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.
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
))
return (tp
->t_gen
== gen
? 0 : ERESTART
);