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