From e5df78fe92fb3a81dd4d2c0d6a4b9c3bf05c6ad2 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 8 Jun 2011 03:36:02 +0200 Subject: [PATCH] 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) --- dom0/restore/qfile-daemon-dvm | 2 + dom0/restore/qubes_prepare_saved_domain.sh | 4 +- dom0/restore/qubes_restore.c | 324 ++++++++++----------- dom0/restore/qvm-create-default-dvm | 6 +- 4 files changed, 172 insertions(+), 164 deletions(-) diff --git a/dom0/restore/qfile-daemon-dvm b/dom0/restore/qfile-daemon-dvm index c166c78d..837db4d4 100755 --- a/dom0/restore/qfile-daemon-dvm +++ b/dom0/restore/qfile-daemon-dvm @@ -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)]) diff --git a/dom0/restore/qubes_prepare_saved_domain.sh b/dom0/restore/qubes_prepare_saved_domain.sh index 2d17e1a5..f45ddf8b 100755 --- a/dom0/restore/qubes_prepare_saved_domain.sh +++ b/dom0/restore/qubes_prepare_saved_domain.sh @@ -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." diff --git a/dom0/restore/qubes_restore.c b/dom0/restore/qubes_restore.c index 15df3d5b..698d4a2e 100644 --- a/dom0/restore/qubes_restore.c +++ b/dom0/restore/qubes_restore.c @@ -12,95 +12,73 @@ #include #include -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[] = - "\n" - "\n" - "xend.domain.restore\n" - "\n" - "\n" - "%s\n" - "\n" - "\n" - "0\n" - "\n" "\n" "\n"; - -char xmlrpc_body_setmem[] = - "\n\nxend.domain.setMemoryTarget\n\n\n%d\n\n\n%d\n\n\n\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, "")) - bad_resp(resp); - if (!strstr(resp, "domid")) - bad_resp(resp); - domid = strstr(resp, ""); - 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", diff --git a/dom0/restore/qvm-create-default-dvm b/dom0/restore/qvm-create-default-dvm index 55a0ac37..75ffeeba 100755 --- a/dom0/restore/qvm-create-default-dvm +++ b/dom0/restore/qvm-create-default-dvm @@ -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