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