[PATCH] Notifier chain update: API changes
[linux-2.6-block.git] / arch / um / drivers / mconsole_kern.c
CommitLineData
1da177e4
LT
1/*
2 * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
3 * Copyright (C) 2001 - 2003 Jeff Dike (jdike@addtoit.com)
4 * Licensed under the GPL
5 */
6
7#include "linux/kernel.h"
8#include "linux/slab.h"
9#include "linux/init.h"
10#include "linux/notifier.h"
11#include "linux/reboot.h"
12#include "linux/utsname.h"
13#include "linux/ctype.h"
14#include "linux/interrupt.h"
15#include "linux/sysrq.h"
16#include "linux/workqueue.h"
17#include "linux/module.h"
18#include "linux/file.h"
19#include "linux/fs.h"
20#include "linux/namei.h"
21#include "linux/proc_fs.h"
22#include "linux/syscalls.h"
6f517d3f 23#include "linux/console.h"
1da177e4
LT
24#include "asm/irq.h"
25#include "asm/uaccess.h"
26#include "user_util.h"
27#include "kern_util.h"
28#include "kern.h"
29#include "mconsole.h"
30#include "mconsole_kern.h"
31#include "irq_user.h"
32#include "init.h"
33#include "os.h"
34#include "umid.h"
35#include "irq_kern.h"
3eddddcf 36#include "choose-mode.h"
1da177e4 37
d50084a2 38static int do_unlink_socket(struct notifier_block *notifier,
1da177e4
LT
39 unsigned long what, void *data)
40{
41 return(mconsole_unlink_socket());
42}
43
44
45static struct notifier_block reboot_notifier = {
46 .notifier_call = do_unlink_socket,
47 .priority = 0,
48};
49
d50084a2 50/* Safe without explicit locking for now. Tasklets provide their own
1da177e4
LT
51 * locking, and the interrupt handler is safe because it can't interrupt
52 * itself and it can only happen on CPU 0.
53 */
54
9010772c 55static LIST_HEAD(mc_requests);
1da177e4
LT
56
57static void mc_work_proc(void *unused)
58{
59 struct mconsole_entry *req;
60 unsigned long flags;
61
62 while(!list_empty(&mc_requests)){
63 local_save_flags(flags);
d50084a2 64 req = list_entry(mc_requests.next, struct mconsole_entry,
1da177e4
LT
65 list);
66 list_del(&req->list);
67 local_irq_restore(flags);
68 req->request.cmd->handler(&req->request);
69 kfree(req);
70 }
71}
72
9010772c 73static DECLARE_WORK(mconsole_work, mc_work_proc, NULL);
1da177e4
LT
74
75static irqreturn_t mconsole_interrupt(int irq, void *dev_id,
76 struct pt_regs *regs)
77{
78 /* long to avoid size mismatch warnings from gcc */
79 long fd;
80 struct mconsole_entry *new;
81 struct mc_request req;
82
83 fd = (long) dev_id;
84 while (mconsole_get_request(fd, &req)){
85 if(req.cmd->context == MCONSOLE_INTR)
86 (*req.cmd->handler)(&req);
87 else {
88 new = kmalloc(sizeof(*new), GFP_ATOMIC);
89 if(new == NULL)
90 mconsole_reply(&req, "Out of memory", 1, 0);
91 else {
92 new->request = req;
93 list_add(&new->list, &mc_requests);
94 }
95 }
96 }
97 if(!list_empty(&mc_requests))
98 schedule_work(&mconsole_work);
99 reactivate_fd(fd, MCONSOLE_IRQ);
100 return(IRQ_HANDLED);
101}
102
103void mconsole_version(struct mc_request *req)
104{
105 char version[256];
106
d50084a2
JD
107 sprintf(version, "%s %s %s %s %s", system_utsname.sysname,
108 system_utsname.nodename, system_utsname.release,
1da177e4
LT
109 system_utsname.version, system_utsname.machine);
110 mconsole_reply(req, version, 0, 0);
111}
112
113void mconsole_log(struct mc_request *req)
114{
115 int len;
116 char *ptr = req->request.data;
117
118 ptr += strlen("log ");
119
120 len = req->len - (ptr - req->request.data);
121 printk("%.*s", len, ptr);
122 mconsole_reply(req, "", 0, 0);
123}
124
125/* This is a more convoluted version of mconsole_proc, which has some stability
126 * problems; however, we need it fixed, because it is expected that UML users
127 * mount HPPFS instead of procfs on /proc. And we want mconsole_proc to still
128 * show the real procfs content, not the ones from hppfs.*/
129#if 0
130void mconsole_proc(struct mc_request *req)
131{
132 struct nameidata nd;
133 struct file_system_type *proc;
134 struct super_block *super;
135 struct file *file;
136 int n, err;
137 char *ptr = req->request.data, *buf;
138
139 ptr += strlen("proc");
140 while(isspace(*ptr)) ptr++;
141
142 proc = get_fs_type("proc");
143 if(proc == NULL){
144 mconsole_reply(req, "procfs not registered", 1, 0);
145 goto out;
146 }
147
148 super = (*proc->get_sb)(proc, 0, NULL, NULL);
149 put_filesystem(proc);
150 if(super == NULL){
151 mconsole_reply(req, "Failed to get procfs superblock", 1, 0);
152 goto out;
153 }
154 up_write(&super->s_umount);
155
156 nd.dentry = super->s_root;
157 nd.mnt = NULL;
158 nd.flags = O_RDONLY + 1;
159 nd.last_type = LAST_ROOT;
160
161 /* START: it was experienced that the stability problems are closed
162 * if commenting out these two calls + the below read cycle. To
163 * make UML crash again, it was enough to readd either one.*/
164 err = link_path_walk(ptr, &nd);
165 if(err){
166 mconsole_reply(req, "Failed to look up file", 1, 0);
167 goto out_kill;
168 }
169
170 file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
171 if(IS_ERR(file)){
172 mconsole_reply(req, "Failed to open file", 1, 0);
173 goto out_kill;
174 }
175 /*END*/
176
177 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
178 if(buf == NULL){
179 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
180 goto out_fput;
181 }
182
183 if((file->f_op != NULL) && (file->f_op->read != NULL)){
184 do {
185 n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1,
186 &file->f_pos);
187 if(n >= 0){
188 buf[n] = '\0';
189 mconsole_reply(req, buf, 0, (n > 0));
190 }
191 else {
192 mconsole_reply(req, "Read of file failed",
193 1, 0);
194 goto out_free;
195 }
196 } while(n > 0);
197 }
198 else mconsole_reply(req, "", 0, 0);
199
200 out_free:
201 kfree(buf);
202 out_fput:
203 fput(file);
204 out_kill:
205 deactivate_super(super);
206 out: ;
207}
208#endif
209
210void mconsole_proc(struct mc_request *req)
211{
212 char path[64];
213 char *buf;
214 int len;
215 int fd;
216 int first_chunk = 1;
217 char *ptr = req->request.data;
218
219 ptr += strlen("proc");
220 while(isspace(*ptr)) ptr++;
221 snprintf(path, sizeof(path), "/proc/%s", ptr);
222
223 fd = sys_open(path, 0, 0);
224 if (fd < 0) {
225 mconsole_reply(req, "Failed to open file", 1, 0);
226 printk("open %s: %d\n",path,fd);
227 goto out;
228 }
229
230 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
231 if(buf == NULL){
232 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
233 goto out_close;
234 }
235
236 for (;;) {
237 len = sys_read(fd, buf, PAGE_SIZE-1);
238 if (len < 0) {
239 mconsole_reply(req, "Read of file failed", 1, 0);
240 goto out_free;
241 }
242 /*Begin the file content on his own line.*/
243 if (first_chunk) {
244 mconsole_reply(req, "\n", 0, 1);
245 first_chunk = 0;
246 }
247 if (len == PAGE_SIZE-1) {
248 buf[len] = '\0';
249 mconsole_reply(req, buf, 0, 1);
250 } else {
251 buf[len] = '\0';
252 mconsole_reply(req, buf, 0, 0);
253 break;
254 }
255 }
256
257 out_free:
258 kfree(buf);
259 out_close:
260 sys_close(fd);
261 out:
262 /* nothing */;
263}
264
265#define UML_MCONSOLE_HELPTEXT \
266"Commands: \n\
267 version - Get kernel version \n\
268 help - Print this message \n\
269 halt - Halt UML \n\
270 reboot - Reboot UML \n\
271 config <dev>=<config> - Add a new device to UML; \n\
272 same syntax as command line \n\
273 config <dev> - Query the configuration of a device \n\
274 remove <dev> - Remove a device from UML \n\
275 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
db805812 276 cad - invoke the Ctrl-Alt-Del handler \n\
1da177e4
LT
277 stop - pause the UML; it will do nothing until it receives a 'go' \n\
278 go - continue the UML after a 'stop' \n\
279 log <string> - make UML enter <string> into the kernel log\n\
280 proc <file> - returns the contents of the UML's /proc/<file>\n\
3eddddcf 281 stack <pid> - returns the stack of the specified pid\n\
1da177e4
LT
282"
283
284void mconsole_help(struct mc_request *req)
285{
286 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
287}
288
289void mconsole_halt(struct mc_request *req)
290{
291 mconsole_reply(req, "", 0, 0);
292 machine_halt();
293}
294
295void mconsole_reboot(struct mc_request *req)
296{
297 mconsole_reply(req, "", 0, 0);
298 machine_restart(NULL);
299}
300
301extern void ctrl_alt_del(void);
302
303void mconsole_cad(struct mc_request *req)
304{
305 mconsole_reply(req, "", 0, 0);
306 ctrl_alt_del();
307}
308
309void mconsole_go(struct mc_request *req)
310{
311 mconsole_reply(req, "Not stopped", 1, 0);
312}
313
314void mconsole_stop(struct mc_request *req)
315{
316 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
317 os_set_fd_block(req->originating_fd, 1);
318 mconsole_reply(req, "", 0, 0);
319 while(mconsole_get_request(req->originating_fd, req)){
320 if(req->cmd->handler == mconsole_go) break;
321 (*req->cmd->handler)(req);
322 }
323 os_set_fd_block(req->originating_fd, 0);
324 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
325 mconsole_reply(req, "", 0, 0);
326}
327
328/* This list is populated by __initcall routines. */
329
42947cb9 330static LIST_HEAD(mconsole_devices);
1da177e4
LT
331
332void mconsole_register_dev(struct mc_device *new)
333{
334 list_add(&new->list, &mconsole_devices);
335}
336
337static struct mc_device *mconsole_find_dev(char *name)
338{
339 struct list_head *ele;
340 struct mc_device *dev;
341
342 list_for_each(ele, &mconsole_devices){
343 dev = list_entry(ele, struct mc_device, list);
344 if(!strncmp(name, dev->name, strlen(dev->name)))
345 return(dev);
346 }
347 return(NULL);
348}
349
350#define CONFIG_BUF_SIZE 64
351
d50084a2 352static void mconsole_get_config(int (*get_config)(char *, char *, int,
1da177e4
LT
353 char **),
354 struct mc_request *req, char *name)
355{
356 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
357 int n, size;
358
359 if(get_config == NULL){
360 mconsole_reply(req, "No get_config routine defined", 1, 0);
361 return;
362 }
363
364 error = NULL;
365 size = sizeof(default_buf)/sizeof(default_buf[0]);
366 buf = default_buf;
367
368 while(1){
369 n = (*get_config)(name, buf, size, &error);
370 if(error != NULL){
371 mconsole_reply(req, error, 1, 0);
372 goto out;
373 }
374
375 if(n <= size){
376 mconsole_reply(req, buf, 0, 0);
377 goto out;
378 }
379
380 if(buf != default_buf)
381 kfree(buf);
382
383 size = n;
384 buf = kmalloc(size, GFP_KERNEL);
385 if(buf == NULL){
386 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
387 return;
388 }
389 }
390 out:
391 if(buf != default_buf)
392 kfree(buf);
1da177e4
LT
393}
394
395void mconsole_config(struct mc_request *req)
396{
397 struct mc_device *dev;
398 char *ptr = req->request.data, *name;
399 int err;
400
401 ptr += strlen("config");
402 while(isspace(*ptr)) ptr++;
403 dev = mconsole_find_dev(ptr);
404 if(dev == NULL){
405 mconsole_reply(req, "Bad configuration option", 1, 0);
406 return;
407 }
408
409 name = &ptr[strlen(dev->name)];
410 ptr = name;
411 while((*ptr != '=') && (*ptr != '\0'))
412 ptr++;
413
414 if(*ptr == '='){
415 err = (*dev->config)(name);
416 mconsole_reply(req, "", err, 0);
417 }
418 else mconsole_get_config(dev->get_config, req, name);
419}
420
421void mconsole_remove(struct mc_request *req)
422{
d50084a2 423 struct mc_device *dev;
29d56cfe 424 char *ptr = req->request.data, *err_msg = "";
3a331a51 425 char error[256];
29d56cfe 426 int err, start, end, n;
1da177e4
LT
427
428 ptr += strlen("remove");
429 while(isspace(*ptr)) ptr++;
430 dev = mconsole_find_dev(ptr);
431 if(dev == NULL){
432 mconsole_reply(req, "Bad remove option", 1, 0);
433 return;
434 }
29d56cfe 435
3a331a51
JD
436 ptr = &ptr[strlen(dev->name)];
437
438 err = 1;
439 n = (*dev->id)(&ptr, &start, &end);
440 if(n < 0){
441 err_msg = "Couldn't parse device number";
442 goto out;
443 }
444 else if((n < start) || (n > end)){
445 sprintf(error, "Invalid device number - must be between "
446 "%d and %d", start, end);
447 err_msg = error;
448 goto out;
449 }
29d56cfe
JD
450
451 err = (*dev->remove)(n);
3a331a51
JD
452 switch(err){
453 case -ENODEV:
454 err_msg = "Device doesn't exist";
455 break;
456 case -EBUSY:
457 err_msg = "Device is currently open";
458 break;
459 default:
460 break;
461 }
462out:
29d56cfe 463 mconsole_reply(req, err_msg, err, 0);
1da177e4
LT
464}
465
6f517d3f
JD
466static DEFINE_SPINLOCK(console_lock);
467static LIST_HEAD(clients);
468static char console_buf[MCONSOLE_MAX_DATA];
469static int console_index = 0;
470
471static void console_write(struct console *console, const char *string,
472 unsigned len)
473{
474 struct list_head *ele;
475 int n;
476
477 if(list_empty(&clients))
478 return;
479
480 while(1){
481 n = min(len, ARRAY_SIZE(console_buf) - console_index);
482 strncpy(&console_buf[console_index], string, n);
483 console_index += n;
484 string += n;
485 len -= n;
486 if(len == 0)
487 return;
488
489 list_for_each(ele, &clients){
490 struct mconsole_entry *entry;
491
492 entry = list_entry(ele, struct mconsole_entry, list);
493 mconsole_reply_len(&entry->request, console_buf,
494 console_index, 0, 1);
495 }
496
497 console_index = 0;
498 }
499}
500
501static struct console mc_console = { .name = "mc",
502 .write = console_write,
a174b30e 503 .flags = CON_ENABLED,
6f517d3f
JD
504 .index = -1 };
505
506static int mc_add_console(void)
507{
508 register_console(&mc_console);
509 return 0;
510}
511
512late_initcall(mc_add_console);
513
514static void with_console(struct mc_request *req, void (*proc)(void *),
515 void *arg)
516{
517 struct mconsole_entry entry;
518 unsigned long flags;
519
520 INIT_LIST_HEAD(&entry.list);
521 entry.request = *req;
522 list_add(&entry.list, &clients);
523 spin_lock_irqsave(&console_lock, flags);
524
525 (*proc)(arg);
526
527 mconsole_reply_len(req, console_buf, console_index, 0, 0);
528 console_index = 0;
529
530 spin_unlock_irqrestore(&console_lock, flags);
531 list_del(&entry.list);
532}
533
4111b025
JD
534#ifdef CONFIG_MAGIC_SYSRQ
535static void sysrq_proc(void *arg)
536{
537 char *op = arg;
538
539 handle_sysrq(*op, &current->thread.regs, NULL);
540}
541
542void mconsole_sysrq(struct mc_request *req)
543{
544 char *ptr = req->request.data;
545
546 ptr += strlen("sysrq");
547 while(isspace(*ptr)) ptr++;
548
549 /* With 'b', the system will shut down without a chance to reply,
550 * so in this case, we reply first.
551 */
552 if(*ptr == 'b')
553 mconsole_reply(req, "", 0, 0);
554
555 with_console(req, sysrq_proc, ptr);
556}
557#else
558void mconsole_sysrq(struct mc_request *req)
559{
560 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
561}
562#endif
563
42947cb9
PBG
564#ifdef CONFIG_MODE_SKAS
565
6f517d3f
JD
566static void stack_proc(void *arg)
567{
568 struct task_struct *from = current, *to = arg;
569
570 to->thread.saved_task = from;
571 switch_to(from, to, from);
572}
573
3eddddcf
JD
574/* Mconsole stack trace
575 * Added by Allan Graves, Jeff Dike
576 * Dumps a stacks registers to the linux console.
577 * Usage stack <pid>.
578 */
42947cb9 579static void do_stack_trace(struct mc_request *req)
3eddddcf 580{
3a331a51
JD
581 char *ptr = req->request.data;
582 int pid_requested= -1;
6f517d3f 583 struct task_struct *from = NULL;
3eddddcf
JD
584 struct task_struct *to = NULL;
585
3a331a51
JD
586 /* Would be nice:
587 * 1) Send showregs output to mconsole.
3eddddcf
JD
588 * 2) Add a way to stack dump all pids.
589 */
590
3a331a51
JD
591 ptr += strlen("stack");
592 while(isspace(*ptr)) ptr++;
3eddddcf 593
3a331a51
JD
594 /* Should really check for multiple pids or reject bad args here */
595 /* What do the arguments in mconsole_reply mean? */
596 if(sscanf(ptr, "%d", &pid_requested) == 0){
597 mconsole_reply(req, "Please specify a pid", 1, 0);
598 return;
599 }
3eddddcf 600
6f517d3f 601 from = current;
3eddddcf 602
6f517d3f 603 to = find_task_by_pid(pid_requested);
3a331a51
JD
604 if((to == NULL) || (pid_requested == 0)) {
605 mconsole_reply(req, "Couldn't find that pid", 1, 0);
606 return;
607 }
6f517d3f 608 with_console(req, stack_proc, to);
3eddddcf 609}
42947cb9 610#endif /* CONFIG_MODE_SKAS */
3eddddcf
JD
611
612void mconsole_stack(struct mc_request *req)
613{
614 /* This command doesn't work in TT mode, so let's check and then
615 * get out of here
616 */
617 CHOOSE_MODE(mconsole_reply(req, "Sorry, this doesn't work in TT mode",
618 1, 0),
42947cb9 619 do_stack_trace(req));
3eddddcf
JD
620}
621
1da177e4
LT
622/* Changed by mconsole_setup, which is __setup, and called before SMP is
623 * active.
624 */
d50084a2 625static char *notify_socket = NULL;
1da177e4 626
9010772c 627static int mconsole_init(void)
1da177e4
LT
628{
629 /* long to avoid size mismatch warnings from gcc */
630 long sock;
631 int err;
632 char file[256];
633
634 if(umid_file_name("mconsole", file, sizeof(file))) return(-1);
635 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
636
637 sock = os_create_unix_socket(file, sizeof(file), 1);
638 if (sock < 0){
639 printk("Failed to initialize management console\n");
640 return(1);
641 }
642
643 register_reboot_notifier(&reboot_notifier);
644
645 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
646 SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM,
647 "mconsole", (void *)sock);
648 if (err){
649 printk("Failed to get IRQ for management console\n");
650 return(1);
651 }
652
653 if(notify_socket != NULL){
970d6e3a 654 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
1da177e4
LT
655 if(notify_socket != NULL)
656 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
d50084a2 657 mconsole_socket_name,
1da177e4
LT
658 strlen(mconsole_socket_name) + 1);
659 else printk(KERN_ERR "mconsole_setup failed to strdup "
660 "string\n");
661 }
662
d50084a2 663 printk("mconsole (version %d) initialized on %s\n",
1da177e4
LT
664 MCONSOLE_VERSION, mconsole_socket_name);
665 return(0);
666}
667
668__initcall(mconsole_init);
669
670static int write_proc_mconsole(struct file *file, const char __user *buffer,
671 unsigned long count, void *data)
672{
673 char *buf;
674
675 buf = kmalloc(count + 1, GFP_KERNEL);
d50084a2 676 if(buf == NULL)
1da177e4
LT
677 return(-ENOMEM);
678
679 if(copy_from_user(buf, buffer, count)){
680 count = -EFAULT;
681 goto out;
682 }
683
684 buf[count] = '\0';
685
686 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
687 out:
688 kfree(buf);
689 return(count);
690}
691
692static int create_proc_mconsole(void)
693{
694 struct proc_dir_entry *ent;
695
696 if(notify_socket == NULL) return(0);
697
698 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
699 if(ent == NULL){
30f417c6 700 printk(KERN_INFO "create_proc_mconsole : create_proc_entry failed\n");
1da177e4
LT
701 return(0);
702 }
703
704 ent->read_proc = NULL;
705 ent->write_proc = write_proc_mconsole;
706 return(0);
707}
708
709static DEFINE_SPINLOCK(notify_spinlock);
710
711void lock_notify(void)
712{
713 spin_lock(&notify_spinlock);
714}
715
716void unlock_notify(void)
717{
718 spin_unlock(&notify_spinlock);
719}
720
721__initcall(create_proc_mconsole);
722
723#define NOTIFY "=notify:"
724
725static int mconsole_setup(char *str)
726{
727 if(!strncmp(str, NOTIFY, strlen(NOTIFY))){
728 str += strlen(NOTIFY);
729 notify_socket = str;
730 }
731 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
732 return(1);
733}
734
735__setup("mconsole", mconsole_setup);
736
737__uml_help(mconsole_setup,
738"mconsole=notify:<socket>\n"
739" Requests that the mconsole driver send a message to the named Unix\n"
740" socket containing the name of the mconsole socket. This also serves\n"
741" to notify outside processes when UML has booted far enough to respond\n"
742" to mconsole requests.\n\n"
743);
744
745static int notify_panic(struct notifier_block *self, unsigned long unused1,
746 void *ptr)
747{
748 char *message = ptr;
749
750 if(notify_socket == NULL) return(0);
751
d50084a2 752 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
1da177e4
LT
753 strlen(message) + 1);
754 return(0);
755}
756
757static struct notifier_block panic_exit_notifier = {
758 .notifier_call = notify_panic,
759 .next = NULL,
760 .priority = 1
761};
762
763static int add_notifier(void)
764{
e041c683
AS
765 atomic_notifier_chain_register(&panic_notifier_list,
766 &panic_exit_notifier);
1da177e4
LT
767 return(0);
768}
769
770__initcall(add_notifier);
771
772char *mconsole_notify_socket(void)
773{
774 return(notify_socket);
775}
776
777EXPORT_SYMBOL(mconsole_notify_socket);