|
@@ -0,0 +1,143 @@
|
|
|
+/*
|
|
|
+ * Handle extern requests for shutdown, reboot and sysrq
|
|
|
+ */
|
|
|
+#include <linux/kernel.h>
|
|
|
+#include <linux/err.h>
|
|
|
+#include <linux/reboot.h>
|
|
|
+#include <linux/sysrq.h>
|
|
|
+
|
|
|
+#include <xen/xenbus.h>
|
|
|
+
|
|
|
+#define SHUTDOWN_INVALID -1
|
|
|
+#define SHUTDOWN_POWEROFF 0
|
|
|
+#define SHUTDOWN_SUSPEND 2
|
|
|
+/* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only
|
|
|
+ * report a crash, not be instructed to crash!
|
|
|
+ * HALT is the same as POWEROFF, as far as we're concerned. The tools use
|
|
|
+ * the distinction when we return the reason code to them.
|
|
|
+ */
|
|
|
+#define SHUTDOWN_HALT 4
|
|
|
+
|
|
|
+/* Ignore multiple shutdown requests. */
|
|
|
+static int shutting_down = SHUTDOWN_INVALID;
|
|
|
+
|
|
|
+static void shutdown_handler(struct xenbus_watch *watch,
|
|
|
+ const char **vec, unsigned int len)
|
|
|
+{
|
|
|
+ char *str;
|
|
|
+ struct xenbus_transaction xbt;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (shutting_down != SHUTDOWN_INVALID)
|
|
|
+ return;
|
|
|
+
|
|
|
+ again:
|
|
|
+ err = xenbus_transaction_start(&xbt);
|
|
|
+ if (err)
|
|
|
+ return;
|
|
|
+
|
|
|
+ str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
|
|
|
+ /* Ignore read errors and empty reads. */
|
|
|
+ if (XENBUS_IS_ERR_READ(str)) {
|
|
|
+ xenbus_transaction_end(xbt, 1);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ xenbus_write(xbt, "control", "shutdown", "");
|
|
|
+
|
|
|
+ err = xenbus_transaction_end(xbt, 0);
|
|
|
+ if (err == -EAGAIN) {
|
|
|
+ kfree(str);
|
|
|
+ goto again;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (strcmp(str, "poweroff") == 0 ||
|
|
|
+ strcmp(str, "halt") == 0)
|
|
|
+ orderly_poweroff(false);
|
|
|
+ else if (strcmp(str, "reboot") == 0)
|
|
|
+ ctrl_alt_del();
|
|
|
+ else {
|
|
|
+ printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
|
|
|
+ shutting_down = SHUTDOWN_INVALID;
|
|
|
+ }
|
|
|
+
|
|
|
+ kfree(str);
|
|
|
+}
|
|
|
+
|
|
|
+static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
|
|
|
+ unsigned int len)
|
|
|
+{
|
|
|
+ char sysrq_key = '\0';
|
|
|
+ struct xenbus_transaction xbt;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ again:
|
|
|
+ err = xenbus_transaction_start(&xbt);
|
|
|
+ if (err)
|
|
|
+ return;
|
|
|
+ if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
|
|
|
+ printk(KERN_ERR "Unable to read sysrq code in "
|
|
|
+ "control/sysrq\n");
|
|
|
+ xenbus_transaction_end(xbt, 1);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (sysrq_key != '\0')
|
|
|
+ xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
|
|
|
+
|
|
|
+ err = xenbus_transaction_end(xbt, 0);
|
|
|
+ if (err == -EAGAIN)
|
|
|
+ goto again;
|
|
|
+
|
|
|
+ if (sysrq_key != '\0')
|
|
|
+ handle_sysrq(sysrq_key, NULL);
|
|
|
+}
|
|
|
+
|
|
|
+static struct xenbus_watch shutdown_watch = {
|
|
|
+ .node = "control/shutdown",
|
|
|
+ .callback = shutdown_handler
|
|
|
+};
|
|
|
+
|
|
|
+static struct xenbus_watch sysrq_watch = {
|
|
|
+ .node = "control/sysrq",
|
|
|
+ .callback = sysrq_handler
|
|
|
+};
|
|
|
+
|
|
|
+static int setup_shutdown_watcher(void)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = register_xenbus_watch(&shutdown_watch);
|
|
|
+ if (err) {
|
|
|
+ printk(KERN_ERR "Failed to set shutdown watcher\n");
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = register_xenbus_watch(&sysrq_watch);
|
|
|
+ if (err) {
|
|
|
+ printk(KERN_ERR "Failed to set sysrq watcher\n");
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int shutdown_event(struct notifier_block *notifier,
|
|
|
+ unsigned long event,
|
|
|
+ void *data)
|
|
|
+{
|
|
|
+ setup_shutdown_watcher();
|
|
|
+ return NOTIFY_DONE;
|
|
|
+}
|
|
|
+
|
|
|
+static int __init setup_shutdown_event(void)
|
|
|
+{
|
|
|
+ static struct notifier_block xenstore_notifier = {
|
|
|
+ .notifier_call = shutdown_event
|
|
|
+ };
|
|
|
+ register_xenstore_notifier(&xenstore_notifier);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+subsys_initcall(setup_shutdown_event);
|