kern_cons.c revision 111815
1/*
2 * Copyright (c) 1988 University of Utah.
3 * Copyright (c) 1991 The Regents of the University of California.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * the Systems Programming Group of the University of Utah Computer
8 * Science Department.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 *    must display the following acknowledgement:
20 *	This product includes software developed by the University of
21 *	California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 *    may be used to endorse or promote products derived from this software
24 *    without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 *
38 *	from: @(#)cons.c	7.2 (Berkeley) 5/9/91
39 * $FreeBSD: head/sys/kern/tty_cons.c 111815 2003-03-03 12:15:54Z phk $
40 */
41
42#include "opt_ddb.h"
43
44#include <sys/param.h>
45#include <sys/systm.h>
46#include <sys/conf.h>
47#include <sys/cons.h>
48#include <sys/fcntl.h>
49#include <sys/kernel.h>
50#include <sys/malloc.h>
51#include <sys/namei.h>
52#include <sys/proc.h>
53#include <sys/queue.h>
54#include <sys/reboot.h>
55#include <sys/sysctl.h>
56#include <sys/tty.h>
57#include <sys/uio.h>
58#include <sys/vnode.h>
59
60#include <ddb/ddb.h>
61
62#include <machine/cpu.h>
63
64static	d_open_t	cnopen;
65static	d_close_t	cnclose;
66static	d_read_t	cnread;
67static	d_write_t	cnwrite;
68static	d_ioctl_t	cnioctl;
69static	d_poll_t	cnpoll;
70static	d_kqfilter_t	cnkqfilter;
71
72#define	CDEV_MAJOR	0
73static struct cdevsw cn_cdevsw = {
74	.d_open =	cnopen,
75	.d_close =	cnclose,
76	.d_read =	cnread,
77	.d_write =	cnwrite,
78	.d_ioctl =	cnioctl,
79	.d_poll =	cnpoll,
80	.d_name =	"console",
81	.d_maj =	CDEV_MAJOR,
82	.d_flags =	D_TTY | D_KQFILTER,
83	.d_kqfilter =	cnkqfilter,
84};
85
86struct cn_device {
87	STAILQ_ENTRY(cn_device) cnd_next;
88	char		cnd_name[16];
89	struct		vnode *cnd_vp;
90	struct		consdev *cnd_cn;
91};
92
93#define CNDEVPATHMAX	32
94#define CNDEVTAB_SIZE	4
95static struct cn_device cn_devtab[CNDEVTAB_SIZE];
96static STAILQ_HEAD(, cn_device) cn_devlist =
97    STAILQ_HEAD_INITIALIZER(cn_devlist);
98
99#define CND_INVALID(cnd, td) 						\
100	(cnd == NULL || cnd->cnd_vp == NULL ||				\
101	    (cnd->cnd_vp->v_type == VBAD && !cn_devopen(cnd, td, 1)))
102
103static udev_t	cn_udev_t;
104SYSCTL_OPAQUE(_machdep, CPU_CONSDEV, consdev, CTLFLAG_RD,
105	&cn_udev_t, sizeof cn_udev_t, "T,dev_t", "");
106
107int	cons_unavail = 0;	/* XXX:
108				 * physical console not available for
109				 * input (i.e., it is in graphics mode)
110				 */
111static int cn_mute;
112static int openflag;			/* how /dev/console was opened */
113static int cn_is_open;
114static u_char console_pausing;		/* pause after each line during probe */
115static char *console_pausestr=
116"<pause; press any key to proceed to next line or '.' to end pause mode>";
117
118void	cndebug(char *);
119
120CONS_DRIVER(cons, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
121SET_DECLARE(cons_set, struct consdev);
122
123void
124cninit(void)
125{
126	struct consdev *best_cn, *cn, **list;
127
128	/*
129	 * Check if we should mute the console (for security reasons perhaps)
130	 * It can be changes dynamically using sysctl kern.consmute
131	 * once we are up and going.
132	 *
133	 */
134        cn_mute = ((boothowto & (RB_MUTE
135			|RB_SINGLE
136			|RB_VERBOSE
137			|RB_ASKNAME
138			|RB_CONFIG)) == RB_MUTE);
139
140	/*
141	 * Find the first console with the highest priority.
142	 */
143	best_cn = NULL;
144	SET_FOREACH(list, cons_set) {
145		cn = *list;
146		cnremove(cn);
147		if (cn->cn_probe == NULL)
148			continue;
149		cn->cn_probe(cn);
150		if (cn->cn_pri == CN_DEAD)
151			continue;
152		if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
153			best_cn = cn;
154		if (boothowto & RB_MULTIPLE) {
155			/*
156			 * Initialize console, and attach to it.
157			 */
158			cnadd(cn);
159			cn->cn_init(cn);
160		}
161	}
162	if (best_cn == NULL)
163		return;
164	if ((boothowto & RB_MULTIPLE) == 0) {
165		cnadd(best_cn);
166		best_cn->cn_init(best_cn);
167	}
168	if (boothowto & RB_PAUSE)
169		console_pausing = 1;
170	/*
171	 * Make the best console the preferred console.
172	 */
173	cnselect(best_cn);
174}
175
176void
177cninit_finish()
178{
179	console_pausing = 0;
180}
181
182/* add a new physical console to back the virtual console */
183int
184cnadd(struct consdev *cn)
185{
186	struct cn_device *cnd;
187	int i;
188
189	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
190		if (cnd->cnd_cn == cn)
191			return (0);
192	for (i = 0; i < CNDEVTAB_SIZE; i++) {
193		cnd = &cn_devtab[i];
194		if (cnd->cnd_cn == NULL)
195			break;
196	}
197	if (cnd->cnd_cn != NULL)
198		return (ENOMEM);
199	cnd->cnd_cn = cn;
200	STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
201	return (0);
202}
203
204void
205cnremove(struct consdev *cn)
206{
207	struct cn_device *cnd;
208
209	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
210		if (cnd->cnd_cn != cn)
211			continue;
212		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
213		if (cnd->cnd_vp != NULL)
214			vn_close(cnd->cnd_vp, openflag, NOCRED, NULL);
215		cnd->cnd_vp = NULL;
216		cnd->cnd_cn = NULL;
217		cnd->cnd_name[0] = '\0';
218#if 0
219		/*
220		 * XXX
221		 * syscons gets really confused if console resources are
222		 * freed after the system has initialized.
223		 */
224		if (cn->cn_term != NULL)
225			cn->cn_term(cn);
226#endif
227		return;
228	}
229}
230
231void
232cnselect(struct consdev *cn)
233{
234	struct cn_device *cnd;
235
236	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
237		if (cnd->cnd_cn != cn)
238			continue;
239		if (cnd == STAILQ_FIRST(&cn_devlist))
240			return;
241		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
242		STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
243		return;
244	}
245}
246
247void
248cndebug(char *str)
249{
250	int i, len;
251
252	len = strlen(str);
253	cnputc('>'); cnputc('>'); cnputc('>'); cnputc(' ');
254	for (i = 0; i < len; i++)
255		cnputc(str[i]);
256	cnputc('\n');
257}
258
259static int
260sysctl_kern_console(SYSCTL_HANDLER_ARGS)
261{
262	struct cn_device *cnd;
263	struct consdev *cp, **list;
264	char *name, *p;
265	int delete, len, error;
266
267	len = 2;
268	SET_FOREACH(list, cons_set) {
269		cp = *list;
270		if (cp->cn_dev != NULL)
271			len += strlen(devtoname(cp->cn_dev)) + 1;
272	}
273	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
274		len += strlen(devtoname(cnd->cnd_cn->cn_dev)) + 1;
275	len = len > CNDEVPATHMAX ? len : CNDEVPATHMAX;
276	MALLOC(name, char *, len, M_TEMP, M_WAITOK | M_ZERO);
277	p = name;
278	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
279		p += sprintf(p, "%s,", devtoname(cnd->cnd_cn->cn_dev));
280	*p++ = '/';
281	SET_FOREACH(list, cons_set) {
282		cp = *list;
283		if (cp->cn_dev != NULL)
284			p += sprintf(p, "%s,", devtoname(cp->cn_dev));
285	}
286	error = sysctl_handle_string(oidp, name, len, req);
287	if (error == 0 && req->newptr != NULL) {
288		p = name;
289		error = ENXIO;
290		delete = 0;
291		if (*p == '-') {
292			delete = 1;
293			p++;
294		}
295		SET_FOREACH(list, cons_set) {
296			cp = *list;
297			if (cp->cn_dev == NULL ||
298			    strcmp(p, devtoname(cp->cn_dev)) != 0)
299				continue;
300			if (delete) {
301				cnremove(cp);
302				error = 0;
303			} else {
304				error = cnadd(cp);
305				if (error == 0)
306					cnselect(cp);
307			}
308			break;
309		}
310	}
311	FREE(name, M_TEMP);
312	return (error);
313}
314
315SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW,
316	0, 0, sysctl_kern_console, "A", "Console device control");
317
318/*
319 * User has changed the state of the console muting.
320 * This may require us to open or close the device in question.
321 */
322static int
323sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
324{
325	int error;
326	int ocn_mute;
327
328	ocn_mute = cn_mute;
329	error = sysctl_handle_int(oidp, &cn_mute, 0, req);
330	if (error != 0 || req->newptr == NULL)
331		return (error);
332	if (ocn_mute && !cn_mute && cn_is_open)
333		error = cnopen(NODEV, openflag, 0, curthread);
334	else if (!ocn_mute && cn_mute && cn_is_open) {
335		error = cnclose(NODEV, openflag, 0, curthread);
336		cn_is_open = 1;		/* XXX hack */
337	}
338	return (error);
339}
340
341SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
342	0, sizeof(cn_mute), sysctl_kern_consmute, "I", "");
343
344static int
345cn_devopen(struct cn_device *cnd, struct thread *td, int forceopen)
346{
347	char path[CNDEVPATHMAX];
348	struct nameidata nd;
349	struct vnode *vp;
350	dev_t dev;
351	int error;
352
353	if ((vp = cnd->cnd_vp) != NULL) {
354		if (!forceopen && vp->v_type != VBAD) {
355			dev = vp->v_rdev;
356			return ((*devsw(dev)->d_open)(dev, openflag, 0, td));
357		}
358		cnd->cnd_vp = NULL;
359		vn_close(vp, openflag, td->td_ucred, td);
360	}
361	if (cnd->cnd_name[0] == '\0') {
362		strlcpy(cnd->cnd_name, devtoname(cnd->cnd_cn->cn_dev),
363		    sizeof(cnd->cnd_name));
364	}
365	snprintf(path, sizeof(path), "/dev/%s", cnd->cnd_name);
366	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td);
367	error = vn_open(&nd, &openflag, 0);
368	if (error == 0) {
369		NDFREE(&nd, NDF_ONLY_PNBUF);
370		VOP_UNLOCK(nd.ni_vp, 0, td);
371		if (nd.ni_vp->v_type == VCHR)
372			cnd->cnd_vp = nd.ni_vp;
373		else
374			vn_close(nd.ni_vp, openflag, td->td_ucred, td);
375	}
376	return (cnd->cnd_vp != NULL);
377}
378
379static int
380cnopen(dev_t dev, int flag, int mode, struct thread *td)
381{
382	struct cn_device *cnd;
383
384	openflag = flag | FWRITE;	/* XXX */
385	cn_is_open = 1;			/* console is logically open */
386	if (cn_mute)
387		return (0);
388	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
389		cn_devopen(cnd, td, 0);
390	return (0);
391}
392
393static int
394cnclose(dev_t dev, int flag, int mode, struct thread *td)
395{
396	struct cn_device *cnd;
397	struct vnode *vp;
398
399	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
400		if ((vp = cnd->cnd_vp) == NULL)
401			continue;
402		cnd->cnd_vp = NULL;
403		vn_close(vp, openflag, td->td_ucred, td);
404	}
405	cn_is_open = 0;
406	return (0);
407}
408
409static int
410cnread(dev_t dev, struct uio *uio, int flag)
411{
412	struct cn_device *cnd;
413
414	cnd = STAILQ_FIRST(&cn_devlist);
415	if (cn_mute || CND_INVALID(cnd, curthread))
416		return (0);
417	dev = cnd->cnd_vp->v_rdev;
418	return ((*devsw(dev)->d_read)(dev, uio, flag));
419}
420
421static int
422cnwrite(dev_t dev, struct uio *uio, int flag)
423{
424	struct cn_device *cnd;
425
426	cnd = STAILQ_FIRST(&cn_devlist);
427	if (cn_mute || CND_INVALID(cnd, curthread))
428		goto done;
429	if (constty)
430		dev = constty->t_dev;
431	else
432		dev = cnd->cnd_vp->v_rdev;
433	if (dev != NULL) {
434		log_console(uio);
435		return ((*devsw(dev)->d_write)(dev, uio, flag));
436	}
437done:
438	uio->uio_resid = 0; /* dump the data */
439	return (0);
440}
441
442static int
443cnioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
444{
445	struct cn_device *cnd;
446	int error;
447
448	cnd = STAILQ_FIRST(&cn_devlist);
449	if (cn_mute || CND_INVALID(cnd, td))
450		return (0);
451	/*
452	 * Superuser can always use this to wrest control of console
453	 * output from the "virtual" console.
454	 */
455	if (cmd == TIOCCONS && constty) {
456		error = suser(td);
457		if (error)
458			return (error);
459		constty = NULL;
460		return (0);
461	}
462	dev = cnd->cnd_vp->v_rdev;
463	if (dev != NULL)
464		return ((*devsw(dev)->d_ioctl)(dev, cmd, data, flag, td));
465	return (0);
466}
467
468/*
469 * XXX
470 * poll/kqfilter do not appear to be correct
471 */
472static int
473cnpoll(dev_t dev, int events, struct thread *td)
474{
475	struct cn_device *cnd;
476
477	cnd = STAILQ_FIRST(&cn_devlist);
478	if (cn_mute || CND_INVALID(cnd, td))
479		return (0);
480	dev = cnd->cnd_vp->v_rdev;
481	if (dev != NULL)
482		return ((*devsw(dev)->d_poll)(dev, events, td));
483	return (0);
484}
485
486static int
487cnkqfilter(dev_t dev, struct knote *kn)
488{
489	struct cn_device *cnd;
490
491	cnd = STAILQ_FIRST(&cn_devlist);
492	if (cn_mute || CND_INVALID(cnd, curthread))
493		return (1);
494	dev = cnd->cnd_vp->v_rdev;
495	if (dev != NULL)
496		return ((*devsw(dev)->d_kqfilter)(dev, kn));
497	return (1);
498}
499
500/*
501 * Low level console routines.
502 */
503int
504cngetc(void)
505{
506	int c;
507
508	if (cn_mute)
509		return (-1);
510	while ((c = cncheckc()) == -1)
511		;
512	if (c == '\r')
513		c = '\n';		/* console input is always ICRNL */
514	return (c);
515}
516
517int
518cncheckc(void)
519{
520	struct cn_device *cnd;
521	struct consdev *cn;
522	int c;
523
524	if (cn_mute)
525		return (-1);
526	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
527		cn = cnd->cnd_cn;
528		c = cn->cn_checkc(cn);
529		if (c != -1) {
530			return (c);
531		}
532	}
533	return (-1);
534}
535
536void
537cnputc(int c)
538{
539	struct cn_device *cnd;
540	struct consdev *cn;
541	char *cp;
542
543	if (cn_mute || c == '\0')
544		return;
545	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
546		cn = cnd->cnd_cn;
547		if (c == '\n')
548			cn->cn_putc(cn, '\r');
549		cn->cn_putc(cn, c);
550	}
551#ifdef DDB
552	if (console_pausing && !db_active && (c == '\n')) {
553#else
554	if (console_pausing && (c == '\n')) {
555#endif
556		for (cp = console_pausestr; *cp != '\0'; cp++)
557			cnputc(*cp);
558		if (cngetc() == '.')
559			console_pausing = 0;
560		cnputc('\r');
561		for (cp = console_pausestr; *cp != '\0'; cp++)
562			cnputc(' ');
563		cnputc('\r');
564	}
565}
566
567void
568cndbctl(int on)
569{
570	struct cn_device *cnd;
571	struct consdev *cn;
572	static int refcount;
573
574	if (!on)
575		refcount--;
576	if (refcount == 0)
577		STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
578			cn = cnd->cnd_cn;
579			if (cn->cn_dbctl != NULL)
580				cn->cn_dbctl(cn, on);
581		}
582	if (on)
583		refcount++;
584}
585
586static void
587cn_drvinit(void *unused)
588{
589
590	make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
591	    "console");
592}
593
594SYSINIT(cndev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cn_drvinit,NULL)
595