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