1 /* $NetBSD: tty_ptm.c,v 1.25 2008/04/28 20:24:05 martin Exp $ */
4 * Copyright (c) 2004 The NetBSD Foundation, Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: tty_ptm.c,v 1.25 2008/04/28 20:24:05 martin Exp $");
34 /* pty multiplexor driver /dev/ptm{,x} */
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/ioctl.h>
44 #include <sys/kernel.h>
45 #include <sys/vnode.h>
46 #include <sys/namei.h>
47 #include <sys/signalvar.h>
48 #include <sys/filedesc.h>
52 #include <sys/kauth.h>
54 #include <miscfs/specfs/specdev.h>
57 #define DPRINTF(a) printf a
63 const struct cdevsw ptm_cdevsw
= {
64 noopen
, noclose
, noread
, nowrite
, noioctl
,
65 nostop
, notty
, nopoll
, nommap
, nokqfilter
, D_TTY
69 static struct ptm_pty
*ptm
;
70 int pts_major
, ptc_major
;
72 static dev_t
pty_getfree(void);
73 static int pty_alloc_master(struct lwp
*, int *, dev_t
*);
74 static int pty_alloc_slave(struct lwp
*, int *, dev_t
);
79 pty_makedev(char ms
, int minor
)
81 return makedev(ms
== 't' ? pts_major
: ptc_major
, minor
);
88 extern kmutex_t pt_softc_mutex
;
91 mutex_enter(&pt_softc_mutex
);
92 for (i
= 0; i
< npty
; i
++) {
96 mutex_exit(&pt_softc_mutex
);
97 return pty_makedev('t', i
);
101 * Hacked up version of vn_open. We _only_ handle ptys and only open
102 * them with FREAD|FWRITE and never deal with creat or stuff like that.
104 * We need it because we have to fake up root credentials to open the pty.
107 pty_vn_open(struct vnode
*vp
, struct lwp
*l
)
111 if (vp
->v_type
!= VCHR
) {
116 error
= VOP_OPEN(vp
, FREAD
|FWRITE
, lwp0
.l_cred
);
129 pty_alloc_master(struct lwp
*l
, int *fd
, dev_t
*dev
)
136 if ((error
= fd_allocfile(&fp
, fd
)) != 0) {
137 DPRINTF(("fd_allocfile %d\n", error
));
141 /* Find and open a free master pty. */
142 *dev
= pty_getfree();
144 if ((error
= pty_check(md
)) != 0) {
145 DPRINTF(("pty_check %d\n", error
));
149 DPRINTF(("no ptm\n"));
153 if ((error
= (*ptm
->allocvp
)(ptm
, l
, &vp
, *dev
, 'p')) != 0) {
154 DPRINTF(("pty_allocvp %d\n", error
));
158 if ((error
= pty_vn_open(vp
, l
)) != 0) {
159 DPRINTF(("pty_vn_open %d\n", error
));
161 * Check if the master open failed because we lost
162 * the race to grab it.
166 error
= !pty_isfree(md
, 1);
167 DPRINTF(("pty_isfree %d\n", error
));
173 fp
->f_flag
= FREAD
|FWRITE
;
174 fp
->f_type
= DTYPE_VNODE
;
178 fd_affix(curproc
, fp
, *fd
);
181 fd_abort(curproc
, fp
, *fd
);
186 pty_grant_slave(struct lwp
*l
, dev_t dev
)
193 * namei -> setattr -> unlock -> revoke -> vrele ->
194 * namei -> open -> unlock
195 * Three stage rocket:
196 * 1. Change the owner and permissions on the slave.
197 * 2. Revoke all the users of the slave.
202 if ((error
= (*ptm
->allocvp
)(ptm
, l
, &vp
, dev
, 't')) != 0)
205 if ((vp
->v_mount
->mnt_flag
& MNT_RDONLY
) == 0) {
207 (*ptm
->getvattr
)(ptm
, l
, &vattr
);
208 /* Do the VOP_SETATTR() as root. */
209 error
= VOP_SETATTR(vp
, &vattr
, lwp0
.l_cred
);
211 DPRINTF(("setattr %d\n", error
));
218 VOP_REVOKE(vp
, REVOKEALL
);
221 * The vnode is useless after the revoke, we need to get it again.
228 pty_alloc_slave(struct lwp
*l
, int *fd
, dev_t dev
)
234 /* Grab a filedescriptor for the slave */
235 if ((error
= fd_allocfile(&fp
, fd
)) != 0) {
236 DPRINTF(("fd_allocfile %d\n", error
));
245 if ((error
= (*ptm
->allocvp
)(ptm
, l
, &vp
, dev
, 't')) != 0)
247 if ((error
= pty_vn_open(vp
, l
)) != 0)
250 fp
->f_flag
= FREAD
|FWRITE
;
251 fp
->f_type
= DTYPE_VNODE
;
255 fd_affix(curproc
, fp
, *fd
);
258 fd_abort(curproc
, fp
, *fd
);
263 pty_sethandler(struct ptm_pty
*nptm
)
265 struct ptm_pty
*optm
= ptm
;
271 pty_fill_ptmget(struct lwp
*l
, dev_t dev
, int cfd
, int sfd
, void *data
)
273 struct ptmget
*ptmg
= data
;
279 ptmg
->cfd
= cfd
== -1 ? minor(dev
) : cfd
;
280 ptmg
->sfd
= sfd
== -1 ? minor(dev
) : sfd
;
282 error
= (*ptm
->makename
)(ptm
, l
, ptmg
->cn
, sizeof(ptmg
->cn
), dev
, 'p');
286 return (*ptm
->makename
)(ptm
, l
, ptmg
->sn
, sizeof(ptmg
->sn
), dev
, 't');
293 extern const struct cdevsw pts_cdevsw
, ptc_cdevsw
;
294 /* find the major and minor of the pty devices */
295 if ((pts_major
= cdevsw_lookup_major(&pts_cdevsw
)) == -1)
296 panic("ptmattach: Can't find pty slave in cdevsw");
297 if ((ptc_major
= cdevsw_lookup_major(&ptc_cdevsw
)) == -1)
298 panic("ptmattach: Can't find pty master in cdevsw");
306 ptmopen(dev_t dev
, int flag
, int mode
, struct lwp
*l
)
313 case 0: /* /dev/ptmx */
314 case 2: /* /emul/linux/dev/ptmx */
315 if ((error
= pty_alloc_master(l
, &fd
, &ttydev
)) != 0)
317 if (minor(dev
) == 2) {
319 * Linux ptyfs grants the pty right here.
320 * Handle this case here, instead of writing
321 * a new linux module.
323 if ((error
= pty_grant_slave(l
, ttydev
)) != 0) {
324 file_t
*fp
= fd_getfile(fd
);
331 curlwp
->l_dupfd
= fd
;
333 case 1: /* /dev/ptm */
342 ptmclose(dev_t dev
, int flag
, int mode
, struct lwp
*l
)
350 ptmioctl(dev_t dev
, u_long cmd
, void *data
, int flag
, struct lwp
*l
)
360 if ((error
= pty_alloc_master(l
, &cfd
, &newdev
)) != 0)
363 if ((error
= pty_grant_slave(l
, newdev
)) != 0)
366 if ((error
= pty_alloc_slave(l
, &sfd
, newdev
)) != 0)
369 /* now, put the indices and names into struct ptmget */
370 return pty_fill_ptmget(l
, newdev
, cfd
, sfd
, data
);
372 DPRINTF(("ptmioctl EINVAL\n"));
376 fp
= fd_getfile(cfd
);
383 const struct cdevsw ptm_cdevsw
= {
384 ptmopen
, ptmclose
, noread
, nowrite
, ptmioctl
,
385 nullstop
, notty
, nopoll
, nommap
, nokqfilter
, D_TTY