1/*	$NetBSD: cons.c,v 1.67 2010/06/24 13:03:08 hannken Exp $	*/
2
3/*
4 * Copyright (c) 1988 University of Utah.
5 * Copyright (c) 1990, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * the Systems Programming Group of the University of Utah Computer
10 * Science Department.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * from: Utah $Hdr: cons.c 1.7 92/01/21$
37 *
38 *	@(#)cons.c	8.2 (Berkeley) 1/12/94
39 */
40
41#include <sys/cdefs.h>
42__KERNEL_RCSID(0, "$NetBSD: cons.c,v 1.67 2010/06/24 13:03:08 hannken Exp $");
43
44#include <sys/param.h>
45#include <sys/proc.h>
46#include <sys/systm.h>
47#include <sys/buf.h>
48#include <sys/ioctl.h>
49#include <sys/poll.h>
50#include <sys/tty.h>
51#include <sys/file.h>
52#include <sys/conf.h>
53#include <sys/vnode.h>
54#include <sys/kauth.h>
55#include <sys/mutex.h>
56
57#include <dev/cons.h>
58
59dev_type_open(cnopen);
60dev_type_close(cnclose);
61dev_type_read(cnread);
62dev_type_write(cnwrite);
63dev_type_ioctl(cnioctl);
64dev_type_poll(cnpoll);
65dev_type_kqfilter(cnkqfilter);
66
67static bool cn_redirect(dev_t *, int, int *);
68
69const struct cdevsw cons_cdevsw = {
70	cnopen, cnclose, cnread, cnwrite, cnioctl,
71	nostop, notty, cnpoll, nommap, cnkqfilter, D_TTY
72};
73
74struct	tty *constty = NULL;	/* virtual console output device */
75struct	consdev *cn_tab;	/* physical console device info */
76struct	vnode *cn_devvp[2];	/* vnode for underlying device. */
77
78int
79cnopen(dev_t dev, int flag, int mode, struct lwp *l)
80{
81	dev_t cndev;
82	int unit, error;
83
84	unit = minor(dev);
85	if (unit > 1)
86		return ENODEV;
87
88	if (cn_tab == NULL)
89		return (0);
90
91	/*
92	 * always open the 'real' console device, so we don't get nailed
93	 * later.  This follows normal device semantics; they always get
94	 * open() calls.
95	 */
96	cndev = cn_tab->cn_dev;
97	if (cndev == NODEV) {
98		/*
99		 * This is most likely an error in the console attach
100		 * code. Panicking looks better than jumping into nowhere
101		 * through cdevsw below....
102		 */
103		panic("cnopen: no console device");
104	}
105	if (dev == cndev) {
106		/*
107		 * This causes cnopen() to be called recursively, which
108		 * is generally a bad thing.  It is often caused when
109		 * dev == 0 and cn_dev has not been set, but was probably
110		 * initialised to 0.
111		 */
112		panic("cnopen: cn_tab->cn_dev == dev");
113	}
114	if (cn_devvp[unit] != NULLVP)
115		return 0;
116	if ((error = cdevvp(cndev, &cn_devvp[unit])) != 0)
117		printf("cnopen: unable to get vnode reference\n");
118	error = vn_lock(cn_devvp[unit], LK_EXCLUSIVE | LK_RETRY);
119	if (error == 0) {
120		error = VOP_OPEN(cn_devvp[unit], flag, kauth_cred_get());
121		VOP_UNLOCK(cn_devvp[unit]);
122	}
123	return error;
124}
125
126int
127cnclose(dev_t dev, int flag, int mode, struct lwp *l)
128{
129	struct vnode *vp;
130	int unit, error;
131
132	unit = minor(dev);
133
134	if (cn_tab == NULL)
135		return (0);
136
137	vp = cn_devvp[unit];
138	cn_devvp[unit] = NULL;
139	error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
140	if (error == 0) {
141		error = VOP_CLOSE(vp, flag, kauth_cred_get());
142		VOP_UNLOCK(vp);
143	}
144	return error;
145}
146
147int
148cnread(dev_t dev, struct uio *uio, int flag)
149{
150	int error;
151
152	/*
153	 * If we would redirect input, punt.  This will keep strange
154	 * things from happening to people who are using the real
155	 * console.  Nothing should be using /dev/console for
156	 * input (except a shell in single-user mode, but then,
157	 * one wouldn't TIOCCONS then).
158	 */
159	if (!cn_redirect(&dev, 1, &error))
160		return error;
161	return cdev_read(dev, uio, flag);
162}
163
164int
165cnwrite(dev_t dev, struct uio *uio, int flag)
166{
167	int error;
168
169	/* Redirect output, if that's appropriate. */
170	if (!cn_redirect(&dev, 0, &error))
171		return error;
172	return cdev_write(dev, uio, flag);
173}
174
175int
176cnioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
177{
178	int error;
179
180	error = 0;
181
182	/*
183	 * Superuser can always use this to wrest control of console
184	 * output from the "virtual" console.
185	 */
186	if (cmd == TIOCCONS && constty != NULL) {
187		error = kauth_authorize_generic(l->l_cred,
188		    KAUTH_GENERIC_ISSUSER, NULL);
189		if (!error)
190			constty = NULL;
191		return (error);
192	}
193
194	/*
195	 * Redirect the ioctl, if that's appropriate.
196	 * Note that strange things can happen, if a program does
197	 * ioctls on /dev/console, then the console is redirected
198	 * out from under it.
199	 */
200	if (!cn_redirect(&dev, 0, &error))
201		return error;
202	return cdev_ioctl(dev, cmd, data, flag, l);
203}
204
205/*ARGSUSED*/
206int
207cnpoll(dev_t dev, int events, struct lwp *l)
208{
209	int error;
210
211	/*
212	 * Redirect the poll, if that's appropriate.
213	 * I don't want to think of the possible side effects
214	 * of console redirection here.
215	 */
216	if (!cn_redirect(&dev, 0, &error))
217		return POLLHUP;
218	return cdev_poll(dev, events, l);
219}
220
221/*ARGSUSED*/
222int
223cnkqfilter(dev_t dev, struct knote *kn)
224{
225	int error;
226
227	/*
228	 * Redirect the kqfilter, if that's appropriate.
229	 * I don't want to think of the possible side effects
230	 * of console redirection here.
231	 */
232	if (!cn_redirect(&dev, 0, &error))
233		return error;
234	return cdev_kqfilter(dev, kn);
235}
236
237int
238cngetc(void)
239{
240	if (cn_tab == NULL)
241		return (0);
242	return ((*cn_tab->cn_getc)(cn_tab->cn_dev));
243}
244
245int
246cngetsn(char *cp, int size)
247{
248	char *lp;
249	int c, len;
250
251	cnpollc(1);
252
253	lp = cp;
254	len = 0;
255	for (;;) {
256		c = cngetc();
257		switch (c) {
258		case '\n':
259		case '\r':
260			printf("\n");
261			*lp++ = '\0';
262			cnpollc(0);
263			return (len);
264		case '\b':
265		case '\177':
266		case '#':
267			if (len) {
268				--len;
269				--lp;
270				printf("\b \b");
271			}
272			continue;
273		case '@':
274		case 'u'&037:	/* CTRL-u */
275			len = 0;
276			lp = cp;
277			printf("\n");
278			continue;
279		default:
280			if (len + 1 >= size || c < ' ') {
281				printf("\007");
282				continue;
283			}
284			printf("%c", c);
285			++len;
286			*lp++ = c;
287		}
288	}
289}
290
291void
292cnputc(int c)
293{
294
295	if (cn_tab == NULL)
296		return;
297
298	if (c) {
299		(*cn_tab->cn_putc)(cn_tab->cn_dev, c);
300		if (c == '\n')
301			(*cn_tab->cn_putc)(cn_tab->cn_dev, '\r');
302	}
303}
304
305void
306cnpollc(int on)
307{
308	static int refcount = 0;
309
310	if (cn_tab == NULL)
311		return;
312	if (!on)
313		--refcount;
314	if (refcount == 0)
315		(*cn_tab->cn_pollc)(cn_tab->cn_dev, on);
316	if (on)
317		++refcount;
318}
319
320void
321nullcnpollc(dev_t dev, int on)
322{
323
324}
325
326void
327cnbell(u_int pitch, u_int period, u_int volume)
328{
329
330	if (cn_tab == NULL || cn_tab->cn_bell == NULL)
331		return;
332	(*cn_tab->cn_bell)(cn_tab->cn_dev, pitch, period, volume);
333}
334
335void
336cnflush(void)
337{
338	if (cn_tab == NULL || cn_tab->cn_flush == NULL)
339		return;
340	(*cn_tab->cn_flush)(cn_tab->cn_dev);
341}
342
343void
344cnhalt(void)
345{
346	if (cn_tab == NULL || cn_tab->cn_halt == NULL)
347		return;
348	(*cn_tab->cn_halt)(cn_tab->cn_dev);
349}
350
351/*
352 * Redirect output, if that's appropriate.  If there's no real console,
353 * return ENXIO.
354 *
355 * Call with tty_mutex held.
356 */
357static bool
358cn_redirect(dev_t *devp, int is_read, int *error)
359{
360	dev_t dev = *devp;
361
362	*error = ENXIO;
363	if (constty != NULL && minor(dev) == 0 &&
364	    (cn_tab == NULL || (cn_tab->cn_pri != CN_REMOTE))) {
365		if (is_read) {
366			*error = 0;
367			return false;
368		}
369		dev = constty->t_dev;
370	} else if (cn_tab == NULL)
371		return false;
372	else
373		dev = cn_tab->cn_dev;
374	*devp = dev;
375	return true;
376}
377