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