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