* Copyright (c) 1982, 1986, 1989 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
* @(#)tty_pty.c 7.21 (Berkeley) 5/30/91
* PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
* -------------------- ----- ----------------------
* CURRENT PATCH LEVEL: 5 00094
* -------------------- ----- ----------------------
* 11 Dec 92 Williams Jolitz Fixed tty handling
* 28 Nov 1991 Warren Toomey Cleaned up the use of COMPAT_43
* 6 Oct 1992 Holger Veit Fixed 'hanging console' bug
* 11 Jan 93 Julian Elischer Fixes multiple processes on one
* 27 Feb 93 Charles Hannum Proper return values for ptsclose()
static char rcsid
[] = "$Header: /a/cvs/386BSD/src/sys/kern/tty_pty.c,v 1.2 1993/08/15 06:11:22 alm Exp $";
* (Actually two drivers, requiring two entries in 'cdevsw')
#define NPTY 32 /* crude XXX */
#define BUFSIZ 100 /* Chunk size iomoved to/from user */
int npty
= NPTY
; /* for pstat -t */
#define PF_PKT 0x08 /* packet mode */
#define PF_STOPPED 0x10 /* user told stopped */
#define PF_REMOTE 0x20 /* remote and flow controlled input */
#define PF_UCNTL 0x80 /* user control mode */
ptsopen(dev
, flag
, devtype
, p
)
tp
= &pt_tty
[minor(dev
)];
if ((tp
->t_state
& TS_ISOPEN
) == 0) {
ttychars(tp
); /* Set up default chars */
tp
->t_iflag
= TTYDEF_IFLAG
;
tp
->t_oflag
= TTYDEF_OFLAG
;
tp
->t_lflag
= TTYDEF_LFLAG
;
tp
->t_cflag
= TTYDEF_CFLAG
;
tp
->t_ispeed
= tp
->t_ospeed
= TTYDEF_SPEED
;
ttsetwater(tp
); /* would be done in xxparam() */
} else if (tp
->t_state
&TS_XCLUDE
&& p
->p_ucred
->cr_uid
!= 0)
if (tp
->t_oproc
) /* Ctrlr still around. */
tp
->t_state
|= TS_CARR_ON
;
while ((tp
->t_state
& TS_CARR_ON
) == 0) {
if (error
= ttysleep(tp
, (caddr_t
)&tp
->t_raw
, TTIPRI
| PCATCH
,
error
= (*linesw
[tp
->t_line
].l_open
)(dev
, tp
, flag
);
ptcwakeup(tp
, FREAD
|FWRITE
);
ptsclose(dev
, flag
, mode
, p
)
tp
= &pt_tty
[minor(dev
)];
(*linesw
[tp
->t_line
].l_close
)(tp
, flag
);
ptcwakeup(tp
, FREAD
|FWRITE
);
struct proc
*p
= curproc
;
register struct tty
*tp
= &pt_tty
[minor(dev
)];
register struct pt_ioctl
*pti
= &pt_ioctl
[minor(dev
)];
if (pti
->pt_flags
& PF_REMOTE
) {
while (isbackground(p
, tp
)) {
if ((p
->p_sigignore
& sigmask(SIGTTIN
)) ||
(p
->p_sigmask
& sigmask(SIGTTIN
)) ||
p
->p_pgrp
->pg_jobc
== 0 ||
pgsignal(p
->p_pgrp
, SIGTTIN
, 1);
if (error
= ttysleep(tp
, (caddr_t
)&lbolt
,
TTIPRI
| PCATCH
, ttybg
, 0))
if (RB_LEN(&tp
->t_can
) == 0) {
if (error
= ttysleep(tp
, (caddr_t
)&tp
->t_can
,
TTIPRI
| PCATCH
, ttyin
, 0))
while (RB_LEN(&tp
->t_can
) > 1 && uio
->uio_resid
> 0)
if (ureadc(getc(&tp
->t_can
), uio
) < 0) {
if (RB_LEN(&tp
->t_can
) == 1)
error
= (*linesw
[tp
->t_line
].l_read
)(tp
, uio
, flag
);
* Wakeups of controlling tty will happen
* indirectly, when tty driver calls ptsstart.
tp
= &pt_tty
[minor(dev
)];
return ((*linesw
[tp
->t_line
].l_write
)(tp
, uio
, flag
));
* Start output on pseudo-tty.
* Wake up process selecting or sleeping for input from controlling tty.
register struct pt_ioctl
*pti
= &pt_ioctl
[minor(tp
->t_dev
)];
if (tp
->t_state
& TS_TTSTOP
)
if (pti
->pt_flags
& PF_STOPPED
) {
pti
->pt_flags
&= ~PF_STOPPED
;
pti
->pt_send
= TIOCPKT_START
;
struct pt_ioctl
*pti
= &pt_ioctl
[minor(tp
->t_dev
)];
selwakeup(pti
->pt_selr
, pti
->pt_flags
& PF_RCOLL
);
pti
->pt_flags
&= ~PF_RCOLL
;
wakeup((caddr_t
)&tp
->t_out
.rb_tl
);
selwakeup(pti
->pt_selw
, pti
->pt_flags
& PF_WCOLL
);
pti
->pt_flags
&= ~PF_WCOLL
;
wakeup((caddr_t
)&tp
->t_raw
.rb_hd
);
ptcopen(dev_t dev
, int flag
, int devtype
, struct proc
*p
)
ptcopen(dev
, flag
, devtype
, p
)
tp
= &pt_tty
[minor(dev
)];
(void)(*linesw
[tp
->t_line
].l_modem
)(tp
, 1);
pti
= &pt_ioctl
[minor(dev
)];
extern struct tty
*constty
; /* -hv- 06.Oct.92*/
tp
= &pt_tty
[minor(dev
)];
(void)(*linesw
[tp
->t_line
].l_modem
)(tp
, 0);
tp
->t_state
&= ~TS_CARR_ON
;
tp
->t_oproc
= 0; /* mark closed */
/* XXX -hv- 6.Oct.92 this prevents the "hanging console bug" with X11 */
register struct tty
*tp
= &pt_tty
[minor(dev
)];
struct pt_ioctl
*pti
= &pt_ioctl
[minor(dev
)];
* We want to block until the slave
* is open, and there's something to read;
* but if we lost the slave or we're NBIO,
* then return the appropriate error instead.
if (tp
->t_state
&TS_ISOPEN
) {
if (pti
->pt_flags
&PF_PKT
&& pti
->pt_send
) {
error
= ureadc((int)pti
->pt_send
, uio
);
if (pti
->pt_send
& TIOCPKT_IOCTL
) {
uiomove(&tp
->t_termios
, cc
, uio
);
if (pti
->pt_flags
&PF_UCNTL
&& pti
->pt_ucntl
) {
error
= ureadc((int)pti
->pt_ucntl
, uio
);
if (RB_LEN(&tp
->t_out
) && (tp
->t_state
&TS_TTSTOP
) == 0)
if ((tp
->t_state
&TS_CARR_ON
) == 0)
if (error
= tsleep((caddr_t
)&tp
->t_out
.rb_tl
, TTIPRI
| PCATCH
,
if (pti
->pt_flags
& (PF_PKT
|PF_UCNTL
))
while (uio
->uio_resid
> 0 && error
== 0) {
cc
= q_to_b(&tp
->t_outq
, buf
, MIN(uio
->uio_resid
, BUFSIZ
));
cc
= min(MIN(uio
->uio_resid
, BUFSIZ
), RB_CONTIGGET(&tp
->t_out
));
bcopy(tp
->t_out
.rb_hd
, buf
, cc
);
RB_ROLLOVER(&tp
->t_out
, tp
->t_out
.rb_hd
+cc
);
error
= uiomove(buf
, cc
, uio
);
if (RB_LEN(&tp
->t_out
) <= tp
->t_lowat
) {
if (tp
->t_state
&TS_ASLEEP
) {
tp
->t_state
&= ~TS_ASLEEP
;
wakeup((caddr_t
)&tp
->t_out
);
selwakeup(tp
->t_wsel
, tp
->t_state
& TS_WCOLL
);
tp
->t_state
&= ~TS_WCOLL
;
struct pt_ioctl
*pti
= &pt_ioctl
[minor(tp
->t_dev
)];
/* note: FLUSHREAD and FLUSHWRITE already ok */
pti
->pt_flags
|= PF_STOPPED
;
pti
->pt_flags
&= ~PF_STOPPED
;
/* change of perspective */
register struct tty
*tp
= &pt_tty
[minor(dev
)];
struct pt_ioctl
*pti
= &pt_ioctl
[minor(dev
)];
if ((tp
->t_state
&TS_CARR_ON
) == 0)
* Need to block timeouts (ttrstart).
if ((tp
->t_state
&TS_ISOPEN
) &&
RB_LEN(&tp
->t_out
) && (tp
->t_state
&TS_TTSTOP
) == 0) {
case 0: /* exceptional */
if ((tp
->t_state
&TS_ISOPEN
) &&
(pti
->pt_flags
&PF_PKT
&& pti
->pt_send
||
pti
->pt_flags
&PF_UCNTL
&& pti
->pt_ucntl
))
if (pti
->pt_selr
&& (prev
= pfind(pti
->pt_selr
)) && prev
->p_wchan
== (caddr_t
)&selwait
)
pti
->pt_flags
|= PF_RCOLL
;
if (tp
->t_state
&TS_ISOPEN
) {
if (pti
->pt_flags
& PF_REMOTE
) {
if (RB_LEN(&tp
->t_can
) == 0)
if (RB_LEN(&tp
->t_raw
) + RB_LEN(&tp
->t_can
) < TTYHOG
-2)
if (RB_LEN(&tp
->t_can
) == 0 && (tp
->t_iflag
&ICANON
))
if (pti
->pt_selw
&& (prev
= pfind(pti
->pt_selw
)) && prev
->p_wchan
== (caddr_t
)&selwait
)
pti
->pt_flags
|= PF_WCOLL
;
register struct uio
*uio
;
register struct tty
*tp
= &pt_tty
[minor(dev
)];
struct pt_ioctl
*pti
= &pt_ioctl
[minor(dev
)];
if ((tp
->t_state
&TS_ISOPEN
) == 0)
if (pti
->pt_flags
& PF_REMOTE
) {
while (uio
->uio_resid
> 0 && RB_LEN(&tp
->t_can
) < TTYHOG
- 1) {
cc
= min(uio
->uio_resid
, BUFSIZ
);
cc
= min(cc
, RB_CONTIGPUT(&tp
->t_can
));
error
= uiomove((caddr_t
)cp
, cc
, uio
);
/* check again for safety */
if ((tp
->t_state
&TS_ISOPEN
) == 0)
(void) b_to_q((char *)cp
, cc
, &tp
->t_canq
);
bcopy(cp
, tp
->t_can
.rb_tl
, cc
);
RB_ROLLOVER(&tp
->t_can
, tp
->t_can
.rb_tl
+cc
);
(void) putc(0, &tp
->t_can
);
wakeup((caddr_t
)&tp
->t_can
);
while (uio
->uio_resid
> 0) {
cc
= min(uio
->uio_resid
, BUFSIZ
);
error
= uiomove((caddr_t
)cp
, cc
, uio
);
/* check again for safety */
if ((tp
->t_state
&TS_ISOPEN
) == 0)
if ((RB_LEN(&tp
->t_raw
) + RB_LEN(&tp
->t_can
)) >= TTYHOG
- 2 &&
(RB_LEN(&tp
->t_can
) > 0 || !(tp
->t_iflag
&ICANON
))) {
wakeup((caddr_t
)&tp
->t_raw
);
(*linesw
[tp
->t_line
].l_rint
)(*cp
++, tp
);
* Come here to wait for slave to open, for space
* in outq, or space in rawq.
if ((tp
->t_state
&TS_CARR_ON
) == 0)
/* adjust for data copied in but not written */
if (error
= tsleep((caddr_t
)&tp
->t_raw
.rb_hd
, TTOPRI
| PCATCH
,
/* adjust for data copied in but not written */
ptyioctl(dev
, cmd
, data
, flag
)
register struct tty
*tp
= &pt_tty
[minor(dev
)];
register struct pt_ioctl
*pti
= &pt_ioctl
[minor(dev
)];
register u_char
*cc
= tp
->t_cc
;
* IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG.
* ttywflush(tp) will hang if there are characters in the outq.
* When the EXTPROC bit is being toggled, we need
* to send an TIOCPKT_IOCTL if the packet driver
if (pti
->pt_flags
& PF_PKT
) {
pti
->pt_send
|= TIOCPKT_IOCTL
;
if ((tp
->t_state
& EXTPROC
) &&
(pti
->pt_flags
& PF_PKT
)) {
pti
->pt_send
|= TIOCPKT_IOCTL
;
if (cdevsw
[major(dev
)].d_open
== ptcopen
)
* We aviod calling ttioctl on the controller since,
* in that case, tp must be the controlling terminal.
*(int *)data
= tp
->t_pgrp
? tp
->t_pgrp
->pg_id
: 0;
if (pti
->pt_flags
& PF_UCNTL
)
pti
->pt_flags
&= ~PF_PKT
;
if (pti
->pt_flags
& PF_PKT
)
pti
->pt_flags
|= PF_UCNTL
;
pti
->pt_flags
&= ~PF_UCNTL
;
pti
->pt_flags
|= PF_REMOTE
;
pti
->pt_flags
&= ~PF_REMOTE
;
ttyflush(tp
, FREAD
|FWRITE
);
while (getc(&tp
->t_out
) >= 0)
if (*(unsigned int *)data
>= NSIG
)
if ((tp
->t_lflag
&NOFLSH
) == 0)
ttyflush(tp
, FREAD
|FWRITE
);
pgsignal(tp
->t_pgrp
, *(unsigned int *)data
, 1);
if ((*(unsigned int *)data
== SIGINFO
) &&
((tp
->t_lflag
&NOKERNINFO
) == 0))
error
= (*linesw
[tp
->t_line
].l_ioctl
)(tp
, cmd
, data
, flag
);
error
= ttioctl(tp
, cmd
, data
, flag
);
* Since we use the tty queues internally,
* pty's can't be switched to disciplines which overwrite
* the queues. We can't tell anything about the discipline
if (linesw
[tp
->t_line
].l_rint
!= ttyinput
) {
(*linesw
[tp
->t_line
].l_close
)(tp
, flag
);
(void)(*linesw
[tp
->t_line
].l_open
)(dev
, tp
, flag
);
if (pti
->pt_flags
& PF_UCNTL
&&
(cmd
& ~0xff) == UIOCCMD(0)) {
pti
->pt_ucntl
= (u_char
)cmd
;
* If external processing and packet mode send ioctl packet.
if ((tp
->t_lflag
&EXTPROC
) && (pti
->pt_flags
& PF_PKT
)) {
pti
->pt_send
|= TIOCPKT_IOCTL
;
stop
= (tp
->t_iflag
& IXON
) && CCEQ(cc
[VSTOP
], CTRL('s'))
&& CCEQ(cc
[VSTART
], CTRL('q'));
if (pti
->pt_flags
& PF_NOSTOP
) {
pti
->pt_send
&= ~TIOCPKT_NOSTOP
;
pti
->pt_send
|= TIOCPKT_DOSTOP
;
pti
->pt_flags
&= ~PF_NOSTOP
;
pti
->pt_send
&= ~TIOCPKT_DOSTOP
;
pti
->pt_send
|= TIOCPKT_NOSTOP
;
pti
->pt_flags
|= PF_NOSTOP
;