1
2
3
4
5
6
7#include <linux/console.h>
8#include <linux/ctype.h>
9#include <linux/interrupt.h>
10#include <linux/list.h>
11#include <linux/mm.h>
12#include <linux/module.h>
13#include <linux/notifier.h>
14#include <linux/reboot.h>
15#include <linux/proc_fs.h>
16#include <linux/slab.h>
17#include <linux/syscalls.h>
18#include <linux/utsname.h>
19#include <linux/socket.h>
20#include <linux/un.h>
21#include <linux/workqueue.h>
22#include <linux/mutex.h>
23#include <asm/uaccess.h>
24
25#include "init.h"
26#include "irq_kern.h"
27#include "irq_user.h"
28#include "kern_util.h"
29#include "mconsole.h"
30#include "mconsole_kern.h"
31#include "os.h"
32
33static int do_unlink_socket(struct notifier_block *notifier,
34 unsigned long what, void *data)
35{
36 return mconsole_unlink_socket();
37}
38
39
40static struct notifier_block reboot_notifier = {
41 .notifier_call = do_unlink_socket,
42 .priority = 0,
43};
44
45
46
47
48
49
50static LIST_HEAD(mc_requests);
51
52static void mc_work_proc(struct work_struct *unused)
53{
54 struct mconsole_entry *req;
55 unsigned long flags;
56
57 while (!list_empty(&mc_requests)) {
58 local_irq_save(flags);
59 req = list_entry(mc_requests.next, struct mconsole_entry, list);
60 list_del(&req->list);
61 local_irq_restore(flags);
62 req->request.cmd->handler(&req->request);
63 kfree(req);
64 }
65}
66
67static DECLARE_WORK(mconsole_work, mc_work_proc);
68
69static irqreturn_t mconsole_interrupt(int irq, void *dev_id)
70{
71
72 long fd;
73 struct mconsole_entry *new;
74 static struct mc_request req;
75
76 fd = (long) dev_id;
77 while (mconsole_get_request(fd, &req)) {
78 if (req.cmd->context == MCONSOLE_INTR)
79 (*req.cmd->handler)(&req);
80 else {
81 new = kmalloc(sizeof(*new), GFP_NOWAIT);
82 if (new == NULL)
83 mconsole_reply(&req, "Out of memory", 1, 0);
84 else {
85 new->request = req;
86 new->request.regs = get_irq_regs()->regs;
87 list_add(&new->list, &mc_requests);
88 }
89 }
90 }
91 if (!list_empty(&mc_requests))
92 schedule_work(&mconsole_work);
93 reactivate_fd(fd, MCONSOLE_IRQ);
94 return IRQ_HANDLED;
95}
96
97void mconsole_version(struct mc_request *req)
98{
99 char version[256];
100
101 sprintf(version, "%s %s %s %s %s", utsname()->sysname,
102 utsname()->nodename, utsname()->release, utsname()->version,
103 utsname()->machine);
104 mconsole_reply(req, version, 0, 0);
105}
106
107void mconsole_log(struct mc_request *req)
108{
109 int len;
110 char *ptr = req->request.data;
111
112 ptr += strlen("log ");
113
114 len = req->len - (ptr - req->request.data);
115 printk(KERN_WARNING "%.*s", len, ptr);
116 mconsole_reply(req, "", 0, 0);
117}
118
119
120
121
122
123#if 0
124void mconsole_proc(struct mc_request *req)
125{
126 struct nameidata nd;
127 struct file_system_type *proc;
128 struct super_block *super;
129 struct file *file;
130 int n, err;
131 char *ptr = req->request.data, *buf;
132
133 ptr += strlen("proc");
134 while (isspace(*ptr)) ptr++;
135
136 proc = get_fs_type("proc");
137 if (proc == NULL) {
138 mconsole_reply(req, "procfs not registered", 1, 0);
139 goto out;
140 }
141
142 super = (*proc->get_sb)(proc, 0, NULL, NULL);
143 put_filesystem(proc);
144 if (super == NULL) {
145 mconsole_reply(req, "Failed to get procfs superblock", 1, 0);
146 goto out;
147 }
148 up_write(&super->s_umount);
149
150 nd.path.dentry = super->s_root;
151 nd.path.mnt = NULL;
152 nd.flags = O_RDONLY + 1;
153 nd.last_type = LAST_ROOT;
154
155
156
157
158 err = link_path_walk(ptr, &nd);
159 if (err) {
160 mconsole_reply(req, "Failed to look up file", 1, 0);
161 goto out_kill;
162 }
163
164 file = dentry_open(nd.path.dentry, nd.path.mnt, O_RDONLY,
165 current_cred());
166 if (IS_ERR(file)) {
167 mconsole_reply(req, "Failed to open file", 1, 0);
168 goto out_kill;
169 }
170
171
172 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
173 if (buf == NULL) {
174 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
175 goto out_fput;
176 }
177
178 if ((file->f_op != NULL) && (file->f_op->read != NULL)) {
179 do {
180 n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1,
181 &file->f_pos);
182 if (n >= 0) {
183 buf[n] = '\0';
184 mconsole_reply(req, buf, 0, (n > 0));
185 }
186 else {
187 mconsole_reply(req, "Read of file failed",
188 1, 0);
189 goto out_free;
190 }
191 } while (n > 0);
192 }
193 else mconsole_reply(req, "", 0, 0);
194
195 out_free:
196 kfree(buf);
197 out_fput:
198 fput(file);
199 out_kill:
200 deactivate_super(super);
201 out: ;
202}
203#endif
204
205void mconsole_proc(struct mc_request *req)
206{
207 char path[64];
208 char *buf;
209 int len;
210 int fd;
211 int first_chunk = 1;
212 char *ptr = req->request.data;
213
214 ptr += strlen("proc");
215 while (isspace(*ptr))
216 ptr++;
217 snprintf(path, sizeof(path), "/proc/%s", ptr);
218
219 fd = sys_open(path, 0, 0);
220 if (fd < 0) {
221 mconsole_reply(req, "Failed to open file", 1, 0);
222 printk(KERN_ERR "open %s: %d\n",path,fd);
223 goto out;
224 }
225
226 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
227 if (buf == NULL) {
228 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
229 goto out_close;
230 }
231
232 for (;;) {
233 len = sys_read(fd, buf, PAGE_SIZE-1);
234 if (len < 0) {
235 mconsole_reply(req, "Read of file failed", 1, 0);
236 goto out_free;
237 }
238
239 if (first_chunk) {
240 mconsole_reply(req, "\n", 0, 1);
241 first_chunk = 0;
242 }
243 if (len == PAGE_SIZE-1) {
244 buf[len] = '\0';
245 mconsole_reply(req, buf, 0, 1);
246 } else {
247 buf[len] = '\0';
248 mconsole_reply(req, buf, 0, 0);
249 break;
250 }
251 }
252
253 out_free:
254 kfree(buf);
255 out_close:
256 sys_close(fd);
257 out:
258 ;
259}
260
261#define UML_MCONSOLE_HELPTEXT \
262"Commands: \n\
263 version - Get kernel version \n\
264 help - Print this message \n\
265 halt - Halt UML \n\
266 reboot - Reboot UML \n\
267 config <dev>=<config> - Add a new device to UML; \n\
268 same syntax as command line \n\
269 config <dev> - Query the configuration of a device \n\
270 remove <dev> - Remove a device from UML \n\
271 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
272 cad - invoke the Ctrl-Alt-Del handler \n\
273 stop - pause the UML; it will do nothing until it receives a 'go' \n\
274 go - continue the UML after a 'stop' \n\
275 log <string> - make UML enter <string> into the kernel log\n\
276 proc <file> - returns the contents of the UML's /proc/<file>\n\
277 stack <pid> - returns the stack of the specified pid\n\
278"
279
280void mconsole_help(struct mc_request *req)
281{
282 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
283}
284
285void mconsole_halt(struct mc_request *req)
286{
287 mconsole_reply(req, "", 0, 0);
288 machine_halt();
289}
290
291void mconsole_reboot(struct mc_request *req)
292{
293 mconsole_reply(req, "", 0, 0);
294 machine_restart(NULL);
295}
296
297void mconsole_cad(struct mc_request *req)
298{
299 mconsole_reply(req, "", 0, 0);
300 ctrl_alt_del();
301}
302
303void mconsole_go(struct mc_request *req)
304{
305 mconsole_reply(req, "Not stopped", 1, 0);
306}
307
308void mconsole_stop(struct mc_request *req)
309{
310 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
311 os_set_fd_block(req->originating_fd, 1);
312 mconsole_reply(req, "stopped", 0, 0);
313 for (;;) {
314 if (!mconsole_get_request(req->originating_fd, req))
315 continue;
316 if (req->cmd->handler == mconsole_go)
317 break;
318 if (req->cmd->handler == mconsole_stop) {
319 mconsole_reply(req, "Already stopped", 1, 0);
320 continue;
321 }
322 if (req->cmd->handler == mconsole_sysrq) {
323 struct pt_regs *old_regs;
324 old_regs = set_irq_regs((struct pt_regs *)&req->regs);
325 mconsole_sysrq(req);
326 set_irq_regs(old_regs);
327 continue;
328 }
329 (*req->cmd->handler)(req);
330 }
331 os_set_fd_block(req->originating_fd, 0);
332 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
333 mconsole_reply(req, "", 0, 0);
334}
335
336static DEFINE_SPINLOCK(mc_devices_lock);
337static LIST_HEAD(mconsole_devices);
338
339void mconsole_register_dev(struct mc_device *new)
340{
341 spin_lock(&mc_devices_lock);
342 BUG_ON(!list_empty(&new->list));
343 list_add(&new->list, &mconsole_devices);
344 spin_unlock(&mc_devices_lock);
345}
346
347static struct mc_device *mconsole_find_dev(char *name)
348{
349 struct list_head *ele;
350 struct mc_device *dev;
351
352 list_for_each(ele, &mconsole_devices) {
353 dev = list_entry(ele, struct mc_device, list);
354 if (!strncmp(name, dev->name, strlen(dev->name)))
355 return dev;
356 }
357 return NULL;
358}
359
360#define UNPLUGGED_PER_PAGE \
361 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
362
363struct unplugged_pages {
364 struct list_head list;
365 void *pages[UNPLUGGED_PER_PAGE];
366};
367
368static DEFINE_MUTEX(plug_mem_mutex);
369static unsigned long long unplugged_pages_count = 0;
370static LIST_HEAD(unplugged_pages);
371static int unplug_index = UNPLUGGED_PER_PAGE;
372
373static int mem_config(char *str, char **error_out)
374{
375 unsigned long long diff;
376 int err = -EINVAL, i, add;
377 char *ret;
378
379 if (str[0] != '=') {
380 *error_out = "Expected '=' after 'mem'";
381 goto out;
382 }
383
384 str++;
385 if (str[0] == '-')
386 add = 0;
387 else if (str[0] == '+') {
388 add = 1;
389 }
390 else {
391 *error_out = "Expected increment to start with '-' or '+'";
392 goto out;
393 }
394
395 str++;
396 diff = memparse(str, &ret);
397 if (*ret != '\0') {
398 *error_out = "Failed to parse memory increment";
399 goto out;
400 }
401
402 diff /= PAGE_SIZE;
403
404 mutex_lock(&plug_mem_mutex);
405 for (i = 0; i < diff; i++) {
406 struct unplugged_pages *unplugged;
407 void *addr;
408
409 if (add) {
410 if (list_empty(&unplugged_pages))
411 break;
412
413 unplugged = list_entry(unplugged_pages.next,
414 struct unplugged_pages, list);
415 if (unplug_index > 0)
416 addr = unplugged->pages[--unplug_index];
417 else {
418 list_del(&unplugged->list);
419 addr = unplugged;
420 unplug_index = UNPLUGGED_PER_PAGE;
421 }
422
423 free_page((unsigned long) addr);
424 unplugged_pages_count--;
425 }
426 else {
427 struct page *page;
428
429 page = alloc_page(GFP_ATOMIC);
430 if (page == NULL)
431 break;
432
433 unplugged = page_address(page);
434 if (unplug_index == UNPLUGGED_PER_PAGE) {
435 list_add(&unplugged->list, &unplugged_pages);
436 unplug_index = 0;
437 }
438 else {
439 struct list_head *entry = unplugged_pages.next;
440 addr = unplugged;
441
442 unplugged = list_entry(entry,
443 struct unplugged_pages,
444 list);
445 err = os_drop_memory(addr, PAGE_SIZE);
446 if (err) {
447 printk(KERN_ERR "Failed to release "
448 "memory - errno = %d\n", err);
449 *error_out = "Failed to release memory";
450 goto out_unlock;
451 }
452 unplugged->pages[unplug_index++] = addr;
453 }
454
455 unplugged_pages_count++;
456 }
457 }
458
459 err = 0;
460out_unlock:
461 mutex_unlock(&plug_mem_mutex);
462out:
463 return err;
464}
465
466static int mem_get_config(char *name, char *str, int size, char **error_out)
467{
468 char buf[sizeof("18446744073709551615")];
469 int len = 0;
470
471 sprintf(buf, "%ld", uml_physmem);
472 CONFIG_CHUNK(str, size, len, buf, 1);
473
474 return len;
475}
476
477static int mem_id(char **str, int *start_out, int *end_out)
478{
479 *start_out = 0;
480 *end_out = 0;
481
482 return 0;
483}
484
485static int mem_remove(int n, char **error_out)
486{
487 *error_out = "Memory doesn't support the remove operation";
488 return -EBUSY;
489}
490
491static struct mc_device mem_mc = {
492 .list = LIST_HEAD_INIT(mem_mc.list),
493 .name = "mem",
494 .config = mem_config,
495 .get_config = mem_get_config,
496 .id = mem_id,
497 .remove = mem_remove,
498};
499
500static int __init mem_mc_init(void)
501{
502 if (can_drop_memory())
503 mconsole_register_dev(&mem_mc);
504 else printk(KERN_ERR "Can't release memory to the host - memory "
505 "hotplug won't be supported\n");
506 return 0;
507}
508
509__initcall(mem_mc_init);
510
511#define CONFIG_BUF_SIZE 64
512
513static void mconsole_get_config(int (*get_config)(char *, char *, int,
514 char **),
515 struct mc_request *req, char *name)
516{
517 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
518 int n, size;
519
520 if (get_config == NULL) {
521 mconsole_reply(req, "No get_config routine defined", 1, 0);
522 return;
523 }
524
525 error = NULL;
526 size = ARRAY_SIZE(default_buf);
527 buf = default_buf;
528
529 while (1) {
530 n = (*get_config)(name, buf, size, &error);
531 if (error != NULL) {
532 mconsole_reply(req, error, 1, 0);
533 goto out;
534 }
535
536 if (n <= size) {
537 mconsole_reply(req, buf, 0, 0);
538 goto out;
539 }
540
541 if (buf != default_buf)
542 kfree(buf);
543
544 size = n;
545 buf = kmalloc(size, GFP_KERNEL);
546 if (buf == NULL) {
547 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
548 return;
549 }
550 }
551 out:
552 if (buf != default_buf)
553 kfree(buf);
554}
555
556void mconsole_config(struct mc_request *req)
557{
558 struct mc_device *dev;
559 char *ptr = req->request.data, *name, *error_string = "";
560 int err;
561
562 ptr += strlen("config");
563 while (isspace(*ptr))
564 ptr++;
565 dev = mconsole_find_dev(ptr);
566 if (dev == NULL) {
567 mconsole_reply(req, "Bad configuration option", 1, 0);
568 return;
569 }
570
571 name = &ptr[strlen(dev->name)];
572 ptr = name;
573 while ((*ptr != '=') && (*ptr != '\0'))
574 ptr++;
575
576 if (*ptr == '=') {
577 err = (*dev->config)(name, &error_string);
578 mconsole_reply(req, error_string, err, 0);
579 }
580 else mconsole_get_config(dev->get_config, req, name);
581}
582
583void mconsole_remove(struct mc_request *req)
584{
585 struct mc_device *dev;
586 char *ptr = req->request.data, *err_msg = "";
587 char error[256];
588 int err, start, end, n;
589
590 ptr += strlen("remove");
591 while (isspace(*ptr)) ptr++;
592 dev = mconsole_find_dev(ptr);
593 if (dev == NULL) {
594 mconsole_reply(req, "Bad remove option", 1, 0);
595 return;
596 }
597
598 ptr = &ptr[strlen(dev->name)];
599
600 err = 1;
601 n = (*dev->id)(&ptr, &start, &end);
602 if (n < 0) {
603 err_msg = "Couldn't parse device number";
604 goto out;
605 }
606 else if ((n < start) || (n > end)) {
607 sprintf(error, "Invalid device number - must be between "
608 "%d and %d", start, end);
609 err_msg = error;
610 goto out;
611 }
612
613 err_msg = NULL;
614 err = (*dev->remove)(n, &err_msg);
615 switch(err) {
616 case 0:
617 err_msg = "";
618 break;
619 case -ENODEV:
620 if (err_msg == NULL)
621 err_msg = "Device doesn't exist";
622 break;
623 case -EBUSY:
624 if (err_msg == NULL)
625 err_msg = "Device is currently open";
626 break;
627 default:
628 break;
629 }
630out:
631 mconsole_reply(req, err_msg, err, 0);
632}
633
634struct mconsole_output {
635 struct list_head list;
636 struct mc_request *req;
637};
638
639static DEFINE_SPINLOCK(client_lock);
640static LIST_HEAD(clients);
641static char console_buf[MCONSOLE_MAX_DATA];
642
643static void console_write(struct console *console, const char *string,
644 unsigned int len)
645{
646 struct list_head *ele;
647 int n;
648
649 if (list_empty(&clients))
650 return;
651
652 while (len > 0) {
653 n = min((size_t) len, ARRAY_SIZE(console_buf));
654 strncpy(console_buf, string, n);
655 string += n;
656 len -= n;
657
658 list_for_each(ele, &clients) {
659 struct mconsole_output *entry;
660
661 entry = list_entry(ele, struct mconsole_output, list);
662 mconsole_reply_len(entry->req, console_buf, n, 0, 1);
663 }
664 }
665}
666
667static struct console mc_console = { .name = "mc",
668 .write = console_write,
669 .flags = CON_ENABLED,
670 .index = -1 };
671
672static int mc_add_console(void)
673{
674 register_console(&mc_console);
675 return 0;
676}
677
678late_initcall(mc_add_console);
679
680static void with_console(struct mc_request *req, void (*proc)(void *),
681 void *arg)
682{
683 struct mconsole_output entry;
684 unsigned long flags;
685
686 entry.req = req;
687 spin_lock_irqsave(&client_lock, flags);
688 list_add(&entry.list, &clients);
689 spin_unlock_irqrestore(&client_lock, flags);
690
691 (*proc)(arg);
692
693 mconsole_reply_len(req, "", 0, 0, 0);
694
695 spin_lock_irqsave(&client_lock, flags);
696 list_del(&entry.list);
697 spin_unlock_irqrestore(&client_lock, flags);
698}
699
700#ifdef CONFIG_MAGIC_SYSRQ
701
702#include <linux/sysrq.h>
703
704static void sysrq_proc(void *arg)
705{
706 char *op = arg;
707 handle_sysrq(*op, NULL);
708}
709
710void mconsole_sysrq(struct mc_request *req)
711{
712 char *ptr = req->request.data;
713
714 ptr += strlen("sysrq");
715 while (isspace(*ptr)) ptr++;
716
717
718
719
720
721 if (*ptr == 'b')
722 mconsole_reply(req, "", 0, 0);
723
724 with_console(req, sysrq_proc, ptr);
725}
726#else
727void mconsole_sysrq(struct mc_request *req)
728{
729 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
730}
731#endif
732
733static void stack_proc(void *arg)
734{
735 struct task_struct *from = current, *to = arg;
736
737 to->thread.saved_task = from;
738 switch_to(from, to, from);
739}
740
741
742
743
744
745
746
747void mconsole_stack(struct mc_request *req)
748{
749 char *ptr = req->request.data;
750 int pid_requested= -1;
751 struct task_struct *to = NULL;
752
753
754
755
756
757
758
759 ptr += strlen("stack");
760 while (isspace(*ptr))
761 ptr++;
762
763
764
765
766
767 if (sscanf(ptr, "%d", &pid_requested) == 0) {
768 mconsole_reply(req, "Please specify a pid", 1, 0);
769 return;
770 }
771
772 to = find_task_by_pid_ns(pid_requested, &init_pid_ns);
773 if ((to == NULL) || (pid_requested == 0)) {
774 mconsole_reply(req, "Couldn't find that pid", 1, 0);
775 return;
776 }
777 with_console(req, stack_proc, to);
778}
779
780
781
782
783
784static char *notify_socket = NULL;
785
786static int __init mconsole_init(void)
787{
788
789 long sock;
790 int err;
791 char file[UNIX_PATH_MAX];
792
793 if (umid_file_name("mconsole", file, sizeof(file)))
794 return -1;
795 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
796
797 sock = os_create_unix_socket(file, sizeof(file), 1);
798 if (sock < 0) {
799 printk(KERN_ERR "Failed to initialize management console\n");
800 return 1;
801 }
802 if (os_set_fd_block(sock, 0))
803 goto out;
804
805 register_reboot_notifier(&reboot_notifier);
806
807 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
808 IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM,
809 "mconsole", (void *)sock);
810 if (err) {
811 printk(KERN_ERR "Failed to get IRQ for management console\n");
812 goto out;
813 }
814
815 if (notify_socket != NULL) {
816 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
817 if (notify_socket != NULL)
818 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
819 mconsole_socket_name,
820 strlen(mconsole_socket_name) + 1);
821 else printk(KERN_ERR "mconsole_setup failed to strdup "
822 "string\n");
823 }
824
825 printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
826 MCONSOLE_VERSION, mconsole_socket_name);
827 return 0;
828
829 out:
830 os_close_file(sock);
831 return 1;
832}
833
834__initcall(mconsole_init);
835
836static int write_proc_mconsole(struct file *file, const char __user *buffer,
837 unsigned long count, void *data)
838{
839 char *buf;
840
841 buf = kmalloc(count + 1, GFP_KERNEL);
842 if (buf == NULL)
843 return -ENOMEM;
844
845 if (copy_from_user(buf, buffer, count)) {
846 count = -EFAULT;
847 goto out;
848 }
849
850 buf[count] = '\0';
851
852 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
853 out:
854 kfree(buf);
855 return count;
856}
857
858static int create_proc_mconsole(void)
859{
860 struct proc_dir_entry *ent;
861
862 if (notify_socket == NULL)
863 return 0;
864
865 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
866 if (ent == NULL) {
867 printk(KERN_INFO "create_proc_mconsole : create_proc_entry "
868 "failed\n");
869 return 0;
870 }
871
872 ent->read_proc = NULL;
873 ent->write_proc = write_proc_mconsole;
874 return 0;
875}
876
877static DEFINE_SPINLOCK(notify_spinlock);
878
879void lock_notify(void)
880{
881 spin_lock(¬ify_spinlock);
882}
883
884void unlock_notify(void)
885{
886 spin_unlock(¬ify_spinlock);
887}
888
889__initcall(create_proc_mconsole);
890
891#define NOTIFY "notify:"
892
893static int mconsole_setup(char *str)
894{
895 if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
896 str += strlen(NOTIFY);
897 notify_socket = str;
898 }
899 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
900 return 1;
901}
902
903__setup("mconsole=", mconsole_setup);
904
905__uml_help(mconsole_setup,
906"mconsole=notify:<socket>\n"
907" Requests that the mconsole driver send a message to the named Unix\n"
908" socket containing the name of the mconsole socket. This also serves\n"
909" to notify outside processes when UML has booted far enough to respond\n"
910" to mconsole requests.\n\n"
911);
912
913static int notify_panic(struct notifier_block *self, unsigned long unused1,
914 void *ptr)
915{
916 char *message = ptr;
917
918 if (notify_socket == NULL)
919 return 0;
920
921 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
922 strlen(message) + 1);
923 return 0;
924}
925
926static struct notifier_block panic_exit_notifier = {
927 .notifier_call = notify_panic,
928 .next = NULL,
929 .priority = 1
930};
931
932static int add_notifier(void)
933{
934 atomic_notifier_chain_register(&panic_notifier_list,
935 &panic_exit_notifier);
936 return 0;
937}
938
939__initcall(add_notifier);
940
941char *mconsole_notify_socket(void)
942{
943 return notify_socket;
944}
945
946EXPORT_SYMBOL(mconsole_notify_socket);
947