/spare/repo/netdev-2.6 branch 'master'
[deliverable/linux.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"
23#include "asm/irq.h"
24#include "asm/uaccess.h"
25#include "user_util.h"
26#include "kern_util.h"
27#include "kern.h"
28#include "mconsole.h"
29#include "mconsole_kern.h"
30#include "irq_user.h"
31#include "init.h"
32#include "os.h"
33#include "umid.h"
34#include "irq_kern.h"
35
36static int do_unlink_socket(struct notifier_block *notifier,
37 unsigned long what, void *data)
38{
39 return(mconsole_unlink_socket());
40}
41
42
43static struct notifier_block reboot_notifier = {
44 .notifier_call = do_unlink_socket,
45 .priority = 0,
46};
47
48/* Safe without explicit locking for now. Tasklets provide their own
49 * locking, and the interrupt handler is safe because it can't interrupt
50 * itself and it can only happen on CPU 0.
51 */
52
53LIST_HEAD(mc_requests);
54
55static void mc_work_proc(void *unused)
56{
57 struct mconsole_entry *req;
58 unsigned long flags;
59
60 while(!list_empty(&mc_requests)){
61 local_save_flags(flags);
62 req = list_entry(mc_requests.next, struct mconsole_entry,
63 list);
64 list_del(&req->list);
65 local_irq_restore(flags);
66 req->request.cmd->handler(&req->request);
67 kfree(req);
68 }
69}
70
71DECLARE_WORK(mconsole_work, mc_work_proc, NULL);
72
73static irqreturn_t mconsole_interrupt(int irq, void *dev_id,
74 struct pt_regs *regs)
75{
76 /* long to avoid size mismatch warnings from gcc */
77 long fd;
78 struct mconsole_entry *new;
79 struct mc_request req;
80
81 fd = (long) dev_id;
82 while (mconsole_get_request(fd, &req)){
83 if(req.cmd->context == MCONSOLE_INTR)
84 (*req.cmd->handler)(&req);
85 else {
86 new = kmalloc(sizeof(*new), GFP_ATOMIC);
87 if(new == NULL)
88 mconsole_reply(&req, "Out of memory", 1, 0);
89 else {
90 new->request = req;
91 list_add(&new->list, &mc_requests);
92 }
93 }
94 }
95 if(!list_empty(&mc_requests))
96 schedule_work(&mconsole_work);
97 reactivate_fd(fd, MCONSOLE_IRQ);
98 return(IRQ_HANDLED);
99}
100
101void mconsole_version(struct mc_request *req)
102{
103 char version[256];
104
105 sprintf(version, "%s %s %s %s %s", system_utsname.sysname,
106 system_utsname.nodename, system_utsname.release,
107 system_utsname.version, system_utsname.machine);
108 mconsole_reply(req, version, 0, 0);
109}
110
111void mconsole_log(struct mc_request *req)
112{
113 int len;
114 char *ptr = req->request.data;
115
116 ptr += strlen("log ");
117
118 len = req->len - (ptr - req->request.data);
119 printk("%.*s", len, ptr);
120 mconsole_reply(req, "", 0, 0);
121}
122
123/* This is a more convoluted version of mconsole_proc, which has some stability
124 * problems; however, we need it fixed, because it is expected that UML users
125 * mount HPPFS instead of procfs on /proc. And we want mconsole_proc to still
126 * show the real procfs content, not the ones from hppfs.*/
127#if 0
128void mconsole_proc(struct mc_request *req)
129{
130 struct nameidata nd;
131 struct file_system_type *proc;
132 struct super_block *super;
133 struct file *file;
134 int n, err;
135 char *ptr = req->request.data, *buf;
136
137 ptr += strlen("proc");
138 while(isspace(*ptr)) ptr++;
139
140 proc = get_fs_type("proc");
141 if(proc == NULL){
142 mconsole_reply(req, "procfs not registered", 1, 0);
143 goto out;
144 }
145
146 super = (*proc->get_sb)(proc, 0, NULL, NULL);
147 put_filesystem(proc);
148 if(super == NULL){
149 mconsole_reply(req, "Failed to get procfs superblock", 1, 0);
150 goto out;
151 }
152 up_write(&super->s_umount);
153
154 nd.dentry = super->s_root;
155 nd.mnt = NULL;
156 nd.flags = O_RDONLY + 1;
157 nd.last_type = LAST_ROOT;
158
159 /* START: it was experienced that the stability problems are closed
160 * if commenting out these two calls + the below read cycle. To
161 * make UML crash again, it was enough to readd either one.*/
162 err = link_path_walk(ptr, &nd);
163 if(err){
164 mconsole_reply(req, "Failed to look up file", 1, 0);
165 goto out_kill;
166 }
167
168 file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
169 if(IS_ERR(file)){
170 mconsole_reply(req, "Failed to open file", 1, 0);
171 goto out_kill;
172 }
173 /*END*/
174
175 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
176 if(buf == NULL){
177 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
178 goto out_fput;
179 }
180
181 if((file->f_op != NULL) && (file->f_op->read != NULL)){
182 do {
183 n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1,
184 &file->f_pos);
185 if(n >= 0){
186 buf[n] = '\0';
187 mconsole_reply(req, buf, 0, (n > 0));
188 }
189 else {
190 mconsole_reply(req, "Read of file failed",
191 1, 0);
192 goto out_free;
193 }
194 } while(n > 0);
195 }
196 else mconsole_reply(req, "", 0, 0);
197
198 out_free:
199 kfree(buf);
200 out_fput:
201 fput(file);
202 out_kill:
203 deactivate_super(super);
204 out: ;
205}
206#endif
207
208void mconsole_proc(struct mc_request *req)
209{
210 char path[64];
211 char *buf;
212 int len;
213 int fd;
214 int first_chunk = 1;
215 char *ptr = req->request.data;
216
217 ptr += strlen("proc");
218 while(isspace(*ptr)) ptr++;
219 snprintf(path, sizeof(path), "/proc/%s", ptr);
220
221 fd = sys_open(path, 0, 0);
222 if (fd < 0) {
223 mconsole_reply(req, "Failed to open file", 1, 0);
224 printk("open %s: %d\n",path,fd);
225 goto out;
226 }
227
228 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
229 if(buf == NULL){
230 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
231 goto out_close;
232 }
233
234 for (;;) {
235 len = sys_read(fd, buf, PAGE_SIZE-1);
236 if (len < 0) {
237 mconsole_reply(req, "Read of file failed", 1, 0);
238 goto out_free;
239 }
240 /*Begin the file content on his own line.*/
241 if (first_chunk) {
242 mconsole_reply(req, "\n", 0, 1);
243 first_chunk = 0;
244 }
245 if (len == PAGE_SIZE-1) {
246 buf[len] = '\0';
247 mconsole_reply(req, buf, 0, 1);
248 } else {
249 buf[len] = '\0';
250 mconsole_reply(req, buf, 0, 0);
251 break;
252 }
253 }
254
255 out_free:
256 kfree(buf);
257 out_close:
258 sys_close(fd);
259 out:
260 /* nothing */;
261}
262
263#define UML_MCONSOLE_HELPTEXT \
264"Commands: \n\
265 version - Get kernel version \n\
266 help - Print this message \n\
267 halt - Halt UML \n\
268 reboot - Reboot UML \n\
269 config <dev>=<config> - Add a new device to UML; \n\
270 same syntax as command line \n\
271 config <dev> - Query the configuration of a device \n\
272 remove <dev> - Remove a device from UML \n\
273 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
274 cad - invoke the Ctl-Alt-Del handler \n\
275 stop - pause the UML; it will do nothing until it receives a 'go' \n\
276 go - continue the UML after a 'stop' \n\
277 log <string> - make UML enter <string> into the kernel log\n\
278 proc <file> - returns the contents of the UML's /proc/<file>\n\
279"
280
281void mconsole_help(struct mc_request *req)
282{
283 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
284}
285
286void mconsole_halt(struct mc_request *req)
287{
288 mconsole_reply(req, "", 0, 0);
289 machine_halt();
290}
291
292void mconsole_reboot(struct mc_request *req)
293{
294 mconsole_reply(req, "", 0, 0);
295 machine_restart(NULL);
296}
297
298extern void ctrl_alt_del(void);
299
300void mconsole_cad(struct mc_request *req)
301{
302 mconsole_reply(req, "", 0, 0);
303 ctrl_alt_del();
304}
305
306void mconsole_go(struct mc_request *req)
307{
308 mconsole_reply(req, "Not stopped", 1, 0);
309}
310
311void mconsole_stop(struct mc_request *req)
312{
313 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
314 os_set_fd_block(req->originating_fd, 1);
315 mconsole_reply(req, "", 0, 0);
316 while(mconsole_get_request(req->originating_fd, req)){
317 if(req->cmd->handler == mconsole_go) break;
318 (*req->cmd->handler)(req);
319 }
320 os_set_fd_block(req->originating_fd, 0);
321 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
322 mconsole_reply(req, "", 0, 0);
323}
324
325/* This list is populated by __initcall routines. */
326
327LIST_HEAD(mconsole_devices);
328
329void mconsole_register_dev(struct mc_device *new)
330{
331 list_add(&new->list, &mconsole_devices);
332}
333
334static struct mc_device *mconsole_find_dev(char *name)
335{
336 struct list_head *ele;
337 struct mc_device *dev;
338
339 list_for_each(ele, &mconsole_devices){
340 dev = list_entry(ele, struct mc_device, list);
341 if(!strncmp(name, dev->name, strlen(dev->name)))
342 return(dev);
343 }
344 return(NULL);
345}
346
347#define CONFIG_BUF_SIZE 64
348
349static void mconsole_get_config(int (*get_config)(char *, char *, int,
350 char **),
351 struct mc_request *req, char *name)
352{
353 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
354 int n, size;
355
356 if(get_config == NULL){
357 mconsole_reply(req, "No get_config routine defined", 1, 0);
358 return;
359 }
360
361 error = NULL;
362 size = sizeof(default_buf)/sizeof(default_buf[0]);
363 buf = default_buf;
364
365 while(1){
366 n = (*get_config)(name, buf, size, &error);
367 if(error != NULL){
368 mconsole_reply(req, error, 1, 0);
369 goto out;
370 }
371
372 if(n <= size){
373 mconsole_reply(req, buf, 0, 0);
374 goto out;
375 }
376
377 if(buf != default_buf)
378 kfree(buf);
379
380 size = n;
381 buf = kmalloc(size, GFP_KERNEL);
382 if(buf == NULL){
383 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
384 return;
385 }
386 }
387 out:
388 if(buf != default_buf)
389 kfree(buf);
390
391}
392
393void mconsole_config(struct mc_request *req)
394{
395 struct mc_device *dev;
396 char *ptr = req->request.data, *name;
397 int err;
398
399 ptr += strlen("config");
400 while(isspace(*ptr)) ptr++;
401 dev = mconsole_find_dev(ptr);
402 if(dev == NULL){
403 mconsole_reply(req, "Bad configuration option", 1, 0);
404 return;
405 }
406
407 name = &ptr[strlen(dev->name)];
408 ptr = name;
409 while((*ptr != '=') && (*ptr != '\0'))
410 ptr++;
411
412 if(*ptr == '='){
413 err = (*dev->config)(name);
414 mconsole_reply(req, "", err, 0);
415 }
416 else mconsole_get_config(dev->get_config, req, name);
417}
418
419void mconsole_remove(struct mc_request *req)
420{
421 struct mc_device *dev;
29d56cfe
JD
422 char *ptr = req->request.data, *err_msg = "";
423 char error[256];
424 int err, start, end, n;
1da177e4
LT
425
426 ptr += strlen("remove");
427 while(isspace(*ptr)) ptr++;
428 dev = mconsole_find_dev(ptr);
429 if(dev == NULL){
430 mconsole_reply(req, "Bad remove option", 1, 0);
431 return;
432 }
29d56cfe
JD
433
434 ptr = &ptr[strlen(dev->name)];
435
436 err = 1;
437 n = (*dev->id)(&ptr, &start, &end);
438 if(n < 0){
439 err_msg = "Couldn't parse device number";
440 goto out;
441 }
442 else if((n < start) || (n > end)){
443 sprintf(error, "Invalid device number - must be between "
444 "%d and %d", start, end);
445 err_msg = error;
446 goto out;
447 }
448
449 err = (*dev->remove)(n);
450 switch(err){
451 case -ENODEV:
452 err_msg = "Device doesn't exist";
453 break;
454 case -EBUSY:
455 err_msg = "Device is currently open";
456 break;
457 default:
458 break;
459 }
460 out:
461 mconsole_reply(req, err_msg, err, 0);
1da177e4
LT
462}
463
464#ifdef CONFIG_MAGIC_SYSRQ
465void mconsole_sysrq(struct mc_request *req)
466{
467 char *ptr = req->request.data;
468
469 ptr += strlen("sysrq");
470 while(isspace(*ptr)) ptr++;
471
472 mconsole_reply(req, "", 0, 0);
473 handle_sysrq(*ptr, &current->thread.regs, NULL);
474}
475#else
476void mconsole_sysrq(struct mc_request *req)
477{
478 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
479}
480#endif
481
482/* Changed by mconsole_setup, which is __setup, and called before SMP is
483 * active.
484 */
485static char *notify_socket = NULL;
486
487int mconsole_init(void)
488{
489 /* long to avoid size mismatch warnings from gcc */
490 long sock;
491 int err;
492 char file[256];
493
494 if(umid_file_name("mconsole", file, sizeof(file))) return(-1);
495 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
496
497 sock = os_create_unix_socket(file, sizeof(file), 1);
498 if (sock < 0){
499 printk("Failed to initialize management console\n");
500 return(1);
501 }
502
503 register_reboot_notifier(&reboot_notifier);
504
505 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
506 SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM,
507 "mconsole", (void *)sock);
508 if (err){
509 printk("Failed to get IRQ for management console\n");
510 return(1);
511 }
512
513 if(notify_socket != NULL){
514 notify_socket = uml_strdup(notify_socket);
515 if(notify_socket != NULL)
516 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
517 mconsole_socket_name,
518 strlen(mconsole_socket_name) + 1);
519 else printk(KERN_ERR "mconsole_setup failed to strdup "
520 "string\n");
521 }
522
523 printk("mconsole (version %d) initialized on %s\n",
524 MCONSOLE_VERSION, mconsole_socket_name);
525 return(0);
526}
527
528__initcall(mconsole_init);
529
530static int write_proc_mconsole(struct file *file, const char __user *buffer,
531 unsigned long count, void *data)
532{
533 char *buf;
534
535 buf = kmalloc(count + 1, GFP_KERNEL);
536 if(buf == NULL)
537 return(-ENOMEM);
538
539 if(copy_from_user(buf, buffer, count)){
540 count = -EFAULT;
541 goto out;
542 }
543
544 buf[count] = '\0';
545
546 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
547 out:
548 kfree(buf);
549 return(count);
550}
551
552static int create_proc_mconsole(void)
553{
554 struct proc_dir_entry *ent;
555
556 if(notify_socket == NULL) return(0);
557
558 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
559 if(ent == NULL){
30f417c6 560 printk(KERN_INFO "create_proc_mconsole : create_proc_entry failed\n");
1da177e4
LT
561 return(0);
562 }
563
564 ent->read_proc = NULL;
565 ent->write_proc = write_proc_mconsole;
566 return(0);
567}
568
569static DEFINE_SPINLOCK(notify_spinlock);
570
571void lock_notify(void)
572{
573 spin_lock(&notify_spinlock);
574}
575
576void unlock_notify(void)
577{
578 spin_unlock(&notify_spinlock);
579}
580
581__initcall(create_proc_mconsole);
582
583#define NOTIFY "=notify:"
584
585static int mconsole_setup(char *str)
586{
587 if(!strncmp(str, NOTIFY, strlen(NOTIFY))){
588 str += strlen(NOTIFY);
589 notify_socket = str;
590 }
591 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
592 return(1);
593}
594
595__setup("mconsole", mconsole_setup);
596
597__uml_help(mconsole_setup,
598"mconsole=notify:<socket>\n"
599" Requests that the mconsole driver send a message to the named Unix\n"
600" socket containing the name of the mconsole socket. This also serves\n"
601" to notify outside processes when UML has booted far enough to respond\n"
602" to mconsole requests.\n\n"
603);
604
605static int notify_panic(struct notifier_block *self, unsigned long unused1,
606 void *ptr)
607{
608 char *message = ptr;
609
610 if(notify_socket == NULL) return(0);
611
612 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
613 strlen(message) + 1);
614 return(0);
615}
616
617static struct notifier_block panic_exit_notifier = {
618 .notifier_call = notify_panic,
619 .next = NULL,
620 .priority = 1
621};
622
623static int add_notifier(void)
624{
625 notifier_chain_register(&panic_notifier_list, &panic_exit_notifier);
626 return(0);
627}
628
629__initcall(add_notifier);
630
631char *mconsole_notify_socket(void)
632{
633 return(notify_socket);
634}
635
636EXPORT_SYMBOL(mconsole_notify_socket);
637
638/*
639 * Overrides for Emacs so that we follow Linus's tabbing style.
640 * Emacs will notice this stuff at the end of the file and automatically
641 * adjust the settings for this buffer only. This must remain at the end
642 * of the file.
643 * ---------------------------------------------------------------------------
644 * Local variables:
645 * c-file-style: "linux"
646 * End:
647 */
This page took 0.07633 seconds and 5 git commands to generate.