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:
parent
81ae4fafcf
commit
e5df78fe92
@ -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)])
|
||||
|
@ -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."
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user