3e2b8fbeec
The guest domain can be asked to shutdown or reboot itself, or have a sysrq key injected, via xenbus. This patch adds a watcher for those events, and does the appropriate action. Signed-off-by: Jeremy Fitzhardinge <jeremy@xensource.com> Cc: Chris Wright <chrisw@sous-sol.org>
143 lines
3 KiB
C
143 lines
3 KiB
C
/*
|
|
* 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);
|