dom0: Migrate qubes_restore (and all DispVM logic) to libxl

Detailed changes:
 - use domain config in separate file (not embeded in savefile)
 - DispVM domain config generated from dvm.conf (introduced by previous patches) by qubes_restore
 - use call 'xl restore' to restore domain (instead of command to xend)
 - additional parameter to qubes_restore - config template
 - minor changes (xenstore perms, block-detach without /dev/ prefix, etc)
This commit is contained in:
Marek Marczykowski 2011-06-08 03:36:02 +02:00
parent 81ae4fafcf
commit e5df78fe92
4 changed files with 172 additions and 164 deletions

View File

@ -31,6 +31,7 @@ from qubes.qubes import QubesDaemonPidfile
from qubes.qmemman_client import QMemmanClient
current_savefile = '/var/run/qubes/current_savefile'
current_dvm_conf = '/var/run/qubes/current_dvm.conf'
notify_object = None
class QfileDaemonDvm:
@ -58,6 +59,7 @@ class QfileDaemonDvm:
return None
retcode = subprocess.call(['/usr/lib/qubes/qubes_restore',
current_savefile,
current_dvm_conf,
'-c', vm.label.color,
'-i', vm.label.icon,
'-l', str(vm.label.index)])

View File

@ -45,7 +45,7 @@ xenstore-write /local/domain/$ID/qubes_save_request 1
xenstore-watch-qubes /local/domain/$ID/device/qubes_used_mem
xenstore-read /local/domain/$ID/qubes_gateway | \
cut -d . -f 3 | tr -d "\n" > $VMDIR/netvm_id.txt
xl block-detach $1 /dev/xvdb
xl block-detach $1 xvdb
MEM=$(xenstore-read /local/domain/$ID/device/qubes_used_mem)
echo "DVM boot complete, memory used=$MEM. Saving image..."
QMEMMAN_STOP=/var/run/qubes/do-not-membalance
@ -59,5 +59,7 @@ if ! xl save $1 $2 ; then
fi
rm -f $QMEMMAN_STOP
cd $VMDIR
# Fix start memory
sed -i -e "s/^memory.*/memory = $((MEM/1000))/" dvm.conf
tar -Scvf saved_cows.tar volatile.img
echo "DVM savefile created successfully."

View File

@ -12,95 +12,73 @@
#include <syslog.h>
#include <xs.h>
char xmlrpc_header[] =
"POST /RPC2 HTTP/1.0\r\n"
"Host: \r\n"
"User-Agent: xmlrpclib.py/1.0.1 (by www.pythonware.com)\r\n"
"Content-Type: text/xml\r\n" "Content-Length: %d\r\n" "\r\n";
char xmlrpc_body_restore[] =
"<?xml version='1.0'?>\n"
"<methodCall>\n"
"<methodName>xend.domain.restore</methodName>\n"
"<params>\n"
"<param>\n"
"<value><string>%s</string></value>\n"
"</param>\n"
"<param>\n"
"<value><boolean>0</boolean></value>\n"
"</param>\n" "</params>\n" "</methodCall>\n";
char xmlrpc_body_setmem[] =
"<?xml version='1.0'?>\n<methodCall>\n<methodName>xend.domain.setMemoryTarget</methodName>\n<params>\n<param>\n<value><string>%d</string></value>\n</param>\n<param>\n<value><int>%d</int></value>\n</param>\n</params>\n</methodCall>\n";
void send_raw(int fd, char *body)
{
char *header;
asprintf(&header, xmlrpc_header, strlen(body));
if (write(fd, header, strlen(header)) != strlen(header)) {
perror("write xend");
int restore_domain(char *restore_file, char *conf_file, char *name) {
int pid, status, domid;
int pipe_fd[2];
char buf[256];
char *endptr;
switch (pid = fork()) {
case -1:
perror("fork");
exit(1);
}
if (write(fd, body, strlen(body)) != strlen(body)) {
perror("write xend");
exit(1);
}
shutdown(fd, SHUT_WR);
}
void send_req_restore(int fd, char *name)
{
char *body;
asprintf(&body, xmlrpc_body_restore, name);
send_raw(fd, body);
}
void send_req_setmem(int fd, int domid, int mem)
{
char *body;
asprintf(&body, xmlrpc_body_setmem, domid, mem);
send_raw(fd, body);
}
char *recv_resp(int fd)
{
#define RESPSIZE 65536
static char buf[RESPSIZE];
int total = 0;
int n;
for (;;) {
n = read(fd, buf + total, RESPSIZE - total);
if (n == 0) {
buf[total] = 0;
close(fd);
return buf;
}
if (n < 0) {
perror("xend read");
case 0:
close(1);
if (dup2(open("/dev/null", O_RDWR), 1)==-1) {
perror("dup2 or open");
exit(1);
}
total += n;
execl("/usr/sbin/xl", "xl", "restore", conf_file, restore_file, NULL);
perror("execl");
exit(1);
default:;
}
if (waitpid(pid, &status, 0) < 0) {
perror("waitpid");
exit(1);
}
if (status != 0) {
fprintf(stderr, "Error starting VM\n");
exit(1);
}
// read domid
if (pipe(pipe_fd)==-1) {
perror("pipe");
exit(1);
}
switch (pid = fork()) {
case -1:
perror("fork");
exit(1);
case 0:
close(1);
if (dup2(pipe_fd[1], 1) == -1) {
perror("dup2");
exit(1);
}
execl("/usr/sbin/xl", "xl", "domid", name, NULL);
perror("execl");
exit(1);
default:;
}
read(pipe_fd[0], buf, sizeof(buf)-1);
buf[sizeof(buf)-1] = 0;
domid = strtoul(buf, &endptr, 10);
if (domid <= 0 || *endptr != '\n') {
fprintf(stderr, "Cannot get DispVM xid\n");
exit(1);
}
if (waitpid(pid, &status, 0) < 0) {
perror("waitpid");
exit(1);
}
if (status != 0) {
fprintf(stderr, "Error getting DispVM xid\n");
exit(1);
}
return domid;
}
void bad_resp(char *resp)
{
fprintf(stderr, "Error; Xend response:\n%s\n", resp);
exit(1);
}
int parse_resp(char *resp)
{
char *domid;
if (strstr(resp, "<fault>"))
bad_resp(resp);
if (!strstr(resp, "domid"))
bad_resp(resp);
domid = strstr(resp, "<int>");
if (!domid)
bad_resp(resp);
return strtoul(domid + 5, NULL, 0);
}
char *gettime()
{
@ -161,27 +139,6 @@ void preload_cache(int fd)
}
}
int xend_connect()
{
struct sockaddr_un server;
int s;
s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s < 0) {
perror("socket af_unix");
exit(1);
}
server.sun_family = AF_UNIX;
strcpy(server.sun_path, "/var/run/xend/xmlrpc.sock");
if (connect
(s, (struct sockaddr *) &server,
strlen(server.sun_path) + sizeof(server.sun_family))) {
perror("connext xend");
exit(1);
}
return s;
}
void start_rexec(int domid)
{
int pid, status;
@ -214,40 +171,13 @@ void start_guid(int domid, int argc, char **argv)
guid_args[0] = "qubes_guid";
guid_args[1] = "-d";
guid_args[2] = dstr;
for (i = 2; i < argc; i++)
for (i = 3; i < argc; i++)
guid_args[i + 1] = argv[i];
guid_args[argc + 1] = NULL;
execv("/usr/bin/qubes_guid", guid_args);
perror("execv");
}
// modify the savefile. fd = fd to the open savefile,
// buf - already read 1st page of the savefile
// pattern - pattern to search for
// val - string to replace pattern with
void fix_savefile(int fd, char *buf, char *pattern, char *val)
{
int i, len = strlen(val), origlen;
char *bracket;
char *loc = strstr(buf + 20, pattern) + strlen(pattern);
if (!loc)
return;
bracket = index(loc, ')');
if (!bracket)
return;
origlen = (long) bracket - (long) loc;
if (origlen < len) {
fprintf(stderr, "too long string %s\n", val);
exit(1);
}
for (i = 0; i < origlen - len; i++)
loc[i] = ' ';
memcpy(loc + i, val, strlen(val));
lseek(fd, (long) loc - (long) buf, SEEK_SET);
write(fd, loc, origlen);
}
char *dispname_by_dispid(int dispid)
{
static char retbuf[16];
@ -269,15 +199,17 @@ char *build_dvm_ip(int netvm, int id)
// normally, it should be "templatename-dvm"
char *get_vmname_from_savefile(int fd)
{
int buflen;
static char buf[4096];
char *name;
char *slash;
lseek(fd, 0, SEEK_SET);
if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
perror("read savefile");
buflen = read(fd, buf, sizeof(buf) - 1);
if (buflen < 0) {
perror("read vm conf");
exit(1);
}
buf[sizeof(buf) - 1] = 0;
buf[buflen] = 0;
name = strstr(buf + 20, NAME_PATTERN);
if (!name) {
fprintf(stderr,
@ -295,25 +227,69 @@ char *get_vmname_from_savefile(int fd)
return slash + 1;
}
void fix_savefile_all(int fd, int dispid, int netvm_id)
void fill_field(FILE *conf, char *field, int dispid, int netvm_id)
{
char val[256];
char buf[4096];
lseek(fd, 0, SEEK_SET);
if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
perror("read savefile");
if (!strcmp(field, "NAME")) {
fprintf(conf, "%s", dispname_by_dispid(dispid));
} else if (!strcmp(field, "MAC")) {
fprintf(conf, "00:16:3e:7c:8b:%02x", dispid);
} else if (!strcmp(field, "IP")) {
fprintf(conf, "%s", build_dvm_ip(netvm_id, dispid));
} else if (!strcmp(field, "UUID")) {
// currently not present in conf file
fprintf(conf, "064cd14c-95ad-4fc2-a4c9-cf9f522e5b%02x", dispid);
} else {
fprintf(stderr, "unknown field in vm conf: %s\n", field);
exit(1);
}
buf[sizeof(buf) - 1] = 0;
snprintf(val, sizeof(val),
"064cd14c-95ad-4fc2-a4c9-cf9f522e5b%02x", dispid);
fix_savefile(fd, buf, "(uuid ", val);
fix_savefile(fd, buf, "(name ", dispname_by_dispid(dispid));
snprintf(val, sizeof(val), "00:16:3e:7c:8b:%02x", dispid);
fix_savefile(fd, buf, "(mac ", val);
fix_savefile(fd, buf, "(ip ", build_dvm_ip(netvm_id, dispid));
}
// modify the config file. conf = FILE of the new config,
// conf_templ - fd of config template
// pattern - pattern to search for
// val - string to replace pattern with
void fix_conffile(FILE *conf, int conf_templ, int dispid, int netvm_id)
{
int buflen, cur_len = 0;
char buf[4096];
char *bufpos = buf;
char *pattern, *patternend;
/* read config template */
lseek(conf_templ, 0, SEEK_SET);
while ((cur_len = read(conf_templ, buf+cur_len, sizeof(buf)-cur_len)) > 0) {
buflen+=cur_len;
}
if (cur_len < 0) {
perror("read vm conf");
exit(1);
}
while ((pattern = index(bufpos, '%'))) {
fwrite(bufpos, 1, pattern-bufpos, conf);
if (ferror(conf)) {
perror("write vm conf");
exit(1);
}
patternend = index(pattern+1, '%');
if (!patternend) {
fprintf(stderr, "Unmatched '%%' in VM config\n");
exit(1);
}
*patternend = '\0';
fill_field(conf, pattern+1, dispid, netvm_id);
bufpos = patternend+1;
}
while ((cur_len = fwrite(bufpos, 1, buflen-(bufpos-buf), conf)) > 0) {
bufpos+=cur_len;
}
if (ferror(conf)) {
perror("write vm conf");
exit(1);
}
}
void unpack_cows(char *name)
{
char vmdir[4096];
@ -354,6 +330,17 @@ void write_xs_single(struct xs_handle *xs, int domid, char *name,
}
}
void perm_xs_single(struct xs_handle *xs, int domid, char *name,
struct xs_permissions *perms, int nperms)
{
char key[256];
snprintf(key, sizeof(key), "/local/domain/%d/%s", domid, name);
if (!xs_set_permissions(xs, XBT_NULL, key, perms, nperms)) {
fprintf(stderr, "xs_set_permissions");
exit(1);
}
}
int get_netvm_id_from_name(char *name)
{
int fd, n;
@ -376,6 +363,7 @@ void setup_xenstore(int netvm_id, int domid, int dvmid, char *name)
{
char val[256];
struct xs_handle *xs = xs_daemon_open();
struct xs_permissions perm[1];
if (!xs) {
perror("xs_daemon_open");
exit(1);
@ -390,6 +378,12 @@ void setup_xenstore(int netvm_id, int domid, int dvmid, char *name)
write_xs_single(xs, domid, "qubes_secondary_dns", val);
write_xs_single(xs, domid, "qubes_vm_type", "AppVM");
write_xs_single(xs, domid, "qubes_restore_complete", "True");
perm[0].id = domid;
perm[0].perms = XS_PERM_NONE;
perm_xs_single(xs, domid, "device", perm, 1);
perm_xs_single(xs, domid, "memory", perm, 1);
xs_daemon_close(xs);
}
@ -436,35 +430,41 @@ void redirect_stderr()
int main(int argc, char **argv)
{
int fd, domid, dispid, netvm_id;
char *resp;
int conf_templ, domid, dispid, netvm_id;
FILE *conf;
char *name;
if (argc < 2) {
char confname[256];
if (argc < 3) {
fprintf(stderr,
"usage: %s savefile [guid args] \n", argv[0]);
"usage: %s savefile conf_templ [guid args] \n", argv[0]);
exit(1);
}
redirect_stderr();
fprintf(stderr, "time=%s, starting\n", gettime());
set_fast_flag();
atexit(rm_fast_flag);
fd = open(argv[1], O_RDWR);
if (fd < 0) {
perror("open savefile");
conf_templ = open(argv[2], O_RDONLY);
if (conf_templ < 0) {
perror("fopen vm conf");
exit(1);
}
dispid = get_next_disposable_id();
name = get_vmname_from_savefile(fd);
name = get_vmname_from_savefile(conf_templ);
netvm_id = get_netvm_id_from_name(name);
fix_savefile_all(fd, dispid, netvm_id);
snprintf(confname, sizeof(confname), "/tmp/qubes-dvm-%d.xl", dispid);
conf = fopen(confname, "w");
if (!conf) {
perror("fopen new vm conf");
exit(1);
}
fix_conffile(conf, conf_templ, dispid, netvm_id);
close(conf_templ);
fclose(conf);
// printf("name=%s\n", name);
unpack_cows(name);
// no preloading for now, assume savefile in shm
// preload_cache(fd);
fd = xend_connect();
send_req_restore(fd, argv[1]);
resp = recv_resp(fd);
domid = parse_resp(resp);
domid=restore_domain(argv[1], confname, dispname_by_dispid(dispid));
write_varrun_domid(domid, dispname_by_dispid(dispid), name);
fprintf(stderr,
"time=%s, created domid=%d, creating xenstore entries\n",

View File

@ -40,12 +40,16 @@ if ! /usr/lib/qubes/qubes_prepare_saved_domain.sh \
fi
ROOT=/var/lib/qubes/dvmdata/savefile_root
DEFAULT=/var/lib/qubes/dvmdata/default_savefile
DEFAULTCONF=/var/lib/qubes/dvmdata/default_dvm.conf
CURRENT=/var/run/qubes/current_savefile
CURRENTCONF=/var/run/qubes/current_dvm.conf
SHMDIR=/dev/shm/qubes
SHMCOPY=$SHMDIR/current_savefile
rm -f $ROOT $DEFAULT $CURRENT
rm -f $ROOT $DEFAULT $CURRENT $DEFAULTCONF $CURRENTCONF
ln -s "/var/lib/qubes/appvms/$DVMTMPL/dvm-savefile" $DEFAULT
ln -s "/var/lib/qubes/vm-templates/$TEMPLATENAME/root.img" $ROOT
ln -s $DVMTMPLDIR/dvm.conf $DEFAULTCONF
ln -s $DVMTMPLDIR/dvm.conf $CURRENTCONF
if [ -f /var/lib/qubes/dvmdata/dont_use_shm ] ; then
ln -s $DEFAULT $CURRENT
else