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