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