1 /* $NetBSD: tty_ptm.c,v 1.2 2004/11/13 08:46:46 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 * 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.2 2004/11/13 08:46:46 christos 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/uio.h> 56 #include <sys/filedesc.h> 57 #include <sys/conf.h> 58 #include <sys/poll.h> 59 #include <sys/malloc.h> 60 #include <sys/pty.h> 61 62 #ifdef DEBUG_PTM 63 #define DPRINTF(a) printf a 64 #else 65 #define DPRINTF(a) 66 #endif 67 68 #ifdef NO_DEV_PTM 69 const struct cdevsw ptm_cdevsw = { 70 noopen, noclose, noread, nowrite, noioctl, 71 nostop, notty, nopoll, nommap, nokqfilter, D_TTY 72 }; 73 #else 74 75 static struct ptm_pty *ptm; 76 int pts_major, ptc_major; 77 78 static dev_t pty_getfree(void); 79 static int pty_alloc_master(struct proc *, int *, dev_t *); 80 static int pty_alloc_slave(struct proc *, int *, dev_t); 81 82 void ptmattach(int); 83 84 dev_type_open(ptmopen); 85 dev_type_close(ptmclose); 86 dev_type_ioctl(ptmioctl); 87 88 const struct cdevsw ptm_cdevsw = { 89 ptmopen, ptmclose, noread, nowrite, ptmioctl, 90 nullstop, notty, nopoll, nommap, nokqfilter, D_TTY 91 }; 92 93 dev_t 94 pty_makedev(char ms, int minor) 95 { 96 return makedev(ms == 't' ? pts_major : ptc_major, minor); 97 } 98 99 static dev_t 100 pty_getfree(void) 101 { 102 extern struct simplelock pt_softc_mutex; 103 int i; 104 105 simple_lock(&pt_softc_mutex); 106 for (i = 0; i < npty; i++) { 107 if (pty_isfree(i, 0)) 108 break; 109 } 110 simple_unlock(&pt_softc_mutex); 111 return pty_makedev('t', i); 112 } 113 114 /* 115 * Hacked up version of vn_open. We _only_ handle ptys and only open 116 * them with FREAD|FWRITE and never deal with creat or stuff like that. 117 * 118 * We need it because we have to fake up root credentials to open the pty. 119 */ 120 int 121 pty_vn_open(struct vnode *vp, struct proc *p) 122 { 123 struct ucred *cred; 124 int error; 125 126 if (vp->v_type != VCHR) { 127 vput(vp); 128 return EINVAL; 129 } 130 131 /* 132 * Get us a fresh cred with root privileges. 133 */ 134 cred = crget(); 135 error = VOP_OPEN(vp, FREAD|FWRITE, cred, p); 136 crfree(cred); 137 138 if (error) { 139 vput(vp); 140 return error; 141 } 142 143 vp->v_writecount++; 144 145 return 0; 146 } 147 148 static int 149 pty_alloc_master(struct proc *p, int *fd, dev_t *dev) 150 { 151 int error; 152 struct file *fp; 153 struct vnode *vp; 154 int md; 155 156 if ((error = falloc(p, &fp, fd)) != 0) { 157 DPRINTF(("falloc %d\n", error)); 158 return error; 159 } 160 retry: 161 /* Find and open a free master pty. */ 162 *dev = pty_getfree(); 163 md = minor(*dev); 164 if ((error = pty_check(md)) != 0) { 165 DPRINTF(("pty_check %d\n", error)); 166 goto bad; 167 } 168 if (ptm == NULL) { 169 error = EOPNOTSUPP; 170 goto bad; 171 } 172 if ((error = (*ptm->allocvp)(ptm, p, &vp, *dev, 'p')) != 0) 173 goto bad; 174 175 if ((error = pty_vn_open(vp, p)) != 0) { 176 /* 177 * Check if the master open failed because we lost 178 * the race to grab it. 179 */ 180 if (error != EIO) 181 goto bad; 182 error = !pty_isfree(md, 1); 183 if (error) 184 goto retry; 185 else 186 goto bad; 187 } 188 fp->f_flag = FREAD|FWRITE; 189 fp->f_type = DTYPE_VNODE; 190 fp->f_ops = &vnops; 191 fp->f_data = vp; 192 VOP_UNLOCK(vp, 0); 193 FILE_SET_MATURE(fp); 194 FILE_UNUSE(fp, p); 195 return 0; 196 bad: 197 FILE_UNUSE(fp, p); 198 fdremove(p->p_fd, *fd); 199 ffree(fp); 200 return error; 201 } 202 203 int 204 pty_grant_slave(struct proc *p, dev_t dev) 205 { 206 int error; 207 struct vnode *vp; 208 209 /* 210 * Open the slave. 211 * namei -> setattr -> unlock -> revoke -> vrele -> 212 * namei -> open -> unlock 213 * Three stage rocket: 214 * 1. Change the owner and permissions on the slave. 215 * 2. Revoke all the users of the slave. 216 * 3. open the slave. 217 */ 218 if (ptm == NULL) 219 return EOPNOTSUPP; 220 221 if ((error = (*ptm->allocvp)(ptm, p, &vp, dev, 't')) != 0) 222 return error; 223 224 if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { 225 struct vattr vattr; 226 struct ucred *cred; 227 /* get real uid */ 228 VATTR_NULL(&vattr); 229 vattr.va_uid = p->p_cred->p_ruid; 230 vattr.va_gid = _TTY_GID; 231 vattr.va_mode = S_IRUSR|S_IWUSR|S_IWGRP; 232 /* Get a fake cred to pretend we're root. */ 233 cred = crget(); 234 error = VOP_SETATTR(vp, &vattr, cred, p); 235 crfree(cred); 236 if (error) { 237 DPRINTF(("setattr %d\n", error)); 238 VOP_UNLOCK(vp, 0); 239 vrele(vp); 240 return error; 241 } 242 } 243 VOP_UNLOCK(vp, 0); 244 if (vp->v_usecount > 1 || 245 (vp->v_flag & (VALIASED | VLAYER))) 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 proc *p, int *fd, dev_t dev) 257 { 258 int error; 259 struct file *fp; 260 struct vnode *vp; 261 262 /* Grab a filedescriptor for the slave */ 263 if ((error = falloc(p, &fp, fd)) != 0) { 264 DPRINTF(("falloc %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)(ptm, p, &vp, dev, 't')) != 0) 274 goto bad; 275 if ((error = pty_vn_open(vp, p)) != 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_data = vp; 282 VOP_UNLOCK(vp, 0); 283 FILE_SET_MATURE(fp); 284 FILE_UNUSE(fp, p); 285 return 0; 286 bad: 287 FILE_UNUSE(fp, p); 288 fdremove(p->p_fd, *fd); 289 ffree(fp); 290 return error; 291 } 292 293 struct ptm_pty * 294 pty_sethandler(struct ptm_pty *nptm) 295 { 296 struct ptm_pty *optm = ptm; 297 ptm = nptm; 298 return optm; 299 } 300 301 int 302 pty_fill_ptmget(dev_t dev, int cfd, int sfd, void *data) 303 { 304 struct ptmget *ptmg = data; 305 int error; 306 307 if (ptm == NULL) 308 return EOPNOTSUPP; 309 310 ptmg->cfd = cfd == -1 ? minor(dev) : cfd; 311 ptmg->sfd = sfd == -1 ? minor(dev) : sfd; 312 313 error = (*ptm->makename)(ptm, ptmg->cn, sizeof(ptmg->cn), dev, 'p'); 314 if (error) 315 return error; 316 317 return (*ptm->makename)(ptm, ptmg->sn, sizeof(ptmg->sn), dev, 't'); 318 } 319 320 void 321 /*ARGSUSED*/ 322 ptmattach(int n) 323 { 324 extern const struct cdevsw pts_cdevsw, ptc_cdevsw; 325 /* find the major and minor of the pty devices */ 326 if ((pts_major = cdevsw_lookup_major(&pts_cdevsw)) == -1) 327 panic("ptmattach: Can't find pty slave in cdevsw"); 328 if ((ptc_major = cdevsw_lookup_major(&ptc_cdevsw)) == -1) 329 panic("ptmattach: Can't find pty master in cdevsw"); 330 #ifdef COMPAT_BSDPTY 331 ptm = &ptm_bsdpty; 332 #endif 333 } 334 335 int 336 /*ARGSUSED*/ 337 ptmopen(dev_t dev, int flag, int mode, struct proc *p) 338 { 339 int error; 340 int fd; 341 342 switch(minor(dev)) { 343 case 0: /* /dev/ptmx */ 344 if ((error = pty_alloc_master(p, &fd, &dev)) != 0) 345 return error; 346 curlwp->l_dupfd = fd; 347 return ENXIO; 348 case 1: /* /dev/ptm */ 349 return 0; 350 default: 351 return ENODEV; 352 } 353 } 354 355 int 356 /*ARGSUSED*/ 357 ptmclose(dev_t dev, int flag, int mode, struct proc *p) 358 { 359 return (0); 360 } 361 362 int 363 /*ARGSUSED*/ 364 ptmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 365 { 366 int error; 367 dev_t newdev; 368 int cfd, sfd; 369 struct file *fp; 370 371 error = 0; 372 switch (cmd) { 373 case TIOCPTMGET: 374 if ((error = pty_alloc_master(p, &cfd, &newdev)) != 0) 375 return error; 376 377 if ((error = pty_grant_slave(p, newdev)) != 0) 378 goto bad; 379 380 if ((error = pty_alloc_slave(p, &sfd, newdev)) != 0) 381 goto bad; 382 383 /* now, put the indices and names into struct ptmget */ 384 return pty_fill_ptmget(newdev, cfd, sfd, data); 385 default: 386 DPRINTF(("ptmioctl EINVAL\n")); 387 return EINVAL; 388 } 389 bad: 390 fp = fd_getfile(p->p_fd, cfd); 391 fdremove(p->p_fd, cfd); 392 ffree(fp); 393 return error; 394 } 395 #endif 396