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_flag & P_CONTROLT ? (p)->p_session->s_ttyvp : NULL) 79 80 /* 81 * This opens /dev/tty. Because multiple opens of /dev/tty only 82 * generate a single open to the actual tty, the file modes are 83 * locked to FREAD|FWRITE. 84 */ 85 static int 86 cttyopen(struct dev_open_args *ap) 87 { 88 struct proc *p = curproc; 89 struct vnode *ttyvp; 90 int error; 91 92 KKASSERT(p); 93 retry: 94 if ((ttyvp = cttyvp(p)) == NULL) 95 return (ENXIO); 96 if (ttyvp->v_flag & VCTTYISOPEN) 97 return (0); 98 99 /* 100 * Messy interlock, don't let the vnode go away while we try to 101 * lock it and check for race after we might have blocked. 102 */ 103 vhold(ttyvp); 104 vn_lock(ttyvp, LK_EXCLUSIVE | LK_RETRY); 105 if (ttyvp != cttyvp(p) || (ttyvp->v_flag & VCTTYISOPEN)) { 106 kprintf("Warning: cttyopen: race avoided\n"); 107 vn_unlock(ttyvp); 108 vdrop(ttyvp); 109 goto retry; 110 } 111 vsetflags(ttyvp, VCTTYISOPEN); 112 error = VOP_OPEN(ttyvp, FREAD|FWRITE, ap->a_cred, NULL); 113 if (error) 114 vclrflags(ttyvp, VCTTYISOPEN); 115 vn_unlock(ttyvp); 116 vdrop(ttyvp); 117 return(error); 118 } 119 120 /* 121 * This closes /dev/tty. Because multiple opens of /dev/tty only 122 * generate a single open to the actual tty, the file modes are 123 * locked to FREAD|FWRITE. 124 */ 125 static int 126 cttyclose(struct dev_close_args *ap) 127 { 128 struct proc *p = curproc; 129 struct vnode *ttyvp; 130 int error; 131 132 KKASSERT(p); 133 retry: 134 /* 135 * The tty may have been TIOCNOTTY'd, don't return an 136 * error on close. We just have nothing to do. 137 */ 138 if ((ttyvp = cttyvp(p)) == NULL) 139 return(0); 140 if (ttyvp->v_flag & VCTTYISOPEN) { 141 /* 142 * Avoid a nasty race if we block while getting the lock. 143 */ 144 vref(ttyvp); 145 error = vn_lock(ttyvp, LK_EXCLUSIVE | LK_RETRY); 146 if (error) { 147 vrele(ttyvp); 148 goto retry; 149 } 150 if (ttyvp != cttyvp(p) || (ttyvp->v_flag & VCTTYISOPEN) == 0) { 151 kprintf("Warning: cttyclose: race avoided\n"); 152 vn_unlock(ttyvp); 153 vrele(ttyvp); 154 goto retry; 155 } 156 vclrflags(ttyvp, VCTTYISOPEN); 157 error = VOP_CLOSE(ttyvp, FREAD|FWRITE); 158 vn_unlock(ttyvp); 159 vrele(ttyvp); 160 } else { 161 error = 0; 162 } 163 return(error); 164 } 165 166 /* 167 * Read from the controlling terminal (/dev/tty). The tty is refed as 168 * of the cttyvp(), but the ref can get ripped out from under us if 169 * the controlling terminal is revoked while we are blocked on the lock, 170 * so use vget() instead of vn_lock(). 171 */ 172 static int 173 cttyread(struct dev_read_args *ap) 174 { 175 struct proc *p = curproc; 176 struct vnode *ttyvp; 177 int error; 178 179 KKASSERT(p); 180 ttyvp = cttyvp(p); 181 if (ttyvp == NULL) 182 return (EIO); 183 if ((error = vget(ttyvp, LK_EXCLUSIVE | LK_RETRY)) == 0) { 184 error = VOP_READ(ttyvp, ap->a_uio, ap->a_ioflag, NOCRED); 185 vput(ttyvp); 186 } 187 return (error); 188 } 189 190 /* 191 * Read from the controlling terminal (/dev/tty). The tty is refed as 192 * of the cttyvp(), but the ref can get ripped out from under us if 193 * the controlling terminal is revoked while we are blocked on the lock, 194 * so use vget() instead of vn_lock(). 195 */ 196 static int 197 cttywrite(struct dev_write_args *ap) 198 { 199 struct proc *p = curproc; 200 struct vnode *ttyvp; 201 int error; 202 203 KKASSERT(p); 204 ttyvp = cttyvp(p); 205 if (ttyvp == NULL) 206 return (EIO); 207 if ((error = vget(ttyvp, LK_EXCLUSIVE | LK_RETRY)) == 0) { 208 error = VOP_WRITE(ttyvp, ap->a_uio, ap->a_ioflag, NOCRED); 209 vput(ttyvp); 210 } 211 return (error); 212 } 213 214 /*ARGSUSED*/ 215 static int 216 cttyioctl(struct dev_ioctl_args *ap) 217 { 218 struct vnode *ttyvp; 219 struct proc *p = curproc; 220 221 KKASSERT(p); 222 lwkt_gettoken(&p->p_token); 223 lwkt_gettoken(&proc_token); 224 ttyvp = cttyvp(p); 225 if (ttyvp == NULL) { 226 lwkt_reltoken(&proc_token); 227 lwkt_reltoken(&p->p_token); 228 return (EIO); 229 } 230 /* 231 * Don't allow controlling tty to be set to the controlling tty 232 * (infinite recursion). 233 */ 234 if (ap->a_cmd == TIOCSCTTY) { 235 lwkt_reltoken(&proc_token); 236 lwkt_reltoken(&p->p_token); 237 return EINVAL; 238 } 239 if (ap->a_cmd == TIOCNOTTY) { 240 if (!SESS_LEADER(p)) { 241 p->p_flag &= ~P_CONTROLT; 242 lwkt_reltoken(&proc_token); 243 lwkt_reltoken(&p->p_token); 244 return (0); 245 } else { 246 lwkt_reltoken(&proc_token); 247 lwkt_reltoken(&p->p_token); 248 return (EINVAL); 249 } 250 } 251 lwkt_reltoken(&proc_token); 252 lwkt_reltoken(&p->p_token); 253 254 return (VOP_IOCTL(ttyvp, ap->a_cmd, ap->a_data, ap->a_fflag, 255 ap->a_cred, ap->a_sysmsg)); 256 } 257 258 static struct filterops cttyfiltops_read = 259 { FILTEROP_ISFD, NULL, cttyfilt_detach, cttyfilt_read }; 260 static struct filterops cttyfiltops_write = 261 { FILTEROP_ISFD, NULL, cttyfilt_detach, cttyfilt_write }; 262 263 static int 264 cttykqfilter(struct dev_kqfilter_args *ap) 265 { 266 cdev_t dev = ap->a_head.a_dev; 267 struct proc *p = curproc; 268 struct knote *kn = ap->a_kn; 269 struct vnode *ttyvp; 270 271 KKASSERT(p); 272 ttyvp = cttyvp(p); 273 274 if (ttyvp != NULL) 275 return (VOP_KQFILTER(ttyvp, kn)); 276 277 ap->a_result = 0; 278 279 switch (kn->kn_filter) { 280 case EVFILT_READ: 281 kn->kn_fop = &cttyfiltops_read; 282 kn->kn_hook = (caddr_t)dev; 283 break; 284 case EVFILT_WRITE: 285 kn->kn_fop = &cttyfiltops_write; 286 kn->kn_hook = (caddr_t)dev; 287 break; 288 default: 289 ap->a_result = EOPNOTSUPP; 290 return (0); 291 } 292 293 return (0); 294 } 295 296 static void 297 cttyfilt_detach(struct knote *kn) {} 298 299 static int 300 cttyfilt_read(struct knote *kn, long hint) 301 { 302 cdev_t dev = (cdev_t)kn->kn_hook; 303 304 if (seltrue(dev, POLLIN | POLLRDNORM)) 305 return (1); 306 307 return (0); 308 } 309 310 static int 311 cttyfilt_write(struct knote *kn, long hint) 312 { 313 cdev_t dev = (cdev_t)kn->kn_hook; 314 315 if (seltrue(dev, POLLOUT | POLLWRNORM)) 316 return (1); 317 318 return (0); 319 } 320 321 static void 322 ctty_drvinit(void *unused __unused) 323 { 324 make_dev(&ctty_ops, 0, 0, 0, 0666, "tty"); 325 } 326 327 SYSINIT(cttydev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,ctty_drvinit,NULL) 328