1 /*- 2 * (MPSAFE) 3 * 4 * Copyright (c) 1982, 1986, 1991, 1993 5 * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * @(#)tty_tty.c 8.2 (Berkeley) 9/23/93 32 * $FreeBSD: src/sys/kern/tty_tty.c,v 1.30 1999/09/25 18:24:24 phk Exp $ 33 */ 34 35 /* 36 * Indirect driver for controlling tty. 37 */ 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/conf.h> 42 #include <sys/device.h> 43 #include <sys/lock.h> 44 #include <sys/fcntl.h> 45 #include <sys/proc.h> 46 #include <sys/ttycom.h> 47 #include <sys/vnode.h> 48 #include <sys/kernel.h> 49 #include <sys/poll.h> /* XXX: poll args used in KQ filters */ 50 #include <sys/event.h> 51 52 static d_open_t cttyopen; 53 static d_close_t cttyclose; 54 static d_read_t cttyread; 55 static d_write_t cttywrite; 56 static d_ioctl_t cttyioctl; 57 static d_kqfilter_t cttykqfilter; 58 59 static void cttyfilt_detach(struct knote *); 60 static int cttyfilt_read(struct knote *, long); 61 static int cttyfilt_write(struct knote *, long); 62 63 #define CDEV_MAJOR 1 64 static struct dev_ops ctty_ops = { 65 { "ctty", 0, D_TTY }, 66 .d_open = cttyopen, 67 .d_close = cttyclose, 68 .d_read = cttyread, 69 .d_write = cttywrite, 70 .d_ioctl = cttyioctl, 71 .d_kqfilter = cttykqfilter 72 }; 73 74 #define cttyvp(p) (((p)->p_flags & P_CONTROLT) ? \ 75 (p)->p_session->s_ttyvp : NULL) 76 77 /* 78 * This opens /dev/tty. Because multiple opens of /dev/tty only 79 * generate a single open to the actual tty, the file modes are 80 * locked to FREAD|FWRITE. 81 */ 82 static int 83 cttyopen(struct dev_open_args *ap) 84 { 85 struct proc *p = curproc; 86 struct vnode *ttyvp; 87 int error; 88 89 KKASSERT(p); 90 retry: 91 if ((ttyvp = cttyvp(p)) == NULL) 92 return (ENXIO); 93 if (ttyvp->v_flag & VCTTYISOPEN) 94 return (0); 95 96 /* 97 * Messy interlock, don't let the vnode go away while we try to 98 * lock it and check for race after we might have blocked. 99 * 100 * WARNING! The device open (devfs_spec_open()) temporarily 101 * releases the vnode lock on ttyvp when issuing the 102 * dev_dopen(), which means that the VCTTYISOPEn flag 103 * can race during the VOP_OPEN(). 104 * 105 * If something does race we have to undo our potentially 106 * extra open. 107 */ 108 vhold(ttyvp); 109 vn_lock(ttyvp, LK_EXCLUSIVE | LK_RETRY); 110 if (ttyvp != cttyvp(p) || (ttyvp->v_flag & VCTTYISOPEN)) { 111 kprintf("Warning: cttyopen: race-1 avoided\n"); 112 vn_unlock(ttyvp); 113 vdrop(ttyvp); 114 goto retry; 115 } 116 error = VOP_OPEN(ttyvp, FREAD|FWRITE, ap->a_cred, NULL); 117 if (ttyvp != cttyvp(p) || (ttyvp->v_flag & VCTTYISOPEN)) { 118 kprintf("Warning: cttyopen: race-2 avoided\n"); 119 if (error == 0) 120 VOP_CLOSE(ttyvp, FREAD|FWRITE); 121 vn_unlock(ttyvp); 122 vdrop(ttyvp); 123 goto retry; 124 } 125 if (error == 0) 126 vsetflags(ttyvp, VCTTYISOPEN); 127 vn_unlock(ttyvp); 128 vdrop(ttyvp); 129 return(error); 130 } 131 132 /* 133 * This closes /dev/tty. Because multiple opens of /dev/tty only 134 * generate a single open to the actual tty, the file modes are 135 * locked to FREAD|FWRITE. 136 */ 137 static int 138 cttyclose(struct dev_close_args *ap) 139 { 140 struct proc *p = curproc; 141 struct vnode *ttyvp; 142 int error; 143 144 KKASSERT(p); 145 retry: 146 /* 147 * The tty may have been TIOCNOTTY'd, don't return an 148 * error on close. We just have nothing to do. 149 */ 150 if ((ttyvp = cttyvp(p)) == NULL) 151 return(0); 152 if (ttyvp->v_flag & VCTTYISOPEN) { 153 /* 154 * Avoid a nasty race if we block while getting the lock. 155 */ 156 vref(ttyvp); 157 error = vn_lock(ttyvp, LK_EXCLUSIVE | LK_RETRY); 158 if (error) { 159 vrele(ttyvp); 160 goto retry; 161 } 162 if (ttyvp != cttyvp(p) || (ttyvp->v_flag & VCTTYISOPEN) == 0) { 163 kprintf("Warning: cttyclose: race avoided\n"); 164 vn_unlock(ttyvp); 165 vrele(ttyvp); 166 goto retry; 167 } 168 vclrflags(ttyvp, VCTTYISOPEN); 169 error = VOP_CLOSE(ttyvp, FREAD|FWRITE); 170 vn_unlock(ttyvp); 171 vrele(ttyvp); 172 } else { 173 error = 0; 174 } 175 return(error); 176 } 177 178 /* 179 * Read from the controlling terminal (/dev/tty). The tty is refed as 180 * of the cttyvp(), but the ref can get ripped out from under us if 181 * the controlling terminal is revoked while we are blocked on the lock, 182 * so use vget() instead of vn_lock(). 183 */ 184 static int 185 cttyread(struct dev_read_args *ap) 186 { 187 struct proc *p = curproc; 188 struct vnode *ttyvp; 189 int error; 190 191 KKASSERT(p); 192 ttyvp = cttyvp(p); 193 if (ttyvp == NULL) 194 return (EIO); 195 if ((error = vget(ttyvp, LK_EXCLUSIVE | LK_RETRY)) == 0) { 196 error = VOP_READ(ttyvp, ap->a_uio, ap->a_ioflag, NOCRED); 197 vput(ttyvp); 198 } 199 return (error); 200 } 201 202 /* 203 * Read from the controlling terminal (/dev/tty). The tty is refed as 204 * of the cttyvp(), but the ref can get ripped out from under us if 205 * the controlling terminal is revoked while we are blocked on the lock, 206 * so use vget() instead of vn_lock(). 207 */ 208 static int 209 cttywrite(struct dev_write_args *ap) 210 { 211 struct proc *p = curproc; 212 struct vnode *ttyvp; 213 int error; 214 215 KKASSERT(p); 216 ttyvp = cttyvp(p); 217 if (ttyvp == NULL) 218 return (EIO); 219 if ((error = vget(ttyvp, LK_EXCLUSIVE | LK_RETRY)) == 0) { 220 error = VOP_WRITE(ttyvp, ap->a_uio, ap->a_ioflag, NOCRED); 221 vput(ttyvp); 222 } 223 return (error); 224 } 225 226 /*ARGSUSED*/ 227 static int 228 cttyioctl(struct dev_ioctl_args *ap) 229 { 230 struct vnode *ttyvp; 231 struct proc *p = curproc; 232 233 KKASSERT(p); 234 lwkt_gettoken(&p->p_token); 235 lwkt_gettoken(&proc_token); 236 ttyvp = cttyvp(p); 237 if (ttyvp == NULL) { 238 lwkt_reltoken(&proc_token); 239 lwkt_reltoken(&p->p_token); 240 return (EIO); 241 } 242 /* 243 * Don't allow controlling tty to be set to the controlling tty 244 * (infinite recursion). 245 */ 246 if (ap->a_cmd == TIOCSCTTY) { 247 lwkt_reltoken(&proc_token); 248 lwkt_reltoken(&p->p_token); 249 return EINVAL; 250 } 251 if (ap->a_cmd == TIOCNOTTY) { 252 if (!SESS_LEADER(p)) { 253 p->p_flags &= ~P_CONTROLT; 254 lwkt_reltoken(&proc_token); 255 lwkt_reltoken(&p->p_token); 256 return (0); 257 } else { 258 lwkt_reltoken(&proc_token); 259 lwkt_reltoken(&p->p_token); 260 return (EINVAL); 261 } 262 } 263 lwkt_reltoken(&proc_token); 264 lwkt_reltoken(&p->p_token); 265 266 return (VOP_IOCTL(ttyvp, ap->a_cmd, ap->a_data, ap->a_fflag, 267 ap->a_cred, ap->a_sysmsg)); 268 } 269 270 static struct filterops cttyfiltops_read = 271 { FILTEROP_ISFD, NULL, cttyfilt_detach, cttyfilt_read }; 272 static struct filterops cttyfiltops_write = 273 { FILTEROP_ISFD, NULL, cttyfilt_detach, cttyfilt_write }; 274 275 static int 276 cttykqfilter(struct dev_kqfilter_args *ap) 277 { 278 cdev_t dev = ap->a_head.a_dev; 279 struct proc *p = curproc; 280 struct knote *kn = ap->a_kn; 281 struct vnode *ttyvp; 282 283 KKASSERT(p); 284 ttyvp = cttyvp(p); 285 286 if (ttyvp != NULL) 287 return (VOP_KQFILTER(ttyvp, kn)); 288 289 ap->a_result = 0; 290 291 switch (kn->kn_filter) { 292 case EVFILT_READ: 293 kn->kn_fop = &cttyfiltops_read; 294 kn->kn_hook = (caddr_t)dev; 295 break; 296 case EVFILT_WRITE: 297 kn->kn_fop = &cttyfiltops_write; 298 kn->kn_hook = (caddr_t)dev; 299 break; 300 default: 301 ap->a_result = EOPNOTSUPP; 302 return (0); 303 } 304 305 return (0); 306 } 307 308 static void 309 cttyfilt_detach(struct knote *kn) {} 310 311 static int 312 cttyfilt_read(struct knote *kn, long hint) 313 { 314 cdev_t dev = (cdev_t)kn->kn_hook; 315 316 if (seltrue(dev, POLLIN | POLLRDNORM)) 317 return (1); 318 319 return (0); 320 } 321 322 static int 323 cttyfilt_write(struct knote *kn, long hint) 324 { 325 cdev_t dev = (cdev_t)kn->kn_hook; 326 327 if (seltrue(dev, POLLOUT | POLLWRNORM)) 328 return (1); 329 330 return (0); 331 } 332 333 static void 334 ctty_drvinit(void *unused __unused) 335 { 336 make_dev(&ctty_ops, 0, 0, 0, 0666, "tty"); 337 } 338 339 SYSINIT(cttydev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,ctty_drvinit,NULL) 340