1 /* $NetBSD: tty_ptm.c,v 1.33 2014/07/25 08:10:40 dholland 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.33 2014/07/25 08:10:40 dholland 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_discard = nodiscard, 80 .d_flag = D_TTY 81 }; 82 #else 83 84 static struct ptm_pty *ptm; 85 int pts_major, ptc_major; 86 87 static dev_t pty_getfree(void); 88 static int pty_alloc_master(struct lwp *, int *, dev_t *, struct mount *); 89 static int pty_alloc_slave(struct lwp *, int *, dev_t, struct mount *); 90 91 void ptmattach(int); 92 93 int 94 pty_getmp(struct lwp *l, struct mount **mpp) 95 { 96 if (ptm == NULL) 97 return EOPNOTSUPP; 98 99 return (*ptm->getmp)(l, mpp); 100 } 101 102 dev_t 103 pty_makedev(char ms, int minor) 104 { 105 return makedev(ms == 't' ? pts_major : ptc_major, minor); 106 } 107 108 109 static dev_t 110 pty_getfree(void) 111 { 112 extern kmutex_t pt_softc_mutex; 113 int i; 114 115 mutex_enter(&pt_softc_mutex); 116 for (i = 0; i < npty; i++) { 117 if (pty_isfree(i, 0)) 118 break; 119 } 120 mutex_exit(&pt_softc_mutex); 121 return pty_makedev('t', i); 122 } 123 124 /* 125 * Hacked up version of vn_open. We _only_ handle ptys and only open 126 * them with FREAD|FWRITE and never deal with creat or stuff like that. 127 * 128 * We need it because we have to fake up root credentials to open the pty. 129 */ 130 int 131 pty_vn_open(struct vnode *vp, struct lwp *l) 132 { 133 int error; 134 135 if (vp->v_type != VCHR) { 136 vput(vp); 137 return EINVAL; 138 } 139 140 error = VOP_OPEN(vp, FREAD|FWRITE, lwp0.l_cred); 141 142 if (error) { 143 vput(vp); 144 return error; 145 } 146 147 vp->v_writecount++; 148 149 return 0; 150 } 151 152 static int 153 pty_alloc_master(struct lwp *l, int *fd, dev_t *dev, struct mount *mp) 154 { 155 int error; 156 struct file *fp; 157 struct vnode *vp; 158 int md; 159 160 if ((error = fd_allocfile(&fp, fd)) != 0) { 161 DPRINTF(("fd_allocfile %d\n", error)); 162 return error; 163 } 164 retry: 165 /* Find and open a free master pty. */ 166 *dev = pty_getfree(); 167 md = minor(*dev); 168 if ((error = pty_check(md)) != 0) { 169 DPRINTF(("pty_check %d\n", error)); 170 goto bad; 171 } 172 if (ptm == NULL) { 173 DPRINTF(("no ptm\n")); 174 error = EOPNOTSUPP; 175 goto bad; 176 } 177 /* 178 * XXX Since PTYFS has now multiple instance support, if we mounted 179 * more than one PTYFS we must check here the ptyfs_used_tbl, to find 180 * out if the ptyfsnode is under the appropriate mount and skip the 181 * node if not, because the pty could has been released, but 182 * ptyfs_reclaim didn't get a chance to release the corresponding 183 * node other mount point yet. 184 * 185 * It's important to have only one mount point's ptyfsnode for each 186 * appropriate device in ptyfs_used_tbl, else we will have a security 187 * problem, because every entry will have access to this device. 188 * 189 * Also we will not have not efficient vnode and memory usage. 190 * You can test this by changing a_recycle from true to false 191 * in ptyfs_inactive. 192 */ 193 if ((error = (*ptm->allocvp)(mp, l, &vp, *dev, 'p')) != 0) { 194 DPRINTF(("pty_allocvp %d\n", error)); 195 goto bad; 196 } 197 198 if ((error = pty_vn_open(vp, l)) != 0) { 199 DPRINTF(("pty_vn_open %d\n", error)); 200 /* 201 * Check if the master open failed because we lost 202 * the race to grab it. 203 */ 204 if (error != EIO) 205 goto bad; 206 error = !pty_isfree(md, 1); 207 DPRINTF(("pty_isfree %d\n", error)); 208 if (error) 209 goto retry; 210 else 211 goto bad; 212 } 213 fp->f_flag = FREAD|FWRITE; 214 fp->f_type = DTYPE_VNODE; 215 fp->f_ops = &vnops; 216 fp->f_data = vp; 217 VOP_UNLOCK(vp); 218 fd_affix(curproc, fp, *fd); 219 return 0; 220 bad: 221 fd_abort(curproc, fp, *fd); 222 return error; 223 } 224 225 int 226 pty_grant_slave(struct lwp *l, dev_t dev, struct mount *mp) 227 { 228 int error; 229 struct vnode *vp; 230 231 /* 232 * Open the slave. 233 * namei -> setattr -> unlock -> revoke -> vrele -> 234 * namei -> open -> unlock 235 * Three stage rocket: 236 * 1. Change the owner and permissions on the slave. 237 * 2. Revoke all the users of the slave. 238 * 3. open the slave. 239 */ 240 if (ptm == NULL) 241 return EOPNOTSUPP; 242 if ((error = (*ptm->allocvp)(mp, l, &vp, dev, 't')) != 0) 243 return error; 244 245 if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { 246 struct vattr vattr; 247 (*ptm->getvattr)(mp, l, &vattr); 248 /* Do the VOP_SETATTR() as root. */ 249 error = VOP_SETATTR(vp, &vattr, lwp0.l_cred); 250 if (error) { 251 DPRINTF(("setattr %d\n", error)); 252 VOP_UNLOCK(vp); 253 vrele(vp); 254 return error; 255 } 256 } 257 VOP_UNLOCK(vp); 258 VOP_REVOKE(vp, REVOKEALL); 259 260 /* 261 * The vnode is useless after the revoke, we need to get it again. 262 */ 263 vrele(vp); 264 return 0; 265 } 266 267 static int 268 pty_alloc_slave(struct lwp *l, int *fd, dev_t dev, struct mount *mp) 269 { 270 int error; 271 struct file *fp; 272 struct vnode *vp; 273 274 /* Grab a filedescriptor for the slave */ 275 if ((error = fd_allocfile(&fp, fd)) != 0) { 276 DPRINTF(("fd_allocfile %d\n", error)); 277 return error; 278 } 279 280 if (ptm == NULL) { 281 error = EOPNOTSUPP; 282 goto bad; 283 } 284 285 if ((error = (*ptm->allocvp)(mp, l, &vp, dev, 't')) != 0) 286 goto bad; 287 if ((error = pty_vn_open(vp, l)) != 0) 288 goto bad; 289 290 fp->f_flag = FREAD|FWRITE; 291 fp->f_type = DTYPE_VNODE; 292 fp->f_ops = &vnops; 293 fp->f_data = vp; 294 VOP_UNLOCK(vp); 295 fd_affix(curproc, fp, *fd); 296 return 0; 297 bad: 298 fd_abort(curproc, fp, *fd); 299 return error; 300 } 301 302 struct ptm_pty * 303 pty_sethandler(struct ptm_pty *nptm) 304 { 305 struct ptm_pty *optm = ptm; 306 ptm = nptm; 307 return optm; 308 } 309 310 int 311 pty_fill_ptmget(struct lwp *l, dev_t dev, int cfd, int sfd, void *data, struct mount *mp) 312 { 313 struct ptmget *ptmg = data; 314 int error; 315 316 if (ptm == NULL) 317 return EOPNOTSUPP; 318 319 ptmg->cfd = cfd == -1 ? minor(dev) : cfd; 320 ptmg->sfd = sfd == -1 ? minor(dev) : sfd; 321 322 error = (*ptm->makename)(mp, l, ptmg->cn, sizeof(ptmg->cn), dev, 'p'); 323 if (error) 324 return error; 325 326 return (*ptm->makename)(mp, l, ptmg->sn, sizeof(ptmg->sn), dev, 't'); 327 } 328 329 void 330 /*ARGSUSED*/ 331 ptmattach(int n) 332 { 333 extern const struct cdevsw pts_cdevsw, ptc_cdevsw; 334 /* find the major and minor of the pty devices */ 335 if ((pts_major = cdevsw_lookup_major(&pts_cdevsw)) == -1) 336 panic("ptmattach: Can't find pty slave in cdevsw"); 337 if ((ptc_major = cdevsw_lookup_major(&ptc_cdevsw)) == -1) 338 panic("ptmattach: Can't find pty master in cdevsw"); 339 #ifdef COMPAT_BSDPTY 340 ptm = &ptm_bsdpty; 341 #endif 342 } 343 344 static int 345 /*ARGSUSED*/ 346 ptmopen(dev_t dev, int flag, int mode, struct lwp *l) 347 { 348 int error; 349 int fd; 350 dev_t ttydev; 351 struct mount *mp; 352 353 switch(minor(dev)) { 354 case 0: /* /dev/ptmx */ 355 case 2: /* /emul/linux/dev/ptmx */ 356 if ((error = pty_getmp(l, &mp)) != 0) 357 return error; 358 if ((error = pty_alloc_master(l, &fd, &ttydev, mp)) != 0) 359 return error; 360 if (minor(dev) == 2) { 361 /* 362 * Linux ptyfs grants the pty right here. 363 * Handle this case here, instead of writing 364 * a new linux module. 365 */ 366 if ((error = pty_grant_slave(l, ttydev, mp)) != 0) { 367 file_t *fp = fd_getfile(fd); 368 if (fp != NULL) { 369 fd_close(fd); 370 } 371 return error; 372 } 373 } 374 curlwp->l_dupfd = fd; 375 return EMOVEFD; 376 case 1: /* /dev/ptm */ 377 return 0; 378 default: 379 return ENODEV; 380 } 381 } 382 383 static int 384 /*ARGSUSED*/ 385 ptmclose(dev_t dev, int flag, int mode, struct lwp *l) 386 { 387 388 return (0); 389 } 390 391 static int 392 /*ARGSUSED*/ 393 ptmioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 394 { 395 int error; 396 dev_t newdev; 397 int cfd, sfd; 398 file_t *fp; 399 struct mount *mp; 400 401 error = 0; 402 switch (cmd) { 403 case TIOCPTMGET: 404 if ((error = pty_getmp(l, &mp)) != 0) 405 return error; 406 407 if ((error = pty_alloc_master(l, &cfd, &newdev, mp)) != 0) 408 return error; 409 410 if ((error = pty_grant_slave(l, newdev, mp)) != 0) 411 goto bad; 412 413 if ((error = pty_alloc_slave(l, &sfd, newdev, mp)) != 0) 414 goto bad; 415 416 /* now, put the indices and names into struct ptmget */ 417 if ((error = pty_fill_ptmget(l, newdev, cfd, sfd, data, mp)) != 0) 418 goto bad2; 419 return 0; 420 default: 421 #ifdef COMPAT_60 422 error = compat_60_ptmioctl(dev, cmd, data, flag, l); 423 if (error != EPASSTHROUGH) 424 return error; 425 #endif /* COMPAT_60 */ 426 DPRINTF(("ptmioctl EINVAL\n")); 427 return EINVAL; 428 } 429 bad2: 430 fp = fd_getfile(sfd); 431 if (fp != NULL) { 432 fd_close(sfd); 433 } 434 bad: 435 fp = fd_getfile(cfd); 436 if (fp != NULL) { 437 fd_close(cfd); 438 } 439 return error; 440 } 441 442 const struct cdevsw ptm_cdevsw = { 443 .d_open = ptmopen, 444 .d_close = ptmclose, 445 .d_read = noread, 446 .d_write = nowrite, 447 .d_ioctl = ptmioctl, 448 .d_stop = nullstop, 449 .d_tty = notty, 450 .d_poll = nopoll, 451 .d_mmap = nommap, 452 .d_kqfilter = nokqfilter, 453 .d_discard = nodiscard, 454 .d_flag = D_TTY 455 }; 456 #endif 457