tty_ptm.c revision 1.29 1 /* $NetBSD: tty_ptm.c,v 1.29 2014/03/16 05:20:30 dholland 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 *
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.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: tty_ptm.c,v 1.29 2014/03/16 05:20:30 dholland Exp $");
31
32 #include "opt_compat_netbsd.h"
33 #include "opt_ptm.h"
34
35 /* pty multiplexor driver /dev/ptm{,x} */
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/ioctl.h>
40 #include <sys/proc.h>
41 #include <sys/tty.h>
42 #include <sys/stat.h>
43 #include <sys/file.h>
44 #include <sys/uio.h>
45 #include <sys/kernel.h>
46 #include <sys/vnode.h>
47 #include <sys/namei.h>
48 #include <sys/signalvar.h>
49 #include <sys/filedesc.h>
50 #include <sys/conf.h>
51 #include <sys/poll.h>
52 #include <sys/pty.h>
53 #include <sys/kauth.h>
54
55 #include <miscfs/specfs/specdev.h>
56
57 #ifdef COMPAT_60
58 #include <compat/sys/ttycom.h>
59 #endif /* COMPAT_60 */
60
61 #ifdef DEBUG_PTM
62 #define DPRINTF(a) printf a
63 #else
64 #define DPRINTF(a)
65 #endif
66
67 #ifdef NO_DEV_PTM
68 const struct cdevsw ptm_cdevsw = {
69 .d_open = noopen,
70 .d_close = noclose,
71 .d_read = noread,
72 .d_write = nowrite,
73 .d_ioctl = noioctl,
74 .d_stop = nostop,
75 .d_tty = notty,
76 .d_poll = nopoll,
77 .d_mmap = nommap,
78 .d_kqfilter = nokqfilter,
79 .d_flag = D_TTY
80 };
81 #else
82
83 static struct ptm_pty *ptm;
84 int pts_major, ptc_major;
85
86 static dev_t pty_getfree(void);
87 static int pty_alloc_master(struct lwp *, int *, dev_t *);
88 static int pty_alloc_slave(struct lwp *, int *, dev_t);
89
90 void ptmattach(int);
91
92 dev_t
93 pty_makedev(char ms, int minor)
94 {
95 return makedev(ms == 't' ? pts_major : ptc_major, minor);
96 }
97
98
99 static dev_t
100 pty_getfree(void)
101 {
102 extern kmutex_t pt_softc_mutex;
103 int i;
104
105 mutex_enter(&pt_softc_mutex);
106 for (i = 0; i < npty; i++) {
107 if (pty_isfree(i, 0))
108 break;
109 }
110 mutex_exit(&pt_softc_mutex);
111 return pty_makedev('t', i);
112 }
113
114 /*
115 * Hacked up version of vn_open. We _only_ handle ptys and only open
116 * them with FREAD|FWRITE and never deal with creat or stuff like that.
117 *
118 * We need it because we have to fake up root credentials to open the pty.
119 */
120 int
121 pty_vn_open(struct vnode *vp, struct lwp *l)
122 {
123 int error;
124
125 if (vp->v_type != VCHR) {
126 vput(vp);
127 return EINVAL;
128 }
129
130 error = VOP_OPEN(vp, FREAD|FWRITE, lwp0.l_cred);
131
132 if (error) {
133 vput(vp);
134 return error;
135 }
136
137 vp->v_writecount++;
138
139 return 0;
140 }
141
142 static int
143 pty_alloc_master(struct lwp *l, int *fd, dev_t *dev)
144 {
145 int error;
146 struct file *fp;
147 struct vnode *vp;
148 int md;
149
150 if ((error = fd_allocfile(&fp, fd)) != 0) {
151 DPRINTF(("fd_allocfile %d\n", error));
152 return error;
153 }
154 retry:
155 /* Find and open a free master pty. */
156 *dev = pty_getfree();
157 md = minor(*dev);
158 if ((error = pty_check(md)) != 0) {
159 DPRINTF(("pty_check %d\n", error));
160 goto bad;
161 }
162 if (ptm == NULL) {
163 DPRINTF(("no ptm\n"));
164 error = EOPNOTSUPP;
165 goto bad;
166 }
167 if ((error = (*ptm->allocvp)(ptm, l, &vp, *dev, 'p')) != 0) {
168 DPRINTF(("pty_allocvp %d\n", error));
169 goto bad;
170 }
171
172 if ((error = pty_vn_open(vp, l)) != 0) {
173 DPRINTF(("pty_vn_open %d\n", error));
174 /*
175 * Check if the master open failed because we lost
176 * the race to grab it.
177 */
178 if (error != EIO)
179 goto bad;
180 error = !pty_isfree(md, 1);
181 DPRINTF(("pty_isfree %d\n", error));
182 if (error)
183 goto retry;
184 else
185 goto bad;
186 }
187 fp->f_flag = FREAD|FWRITE;
188 fp->f_type = DTYPE_VNODE;
189 fp->f_ops = &vnops;
190 fp->f_data = vp;
191 VOP_UNLOCK(vp);
192 fd_affix(curproc, fp, *fd);
193 return 0;
194 bad:
195 fd_abort(curproc, fp, *fd);
196 return error;
197 }
198
199 int
200 pty_grant_slave(struct lwp *l, dev_t dev)
201 {
202 int error;
203 struct vnode *vp;
204
205 /*
206 * Open the slave.
207 * namei -> setattr -> unlock -> revoke -> vrele ->
208 * namei -> open -> unlock
209 * Three stage rocket:
210 * 1. Change the owner and permissions on the slave.
211 * 2. Revoke all the users of the slave.
212 * 3. open the slave.
213 */
214 if (ptm == NULL)
215 return EOPNOTSUPP;
216 if ((error = (*ptm->allocvp)(ptm, l, &vp, dev, 't')) != 0)
217 return error;
218
219 if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
220 struct vattr vattr;
221 (*ptm->getvattr)(ptm, l, &vattr);
222 /* Do the VOP_SETATTR() as root. */
223 error = VOP_SETATTR(vp, &vattr, lwp0.l_cred);
224 if (error) {
225 DPRINTF(("setattr %d\n", error));
226 VOP_UNLOCK(vp);
227 vrele(vp);
228 return error;
229 }
230 }
231 VOP_UNLOCK(vp);
232 VOP_REVOKE(vp, REVOKEALL);
233
234 /*
235 * The vnode is useless after the revoke, we need to get it again.
236 */
237 vrele(vp);
238 return 0;
239 }
240
241 static int
242 pty_alloc_slave(struct lwp *l, int *fd, dev_t dev)
243 {
244 int error;
245 struct file *fp;
246 struct vnode *vp;
247
248 /* Grab a filedescriptor for the slave */
249 if ((error = fd_allocfile(&fp, fd)) != 0) {
250 DPRINTF(("fd_allocfile %d\n", error));
251 return error;
252 }
253
254 if (ptm == NULL) {
255 error = EOPNOTSUPP;
256 goto bad;
257 }
258
259 if ((error = (*ptm->allocvp)(ptm, l, &vp, dev, 't')) != 0)
260 goto bad;
261 if ((error = pty_vn_open(vp, l)) != 0)
262 goto bad;
263
264 fp->f_flag = FREAD|FWRITE;
265 fp->f_type = DTYPE_VNODE;
266 fp->f_ops = &vnops;
267 fp->f_data = vp;
268 VOP_UNLOCK(vp);
269 fd_affix(curproc, fp, *fd);
270 return 0;
271 bad:
272 fd_abort(curproc, fp, *fd);
273 return error;
274 }
275
276 struct ptm_pty *
277 pty_sethandler(struct ptm_pty *nptm)
278 {
279 struct ptm_pty *optm = ptm;
280 ptm = nptm;
281 return optm;
282 }
283
284 int
285 pty_fill_ptmget(struct lwp *l, dev_t dev, int cfd, int sfd, void *data)
286 {
287 struct ptmget *ptmg = data;
288 int error;
289
290 if (ptm == NULL)
291 return EOPNOTSUPP;
292
293 ptmg->cfd = cfd == -1 ? minor(dev) : cfd;
294 ptmg->sfd = sfd == -1 ? minor(dev) : sfd;
295
296 error = (*ptm->makename)(ptm, l, ptmg->cn, sizeof(ptmg->cn), dev, 'p');
297 if (error)
298 return error;
299
300 return (*ptm->makename)(ptm, l, ptmg->sn, sizeof(ptmg->sn), dev, 't');
301 }
302
303 void
304 /*ARGSUSED*/
305 ptmattach(int n)
306 {
307 extern const struct cdevsw pts_cdevsw, ptc_cdevsw;
308 /* find the major and minor of the pty devices */
309 if ((pts_major = cdevsw_lookup_major(&pts_cdevsw)) == -1)
310 panic("ptmattach: Can't find pty slave in cdevsw");
311 if ((ptc_major = cdevsw_lookup_major(&ptc_cdevsw)) == -1)
312 panic("ptmattach: Can't find pty master in cdevsw");
313 #ifdef COMPAT_BSDPTY
314 ptm = &ptm_bsdpty;
315 #endif
316 }
317
318 static int
319 /*ARGSUSED*/
320 ptmopen(dev_t dev, int flag, int mode, struct lwp *l)
321 {
322 int error;
323 int fd;
324 dev_t ttydev;
325
326 switch(minor(dev)) {
327 case 0: /* /dev/ptmx */
328 case 2: /* /emul/linux/dev/ptmx */
329 if ((error = pty_alloc_master(l, &fd, &ttydev)) != 0)
330 return error;
331 if (minor(dev) == 2) {
332 /*
333 * Linux ptyfs grants the pty right here.
334 * Handle this case here, instead of writing
335 * a new linux module.
336 */
337 if ((error = pty_grant_slave(l, ttydev)) != 0) {
338 file_t *fp = fd_getfile(fd);
339 if (fp != NULL) {
340 fd_close(fd);
341 }
342 return error;
343 }
344 }
345 curlwp->l_dupfd = fd;
346 return EMOVEFD;
347 case 1: /* /dev/ptm */
348 return 0;
349 default:
350 return ENODEV;
351 }
352 }
353
354 static int
355 /*ARGSUSED*/
356 ptmclose(dev_t dev, int flag, int mode, struct lwp *l)
357 {
358
359 return (0);
360 }
361
362 static int
363 /*ARGSUSED*/
364 ptmioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
365 {
366 int error;
367 dev_t newdev;
368 int cfd, sfd;
369 file_t *fp;
370
371 error = 0;
372 switch (cmd) {
373 case TIOCPTMGET:
374 if ((error = pty_alloc_master(l, &cfd, &newdev)) != 0)
375 return error;
376
377 if ((error = pty_grant_slave(l, newdev)) != 0)
378 goto bad;
379
380 if ((error = pty_alloc_slave(l, &sfd, newdev)) != 0)
381 goto bad;
382
383 /* now, put the indices and names into struct ptmget */
384 return pty_fill_ptmget(l, newdev, cfd, sfd, data);
385 default:
386 #ifdef COMPAT_60
387 error = compat_60_ptmioctl(dev, cmd, data, flag, l);
388 if (error != EPASSTHROUGH)
389 return error;
390 #endif /* COMPAT_60 */
391 DPRINTF(("ptmioctl EINVAL\n"));
392 return EINVAL;
393 }
394 bad:
395 fp = fd_getfile(cfd);
396 if (fp != NULL) {
397 fd_close(cfd);
398 }
399 return error;
400 }
401
402 const struct cdevsw ptm_cdevsw = {
403 .d_open = ptmopen,
404 .d_close = ptmclose,
405 .d_read = noread,
406 .d_write = nowrite,
407 .d_ioctl = ptmioctl,
408 .d_stop = nullstop,
409 .d_tty = notty,
410 .d_poll = nopoll,
411 .d_mmap = nommap,
412 .d_kqfilter = nokqfilter,
413 .d_flag = D_TTY
414 };
415 #endif
416