]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - arch/um/drivers/mconsole_kern.c
take descriptor-related part of close() to file.c
[mirror_ubuntu-zesty-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>
a3a85a76 25#include <asm/switch_to.h>
827b3f6a 26
ae2587e4
JD
27#include "init.h"
28#include "irq_kern.h"
29#include "irq_user.h"
1da177e4 30#include "kern_util.h"
1da177e4
LT
31#include "mconsole.h"
32#include "mconsole_kern.h"
1da177e4 33#include "os.h"
1da177e4 34
d50084a2 35static int do_unlink_socket(struct notifier_block *notifier,
1da177e4
LT
36 unsigned long what, void *data)
37{
ae2587e4 38 return mconsole_unlink_socket();
1da177e4
LT
39}
40
41
42static struct notifier_block reboot_notifier = {
43 .notifier_call = do_unlink_socket,
44 .priority = 0,
45};
46
d50084a2 47/* Safe without explicit locking for now. Tasklets provide their own
1da177e4
LT
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
9010772c 52static LIST_HEAD(mc_requests);
1da177e4 53
6d5aefb8 54static void mc_work_proc(struct work_struct *unused)
1da177e4
LT
55{
56 struct mconsole_entry *req;
57 unsigned long flags;
58
ae2587e4 59 while (!list_empty(&mc_requests)) {
dbdb4c06 60 local_irq_save(flags);
ae2587e4 61 req = list_entry(mc_requests.next, struct mconsole_entry, list);
1da177e4
LT
62 list_del(&req->list);
63 local_irq_restore(flags);
64 req->request.cmd->handler(&req->request);
65 kfree(req);
66 }
67}
68
6d5aefb8 69static DECLARE_WORK(mconsole_work, mc_work_proc);
1da177e4 70
7bea96fd 71static irqreturn_t mconsole_interrupt(int irq, void *dev_id)
1da177e4
LT
72{
73 /* long to avoid size mismatch warnings from gcc */
74 long fd;
75 struct mconsole_entry *new;
3a51237d 76 static struct mc_request req; /* that's OK */
1da177e4
LT
77
78 fd = (long) dev_id;
ae2587e4
JD
79 while (mconsole_get_request(fd, &req)) {
80 if (req.cmd->context == MCONSOLE_INTR)
1da177e4
LT
81 (*req.cmd->handler)(&req);
82 else {
60baa158 83 new = kmalloc(sizeof(*new), GFP_NOWAIT);
ae2587e4 84 if (new == NULL)
1da177e4
LT
85 mconsole_reply(&req, "Out of memory", 1, 0);
86 else {
87 new->request = req;
3a51237d 88 new->request.regs = get_irq_regs()->regs;
1da177e4
LT
89 list_add(&new->list, &mc_requests);
90 }
91 }
92 }
ae2587e4 93 if (!list_empty(&mc_requests))
1da177e4
LT
94 schedule_work(&mconsole_work);
95 reactivate_fd(fd, MCONSOLE_IRQ);
ae2587e4 96 return IRQ_HANDLED;
1da177e4
LT
97}
98
99void mconsole_version(struct mc_request *req)
100{
101 char version[256];
102
e9ff3990 103 sprintf(version, "%s %s %s %s %s", utsname()->sysname,
ae2587e4
JD
104 utsname()->nodename, utsname()->release, utsname()->version,
105 utsname()->machine);
1da177e4
LT
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);
ae2587e4 117 printk(KERN_WARNING "%.*s", len, ptr);
1da177e4
LT
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{
4ecf09fd 128 struct vfsmount *mnt = current->nsproxy->pid_ns->proc_mnt;
1da177e4 129 struct file *file;
73d049a4 130 int n;
1da177e4 131 char *ptr = req->request.data, *buf;
4ecf09fd 132 mm_segment_t old_fs = get_fs();
1da177e4
LT
133
134 ptr += strlen("proc");
e7d2860b 135 ptr = skip_spaces(ptr);
1da177e4 136
73d049a4 137 file = file_open_root(mnt->mnt_root, mnt, ptr, O_RDONLY);
ae2587e4 138 if (IS_ERR(file)) {
1da177e4 139 mconsole_reply(req, "Failed to open file", 1, 0);
4ecf09fd 140 goto out;
1da177e4 141 }
1da177e4
LT
142
143 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
ae2587e4 144 if (buf == NULL) {
1da177e4
LT
145 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
146 goto out_fput;
147 }
148
4ecf09fd 149 if (file->f_op->read) {
1da177e4 150 do {
4ecf09fd
AV
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);
ae2587e4 156 if (n >= 0) {
1da177e4
LT
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 }
ae2587e4 165 } while (n > 0);
1da177e4
LT
166 }
167 else mconsole_reply(req, "", 0, 0);
168
169 out_free:
170 kfree(buf);
171 out_fput:
172 fput(file);
1da177e4
LT
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");
e7d2860b 187 ptr = skip_spaces(ptr);
1da177e4
LT
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);
ae2587e4 193 printk(KERN_ERR "open %s: %d\n",path,fd);
1da177e4
LT
194 goto out;
195 }
196
197 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
ae2587e4 198 if (buf == NULL) {
1da177e4
LT
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 }
ae2587e4 209 /* Begin the file content on his own line. */
1da177e4
LT
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\
db805812 243 cad - invoke the Ctrl-Alt-Del handler \n\
1da177e4
LT
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\
3eddddcf 248 stack <pid> - returns the stack of the specified pid\n\
1da177e4
LT
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
1da177e4
LT
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);
3a51237d 283 mconsole_reply(req, "stopped", 0, 0);
cc0be0fb
KS
284 for (;;) {
285 if (!mconsole_get_request(req->originating_fd, req))
286 continue;
3a51237d
AV
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 }
1da177e4
LT
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
84f48d4f 307static DEFINE_SPINLOCK(mc_devices_lock);
42947cb9 308static LIST_HEAD(mconsole_devices);
1da177e4
LT
309
310void mconsole_register_dev(struct mc_device *new)
311{
84f48d4f
JD
312 spin_lock(&mc_devices_lock);
313 BUG_ON(!list_empty(&new->list));
1da177e4 314 list_add(&new->list, &mconsole_devices);
84f48d4f 315 spin_unlock(&mc_devices_lock);
1da177e4
LT
316}
317
318static struct mc_device *mconsole_find_dev(char *name)
319{
320 struct list_head *ele;
321 struct mc_device *dev;
322
ae2587e4 323 list_for_each(ele, &mconsole_devices) {
1da177e4 324 dev = list_entry(ele, struct mc_device, list);
ae2587e4
JD
325 if (!strncmp(name, dev->name, strlen(dev->name)))
326 return dev;
1da177e4 327 }
ae2587e4 328 return NULL;
1da177e4
LT
329}
330
02dea087
JD
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
e98fa281 339static DEFINE_MUTEX(plug_mem_mutex);
02dea087 340static unsigned long long unplugged_pages_count = 0;
c59bce62 341static LIST_HEAD(unplugged_pages);
02dea087
JD
342static int unplug_index = UNPLUGGED_PER_PAGE;
343
f28169d2 344static int mem_config(char *str, char **error_out)
02dea087
JD
345{
346 unsigned long long diff;
347 int err = -EINVAL, i, add;
348 char *ret;
349
ae2587e4 350 if (str[0] != '=') {
f28169d2 351 *error_out = "Expected '=' after 'mem'";
02dea087 352 goto out;
f28169d2 353 }
02dea087
JD
354
355 str++;
ae2587e4 356 if (str[0] == '-')
02dea087 357 add = 0;
ae2587e4 358 else if (str[0] == '+') {
02dea087
JD
359 add = 1;
360 }
f28169d2
JD
361 else {
362 *error_out = "Expected increment to start with '-' or '+'";
363 goto out;
364 }
02dea087
JD
365
366 str++;
367 diff = memparse(str, &ret);
ae2587e4 368 if (*ret != '\0') {
f28169d2 369 *error_out = "Failed to parse memory increment";
02dea087 370 goto out;
f28169d2 371 }
02dea087
JD
372
373 diff /= PAGE_SIZE;
374
e98fa281 375 mutex_lock(&plug_mem_mutex);
ae2587e4 376 for (i = 0; i < diff; i++) {
02dea087
JD
377 struct unplugged_pages *unplugged;
378 void *addr;
379
ae2587e4
JD
380 if (add) {
381 if (list_empty(&unplugged_pages))
02dea087
JD
382 break;
383
384 unplugged = list_entry(unplugged_pages.next,
385 struct unplugged_pages, list);
ae2587e4 386 if (unplug_index > 0)
02dea087
JD
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);
ae2587e4 401 if (page == NULL)
02dea087
JD
402 break;
403
404 unplugged = page_address(page);
ae2587e4 405 if (unplug_index == UNPLUGGED_PER_PAGE) {
02dea087
JD
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);
02dea087 416 err = os_drop_memory(addr, PAGE_SIZE);
ae2587e4
JD
417 if (err) {
418 printk(KERN_ERR "Failed to release "
419 "memory - errno = %d\n", err);
f28169d2 420 *error_out = "Failed to release memory";
84f48d4f 421 goto out_unlock;
f28169d2
JD
422 }
423 unplugged->pages[unplug_index++] = addr;
02dea087
JD
424 }
425
426 unplugged_pages_count++;
427 }
428 }
429
430 err = 0;
84f48d4f 431out_unlock:
e98fa281 432 mutex_unlock(&plug_mem_mutex);
02dea087
JD
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
f28169d2 456static int mem_remove(int n, char **error_out)
02dea087 457{
f28169d2 458 *error_out = "Memory doesn't support the remove operation";
02dea087
JD
459 return -EBUSY;
460}
461
462static struct mc_device mem_mc = {
84f48d4f 463 .list = LIST_HEAD_INIT(mem_mc.list),
02dea087
JD
464 .name = "mem",
465 .config = mem_config,
466 .get_config = mem_get_config,
467 .id = mem_id,
468 .remove = mem_remove,
469};
470
97a1fcbb 471static int __init mem_mc_init(void)
02dea087 472{
ae2587e4 473 if (can_drop_memory())
02dea087 474 mconsole_register_dev(&mem_mc);
ae2587e4
JD
475 else printk(KERN_ERR "Can't release memory to the host - memory "
476 "hotplug won't be supported\n");
02dea087
JD
477 return 0;
478}
479
480__initcall(mem_mc_init);
481
1da177e4
LT
482#define CONFIG_BUF_SIZE 64
483
d50084a2 484static void mconsole_get_config(int (*get_config)(char *, char *, int,
1da177e4
LT
485 char **),
486 struct mc_request *req, char *name)
487{
488 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
489 int n, size;
490
ae2587e4 491 if (get_config == NULL) {
1da177e4
LT
492 mconsole_reply(req, "No get_config routine defined", 1, 0);
493 return;
494 }
495
496 error = NULL;
91b165c0 497 size = ARRAY_SIZE(default_buf);
1da177e4
LT
498 buf = default_buf;
499
ae2587e4 500 while (1) {
1da177e4 501 n = (*get_config)(name, buf, size, &error);
ae2587e4 502 if (error != NULL) {
1da177e4
LT
503 mconsole_reply(req, error, 1, 0);
504 goto out;
505 }
506
ae2587e4 507 if (n <= size) {
1da177e4
LT
508 mconsole_reply(req, buf, 0, 0);
509 goto out;
510 }
511
ae2587e4 512 if (buf != default_buf)
1da177e4
LT
513 kfree(buf);
514
515 size = n;
516 buf = kmalloc(size, GFP_KERNEL);
ae2587e4 517 if (buf == NULL) {
1da177e4
LT
518 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
519 return;
520 }
521 }
522 out:
ae2587e4 523 if (buf != default_buf)
1da177e4 524 kfree(buf);
1da177e4
LT
525}
526
527void mconsole_config(struct mc_request *req)
528{
529 struct mc_device *dev;
f28169d2 530 char *ptr = req->request.data, *name, *error_string = "";
1da177e4
LT
531 int err;
532
533 ptr += strlen("config");
e7d2860b 534 ptr = skip_spaces(ptr);
1da177e4 535 dev = mconsole_find_dev(ptr);
ae2587e4 536 if (dev == NULL) {
1da177e4
LT
537 mconsole_reply(req, "Bad configuration option", 1, 0);
538 return;
539 }
540
541 name = &ptr[strlen(dev->name)];
542 ptr = name;
ae2587e4 543 while ((*ptr != '=') && (*ptr != '\0'))
1da177e4
LT
544 ptr++;
545
ae2587e4 546 if (*ptr == '=') {
f28169d2
JD
547 err = (*dev->config)(name, &error_string);
548 mconsole_reply(req, error_string, err, 0);
1da177e4
LT
549 }
550 else mconsole_get_config(dev->get_config, req, name);
551}
552
553void mconsole_remove(struct mc_request *req)
554{
d50084a2 555 struct mc_device *dev;
29d56cfe 556 char *ptr = req->request.data, *err_msg = "";
3a331a51 557 char error[256];
29d56cfe 558 int err, start, end, n;
1da177e4
LT
559
560 ptr += strlen("remove");
e7d2860b 561 ptr = skip_spaces(ptr);
1da177e4 562 dev = mconsole_find_dev(ptr);
ae2587e4 563 if (dev == NULL) {
1da177e4
LT
564 mconsole_reply(req, "Bad remove option", 1, 0);
565 return;
566 }
29d56cfe 567
3a331a51
JD
568 ptr = &ptr[strlen(dev->name)];
569
570 err = 1;
571 n = (*dev->id)(&ptr, &start, &end);
ae2587e4 572 if (n < 0) {
3a331a51
JD
573 err_msg = "Couldn't parse device number";
574 goto out;
575 }
ae2587e4 576 else if ((n < start) || (n > end)) {
3a331a51
JD
577 sprintf(error, "Invalid device number - must be between "
578 "%d and %d", start, end);
579 err_msg = error;
580 goto out;
581 }
29d56cfe 582
f28169d2
JD
583 err_msg = NULL;
584 err = (*dev->remove)(n, &err_msg);
ae2587e4 585 switch(err) {
d40f6d71
JD
586 case 0:
587 err_msg = "";
588 break;
3a331a51 589 case -ENODEV:
ae2587e4 590 if (err_msg == NULL)
f28169d2 591 err_msg = "Device doesn't exist";
3a331a51
JD
592 break;
593 case -EBUSY:
ae2587e4 594 if (err_msg == NULL)
f28169d2 595 err_msg = "Device is currently open";
3a331a51
JD
596 break;
597 default:
598 break;
599 }
600out:
29d56cfe 601 mconsole_reply(req, err_msg, err, 0);
1da177e4
LT
602}
603
f92afe56
JD
604struct mconsole_output {
605 struct list_head list;
606 struct mc_request *req;
607};
608
84f48d4f 609static DEFINE_SPINLOCK(client_lock);
6f517d3f
JD
610static LIST_HEAD(clients);
611static char console_buf[MCONSOLE_MAX_DATA];
6f517d3f
JD
612
613static void console_write(struct console *console, const char *string,
54fa0ba4 614 unsigned int len)
6f517d3f
JD
615{
616 struct list_head *ele;
617 int n;
618
ae2587e4 619 if (list_empty(&clients))
6f517d3f
JD
620 return;
621
54fa0ba4
JD
622 while (len > 0) {
623 n = min((size_t) len, ARRAY_SIZE(console_buf));
624 strncpy(console_buf, string, n);
6f517d3f
JD
625 string += n;
626 len -= n;
6f517d3f 627
ae2587e4 628 list_for_each(ele, &clients) {
f92afe56 629 struct mconsole_output *entry;
6f517d3f 630
f92afe56 631 entry = list_entry(ele, struct mconsole_output, list);
54fa0ba4 632 mconsole_reply_len(entry->req, console_buf, n, 0, 1);
6f517d3f 633 }
6f517d3f
JD
634 }
635}
636
637static struct console mc_console = { .name = "mc",
638 .write = console_write,
a174b30e 639 .flags = CON_ENABLED,
6f517d3f
JD
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{
f92afe56 653 struct mconsole_output entry;
6f517d3f
JD
654 unsigned long flags;
655
f92afe56 656 entry.req = req;
84f48d4f 657 spin_lock_irqsave(&client_lock, flags);
6f517d3f 658 list_add(&entry.list, &clients);
84f48d4f 659 spin_unlock_irqrestore(&client_lock, flags);
6f517d3f
JD
660
661 (*proc)(arg);
662
54fa0ba4 663 mconsole_reply_len(req, "", 0, 0, 0);
6f517d3f 664
84f48d4f 665 spin_lock_irqsave(&client_lock, flags);
6f517d3f 666 list_del(&entry.list);
84f48d4f 667 spin_unlock_irqrestore(&client_lock, flags);
6f517d3f
JD
668}
669
4111b025 670#ifdef CONFIG_MAGIC_SYSRQ
54fa0ba4
JD
671
672#include <linux/sysrq.h>
673
4111b025
JD
674static void sysrq_proc(void *arg)
675{
676 char *op = arg;
f335397d 677 handle_sysrq(*op);
4111b025
JD
678}
679
680void mconsole_sysrq(struct mc_request *req)
681{
682 char *ptr = req->request.data;
683
684 ptr += strlen("sysrq");
e7d2860b 685 ptr = skip_spaces(ptr);
4111b025 686
ae2587e4
JD
687 /*
688 * With 'b', the system will shut down without a chance to reply,
4111b025
JD
689 * so in this case, we reply first.
690 */
ae2587e4 691 if (*ptr == 'b')
4111b025
JD
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
6f517d3f
JD
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
ae2587e4
JD
711/*
712 * Mconsole stack trace
3eddddcf
JD
713 * Added by Allan Graves, Jeff Dike
714 * Dumps a stacks registers to the linux console.
715 * Usage stack <pid>.
716 */
42fda663 717void mconsole_stack(struct mc_request *req)
3eddddcf 718{
3a331a51
JD
719 char *ptr = req->request.data;
720 int pid_requested= -1;
3eddddcf
JD
721 struct task_struct *to = NULL;
722
ae2587e4
JD
723 /*
724 * Would be nice:
3a331a51 725 * 1) Send showregs output to mconsole.
3eddddcf
JD
726 * 2) Add a way to stack dump all pids.
727 */
728
3a331a51 729 ptr += strlen("stack");
e7d2860b 730 ptr = skip_spaces(ptr);
3eddddcf 731
ae2587e4
JD
732 /*
733 * Should really check for multiple pids or reject bad args here
734 */
3a331a51 735 /* What do the arguments in mconsole_reply mean? */
ae2587e4 736 if (sscanf(ptr, "%d", &pid_requested) == 0) {
3a331a51
JD
737 mconsole_reply(req, "Please specify a pid", 1, 0);
738 return;
739 }
3eddddcf 740
827b3f6a 741 to = find_task_by_pid_ns(pid_requested, &init_pid_ns);
ae2587e4 742 if ((to == NULL) || (pid_requested == 0)) {
3a331a51
JD
743 mconsole_reply(req, "Couldn't find that pid", 1, 0);
744 return;
745 }
6f517d3f 746 with_console(req, stack_proc, to);
3eddddcf 747}
3eddddcf 748
ae2587e4
JD
749/*
750 * Changed by mconsole_setup, which is __setup, and called before SMP is
1da177e4
LT
751 * active.
752 */
d50084a2 753static char *notify_socket = NULL;
1da177e4 754
97a1fcbb 755static int __init mconsole_init(void)
1da177e4
LT
756{
757 /* long to avoid size mismatch warnings from gcc */
758 long sock;
759 int err;
36137120 760 char file[UNIX_PATH_MAX];
1da177e4 761
ae2587e4
JD
762 if (umid_file_name("mconsole", file, sizeof(file)))
763 return -1;
1da177e4
LT
764 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
765
766 sock = os_create_unix_socket(file, sizeof(file), 1);
ae2587e4
JD
767 if (sock < 0) {
768 printk(KERN_ERR "Failed to initialize management console\n");
769 return 1;
1da177e4 770 }
438ee679
JD
771 if (os_set_fd_block(sock, 0))
772 goto out;
1da177e4
LT
773
774 register_reboot_notifier(&reboot_notifier);
775
776 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
aab94460 777 IRQF_SHARED, "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);