version 1.4, 2004/11/30 04:25:44 |
version 1.4.12.8, 2008/03/24 09:39:02 |
Line 52 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 52 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <sys/vnode.h> |
#include <sys/vnode.h> |
#include <sys/namei.h> |
#include <sys/namei.h> |
#include <sys/signalvar.h> |
#include <sys/signalvar.h> |
#include <sys/uio.h> |
|
#include <sys/filedesc.h> |
#include <sys/filedesc.h> |
#include <sys/conf.h> |
#include <sys/conf.h> |
#include <sys/poll.h> |
#include <sys/poll.h> |
#include <sys/malloc.h> |
#include <sys/malloc.h> |
#include <sys/pty.h> |
#include <sys/pty.h> |
|
#include <sys/kauth.h> |
|
|
|
#include <miscfs/specfs/specdev.h> |
|
|
#ifdef DEBUG_PTM |
#ifdef DEBUG_PTM |
#define DPRINTF(a) printf a |
#define DPRINTF(a) printf a |
Line 76 static struct ptm_pty *ptm; |
|
Line 78 static struct ptm_pty *ptm; |
|
int pts_major, ptc_major; |
int pts_major, ptc_major; |
|
|
static dev_t pty_getfree(void); |
static dev_t pty_getfree(void); |
static int pty_alloc_master(struct proc *, int *, dev_t *); |
static int pty_alloc_master(struct lwp *, int *, dev_t *); |
static int pty_alloc_slave(struct proc *, int *, dev_t); |
static int pty_alloc_slave(struct lwp *, int *, dev_t); |
|
|
void ptmattach(int); |
void ptmattach(int); |
|
|
dev_type_open(ptmopen); |
|
dev_type_close(ptmclose); |
|
dev_type_ioctl(ptmioctl); |
|
|
|
const struct cdevsw ptm_cdevsw = { |
|
ptmopen, ptmclose, noread, nowrite, ptmioctl, |
|
nullstop, notty, nopoll, nommap, nokqfilter, D_TTY |
|
}; |
|
|
|
dev_t |
dev_t |
pty_makedev(char ms, int minor) |
pty_makedev(char ms, int minor) |
{ |
{ |
return makedev(ms == 't' ? pts_major : ptc_major, minor); |
return makedev(ms == 't' ? pts_major : ptc_major, minor); |
} |
} |
|
|
|
|
static dev_t |
static dev_t |
pty_getfree(void) |
pty_getfree(void) |
{ |
{ |
extern struct simplelock pt_softc_mutex; |
extern kmutex_t pt_softc_mutex; |
int i; |
int i; |
|
|
simple_lock(&pt_softc_mutex); |
mutex_enter(&pt_softc_mutex); |
for (i = 0; i < npty; i++) { |
for (i = 0; i < npty; i++) { |
if (pty_isfree(i, 0)) |
if (pty_isfree(i, 0)) |
break; |
break; |
} |
} |
simple_unlock(&pt_softc_mutex); |
mutex_exit(&pt_softc_mutex); |
return pty_makedev('t', i); |
return pty_makedev('t', i); |
} |
} |
|
|
Line 118 pty_getfree(void) |
|
Line 112 pty_getfree(void) |
|
* We need it because we have to fake up root credentials to open the pty. |
* We need it because we have to fake up root credentials to open the pty. |
*/ |
*/ |
int |
int |
pty_vn_open(struct vnode *vp, struct proc *p) |
pty_vn_open(struct vnode *vp, struct lwp *l) |
{ |
{ |
struct ucred *cred; |
|
int error; |
int error; |
|
|
if (vp->v_type != VCHR) { |
if (vp->v_type != VCHR) { |
Line 128 pty_vn_open(struct vnode *vp, struct pro |
|
Line 121 pty_vn_open(struct vnode *vp, struct pro |
|
return EINVAL; |
return EINVAL; |
} |
} |
|
|
/* |
error = VOP_OPEN(vp, FREAD|FWRITE, lwp0.l_cred); |
* Get us a fresh cred with root privileges. |
|
*/ |
|
cred = crget(); |
|
error = VOP_OPEN(vp, FREAD|FWRITE, cred, p); |
|
crfree(cred); |
|
|
|
if (error) { |
if (error) { |
vput(vp); |
vput(vp); |
Line 146 pty_vn_open(struct vnode *vp, struct pro |
|
Line 134 pty_vn_open(struct vnode *vp, struct pro |
|
} |
} |
|
|
static int |
static int |
pty_alloc_master(struct proc *p, int *fd, dev_t *dev) |
pty_alloc_master(struct lwp *l, int *fd, dev_t *dev) |
{ |
{ |
int error; |
int error; |
struct file *fp; |
struct file *fp; |
struct vnode *vp; |
struct vnode *vp; |
int md; |
int md; |
|
|
if ((error = falloc(p, &fp, fd)) != 0) { |
if ((error = fd_allocfile(&fp, fd)) != 0) { |
DPRINTF(("falloc %d\n", error)); |
DPRINTF(("fd_allocfile %d\n", error)); |
return error; |
return error; |
} |
} |
retry: |
retry: |
|
|
goto bad; |
goto bad; |
} |
} |
if (ptm == NULL) { |
if (ptm == NULL) { |
|
DPRINTF(("no ptm\n")); |
error = EOPNOTSUPP; |
error = EOPNOTSUPP; |
goto bad; |
goto bad; |
} |
} |
if ((error = (*ptm->allocvp)(ptm, p, &vp, *dev, 'p')) != 0) |
if ((error = (*ptm->allocvp)(ptm, l, &vp, *dev, 'p')) != 0) { |
|
DPRINTF(("pty_allocvp %d\n", error)); |
goto bad; |
goto bad; |
|
} |
|
|
if ((error = pty_vn_open(vp, p)) != 0) { |
if ((error = pty_vn_open(vp, l)) != 0) { |
|
DPRINTF(("pty_vn_open %d\n", error)); |
/* |
/* |
* Check if the master open failed because we lost |
* Check if the master open failed because we lost |
* the race to grab it. |
* the race to grab it. |
|
|
if (error != EIO) |
if (error != EIO) |
goto bad; |
goto bad; |
error = !pty_isfree(md, 1); |
error = !pty_isfree(md, 1); |
|
DPRINTF(("pty_isfree %d\n", error)); |
if (error) |
if (error) |
goto retry; |
goto retry; |
else |
else |
|
|
fp->f_ops = &vnops; |
fp->f_ops = &vnops; |
fp->f_data = vp; |
fp->f_data = vp; |
VOP_UNLOCK(vp, 0); |
VOP_UNLOCK(vp, 0); |
FILE_SET_MATURE(fp); |
fd_affix(curproc, fp, *fd); |
FILE_UNUSE(fp, p); |
|
return 0; |
return 0; |
bad: |
bad: |
FILE_UNUSE(fp, p); |
fd_abort(curproc, fp, *fd); |
fdremove(p->p_fd, *fd); |
|
ffree(fp); |
|
return error; |
return error; |
} |
} |
|
|
int |
int |
pty_grant_slave(struct proc *p, dev_t dev) |
pty_grant_slave(struct lwp *l, dev_t dev) |
{ |
{ |
int error; |
int error; |
struct vnode *vp; |
struct vnode *vp; |
Line 217 pty_grant_slave(struct proc *p, dev_t de |
|
Line 207 pty_grant_slave(struct proc *p, dev_t de |
|
*/ |
*/ |
if (ptm == NULL) |
if (ptm == NULL) |
return EOPNOTSUPP; |
return EOPNOTSUPP; |
|
if ((error = (*ptm->allocvp)(ptm, l, &vp, dev, 't')) != 0) |
if ((error = (*ptm->allocvp)(ptm, p, &vp, dev, 't')) != 0) |
|
return error; |
return error; |
|
|
if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { |
if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { |
struct vattr vattr; |
struct vattr vattr; |
struct ucred *cred; |
(*ptm->getvattr)(ptm, l, &vattr); |
(*ptm->getvattr)(ptm, p, &vattr); |
/* Do the VOP_SETATTR() as root. */ |
/* Get a fake cred to pretend we're root. */ |
error = VOP_SETATTR(vp, &vattr, lwp0.l_cred); |
cred = crget(); |
|
error = VOP_SETATTR(vp, &vattr, cred, p); |
|
crfree(cred); |
|
if (error) { |
if (error) { |
DPRINTF(("setattr %d\n", error)); |
DPRINTF(("setattr %d\n", error)); |
VOP_UNLOCK(vp, 0); |
VOP_UNLOCK(vp, 0); |
Line 237 pty_grant_slave(struct proc *p, dev_t de |
|
Line 223 pty_grant_slave(struct proc *p, dev_t de |
|
} |
} |
} |
} |
VOP_UNLOCK(vp, 0); |
VOP_UNLOCK(vp, 0); |
if (vp->v_usecount > 1 || |
VOP_REVOKE(vp, REVOKEALL); |
(vp->v_flag & (VALIASED | VLAYER))) |
|
VOP_REVOKE(vp, REVOKEALL); |
|
|
|
/* |
/* |
* The vnode is useless after the revoke, we need to get it again. |
* The vnode is useless after the revoke, we need to get it again. |
Line 249 pty_grant_slave(struct proc *p, dev_t de |
|
Line 233 pty_grant_slave(struct proc *p, dev_t de |
|
} |
} |
|
|
static int |
static int |
pty_alloc_slave(struct proc *p, int *fd, dev_t dev) |
pty_alloc_slave(struct lwp *l, int *fd, dev_t dev) |
{ |
{ |
int error; |
int error; |
struct file *fp; |
struct file *fp; |
struct vnode *vp; |
struct vnode *vp; |
|
|
/* Grab a filedescriptor for the slave */ |
/* Grab a filedescriptor for the slave */ |
if ((error = falloc(p, &fp, fd)) != 0) { |
if ((error = fd_allocfile(&fp, fd)) != 0) { |
DPRINTF(("falloc %d\n", error)); |
DPRINTF(("fd_allocfile %d\n", error)); |
return error; |
return error; |
} |
} |
|
|
Line 266 pty_alloc_slave(struct proc *p, int *fd, |
|
Line 250 pty_alloc_slave(struct proc *p, int *fd, |
|
goto bad; |
goto bad; |
} |
} |
|
|
if ((error = (*ptm->allocvp)(ptm, p, &vp, dev, 't')) != 0) |
if ((error = (*ptm->allocvp)(ptm, l, &vp, dev, 't')) != 0) |
goto bad; |
goto bad; |
if ((error = pty_vn_open(vp, p)) != 0) |
if ((error = pty_vn_open(vp, l)) != 0) |
goto bad; |
goto bad; |
|
|
fp->f_flag = FREAD|FWRITE; |
fp->f_flag = FREAD|FWRITE; |
Line 276 pty_alloc_slave(struct proc *p, int *fd, |
|
Line 260 pty_alloc_slave(struct proc *p, int *fd, |
|
fp->f_ops = &vnops; |
fp->f_ops = &vnops; |
fp->f_data = vp; |
fp->f_data = vp; |
VOP_UNLOCK(vp, 0); |
VOP_UNLOCK(vp, 0); |
FILE_SET_MATURE(fp); |
fd_affix(curproc, fp, *fd); |
FILE_UNUSE(fp, p); |
|
return 0; |
return 0; |
bad: |
bad: |
FILE_UNUSE(fp, p); |
fd_abort(curproc, fp, *fd); |
fdremove(p->p_fd, *fd); |
|
ffree(fp); |
|
return error; |
return error; |
} |
} |
|
|
Line 295 pty_sethandler(struct ptm_pty *nptm) |
|
Line 276 pty_sethandler(struct ptm_pty *nptm) |
|
} |
} |
|
|
int |
int |
pty_fill_ptmget(dev_t dev, int cfd, int sfd, void *data) |
pty_fill_ptmget(struct lwp *l, dev_t dev, int cfd, int sfd, void *data) |
{ |
{ |
struct ptmget *ptmg = data; |
struct ptmget *ptmg = data; |
int error; |
int error; |
Line 306 pty_fill_ptmget(dev_t dev, int cfd, int |
|
Line 287 pty_fill_ptmget(dev_t dev, int cfd, int |
|
ptmg->cfd = cfd == -1 ? minor(dev) : cfd; |
ptmg->cfd = cfd == -1 ? minor(dev) : cfd; |
ptmg->sfd = sfd == -1 ? minor(dev) : sfd; |
ptmg->sfd = sfd == -1 ? minor(dev) : sfd; |
|
|
error = (*ptm->makename)(ptm, ptmg->cn, sizeof(ptmg->cn), dev, 'p'); |
error = (*ptm->makename)(ptm, l, ptmg->cn, sizeof(ptmg->cn), dev, 'p'); |
if (error) |
if (error) |
return error; |
return error; |
|
|
return (*ptm->makename)(ptm, ptmg->sn, sizeof(ptmg->sn), dev, 't'); |
return (*ptm->makename)(ptm, l, ptmg->sn, sizeof(ptmg->sn), dev, 't'); |
} |
} |
|
|
void |
void |
Line 328 ptmattach(int n) |
|
Line 309 ptmattach(int n) |
|
#endif |
#endif |
} |
} |
|
|
int |
static int |
/*ARGSUSED*/ |
/*ARGSUSED*/ |
ptmopen(dev_t dev, int flag, int mode, struct proc *p) |
ptmopen(dev_t dev, int flag, int mode, struct lwp *l) |
{ |
{ |
int error; |
int error; |
int fd; |
int fd; |
|
dev_t ttydev; |
|
|
switch(minor(dev)) { |
switch(minor(dev)) { |
case 0: /* /dev/ptmx */ |
case 0: /* /dev/ptmx */ |
if ((error = pty_alloc_master(p, &fd, &dev)) != 0) |
case 2: /* /emul/linux/dev/ptmx */ |
|
if ((error = pty_alloc_master(l, &fd, &ttydev)) != 0) |
return error; |
return error; |
|
if (minor(dev) == 2) { |
|
/* |
|
* Linux ptyfs grants the pty right here. |
|
* Handle this case here, instead of writing |
|
* a new linux module. |
|
*/ |
|
if ((error = pty_grant_slave(l, ttydev)) != 0) { |
|
file_t *fp = fd_getfile(fd); |
|
if (fp != NULL) { |
|
fd_close(fd); |
|
} |
|
return error; |
|
} |
|
} |
curlwp->l_dupfd = fd; |
curlwp->l_dupfd = fd; |
return EMOVEFD; |
return EMOVEFD; |
case 1: /* /dev/ptm */ |
case 1: /* /dev/ptm */ |
Line 348 ptmopen(dev_t dev, int flag, int mode, s |
|
Line 345 ptmopen(dev_t dev, int flag, int mode, s |
|
} |
} |
} |
} |
|
|
int |
static int |
/*ARGSUSED*/ |
/*ARGSUSED*/ |
ptmclose(dev_t dev, int flag, int mode, struct proc *p) |
ptmclose(dev_t dev, int flag, int mode, struct lwp *l) |
{ |
{ |
|
|
return (0); |
return (0); |
} |
} |
|
|
int |
static int |
/*ARGSUSED*/ |
/*ARGSUSED*/ |
ptmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) |
ptmioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) |
{ |
{ |
int error; |
int error; |
dev_t newdev; |
dev_t newdev; |
int cfd, sfd; |
int cfd, sfd; |
struct file *fp; |
file_t *fp; |
|
|
error = 0; |
error = 0; |
switch (cmd) { |
switch (cmd) { |
case TIOCPTMGET: |
case TIOCPTMGET: |
if ((error = pty_alloc_master(p, &cfd, &newdev)) != 0) |
if ((error = pty_alloc_master(l, &cfd, &newdev)) != 0) |
return error; |
return error; |
|
|
if ((error = pty_grant_slave(p, newdev)) != 0) |
if ((error = pty_grant_slave(l, newdev)) != 0) |
goto bad; |
goto bad; |
|
|
if ((error = pty_alloc_slave(p, &sfd, newdev)) != 0) |
if ((error = pty_alloc_slave(l, &sfd, newdev)) != 0) |
goto bad; |
goto bad; |
|
|
/* now, put the indices and names into struct ptmget */ |
/* now, put the indices and names into struct ptmget */ |
return pty_fill_ptmget(newdev, cfd, sfd, data); |
return pty_fill_ptmget(l, newdev, cfd, sfd, data); |
default: |
default: |
DPRINTF(("ptmioctl EINVAL\n")); |
DPRINTF(("ptmioctl EINVAL\n")); |
return EINVAL; |
return EINVAL; |
} |
} |
bad: |
bad: |
fp = fd_getfile(p->p_fd, cfd); |
fp = fd_getfile(cfd); |
fdremove(p->p_fd, cfd); |
if (fp != NULL) { |
ffree(fp); |
fd_close(cfd); |
|
} |
return error; |
return error; |
} |
} |
|
|
|
const struct cdevsw ptm_cdevsw = { |
|
ptmopen, ptmclose, noread, nowrite, ptmioctl, |
|
nullstop, notty, nopoll, nommap, nokqfilter, D_TTY |
|
}; |
#endif |
#endif |