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