1 /* $NetBSD: tty_ptm.c,v 1.9 2006/05/14 21:15:11 elad 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.9 2006/05/14 21:15:11 elad 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 kauth_cred_t cred; 117 int error; 118 119 if (vp->v_type != VCHR) { 120 vput(vp); 121 return EINVAL; 122 } 123 124 /* 125 * Get us a fresh cred with root privileges. 126 */ 127 cred = kauth_cred_alloc(); 128 error = VOP_OPEN(vp, FREAD|FWRITE, cred, l); 129 kauth_cred_free(cred); 130 131 if (error) { 132 vput(vp); 133 return error; 134 } 135 136 vp->v_writecount++; 137 138 return 0; 139 } 140 141 static int 142 pty_alloc_master(struct lwp *l, int *fd, dev_t *dev) 143 { 144 int error; 145 struct file *fp; 146 struct vnode *vp; 147 struct proc *p = l->l_proc; 148 int md; 149 150 if ((error = falloc(p, &fp, fd)) != 0) { 151 DPRINTF(("falloc %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 error = EOPNOTSUPP; 164 goto bad; 165 } 166 if ((error = (*ptm->allocvp)(ptm, l, &vp, *dev, 'p')) != 0) 167 goto bad; 168 169 if ((error = pty_vn_open(vp, l)) != 0) { 170 /* 171 * Check if the master open failed because we lost 172 * the race to grab it. 173 */ 174 if (error != EIO) 175 goto bad; 176 error = !pty_isfree(md, 1); 177 if (error) 178 goto retry; 179 else 180 goto bad; 181 } 182 fp->f_flag = FREAD|FWRITE; 183 fp->f_type = DTYPE_VNODE; 184 fp->f_ops = &vnops; 185 fp->f_data = vp; 186 VOP_UNLOCK(vp, 0); 187 FILE_SET_MATURE(fp); 188 FILE_UNUSE(fp, l); 189 return 0; 190 bad: 191 FILE_UNUSE(fp, l); 192 fdremove(p->p_fd, *fd); 193 ffree(fp); 194 return error; 195 } 196 197 int 198 pty_grant_slave(struct lwp *l, dev_t dev) 199 { 200 int error; 201 struct vnode *vp; 202 203 /* 204 * Open the slave. 205 * namei -> setattr -> unlock -> revoke -> vrele -> 206 * namei -> open -> unlock 207 * Three stage rocket: 208 * 1. Change the owner and permissions on the slave. 209 * 2. Revoke all the users of the slave. 210 * 3. open the slave. 211 */ 212 if (ptm == NULL) 213 return EOPNOTSUPP; 214 215 if ((error = (*ptm->allocvp)(ptm, l, &vp, dev, 't')) != 0) 216 return error; 217 218 if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { 219 struct vattr vattr; 220 kauth_cred_t cred; 221 (*ptm->getvattr)(ptm, l->l_proc, &vattr); 222 /* Get a fake cred to pretend we're root. */ 223 cred = kauth_cred_alloc(); 224 error = VOP_SETATTR(vp, &vattr, cred, l); 225 kauth_cred_free(cred); 226 if (error) { 227 DPRINTF(("setattr %d\n", error)); 228 VOP_UNLOCK(vp, 0); 229 vrele(vp); 230 return error; 231 } 232 } 233 VOP_UNLOCK(vp, 0); 234 if (vp->v_usecount > 1 || 235 (vp->v_flag & (VALIASED | VLAYER))) 236 VOP_REVOKE(vp, REVOKEALL); 237 238 /* 239 * The vnode is useless after the revoke, we need to get it again. 240 */ 241 vrele(vp); 242 return 0; 243 } 244 245 static int 246 pty_alloc_slave(struct lwp *l, int *fd, dev_t dev) 247 { 248 int error; 249 struct file *fp; 250 struct vnode *vp; 251 struct proc *p = l->l_proc; 252 253 /* Grab a filedescriptor for the slave */ 254 if ((error = falloc(p, &fp, fd)) != 0) { 255 DPRINTF(("falloc %d\n", error)); 256 return error; 257 } 258 259 if (ptm == NULL) { 260 error = EOPNOTSUPP; 261 goto bad; 262 } 263 264 if ((error = (*ptm->allocvp)(ptm, l, &vp, dev, 't')) != 0) 265 goto bad; 266 if ((error = pty_vn_open(vp, l)) != 0) 267 goto bad; 268 269 fp->f_flag = FREAD|FWRITE; 270 fp->f_type = DTYPE_VNODE; 271 fp->f_ops = &vnops; 272 fp->f_data = vp; 273 VOP_UNLOCK(vp, 0); 274 FILE_SET_MATURE(fp); 275 FILE_UNUSE(fp, l); 276 return 0; 277 bad: 278 FILE_UNUSE(fp, l); 279 fdremove(p->p_fd, *fd); 280 ffree(fp); 281 return error; 282 } 283 284 struct ptm_pty * 285 pty_sethandler(struct ptm_pty *nptm) 286 { 287 struct ptm_pty *optm = ptm; 288 ptm = nptm; 289 return optm; 290 } 291 292 int 293 pty_fill_ptmget(struct lwp *l, dev_t dev, int cfd, int sfd, void *data) 294 { 295 struct ptmget *ptmg = data; 296 int error; 297 298 if (ptm == NULL) 299 return EOPNOTSUPP; 300 301 ptmg->cfd = cfd == -1 ? minor(dev) : cfd; 302 ptmg->sfd = sfd == -1 ? minor(dev) : sfd; 303 304 error = (*ptm->makename)(ptm, l, ptmg->cn, sizeof(ptmg->cn), dev, 'p'); 305 if (error) 306 return error; 307 308 return (*ptm->makename)(ptm, l, ptmg->sn, sizeof(ptmg->sn), dev, 't'); 309 } 310 311 void 312 /*ARGSUSED*/ 313 ptmattach(int n) 314 { 315 extern const struct cdevsw pts_cdevsw, ptc_cdevsw; 316 /* find the major and minor of the pty devices */ 317 if ((pts_major = cdevsw_lookup_major(&pts_cdevsw)) == -1) 318 panic("ptmattach: Can't find pty slave in cdevsw"); 319 if ((ptc_major = cdevsw_lookup_major(&ptc_cdevsw)) == -1) 320 panic("ptmattach: Can't find pty master in cdevsw"); 321 #ifdef COMPAT_BSDPTY 322 ptm = &ptm_bsdpty; 323 #endif 324 } 325 326 static int 327 /*ARGSUSED*/ 328 ptmopen(dev_t dev, int flag, int mode, struct lwp *l) 329 { 330 int error; 331 int fd; 332 333 switch(minor(dev)) { 334 case 0: /* /dev/ptmx */ 335 if ((error = pty_alloc_master(l, &fd, &dev)) != 0) 336 return error; 337 curlwp->l_dupfd = fd; 338 return EMOVEFD; 339 case 1: /* /dev/ptm */ 340 return 0; 341 default: 342 return ENODEV; 343 } 344 } 345 346 static int 347 /*ARGSUSED*/ 348 ptmclose(dev_t dev, int flag, int mode, struct lwp *l) 349 { 350 return (0); 351 } 352 353 static int 354 /*ARGSUSED*/ 355 ptmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct lwp *l) 356 { 357 int error; 358 dev_t newdev; 359 int cfd, sfd; 360 struct file *fp; 361 struct proc *p = l->l_proc; 362 363 error = 0; 364 switch (cmd) { 365 case TIOCPTMGET: 366 if ((error = pty_alloc_master(l, &cfd, &newdev)) != 0) 367 return error; 368 369 if ((error = pty_grant_slave(l, newdev)) != 0) 370 goto bad; 371 372 if ((error = pty_alloc_slave(l, &sfd, newdev)) != 0) 373 goto bad; 374 375 /* now, put the indices and names into struct ptmget */ 376 return pty_fill_ptmget(l, newdev, cfd, sfd, data); 377 default: 378 DPRINTF(("ptmioctl EINVAL\n")); 379 return EINVAL; 380 } 381 bad: 382 fp = fd_getfile(p->p_fd, cfd); 383 fdremove(p->p_fd, cfd); 384 ffree(fp); 385 return error; 386 } 387 388 const struct cdevsw ptm_cdevsw = { 389 ptmopen, ptmclose, noread, nowrite, ptmioctl, 390 nullstop, notty, nopoll, nommap, nokqfilter, D_TTY 391 }; 392 #endif 393