tty_ptm.c revision 1.12 1 /* $NetBSD: tty_ptm.c,v 1.12 2006/09/22 15:15:56 christos Exp $ */
2
3 /*-
4 * Copyright (c) 2004 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the NetBSD
18 * Foundation, Inc. and its contributors.
19 * 4. Neither the name of The NetBSD Foundation nor the names of its
20 * contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: tty_ptm.c,v 1.12 2006/09/22 15:15:56 christos Exp $");
38
39 #include "opt_ptm.h"
40
41 /* pty multiplexor driver /dev/ptm{,x} */
42
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/ioctl.h>
46 #include <sys/proc.h>
47 #include <sys/tty.h>
48 #include <sys/stat.h>
49 #include <sys/file.h>
50 #include <sys/uio.h>
51 #include <sys/kernel.h>
52 #include <sys/vnode.h>
53 #include <sys/namei.h>
54 #include <sys/signalvar.h>
55 #include <sys/uio.h>
56 #include <sys/filedesc.h>
57 #include <sys/conf.h>
58 #include <sys/poll.h>
59 #include <sys/malloc.h>
60 #include <sys/pty.h>
61 #include <sys/kauth.h>
62
63 #ifdef DEBUG_PTM
64 #define DPRINTF(a) printf a
65 #else
66 #define DPRINTF(a)
67 #endif
68
69 #ifdef NO_DEV_PTM
70 const struct cdevsw ptm_cdevsw = {
71 noopen, noclose, noread, nowrite, noioctl,
72 nostop, notty, nopoll, nommap, nokqfilter, D_TTY
73 };
74 #else
75
76 static struct ptm_pty *ptm;
77 int pts_major, ptc_major;
78
79 static dev_t pty_getfree(void);
80 static int pty_alloc_master(struct lwp *, int *, dev_t *);
81 static int pty_alloc_slave(struct lwp *, int *, dev_t);
82
83 void ptmattach(int);
84
85 dev_t
86 pty_makedev(char ms, int minor)
87 {
88 return makedev(ms == 't' ? pts_major : ptc_major, minor);
89 }
90
91
92 static dev_t
93 pty_getfree(void)
94 {
95 extern struct simplelock pt_softc_mutex;
96 int i;
97
98 simple_lock(&pt_softc_mutex);
99 for (i = 0; i < npty; i++) {
100 if (pty_isfree(i, 0))
101 break;
102 }
103 simple_unlock(&pt_softc_mutex);
104 return pty_makedev('t', i);
105 }
106
107 /*
108 * Hacked up version of vn_open. We _only_ handle ptys and only open
109 * them with FREAD|FWRITE and never deal with creat or stuff like that.
110 *
111 * We need it because we have to fake up root credentials to open the pty.
112 */
113 int
114 pty_vn_open(struct vnode *vp, struct lwp *l)
115 {
116 int error;
117
118 if (vp->v_type != VCHR) {
119 vput(vp);
120 return EINVAL;
121 }
122
123 error = VOP_OPEN(vp, FREAD|FWRITE, lwp0.l_cred, l);
124
125 if (error) {
126 vput(vp);
127 return error;
128 }
129
130 vp->v_writecount++;
131
132 return 0;
133 }
134
135 static int
136 pty_alloc_master(struct lwp *l, int *fd, dev_t *dev)
137 {
138 int error;
139 struct file *fp;
140 struct vnode *vp;
141 int md;
142
143 if ((error = falloc(l, &fp, fd)) != 0) {
144 DPRINTF(("falloc %d\n", error));
145 return error;
146 }
147 retry:
148 /* Find and open a free master pty. */
149 *dev = pty_getfree();
150 md = minor(*dev);
151 if ((error = pty_check(md)) != 0) {
152 DPRINTF(("pty_check %d\n", error));
153 goto bad;
154 }
155 if (ptm == NULL) {
156 error = EOPNOTSUPP;
157 goto bad;
158 }
159 if ((error = (*ptm->allocvp)(ptm, l, &vp, *dev, 'p')) != 0)
160 goto bad;
161
162 if ((error = pty_vn_open(vp, l)) != 0) {
163 /*
164 * Check if the master open failed because we lost
165 * the race to grab it.
166 */
167 if (error != EIO)
168 goto bad;
169 error = !pty_isfree(md, 1);
170 if (error)
171 goto retry;
172 else
173 goto bad;
174 }
175 fp->f_flag = FREAD|FWRITE;
176 fp->f_type = DTYPE_VNODE;
177 fp->f_ops = &vnops;
178 fp->f_data = vp;
179 VOP_UNLOCK(vp, 0);
180 FILE_SET_MATURE(fp);
181 FILE_UNUSE(fp, l);
182 return 0;
183 bad:
184 FILE_UNUSE(fp, l);
185 fdremove(l->l_proc->p_fd, *fd);
186 ffree(fp);
187 return error;
188 }
189
190 int
191 pty_grant_slave(struct lwp *l, dev_t dev)
192 {
193 int error;
194 struct vnode *vp;
195
196 /*
197 * Open the slave.
198 * namei -> setattr -> unlock -> revoke -> vrele ->
199 * namei -> open -> unlock
200 * Three stage rocket:
201 * 1. Change the owner and permissions on the slave.
202 * 2. Revoke all the users of the slave.
203 * 3. open the slave.
204 */
205 if (ptm == NULL)
206 return EOPNOTSUPP;
207
208 if ((error = (*ptm->allocvp)(ptm, l, &vp, dev, 't')) != 0)
209 return error;
210
211 if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
212 struct vattr vattr;
213 (*ptm->getvattr)(ptm, l, &vattr);
214 /* Do the VOP_SETATTR() as root. */
215 error = VOP_SETATTR(vp, &vattr, lwp0.l_cred, l);
216 if (error) {
217 DPRINTF(("setattr %d\n", error));
218 VOP_UNLOCK(vp, 0);
219 vrele(vp);
220 return error;
221 }
222 }
223 VOP_UNLOCK(vp, 0);
224 if (vp->v_usecount > 1 ||
225 (vp->v_flag & (VALIASED | VLAYER)))
226 VOP_REVOKE(vp, REVOKEALL);
227
228 /*
229 * The vnode is useless after the revoke, we need to get it again.
230 */
231 vrele(vp);
232 return 0;
233 }
234
235 static int
236 pty_alloc_slave(struct lwp *l, int *fd, dev_t dev)
237 {
238 int error;
239 struct file *fp;
240 struct vnode *vp;
241
242 /* Grab a filedescriptor for the slave */
243 if ((error = falloc(l, &fp, fd)) != 0) {
244 DPRINTF(("falloc %d\n", error));
245 return error;
246 }
247
248 if (ptm == NULL) {
249 error = EOPNOTSUPP;
250 goto bad;
251 }
252
253 if ((error = (*ptm->allocvp)(ptm, l, &vp, dev, 't')) != 0)
254 goto bad;
255 if ((error = pty_vn_open(vp, l)) != 0)
256 goto bad;
257
258 fp->f_flag = FREAD|FWRITE;
259 fp->f_type = DTYPE_VNODE;
260 fp->f_ops = &vnops;
261 fp->f_data = vp;
262 VOP_UNLOCK(vp, 0);
263 FILE_SET_MATURE(fp);
264 FILE_UNUSE(fp, l);
265 return 0;
266 bad:
267 FILE_UNUSE(fp, l);
268 fdremove(l->l_proc->p_fd, *fd);
269 ffree(fp);
270 return error;
271 }
272
273 struct ptm_pty *
274 pty_sethandler(struct ptm_pty *nptm)
275 {
276 struct ptm_pty *optm = ptm;
277 ptm = nptm;
278 return optm;
279 }
280
281 int
282 pty_fill_ptmget(struct lwp *l, dev_t dev, int cfd, int sfd, void *data)
283 {
284 struct ptmget *ptmg = data;
285 int error;
286
287 if (ptm == NULL)
288 return EOPNOTSUPP;
289
290 ptmg->cfd = cfd == -1 ? minor(dev) : cfd;
291 ptmg->sfd = sfd == -1 ? minor(dev) : sfd;
292
293 error = (*ptm->makename)(ptm, l, ptmg->cn, sizeof(ptmg->cn), dev, 'p');
294 if (error)
295 return error;
296
297 return (*ptm->makename)(ptm, l, ptmg->sn, sizeof(ptmg->sn), dev, 't');
298 }
299
300 void
301 /*ARGSUSED*/
302 ptmattach(int n)
303 {
304 extern const struct cdevsw pts_cdevsw, ptc_cdevsw;
305 /* find the major and minor of the pty devices */
306 if ((pts_major = cdevsw_lookup_major(&pts_cdevsw)) == -1)
307 panic("ptmattach: Can't find pty slave in cdevsw");
308 if ((ptc_major = cdevsw_lookup_major(&ptc_cdevsw)) == -1)
309 panic("ptmattach: Can't find pty master in cdevsw");
310 #ifdef COMPAT_BSDPTY
311 ptm = &ptm_bsdpty;
312 #endif
313 }
314
315 static int
316 /*ARGSUSED*/
317 ptmopen(dev_t dev, int flag, int mode, struct lwp *l)
318 {
319 int error;
320 int fd;
321
322 switch(minor(dev)) {
323 case 0: /* /dev/ptmx */
324 case 2: /* /emul/linux/dev/ptmx */
325 if ((error = pty_alloc_master(l, &fd, &dev)) != 0)
326 return error;
327 if (minor(dev) == 2) {
328 /*
329 * Linux ptyfs grants the pty right here.
330 * Handle this case here, instead of writing
331 * a new linux module.
332 */
333 if ((error = pty_grant_slave(l, dev)) != 0) {
334 struct file *fp =
335 fd_getfile(l->l_proc->p_fd, fd);
336 FILE_UNUSE(fp, l);
337 fdremove(l->l_proc->p_fd, fd);
338 ffree(fp);
339 return error;
340 }
341 }
342 curlwp->l_dupfd = fd;
343 return EMOVEFD;
344 case 1: /* /dev/ptm */
345 return 0;
346 default:
347 return ENODEV;
348 }
349 }
350
351 static int
352 /*ARGSUSED*/
353 ptmclose(dev_t dev, int flag, int mode, struct lwp *l)
354 {
355 return (0);
356 }
357
358 static int
359 /*ARGSUSED*/
360 ptmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct lwp *l)
361 {
362 int error;
363 dev_t newdev;
364 int cfd, sfd;
365 struct file *fp;
366 struct proc *p = l->l_proc;
367
368 error = 0;
369 switch (cmd) {
370 case TIOCPTMGET:
371 if ((error = pty_alloc_master(l, &cfd, &newdev)) != 0)
372 return error;
373
374 if ((error = pty_grant_slave(l, newdev)) != 0)
375 goto bad;
376
377 if ((error = pty_alloc_slave(l, &sfd, newdev)) != 0)
378 goto bad;
379
380 /* now, put the indices and names into struct ptmget */
381 return pty_fill_ptmget(l, newdev, cfd, sfd, data);
382 default:
383 DPRINTF(("ptmioctl EINVAL\n"));
384 return EINVAL;
385 }
386 bad:
387 fp = fd_getfile(p->p_fd, cfd);
388 FILE_UNUSE(fp, l);
389 fdremove(p->p_fd, cfd);
390 ffree(fp);
391 return error;
392 }
393
394 const struct cdevsw ptm_cdevsw = {
395 ptmopen, ptmclose, noread, nowrite, ptmioctl,
396 nullstop, notty, nopoll, nommap, nokqfilter, D_TTY
397 };
398 #endif
399