Deleted Added
full compact
kern_cons.c (120456) kern_cons.c (120491)
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 */
40
41#include <sys/cdefs.h>
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 */
40
41#include <sys/cdefs.h>
42__FBSDID("$FreeBSD: head/sys/kern/tty_cons.c 120456 2003-09-26 07:26:54Z phk $");
42__FBSDID("$FreeBSD: head/sys/kern/tty_cons.c 120491 2003-09-26 19:35:50Z phk $");
43
44#include "opt_ddb.h"
45
46#include <sys/param.h>
47#include <sys/systm.h>
48#include <sys/conf.h>
49#include <sys/cons.h>
50#include <sys/fcntl.h>
51#include <sys/kernel.h>
52#include <sys/malloc.h>
53#include <sys/msgbuf.h>
54#include <sys/namei.h>
55#include <sys/proc.h>
56#include <sys/queue.h>
57#include <sys/reboot.h>
58#include <sys/sysctl.h>
59#include <sys/tty.h>
60#include <sys/uio.h>
61#include <sys/vnode.h>
62
63#include <ddb/ddb.h>
64
65#include <machine/cpu.h>
66
67static d_open_t cnopen;
68static d_close_t cnclose;
69static d_read_t cnread;
70static d_write_t cnwrite;
71static d_ioctl_t cnioctl;
72static d_poll_t cnpoll;
73static d_kqfilter_t cnkqfilter;
74
75static struct cdevsw cn_cdevsw = {
76 .d_open = cnopen,
77 .d_close = cnclose,
78 .d_read = cnread,
79 .d_write = cnwrite,
80 .d_ioctl = cnioctl,
81 .d_poll = cnpoll,
82 .d_name = "console",
83 .d_maj = 256,
84 /*
85 * XXX: We really want major #0, but zero here means
86 * XXX: allocate a major number automatically.
87 * XXX: kern_conf.c knows what to do when it sees 256.
88 */
89 .d_flags = D_TTY,
90 .d_kqfilter = cnkqfilter,
91};
92
93struct cn_device {
94 STAILQ_ENTRY(cn_device) cnd_next;
95 struct vnode *cnd_vp;
96 struct consdev *cnd_cn;
97};
98
99#define CNDEVPATHMAX 32
100#define CNDEVTAB_SIZE 4
101static struct cn_device cn_devtab[CNDEVTAB_SIZE];
102static STAILQ_HEAD(, cn_device) cn_devlist =
103 STAILQ_HEAD_INITIALIZER(cn_devlist);
104
105#define CND_INVALID(cnd, td) \
106 (cnd == NULL || cnd->cnd_vp == NULL || \
107 (cnd->cnd_vp->v_type == VBAD && !cn_devopen(cnd, td, 1)))
108
109static udev_t cn_udev_t;
110SYSCTL_OPAQUE(_machdep, CPU_CONSDEV, consdev, CTLFLAG_RD,
111 &cn_udev_t, sizeof cn_udev_t, "T,dev_t", "");
112
113int cons_unavail = 0; /* XXX:
114 * physical console not available for
115 * input (i.e., it is in graphics mode)
116 */
117static int cn_mute;
118static int openflag; /* how /dev/console was opened */
119static int cn_is_open;
120static char *consbuf; /* buffer used by `consmsgbuf' */
121static struct callout conscallout; /* callout for outputting to constty */
122struct msgbuf consmsgbuf; /* message buffer for console tty */
123static u_char console_pausing; /* pause after each line during probe */
124static char *console_pausestr=
125"<pause; press any key to proceed to next line or '.' to end pause mode>";
126struct tty *constty; /* pointer to console "window" tty */
127
128void cndebug(char *);
129static void constty_timeout(void *arg);
130
131CONS_DRIVER(cons, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
132SET_DECLARE(cons_set, struct consdev);
133
134void
135cninit(void)
136{
137 struct consdev *best_cn, *cn, **list;
138
139 /*
140 * Check if we should mute the console (for security reasons perhaps)
141 * It can be changes dynamically using sysctl kern.consmute
142 * once we are up and going.
143 *
144 */
145 cn_mute = ((boothowto & (RB_MUTE
146 |RB_SINGLE
147 |RB_VERBOSE
148 |RB_ASKNAME
149 |RB_CONFIG)) == RB_MUTE);
150
151 /*
152 * Find the first console with the highest priority.
153 */
154 best_cn = NULL;
155 SET_FOREACH(list, cons_set) {
156 cn = *list;
157 cnremove(cn);
158 if (cn->cn_probe == NULL)
159 continue;
160 cn->cn_probe(cn);
161 if (cn->cn_pri == CN_DEAD)
162 continue;
163 if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
164 best_cn = cn;
165 if (boothowto & RB_MULTIPLE) {
166 /*
167 * Initialize console, and attach to it.
168 */
169 cnadd(cn);
170 cn->cn_init(cn);
171 }
172 }
173 if (best_cn == NULL)
174 return;
175 if ((boothowto & RB_MULTIPLE) == 0) {
176 cnadd(best_cn);
177 best_cn->cn_init(best_cn);
178 }
179 if (boothowto & RB_PAUSE)
180 console_pausing = 1;
181 /*
182 * Make the best console the preferred console.
183 */
184 cnselect(best_cn);
185}
186
187void
188cninit_finish()
189{
190 console_pausing = 0;
191}
192
193/* add a new physical console to back the virtual console */
194int
195cnadd(struct consdev *cn)
196{
197 struct cn_device *cnd;
198 int i;
199
200 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
201 if (cnd->cnd_cn == cn)
202 return (0);
203 for (i = 0; i < CNDEVTAB_SIZE; i++) {
204 cnd = &cn_devtab[i];
205 if (cnd->cnd_cn == NULL)
206 break;
207 }
208 if (cnd->cnd_cn != NULL)
209 return (ENOMEM);
210 cnd->cnd_cn = cn;
43
44#include "opt_ddb.h"
45
46#include <sys/param.h>
47#include <sys/systm.h>
48#include <sys/conf.h>
49#include <sys/cons.h>
50#include <sys/fcntl.h>
51#include <sys/kernel.h>
52#include <sys/malloc.h>
53#include <sys/msgbuf.h>
54#include <sys/namei.h>
55#include <sys/proc.h>
56#include <sys/queue.h>
57#include <sys/reboot.h>
58#include <sys/sysctl.h>
59#include <sys/tty.h>
60#include <sys/uio.h>
61#include <sys/vnode.h>
62
63#include <ddb/ddb.h>
64
65#include <machine/cpu.h>
66
67static d_open_t cnopen;
68static d_close_t cnclose;
69static d_read_t cnread;
70static d_write_t cnwrite;
71static d_ioctl_t cnioctl;
72static d_poll_t cnpoll;
73static d_kqfilter_t cnkqfilter;
74
75static struct cdevsw cn_cdevsw = {
76 .d_open = cnopen,
77 .d_close = cnclose,
78 .d_read = cnread,
79 .d_write = cnwrite,
80 .d_ioctl = cnioctl,
81 .d_poll = cnpoll,
82 .d_name = "console",
83 .d_maj = 256,
84 /*
85 * XXX: We really want major #0, but zero here means
86 * XXX: allocate a major number automatically.
87 * XXX: kern_conf.c knows what to do when it sees 256.
88 */
89 .d_flags = D_TTY,
90 .d_kqfilter = cnkqfilter,
91};
92
93struct cn_device {
94 STAILQ_ENTRY(cn_device) cnd_next;
95 struct vnode *cnd_vp;
96 struct consdev *cnd_cn;
97};
98
99#define CNDEVPATHMAX 32
100#define CNDEVTAB_SIZE 4
101static struct cn_device cn_devtab[CNDEVTAB_SIZE];
102static STAILQ_HEAD(, cn_device) cn_devlist =
103 STAILQ_HEAD_INITIALIZER(cn_devlist);
104
105#define CND_INVALID(cnd, td) \
106 (cnd == NULL || cnd->cnd_vp == NULL || \
107 (cnd->cnd_vp->v_type == VBAD && !cn_devopen(cnd, td, 1)))
108
109static udev_t cn_udev_t;
110SYSCTL_OPAQUE(_machdep, CPU_CONSDEV, consdev, CTLFLAG_RD,
111 &cn_udev_t, sizeof cn_udev_t, "T,dev_t", "");
112
113int cons_unavail = 0; /* XXX:
114 * physical console not available for
115 * input (i.e., it is in graphics mode)
116 */
117static int cn_mute;
118static int openflag; /* how /dev/console was opened */
119static int cn_is_open;
120static char *consbuf; /* buffer used by `consmsgbuf' */
121static struct callout conscallout; /* callout for outputting to constty */
122struct msgbuf consmsgbuf; /* message buffer for console tty */
123static u_char console_pausing; /* pause after each line during probe */
124static char *console_pausestr=
125"<pause; press any key to proceed to next line or '.' to end pause mode>";
126struct tty *constty; /* pointer to console "window" tty */
127
128void cndebug(char *);
129static void constty_timeout(void *arg);
130
131CONS_DRIVER(cons, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
132SET_DECLARE(cons_set, struct consdev);
133
134void
135cninit(void)
136{
137 struct consdev *best_cn, *cn, **list;
138
139 /*
140 * Check if we should mute the console (for security reasons perhaps)
141 * It can be changes dynamically using sysctl kern.consmute
142 * once we are up and going.
143 *
144 */
145 cn_mute = ((boothowto & (RB_MUTE
146 |RB_SINGLE
147 |RB_VERBOSE
148 |RB_ASKNAME
149 |RB_CONFIG)) == RB_MUTE);
150
151 /*
152 * Find the first console with the highest priority.
153 */
154 best_cn = NULL;
155 SET_FOREACH(list, cons_set) {
156 cn = *list;
157 cnremove(cn);
158 if (cn->cn_probe == NULL)
159 continue;
160 cn->cn_probe(cn);
161 if (cn->cn_pri == CN_DEAD)
162 continue;
163 if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
164 best_cn = cn;
165 if (boothowto & RB_MULTIPLE) {
166 /*
167 * Initialize console, and attach to it.
168 */
169 cnadd(cn);
170 cn->cn_init(cn);
171 }
172 }
173 if (best_cn == NULL)
174 return;
175 if ((boothowto & RB_MULTIPLE) == 0) {
176 cnadd(best_cn);
177 best_cn->cn_init(best_cn);
178 }
179 if (boothowto & RB_PAUSE)
180 console_pausing = 1;
181 /*
182 * Make the best console the preferred console.
183 */
184 cnselect(best_cn);
185}
186
187void
188cninit_finish()
189{
190 console_pausing = 0;
191}
192
193/* add a new physical console to back the virtual console */
194int
195cnadd(struct consdev *cn)
196{
197 struct cn_device *cnd;
198 int i;
199
200 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
201 if (cnd->cnd_cn == cn)
202 return (0);
203 for (i = 0; i < CNDEVTAB_SIZE; i++) {
204 cnd = &cn_devtab[i];
205 if (cnd->cnd_cn == NULL)
206 break;
207 }
208 if (cnd->cnd_cn != NULL)
209 return (ENOMEM);
210 cnd->cnd_cn = cn;
211 if (cn->cn_name[0] == '\0' && cn->cn_dev != NULL) {
212 strcpy(cn->cn_name, devtoname(cn->cn_dev));
213 /* XXX: it is unclear if/where this print might output */
214 printf("NOTE: console \"%s\" didn't set name\n", cn->cn_name);
215 }
216 if (cn->cn_name[0] == '\0') {
217 /* XXX: it is unclear if/where this print might output */
218 printf("WARNING: console at %p has no name\n", cn);
219 }
220 STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
221 return (0);
222}
223
224void
225cnremove(struct consdev *cn)
226{
227 struct cn_device *cnd;
228
229 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
230 if (cnd->cnd_cn != cn)
231 continue;
232 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
233 if (cnd->cnd_vp != NULL)
234 vn_close(cnd->cnd_vp, openflag, NOCRED, NULL);
235 cnd->cnd_vp = NULL;
236 cnd->cnd_cn = NULL;
237#if 0
238 /*
239 * XXX
240 * syscons gets really confused if console resources are
241 * freed after the system has initialized.
242 */
243 if (cn->cn_term != NULL)
244 cn->cn_term(cn);
245#endif
246 return;
247 }
248}
249
250void
251cnselect(struct consdev *cn)
252{
253 struct cn_device *cnd;
254
255 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
256 if (cnd->cnd_cn != cn)
257 continue;
258 if (cnd == STAILQ_FIRST(&cn_devlist))
259 return;
260 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
261 STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
262 return;
263 }
264}
265
266void
267cndebug(char *str)
268{
269 int i, len;
270
271 len = strlen(str);
272 cnputc('>'); cnputc('>'); cnputc('>'); cnputc(' ');
273 for (i = 0; i < len; i++)
274 cnputc(str[i]);
275 cnputc('\n');
276}
277
278/*
279 * XXX: rewrite to use sbufs instead
280 */
281
282static int
283sysctl_kern_console(SYSCTL_HANDLER_ARGS)
284{
285 struct cn_device *cnd;
286 struct consdev *cp, **list;
287 char *name, *p;
288 int delete, len, error;
289
290 len = 2;
291 SET_FOREACH(list, cons_set) {
292 cp = *list;
293 if (cp->cn_name[0] != '\0')
294 len += strlen(cp->cn_name) + 1;
295 }
296 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
297 len += strlen(cnd->cnd_cn->cn_name) + 1;
298 len = len > CNDEVPATHMAX ? len : CNDEVPATHMAX;
299 MALLOC(name, char *, len, M_TEMP, M_WAITOK | M_ZERO);
300 p = name;
301 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
302 p += sprintf(p, "%s,", cnd->cnd_cn->cn_name);
303 *p++ = '/';
304 SET_FOREACH(list, cons_set) {
305 cp = *list;
306 if (cp->cn_name[0] != '\0')
307 p += sprintf(p, "%s,", cp->cn_name);
308 }
309 error = sysctl_handle_string(oidp, name, len, req);
310 if (error == 0 && req->newptr != NULL) {
311 p = name;
312 error = ENXIO;
313 delete = 0;
314 if (*p == '-') {
315 delete = 1;
316 p++;
317 }
318 SET_FOREACH(list, cons_set) {
319 cp = *list;
320 if (strcmp(p, cp->cn_name) != 0)
321 continue;
322 if (delete) {
323 cnremove(cp);
324 error = 0;
325 } else {
326 error = cnadd(cp);
327 if (error == 0)
328 cnselect(cp);
329 }
330 break;
331 }
332 }
333 FREE(name, M_TEMP);
334 return (error);
335}
336
337SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW,
338 0, 0, sysctl_kern_console, "A", "Console device control");
339
340/*
341 * User has changed the state of the console muting.
342 * This may require us to open or close the device in question.
343 */
344static int
345sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
346{
347 int error;
348 int ocn_mute;
349
350 ocn_mute = cn_mute;
351 error = sysctl_handle_int(oidp, &cn_mute, 0, req);
352 if (error != 0 || req->newptr == NULL)
353 return (error);
354 if (ocn_mute && !cn_mute && cn_is_open)
355 error = cnopen(NODEV, openflag, 0, curthread);
356 else if (!ocn_mute && cn_mute && cn_is_open) {
357 error = cnclose(NODEV, openflag, 0, curthread);
358 cn_is_open = 1; /* XXX hack */
359 }
360 return (error);
361}
362
363SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
364 0, sizeof(cn_mute), sysctl_kern_consmute, "I", "");
365
366static int
367cn_devopen(struct cn_device *cnd, struct thread *td, int forceopen)
368{
369 char path[CNDEVPATHMAX];
370 struct nameidata nd;
371 struct vnode *vp;
372 dev_t dev;
373 int error;
374
375 if ((vp = cnd->cnd_vp) != NULL) {
376 if (!forceopen && vp->v_type != VBAD) {
377 dev = vp->v_rdev;
378 return ((*devsw(dev)->d_open)(dev, openflag, 0, td));
379 }
380 cnd->cnd_vp = NULL;
381 vn_close(vp, openflag, td->td_ucred, td);
382 }
383 snprintf(path, sizeof(path), "/dev/%s", cnd->cnd_cn->cn_name);
384 NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td);
385 error = vn_open(&nd, &openflag, 0, -1);
386 if (error == 0) {
387 NDFREE(&nd, NDF_ONLY_PNBUF);
388 VOP_UNLOCK(nd.ni_vp, 0, td);
389 if (nd.ni_vp->v_type == VCHR)
390 cnd->cnd_vp = nd.ni_vp;
391 else
392 vn_close(nd.ni_vp, openflag, td->td_ucred, td);
393 }
394 return (cnd->cnd_vp != NULL);
395}
396
397static int
398cnopen(dev_t dev, int flag, int mode, struct thread *td)
399{
400 struct cn_device *cnd;
401
402 openflag = flag | FWRITE; /* XXX */
403 cn_is_open = 1; /* console is logically open */
404 if (cn_mute)
405 return (0);
406 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
407 cn_devopen(cnd, td, 0);
408 return (0);
409}
410
411static int
412cnclose(dev_t dev, int flag, int mode, struct thread *td)
413{
414 struct cn_device *cnd;
415 struct vnode *vp;
416
417 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
418 if ((vp = cnd->cnd_vp) == NULL)
419 continue;
420 cnd->cnd_vp = NULL;
421 vn_close(vp, openflag, td->td_ucred, td);
422 }
423 cn_is_open = 0;
424 return (0);
425}
426
427static int
428cnread(dev_t dev, struct uio *uio, int flag)
429{
430 struct cn_device *cnd;
431
432 cnd = STAILQ_FIRST(&cn_devlist);
433 if (cn_mute || CND_INVALID(cnd, curthread))
434 return (0);
435 dev = cnd->cnd_vp->v_rdev;
436 return ((*devsw(dev)->d_read)(dev, uio, flag));
437}
438
439static int
440cnwrite(dev_t dev, struct uio *uio, int flag)
441{
442 struct cn_device *cnd;
443
444 cnd = STAILQ_FIRST(&cn_devlist);
445 if (cn_mute || CND_INVALID(cnd, curthread))
446 goto done;
447 if (constty)
448 dev = constty->t_dev;
449 else
450 dev = cnd->cnd_vp->v_rdev;
451 if (dev != NULL) {
452 log_console(uio);
453 return ((*devsw(dev)->d_write)(dev, uio, flag));
454 }
455done:
456 uio->uio_resid = 0; /* dump the data */
457 return (0);
458}
459
460static int
461cnioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
462{
463 struct cn_device *cnd;
464 int error;
465
466 cnd = STAILQ_FIRST(&cn_devlist);
467 if (cn_mute || CND_INVALID(cnd, td))
468 return (0);
469 /*
470 * Superuser can always use this to wrest control of console
471 * output from the "virtual" console.
472 */
473 if (cmd == TIOCCONS && constty) {
474 error = suser(td);
475 if (error)
476 return (error);
477 constty = NULL;
478 return (0);
479 }
480 dev = cnd->cnd_vp->v_rdev;
481 if (dev != NULL)
482 return ((*devsw(dev)->d_ioctl)(dev, cmd, data, flag, td));
483 return (0);
484}
485
486/*
487 * XXX
488 * poll/kqfilter do not appear to be correct
489 */
490static int
491cnpoll(dev_t dev, int events, struct thread *td)
492{
493 struct cn_device *cnd;
494
495 cnd = STAILQ_FIRST(&cn_devlist);
496 if (cn_mute || CND_INVALID(cnd, td))
497 return (0);
498 dev = cnd->cnd_vp->v_rdev;
499 if (dev != NULL)
500 return ((*devsw(dev)->d_poll)(dev, events, td));
501 return (0);
502}
503
504static int
505cnkqfilter(dev_t dev, struct knote *kn)
506{
507 struct cn_device *cnd;
508
509 cnd = STAILQ_FIRST(&cn_devlist);
510 if (cn_mute || CND_INVALID(cnd, curthread))
511 return (1);
512 dev = cnd->cnd_vp->v_rdev;
513 if (dev != NULL)
514 return ((*devsw(dev)->d_kqfilter)(dev, kn));
515 return (1);
516}
517
518/*
519 * Low level console routines.
520 */
521int
522cngetc(void)
523{
524 int c;
525
526 if (cn_mute)
527 return (-1);
528 while ((c = cncheckc()) == -1)
529 ;
530 if (c == '\r')
531 c = '\n'; /* console input is always ICRNL */
532 return (c);
533}
534
535int
536cncheckc(void)
537{
538 struct cn_device *cnd;
539 struct consdev *cn;
540 int c;
541
542 if (cn_mute)
543 return (-1);
544 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
545 cn = cnd->cnd_cn;
546 c = cn->cn_checkc(cn);
547 if (c != -1) {
548 return (c);
549 }
550 }
551 return (-1);
552}
553
554void
555cnputc(int c)
556{
557 struct cn_device *cnd;
558 struct consdev *cn;
559 char *cp;
560
561 if (cn_mute || c == '\0')
562 return;
563 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
564 cn = cnd->cnd_cn;
565 if (c == '\n')
566 cn->cn_putc(cn, '\r');
567 cn->cn_putc(cn, c);
568 }
569#ifdef DDB
570 if (console_pausing && !db_active && (c == '\n')) {
571#else
572 if (console_pausing && (c == '\n')) {
573#endif
574 for (cp = console_pausestr; *cp != '\0'; cp++)
575 cnputc(*cp);
576 if (cngetc() == '.')
577 console_pausing = 0;
578 cnputc('\r');
579 for (cp = console_pausestr; *cp != '\0'; cp++)
580 cnputc(' ');
581 cnputc('\r');
582 }
583}
584
585void
586cndbctl(int on)
587{
588 struct cn_device *cnd;
589 struct consdev *cn;
590 static int refcount;
591
592 if (!on)
593 refcount--;
594 if (refcount == 0)
595 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
596 cn = cnd->cnd_cn;
597 if (cn->cn_dbctl != NULL)
598 cn->cn_dbctl(cn, on);
599 }
600 if (on)
601 refcount++;
602}
603
604static int consmsgbuf_size = 8192;
605SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0,
606 "");
607
608/*
609 * Redirect console output to a tty.
610 */
611void
612constty_set(struct tty *tp)
613{
614 int size;
615
616 KASSERT(tp != NULL, ("constty_set: NULL tp"));
617 if (consbuf == NULL) {
618 size = consmsgbuf_size;
619 consbuf = malloc(size, M_TTYS, M_WAITOK);
620 msgbuf_init(&consmsgbuf, consbuf, size);
621 callout_init(&conscallout, 0);
622 }
623 constty = tp;
624 constty_timeout(NULL);
625}
626
627/*
628 * Disable console redirection to a tty.
629 */
630void
631constty_clear(void)
632{
633 int c;
634
635 constty = NULL;
636 if (consbuf == NULL)
637 return;
638 callout_stop(&conscallout);
639 while ((c = msgbuf_getchar(&consmsgbuf)) != -1)
640 cnputc(c);
641 free(consbuf, M_TTYS);
642 consbuf = NULL;
643}
644
645/* Times per second to check for pending console tty messages. */
646static int constty_wakeups_per_second = 5;
647SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW,
648 &constty_wakeups_per_second, 0, "");
649
650static void
651constty_timeout(void *arg)
652{
653 int c;
654
655 while (constty != NULL && (c = msgbuf_getchar(&consmsgbuf)) != -1) {
656 if (tputchar(c, constty) < 0)
657 constty = NULL;
658 }
659 if (constty != NULL) {
660 callout_reset(&conscallout, hz / constty_wakeups_per_second,
661 constty_timeout, NULL);
662 } else {
663 /* Deallocate the constty buffer memory. */
664 constty_clear();
665 }
666}
667
668static void
669cn_drvinit(void *unused)
670{
671
672 make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "console");
673}
674
675SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL)
211 if (cn->cn_name[0] == '\0') {
212 /* XXX: it is unclear if/where this print might output */
213 printf("WARNING: console at %p has no name\n", cn);
214 }
215 STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
216 return (0);
217}
218
219void
220cnremove(struct consdev *cn)
221{
222 struct cn_device *cnd;
223
224 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
225 if (cnd->cnd_cn != cn)
226 continue;
227 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
228 if (cnd->cnd_vp != NULL)
229 vn_close(cnd->cnd_vp, openflag, NOCRED, NULL);
230 cnd->cnd_vp = NULL;
231 cnd->cnd_cn = NULL;
232#if 0
233 /*
234 * XXX
235 * syscons gets really confused if console resources are
236 * freed after the system has initialized.
237 */
238 if (cn->cn_term != NULL)
239 cn->cn_term(cn);
240#endif
241 return;
242 }
243}
244
245void
246cnselect(struct consdev *cn)
247{
248 struct cn_device *cnd;
249
250 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
251 if (cnd->cnd_cn != cn)
252 continue;
253 if (cnd == STAILQ_FIRST(&cn_devlist))
254 return;
255 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
256 STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
257 return;
258 }
259}
260
261void
262cndebug(char *str)
263{
264 int i, len;
265
266 len = strlen(str);
267 cnputc('>'); cnputc('>'); cnputc('>'); cnputc(' ');
268 for (i = 0; i < len; i++)
269 cnputc(str[i]);
270 cnputc('\n');
271}
272
273/*
274 * XXX: rewrite to use sbufs instead
275 */
276
277static int
278sysctl_kern_console(SYSCTL_HANDLER_ARGS)
279{
280 struct cn_device *cnd;
281 struct consdev *cp, **list;
282 char *name, *p;
283 int delete, len, error;
284
285 len = 2;
286 SET_FOREACH(list, cons_set) {
287 cp = *list;
288 if (cp->cn_name[0] != '\0')
289 len += strlen(cp->cn_name) + 1;
290 }
291 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
292 len += strlen(cnd->cnd_cn->cn_name) + 1;
293 len = len > CNDEVPATHMAX ? len : CNDEVPATHMAX;
294 MALLOC(name, char *, len, M_TEMP, M_WAITOK | M_ZERO);
295 p = name;
296 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
297 p += sprintf(p, "%s,", cnd->cnd_cn->cn_name);
298 *p++ = '/';
299 SET_FOREACH(list, cons_set) {
300 cp = *list;
301 if (cp->cn_name[0] != '\0')
302 p += sprintf(p, "%s,", cp->cn_name);
303 }
304 error = sysctl_handle_string(oidp, name, len, req);
305 if (error == 0 && req->newptr != NULL) {
306 p = name;
307 error = ENXIO;
308 delete = 0;
309 if (*p == '-') {
310 delete = 1;
311 p++;
312 }
313 SET_FOREACH(list, cons_set) {
314 cp = *list;
315 if (strcmp(p, cp->cn_name) != 0)
316 continue;
317 if (delete) {
318 cnremove(cp);
319 error = 0;
320 } else {
321 error = cnadd(cp);
322 if (error == 0)
323 cnselect(cp);
324 }
325 break;
326 }
327 }
328 FREE(name, M_TEMP);
329 return (error);
330}
331
332SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW,
333 0, 0, sysctl_kern_console, "A", "Console device control");
334
335/*
336 * User has changed the state of the console muting.
337 * This may require us to open or close the device in question.
338 */
339static int
340sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
341{
342 int error;
343 int ocn_mute;
344
345 ocn_mute = cn_mute;
346 error = sysctl_handle_int(oidp, &cn_mute, 0, req);
347 if (error != 0 || req->newptr == NULL)
348 return (error);
349 if (ocn_mute && !cn_mute && cn_is_open)
350 error = cnopen(NODEV, openflag, 0, curthread);
351 else if (!ocn_mute && cn_mute && cn_is_open) {
352 error = cnclose(NODEV, openflag, 0, curthread);
353 cn_is_open = 1; /* XXX hack */
354 }
355 return (error);
356}
357
358SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
359 0, sizeof(cn_mute), sysctl_kern_consmute, "I", "");
360
361static int
362cn_devopen(struct cn_device *cnd, struct thread *td, int forceopen)
363{
364 char path[CNDEVPATHMAX];
365 struct nameidata nd;
366 struct vnode *vp;
367 dev_t dev;
368 int error;
369
370 if ((vp = cnd->cnd_vp) != NULL) {
371 if (!forceopen && vp->v_type != VBAD) {
372 dev = vp->v_rdev;
373 return ((*devsw(dev)->d_open)(dev, openflag, 0, td));
374 }
375 cnd->cnd_vp = NULL;
376 vn_close(vp, openflag, td->td_ucred, td);
377 }
378 snprintf(path, sizeof(path), "/dev/%s", cnd->cnd_cn->cn_name);
379 NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td);
380 error = vn_open(&nd, &openflag, 0, -1);
381 if (error == 0) {
382 NDFREE(&nd, NDF_ONLY_PNBUF);
383 VOP_UNLOCK(nd.ni_vp, 0, td);
384 if (nd.ni_vp->v_type == VCHR)
385 cnd->cnd_vp = nd.ni_vp;
386 else
387 vn_close(nd.ni_vp, openflag, td->td_ucred, td);
388 }
389 return (cnd->cnd_vp != NULL);
390}
391
392static int
393cnopen(dev_t dev, int flag, int mode, struct thread *td)
394{
395 struct cn_device *cnd;
396
397 openflag = flag | FWRITE; /* XXX */
398 cn_is_open = 1; /* console is logically open */
399 if (cn_mute)
400 return (0);
401 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
402 cn_devopen(cnd, td, 0);
403 return (0);
404}
405
406static int
407cnclose(dev_t dev, int flag, int mode, struct thread *td)
408{
409 struct cn_device *cnd;
410 struct vnode *vp;
411
412 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
413 if ((vp = cnd->cnd_vp) == NULL)
414 continue;
415 cnd->cnd_vp = NULL;
416 vn_close(vp, openflag, td->td_ucred, td);
417 }
418 cn_is_open = 0;
419 return (0);
420}
421
422static int
423cnread(dev_t dev, struct uio *uio, int flag)
424{
425 struct cn_device *cnd;
426
427 cnd = STAILQ_FIRST(&cn_devlist);
428 if (cn_mute || CND_INVALID(cnd, curthread))
429 return (0);
430 dev = cnd->cnd_vp->v_rdev;
431 return ((*devsw(dev)->d_read)(dev, uio, flag));
432}
433
434static int
435cnwrite(dev_t dev, struct uio *uio, int flag)
436{
437 struct cn_device *cnd;
438
439 cnd = STAILQ_FIRST(&cn_devlist);
440 if (cn_mute || CND_INVALID(cnd, curthread))
441 goto done;
442 if (constty)
443 dev = constty->t_dev;
444 else
445 dev = cnd->cnd_vp->v_rdev;
446 if (dev != NULL) {
447 log_console(uio);
448 return ((*devsw(dev)->d_write)(dev, uio, flag));
449 }
450done:
451 uio->uio_resid = 0; /* dump the data */
452 return (0);
453}
454
455static int
456cnioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
457{
458 struct cn_device *cnd;
459 int error;
460
461 cnd = STAILQ_FIRST(&cn_devlist);
462 if (cn_mute || CND_INVALID(cnd, td))
463 return (0);
464 /*
465 * Superuser can always use this to wrest control of console
466 * output from the "virtual" console.
467 */
468 if (cmd == TIOCCONS && constty) {
469 error = suser(td);
470 if (error)
471 return (error);
472 constty = NULL;
473 return (0);
474 }
475 dev = cnd->cnd_vp->v_rdev;
476 if (dev != NULL)
477 return ((*devsw(dev)->d_ioctl)(dev, cmd, data, flag, td));
478 return (0);
479}
480
481/*
482 * XXX
483 * poll/kqfilter do not appear to be correct
484 */
485static int
486cnpoll(dev_t dev, int events, struct thread *td)
487{
488 struct cn_device *cnd;
489
490 cnd = STAILQ_FIRST(&cn_devlist);
491 if (cn_mute || CND_INVALID(cnd, td))
492 return (0);
493 dev = cnd->cnd_vp->v_rdev;
494 if (dev != NULL)
495 return ((*devsw(dev)->d_poll)(dev, events, td));
496 return (0);
497}
498
499static int
500cnkqfilter(dev_t dev, struct knote *kn)
501{
502 struct cn_device *cnd;
503
504 cnd = STAILQ_FIRST(&cn_devlist);
505 if (cn_mute || CND_INVALID(cnd, curthread))
506 return (1);
507 dev = cnd->cnd_vp->v_rdev;
508 if (dev != NULL)
509 return ((*devsw(dev)->d_kqfilter)(dev, kn));
510 return (1);
511}
512
513/*
514 * Low level console routines.
515 */
516int
517cngetc(void)
518{
519 int c;
520
521 if (cn_mute)
522 return (-1);
523 while ((c = cncheckc()) == -1)
524 ;
525 if (c == '\r')
526 c = '\n'; /* console input is always ICRNL */
527 return (c);
528}
529
530int
531cncheckc(void)
532{
533 struct cn_device *cnd;
534 struct consdev *cn;
535 int c;
536
537 if (cn_mute)
538 return (-1);
539 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
540 cn = cnd->cnd_cn;
541 c = cn->cn_checkc(cn);
542 if (c != -1) {
543 return (c);
544 }
545 }
546 return (-1);
547}
548
549void
550cnputc(int c)
551{
552 struct cn_device *cnd;
553 struct consdev *cn;
554 char *cp;
555
556 if (cn_mute || c == '\0')
557 return;
558 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
559 cn = cnd->cnd_cn;
560 if (c == '\n')
561 cn->cn_putc(cn, '\r');
562 cn->cn_putc(cn, c);
563 }
564#ifdef DDB
565 if (console_pausing && !db_active && (c == '\n')) {
566#else
567 if (console_pausing && (c == '\n')) {
568#endif
569 for (cp = console_pausestr; *cp != '\0'; cp++)
570 cnputc(*cp);
571 if (cngetc() == '.')
572 console_pausing = 0;
573 cnputc('\r');
574 for (cp = console_pausestr; *cp != '\0'; cp++)
575 cnputc(' ');
576 cnputc('\r');
577 }
578}
579
580void
581cndbctl(int on)
582{
583 struct cn_device *cnd;
584 struct consdev *cn;
585 static int refcount;
586
587 if (!on)
588 refcount--;
589 if (refcount == 0)
590 STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
591 cn = cnd->cnd_cn;
592 if (cn->cn_dbctl != NULL)
593 cn->cn_dbctl(cn, on);
594 }
595 if (on)
596 refcount++;
597}
598
599static int consmsgbuf_size = 8192;
600SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0,
601 "");
602
603/*
604 * Redirect console output to a tty.
605 */
606void
607constty_set(struct tty *tp)
608{
609 int size;
610
611 KASSERT(tp != NULL, ("constty_set: NULL tp"));
612 if (consbuf == NULL) {
613 size = consmsgbuf_size;
614 consbuf = malloc(size, M_TTYS, M_WAITOK);
615 msgbuf_init(&consmsgbuf, consbuf, size);
616 callout_init(&conscallout, 0);
617 }
618 constty = tp;
619 constty_timeout(NULL);
620}
621
622/*
623 * Disable console redirection to a tty.
624 */
625void
626constty_clear(void)
627{
628 int c;
629
630 constty = NULL;
631 if (consbuf == NULL)
632 return;
633 callout_stop(&conscallout);
634 while ((c = msgbuf_getchar(&consmsgbuf)) != -1)
635 cnputc(c);
636 free(consbuf, M_TTYS);
637 consbuf = NULL;
638}
639
640/* Times per second to check for pending console tty messages. */
641static int constty_wakeups_per_second = 5;
642SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW,
643 &constty_wakeups_per_second, 0, "");
644
645static void
646constty_timeout(void *arg)
647{
648 int c;
649
650 while (constty != NULL && (c = msgbuf_getchar(&consmsgbuf)) != -1) {
651 if (tputchar(c, constty) < 0)
652 constty = NULL;
653 }
654 if (constty != NULL) {
655 callout_reset(&conscallout, hz / constty_wakeups_per_second,
656 constty_timeout, NULL);
657 } else {
658 /* Deallocate the constty buffer memory. */
659 constty_clear();
660 }
661}
662
663static void
664cn_drvinit(void *unused)
665{
666
667 make_dev(&cn_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "console");
668}
669
670SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL)