Jeremy Fitzhardinge | 3e2b8fb | 2007-07-17 18:37:07 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Handle extern requests for shutdown, reboot and sysrq |
| 3 | */ |
| 4 | #include <linux/kernel.h> |
| 5 | #include <linux/err.h> |
| 6 | #include <linux/reboot.h> |
| 7 | #include <linux/sysrq.h> |
| 8 | |
| 9 | #include <xen/xenbus.h> |
| 10 | |
| 11 | #define SHUTDOWN_INVALID -1 |
| 12 | #define SHUTDOWN_POWEROFF 0 |
| 13 | #define SHUTDOWN_SUSPEND 2 |
| 14 | /* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only |
| 15 | * report a crash, not be instructed to crash! |
| 16 | * HALT is the same as POWEROFF, as far as we're concerned. The tools use |
| 17 | * the distinction when we return the reason code to them. |
| 18 | */ |
| 19 | #define SHUTDOWN_HALT 4 |
| 20 | |
| 21 | /* Ignore multiple shutdown requests. */ |
| 22 | static int shutting_down = SHUTDOWN_INVALID; |
| 23 | |
| 24 | static void shutdown_handler(struct xenbus_watch *watch, |
| 25 | const char **vec, unsigned int len) |
| 26 | { |
| 27 | char *str; |
| 28 | struct xenbus_transaction xbt; |
| 29 | int err; |
| 30 | |
| 31 | if (shutting_down != SHUTDOWN_INVALID) |
| 32 | return; |
| 33 | |
| 34 | again: |
| 35 | err = xenbus_transaction_start(&xbt); |
| 36 | if (err) |
| 37 | return; |
| 38 | |
| 39 | str = (char *)xenbus_read(xbt, "control", "shutdown", NULL); |
| 40 | /* Ignore read errors and empty reads. */ |
| 41 | if (XENBUS_IS_ERR_READ(str)) { |
| 42 | xenbus_transaction_end(xbt, 1); |
| 43 | return; |
| 44 | } |
| 45 | |
| 46 | xenbus_write(xbt, "control", "shutdown", ""); |
| 47 | |
| 48 | err = xenbus_transaction_end(xbt, 0); |
| 49 | if (err == -EAGAIN) { |
| 50 | kfree(str); |
| 51 | goto again; |
| 52 | } |
| 53 | |
| 54 | if (strcmp(str, "poweroff") == 0 || |
| 55 | strcmp(str, "halt") == 0) |
| 56 | orderly_poweroff(false); |
| 57 | else if (strcmp(str, "reboot") == 0) |
| 58 | ctrl_alt_del(); |
| 59 | else { |
| 60 | printk(KERN_INFO "Ignoring shutdown request: %s\n", str); |
| 61 | shutting_down = SHUTDOWN_INVALID; |
| 62 | } |
| 63 | |
| 64 | kfree(str); |
| 65 | } |
| 66 | |
| 67 | static void sysrq_handler(struct xenbus_watch *watch, const char **vec, |
| 68 | unsigned int len) |
| 69 | { |
| 70 | char sysrq_key = '\0'; |
| 71 | struct xenbus_transaction xbt; |
| 72 | int err; |
| 73 | |
| 74 | again: |
| 75 | err = xenbus_transaction_start(&xbt); |
| 76 | if (err) |
| 77 | return; |
| 78 | if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) { |
| 79 | printk(KERN_ERR "Unable to read sysrq code in " |
| 80 | "control/sysrq\n"); |
| 81 | xenbus_transaction_end(xbt, 1); |
| 82 | return; |
| 83 | } |
| 84 | |
| 85 | if (sysrq_key != '\0') |
| 86 | xenbus_printf(xbt, "control", "sysrq", "%c", '\0'); |
| 87 | |
| 88 | err = xenbus_transaction_end(xbt, 0); |
| 89 | if (err == -EAGAIN) |
| 90 | goto again; |
| 91 | |
| 92 | if (sysrq_key != '\0') |
| 93 | handle_sysrq(sysrq_key, NULL); |
| 94 | } |
| 95 | |
| 96 | static struct xenbus_watch shutdown_watch = { |
| 97 | .node = "control/shutdown", |
| 98 | .callback = shutdown_handler |
| 99 | }; |
| 100 | |
| 101 | static struct xenbus_watch sysrq_watch = { |
| 102 | .node = "control/sysrq", |
| 103 | .callback = sysrq_handler |
| 104 | }; |
| 105 | |
| 106 | static int setup_shutdown_watcher(void) |
| 107 | { |
| 108 | int err; |
| 109 | |
| 110 | err = register_xenbus_watch(&shutdown_watch); |
| 111 | if (err) { |
| 112 | printk(KERN_ERR "Failed to set shutdown watcher\n"); |
| 113 | return err; |
| 114 | } |
| 115 | |
| 116 | err = register_xenbus_watch(&sysrq_watch); |
| 117 | if (err) { |
| 118 | printk(KERN_ERR "Failed to set sysrq watcher\n"); |
| 119 | return err; |
| 120 | } |
| 121 | |
| 122 | return 0; |
| 123 | } |
| 124 | |
| 125 | static int shutdown_event(struct notifier_block *notifier, |
| 126 | unsigned long event, |
| 127 | void *data) |
| 128 | { |
| 129 | setup_shutdown_watcher(); |
| 130 | return NOTIFY_DONE; |
| 131 | } |
| 132 | |
| 133 | static int __init setup_shutdown_event(void) |
| 134 | { |
| 135 | static struct notifier_block xenstore_notifier = { |
| 136 | .notifier_call = shutdown_event |
| 137 | }; |
| 138 | register_xenstore_notifier(&xenstore_notifier); |
| 139 | |
| 140 | return 0; |
| 141 | } |
| 142 | |
| 143 | subsys_initcall(setup_shutdown_event); |