/* * linux/kernel/printk.c * * Copyright (C) 1991, 1992 Linus Torvalds * * Modified to make sys_syslog() more flexible: added commands to * return the last 4k of kernel messages, regardless of whether * they've been read or not. Added option to suppress kernel printk's * to the console. Added hook for sending the console messages * elsewhere, in preparation for a serial line console (someday). * Ted Ts'o, 2/11/93. * Modified for sysctl support, 1/8/97, Chris Horn. * Fixed SMP synchronization, 08/08/99, Manfred Spraul * manfreds@colorfullife.com */ #include <linux/mm.h> #include <linux/tty_driver.h> #include <linux/smp_lock.h> #include <linux/console.h> #include <linux/init.h> #include <asm/uaccess.h> #define LOG_BUF_LEN (16384) #define LOG_BUF_MASK (LOG_BUF_LEN-1) static char buf[1024]; /* printk's without a loglevel use this.. */ #define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */ /* We show everything that is MORE important than this.. */ #define MINIMUM_CONSOLE_LOGLEVEL 1 /* Minimum loglevel we let people use */ #define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG */ unsigned long log_size; DECLARE_WAIT_QUEUE_HEAD(log_wait); /* Keep together for sysctl support */ int console_loglevel = DEFAULT_CONSOLE_LOGLEVEL; int default_message_loglevel = DEFAULT_MESSAGE_LOGLEVEL; int minimum_console_loglevel = MINIMUM_CONSOLE_LOGLEVEL; int default_console_loglevel = DEFAULT_CONSOLE_LOGLEVEL; spinlock_t console_lock = SPIN_LOCK_UNLOCKED; struct console *console_drivers; static char log_buf[LOG_BUF_LEN]; static unsigned long log_start; static unsigned long logged_chars; struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES]; static int preferred_console = -1; /* * Setup a list of consoles. Called from init/main.c */ 58 static int __init console_setup(char *str) { struct console_cmdline *c; char name[sizeof(c->name)]; char *s, *options; int i, idx; /* * Decode str into name, index, options. */ 68 if (str[0] >= '0' && str[0] <= '9') { strcpy(name, "ttyS"); strncpy(name + 4, str, sizeof(name) - 5); 71 } else strncpy(name, str, sizeof(name) - 1); name[sizeof(name) - 1] = 0; 74 if ((options = strchr(str, ',')) != NULL) *(options++) = 0; #ifdef __sparc__ if (!strcmp(str, "ttya")) strcpy(name, "ttyS0"); if (!strcmp(str, "ttyb")) strcpy(name, "ttyS1"); #endif 82 for(s = name; *s; s++) 83 if (*s >= '0' && *s <= '9') 84 break; idx = simple_strtoul(s, NULL, 10); *s = 0; /* * See if this tty is not yet registered, and * if we have a slot free. */ 92 for(i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++) if (strcmp(console_cmdline[i].name, name) == 0 && 94 console_cmdline[i].index == idx) { preferred_console = i; 96 return 1; } 98 if (i == MAX_CMDLINECONSOLES) 99 return 1; preferred_console = i; c = &console_cmdline[i]; memcpy(c->name, name, sizeof(c->name)); c->options = options; c->index = idx; 105 return 1; } __setup("console=", console_setup); /* * Commands to do_syslog: * * 0 -- Close the log. Currently a NOP. * 1 -- Open the log. Currently a NOP. * 2 -- Read from the log. * 3 -- Read all messages remaining in the ring buffer. * 4 -- Read and clear all messages remaining in the ring buffer * 5 -- Clear ring buffer. * 6 -- Disable printk's to console * 7 -- Enable printk's to console * 8 -- Set level of messages printed to console */ 123 int do_syslog(int type, char * buf, int len) { unsigned long i, j, limit, count; int do_clear = 0; char c; int error = -EPERM; error = 0; 131 switch (type) { 132 case 0: /* Close log */ 133 break; 134 case 1: /* Open log */ 135 break; 136 case 2: /* Read from log */ error = -EINVAL; 138 if (!buf || len < 0) 139 goto out; error = 0; 141 if (!len) 142 goto out; error = verify_area(VERIFY_WRITE,buf,len); 144 if (error) 145 goto out; error = wait_event_interruptible(log_wait, log_size); 147 if (error) 148 goto out; i = 0; 150 spin_lock_irq(&console_lock); 151 while (log_size && i < len) { c = log_buf[log_start & LOG_BUF_MASK]; log_start++; log_size--; 155 spin_unlock_irq(&console_lock); __put_user(c,buf); buf++; i++; 159 spin_lock_irq(&console_lock); } 161 spin_unlock_irq(&console_lock); error = i; 163 break; 164 case 4: /* Read/clear last kernel messages */ do_clear = 1; /* FALL THRU */ 167 case 3: /* Read last kernel messages */ error = -EINVAL; 169 if (!buf || len < 0) 170 goto out; error = 0; 172 if (!len) 173 goto out; error = verify_area(VERIFY_WRITE,buf,len); 175 if (error) 176 goto out; count = len; 178 if (count > LOG_BUF_LEN) count = LOG_BUF_LEN; 180 spin_lock_irq(&console_lock); 181 if (count > logged_chars) count = logged_chars; 183 if (do_clear) logged_chars = 0; limit = log_start + log_size; /* * __put_user() could sleep, and while we sleep * printk() could overwrite the messages * we try to copy to user space. Therefore * the messages are copied in reverse. <manfreds> */ 192 for(i=0;i < count;i++) { j = limit-1-i; 194 if (j+LOG_BUF_LEN < log_start+log_size) 195 break; c = log_buf[ j & LOG_BUF_MASK ]; 197 spin_unlock_irq(&console_lock); __put_user(c,&buf[count-1-i]); 199 spin_lock_irq(&console_lock); } 201 spin_unlock_irq(&console_lock); error = i; 203 if(i != count) { int offset = count-error; /* buffer overflow during copy, correct user buffer. */ 206 for(i=0;i<error;i++) { __get_user(c,&buf[i+offset]); __put_user(c,&buf[i]); } } 212 break; 213 case 5: /* Clear ring buffer */ 214 spin_lock_irq(&console_lock); logged_chars = 0; 216 spin_unlock_irq(&console_lock); 217 break; 218 case 6: /* Disable logging to console */ 219 spin_lock_irq(&console_lock); console_loglevel = minimum_console_loglevel; 221 spin_unlock_irq(&console_lock); 222 break; 223 case 7: /* Enable logging to console */ 224 spin_lock_irq(&console_lock); console_loglevel = default_console_loglevel; 226 spin_unlock_irq(&console_lock); 227 break; 228 case 8: error = -EINVAL; 230 if (len < 1 || len > 8) 231 goto out; 232 if (len < minimum_console_loglevel) len = minimum_console_loglevel; 234 spin_lock_irq(&console_lock); console_loglevel = len; 236 spin_unlock_irq(&console_lock); error = 0; 238 break; 239 default: error = -EINVAL; 241 break; } out: 244 return error; } 247 asmlinkage long sys_syslog(int type, char * buf, int len) { 249 if ((type != 3) && !capable(CAP_SYS_ADMIN)) 250 return -EPERM; 251 return do_syslog(type, buf, len); } 254 asmlinkage int printk(const char *fmt, ...) { va_list args; int i; char *msg, *p, *buf_end; int line_feed; static signed char msg_level = -1; long flags; 263 spin_lock_irqsave(&console_lock, flags); va_start(args, fmt); i = vsprintf(buf + 3, fmt, args); /* hopefully i < sizeof(buf)-4 */ buf_end = buf + 3 + i; va_end(args); 268 for (p = buf + 3; p < buf_end; p++) { msg = p; 270 if (msg_level < 0) { if ( p[0] != '<' || p[1] < '0' || p[1] > '7' || 275 p[2] != '>' 276 ) { p -= 3; p[0] = '<'; p[1] = default_message_loglevel + '0'; p[2] = '>'; 281 } else msg += 3; msg_level = p[1] - '0'; } line_feed = 0; 286 for (; p < buf_end; p++) { log_buf[(log_start+log_size) & LOG_BUF_MASK] = *p; 288 if (log_size < LOG_BUF_LEN) log_size++; 290 else log_start++; logged_chars++; 294 if (*p == '\n') { line_feed = 1; 296 break; } } 299 if (msg_level < console_loglevel && console_drivers) { struct console *c = console_drivers; 301 while(c) { 302 if ((c->flags & CON_ENABLED) && c->write) c->write(c, msg, p - msg + line_feed); c = c->next; } } 307 if (line_feed) msg_level = -1; } 310 spin_unlock_irqrestore(&console_lock, flags); wake_up_interruptible(&log_wait); 312 return i; } 315 void console_print(const char *s) { struct console *c; unsigned long flags; int len = strlen(s); 321 spin_lock_irqsave(&console_lock, flags); c = console_drivers; 323 while(c) { 324 if ((c->flags & CON_ENABLED) && c->write) c->write(c, s, len); c = c->next; } 328 spin_unlock_irqrestore(&console_lock, flags); } 331 void unblank_console(void) { struct console *c; unsigned long flags; 336 spin_lock_irqsave(&console_lock, flags); c = console_drivers; 338 while(c) { 339 if ((c->flags & CON_ENABLED) && c->unblank) c->unblank(); c = c->next; } 343 spin_unlock_irqrestore(&console_lock, flags); } /* * The console driver calls this routine during kernel initialization * to register the console printing procedure with printk() and to * print any messages that were printed by the kernel before the * console driver was initialized. */ 352 void register_console(struct console * console) { int i, j,len; int p; char buf[16]; signed char msg_level = -1; char *q; unsigned long flags; /* * See if we want to use this console driver. If we * didn't select a console we take the first one * that registers here. */ 366 if (preferred_console < 0) { 367 if (console->index < 0) console->index = 0; if (console->setup == NULL || 370 console->setup(console, NULL) == 0) { console->flags |= CON_ENABLED | CON_CONSDEV; preferred_console = 0; } } /* * See if this console matches one we selected on * the command line. */ 380 for(i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++) { 381 if (strcmp(console_cmdline[i].name, console->name) != 0) 382 continue; if (console->index >= 0 && 384 console->index != console_cmdline[i].index) 385 continue; 386 if (console->index < 0) console->index = console_cmdline[i].index; if (console->setup && 389 console->setup(console, console_cmdline[i].options) != 0) 390 break; console->flags |= CON_ENABLED; console->index = console_cmdline[i].index; 393 if (i == preferred_console) console->flags |= CON_CONSDEV; 395 break; } 398 if (!(console->flags & CON_ENABLED)) 399 return; /* * Put this console in the list - keep the * preferred driver at the head of the list. */ 405 spin_lock_irqsave(&console_lock, flags); 406 if ((console->flags & CON_CONSDEV) || console_drivers == NULL) { console->next = console_drivers; console_drivers = console; 409 } else { console->next = console_drivers->next; console_drivers->next = console; } 413 if ((console->flags & CON_PRINTBUFFER) == 0) 414 goto done; /* * Print out buffered log messages. */ p = log_start & LOG_BUF_MASK; 420 for (i=0,j=0; i < log_size; i++) { buf[j++] = log_buf[p]; p = (p+1) & LOG_BUF_MASK; 423 if (buf[j-1] != '\n' && i < log_size - 1 && j < sizeof(buf)-1) 424 continue; buf[j] = 0; q = buf; len = j; 428 if (msg_level < 0) { if(buf[0] == '<' && buf[1] >= '0' && buf[1] <= '7' && 432 buf[2] == '>') { msg_level = buf[1] - '0'; q = buf + 3; len -= 3; 436 } else { msg_level = default_message_loglevel; } } 441 if (msg_level < console_loglevel) console->write(console, q, len); 443 if (buf[j-1] == '\n') msg_level = -1; j = 0; } done: 448 spin_unlock_irqrestore(&console_lock, flags); } 452 int unregister_console(struct console * console) { struct console *a,*b; unsigned long flags; int res = 1; 458 spin_lock_irqsave(&console_lock, flags); 459 if (console_drivers == console) { console_drivers=console->next; res = 0; 462 } else { for (a=console_drivers->next, b=console_drivers ; 465 a; b=a, a=b->next) { 466 if (a == console) { b->next = a->next; res = 0; 469 break; } } } /* If last console is removed, we re-enable picking the first * one that gets registered. Without that, pmac early boot console * would prevent fbcon from taking over. */ 478 if (console_drivers == NULL) preferred_console = -1; 482 spin_unlock_irqrestore(&console_lock, flags); 483 return res; } /* * Write a message to a certain tty, not just the console. This is used for * messages that need to be redirected to a specific tty. * We don't put it into the syslog queue right now maybe in the future if * really needed. */ 492 void tty_write_message(struct tty_struct *tty, char *msg) { 494 if (tty && tty->driver.write) tty->driver.write(tty, 0, msg, strlen(msg)); 496 return; }