1 /* $NetBSD: tty_ptm.c,v 1.32 2014/04/04 18:11:58 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: tty_ptm.c,v 1.32 2014/04/04 18:11:58 christos Exp $"); 31 32 #include "opt_compat_netbsd.h" 33 #include "opt_ptm.h" 34 35 /* pty multiplexor driver /dev/ptm{,x} */ 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/ioctl.h> 40 #include <sys/proc.h> 41 #include <sys/tty.h> 42 #include <sys/stat.h> 43 #include <sys/file.h> 44 #include <sys/uio.h> 45 #include <sys/kernel.h> 46 #include <sys/vnode.h> 47 #include <sys/namei.h> 48 #include <sys/signalvar.h> 49 #include <sys/filedesc.h> 50 #include <sys/conf.h> 51 #include <sys/poll.h> 52 #include <sys/pty.h> 53 #include <sys/kauth.h> 54 55 #include <miscfs/specfs/specdev.h> 56 57 #ifdef COMPAT_60 58 #include <compat/sys/ttycom.h> 59 #endif /* COMPAT_60 */ 60 61 #ifdef DEBUG_PTM 62 #define DPRINTF(a) printf a 63 #else 64 #define DPRINTF(a) 65 #endif 66 67 #ifdef NO_DEV_PTM 68 const struct cdevsw ptm_cdevsw = { 69 .d_open = noopen, 70 .d_close = noclose, 71 .d_read = noread, 72 .d_write = nowrite, 73 .d_ioctl = noioctl, 74 .d_stop = nostop, 75 .d_tty = notty, 76 .d_poll = nopoll, 77 .d_mmap = nommap, 78 .d_kqfilter = nokqfilter, 79 .d_flag = D_TTY 80 }; 81 #else 82 83 static struct ptm_pty *ptm; 84 int pts_major, ptc_major; 85 86 static dev_t pty_getfree(void); 87 static int pty_alloc_master(struct lwp *, int *, dev_t *, struct mount *); 88 static int pty_alloc_slave(struct lwp *, int *, dev_t, struct mount *); 89 90 void ptmattach(int); 91 92 int 93 pty_getmp(struct lwp *l, struct mount **mpp) 94 { 95 if (ptm == NULL) 96 return EOPNOTSUPP; 97 98 return (*ptm->getmp)(l, mpp); 99 } 100 101 dev_t 102 pty_makedev(char ms, int minor) 103 { 104 return makedev(ms == 't' ? pts_major : ptc_major, minor); 105 } 106 107 108 static dev_t 109 pty_getfree(void) 110 { 111 extern kmutex_t pt_softc_mutex; 112 int i; 113 114 mutex_enter(&pt_softc_mutex); 115 for (i = 0; i < npty; i++) { 116 if (pty_isfree(i, 0)) 117 break; 118 } 119 mutex_exit(&pt_softc_mutex); 120 return pty_makedev('t', i); 121 } 122 123 /* 124 * Hacked up version of vn_open. We _only_ handle ptys and only open 125 * them with FREAD|FWRITE and never deal with creat or stuff like that. 126 * 127 * We need it because we have to fake up root credentials to open the pty. 128 */ 129 int 130 pty_vn_open(struct vnode *vp, struct lwp *l) 131 { 132 int error; 133 134 if (vp->v_type != VCHR) { 135 vput(vp); 136 return EINVAL; 137 } 138 139 error = VOP_OPEN(vp, FREAD|FWRITE, lwp0.l_cred); 140 141 if (error) { 142 vput(vp); 143 return error; 144 } 145 146 vp->v_writecount++; 147 148 return 0; 149 } 150 151 static int 152 pty_alloc_master(struct lwp *l, int *fd, dev_t *dev, struct mount *mp) 153 { 154 int error; 155 struct file *fp; 156 struct vnode *vp; 157 int md; 158 159 if ((error = fd_allocfile(&fp, fd)) != 0) { 160 DPRINTF(("fd_allocfile %d\n", error)); 161 return error; 162 } 163 retry: 164 /* Find and open a free master pty. */ 165 *dev = pty_getfree(); 166 md = minor(*dev); 167 if ((error = pty_check(md)) != 0) { 168 DPRINTF(("pty_check %d\n", error)); 169 goto bad; 170 } 171 if (ptm == NULL) { 172 DPRINTF(("no ptm\n")); 173 error = EOPNOTSUPP; 174 goto bad; 175 } 176 /* 177 * XXX Since PTYFS has now multiple instance support, if we mounted 178 * more than one PTYFS we must check here the ptyfs_used_tbl, to find 179 * out if the ptyfsnode is under the appropriate mount and skip the 180 * node if not, because the pty could has been released, but 181 * ptyfs_reclaim didn't get a chance to release the corresponding 182 * node other mount point yet. 183 * 184 * It's important to have only one mount point's ptyfsnode for each 185 * appropriate device in ptyfs_used_tbl, else we will have a security 186 * problem, because every entry will have access to this device. 187 * 188 * Also we will not have not efficient vnode and memory usage. 189 * You can test this by changing a_recycle from true to false 190 * in ptyfs_inactive. 191 */ 192 if ((error = (*ptm->allocvp)(mp, l, &vp, *dev, 'p')) != 0) { 193 DPRINTF(("pty_allocvp %d\n", error)); 194 goto bad; 195 } 196 197 if ((error = pty_vn_open(vp, l)) != 0) { 198 DPRINTF(("pty_vn_open %d\n", error)); 199 /* 200 * Check if the master open failed because we lost 201 * the race to grab it. 202 */ 203 if (error != EIO) 204 goto bad; 205 error = !pty_isfree(md, 1); 206 DPRINTF(("pty_isfree %d\n", error)); 207 if (error) 208 goto retry; 209 else 210 goto bad; 211 } 212 fp->f_flag = FREAD|FWRITE; 213 fp->f_type = DTYPE_VNODE; 214 fp->f_ops = &vnops; 215 fp->f_data = vp; 216 VOP_UNLOCK(vp); 217 fd_affix(curproc, fp, *fd); 218 return 0; 219 bad: 220 fd_abort(curproc, fp, *fd); 221 return error; 222 } 223 224 int 225 pty_grant_slave(struct lwp *l, dev_t dev, struct mount *mp) 226 { 227 int error; 228 struct vnode *vp; 229 230 /* 231 * Open the slave. 232 * namei -> setattr -> unlock -> revoke -> vrele -> 233 * namei -> open -> unlock 234 * Three stage rocket: 235 * 1. Change the owner and permissions on the slave. 236 * 2. Revoke all the users of the slave. 237 * 3. open the slave. 238 */ 239 if (ptm == NULL) 240 return EOPNOTSUPP; 241 if ((error = (*ptm->allocvp)(mp, l, &vp, dev, 't')) != 0) 242 return error; 243 244 if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { 245 struct vattr vattr; 246 (*ptm->getvattr)(mp, l, &vattr); 247 /* Do the VOP_SETATTR() as root. */ 248 error = VOP_SETATTR(vp, &vattr, lwp0.l_cred); 249 if (error) { 250 DPRINTF(("setattr %d\n", error)); 251 VOP_UNLOCK(vp); 252 vrele(vp); 253 return error; 254 } 255 } 256 VOP_UNLOCK(vp); 257 VOP_REVOKE(vp, REVOKEALL); 258 259 /* 260 * The vnode is useless after the revoke, we need to get it again. 261 */ 262 vrele(vp); 263 return 0; 264 } 265 266 static int 267 pty_alloc_slave(struct lwp *l, int *fd, dev_t dev, struct mount *mp) 268 { 269 int error; 270 struct file *fp; 271 struct vnode *vp; 272 273 /* Grab a filedescriptor for the slave */ 274 if ((error = fd_allocfile(&fp, fd)) != 0) { 275 DPRINTF(("fd_allocfile %d\n", error)); 276 return error; 277 } 278 279 if (ptm == NULL) { 280 error = EOPNOTSUPP; 281 goto bad; 282 } 283 284 if ((error = (*ptm->allocvp)(mp, l, &vp, dev, 't')) != 0) 285 goto bad; 286 if ((error = pty_vn_open(vp, l)) != 0) 287 goto bad; 288 289 fp->f_flag = FREAD|FWRITE; 290 fp->f_type = DTYPE_VNODE; 291 fp->f_ops = &vnops; 292 fp->f_data = vp; 293 VOP_UNLOCK(vp); 294 fd_affix(curproc, fp, *fd); 295 return 0; 296 bad: 297 fd_abort(curproc, fp, *fd); 298 return error; 299 } 300 301 struct ptm_pty * 302 pty_sethandler(struct ptm_pty *nptm) 303 { 304 struct ptm_pty *optm = ptm; 305 ptm = nptm; 306 return optm; 307 } 308 309 int 310 pty_fill_ptmget(struct lwp *l, dev_t dev, int cfd, int sfd, void *data, struct mount *mp) 311 { 312 struct ptmget *ptmg = data; 313 int error; 314 315 if (ptm == NULL) 316 return EOPNOTSUPP; 317 318 ptmg->cfd = cfd == -1 ? minor(dev) : cfd; 319 ptmg->sfd = sfd == -1 ? minor(dev) : sfd; 320 321 error = (*ptm->makename)(mp, l, ptmg->cn, sizeof(ptmg->cn), dev, 'p'); 322 if (error) 323 return error; 324 325 return (*ptm->makename)(mp, l, ptmg->sn, sizeof(ptmg->sn), dev, 't'); 326 } 327 328 void 329 /*ARGSUSED*/ 330 ptmattach(int n) 331 { 332 extern const struct cdevsw pts_cdevsw, ptc_cdevsw; 333 /* find the major and minor of the pty devices */ 334 if ((pts_major = cdevsw_lookup_major(&pts_cdevsw)) == -1) 335 panic("ptmattach: Can't find pty slave in cdevsw"); 336 if ((ptc_major = cdevsw_lookup_major(&ptc_cdevsw)) == -1) 337 panic("ptmattach: Can't find pty master in cdevsw"); 338 #ifdef COMPAT_BSDPTY 339 ptm = &ptm_bsdpty; 340 #endif 341 } 342 343 static int 344 /*ARGSUSED*/ 345 ptmopen(dev_t dev, int flag, int mode, struct lwp *l) 346 { 347 int error; 348 int fd; 349 dev_t ttydev; 350 struct mount *mp; 351 352 switch(minor(dev)) { 353 case 0: /* /dev/ptmx */ 354 case 2: /* /emul/linux/dev/ptmx */ 355 if ((error = pty_getmp(l, &mp)) != 0) 356 return error; 357 if ((error = pty_alloc_master(l, &fd, &ttydev, mp)) != 0) 358 return error; 359 if (minor(dev) == 2) { 360 /* 361 * Linux ptyfs grants the pty right here. 362 * Handle this case here, instead of writing 363 * a new linux module. 364 */ 365 if ((error = pty_grant_slave(l, ttydev, mp)) != 0) { 366 file_t *fp = fd_getfile(fd); 367 if (fp != NULL) { 368 fd_close(fd); 369 } 370 return error; 371 } 372 } 373 curlwp->l_dupfd = fd; 374 return EMOVEFD; 375 case 1: /* /dev/ptm */ 376 return 0; 377 default: 378 return ENODEV; 379 } 380 } 381 382 static int 383 /*ARGSUSED*/ 384 ptmclose(dev_t dev, int flag, int mode, struct lwp *l) 385 { 386 387 return (0); 388 } 389 390 static int 391 /*ARGSUSED*/ 392 ptmioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 393 { 394 int error; 395 dev_t newdev; 396 int cfd, sfd; 397 file_t *fp; 398 struct mount *mp; 399 400 error = 0; 401 switch (cmd) { 402 case TIOCPTMGET: 403 if ((error = pty_getmp(l, &mp)) != 0) 404 return error; 405 406 if ((error = pty_alloc_master(l, &cfd, &newdev, mp)) != 0) 407 return error; 408 409 if ((error = pty_grant_slave(l, newdev, mp)) != 0) 410 goto bad; 411 412 if ((error = pty_alloc_slave(l, &sfd, newdev, mp)) != 0) 413 goto bad; 414 415 /* now, put the indices and names into struct ptmget */ 416 if ((error = pty_fill_ptmget(l, newdev, cfd, sfd, data, mp)) != 0) 417 goto bad2; 418 return 0; 419 default: 420 #ifdef COMPAT_60 421 error = compat_60_ptmioctl(dev, cmd, data, flag, l); 422 if (error != EPASSTHROUGH) 423 return error; 424 #endif /* COMPAT_60 */ 425 DPRINTF(("ptmioctl EINVAL\n")); 426 return EINVAL; 427 } 428 bad2: 429 fp = fd_getfile(sfd); 430 if (fp != NULL) { 431 fd_close(sfd); 432 } 433 bad: 434 fp = fd_getfile(cfd); 435 if (fp != NULL) { 436 fd_close(cfd); 437 } 438 return error; 439 } 440 441 const struct cdevsw ptm_cdevsw = { 442 .d_open = ptmopen, 443 .d_close = ptmclose, 444 .d_read = noread, 445 .d_write = nowrite, 446 .d_ioctl = ptmioctl, 447 .d_stop = nullstop, 448 .d_tty = notty, 449 .d_poll = nopoll, 450 .d_mmap = nommap, 451 .d_kqfilter = nokqfilter, 452 .d_flag = D_TTY 453 }; 454 #endif 455