| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 |  | 
 | 36 | static int do_unlink_socket(struct notifier_block *notifier,  | 
 | 37 | 			    unsigned long what, void *data) | 
 | 38 | { | 
 | 39 | 	return(mconsole_unlink_socket()); | 
 | 40 | } | 
 | 41 |  | 
 | 42 |  | 
 | 43 | static 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 |  | 
 | 53 | LIST_HEAD(mc_requests); | 
 | 54 |  | 
 | 55 | static 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 |  | 
 | 71 | DECLARE_WORK(mconsole_work, mc_work_proc, NULL); | 
 | 72 |  | 
 | 73 | static 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 |  | 
 | 101 | void 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 |  | 
 | 111 | void 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 | 
 | 128 | void 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 |  | 
 | 208 | void 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 |  | 
 | 281 | void mconsole_help(struct mc_request *req) | 
 | 282 | { | 
 | 283 | 	mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0); | 
 | 284 | } | 
 | 285 |  | 
 | 286 | void mconsole_halt(struct mc_request *req) | 
 | 287 | { | 
 | 288 | 	mconsole_reply(req, "", 0, 0); | 
 | 289 | 	machine_halt(); | 
 | 290 | } | 
 | 291 |  | 
 | 292 | void mconsole_reboot(struct mc_request *req) | 
 | 293 | { | 
 | 294 | 	mconsole_reply(req, "", 0, 0); | 
 | 295 | 	machine_restart(NULL); | 
 | 296 | } | 
 | 297 |  | 
 | 298 | extern void ctrl_alt_del(void); | 
 | 299 |  | 
 | 300 | void mconsole_cad(struct mc_request *req) | 
 | 301 | { | 
 | 302 | 	mconsole_reply(req, "", 0, 0); | 
 | 303 | 	ctrl_alt_del(); | 
 | 304 | } | 
 | 305 |  | 
 | 306 | void mconsole_go(struct mc_request *req) | 
 | 307 | { | 
 | 308 | 	mconsole_reply(req, "Not stopped", 1, 0); | 
 | 309 | } | 
 | 310 |  | 
 | 311 | void 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 |  | 
 | 327 | LIST_HEAD(mconsole_devices); | 
 | 328 |  | 
 | 329 | void mconsole_register_dev(struct mc_device *new) | 
 | 330 | { | 
 | 331 | 	list_add(&new->list, &mconsole_devices); | 
 | 332 | } | 
 | 333 |  | 
 | 334 | static 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 |  | 
 | 349 | static 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 |  | 
 | 393 | void 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 |  | 
 | 419 | void mconsole_remove(struct mc_request *req) | 
 | 420 | { | 
 | 421 | 	struct mc_device *dev;	 | 
| Jeff Dike | 29d56cf | 2005-06-25 14:55:25 -0700 | [diff] [blame] | 422 | 	char *ptr = req->request.data, *err_msg = ""; | 
 | 423 |         char error[256]; | 
 | 424 | 	int err, start, end, n; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | 	} | 
| Jeff Dike | 29d56cf | 2005-06-25 14:55:25 -0700 | [diff] [blame] | 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); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 462 | } | 
 | 463 |  | 
 | 464 | #ifdef CONFIG_MAGIC_SYSRQ | 
 | 465 | void 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, ¤t->thread.regs, NULL); | 
 | 474 | } | 
 | 475 | #else | 
 | 476 | void 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 |  */ | 
 | 485 | static char *notify_socket = NULL;  | 
 | 486 |  | 
 | 487 | int 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 |  | 
 | 530 | static 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 |  | 
 | 552 | static 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){ | 
 | 560 | 		printk("create_proc_mconsole : create_proc_entry failed\n"); | 
 | 561 | 		return(0); | 
 | 562 | 	} | 
 | 563 |  | 
 | 564 | 	ent->read_proc = NULL; | 
 | 565 | 	ent->write_proc = write_proc_mconsole; | 
 | 566 | 	return(0); | 
 | 567 | } | 
 | 568 |  | 
 | 569 | static DEFINE_SPINLOCK(notify_spinlock); | 
 | 570 |  | 
 | 571 | void lock_notify(void) | 
 | 572 | { | 
 | 573 | 	spin_lock(¬ify_spinlock); | 
 | 574 | } | 
 | 575 |  | 
 | 576 | void unlock_notify(void) | 
 | 577 | { | 
 | 578 | 	spin_unlock(¬ify_spinlock); | 
 | 579 | } | 
 | 580 |  | 
 | 581 | __initcall(create_proc_mconsole); | 
 | 582 |  | 
 | 583 | #define NOTIFY "=notify:" | 
 | 584 |  | 
 | 585 | static 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 |  | 
 | 605 | static 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 |  | 
 | 617 | static struct notifier_block panic_exit_notifier = { | 
 | 618 | 	.notifier_call 		= notify_panic, | 
 | 619 | 	.next 			= NULL, | 
 | 620 | 	.priority 		= 1 | 
 | 621 | }; | 
 | 622 |  | 
 | 623 | static 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 |  | 
 | 631 | char *mconsole_notify_socket(void) | 
 | 632 | { | 
 | 633 | 	return(notify_socket); | 
 | 634 | } | 
 | 635 |  | 
 | 636 | EXPORT_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 |  */ |