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