blob: 8b453a7e685c375f0f8796fd987b4a45ba62f6f2 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
3 * Copyright (C) 2001 - 2003 Jeff Dike (jdike@addtoit.com)
4 * Licensed under the GPL
5 */
6
7#include "linux/kernel.h"
8#include "linux/slab.h"
9#include "linux/init.h"
10#include "linux/notifier.h"
11#include "linux/reboot.h"
12#include "linux/utsname.h"
13#include "linux/ctype.h"
14#include "linux/interrupt.h"
15#include "linux/sysrq.h"
16#include "linux/workqueue.h"
17#include "linux/module.h"
18#include "linux/file.h"
19#include "linux/fs.h"
20#include "linux/namei.h"
21#include "linux/proc_fs.h"
22#include "linux/syscalls.h"
Jeff Dike6f517d32006-01-06 00:19:04 -080023#include "linux/console.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include "asm/irq.h"
25#include "asm/uaccess.h"
26#include "user_util.h"
27#include "kern_util.h"
28#include "kern.h"
29#include "mconsole.h"
30#include "mconsole_kern.h"
31#include "irq_user.h"
32#include "init.h"
33#include "os.h"
34#include "umid.h"
35#include "irq_kern.h"
Jeff Dike3eddddc2005-09-16 19:27:46 -070036#include "choose-mode.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070037
Jeff Diked50084a2006-01-06 00:18:50 -080038static int do_unlink_socket(struct notifier_block *notifier,
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 unsigned long what, void *data)
40{
41 return(mconsole_unlink_socket());
42}
43
44
45static struct notifier_block reboot_notifier = {
46 .notifier_call = do_unlink_socket,
47 .priority = 0,
48};
49
Jeff Diked50084a2006-01-06 00:18:50 -080050/* Safe without explicit locking for now. Tasklets provide their own
Linus Torvalds1da177e2005-04-16 15:20:36 -070051 * locking, and the interrupt handler is safe because it can't interrupt
52 * itself and it can only happen on CPU 0.
53 */
54
Jeff Dike90107722006-01-06 00:18:54 -080055static LIST_HEAD(mc_requests);
Linus Torvalds1da177e2005-04-16 15:20:36 -070056
57static void mc_work_proc(void *unused)
58{
59 struct mconsole_entry *req;
60 unsigned long flags;
61
62 while(!list_empty(&mc_requests)){
63 local_save_flags(flags);
Jeff Diked50084a2006-01-06 00:18:50 -080064 req = list_entry(mc_requests.next, struct mconsole_entry,
Linus Torvalds1da177e2005-04-16 15:20:36 -070065 list);
66 list_del(&req->list);
67 local_irq_restore(flags);
68 req->request.cmd->handler(&req->request);
69 kfree(req);
70 }
71}
72
Jeff Dike90107722006-01-06 00:18:54 -080073static DECLARE_WORK(mconsole_work, mc_work_proc, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -070074
75static irqreturn_t mconsole_interrupt(int irq, void *dev_id,
76 struct pt_regs *regs)
77{
78 /* long to avoid size mismatch warnings from gcc */
79 long fd;
80 struct mconsole_entry *new;
81 struct mc_request req;
82
83 fd = (long) dev_id;
84 while (mconsole_get_request(fd, &req)){
85 if(req.cmd->context == MCONSOLE_INTR)
86 (*req.cmd->handler)(&req);
87 else {
88 new = kmalloc(sizeof(*new), GFP_ATOMIC);
89 if(new == NULL)
90 mconsole_reply(&req, "Out of memory", 1, 0);
91 else {
92 new->request = req;
93 list_add(&new->list, &mc_requests);
94 }
95 }
96 }
97 if(!list_empty(&mc_requests))
98 schedule_work(&mconsole_work);
99 reactivate_fd(fd, MCONSOLE_IRQ);
100 return(IRQ_HANDLED);
101}
102
103void mconsole_version(struct mc_request *req)
104{
105 char version[256];
106
Jeff Diked50084a2006-01-06 00:18:50 -0800107 sprintf(version, "%s %s %s %s %s", system_utsname.sysname,
108 system_utsname.nodename, system_utsname.release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 system_utsname.version, system_utsname.machine);
110 mconsole_reply(req, version, 0, 0);
111}
112
113void mconsole_log(struct mc_request *req)
114{
115 int len;
116 char *ptr = req->request.data;
117
118 ptr += strlen("log ");
119
120 len = req->len - (ptr - req->request.data);
121 printk("%.*s", len, ptr);
122 mconsole_reply(req, "", 0, 0);
123}
124
125/* This is a more convoluted version of mconsole_proc, which has some stability
126 * problems; however, we need it fixed, because it is expected that UML users
127 * mount HPPFS instead of procfs on /proc. And we want mconsole_proc to still
128 * show the real procfs content, not the ones from hppfs.*/
129#if 0
130void mconsole_proc(struct mc_request *req)
131{
132 struct nameidata nd;
133 struct file_system_type *proc;
134 struct super_block *super;
135 struct file *file;
136 int n, err;
137 char *ptr = req->request.data, *buf;
138
139 ptr += strlen("proc");
140 while(isspace(*ptr)) ptr++;
141
142 proc = get_fs_type("proc");
143 if(proc == NULL){
144 mconsole_reply(req, "procfs not registered", 1, 0);
145 goto out;
146 }
147
148 super = (*proc->get_sb)(proc, 0, NULL, NULL);
149 put_filesystem(proc);
150 if(super == NULL){
151 mconsole_reply(req, "Failed to get procfs superblock", 1, 0);
152 goto out;
153 }
154 up_write(&super->s_umount);
155
156 nd.dentry = super->s_root;
157 nd.mnt = NULL;
158 nd.flags = O_RDONLY + 1;
159 nd.last_type = LAST_ROOT;
160
161 /* START: it was experienced that the stability problems are closed
162 * if commenting out these two calls + the below read cycle. To
163 * make UML crash again, it was enough to readd either one.*/
164 err = link_path_walk(ptr, &nd);
165 if(err){
166 mconsole_reply(req, "Failed to look up file", 1, 0);
167 goto out_kill;
168 }
169
170 file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
171 if(IS_ERR(file)){
172 mconsole_reply(req, "Failed to open file", 1, 0);
173 goto out_kill;
174 }
175 /*END*/
176
177 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
178 if(buf == NULL){
179 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
180 goto out_fput;
181 }
182
183 if((file->f_op != NULL) && (file->f_op->read != NULL)){
184 do {
185 n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1,
186 &file->f_pos);
187 if(n >= 0){
188 buf[n] = '\0';
189 mconsole_reply(req, buf, 0, (n > 0));
190 }
191 else {
192 mconsole_reply(req, "Read of file failed",
193 1, 0);
194 goto out_free;
195 }
196 } while(n > 0);
197 }
198 else mconsole_reply(req, "", 0, 0);
199
200 out_free:
201 kfree(buf);
202 out_fput:
203 fput(file);
204 out_kill:
205 deactivate_super(super);
206 out: ;
207}
208#endif
209
210void mconsole_proc(struct mc_request *req)
211{
212 char path[64];
213 char *buf;
214 int len;
215 int fd;
216 int first_chunk = 1;
217 char *ptr = req->request.data;
218
219 ptr += strlen("proc");
220 while(isspace(*ptr)) ptr++;
221 snprintf(path, sizeof(path), "/proc/%s", ptr);
222
223 fd = sys_open(path, 0, 0);
224 if (fd < 0) {
225 mconsole_reply(req, "Failed to open file", 1, 0);
226 printk("open %s: %d\n",path,fd);
227 goto out;
228 }
229
230 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
231 if(buf == NULL){
232 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
233 goto out_close;
234 }
235
236 for (;;) {
237 len = sys_read(fd, buf, PAGE_SIZE-1);
238 if (len < 0) {
239 mconsole_reply(req, "Read of file failed", 1, 0);
240 goto out_free;
241 }
242 /*Begin the file content on his own line.*/
243 if (first_chunk) {
244 mconsole_reply(req, "\n", 0, 1);
245 first_chunk = 0;
246 }
247 if (len == PAGE_SIZE-1) {
248 buf[len] = '\0';
249 mconsole_reply(req, buf, 0, 1);
250 } else {
251 buf[len] = '\0';
252 mconsole_reply(req, buf, 0, 0);
253 break;
254 }
255 }
256
257 out_free:
258 kfree(buf);
259 out_close:
260 sys_close(fd);
261 out:
262 /* nothing */;
263}
264
265#define UML_MCONSOLE_HELPTEXT \
266"Commands: \n\
267 version - Get kernel version \n\
268 help - Print this message \n\
269 halt - Halt UML \n\
270 reboot - Reboot UML \n\
271 config <dev>=<config> - Add a new device to UML; \n\
272 same syntax as command line \n\
273 config <dev> - Query the configuration of a device \n\
274 remove <dev> - Remove a device from UML \n\
275 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
276 cad - invoke the Ctl-Alt-Del handler \n\
277 stop - pause the UML; it will do nothing until it receives a 'go' \n\
278 go - continue the UML after a 'stop' \n\
279 log <string> - make UML enter <string> into the kernel log\n\
280 proc <file> - returns the contents of the UML's /proc/<file>\n\
Jeff Dike3eddddc2005-09-16 19:27:46 -0700281 stack <pid> - returns the stack of the specified pid\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282"
283
284void mconsole_help(struct mc_request *req)
285{
286 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
287}
288
289void mconsole_halt(struct mc_request *req)
290{
291 mconsole_reply(req, "", 0, 0);
292 machine_halt();
293}
294
295void mconsole_reboot(struct mc_request *req)
296{
297 mconsole_reply(req, "", 0, 0);
298 machine_restart(NULL);
299}
300
301extern void ctrl_alt_del(void);
302
303void mconsole_cad(struct mc_request *req)
304{
305 mconsole_reply(req, "", 0, 0);
306 ctrl_alt_del();
307}
308
309void mconsole_go(struct mc_request *req)
310{
311 mconsole_reply(req, "Not stopped", 1, 0);
312}
313
314void mconsole_stop(struct mc_request *req)
315{
316 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
317 os_set_fd_block(req->originating_fd, 1);
318 mconsole_reply(req, "", 0, 0);
319 while(mconsole_get_request(req->originating_fd, req)){
320 if(req->cmd->handler == mconsole_go) break;
321 (*req->cmd->handler)(req);
322 }
323 os_set_fd_block(req->originating_fd, 0);
324 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
325 mconsole_reply(req, "", 0, 0);
326}
327
328/* This list is populated by __initcall routines. */
329
330LIST_HEAD(mconsole_devices);
331
332void mconsole_register_dev(struct mc_device *new)
333{
334 list_add(&new->list, &mconsole_devices);
335}
336
337static struct mc_device *mconsole_find_dev(char *name)
338{
339 struct list_head *ele;
340 struct mc_device *dev;
341
342 list_for_each(ele, &mconsole_devices){
343 dev = list_entry(ele, struct mc_device, list);
344 if(!strncmp(name, dev->name, strlen(dev->name)))
345 return(dev);
346 }
347 return(NULL);
348}
349
350#define CONFIG_BUF_SIZE 64
351
Jeff Diked50084a2006-01-06 00:18:50 -0800352static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 char **),
354 struct mc_request *req, char *name)
355{
356 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
357 int n, size;
358
359 if(get_config == NULL){
360 mconsole_reply(req, "No get_config routine defined", 1, 0);
361 return;
362 }
363
364 error = NULL;
365 size = sizeof(default_buf)/sizeof(default_buf[0]);
366 buf = default_buf;
367
368 while(1){
369 n = (*get_config)(name, buf, size, &error);
370 if(error != NULL){
371 mconsole_reply(req, error, 1, 0);
372 goto out;
373 }
374
375 if(n <= size){
376 mconsole_reply(req, buf, 0, 0);
377 goto out;
378 }
379
380 if(buf != default_buf)
381 kfree(buf);
382
383 size = n;
384 buf = kmalloc(size, GFP_KERNEL);
385 if(buf == NULL){
386 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
387 return;
388 }
389 }
390 out:
391 if(buf != default_buf)
392 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393}
394
395void mconsole_config(struct mc_request *req)
396{
397 struct mc_device *dev;
398 char *ptr = req->request.data, *name;
399 int err;
400
401 ptr += strlen("config");
402 while(isspace(*ptr)) ptr++;
403 dev = mconsole_find_dev(ptr);
404 if(dev == NULL){
405 mconsole_reply(req, "Bad configuration option", 1, 0);
406 return;
407 }
408
409 name = &ptr[strlen(dev->name)];
410 ptr = name;
411 while((*ptr != '=') && (*ptr != '\0'))
412 ptr++;
413
414 if(*ptr == '='){
415 err = (*dev->config)(name);
416 mconsole_reply(req, "", err, 0);
417 }
418 else mconsole_get_config(dev->get_config, req, name);
419}
420
421void mconsole_remove(struct mc_request *req)
422{
Jeff Diked50084a2006-01-06 00:18:50 -0800423 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700424 char *ptr = req->request.data, *err_msg = "";
425 char error[256];
426 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427
428 ptr += strlen("remove");
429 while(isspace(*ptr)) ptr++;
430 dev = mconsole_find_dev(ptr);
431 if(dev == NULL){
432 mconsole_reply(req, "Bad remove option", 1, 0);
433 return;
434 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700435
436 ptr = &ptr[strlen(dev->name)];
437
438 err = 1;
439 n = (*dev->id)(&ptr, &start, &end);
440 if(n < 0){
441 err_msg = "Couldn't parse device number";
442 goto out;
443 }
444 else if((n < start) || (n > end)){
445 sprintf(error, "Invalid device number - must be between "
446 "%d and %d", start, end);
447 err_msg = error;
448 goto out;
449 }
450
451 err = (*dev->remove)(n);
452 switch(err){
453 case -ENODEV:
454 err_msg = "Device doesn't exist";
455 break;
456 case -EBUSY:
457 err_msg = "Device is currently open";
458 break;
459 default:
460 break;
461 }
462 out:
463 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464}
465
Jeff Dike6f517d32006-01-06 00:19:04 -0800466static DEFINE_SPINLOCK(console_lock);
467static LIST_HEAD(clients);
468static char console_buf[MCONSOLE_MAX_DATA];
469static int console_index = 0;
470
471static void console_write(struct console *console, const char *string,
472 unsigned len)
473{
474 struct list_head *ele;
475 int n;
476
477 if(list_empty(&clients))
478 return;
479
480 while(1){
481 n = min(len, ARRAY_SIZE(console_buf) - console_index);
482 strncpy(&console_buf[console_index], string, n);
483 console_index += n;
484 string += n;
485 len -= n;
486 if(len == 0)
487 return;
488
489 list_for_each(ele, &clients){
490 struct mconsole_entry *entry;
491
492 entry = list_entry(ele, struct mconsole_entry, list);
493 mconsole_reply_len(&entry->request, console_buf,
494 console_index, 0, 1);
495 }
496
497 console_index = 0;
498 }
499}
500
501static struct console mc_console = { .name = "mc",
502 .write = console_write,
503 .flags = CON_PRINTBUFFER | CON_ENABLED,
504 .index = -1 };
505
506static int mc_add_console(void)
507{
508 register_console(&mc_console);
509 return 0;
510}
511
512late_initcall(mc_add_console);
513
514static void with_console(struct mc_request *req, void (*proc)(void *),
515 void *arg)
516{
517 struct mconsole_entry entry;
518 unsigned long flags;
519
520 INIT_LIST_HEAD(&entry.list);
521 entry.request = *req;
522 list_add(&entry.list, &clients);
523 spin_lock_irqsave(&console_lock, flags);
524
525 (*proc)(arg);
526
527 mconsole_reply_len(req, console_buf, console_index, 0, 0);
528 console_index = 0;
529
530 spin_unlock_irqrestore(&console_lock, flags);
531 list_del(&entry.list);
532}
533
Jeff Dike4111b022006-01-06 00:19:05 -0800534#ifdef CONFIG_MAGIC_SYSRQ
535static void sysrq_proc(void *arg)
536{
537 char *op = arg;
538
539 handle_sysrq(*op, &current->thread.regs, NULL);
540}
541
542void mconsole_sysrq(struct mc_request *req)
543{
544 char *ptr = req->request.data;
545
546 ptr += strlen("sysrq");
547 while(isspace(*ptr)) ptr++;
548
549 /* With 'b', the system will shut down without a chance to reply,
550 * so in this case, we reply first.
551 */
552 if(*ptr == 'b')
553 mconsole_reply(req, "", 0, 0);
554
555 with_console(req, sysrq_proc, ptr);
556}
557#else
558void mconsole_sysrq(struct mc_request *req)
559{
560 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
561}
562#endif
563
Jeff Dike6f517d32006-01-06 00:19:04 -0800564static void stack_proc(void *arg)
565{
566 struct task_struct *from = current, *to = arg;
567
568 to->thread.saved_task = from;
569 switch_to(from, to, from);
570}
571
Jeff Dike3eddddc2005-09-16 19:27:46 -0700572/* Mconsole stack trace
573 * Added by Allan Graves, Jeff Dike
574 * Dumps a stacks registers to the linux console.
575 * Usage stack <pid>.
576 */
577void do_stack(struct mc_request *req)
578{
579 char *ptr = req->request.data;
580 int pid_requested= -1;
Jeff Dike6f517d32006-01-06 00:19:04 -0800581 struct task_struct *from = NULL;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700582 struct task_struct *to = NULL;
583
584 /* Would be nice:
585 * 1) Send showregs output to mconsole.
586 * 2) Add a way to stack dump all pids.
587 */
588
589 ptr += strlen("stack");
590 while(isspace(*ptr)) ptr++;
591
592 /* Should really check for multiple pids or reject bad args here */
593 /* What do the arguments in mconsole_reply mean? */
594 if(sscanf(ptr, "%d", &pid_requested) == 0){
595 mconsole_reply(req, "Please specify a pid", 1, 0);
596 return;
597 }
598
Jeff Dike6f517d32006-01-06 00:19:04 -0800599 from = current;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700600
Jeff Dike6f517d32006-01-06 00:19:04 -0800601 to = find_task_by_pid(pid_requested);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700602 if((to == NULL) || (pid_requested == 0)) {
603 mconsole_reply(req, "Couldn't find that pid", 1, 0);
604 return;
605 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700606
Jeff Dike6f517d32006-01-06 00:19:04 -0800607 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700608}
609
610void mconsole_stack(struct mc_request *req)
611{
612 /* This command doesn't work in TT mode, so let's check and then
613 * get out of here
614 */
615 CHOOSE_MODE(mconsole_reply(req, "Sorry, this doesn't work in TT mode",
616 1, 0),
617 do_stack(req));
618}
619
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620/* Changed by mconsole_setup, which is __setup, and called before SMP is
621 * active.
622 */
Jeff Diked50084a2006-01-06 00:18:50 -0800623static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624
Jeff Dike90107722006-01-06 00:18:54 -0800625static int mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626{
627 /* long to avoid size mismatch warnings from gcc */
628 long sock;
629 int err;
630 char file[256];
631
632 if(umid_file_name("mconsole", file, sizeof(file))) return(-1);
633 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
634
635 sock = os_create_unix_socket(file, sizeof(file), 1);
636 if (sock < 0){
637 printk("Failed to initialize management console\n");
638 return(1);
639 }
640
641 register_reboot_notifier(&reboot_notifier);
642
643 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
644 SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM,
645 "mconsole", (void *)sock);
646 if (err){
647 printk("Failed to get IRQ for management console\n");
648 return(1);
649 }
650
651 if(notify_socket != NULL){
Jeff Dike970d6e32006-01-06 00:18:48 -0800652 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 if(notify_socket != NULL)
654 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800655 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 strlen(mconsole_socket_name) + 1);
657 else printk(KERN_ERR "mconsole_setup failed to strdup "
658 "string\n");
659 }
660
Jeff Diked50084a2006-01-06 00:18:50 -0800661 printk("mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 MCONSOLE_VERSION, mconsole_socket_name);
663 return(0);
664}
665
666__initcall(mconsole_init);
667
668static int write_proc_mconsole(struct file *file, const char __user *buffer,
669 unsigned long count, void *data)
670{
671 char *buf;
672
673 buf = kmalloc(count + 1, GFP_KERNEL);
Jeff Diked50084a2006-01-06 00:18:50 -0800674 if(buf == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 return(-ENOMEM);
676
677 if(copy_from_user(buf, buffer, count)){
678 count = -EFAULT;
679 goto out;
680 }
681
682 buf[count] = '\0';
683
684 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
685 out:
686 kfree(buf);
687 return(count);
688}
689
690static int create_proc_mconsole(void)
691{
692 struct proc_dir_entry *ent;
693
694 if(notify_socket == NULL) return(0);
695
696 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
697 if(ent == NULL){
Christophe Lucas30f417c2005-07-28 21:16:12 -0700698 printk(KERN_INFO "create_proc_mconsole : create_proc_entry failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 return(0);
700 }
701
702 ent->read_proc = NULL;
703 ent->write_proc = write_proc_mconsole;
704 return(0);
705}
706
707static DEFINE_SPINLOCK(notify_spinlock);
708
709void lock_notify(void)
710{
711 spin_lock(&notify_spinlock);
712}
713
714void unlock_notify(void)
715{
716 spin_unlock(&notify_spinlock);
717}
718
719__initcall(create_proc_mconsole);
720
721#define NOTIFY "=notify:"
722
723static int mconsole_setup(char *str)
724{
725 if(!strncmp(str, NOTIFY, strlen(NOTIFY))){
726 str += strlen(NOTIFY);
727 notify_socket = str;
728 }
729 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
730 return(1);
731}
732
733__setup("mconsole", mconsole_setup);
734
735__uml_help(mconsole_setup,
736"mconsole=notify:<socket>\n"
737" Requests that the mconsole driver send a message to the named Unix\n"
738" socket containing the name of the mconsole socket. This also serves\n"
739" to notify outside processes when UML has booted far enough to respond\n"
740" to mconsole requests.\n\n"
741);
742
743static int notify_panic(struct notifier_block *self, unsigned long unused1,
744 void *ptr)
745{
746 char *message = ptr;
747
748 if(notify_socket == NULL) return(0);
749
Jeff Diked50084a2006-01-06 00:18:50 -0800750 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 strlen(message) + 1);
752 return(0);
753}
754
755static struct notifier_block panic_exit_notifier = {
756 .notifier_call = notify_panic,
757 .next = NULL,
758 .priority = 1
759};
760
761static int add_notifier(void)
762{
763 notifier_chain_register(&panic_notifier_list, &panic_exit_notifier);
764 return(0);
765}
766
767__initcall(add_notifier);
768
769char *mconsole_notify_socket(void)
770{
771 return(notify_socket);
772}
773
774EXPORT_SYMBOL(mconsole_notify_socket);
775
776/*
777 * Overrides for Emacs so that we follow Linus's tabbing style.
778 * Emacs will notice this stuff at the end of the file and automatically
779 * adjust the settings for this buffer only. This must remain at the end
780 * of the file.
781 * ---------------------------------------------------------------------------
782 * Local variables:
783 * c-file-style: "linux"
784 * End:
785 */