kernel/slaballoc: Separate some parts better that need INVARIANTS.
[dragonfly.git] / sys / kern / tty_tty.c
bloba226a4881247ce13d7055b93336285fb63b6150e
1 /*-
2 * (MPSAFE)
4 * Copyright (c) 1982, 1986, 1991, 1993
5 * The Regents of the University of California. All rights reserved.
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.
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.
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 $
36 * Indirect driver for controlling tty.
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>
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;
59 static void cttyfilt_detach(struct knote *);
60 static int cttyfilt_read(struct knote *, long);
61 static int cttyfilt_write(struct knote *, long);
63 #define CDEV_MAJOR 1
64 static struct dev_ops ctty_ops = {
65 { "ctty", 0, D_TTY | D_MPSAFE },
66 .d_open = cttyopen,
67 .d_close = cttyclose,
68 .d_read = cttyread,
69 .d_write = cttywrite,
70 .d_ioctl = cttyioctl,
71 .d_kqfilter = cttykqfilter
74 #define cttyvp(p) (((p)->p_flags & P_CONTROLT) ? \
75 (p)->p_session->s_ttyvp : NULL)
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.
82 static int
83 cttyopen(struct dev_open_args *ap)
85 struct proc *p = curproc;
86 struct vnode *ttyvp;
87 int error;
89 KKASSERT(p);
90 retry:
91 if ((ttyvp = cttyvp(p)) == NULL)
92 return (ENXIO);
93 if (ttyvp->v_flag & VCTTYISOPEN)
94 return (0);
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.
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().
105 * If something does race we have to undo our potentially
106 * extra open.
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;
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, NULL);
121 vn_unlock(ttyvp);
122 vdrop(ttyvp);
123 goto retry;
125 if (error == 0)
126 vsetflags(ttyvp, VCTTYISOPEN);
127 vn_unlock(ttyvp);
128 vdrop(ttyvp);
129 return(error);
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.
137 static int
138 cttyclose(struct dev_close_args *ap)
140 struct proc *p = curproc;
141 struct vnode *ttyvp;
142 int error;
144 KKASSERT(p);
145 retry:
147 * The tty may have been TIOCNOTTY'd, don't return an
148 * error on close. We just have nothing to do.
150 if ((ttyvp = cttyvp(p)) == NULL)
151 return(0);
152 if (ttyvp->v_flag & VCTTYISOPEN) {
154 * Avoid a nasty race if we block while getting the lock.
156 vref(ttyvp);
157 error = vn_lock(ttyvp, LK_EXCLUSIVE | LK_RETRY |
158 LK_FAILRECLAIM);
159 if (error) {
160 vrele(ttyvp);
161 goto retry;
163 if (ttyvp != cttyvp(p) || (ttyvp->v_flag & VCTTYISOPEN) == 0) {
164 kprintf("Warning: cttyclose: race avoided\n");
165 vn_unlock(ttyvp);
166 vrele(ttyvp);
167 goto retry;
169 vclrflags(ttyvp, VCTTYISOPEN);
170 error = VOP_CLOSE(ttyvp, FREAD|FWRITE, NULL);
171 vn_unlock(ttyvp);
172 vrele(ttyvp);
173 } else {
174 error = 0;
176 return(error);
180 * Read from the controlling terminal (/dev/tty). The tty is refed as
181 * of the cttyvp(), but the ref can get ripped out from under us if
182 * the controlling terminal is revoked while we are blocked on the lock,
183 * so use vget() instead of vn_lock().
185 static int
186 cttyread(struct dev_read_args *ap)
188 struct proc *p = curproc;
189 struct vnode *ttyvp;
190 int error;
192 KKASSERT(p);
193 ttyvp = cttyvp(p);
194 if (ttyvp == NULL)
195 return (EIO);
196 if ((error = vget(ttyvp, LK_EXCLUSIVE | LK_RETRY)) == 0) {
197 error = VOP_READ(ttyvp, ap->a_uio, ap->a_ioflag, NOCRED);
198 vput(ttyvp);
200 return (error);
204 * Read from the controlling terminal (/dev/tty). The tty is refed as
205 * of the cttyvp(), but the ref can get ripped out from under us if
206 * the controlling terminal is revoked while we are blocked on the lock,
207 * so use vget() instead of vn_lock().
209 static int
210 cttywrite(struct dev_write_args *ap)
212 struct proc *p = curproc;
213 struct vnode *ttyvp;
214 int error;
216 KKASSERT(p);
217 ttyvp = cttyvp(p);
218 if (ttyvp == NULL)
219 return (EIO);
220 if ((error = vget(ttyvp, LK_EXCLUSIVE | LK_RETRY)) == 0) {
221 error = VOP_WRITE(ttyvp, ap->a_uio, ap->a_ioflag, NOCRED);
222 vput(ttyvp);
224 return (error);
227 /*ARGSUSED*/
228 static int
229 cttyioctl(struct dev_ioctl_args *ap)
231 struct vnode *ttyvp;
232 struct proc *p = curproc;
234 KKASSERT(p);
235 lwkt_gettoken(&p->p_token);
236 ttyvp = cttyvp(p);
237 if (ttyvp == NULL) {
238 lwkt_reltoken(&p->p_token);
239 return (EIO);
242 * Don't allow controlling tty to be set to the controlling tty
243 * (infinite recursion).
245 if (ap->a_cmd == TIOCSCTTY) {
246 lwkt_reltoken(&p->p_token);
247 return EINVAL;
249 if (ap->a_cmd == TIOCNOTTY) {
250 if (!SESS_LEADER(p)) {
251 p->p_flags &= ~P_CONTROLT;
252 lwkt_reltoken(&p->p_token);
253 return (0);
254 } else {
255 lwkt_reltoken(&p->p_token);
256 return (EINVAL);
259 lwkt_reltoken(&p->p_token);
261 return (VOP_IOCTL(ttyvp, ap->a_cmd, ap->a_data, ap->a_fflag,
262 ap->a_cred, ap->a_sysmsg));
265 static struct filterops cttyfiltops_read =
266 { FILTEROP_ISFD | FILTEROP_MPSAFE, NULL,
267 cttyfilt_detach, cttyfilt_read };
268 static struct filterops cttyfiltops_write =
269 { FILTEROP_ISFD | FILTEROP_MPSAFE, NULL,
270 cttyfilt_detach, cttyfilt_write };
272 static int
273 cttykqfilter(struct dev_kqfilter_args *ap)
275 cdev_t dev = ap->a_head.a_dev;
276 struct proc *p = curproc;
277 struct knote *kn = ap->a_kn;
278 struct vnode *ttyvp;
280 KKASSERT(p);
281 ttyvp = cttyvp(p);
283 if (ttyvp != NULL)
284 return (VOP_KQFILTER(ttyvp, kn));
286 ap->a_result = 0;
288 switch (kn->kn_filter) {
289 case EVFILT_READ:
290 kn->kn_fop = &cttyfiltops_read;
291 kn->kn_hook = (caddr_t)dev;
292 break;
293 case EVFILT_WRITE:
294 kn->kn_fop = &cttyfiltops_write;
295 kn->kn_hook = (caddr_t)dev;
296 break;
297 default:
298 ap->a_result = EOPNOTSUPP;
299 return (0);
302 return (0);
305 static void
306 cttyfilt_detach(struct knote *kn) {}
308 static int
309 cttyfilt_read(struct knote *kn, long hint)
311 cdev_t dev = (cdev_t)kn->kn_hook;
313 if (seltrue(dev, POLLIN | POLLRDNORM))
314 return (1);
316 return (0);
319 static int
320 cttyfilt_write(struct knote *kn, long hint)
322 cdev_t dev = (cdev_t)kn->kn_hook;
324 if (seltrue(dev, POLLOUT | POLLWRNORM))
325 return (1);
327 return (0);
330 static void
331 ctty_drvinit(void *unused __unused)
333 make_dev(&ctty_ops, 0, 0, 0, 0666, "tty");
336 SYSINIT(cttydev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE + CDEV_MAJOR,
337 ctty_drvinit, NULL);