1 /* $NetBSD: tty_ptm.c,v 1.14 2006/10/12 01:32:19 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.14 2006/10/12 01:32:19 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 DPRINTF(("no ptm\n")); 157 error = EOPNOTSUPP; 158 goto bad; 159 } 160 if ((error = (*ptm->allocvp)(ptm, l, &vp, *dev, 'p')) != 0) { 161 DPRINTF(("pty_allocvp %d\n", error)); 162 goto bad; 163 } 164 165 if ((error = pty_vn_open(vp, l)) != 0) { 166 DPRINTF(("pty_vn_open %d\n", error)); 167 /* 168 * Check if the master open failed because we lost 169 * the race to grab it. 170 */ 171 if (error != EIO) 172 goto bad; 173 error = !pty_isfree(md, 1); 174 DPRINTF(("pty_isfree %d\n", error)); 175 if (error) 176 goto retry; 177 else 178 goto bad; 179 } 180 fp->f_flag = FREAD|FWRITE; 181 fp->f_type = DTYPE_VNODE; 182 fp->f_ops = &vnops; 183 fp->f_data = vp; 184 VOP_UNLOCK(vp, 0); 185 FILE_SET_MATURE(fp); 186 FILE_UNUSE(fp, l); 187 return 0; 188 bad: 189 FILE_UNUSE(fp, l); 190 fdremove(l->l_proc->p_fd, *fd); 191 ffree(fp); 192 return error; 193 } 194 195 int 196 pty_grant_slave(struct lwp *l, dev_t dev) 197 { 198 int error; 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 VOP_UNLOCK(vp, 0); 228 if (vp->v_usecount > 1 || 229 (vp->v_flag & (VALIASED | VLAYER))) 230 VOP_REVOKE(vp, REVOKEALL); 231 232 /* 233 * The vnode is useless after the revoke, we need to get it again. 234 */ 235 vrele(vp); 236 return 0; 237 } 238 239 static int 240 pty_alloc_slave(struct lwp *l, int *fd, dev_t dev) 241 { 242 int error; 243 struct file *fp; 244 struct vnode *vp; 245 246 /* Grab a filedescriptor for the slave */ 247 if ((error = falloc(l, &fp, fd)) != 0) { 248 DPRINTF(("falloc %d\n", error)); 249 return error; 250 } 251 252 if (ptm == NULL) { 253 error = EOPNOTSUPP; 254 goto bad; 255 } 256 257 if ((error = (*ptm->allocvp)(ptm, l, &vp, dev, 't')) != 0) 258 goto bad; 259 if ((error = pty_vn_open(vp, l)) != 0) 260 goto bad; 261 262 fp->f_flag = FREAD|FWRITE; 263 fp->f_type = DTYPE_VNODE; 264 fp->f_ops = &vnops; 265 fp->f_data = vp; 266 VOP_UNLOCK(vp, 0); 267 FILE_SET_MATURE(fp); 268 FILE_UNUSE(fp, l); 269 return 0; 270 bad: 271 FILE_UNUSE(fp, l); 272 fdremove(l->l_proc->p_fd, *fd); 273 ffree(fp); 274 return error; 275 } 276 277 struct ptm_pty * 278 pty_sethandler(struct ptm_pty *nptm) 279 { 280 struct ptm_pty *optm = ptm; 281 ptm = nptm; 282 return optm; 283 } 284 285 int 286 pty_fill_ptmget(struct lwp *l, dev_t dev, int cfd, int sfd, void *data) 287 { 288 struct ptmget *ptmg = data; 289 int error; 290 291 if (ptm == NULL) 292 return EOPNOTSUPP; 293 294 ptmg->cfd = cfd == -1 ? minor(dev) : cfd; 295 ptmg->sfd = sfd == -1 ? minor(dev) : sfd; 296 297 error = (*ptm->makename)(ptm, l, ptmg->cn, sizeof(ptmg->cn), dev, 'p'); 298 if (error) 299 return error; 300 301 return (*ptm->makename)(ptm, l, ptmg->sn, sizeof(ptmg->sn), dev, 't'); 302 } 303 304 void 305 /*ARGSUSED*/ 306 ptmattach(int n __unused) 307 { 308 extern const struct cdevsw pts_cdevsw, ptc_cdevsw; 309 /* find the major and minor of the pty devices */ 310 if ((pts_major = cdevsw_lookup_major(&pts_cdevsw)) == -1) 311 panic("ptmattach: Can't find pty slave in cdevsw"); 312 if ((ptc_major = cdevsw_lookup_major(&ptc_cdevsw)) == -1) 313 panic("ptmattach: Can't find pty master in cdevsw"); 314 #ifdef COMPAT_BSDPTY 315 ptm = &ptm_bsdpty; 316 #endif 317 } 318 319 static int 320 /*ARGSUSED*/ 321 ptmopen(dev_t dev, int flag __unused, int mode __unused, struct lwp *l) 322 { 323 int error; 324 int fd; 325 dev_t ttydev; 326 327 switch(minor(dev)) { 328 case 0: /* /dev/ptmx */ 329 case 2: /* /emul/linux/dev/ptmx */ 330 if ((error = pty_alloc_master(l, &fd, &ttydev)) != 0) 331 return error; 332 if (minor(dev) == 2) { 333 /* 334 * Linux ptyfs grants the pty right here. 335 * Handle this case here, instead of writing 336 * a new linux module. 337 */ 338 if ((error = pty_grant_slave(l, ttydev)) != 0) { 339 struct file *fp = 340 fd_getfile(l->l_proc->p_fd, fd); 341 FILE_UNUSE(fp, l); 342 fdremove(l->l_proc->p_fd, fd); 343 ffree(fp); 344 return error; 345 } 346 } 347 curlwp->l_dupfd = fd; 348 return EMOVEFD; 349 case 1: /* /dev/ptm */ 350 return 0; 351 default: 352 return ENODEV; 353 } 354 } 355 356 static int 357 /*ARGSUSED*/ 358 ptmclose(dev_t dev __unused, int flag __unused, int mode __unused, 359 struct lwp *l __unused) 360 { 361 return (0); 362 } 363 364 static int 365 /*ARGSUSED*/ 366 ptmioctl(dev_t dev __unused, u_long cmd, caddr_t data, int flag __unused, 367 struct lwp *l) 368 { 369 int error; 370 dev_t newdev; 371 int cfd, sfd; 372 struct file *fp; 373 struct proc *p = l->l_proc; 374 375 error = 0; 376 switch (cmd) { 377 case TIOCPTMGET: 378 if ((error = pty_alloc_master(l, &cfd, &newdev)) != 0) 379 return error; 380 381 if ((error = pty_grant_slave(l, newdev)) != 0) 382 goto bad; 383 384 if ((error = pty_alloc_slave(l, &sfd, newdev)) != 0) 385 goto bad; 386 387 /* now, put the indices and names into struct ptmget */ 388 return pty_fill_ptmget(l, newdev, cfd, sfd, data); 389 default: 390 DPRINTF(("ptmioctl EINVAL\n")); 391 return EINVAL; 392 } 393 bad: 394 fp = fd_getfile(p->p_fd, cfd); 395 FILE_UNUSE(fp, l); 396 fdremove(p->p_fd, cfd); 397 ffree(fp); 398 return error; 399 } 400 401 const struct cdevsw ptm_cdevsw = { 402 ptmopen, ptmclose, noread, nowrite, ptmioctl, 403 nullstop, notty, nopoll, nommap, nokqfilter, D_TTY 404 }; 405 #endif 406