]> CyberLeo.Net >> Repos - FreeBSD/releng/10.1.git/blob - sys/kern/kern_cons.c
Improve patch for SA-15:04.igmp to solve a potential buffer overflow.
[FreeBSD/releng/10.1.git] / sys / kern / kern_cons.c
1 /*-
2  * Copyright (c) 1988 University of Utah.
3  * Copyright (c) 1991 The Regents of the University of California.
4  * Copyright (c) 1999 Michael Smith
5  * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
6  *
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * the Systems Programming Group of the University of Utah Computer
11  * Science Department.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  *      from: @(#)cons.c        7.2 (Berkeley) 5/9/91
38  */
39
40 #include <sys/cdefs.h>
41 __FBSDID("$FreeBSD$");
42
43 #include "opt_ddb.h"
44 #include "opt_syscons.h"
45
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/lock.h>
49 #include <sys/mutex.h>
50 #include <sys/conf.h>
51 #include <sys/cons.h>
52 #include <sys/fcntl.h>
53 #include <sys/kdb.h>
54 #include <sys/kernel.h>
55 #include <sys/malloc.h>
56 #include <sys/msgbuf.h>
57 #include <sys/namei.h>
58 #include <sys/priv.h>
59 #include <sys/proc.h>
60 #include <sys/queue.h>
61 #include <sys/reboot.h>
62 #include <sys/sysctl.h>
63 #include <sys/sbuf.h>
64 #include <sys/tty.h>
65 #include <sys/uio.h>
66 #include <sys/vnode.h>
67
68 #include <ddb/ddb.h>
69
70 #include <machine/cpu.h>
71 #include <machine/clock.h>
72
73 static MALLOC_DEFINE(M_TTYCONS, "tty console", "tty console handling");
74
75 struct cn_device {
76         STAILQ_ENTRY(cn_device) cnd_next;
77         struct          consdev *cnd_cn;
78 };
79
80 #define CNDEVPATHMAX    32
81 #define CNDEVTAB_SIZE   4
82 static struct cn_device cn_devtab[CNDEVTAB_SIZE];
83 static STAILQ_HEAD(, cn_device) cn_devlist =
84     STAILQ_HEAD_INITIALIZER(cn_devlist);
85
86 int     cons_avail_mask = 0;    /* Bit mask. Each registered low level console
87                                  * which is currently unavailable for inpit
88                                  * (i.e., if it is in graphics mode) will have
89                                  * this bit cleared.
90                                  */
91 static int cn_mute;
92 static char *consbuf;                   /* buffer used by `consmsgbuf' */
93 static struct callout conscallout;      /* callout for outputting to constty */
94 struct msgbuf consmsgbuf;               /* message buffer for console tty */
95 static u_char console_pausing;          /* pause after each line during probe */
96 static char *console_pausestr=
97 "<pause; press any key to proceed to next line or '.' to end pause mode>";
98 struct tty *constty;                    /* pointer to console "window" tty */
99 static struct mtx cnputs_mtx;           /* Mutex for cnputs(). */
100 static int use_cnputs_mtx = 0;          /* != 0 if cnputs_mtx locking reqd. */
101
102 static void constty_timeout(void *arg);
103
104 static struct consdev cons_consdev;
105 DATA_SET(cons_set, cons_consdev);
106 SET_DECLARE(cons_set, struct consdev);
107
108 void
109 cninit(void)
110 {
111         struct consdev *best_cn, *cn, **list;
112
113         /*
114          * Check if we should mute the console (for security reasons perhaps)
115          * It can be changes dynamically using sysctl kern.consmute
116          * once we are up and going.
117          * 
118          */
119         cn_mute = ((boothowto & (RB_MUTE
120                         |RB_SINGLE
121                         |RB_VERBOSE
122                         |RB_ASKNAME)) == RB_MUTE);
123
124         /*
125          * Find the first console with the highest priority.
126          */
127         best_cn = NULL;
128         SET_FOREACH(list, cons_set) {
129                 cn = *list;
130                 cnremove(cn);
131                 /* Skip cons_consdev. */
132                 if (cn->cn_ops == NULL)
133                         continue;
134                 cn->cn_ops->cn_probe(cn);
135                 if (cn->cn_pri == CN_DEAD)
136                         continue;
137                 if (best_cn == NULL || cn->cn_pri > best_cn->cn_pri)
138                         best_cn = cn;
139                 if (boothowto & RB_MULTIPLE) {
140                         /*
141                          * Initialize console, and attach to it.
142                          */
143                         cn->cn_ops->cn_init(cn);
144                         cnadd(cn);
145                 }
146         }
147         if (best_cn == NULL)
148                 return;
149         if ((boothowto & RB_MULTIPLE) == 0) {
150                 best_cn->cn_ops->cn_init(best_cn);
151                 cnadd(best_cn);
152         }
153         if (boothowto & RB_PAUSE)
154                 console_pausing = 1;
155         /*
156          * Make the best console the preferred console.
157          */
158         cnselect(best_cn);
159 }
160
161 void
162 cninit_finish()
163 {
164         console_pausing = 0;
165
166
167 /* add a new physical console to back the virtual console */
168 int
169 cnadd(struct consdev *cn)
170 {
171         struct cn_device *cnd;
172         int i;
173
174         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
175                 if (cnd->cnd_cn == cn)
176                         return (0);
177         for (i = 0; i < CNDEVTAB_SIZE; i++) {
178                 cnd = &cn_devtab[i];
179                 if (cnd->cnd_cn == NULL)
180                         break;
181         }
182         if (cnd->cnd_cn != NULL)
183                 return (ENOMEM);
184         cnd->cnd_cn = cn;
185         if (cn->cn_name[0] == '\0') {
186                 /* XXX: it is unclear if/where this print might output */
187                 printf("WARNING: console at %p has no name\n", cn);
188         }
189         STAILQ_INSERT_TAIL(&cn_devlist, cnd, cnd_next);
190         if (STAILQ_FIRST(&cn_devlist) == cnd)
191                 ttyconsdev_select(cnd->cnd_cn->cn_name);
192
193         /* Add device to the active mask. */
194         cnavailable(cn, (cn->cn_flags & CN_FLAG_NOAVAIL) == 0);
195
196         return (0);
197 }
198
199 void
200 cnremove(struct consdev *cn)
201 {
202         struct cn_device *cnd;
203         int i;
204
205         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
206                 if (cnd->cnd_cn != cn)
207                         continue;
208                 if (STAILQ_FIRST(&cn_devlist) == cnd)
209                         ttyconsdev_select(NULL);
210                 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
211                 cnd->cnd_cn = NULL;
212
213                 /* Remove this device from available mask. */
214                 for (i = 0; i < CNDEVTAB_SIZE; i++) 
215                         if (cnd == &cn_devtab[i]) {
216                                 cons_avail_mask &= ~(1 << i);
217                                 break;
218                         }
219 #if 0
220                 /*
221                  * XXX
222                  * syscons gets really confused if console resources are
223                  * freed after the system has initialized.
224                  */
225                 if (cn->cn_term != NULL)
226                         cn->cn_ops->cn_term(cn);
227 #endif
228                 return;
229         }
230 }
231
232 void
233 cnselect(struct consdev *cn)
234 {
235         struct cn_device *cnd;
236
237         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
238                 if (cnd->cnd_cn != cn)
239                         continue;
240                 if (cnd == STAILQ_FIRST(&cn_devlist))
241                         return;
242                 STAILQ_REMOVE(&cn_devlist, cnd, cn_device, cnd_next);
243                 STAILQ_INSERT_HEAD(&cn_devlist, cnd, cnd_next);
244                 ttyconsdev_select(cnd->cnd_cn->cn_name);
245                 return;
246         }
247 }
248
249 void
250 cnavailable(struct consdev *cn, int available)
251 {
252         int i;
253
254         for (i = 0; i < CNDEVTAB_SIZE; i++) {
255                 if (cn_devtab[i].cnd_cn == cn)
256                         break;
257         }
258         if (available) {
259                 if (i < CNDEVTAB_SIZE)
260                         cons_avail_mask |= (1 << i); 
261                 cn->cn_flags &= ~CN_FLAG_NOAVAIL;
262         } else {
263                 if (i < CNDEVTAB_SIZE)
264                         cons_avail_mask &= ~(1 << i);
265                 cn->cn_flags |= CN_FLAG_NOAVAIL;
266         }
267 }
268
269 int
270 cnunavailable(void)
271 {
272
273         return (cons_avail_mask == 0);
274 }
275
276 /*
277  * sysctl_kern_console() provides output parseable in conscontrol(1).
278  */
279 static int
280 sysctl_kern_console(SYSCTL_HANDLER_ARGS)
281 {
282         struct cn_device *cnd;
283         struct consdev *cp, **list;
284         char *p;
285         int delete, error;
286         struct sbuf *sb;
287
288         sb = sbuf_new(NULL, NULL, CNDEVPATHMAX * 2, SBUF_AUTOEXTEND);
289         if (sb == NULL)
290                 return (ENOMEM);
291         sbuf_clear(sb);
292         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next)
293                 sbuf_printf(sb, "%s,", cnd->cnd_cn->cn_name);
294         sbuf_printf(sb, "/");
295         SET_FOREACH(list, cons_set) {
296                 cp = *list;
297                 if (cp->cn_name[0] != '\0')
298                         sbuf_printf(sb, "%s,", cp->cn_name);
299         }
300         sbuf_finish(sb);
301         error = sysctl_handle_string(oidp, sbuf_data(sb), sbuf_len(sb), req);
302         if (error == 0 && req->newptr != NULL) {
303                 p = sbuf_data(sb);
304                 error = ENXIO;
305                 delete = 0;
306                 if (*p == '-') {
307                         delete = 1;
308                         p++;
309                 }
310                 SET_FOREACH(list, cons_set) {
311                         cp = *list;
312                         if (strcmp(p, cp->cn_name) != 0)
313                                 continue;
314                         if (delete) {
315                                 cnremove(cp);
316                                 error = 0;
317                         } else {
318                                 error = cnadd(cp);
319                                 if (error == 0)
320                                         cnselect(cp);
321                         }
322                         break;
323                 }
324         }
325         sbuf_delete(sb);
326         return (error);
327 }
328
329 SYSCTL_PROC(_kern, OID_AUTO, console, CTLTYPE_STRING|CTLFLAG_RW,
330         0, 0, sysctl_kern_console, "A", "Console device control");
331
332 /*
333  * User has changed the state of the console muting.
334  * This may require us to open or close the device in question.
335  */
336 static int
337 sysctl_kern_consmute(SYSCTL_HANDLER_ARGS)
338 {
339         int error;
340
341         error = sysctl_handle_int(oidp, &cn_mute, 0, req);
342         if (error != 0 || req->newptr == NULL)
343                 return (error);
344         return (error);
345 }
346
347 SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW,
348         0, sizeof(cn_mute), sysctl_kern_consmute, "I",
349         "State of the console muting");
350
351 void
352 cngrab()
353 {
354         struct cn_device *cnd;
355         struct consdev *cn;
356
357         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
358                 cn = cnd->cnd_cn;
359                 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG))
360                         cn->cn_ops->cn_grab(cn);
361         }
362 }
363
364 void
365 cnungrab()
366 {
367         struct cn_device *cnd;
368         struct consdev *cn;
369
370         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
371                 cn = cnd->cnd_cn;
372                 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG))
373                         cn->cn_ops->cn_ungrab(cn);
374         }
375 }
376
377 /*
378  * Low level console routines.
379  */
380 int
381 cngetc(void)
382 {
383         int c;
384
385         if (cn_mute)
386                 return (-1);
387         while ((c = cncheckc()) == -1)
388                 cpu_spinwait();
389         if (c == '\r')
390                 c = '\n';               /* console input is always ICRNL */
391         return (c);
392 }
393
394 int
395 cncheckc(void)
396 {
397         struct cn_device *cnd;
398         struct consdev *cn;
399         int c;
400
401         if (cn_mute)
402                 return (-1);
403         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
404                 cn = cnd->cnd_cn;
405                 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
406                         c = cn->cn_ops->cn_getc(cn);
407                         if (c != -1)
408                                 return (c);
409                 }
410         }
411         return (-1);
412 }
413
414 void
415 cngets(char *cp, size_t size, int visible)
416 {
417         char *lp, *end;
418         int c;
419
420         cngrab();
421
422         lp = cp;
423         end = cp + size - 1;
424         for (;;) {
425                 c = cngetc() & 0177;
426                 switch (c) {
427                 case '\n':
428                 case '\r':
429                         cnputc(c);
430                         *lp = '\0';
431                         cnungrab();
432                         return;
433                 case '\b':
434                 case '\177':
435                         if (lp > cp) {
436                                 if (visible)
437                                         cnputs("\b \b");
438                                 lp--;
439                         }
440                         continue;
441                 case '\0':
442                         continue;
443                 default:
444                         if (lp < end) {
445                                 switch (visible) {
446                                 case GETS_NOECHO:
447                                         break;
448                                 case GETS_ECHOPASS:
449                                         cnputc('*');
450                                         break;
451                                 default:
452                                         cnputc(c);
453                                         break;
454                                 }
455                                 *lp++ = c;
456                         }
457                 }
458         }
459 }
460
461 void
462 cnputc(int c)
463 {
464         struct cn_device *cnd;
465         struct consdev *cn;
466         char *cp;
467
468 #ifdef EARLY_PRINTF
469         if (early_putc != NULL) {
470                 if (c == '\n')
471                         early_putc('\r');
472                 early_putc(c);
473                 return;
474         }
475 #endif
476
477         if (cn_mute || c == '\0')
478                 return;
479         STAILQ_FOREACH(cnd, &cn_devlist, cnd_next) {
480                 cn = cnd->cnd_cn;
481                 if (!kdb_active || !(cn->cn_flags & CN_FLAG_NODEBUG)) {
482                         if (c == '\n')
483                                 cn->cn_ops->cn_putc(cn, '\r');
484                         cn->cn_ops->cn_putc(cn, c);
485                 }
486         }
487         if (console_pausing && c == '\n' && !kdb_active) {
488                 for (cp = console_pausestr; *cp != '\0'; cp++)
489                         cnputc(*cp);
490                 cngrab();
491                 if (cngetc() == '.')
492                         console_pausing = 0;
493                 cnungrab();
494                 cnputc('\r');
495                 for (cp = console_pausestr; *cp != '\0'; cp++)
496                         cnputc(' ');
497                 cnputc('\r');
498         }
499 }
500
501 void
502 cnputs(char *p)
503 {
504         int c;
505         int unlock_reqd = 0;
506
507         if (use_cnputs_mtx) {
508                 mtx_lock_spin(&cnputs_mtx);
509                 unlock_reqd = 1;
510         }
511
512         while ((c = *p++) != '\0')
513                 cnputc(c);
514
515         if (unlock_reqd)
516                 mtx_unlock_spin(&cnputs_mtx);
517 }
518
519 static int consmsgbuf_size = 8192;
520 SYSCTL_INT(_kern, OID_AUTO, consmsgbuf_size, CTLFLAG_RW, &consmsgbuf_size, 0,
521     "Console tty buffer size");
522
523 /*
524  * Redirect console output to a tty.
525  */
526 void
527 constty_set(struct tty *tp)
528 {
529         int size;
530
531         KASSERT(tp != NULL, ("constty_set: NULL tp"));
532         if (consbuf == NULL) {
533                 size = consmsgbuf_size;
534                 consbuf = malloc(size, M_TTYCONS, M_WAITOK);
535                 msgbuf_init(&consmsgbuf, consbuf, size);
536                 callout_init(&conscallout, 0);
537         }
538         constty = tp;
539         constty_timeout(NULL);
540 }
541
542 /*
543  * Disable console redirection to a tty.
544  */
545 void
546 constty_clear(void)
547 {
548         int c;
549
550         constty = NULL;
551         if (consbuf == NULL)
552                 return;
553         callout_stop(&conscallout);
554         while ((c = msgbuf_getchar(&consmsgbuf)) != -1)
555                 cnputc(c);
556         free(consbuf, M_TTYCONS);
557         consbuf = NULL;
558 }
559
560 /* Times per second to check for pending console tty messages. */
561 static int constty_wakeups_per_second = 5;
562 SYSCTL_INT(_kern, OID_AUTO, constty_wakeups_per_second, CTLFLAG_RW,
563     &constty_wakeups_per_second, 0,
564     "Times per second to check for pending console tty messages");
565
566 static void
567 constty_timeout(void *arg)
568 {
569         int c;
570
571         if (constty != NULL) {
572                 tty_lock(constty);
573                 while ((c = msgbuf_getchar(&consmsgbuf)) != -1) {
574                         if (tty_putchar(constty, c) < 0) {
575                                 tty_unlock(constty);
576                                 constty = NULL;
577                                 break;
578                         }
579                 }
580
581                 if (constty != NULL)
582                         tty_unlock(constty);
583         }
584         if (constty != NULL) {
585                 callout_reset(&conscallout, hz / constty_wakeups_per_second,
586                     constty_timeout, NULL);
587         } else {
588                 /* Deallocate the constty buffer memory. */
589                 constty_clear();
590         }
591 }
592
593 static void
594 cn_drvinit(void *unused)
595 {
596
597         mtx_init(&cnputs_mtx, "cnputs_mtx", NULL, MTX_SPIN | MTX_NOWITNESS);
598         use_cnputs_mtx = 1;
599 }
600
601 SYSINIT(cndev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, cn_drvinit, NULL);
602
603 /*
604  * Sysbeep(), if we have hardware for it
605  */
606
607 #ifdef HAS_TIMER_SPKR
608
609 static int beeping;
610
611 static void
612 sysbeepstop(void *chan)
613 {
614
615         timer_spkr_release();
616         beeping = 0;
617 }
618
619 int
620 sysbeep(int pitch, int period)
621 {
622
623         if (timer_spkr_acquire()) {
624                 if (!beeping) {
625                         /* Something else owns it. */
626                         return (EBUSY);
627                 }
628         }
629         timer_spkr_setfreq(pitch);
630         if (!beeping) {
631                 beeping = period;
632                 timeout(sysbeepstop, (void *)NULL, period);
633         }
634         return (0);
635 }
636
637 #else
638
639 /*
640  * No hardware, no sound
641  */
642
643 int
644 sysbeep(int pitch __unused, int period __unused)
645 {
646
647         return (ENODEV);
648 }
649
650 #endif
651
652 /*
653  * Temporary support for sc(4) to vt(4) transition.
654  */
655 static unsigned vty_prefer;
656 static char vty_name[16];
657 SYSCTL_STRING(_kern, OID_AUTO, vty, CTLFLAG_RDTUN, vty_name, 0,
658     "Console vty driver");
659
660 int
661 vty_enabled(unsigned vty)
662 {
663         static unsigned vty_selected = 0;
664
665         if (vty_selected == 0) {
666                 TUNABLE_STR_FETCH("kern.vty", vty_name, sizeof(vty_name));
667                 do {
668 #if defined(DEV_SC)
669                         if (strcmp(vty_name, "sc") == 0) {
670                                 vty_selected = VTY_SC;
671                                 break;
672                         }
673 #endif
674 #if defined(DEV_VT)
675                         if (strcmp(vty_name, "vt") == 0) {
676                                 vty_selected = VTY_VT;
677                                 break;
678                         }
679 #endif
680                         if (vty_prefer != 0) {
681                                 vty_selected = vty_prefer;
682                                 break;
683                         }
684 #if defined(DEV_SC)
685                         vty_selected = VTY_SC;
686 #elif defined(DEV_VT)
687                         vty_selected = VTY_VT;
688 #endif
689                 } while (0);
690
691                 if (vty_selected == VTY_VT)
692                         strcpy(vty_name, "vt");
693                 else if (vty_selected == VTY_SC)
694                         strcpy(vty_name, "sc");
695         }
696         return ((vty_selected & vty) != 0);
697 }
698
699 void
700 vty_set_preferred(unsigned vty)
701 {
702
703         vty_prefer = vty;
704 #if !defined(DEV_SC)
705         vty_prefer &= ~VTY_SC;
706 #endif
707 #if !defined(DEV_VT)
708         vty_prefer &= ~VTY_VT;
709 #endif
710 }
711