1 /* $NetBSD: tty_ptm.c,v 1.43 2021/06/29 22:40:53 dholland Exp $ */ 2 3 /*- 4 * Copyright (c) 2004, 2020 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.43 2021/06/29 22:40:53 dholland Exp $"); 31 32 #ifdef _KERNEL_OPT 33 #include "opt_compat_netbsd.h" 34 #include "opt_ptm.h" 35 #endif 36 37 /* pty multiplexor driver /dev/ptm{,x} */ 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/ioctl.h> 42 #include <sys/proc.h> 43 #include <sys/tty.h> 44 #include <sys/stat.h> 45 #include <sys/file.h> 46 #include <sys/uio.h> 47 #include <sys/kernel.h> 48 #include <sys/vnode.h> 49 #include <sys/namei.h> 50 #include <sys/signalvar.h> 51 #include <sys/filedesc.h> 52 #include <sys/conf.h> 53 #include <sys/poll.h> 54 #include <sys/pty.h> 55 #include <sys/kauth.h> 56 #include <sys/compat_stub.h> 57 58 #include <miscfs/specfs/specdev.h> 59 60 #include <compat/sys/ttycom.h> 61 62 #include "ioconf.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 .d_open = noopen, 73 .d_close = noclose, 74 .d_read = noread, 75 .d_write = nowrite, 76 .d_ioctl = noioctl, 77 .d_stop = nostop, 78 .d_tty = notty, 79 .d_poll = nopoll, 80 .d_mmap = nommap, 81 .d_kqfilter = nokqfilter, 82 .d_discard = nodiscard, 83 .d_flag = D_TTY 84 }; 85 #else 86 87 int pts_major, ptc_major; 88 89 static dev_t pty_getfree(void); 90 static int pty_alloc_master(struct lwp *, int *, dev_t *, struct mount *); 91 static int pty_alloc_slave(struct lwp *, int *, dev_t, struct mount *); 92 static int pty_vn_open(struct vnode *, struct lwp *); 93 94 int 95 pty_getmp(struct lwp *l, struct mount **mpp) 96 { 97 if (ptm == NULL) 98 return EOPNOTSUPP; 99 100 return (*ptm->getmp)(l, mpp); 101 } 102 103 dev_t 104 pty_makedev(char ms, int minor) 105 { 106 return makedev(ms == 't' ? pts_major : ptc_major, minor); 107 } 108 109 110 static dev_t 111 pty_getfree(void) 112 { 113 extern kmutex_t pt_softc_mutex; 114 int i; 115 116 mutex_enter(&pt_softc_mutex); 117 for (i = 0; i < npty; i++) { 118 if (pty_isfree(i, 0)) 119 break; 120 } 121 mutex_exit(&pt_softc_mutex); 122 return pty_makedev('t', i); 123 } 124 125 /* 126 * Hacked up version of vn_open. We _only_ handle ptys and only open 127 * them with FREAD|FWRITE and never deal with creat or stuff like that. 128 * 129 * We need it because we have to fake up root credentials to open the pty. 130 */ 131 int 132 pty_vn_open(struct vnode *vp, struct lwp *l) 133 { 134 int error; 135 136 if (vp->v_type != VCHR) { 137 vput(vp); 138 return EINVAL; 139 } 140 141 error = VOP_OPEN(vp, FREAD|FWRITE, lwp0.l_cred); 142 143 if (error) { 144 /* only ptys mean we can't get these */ 145 KASSERT(error != EDUPFD && error != EMOVEFD); 146 vput(vp); 147 return error; 148 } 149 150 mutex_enter(vp->v_interlock); 151 vp->v_writecount++; 152 mutex_exit(vp->v_interlock); 153 154 return 0; 155 } 156 157 static int 158 pty_alloc_master(struct lwp *l, int *fd, dev_t *dev, struct mount *mp) 159 { 160 int error; 161 struct file *fp; 162 struct vnode *vp; 163 int md; 164 165 if ((error = fd_allocfile(&fp, fd)) != 0) { 166 DPRINTF(("fd_allocfile %d\n", error)); 167 return error; 168 } 169 retry: 170 /* Find and open a free master pty. */ 171 *dev = pty_getfree(); 172 md = minor(*dev); 173 if ((error = pty_check(md)) != 0) { 174 DPRINTF(("pty_check %d\n", error)); 175 goto bad; 176 } 177 if (ptm == NULL) { 178 DPRINTF(("no ptm\n")); 179 error = EOPNOTSUPP; 180 goto bad; 181 } 182 if ((error = (*ptm->allocvp)(mp, l, &vp, *dev, 'p')) != 0) { 183 DPRINTF(("pty_allocvp %d\n", error)); 184 goto bad; 185 } 186 187 if ((error = pty_vn_open(vp, l)) != 0) { 188 DPRINTF(("pty_vn_open %d\n", error)); 189 /* 190 * Check if the master open failed because we lost 191 * the race to grab it. 192 */ 193 if (error != EIO) 194 goto bad; 195 error = !pty_isfree(md, 1); 196 DPRINTF(("pty_isfree %d\n", error)); 197 if (error) 198 goto retry; 199 else 200 goto bad; 201 } 202 fp->f_flag = FREAD|FWRITE; 203 fp->f_type = DTYPE_VNODE; 204 fp->f_ops = &vnops; 205 fp->f_vnode = vp; 206 VOP_UNLOCK(vp); 207 fd_affix(curproc, fp, *fd); 208 return 0; 209 bad: 210 fd_abort(curproc, fp, *fd); 211 return error; 212 } 213 214 int 215 pty_grant_slave(struct lwp *l, dev_t dev, struct mount *mp) 216 { 217 int error; 218 struct vnode *vp; 219 220 /* 221 * Open the slave. 222 * namei -> setattr -> unlock -> revoke -> vrele -> 223 * namei -> open -> unlock 224 * Three stage rocket: 225 * 1. Change the owner and permissions on the slave. 226 * 2. Revoke all the users of the slave. 227 * 3. open the slave. 228 */ 229 if (ptm == NULL) 230 return EOPNOTSUPP; 231 if ((error = (*ptm->allocvp)(mp, l, &vp, dev, 't')) != 0) 232 return error; 233 234 if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { 235 struct vattr vattr; 236 (*ptm->getvattr)(mp, l, &vattr); 237 /* Do the VOP_SETATTR() as root. */ 238 error = VOP_SETATTR(vp, &vattr, lwp0.l_cred); 239 if (error) { 240 DPRINTF(("setattr %d\n", error)); 241 vput(vp); 242 return error; 243 } 244 } 245 VOP_UNLOCK(vp); 246 VOP_REVOKE(vp, REVOKEALL); 247 248 /* 249 * The vnode is useless after the revoke, we need to get it again. 250 */ 251 vrele(vp); 252 return 0; 253 } 254 255 static int 256 pty_alloc_slave(struct lwp *l, int *fd, dev_t dev, struct mount *mp) 257 { 258 int error; 259 struct file *fp; 260 struct vnode *vp; 261 262 /* Grab a filedescriptor for the slave */ 263 if ((error = fd_allocfile(&fp, fd)) != 0) { 264 DPRINTF(("fd_allocfile %d\n", error)); 265 return error; 266 } 267 268 if (ptm == NULL) { 269 error = EOPNOTSUPP; 270 goto bad; 271 } 272 273 if ((error = (*ptm->allocvp)(mp, l, &vp, dev, 't')) != 0) 274 goto bad; 275 if ((error = pty_vn_open(vp, l)) != 0) 276 goto bad; 277 278 fp->f_flag = FREAD|FWRITE; 279 fp->f_type = DTYPE_VNODE; 280 fp->f_ops = &vnops; 281 fp->f_vnode = vp; 282 VOP_UNLOCK(vp); 283 fd_affix(curproc, fp, *fd); 284 return 0; 285 bad: 286 fd_abort(curproc, fp, *fd); 287 return error; 288 } 289 290 struct ptm_pty * 291 pty_sethandler(struct ptm_pty *nptm) 292 { 293 struct ptm_pty *optm = ptm; 294 ptm = nptm; 295 return optm; 296 } 297 298 int 299 pty_fill_ptmget(struct lwp *l, dev_t dev, int cfd, int sfd, void *data, struct mount *mp) 300 { 301 struct ptmget *ptmg = data; 302 int error; 303 304 if (ptm == NULL) 305 return EOPNOTSUPP; 306 307 ptmg->cfd = cfd == -1 ? minor(dev) : cfd; 308 ptmg->sfd = sfd == -1 ? minor(dev) : sfd; 309 310 error = (*ptm->makename)(mp, l, ptmg->cn, sizeof(ptmg->cn), dev, 'p'); 311 if (error) 312 return error; 313 314 return (*ptm->makename)(mp, l, ptmg->sn, sizeof(ptmg->sn), dev, 't'); 315 } 316 317 void 318 /*ARGSUSED*/ 319 ptmattach(int n) 320 { 321 extern const struct cdevsw pts_cdevsw, ptc_cdevsw; 322 /* find the major and minor of the pty devices */ 323 if ((pts_major = cdevsw_lookup_major(&pts_cdevsw)) == -1) 324 panic("ptmattach: Can't find pty slave in cdevsw"); 325 if ((ptc_major = cdevsw_lookup_major(&ptc_cdevsw)) == -1) 326 panic("ptmattach: Can't find pty master in cdevsw"); 327 #ifdef COMPAT_BSDPTY 328 ptm = &ptm_bsdpty; 329 #endif 330 } 331 332 static int 333 /*ARGSUSED*/ 334 ptmopen(dev_t dev, int flag, int mode, struct lwp *l) 335 { 336 int error; 337 int fd; 338 dev_t ttydev; 339 struct mount *mp; 340 341 switch(minor(dev)) { 342 case 0: /* /dev/ptmx */ 343 case 2: /* /emul/linux/dev/ptmx */ 344 if ((error = pty_getmp(l, &mp)) != 0) 345 return error; 346 if ((error = pty_alloc_master(l, &fd, &ttydev, mp)) != 0) 347 return error; 348 if (minor(dev) == 2) { 349 /* 350 * Linux ptyfs grants the pty right here. 351 * Handle this case here, instead of writing 352 * a new linux module. 353 */ 354 if ((error = pty_grant_slave(l, ttydev, mp)) != 0) { 355 file_t *fp = fd_getfile(fd); 356 if (fp != NULL) { 357 fd_close(fd); 358 } 359 return error; 360 } 361 } 362 curlwp->l_dupfd = fd; 363 return EMOVEFD; 364 case 1: /* /dev/ptm */ 365 return 0; 366 default: 367 return ENODEV; 368 } 369 } 370 371 static int 372 /*ARGSUSED*/ 373 ptmclose(dev_t dev, int flag, int mode, struct lwp *l) 374 { 375 376 return (0); 377 } 378 379 static int 380 /*ARGSUSED*/ 381 ptmioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 382 { 383 int error; 384 dev_t newdev; 385 int cfd, sfd; 386 file_t *fp; 387 struct mount *mp; 388 389 error = 0; 390 switch (cmd) { 391 case TIOCPTMGET: 392 if ((error = pty_getmp(l, &mp)) != 0) 393 return error; 394 395 if ((error = pty_alloc_master(l, &cfd, &newdev, mp)) != 0) 396 return error; 397 398 if ((error = pty_grant_slave(l, newdev, mp)) != 0) 399 goto bad; 400 401 if ((error = pty_alloc_slave(l, &sfd, newdev, mp)) != 0) 402 goto bad; 403 404 /* now, put the indices and names into struct ptmget */ 405 if ((error = pty_fill_ptmget(l, newdev, cfd, sfd, data, mp)) != 0) 406 goto bad2; 407 return 0; 408 default: 409 MODULE_HOOK_CALL(tty_ptmioctl_60_hook, 410 (dev, cmd, data, flag, l), EPASSTHROUGH, error); 411 if (error != EPASSTHROUGH) 412 return error; 413 DPRINTF(("ptmioctl EINVAL\n")); 414 return EINVAL; 415 } 416 bad2: 417 fp = fd_getfile(sfd); 418 if (fp != NULL) { 419 fd_close(sfd); 420 } 421 bad: 422 fp = fd_getfile(cfd); 423 if (fp != NULL) { 424 fd_close(cfd); 425 } 426 return error; 427 } 428 429 const struct cdevsw ptm_cdevsw = { 430 .d_open = ptmopen, 431 .d_close = ptmclose, 432 .d_read = noread, 433 .d_write = nowrite, 434 .d_ioctl = ptmioctl, 435 .d_stop = nullstop, 436 .d_tty = notty, 437 .d_poll = nopoll, 438 .d_mmap = nommap, 439 .d_kqfilter = nokqfilter, 440 .d_discard = nodiscard, 441 .d_flag = D_TTY 442 }; 443 #endif 444