blob: d7c7adcc0a6731a2acec59eceffdb18a949ffcad [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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;
422 char *ptr = req->request.data;
423 int err;
424
425 ptr += strlen("remove");
426 while(isspace(*ptr)) ptr++;
427 dev = mconsole_find_dev(ptr);
428 if(dev == NULL){
429 mconsole_reply(req, "Bad remove option", 1, 0);
430 return;
431 }
432 err = (*dev->remove)(&ptr[strlen(dev->name)]);
433 mconsole_reply(req, "", err, 0);
434}
435
436#ifdef CONFIG_MAGIC_SYSRQ
437void mconsole_sysrq(struct mc_request *req)
438{
439 char *ptr = req->request.data;
440
441 ptr += strlen("sysrq");
442 while(isspace(*ptr)) ptr++;
443
444 mconsole_reply(req, "", 0, 0);
445 handle_sysrq(*ptr, &current->thread.regs, NULL);
446}
447#else
448void mconsole_sysrq(struct mc_request *req)
449{
450 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
451}
452#endif
453
454/* Changed by mconsole_setup, which is __setup, and called before SMP is
455 * active.
456 */
457static char *notify_socket = NULL;
458
459int mconsole_init(void)
460{
461 /* long to avoid size mismatch warnings from gcc */
462 long sock;
463 int err;
464 char file[256];
465
466 if(umid_file_name("mconsole", file, sizeof(file))) return(-1);
467 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
468
469 sock = os_create_unix_socket(file, sizeof(file), 1);
470 if (sock < 0){
471 printk("Failed to initialize management console\n");
472 return(1);
473 }
474
475 register_reboot_notifier(&reboot_notifier);
476
477 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
478 SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM,
479 "mconsole", (void *)sock);
480 if (err){
481 printk("Failed to get IRQ for management console\n");
482 return(1);
483 }
484
485 if(notify_socket != NULL){
486 notify_socket = uml_strdup(notify_socket);
487 if(notify_socket != NULL)
488 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
489 mconsole_socket_name,
490 strlen(mconsole_socket_name) + 1);
491 else printk(KERN_ERR "mconsole_setup failed to strdup "
492 "string\n");
493 }
494
495 printk("mconsole (version %d) initialized on %s\n",
496 MCONSOLE_VERSION, mconsole_socket_name);
497 return(0);
498}
499
500__initcall(mconsole_init);
501
502static int write_proc_mconsole(struct file *file, const char __user *buffer,
503 unsigned long count, void *data)
504{
505 char *buf;
506
507 buf = kmalloc(count + 1, GFP_KERNEL);
508 if(buf == NULL)
509 return(-ENOMEM);
510
511 if(copy_from_user(buf, buffer, count)){
512 count = -EFAULT;
513 goto out;
514 }
515
516 buf[count] = '\0';
517
518 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
519 out:
520 kfree(buf);
521 return(count);
522}
523
524static int create_proc_mconsole(void)
525{
526 struct proc_dir_entry *ent;
527
528 if(notify_socket == NULL) return(0);
529
530 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
531 if(ent == NULL){
532 printk("create_proc_mconsole : create_proc_entry failed\n");
533 return(0);
534 }
535
536 ent->read_proc = NULL;
537 ent->write_proc = write_proc_mconsole;
538 return(0);
539}
540
541static DEFINE_SPINLOCK(notify_spinlock);
542
543void lock_notify(void)
544{
545 spin_lock(&notify_spinlock);
546}
547
548void unlock_notify(void)
549{
550 spin_unlock(&notify_spinlock);
551}
552
553__initcall(create_proc_mconsole);
554
555#define NOTIFY "=notify:"
556
557static int mconsole_setup(char *str)
558{
559 if(!strncmp(str, NOTIFY, strlen(NOTIFY))){
560 str += strlen(NOTIFY);
561 notify_socket = str;
562 }
563 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
564 return(1);
565}
566
567__setup("mconsole", mconsole_setup);
568
569__uml_help(mconsole_setup,
570"mconsole=notify:<socket>\n"
571" Requests that the mconsole driver send a message to the named Unix\n"
572" socket containing the name of the mconsole socket. This also serves\n"
573" to notify outside processes when UML has booted far enough to respond\n"
574" to mconsole requests.\n\n"
575);
576
577static int notify_panic(struct notifier_block *self, unsigned long unused1,
578 void *ptr)
579{
580 char *message = ptr;
581
582 if(notify_socket == NULL) return(0);
583
584 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
585 strlen(message) + 1);
586 return(0);
587}
588
589static struct notifier_block panic_exit_notifier = {
590 .notifier_call = notify_panic,
591 .next = NULL,
592 .priority = 1
593};
594
595static int add_notifier(void)
596{
597 notifier_chain_register(&panic_notifier_list, &panic_exit_notifier);
598 return(0);
599}
600
601__initcall(add_notifier);
602
603char *mconsole_notify_socket(void)
604{
605 return(notify_socket);
606}
607
608EXPORT_SYMBOL(mconsole_notify_socket);
609
610/*
611 * Overrides for Emacs so that we follow Linus's tabbing style.
612 * Emacs will notice this stuff at the end of the file and automatically
613 * adjust the settings for this buffer only. This must remain at the end
614 * of the file.
615 * ---------------------------------------------------------------------------
616 * Local variables:
617 * c-file-style: "linux"
618 * End:
619 */