kern_cons.c revision 107984
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 107984 2002-12-17 19:30:50Z 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	/* open */	cnopen,
75	/* close */	cnclose,
76	/* read */	cnread,
77	/* write */	cnwrite,
78	/* ioctl */	cnioctl,
79	/* poll */	cnpoll,
80	/* mmap */	nommap,
81	/* strategy */	nostrategy,
82	/* name */	"console",
83	/* maj */	CDEV_MAJOR,
84	/* dump */	nodump,
85	/* psize */	nopsize,
86	/* flags */	D_TTY | D_KQFILTER,
87	/* kqfilter */	cnkqfilter,
88};
89
90struct cn_device {
91	STAILQ_ENTRY(cn_device) cnd_next;
92	char		cnd_name[16];
93	struct		vnode *cnd_vp;
94	struct		consdev *cnd_cn;
95};
96
97#define CNDEVPATHMAX	32
98#define CNDEVTAB_SIZE	4
99static struct cn_device cn_devtab[CNDEVTAB_SIZE];
100static STAILQ_HEAD(, cn_device) cn_devlist =
101    STAILQ_HEAD_INITIALIZER(cn_devlist);
102
103#define CND_INVALID(cnd, td) 						\
104	(cnd == NULL || cnd->cnd_vp == NULL ||				\
105	    (cnd->cnd_vp->v_type == VBAD && !cn_devopen(cnd, td, 1)))
106
107static udev_t	cn_udev_t;
108SYSCTL_OPAQUE(_machdep, CPU_CONSDEV, consdev, CTLFLAG_RD,
109	&cn_udev_t, sizeof cn_udev_t, "T,dev_t", "");
110
111int	cons_unavail = 0;	/* XXX:
112				 * physical console not available for
113				 * input (i.e., it is in graphics mode)
114				 */
115static int cn_mute;
116static int openflag;			/* how /dev/console was opened */
117static int cn_is_open;
118static u_char console_pausing;		/* pause after each line during probe */
119static char *console_pausestr=
120"<pause; press any key to proceed to next line or '.' to end pause mode>";
121
122void	cndebug(char *);
123
124CONS_DRIVER(cons, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
125SET_DECLARE(cons_set, struct consdev);
126
127void
128cninit(void)
129{
130	struct consdev *best_cn, *cn, **list;
131
132	/*
133	 * Check if we should mute the console (for security reasons perhaps)
134	 * It can be changes dynamically using sysctl kern.consmute
135	 * once we are up and going.
136	 *
137	 */
138        cn_mute = ((boothowto & (RB_MUTE
139			|RB_SINGLE
140			|RB_VERBOSE
141			|RB_ASKNAME
142			|RB_CONFIG)) == RB_MUTE);
143
144	/*
145	 * Find the first console with the highest priority.
146	 */
147	best_cn = NULL;
148	SET_FOREACH(list, cons_set) {
149		cn = *list;
150		cnremove(cn);
151		if (cn->cn_probe == NULL)
152			continue;
153		cn->cn_probe(cn);
154		if (cn->cn_pri == CN_DEAD)
155			continue;
156		if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
157			best_cn = cn;
158		if (boothowto & RB_MULTIPLE) {
159			/*
160			 * Initialize console, and attach to it.
161			 */
162			cnadd(cn);
163			cn->cn_init(cn);
164		}
165	}
166	if (best_cn == NULL)
167		return;
168	if ((boothowto & RB_MULTIPLE) == 0) {
169		cnadd(best_cn);
170		best_cn->cn_init(best_cn);
171	}
172	if (boothowto & RB_PAUSE)
173		console_pausing = 1;
174	/*
175	 * Make the best console the preferred console.
176	 */
177	cnselect(best_cn);
178}
179
180void
181cninit_finish()
182{
183	console_pausing = 0;
184}
185
186/* add a new physical console to back the virtual console */
187int
188cnadd(struct consdev *cn)
189{
190	struct cn_device *cnd;
191	int i;
192
193	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
194		if (cnd->cnd_cn == cn)
195			return (0);
196	for (i = 0; i < CNDEVTAB_SIZE; i++) {
197		cnd = &cn_devtab[i];
198		if (cnd->cnd_cn == NULL)
199			break;
200	}
201	if (cnd->cnd_cn != NULL)
202		return (ENOMEM);
203	cnd->cnd_cn = cn;
204	STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
205	return (0);
206}
207
208void
209cnremove(struct consdev *cn)
210{
211	struct cn_device *cnd;
212
213	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
214		if (cnd->cnd_cn != cn)
215			continue;
216		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
217		if (cnd->cnd_vp != NULL)
218			vn_close(cnd->cnd_vp, openflag, NOCRED, NULL);
219		cnd->cnd_vp = NULL;
220		cnd->cnd_cn = NULL;
221		cnd->cnd_name[0] = '\0';
222#if 0
223		/*
224		 * XXX
225		 * syscons gets really confused if console resources are
226		 * freed after the system has initialized.
227		 */
228		if (cn->cn_term != NULL)
229			cn->cn_term(cn);
230#endif
231		return;
232	}
233}
234
235void
236cnselect(struct consdev *cn)
237{
238	struct cn_device *cnd;
239
240	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
241		if (cnd->cnd_cn != cn)
242			continue;
243		if (cnd == STAILQ_FIRST(&cn_devlist))
244			return;
245		STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
246		STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
247		return;
248	}
249}
250
251void
252cndebug(char *str)
253{
254	int i, len;
255
256	len = strlen(str);
257	cnputc('>'); cnputc('>'); cnputc('>'); cnputc(' ');
258	for (i = 0; i < len; i++)
259		cnputc(str[i]);
260	cnputc('\n');
261}
262
263static int
264sysctl_kern_console(SYSCTL_HANDLER_ARGS)
265{
266	struct cn_device *cnd;
267	struct consdev *cp, **list;
268	char *name, *p;
269	int delete, len, error;
270
271	len = 2;
272	SET_FOREACH(list, cons_set) {
273		cp = *list;
274		if (cp->cn_dev != NULL)
275			len += strlen(devtoname(cp->cn_dev)) + 1;
276	}
277	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
278		len += strlen(devtoname(cnd->cnd_cn->cn_dev)) + 1;
279	len = len > CNDEVPATHMAX ? len : CNDEVPATHMAX;
280	MALLOC(name, char *, len, M_TEMP, M_WAITOK | M_ZERO);
281	p = name;
282	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
283		p += sprintf(p, "%s,", devtoname(cnd->cnd_cn->cn_dev));
284	*p++ = '/';
285	SET_FOREACH(list, cons_set) {
286		cp = *list;
287		if (cp->cn_dev != NULL)
288			p += sprintf(p, "%s,", devtoname(cp->cn_dev));
289	}
290	error = sysctl_handle_string(oidp, name, len, req);
291	if (error == 0 && req->newptr != NULL) {
292		p = name;
293		error = ENXIO;
294		delete = 0;
295		if (*p == '-') {
296			delete = 1;
297			p++;
298		}
299		SET_FOREACH(list, cons_set) {
300			cp = *list;
301			if (cp->cn_dev == NULL ||
302			    strcmp(p, devtoname(cp->cn_dev)) != 0)
303				continue;
304			if (delete) {
305				cnremove(cp);
306				error = 0;
307			} else {
308				error = cnadd(cp);
309				if (error == 0)
310					cnselect(cp);
311			}
312			break;
313		}
314	}
315	FREE(name, M_TEMP);
316	return (error);
317}
318
319SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW,
320	0, 0, sysctl_kern_console, "A", "Console device control");
321
322/*
323 * User has changed the state of the console muting.
324 * This may require us to open or close the device in question.
325 */
326static int
327sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
328{
329	int error;
330	int ocn_mute;
331
332	ocn_mute = cn_mute;
333	error = sysctl_handle_int(oidp, &cn_mute, 0, req);
334	if (error != 0 || req->newptr == NULL)
335		return (error);
336	if (ocn_mute && !cn_mute && cn_is_open)
337		error = cnopen(NODEV, openflag, 0, curthread);
338	else if (!ocn_mute && cn_mute && cn_is_open) {
339		error = cnclose(NODEV, openflag, 0, curthread);
340		cn_is_open = 1;		/* XXX hack */
341	}
342	return (error);
343}
344
345SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
346	0, sizeof(cn_mute), sysctl_kern_consmute, "I", "");
347
348static int
349cn_devopen(struct cn_device *cnd, struct thread *td, int forceopen)
350{
351	char path[CNDEVPATHMAX];
352	struct nameidata nd;
353	struct vnode *vp;
354	dev_t dev;
355	int error;
356
357	if ((vp = cnd->cnd_vp) != NULL) {
358		if (!forceopen && vp->v_type != VBAD) {
359			dev = vp->v_rdev;
360			return ((*devsw(dev)->d_open)(dev, openflag, 0, td));
361		}
362		cnd->cnd_vp = NULL;
363		vn_close(vp, openflag, td->td_ucred, td);
364	}
365	if (cnd->cnd_name[0] == '\0') {
366		strlcpy(cnd->cnd_name, devtoname(cnd->cnd_cn->cn_dev),
367		    sizeof(cnd->cnd_name));
368	}
369	snprintf(path, sizeof(path), "/dev/%s", cnd->cnd_name);
370	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td);
371	error = vn_open(&nd, &openflag, 0);
372	if (error == 0) {
373		NDFREE(&nd, NDF_ONLY_PNBUF);
374		VOP_UNLOCK(nd.ni_vp, 0, td);
375		if (nd.ni_vp->v_type == VCHR)
376			cnd->cnd_vp = nd.ni_vp;
377		else
378			vn_close(nd.ni_vp, openflag, td->td_ucred, td);
379	}
380	return (cnd->cnd_vp != NULL);
381}
382
383static int
384cnopen(dev_t dev, int flag, int mode, struct thread *td)
385{
386	struct cn_device *cnd;
387
388	openflag = flag | FWRITE;	/* XXX */
389	cn_is_open = 1;			/* console is logically open */
390	if (cn_mute)
391		return (0);
392	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
393		cn_devopen(cnd, td, 0);
394	return (0);
395}
396
397static int
398cnclose(dev_t dev, int flag, int mode, struct thread *td)
399{
400	struct cn_device *cnd;
401	struct vnode *vp;
402
403	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
404		if ((vp = cnd->cnd_vp) == NULL)
405			continue;
406		cnd->cnd_vp = NULL;
407		vn_close(vp, openflag, td->td_ucred, td);
408	}
409	cn_is_open = 0;
410	return (0);
411}
412
413static int
414cnread(dev_t dev, struct uio *uio, int flag)
415{
416	struct cn_device *cnd;
417
418	cnd = STAILQ_FIRST(&cn_devlist);
419	if (cn_mute || CND_INVALID(cnd, curthread))
420		return (0);
421	dev = cnd->cnd_vp->v_rdev;
422	return ((*devsw(dev)->d_read)(dev, uio, flag));
423}
424
425static int
426cnwrite(dev_t dev, struct uio *uio, int flag)
427{
428	struct cn_device *cnd;
429
430	cnd = STAILQ_FIRST(&cn_devlist);
431	if (cn_mute || CND_INVALID(cnd, curthread))
432		goto done;
433	if (constty)
434		dev = constty->t_dev;
435	else
436		dev = cnd->cnd_vp->v_rdev;
437	if (dev != NULL) {
438		log_console(uio);
439		return ((*devsw(dev)->d_write)(dev, uio, flag));
440	}
441done:
442	uio->uio_resid = 0; /* dump the data */
443	return (0);
444}
445
446static int
447cnioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
448{
449	struct cn_device *cnd;
450	int error;
451
452	cnd = STAILQ_FIRST(&cn_devlist);
453	if (cn_mute || CND_INVALID(cnd, td))
454		return (0);
455	/*
456	 * Superuser can always use this to wrest control of console
457	 * output from the "virtual" console.
458	 */
459	if (cmd == TIOCCONS && constty) {
460		error = suser(td);
461		if (error)
462			return (error);
463		constty = NULL;
464		return (0);
465	}
466	dev = cnd->cnd_vp->v_rdev;
467	if (dev != NULL)
468		return ((*devsw(dev)->d_ioctl)(dev, cmd, data, flag, td));
469	return (0);
470}
471
472/*
473 * XXX
474 * poll/kqfilter do not appear to be correct
475 */
476static int
477cnpoll(dev_t dev, int events, struct thread *td)
478{
479	struct cn_device *cnd;
480
481	cnd = STAILQ_FIRST(&cn_devlist);
482	if (cn_mute || CND_INVALID(cnd, td))
483		return (0);
484	dev = cnd->cnd_vp->v_rdev;
485	if (dev != NULL)
486		return ((*devsw(dev)->d_poll)(dev, events, td));
487	return (0);
488}
489
490static int
491cnkqfilter(dev_t dev, struct knote *kn)
492{
493	struct cn_device *cnd;
494
495	cnd = STAILQ_FIRST(&cn_devlist);
496	if (cn_mute || CND_INVALID(cnd, curthread))
497		return (1);
498	dev = cnd->cnd_vp->v_rdev;
499	if (dev != NULL)
500		return ((*devsw(dev)->d_kqfilter)(dev, kn));
501	return (1);
502}
503
504/*
505 * Low level console routines.
506 */
507int
508cngetc(void)
509{
510	int c;
511
512	if (cn_mute)
513		return (-1);
514	while ((c = cncheckc()) == -1)
515		;
516	if (c == '\r')
517		c = '\n';		/* console input is always ICRNL */
518	return (c);
519}
520
521int
522cncheckc(void)
523{
524	struct cn_device *cnd;
525	struct consdev *cn;
526	int c;
527
528	if (cn_mute)
529		return (-1);
530	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
531		cn = cnd->cnd_cn;
532		c = cn->cn_checkc(cn->cn_dev);
533		if (c != -1) {
534			return (c);
535		}
536	}
537	return (-1);
538}
539
540void
541cnputc(int c)
542{
543	struct cn_device *cnd;
544	struct consdev *cn;
545	char *cp;
546
547	if (cn_mute || c == '\0')
548		return;
549	STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
550		cn = cnd->cnd_cn;
551		if (c == '\n')
552			cn->cn_putc(cn->cn_dev, '\r');
553		cn->cn_putc(cn->cn_dev, c);
554	}
555#ifdef DDB
556	if (console_pausing && !db_active && (c == '\n')) {
557#else
558	if (console_pausing && (c == '\n')) {
559#endif
560		for (cp = console_pausestr; *cp != '\0'; cp++)
561			cnputc(*cp);
562		if (cngetc() == '.')
563			console_pausing = 0;
564		cnputc('\r');
565		for (cp = console_pausestr; *cp != '\0'; cp++)
566			cnputc(' ');
567		cnputc('\r');
568	}
569}
570
571void
572cndbctl(int on)
573{
574	struct cn_device *cnd;
575	struct consdev *cn;
576	static int refcount;
577
578	if (!on)
579		refcount--;
580	if (refcount == 0)
581		STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
582			cn = cnd->cnd_cn;
583			if (cn->cn_dbctl != NULL)
584				cn->cn_dbctl(cn->cn_dev, on);
585		}
586	if (on)
587		refcount++;
588}
589
590static void
591cn_drvinit(void *unused)
592{
593
594	make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
595	    "console");
596}
597
598SYSINIT(cndev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cn_drvinit,NULL)
599