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 * $DragonFly: src/sys/kern/tty_tty.c,v 1.19 2007/07/03 17:22:14 dillon Exp $ 38 */ 39 40 /* 41 * Indirect driver for controlling tty. 42 */ 43 44 #include <sys/param.h> 45 #include <sys/systm.h> 46 #include <sys/conf.h> 47 #include <sys/device.h> 48 #include <sys/lock.h> 49 #include <sys/fcntl.h> 50 #include <sys/proc.h> 51 #include <sys/ttycom.h> 52 #include <sys/vnode.h> 53 #include <sys/kernel.h> 54 #include <sys/poll.h> /* XXX: poll args used in KQ filters */ 55 #include <sys/event.h> 56 57 static d_open_t cttyopen; 58 static d_close_t cttyclose; 59 static d_read_t cttyread; 60 static d_write_t cttywrite; 61 static d_ioctl_t cttyioctl; 62 static d_kqfilter_t cttykqfilter; 63 64 static void cttyfilt_detach(struct knote *); 65 static int cttyfilt_read(struct knote *, long); 66 static int cttyfilt_write(struct knote *, long); 67 68 #define CDEV_MAJOR 1 69 static struct dev_ops ctty_ops = { 70 { "ctty", CDEV_MAJOR, D_TTY }, 71 .d_open = cttyopen, 72 .d_close = cttyclose, 73 .d_read = cttyread, 74 .d_write = cttywrite, 75 .d_ioctl = cttyioctl, 76 .d_kqfilter = cttykqfilter 77 }; 78 79 #define cttyvp(p) ((p)->p_flag & P_CONTROLT ? (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(&proc_token); 224 ttyvp = cttyvp(p); 225 if (ttyvp == NULL) { 226 lwkt_reltoken(&proc_token); 227 return (EIO); 228 } 229 /* 230 * Don't allow controlling tty to be set to the controlling tty 231 * (infinite recursion). 232 */ 233 if (ap->a_cmd == TIOCSCTTY) { 234 lwkt_reltoken(&proc_token); 235 return EINVAL; 236 } 237 if (ap->a_cmd == TIOCNOTTY) { 238 if (!SESS_LEADER(p)) { 239 p->p_flag &= ~P_CONTROLT; 240 lwkt_reltoken(&proc_token); 241 return (0); 242 } else { 243 lwkt_reltoken(&proc_token); 244 return (EINVAL); 245 } 246 } 247 lwkt_reltoken(&proc_token); 248 249 return (VOP_IOCTL(ttyvp, ap->a_cmd, ap->a_data, ap->a_fflag, 250 ap->a_cred, ap->a_sysmsg)); 251 } 252 253 static struct filterops cttyfiltops_read = 254 { FILTEROP_ISFD, NULL, cttyfilt_detach, cttyfilt_read }; 255 static struct filterops cttyfiltops_write = 256 { FILTEROP_ISFD, NULL, cttyfilt_detach, cttyfilt_write }; 257 258 static int 259 cttykqfilter(struct dev_kqfilter_args *ap) 260 { 261 cdev_t dev = ap->a_head.a_dev; 262 struct proc *p = curproc; 263 struct knote *kn = ap->a_kn; 264 struct vnode *ttyvp; 265 266 KKASSERT(p); 267 ttyvp = cttyvp(p); 268 269 if (ttyvp != NULL) 270 return (VOP_KQFILTER(ttyvp, kn)); 271 272 ap->a_result = 0; 273 274 switch (kn->kn_filter) { 275 case EVFILT_READ: 276 kn->kn_fop = &cttyfiltops_read; 277 kn->kn_hook = (caddr_t)dev; 278 break; 279 case EVFILT_WRITE: 280 kn->kn_fop = &cttyfiltops_write; 281 kn->kn_hook = (caddr_t)dev; 282 break; 283 default: 284 ap->a_result = EOPNOTSUPP; 285 return (0); 286 } 287 288 return (0); 289 } 290 291 static void 292 cttyfilt_detach(struct knote *kn) {} 293 294 static int 295 cttyfilt_read(struct knote *kn, long hint) 296 { 297 cdev_t dev = (cdev_t)kn->kn_hook; 298 299 if (seltrue(dev, POLLIN | POLLRDNORM)) 300 return (1); 301 302 return (0); 303 } 304 305 static int 306 cttyfilt_write(struct knote *kn, long hint) 307 { 308 cdev_t dev = (cdev_t)kn->kn_hook; 309 310 if (seltrue(dev, POLLOUT | POLLWRNORM)) 311 return (1); 312 313 return (0); 314 } 315 316 static void 317 ctty_drvinit(void *unused __unused) 318 { 319 make_dev(&ctty_ops, 0, 0, 0, 0666, "tty"); 320 } 321 322 SYSINIT(cttydev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,ctty_drvinit,NULL) 323