#include <sys/param.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/proc.h>
#include <sys/caps.h>
#include <sys/tty.h>
#include <sys/ttydefaults.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <sys/kernel.h>
#include <sys/vnode.h>
#include <sys/signalvar.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/devfs.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
MALLOC_DEFINE(M_PTY, "ptys", "pty data structures");
static void ptsstart (struct tty *tp);
static void ptsstop (struct tty *tp, int rw);
static void ptsunhold (struct tty *tp);
static void ptcwakeup (struct tty *tp, int flag);
static int filt_ptcread (struct knote *kn, long hint);
static void filt_ptcrdetach (struct knote *kn);
static int filt_ptcwrite (struct knote *kn, long hint);
static void filt_ptcwdetach (struct knote *kn);
static d_open_t ptsopen;
static d_close_t ptsclose;
static d_read_t ptsread;
static d_write_t ptswrite;
static d_ioctl_t ptyioctl;
static d_open_t ptcopen;
static d_close_t ptcclose;
static d_read_t ptcread;
static d_write_t ptcwrite;
static d_kqfilter_t ptckqfilter;
DEVFS_DEFINE_CLONE_BITMAP(pty);
static struct pt_ioctl **ptis;
static d_clone_t ptyclone;
static int pty_debug_level = 0;
static struct dev_ops pts98_ops = {
{ "pts98", 0, D_TTY | D_MPSAFE },
.d_open = ptsopen,
.d_close = ptsclose,
.d_read = ptsread,
.d_write = ptswrite,
.d_ioctl = ptyioctl,
.d_kqfilter = ttykqfilter,
.d_revoke = ttyrevoke
};
static struct dev_ops ptc98_ops = {
{ "ptc98", 0, D_TTY | D_MASTER | D_MPSAFE },
.d_open = ptcopen,
.d_close = ptcclose,
.d_read = ptcread,
.d_write = ptcwrite,
.d_ioctl = ptyioctl,
.d_kqfilter = ptckqfilter,
.d_revoke = ttyrevoke
};
static struct dev_ops ptc_ops = {
{ "ptc", 0, D_TTY | D_MASTER | D_MPSAFE },
.d_open = ptcopen,
.d_close = ptcclose,
.d_read = ptcread,
.d_write = ptcwrite,
.d_ioctl = ptyioctl,
.d_kqfilter = ptckqfilter,
.d_revoke = ttyrevoke
};
#define BUFSIZ 100
#define MAXPTYS 1000
struct pt_ioctl {
int pt_flags;
int pt_refs;
int pt_uminor;
struct kqinfo pt_kqr, pt_kqw;
u_char pt_send;
u_char pt_ucntl;
struct tty pt_tty;
cdev_t devs, devc;
struct prison *pt_prison;
};
#define PF_PKT 0x0008
#define PF_STOPPED 0x0010
#define PF_REMOTE 0x0020
#define PF_NOSTOP 0x0040
#define PF_UCNTL 0x0080
#define PF_PTCSTATEMASK 0x00FF
#define PF_UNIX98 0x0100
#define PF_SOPEN 0x0200
#define PF_MOPEN 0x0400
#define PF_SCLOSED 0x0800
#define PF_TERMINATED 0x8000
static int
ptyclone(struct dev_clone_args *ap)
{
int unit;
struct pt_ioctl *pti;
unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(pty), MAXPTYS);
if (unit < 0) {
ap->a_dev = NULL;
return 1;
}
if ((pti = ptis[unit]) == NULL) {
lwkt_gettoken(&tty_token);
pti = kmalloc(sizeof(*pti), M_PTY, M_WAITOK | M_ZERO);
if (ptis[unit] == NULL) {
ptis[unit] = pti;
ttyinit(&pti->pt_tty);
} else {
kfree(pti, M_PTY);
}
lwkt_reltoken(&tty_token);
}
pti->devc = make_only_dev(&ptc98_ops, unit,
ap->a_cred->cr_ruid,
0, 0600, "ptm/%d", unit);
pti->devs = make_dev(&pts98_ops, unit,
ap->a_cred->cr_ruid,
GID_TTY, 0620, "pts/%d", unit);
ap->a_dev = pti->devc;
pti->devs->si_flags |= SI_OVERRIDE;
pti->devc->si_flags |= SI_OVERRIDE;
pti->pt_tty.t_dev = pti->devs;
pti->pt_flags = PF_UNIX98;
pti->pt_uminor = unit;
pti->devs->si_drv1 = pti->devc->si_drv1 = pti;
pti->devs->si_tty = pti->devc->si_tty = &pti->pt_tty;
ttyregister(&pti->pt_tty);
return 0;
}
static int
pti_hold(struct pt_ioctl *pti)
{
if (pti->pt_flags & PF_TERMINATED)
return(ENXIO);
++pti->pt_refs;
return(0);
}
static void
pti_done(struct pt_ioctl *pti)
{
lwkt_gettoken(&pti->pt_tty.t_token);
if (--pti->pt_refs == 0) {
cdev_t dev;
int uminor_no;
if ((pti->pt_flags & PF_UNIX98) == 0) {
lwkt_reltoken(&pti->pt_tty.t_token);
return;
}
if ((pti->pt_flags & (PF_SOPEN|PF_MOPEN)) == 0 &&
pti->pt_tty.t_refs == 0) {
pti->pt_flags |= PF_TERMINATED;
uminor_no = pti->pt_uminor;
if ((dev = pti->devs) != NULL) {
dev->si_drv1 = NULL;
pti->devs = NULL;
destroy_dev(dev);
}
if ((dev = pti->devc) != NULL) {
dev->si_drv1 = NULL;
pti->devc = NULL;
destroy_dev(dev);
}
ttyunregister(&pti->pt_tty);
pti->pt_tty.t_dev = NULL;
devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(pty),
uminor_no);
}
}
lwkt_reltoken(&pti->pt_tty.t_token);
}
static int
ptsopen(struct dev_open_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct tty *tp;
int error;
struct pt_ioctl *pti;
if (dev->si_drv1 == NULL)
return(ENXIO);
pti = dev->si_drv1;
lwkt_gettoken(&pti->pt_tty.t_token);
if (pti_hold(pti)) {
lwkt_reltoken(&pti->pt_tty.t_token);
return(ENXIO);
}
tp = dev->si_tty;
if ((tp->t_state & TS_ISOPEN) == 0) {
ttychars(tp);
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;
} else if ((tp->t_state & TS_XCLUDE) &&
caps_priv_check(ap->a_cred, SYSCAP_RESTRICTEDROOT))
{
pti_done(pti);
lwkt_reltoken(&pti->pt_tty.t_token);
return (EBUSY);
} else if (pti->pt_prison != ap->a_cred->cr_prison) {
pti_done(pti);
lwkt_reltoken(&pti->pt_tty.t_token);
return (EBUSY);
}
if (tp->t_oproc)
(void)(*linesw[tp->t_line].l_modem)(tp, 1);
else if ((pti->pt_flags & PF_SOPEN) == 0)
tp->t_state &= ~TS_ZOMBIE;
while ((tp->t_state & TS_CARR_ON) == 0) {
if (ap->a_oflags & FNONBLOCK)
break;
error = ttysleep(tp, TSA_CARR_ON(tp), PCATCH, "ptsopn", 0);
if (error) {
pti_done(pti);
lwkt_reltoken(&pti->pt_tty.t_token);
return (error);
}
}
error = (*linesw[tp->t_line].l_open)(dev, tp);
if (error == 0) {
pti->pt_flags |= PF_SOPEN;
pti->pt_flags &= ~PF_SCLOSED;
ptcwakeup(tp, FREAD|FWRITE);
}
pti_done(pti);
lwkt_reltoken(&pti->pt_tty.t_token);
return (error);
}
static int
ptsclose(struct dev_close_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct tty *tp;
struct pt_ioctl *pti = dev->si_drv1;
int err;
lwkt_gettoken(&pti->pt_tty.t_token);
if (pti_hold(pti))
panic("ptsclose on terminated pti");
tp = dev->si_tty;
err = (*linesw[tp->t_line].l_close)(tp, ap->a_fflag);
ptsstop(tp, FREAD|FWRITE);
ttyclose(tp);
pti->pt_flags &= ~PF_SOPEN;
pti->pt_flags |= PF_SCLOSED;
if (tp->t_oproc)
ptcwakeup(tp, FREAD);
pti_done(pti);
lwkt_reltoken(&pti->pt_tty.t_token);
return (err);
}
static int
ptsread(struct dev_read_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct proc *p = curproc;
struct tty *tp = dev->si_tty;
struct pt_ioctl *pti = dev->si_drv1;
struct lwp *lp;
int error = 0;
lp = curthread->td_lwp;
lwkt_gettoken(&pti->pt_tty.t_token);
again:
if (pti->pt_flags & PF_REMOTE) {
while (isbackground(p, tp)) {
if (SIGISMEMBER(p->p_sigignore, SIGTTIN) ||
SIGISMEMBER(lp->lwp_sigmask, SIGTTIN) ||
p->p_pgrp->pg_jobc == 0 ||
(p->p_flags & P_PPWAIT)) {
lwkt_reltoken(&pti->pt_tty.t_token);
return (EIO);
}
pgsignal(p->p_pgrp, SIGTTIN, 1);
error = ttysleep(tp, &lbolt, PCATCH, "ptsbg", 0);
if (error) {
lwkt_reltoken(&pti->pt_tty.t_token);
return (error);
}
}
if (tp->t_canq.c_cc == 0) {
if (ap->a_ioflag & IO_NDELAY) {
lwkt_reltoken(&pti->pt_tty.t_token);
return (EWOULDBLOCK);
}
error = ttysleep(tp, TSA_PTS_READ(tp), PCATCH,
"ptsin", 0);
if (error) {
lwkt_reltoken(&pti->pt_tty.t_token);
return (error);
}
goto again;
}
while (tp->t_canq.c_cc > 1 && ap->a_uio->uio_resid > 0)
if (ureadc(clist_getc(&tp->t_canq), ap->a_uio) < 0) {
error = EFAULT;
break;
}
if (tp->t_canq.c_cc == 1)
clist_getc(&tp->t_canq);
if (tp->t_canq.c_cc) {
lwkt_reltoken(&pti->pt_tty.t_token);
return (error);
}
} else
if (tp->t_oproc)
error = (*linesw[tp->t_line].l_read)(tp, ap->a_uio, ap->a_ioflag);
ptcwakeup(tp, FWRITE);
lwkt_reltoken(&pti->pt_tty.t_token);
return (error);
}
static int
ptswrite(struct dev_write_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct tty *tp;
int ret;
tp = dev->si_tty;
lwkt_gettoken(&tp->t_token);
if (tp->t_oproc == NULL) {
lwkt_reltoken(&tp->t_token);
return (EIO);
}
ret = ((*linesw[tp->t_line].l_write)(tp, ap->a_uio, ap->a_ioflag));
lwkt_reltoken(&tp->t_token);
return ret;
}
static void
ptsstart(struct tty *tp)
{
struct pt_ioctl *pti = tp->t_dev->si_drv1;
lwkt_gettoken(&pti->pt_tty.t_token);
lwkt_gettoken(&tp->t_token);
if (tp->t_state & TS_TTSTOP) {
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return;
}
if (pti) {
if (pti->pt_flags & PF_STOPPED) {
pti->pt_flags &= ~PF_STOPPED;
pti->pt_send = TIOCPKT_START;
}
}
ptcwakeup(tp, FREAD);
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
}
static void
ptcwakeup(struct tty *tp, int flag)
{
if (flag & FREAD) {
wakeup(TSA_PTC_READ(tp));
KNOTE(&tp->t_rkq.ki_note, 0);
}
if (flag & FWRITE) {
wakeup(TSA_PTC_WRITE(tp));
KNOTE(&tp->t_wkq.ki_note, 0);
}
}
static int
ptcopen(struct dev_open_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct tty *tp;
struct pt_ioctl *pti;
pti = dev->si_drv1;
if (pti == NULL)
return(ENXIO);
lwkt_gettoken(&pti->pt_tty.t_token);
if (pti_hold(pti)) {
lwkt_reltoken(&pti->pt_tty.t_token);
return(ENXIO);
}
if (pti->pt_prison && pti->pt_prison != ap->a_cred->cr_prison) {
pti_done(pti);
lwkt_reltoken(&pti->pt_tty.t_token);
return(EBUSY);
}
tp = dev->si_tty;
lwkt_gettoken(&tp->t_token);
if (tp->t_oproc) {
pti_done(pti);
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (EIO);
}
if ((pti->pt_flags & PF_SOPEN) == 0)
tp->t_state &= ~TS_ZOMBIE;
tp->t_oproc = ptsstart;
tp->t_stop = ptsstop;
tp->t_unhold = ptsunhold;
(void)(*linesw[tp->t_line].l_modem)(tp, 1);
tp->t_lflag &= ~EXTPROC;
pti->pt_prison = ap->a_cred->cr_prison;
pti->pt_flags &= ~PF_PTCSTATEMASK;
pti->pt_send = 0;
pti->pt_ucntl = 0;
pti->devs->si_uid = ap->a_cred->cr_uid;
pti->devs->si_gid = ap->a_cred->cr_uid ? GID_TTY : 0;
pti->devs->si_perms = 0600;
pti->devc->si_uid = ap->a_cred->cr_uid;
pti->devc->si_gid = 0;
pti->devc->si_perms = 0600;
pti->pt_flags |= PF_MOPEN;
pti_done(pti);
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (0);
}
static int
ptcclose(struct dev_close_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct tty *tp;
struct pt_ioctl *pti = dev->si_drv1;
lwkt_gettoken(&pti->pt_tty.t_token);
if (pti_hold(pti)) {
lwkt_reltoken(&pti->pt_tty.t_token);
panic("ptcclose on terminated pti");
}
tp = dev->si_tty;
lwkt_gettoken(&tp->t_token);
(void)(*linesw[tp->t_line].l_modem)(tp, 0);
pti->pt_flags &= ~PF_MOPEN;
if (pti->pt_flags & PF_SOPEN)
tp->t_state |= TS_ZOMBIE;
if (tp->t_state & TS_ISOPEN) {
tp->t_state &= ~(TS_CARR_ON | TS_CONNECTED);
ttyflush(tp, FREAD | FWRITE);
}
tp->t_oproc = NULL;
pti->pt_prison = NULL;
pti->devs->si_uid = 0;
pti->devs->si_gid = 0;
pti->devs->si_perms = 0666;
pti->devc->si_uid = 0;
pti->devc->si_gid = 0;
pti->devc->si_perms = 0666;
pti_done(pti);
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (0);
}
static int
ptcread(struct dev_read_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct tty *tp = dev->si_tty;
struct pt_ioctl *pti = dev->si_drv1;
char buf[BUFSIZ];
int error = 0, cc;
lwkt_gettoken(&pti->pt_tty.t_token);
lwkt_gettoken(&tp->t_token);
for (;;) {
if (tp->t_state&TS_ISOPEN) {
if ((pti->pt_flags & PF_PKT) && pti->pt_send) {
error = ureadc((int)pti->pt_send, ap->a_uio);
if (error) {
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (error);
}
if (pti->pt_send & TIOCPKT_IOCTL) {
cc = (int)szmin(ap->a_uio->uio_resid,
sizeof(tp->t_termios));
uiomove((caddr_t)&tp->t_termios, cc,
ap->a_uio);
}
pti->pt_send = 0;
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (0);
}
if ((pti->pt_flags & PF_UCNTL) && pti->pt_ucntl) {
error = ureadc((int)pti->pt_ucntl, ap->a_uio);
if (error) {
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (error);
}
pti->pt_ucntl = 0;
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (0);
}
if (tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0)
break;
}
if ((tp->t_state & TS_CONNECTED) == 0) {
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (0);
}
if (ap->a_ioflag & IO_NDELAY) {
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (EWOULDBLOCK);
}
error = tsleep(TSA_PTC_READ(tp), PCATCH, "ptcin", 0);
if (error) {
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (error);
}
}
if (pti->pt_flags & (PF_PKT|PF_UCNTL))
error = ureadc(0, ap->a_uio);
while (ap->a_uio->uio_resid > 0 && error == 0) {
cc = clist_qtob(&tp->t_outq, buf,
(int)szmin(ap->a_uio->uio_resid, BUFSIZ));
if (cc <= 0)
break;
error = uiomove(buf, (size_t)cc, ap->a_uio);
}
ttwwakeup(tp);
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (error);
}
static void
ptsstop(struct tty *tp, int flush)
{
struct pt_ioctl *pti = tp->t_dev->si_drv1;
int flag;
lwkt_gettoken(&pti->pt_tty.t_token);
if (pti) {
if (flush == 0) {
flush = TIOCPKT_STOP;
pti->pt_flags |= PF_STOPPED;
} else {
pti->pt_flags &= ~PF_STOPPED;
}
pti->pt_send |= flush;
}
flag = 0;
if (flush & FREAD)
flag |= FWRITE;
if (flush & FWRITE)
flag |= FREAD;
ptcwakeup(tp, flag);
lwkt_reltoken(&pti->pt_tty.t_token);
}
static void
ptsunhold(struct tty *tp)
{
struct pt_ioctl *pti = tp->t_dev->si_drv1;
lwkt_gettoken(&pti->pt_tty.t_token);
lwkt_gettoken(&tp->t_token);
pti_hold(pti);
--tp->t_refs;
pti_done(pti);
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
}
static struct filterops ptcread_filtops =
{ FILTEROP_ISFD|FILTEROP_MPSAFE, NULL, filt_ptcrdetach, filt_ptcread };
static struct filterops ptcwrite_filtops =
{ FILTEROP_ISFD|FILTEROP_MPSAFE, NULL, filt_ptcwdetach, filt_ptcwrite };
static int
ptckqfilter(struct dev_kqfilter_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct knote *kn = ap->a_kn;
struct tty *tp = dev->si_tty;
struct klist *klist;
ap->a_result = 0;
switch (kn->kn_filter) {
case EVFILT_READ:
klist = &tp->t_rkq.ki_note;
kn->kn_fop = &ptcread_filtops;
break;
case EVFILT_WRITE:
klist = &tp->t_wkq.ki_note;
kn->kn_fop = &ptcwrite_filtops;
break;
default:
ap->a_result = EOPNOTSUPP;
return (0);
}
kn->kn_hook = (caddr_t)dev;
knote_insert(klist, kn);
return (0);
}
static int
filt_ptcread (struct knote *kn, long hint)
{
struct tty *tp = ((cdev_t)kn->kn_hook)->si_tty;
struct pt_ioctl *pti = ((cdev_t)kn->kn_hook)->si_drv1;
lwkt_gettoken(&pti->pt_tty.t_token);
lwkt_gettoken(&tp->t_token);
if ((tp->t_state & TS_ZOMBIE) || (pti->pt_flags & PF_SCLOSED)) {
kn->kn_flags |= (EV_EOF | EV_NODATA);
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (1);
}
if ((tp->t_state & TS_ISOPEN) &&
((tp->t_outq.c_cc && (tp->t_state & TS_TTSTOP) == 0) ||
((pti->pt_flags & PF_PKT) && pti->pt_send) ||
((pti->pt_flags & PF_UCNTL) && pti->pt_ucntl))) {
kn->kn_data = tp->t_outq.c_cc;
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return(1);
} else {
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return(0);
}
}
static int
filt_ptcwrite (struct knote *kn, long hint)
{
struct tty *tp = ((cdev_t)kn->kn_hook)->si_tty;
struct pt_ioctl *pti = ((cdev_t)kn->kn_hook)->si_drv1;
lwkt_gettoken(&pti->pt_tty.t_token);
lwkt_gettoken(&tp->t_token);
if (tp->t_state & TS_ZOMBIE) {
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
kn->kn_flags |= (EV_EOF | EV_NODATA);
return (1);
}
if (tp->t_state & TS_ISOPEN &&
((pti->pt_flags & PF_REMOTE) ?
(tp->t_canq.c_cc == 0) :
((tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG - 2) ||
(tp->t_canq.c_cc == 0 && (tp->t_lflag & ICANON))))) {
kn->kn_data = tp->t_canq.c_cc + tp->t_rawq.c_cc;
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return(1);
} else {
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return(0);
}
}
static void
filt_ptcrdetach (struct knote *kn)
{
struct tty *tp = ((cdev_t)kn->kn_hook)->si_tty;
knote_remove(&tp->t_rkq.ki_note, kn);
}
static void
filt_ptcwdetach (struct knote *kn)
{
struct tty *tp = ((cdev_t)kn->kn_hook)->si_tty;
knote_remove(&tp->t_wkq.ki_note, kn);
}
static int
ptcwrite(struct dev_write_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct tty *tp = dev->si_tty;
u_char *cp = NULL;
int cc = 0;
u_char locbuf[BUFSIZ];
int cnt = 0;
struct pt_ioctl *pti = dev->si_drv1;
int error = 0;
lwkt_gettoken(&pti->pt_tty.t_token);
lwkt_gettoken(&tp->t_token);
again:
if ((tp->t_state&TS_ISOPEN) == 0)
goto block;
if (pti->pt_flags & PF_REMOTE) {
if (tp->t_canq.c_cc)
goto block;
while ((ap->a_uio->uio_resid > 0 || cc > 0) &&
tp->t_canq.c_cc < TTYHOG - 1) {
if (cc == 0) {
cc = (int)szmin(ap->a_uio->uio_resid, BUFSIZ);
cc = imin(cc, TTYHOG - 1 - tp->t_canq.c_cc);
cp = locbuf;
error = uiomove(cp, (size_t)cc, ap->a_uio);
if (error) {
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (error);
}
if ((tp->t_state & TS_ISOPEN) == 0) {
ap->a_uio->uio_resid += cc;
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (EIO);
}
}
if (cc > 0) {
cc = clist_btoq((char *)cp, cc, &tp->t_canq);
if (cc > 0)
break;
}
}
ap->a_uio->uio_resid += cc;
clist_putc(0, &tp->t_canq);
ttwakeup(tp);
wakeup(TSA_PTS_READ(tp));
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (0);
}
while (ap->a_uio->uio_resid > 0 || cc > 0) {
if (cc == 0) {
cc = (int)szmin(ap->a_uio->uio_resid, BUFSIZ);
cp = locbuf;
error = uiomove(cp, (size_t)cc, ap->a_uio);
if (error) {
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (error);
}
if ((tp->t_state & TS_ISOPEN) == 0) {
ap->a_uio->uio_resid += cc;
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (EIO);
}
}
while (cc > 0) {
if ((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= TTYHOG - 2 &&
(tp->t_canq.c_cc > 0 || !(tp->t_lflag&ICANON))) {
wakeup(TSA_HUP_OR_INPUT(tp));
goto block;
}
(*linesw[tp->t_line].l_rint)(*cp++, tp);
cnt++;
cc--;
}
cc = 0;
}
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (0);
block:
if ((tp->t_state & TS_CONNECTED) == 0) {
ap->a_uio->uio_resid += cc;
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (EIO);
}
if (ap->a_ioflag & IO_NDELAY) {
ap->a_uio->uio_resid += cc;
if (cnt == 0) {
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (EWOULDBLOCK);
}
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (0);
}
error = tsleep(TSA_PTC_WRITE(tp), PCATCH, "ptcout", 0);
if (error) {
ap->a_uio->uio_resid += cc;
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (error);
}
goto again;
}
static int
ptyioctl(struct dev_ioctl_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct tty *tp = dev->si_tty;
struct pt_ioctl *pti = dev->si_drv1;
u_char *cc = tp->t_cc;
int stop, error;
lwkt_gettoken(&pti->pt_tty.t_token);
lwkt_gettoken(&tp->t_token);
if (dev_dflags(dev) & D_MASTER) {
switch (ap->a_cmd) {
case TIOCGPGRP:
*(int *)ap->a_data = tp->t_pgrp ? tp->t_pgrp->pg_id : 0;
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (0);
case TIOCPKT:
if (*(int *)ap->a_data) {
if (pti->pt_flags & PF_UCNTL) {
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (EINVAL);
}
pti->pt_flags |= PF_PKT;
} else {
pti->pt_flags &= ~PF_PKT;
}
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (0);
case TIOCUCNTL:
if (*(int *)ap->a_data) {
if (pti->pt_flags & PF_PKT) {
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (EINVAL);
}
pti->pt_flags |= PF_UCNTL;
} else {
pti->pt_flags &= ~PF_UCNTL;
}
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (0);
case TIOCREMOTE:
if (*(int *)ap->a_data)
pti->pt_flags |= PF_REMOTE;
else
pti->pt_flags &= ~PF_REMOTE;
ttyflush(tp, FREAD|FWRITE);
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (0);
case TIOCISPTMASTER:
if ((pti->pt_flags & PF_UNIX98) &&
(pti->devc == dev)) {
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (0);
} else {
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (EINVAL);
}
}
if ((tp->t_state & TS_ISOPEN) == 0) {
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (EAGAIN);
}
switch (ap->a_cmd) {
case TIOCSETD:
case TIOCSETA:
case TIOCSETAW:
case TIOCSETAF:
ndflush(&tp->t_outq, tp->t_outq.c_cc);
break;
case TIOCSIG:
if (*(unsigned int *)ap->a_data >= NSIG ||
*(unsigned int *)ap->a_data == 0) {
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return(EINVAL);
}
if ((tp->t_lflag&NOFLSH) == 0)
ttyflush(tp, FREAD|FWRITE);
pgsignal(tp->t_pgrp, *(unsigned int *)ap->a_data, 1);
if ((*(unsigned int *)ap->a_data == SIGINFO) &&
((tp->t_lflag&NOKERNINFO) == 0))
ttyinfo(tp);
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return(0);
}
}
if (ap->a_cmd == TIOCEXT) {
if (*(int *)ap->a_data) {
if (pti->pt_flags & PF_PKT) {
pti->pt_send |= TIOCPKT_IOCTL;
ptcwakeup(tp, FREAD);
}
tp->t_lflag |= EXTPROC;
} else {
if ((tp->t_lflag & EXTPROC) &&
(pti->pt_flags & PF_PKT)) {
pti->pt_send |= TIOCPKT_IOCTL;
ptcwakeup(tp, FREAD);
}
tp->t_lflag &= ~EXTPROC;
}
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return(0);
}
error = (*linesw[tp->t_line].l_ioctl)(tp, ap->a_cmd, ap->a_data,
ap->a_fflag, ap->a_cred);
if (error == ENOIOCTL)
error = ttioctl(tp, ap->a_cmd, ap->a_data, ap->a_fflag);
if (error == ENOIOCTL) {
if (pti->pt_flags & PF_UCNTL &&
(ap->a_cmd & ~0xff) == UIOCCMD(0)) {
if (ap->a_cmd & 0xff) {
pti->pt_ucntl = (u_char)ap->a_cmd;
ptcwakeup(tp, FREAD);
}
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (0);
}
error = ENOTTY;
}
if ((tp->t_lflag&EXTPROC) && (pti->pt_flags & PF_PKT)) {
switch(ap->a_cmd) {
case TIOCSETA:
case TIOCSETAW:
case TIOCSETAF:
pti->pt_send |= TIOCPKT_IOCTL;
ptcwakeup(tp, FREAD);
default:
break;
}
}
stop = (tp->t_iflag & IXON) && CCEQ(cc[VSTOP], CTRL('s'))
&& CCEQ(cc[VSTART], CTRL('q'));
if (pti->pt_flags & PF_NOSTOP) {
if (stop) {
pti->pt_send &= ~TIOCPKT_NOSTOP;
pti->pt_send |= TIOCPKT_DOSTOP;
pti->pt_flags &= ~PF_NOSTOP;
ptcwakeup(tp, FREAD);
}
} else {
if (!stop) {
pti->pt_send &= ~TIOCPKT_DOSTOP;
pti->pt_send |= TIOCPKT_NOSTOP;
pti->pt_flags |= PF_NOSTOP;
ptcwakeup(tp, FREAD);
}
}
lwkt_reltoken(&tp->t_token);
lwkt_reltoken(&pti->pt_tty.t_token);
return (error);
}
static void ptc_drvinit (void *unused);
SYSCTL_INT(_kern, OID_AUTO, pty_debug, CTLFLAG_RW, &pty_debug_level,
0, "Change pty debug level");
static void
ptc_drvinit(void *unused)
{
make_autoclone_dev(&ptc_ops, &DEVFS_CLONE_BITMAP(pty), ptyclone,
0, 0, 0666, "ptmx");
ptis = kmalloc(sizeof(struct pt_ioctl *) * MAXPTYS, M_PTY,
M_WAITOK | M_ZERO);
}
SYSINIT(ptcdev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, ptc_drvinit, NULL);