2 * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
3 * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
4 * Licensed under the GPL
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/proc_fs.h"
20 #include "asm/uaccess.h"
21 #include "user_util.h"
22 #include "kern_util.h"
25 #include "mconsole_kern.h"
31 static int do_unlink_socket(struct notifier_block *notifier,
32 unsigned long what, void *data)
34 return(mconsole_unlink_socket());
38 static struct notifier_block reboot_notifier = {
39 .notifier_call = do_unlink_socket,
43 /* Safe without explicit locking for now. Tasklets provide their own
44 * locking, and the interrupt handler is safe because it can't interrupt
45 * itself and it can only happen on CPU 0.
48 LIST_HEAD(mc_requests);
50 void mc_work_proc(void *unused)
52 struct mconsole_entry *req;
57 local_save_flags(flags);
58 req = list_entry(mc_requests.next, struct mconsole_entry,
61 done = list_empty(&mc_requests);
62 local_irq_restore(flags);
63 req->request.cmd->handler(&req->request);
68 DECLARE_WORK(mconsole_work, mc_work_proc, NULL);
70 void mconsole_interrupt(int irq, void *dev_id, struct pt_regs *regs)
73 struct mconsole_entry *new;
74 struct mc_request req;
77 while (mconsole_get_request(fd, &req)){
78 if(req.cmd->as_interrupt) (*req.cmd->handler)(&req);
80 new = kmalloc(sizeof(req), GFP_ATOMIC);
82 mconsole_reply(&req, "Out of memory", 1, 0);
85 list_add(&new->list, &mc_requests);
89 if(!list_empty(&mc_requests)) schedule_work(&mconsole_work);
90 reactivate_fd(fd, MCONSOLE_IRQ);
93 void mconsole_version(struct mc_request *req)
97 sprintf(version, "%s %s %s %s %s", system_utsname.sysname,
98 system_utsname.nodename, system_utsname.release,
99 system_utsname.version, system_utsname.machine);
100 mconsole_reply(req, version, 0, 0);
103 #define UML_MCONSOLE_HELPTEXT \
105 version - Get kernel version
106 help - Print this message
109 config <dev>=<config> - Add a new device to UML;
110 same syntax as command line
111 config <dev> - Query the configuration of a device
112 remove <dev> - Remove a device from UML
113 sysrq <letter> - Performs the SysRq action controlled by the letter
114 cad - invoke the Ctl-Alt-Del handler
115 stop - pause the UML; it will do nothing until it receives a 'go'
116 go - continue the UML after a 'stop'
119 void mconsole_help(struct mc_request *req)
121 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
124 void mconsole_halt(struct mc_request *req)
126 mconsole_reply(req, "", 0, 0);
130 void mconsole_reboot(struct mc_request *req)
132 mconsole_reply(req, "", 0, 0);
133 machine_restart(NULL);
136 extern void ctrl_alt_del(void);
138 void mconsole_cad(struct mc_request *req)
140 mconsole_reply(req, "", 0, 0);
144 void mconsole_go(struct mc_request *req)
146 mconsole_reply(req, "Not stopped", 1, 0);
149 void mconsole_stop(struct mc_request *req)
151 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
152 os_set_fd_block(req->originating_fd, 1);
153 mconsole_reply(req, "", 0, 0);
154 while(mconsole_get_request(req->originating_fd, req)){
155 if(req->cmd->handler == mconsole_go) break;
156 (*req->cmd->handler)(req);
158 os_set_fd_block(req->originating_fd, 0);
159 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
160 mconsole_reply(req, "", 0, 0);
163 /* This list is populated by __initcall routines. */
165 LIST_HEAD(mconsole_devices);
167 void mconsole_register_dev(struct mc_device *new)
169 list_add(&new->list, &mconsole_devices);
172 static struct mc_device *mconsole_find_dev(char *name)
174 struct list_head *ele;
175 struct mc_device *dev;
177 list_for_each(ele, &mconsole_devices){
178 dev = list_entry(ele, struct mc_device, list);
179 if(!strncmp(name, dev->name, strlen(dev->name)))
185 #define CONFIG_BUF_SIZE 64
187 static void mconsole_get_config(int (*get_config)(char *, char *, int,
189 struct mc_request *req, char *name)
191 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
194 if(get_config == NULL){
195 mconsole_reply(req, "No get_config routine defined", 1, 0);
200 size = sizeof(default_buf)/sizeof(default_buf[0]);
204 n = (*get_config)(name, buf, size, &error);
206 mconsole_reply(req, error, 1, 0);
211 mconsole_reply(req, buf, 0, 0);
215 if(buf != default_buf)
219 buf = kmalloc(size, GFP_KERNEL);
221 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
226 if(buf != default_buf)
231 void mconsole_config(struct mc_request *req)
233 struct mc_device *dev;
234 char *ptr = req->request.data, *name;
237 ptr += strlen("config");
238 while(isspace(*ptr)) ptr++;
239 dev = mconsole_find_dev(ptr);
241 mconsole_reply(req, "Bad configuration option", 1, 0);
245 name = &ptr[strlen(dev->name)];
247 while((*ptr != '=') && (*ptr != '\0'))
251 err = (*dev->config)(name);
252 mconsole_reply(req, "", err, 0);
254 else mconsole_get_config(dev->get_config, req, name);
257 void mconsole_remove(struct mc_request *req)
259 struct mc_device *dev;
260 char *ptr = req->request.data;
263 ptr += strlen("remove");
264 while(isspace(*ptr)) ptr++;
265 dev = mconsole_find_dev(ptr);
267 mconsole_reply(req, "Bad remove option", 1, 0);
270 err = (*dev->remove)(&ptr[strlen(dev->name)]);
271 mconsole_reply(req, "", err, 0);
274 #ifdef CONFIG_MAGIC_SYSRQ
275 void mconsole_sysrq(struct mc_request *req)
277 char *ptr = req->request.data;
279 ptr += strlen("sysrq");
280 while(isspace(*ptr)) ptr++;
282 handle_sysrq(*ptr, ¤t->thread.regs, NULL);
283 mconsole_reply(req, "", 0, 0);
286 void mconsole_sysrq(struct mc_request *req)
288 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
292 /* Changed by mconsole_setup, which is __setup, and called before SMP is
295 static char *notify_socket = NULL;
297 int mconsole_init(void)
302 if(umid_file_name("mconsole", file, sizeof(file))) return(-1);
303 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
305 sock = create_unix_socket(file, sizeof(file));
307 printk("Failed to initialize management console\n");
311 register_reboot_notifier(&reboot_notifier);
313 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
314 SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM,
315 "mconsole", (void *)sock);
317 printk("Failed to get IRQ for management console\n");
321 if(notify_socket != NULL){
322 notify_socket = uml_strdup(notify_socket);
323 if(notify_socket != NULL)
324 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
325 mconsole_socket_name,
326 strlen(mconsole_socket_name) + 1);
327 else printk(KERN_ERR "mconsole_setup failed to strdup "
331 printk("mconsole (version %d) initialized on %s\n",
332 MCONSOLE_VERSION, mconsole_socket_name);
336 __initcall(mconsole_init);
338 static int write_proc_mconsole(struct file *file, const char *buffer,
339 unsigned long count, void *data)
343 buf = kmalloc(count + 1, GFP_KERNEL);
347 if(copy_from_user(buf, buffer, count))
351 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
355 static int create_proc_mconsole(void)
357 struct proc_dir_entry *ent;
359 if(notify_socket == NULL) return(0);
361 ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL);
363 printk("create_proc_mconsole : create_proc_entry failed\n");
367 ent->read_proc = NULL;
368 ent->write_proc = write_proc_mconsole;
372 static spinlock_t notify_spinlock = SPIN_LOCK_UNLOCKED;
374 void lock_notify(void)
376 spin_lock(¬ify_spinlock);
379 void unlock_notify(void)
381 spin_unlock(¬ify_spinlock);
384 __initcall(create_proc_mconsole);
386 #define NOTIFY "=notify:"
388 static int mconsole_setup(char *str)
390 if(!strncmp(str, NOTIFY, strlen(NOTIFY))){
391 str += strlen(NOTIFY);
394 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
398 __setup("mconsole", mconsole_setup);
400 __uml_help(mconsole_setup,
401 "mconsole=notify:<socket>\n"
402 " Requests that the mconsole driver send a message to the named Unix\n"
403 " socket containing the name of the mconsole socket. This also serves\n"
404 " to notify outside processes when UML has booted far enough to respond\n"
405 " to mconsole requests.\n\n"
408 static int notify_panic(struct notifier_block *self, unsigned long unused1,
413 if(notify_socket == NULL) return(0);
415 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
416 strlen(message) + 1);
420 static struct notifier_block panic_exit_notifier = {
421 .notifier_call = notify_panic,
426 static int add_notifier(void)
428 notifier_chain_register(&panic_notifier_list, &panic_exit_notifier);
432 __initcall(add_notifier);
434 char *mconsole_notify_socket(void)
436 return(notify_socket);
439 EXPORT_SYMBOL(mconsole_notify_socket);
442 * Overrides for Emacs so that we follow Linus's tabbing style.
443 * Emacs will notice this stuff at the end of the file and automatically
444 * adjust the settings for this buffer only. This must remain at the end
446 * ---------------------------------------------------------------------------
448 * c-file-style: "linux"