1 /* $NetBSD: tty_ptm.c,v 1.23 2008/01/24 17:32:54 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.23 2008/01/24 17:32:54 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 #include <miscfs/specfs/specdev.h> 63 64 #ifdef DEBUG_PTM 65 #define DPRINTF(a) printf a 66 #else 67 #define DPRINTF(a) 68 #endif 69 70 #ifdef NO_DEV_PTM 71 const struct cdevsw ptm_cdevsw = { 72 noopen, noclose, noread, nowrite, noioctl, 73 nostop, notty, nopoll, nommap, nokqfilter, D_TTY 74 }; 75 #else 76 77 static struct ptm_pty *ptm; 78 int pts_major, ptc_major; 79 80 static dev_t pty_getfree(void); 81 static int pty_alloc_master(struct lwp *, int *, dev_t *); 82 static int pty_alloc_slave(struct lwp *, int *, dev_t); 83 84 void ptmattach(int); 85 86 dev_t 87 pty_makedev(char ms, int minor) 88 { 89 return makedev(ms == 't' ? pts_major : ptc_major, minor); 90 } 91 92 93 static dev_t 94 pty_getfree(void) 95 { 96 extern kmutex_t pt_softc_mutex; 97 int i; 98 99 mutex_enter(&pt_softc_mutex); 100 for (i = 0; i < npty; i++) { 101 if (pty_isfree(i, 0)) 102 break; 103 } 104 mutex_exit(&pt_softc_mutex); 105 return pty_makedev('t', i); 106 } 107 108 /* 109 * Hacked up version of vn_open. We _only_ handle ptys and only open 110 * them with FREAD|FWRITE and never deal with creat or stuff like that. 111 * 112 * We need it because we have to fake up root credentials to open the pty. 113 */ 114 int 115 pty_vn_open(struct vnode *vp, struct lwp *l) 116 { 117 int error; 118 119 if (vp->v_type != VCHR) { 120 vput(vp); 121 return EINVAL; 122 } 123 124 error = VOP_OPEN(vp, FREAD|FWRITE, lwp0.l_cred); 125 126 if (error) { 127 vput(vp); 128 return error; 129 } 130 131 vp->v_writecount++; 132 133 return 0; 134 } 135 136 static int 137 pty_alloc_master(struct lwp *l, int *fd, dev_t *dev) 138 { 139 int error; 140 struct file *fp; 141 struct vnode *vp; 142 int md; 143 144 if ((error = falloc(l, &fp, fd)) != 0) { 145 DPRINTF(("falloc %d\n", error)); 146 return error; 147 } 148 retry: 149 /* Find and open a free master pty. */ 150 *dev = pty_getfree(); 151 md = minor(*dev); 152 if ((error = pty_check(md)) != 0) { 153 DPRINTF(("pty_check %d\n", error)); 154 goto bad; 155 } 156 if (ptm == NULL) { 157 DPRINTF(("no ptm\n")); 158 error = EOPNOTSUPP; 159 goto bad; 160 } 161 if ((error = (*ptm->allocvp)(ptm, l, &vp, *dev, 'p')) != 0) { 162 DPRINTF(("pty_allocvp %d\n", error)); 163 goto bad; 164 } 165 166 if ((error = pty_vn_open(vp, l)) != 0) { 167 DPRINTF(("pty_vn_open %d\n", error)); 168 /* 169 * Check if the master open failed because we lost 170 * the race to grab it. 171 */ 172 if (error != EIO) 173 goto bad; 174 error = !pty_isfree(md, 1); 175 DPRINTF(("pty_isfree %d\n", error)); 176 if (error) 177 goto retry; 178 else 179 goto bad; 180 } 181 fp->f_flag = FREAD|FWRITE; 182 fp->f_type = DTYPE_VNODE; 183 fp->f_ops = &vnops; 184 fp->f_data = vp; 185 VOP_UNLOCK(vp, 0); 186 FILE_SET_MATURE(fp); 187 FILE_UNUSE(fp, l); 188 return 0; 189 bad: 190 FILE_UNUSE(fp, l); 191 fdremove(l->l_proc->p_fd, *fd); 192 ffree(fp); 193 return error; 194 } 195 196 int 197 pty_grant_slave(struct lwp *l, dev_t dev) 198 { 199 int error; 200 struct vnode *vp; 201 202 /* 203 * Open the slave. 204 * namei -> setattr -> unlock -> revoke -> vrele -> 205 * namei -> open -> unlock 206 * Three stage rocket: 207 * 1. Change the owner and permissions on the slave. 208 * 2. Revoke all the users of the slave. 209 * 3. open the slave. 210 */ 211 if (ptm == NULL) 212 return EOPNOTSUPP; 213 if ((error = (*ptm->allocvp)(ptm, l, &vp, dev, 't')) != 0) 214 return error; 215 216 if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { 217 struct vattr vattr; 218 (*ptm->getvattr)(ptm, l, &vattr); 219 /* Do the VOP_SETATTR() as root. */ 220 error = VOP_SETATTR(vp, &vattr, lwp0.l_cred); 221 if (error) { 222 DPRINTF(("setattr %d\n", error)); 223 VOP_UNLOCK(vp, 0); 224 vrele(vp); 225 return error; 226 } 227 } 228 VOP_UNLOCK(vp, 0); 229 VOP_REVOKE(vp, REVOKEALL); 230 231 /* 232 * The vnode is useless after the revoke, we need to get it again. 233 */ 234 vrele(vp); 235 return 0; 236 } 237 238 static int 239 pty_alloc_slave(struct lwp *l, int *fd, dev_t dev) 240 { 241 int error; 242 struct file *fp; 243 struct vnode *vp; 244 245 /* Grab a filedescriptor for the slave */ 246 if ((error = falloc(l, &fp, fd)) != 0) { 247 DPRINTF(("falloc %d\n", error)); 248 return error; 249 } 250 251 if (ptm == NULL) { 252 error = EOPNOTSUPP; 253 goto bad; 254 } 255 256 if ((error = (*ptm->allocvp)(ptm, l, &vp, dev, 't')) != 0) 257 goto bad; 258 if ((error = pty_vn_open(vp, l)) != 0) 259 goto bad; 260 261 fp->f_flag = FREAD|FWRITE; 262 fp->f_type = DTYPE_VNODE; 263 fp->f_ops = &vnops; 264 fp->f_data = vp; 265 VOP_UNLOCK(vp, 0); 266 FILE_SET_MATURE(fp); 267 FILE_UNUSE(fp, l); 268 return 0; 269 bad: 270 FILE_UNUSE(fp, l); 271 fdremove(l->l_proc->p_fd, *fd); 272 ffree(fp); 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 struct file *fp = 339 fd_getfile(l->l_proc->p_fd, fd); 340 if (fp != NULL) { 341 FILE_UNUSE(fp, l); 342 fdremove(l->l_proc->p_fd, fd); 343 ffree(fp); 344 } 345 return error; 346 } 347 } 348 curlwp->l_dupfd = fd; 349 return EMOVEFD; 350 case 1: /* /dev/ptm */ 351 return 0; 352 default: 353 return ENODEV; 354 } 355 } 356 357 static int 358 /*ARGSUSED*/ 359 ptmclose(dev_t dev, int flag, int mode, struct lwp *l) 360 { 361 362 return (0); 363 } 364 365 static int 366 /*ARGSUSED*/ 367 ptmioctl(dev_t dev, u_long cmd, void *data, int flag, 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 if (fp != NULL) { 396 FILE_UNUSE(fp, l); 397 fdremove(p->p_fd, cfd); 398 ffree(fp); 399 } 400 return error; 401 } 402 403 const struct cdevsw ptm_cdevsw = { 404 ptmopen, ptmclose, noread, nowrite, ptmioctl, 405 nullstop, notty, nopoll, nommap, nokqfilter, D_TTY 406 }; 407 #endif 408