1 /* $NetBSD: tty_ptm.c,v 1.24 2008/03/21 21:55:00 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.24 2008/03/21 21:55:00 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 = fd_allocfile(&fp, fd)) != 0) { 145 DPRINTF(("fd_allocfile %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 fd_affix(curproc, fp, *fd); 187 return 0; 188 bad: 189 fd_abort(curproc, fp, *fd); 190 return error; 191 } 192 193 int 194 pty_grant_slave(struct lwp *l, dev_t dev) 195 { 196 int error; 197 struct vnode *vp; 198 199 /* 200 * Open the slave. 201 * namei -> setattr -> unlock -> revoke -> vrele -> 202 * namei -> open -> unlock 203 * Three stage rocket: 204 * 1. Change the owner and permissions on the slave. 205 * 2. Revoke all the users of the slave. 206 * 3. open the slave. 207 */ 208 if (ptm == NULL) 209 return EOPNOTSUPP; 210 if ((error = (*ptm->allocvp)(ptm, l, &vp, dev, 't')) != 0) 211 return error; 212 213 if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { 214 struct vattr vattr; 215 (*ptm->getvattr)(ptm, l, &vattr); 216 /* Do the VOP_SETATTR() as root. */ 217 error = VOP_SETATTR(vp, &vattr, lwp0.l_cred); 218 if (error) { 219 DPRINTF(("setattr %d\n", error)); 220 VOP_UNLOCK(vp, 0); 221 vrele(vp); 222 return error; 223 } 224 } 225 VOP_UNLOCK(vp, 0); 226 VOP_REVOKE(vp, REVOKEALL); 227 228 /* 229 * The vnode is useless after the revoke, we need to get it again. 230 */ 231 vrele(vp); 232 return 0; 233 } 234 235 static int 236 pty_alloc_slave(struct lwp *l, int *fd, dev_t dev) 237 { 238 int error; 239 struct file *fp; 240 struct vnode *vp; 241 242 /* Grab a filedescriptor for the slave */ 243 if ((error = fd_allocfile(&fp, fd)) != 0) { 244 DPRINTF(("fd_allocfile %d\n", error)); 245 return error; 246 } 247 248 if (ptm == NULL) { 249 error = EOPNOTSUPP; 250 goto bad; 251 } 252 253 if ((error = (*ptm->allocvp)(ptm, l, &vp, dev, 't')) != 0) 254 goto bad; 255 if ((error = pty_vn_open(vp, l)) != 0) 256 goto bad; 257 258 fp->f_flag = FREAD|FWRITE; 259 fp->f_type = DTYPE_VNODE; 260 fp->f_ops = &vnops; 261 fp->f_data = vp; 262 VOP_UNLOCK(vp, 0); 263 fd_affix(curproc, fp, *fd); 264 return 0; 265 bad: 266 fd_abort(curproc, fp, *fd); 267 return error; 268 } 269 270 struct ptm_pty * 271 pty_sethandler(struct ptm_pty *nptm) 272 { 273 struct ptm_pty *optm = ptm; 274 ptm = nptm; 275 return optm; 276 } 277 278 int 279 pty_fill_ptmget(struct lwp *l, dev_t dev, int cfd, int sfd, void *data) 280 { 281 struct ptmget *ptmg = data; 282 int error; 283 284 if (ptm == NULL) 285 return EOPNOTSUPP; 286 287 ptmg->cfd = cfd == -1 ? minor(dev) : cfd; 288 ptmg->sfd = sfd == -1 ? minor(dev) : sfd; 289 290 error = (*ptm->makename)(ptm, l, ptmg->cn, sizeof(ptmg->cn), dev, 'p'); 291 if (error) 292 return error; 293 294 return (*ptm->makename)(ptm, l, ptmg->sn, sizeof(ptmg->sn), dev, 't'); 295 } 296 297 void 298 /*ARGSUSED*/ 299 ptmattach(int n) 300 { 301 extern const struct cdevsw pts_cdevsw, ptc_cdevsw; 302 /* find the major and minor of the pty devices */ 303 if ((pts_major = cdevsw_lookup_major(&pts_cdevsw)) == -1) 304 panic("ptmattach: Can't find pty slave in cdevsw"); 305 if ((ptc_major = cdevsw_lookup_major(&ptc_cdevsw)) == -1) 306 panic("ptmattach: Can't find pty master in cdevsw"); 307 #ifdef COMPAT_BSDPTY 308 ptm = &ptm_bsdpty; 309 #endif 310 } 311 312 static int 313 /*ARGSUSED*/ 314 ptmopen(dev_t dev, int flag, int mode, struct lwp *l) 315 { 316 int error; 317 int fd; 318 dev_t ttydev; 319 320 switch(minor(dev)) { 321 case 0: /* /dev/ptmx */ 322 case 2: /* /emul/linux/dev/ptmx */ 323 if ((error = pty_alloc_master(l, &fd, &ttydev)) != 0) 324 return error; 325 if (minor(dev) == 2) { 326 /* 327 * Linux ptyfs grants the pty right here. 328 * Handle this case here, instead of writing 329 * a new linux module. 330 */ 331 if ((error = pty_grant_slave(l, ttydev)) != 0) { 332 file_t *fp = fd_getfile(fd); 333 if (fp != NULL) { 334 fd_close(fd); 335 } 336 return error; 337 } 338 } 339 curlwp->l_dupfd = fd; 340 return EMOVEFD; 341 case 1: /* /dev/ptm */ 342 return 0; 343 default: 344 return ENODEV; 345 } 346 } 347 348 static int 349 /*ARGSUSED*/ 350 ptmclose(dev_t dev, int flag, int mode, struct lwp *l) 351 { 352 353 return (0); 354 } 355 356 static int 357 /*ARGSUSED*/ 358 ptmioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 359 { 360 int error; 361 dev_t newdev; 362 int cfd, sfd; 363 file_t *fp; 364 365 error = 0; 366 switch (cmd) { 367 case TIOCPTMGET: 368 if ((error = pty_alloc_master(l, &cfd, &newdev)) != 0) 369 return error; 370 371 if ((error = pty_grant_slave(l, newdev)) != 0) 372 goto bad; 373 374 if ((error = pty_alloc_slave(l, &sfd, newdev)) != 0) 375 goto bad; 376 377 /* now, put the indices and names into struct ptmget */ 378 return pty_fill_ptmget(l, newdev, cfd, sfd, data); 379 default: 380 DPRINTF(("ptmioctl EINVAL\n")); 381 return EINVAL; 382 } 383 bad: 384 fp = fd_getfile(cfd); 385 if (fp != NULL) { 386 fd_close(cfd); 387 } 388 return error; 389 } 390 391 const struct cdevsw ptm_cdevsw = { 392 ptmopen, ptmclose, noread, nowrite, ptmioctl, 393 nullstop, notty, nopoll, nommap, nokqfilter, D_TTY 394 }; 395 #endif 396