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