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