123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461 |
- #define _GNU_SOURCE
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #include <sys/socket.h>
- #include <sys/un.h>
- #include <signal.h>
- #include <unistd.h>
- #include <sys/time.h>
- #include <sys/wait.h>
- #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");
- 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");
- exit(1);
- }
- total += n;
- }
- }
- 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()
- {
- static char retbuf[60];
- struct timeval tv;
- gettimeofday(&tv, NULL);
- snprintf(retbuf, sizeof(retbuf), "%lld.%lld",
- (long long) tv.tv_sec, (long long) tv.tv_usec);
- return retbuf;
- }
- int actually_do_unlink = 1;
- #define FAST_FLAG_PATH "/var/run/qubes/fast_block_attach"
- void set_fast_flag()
- {
- int fd = open(FAST_FLAG_PATH, O_CREAT | O_RDONLY, 0600);
- if (fd < 0) {
- perror("set_fast_flag");
- exit(1);
- }
- close(fd);
- }
- void rm_fast_flag()
- {
- if (actually_do_unlink)
- unlink(FAST_FLAG_PATH);
- }
- #define BUFSIZE (512*1024)
- void do_read(int fd)
- {
- static char buf[BUFSIZE];
- int n;
- while ((n = read(fd, buf, BUFSIZE))) {
- if (n < 0) {
- perror("read savefile");
- exit(1);
- }
- }
- }
- void preload_cache(int fd)
- {
- signal(SIGCHLD, SIG_IGN);
- switch (fork()) {
- case -1:
- perror("fork");
- exit(1);
- case 0:
- actually_do_unlink = 0;
- do_read(fd);
- fprintf(stderr, "time=%s, fs cache preload complete\n",
- gettime());
- exit(0);
- default:
- close(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_guid(int domid, int argc, char **argv)
- {
- int i;
- char dstr[40];
- char *guid_args[argc + 2];
- snprintf(dstr, sizeof(dstr), "%d", domid);
- guid_args[0] = "qubes_guid";
- guid_args[1] = "-d";
- guid_args[2] = dstr;
- for (i = 2; 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];
- snprintf(retbuf, sizeof(retbuf), "disp%d", dispid);
- return retbuf;
- }
- char *build_dvm_ip(int netvm, int id)
- {
- static char buf[256];
- snprintf(buf, sizeof(buf), "10.%d.%d.%d", netvm, id / 254 + 200,
- (id % 254) + 1);
- return buf;
- }
- #define NAME_PATTERN "/root-cow.img"
- // replaces the unique portions of the savefile with per-dvm values
- // returns the name of VM the savefile was taken for
- // by looking for /.../vmname/root-cow.img
- // normally, it should be "templatename-dvm"
- char *get_vmname_from_savefile(int fd)
- {
- static char buf[4096];
- char *name;
- char *slash;
- lseek(fd, 0, SEEK_SET);
- if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
- perror("read savefile");
- exit(1);
- }
- buf[sizeof(buf) - 1] = 0;
- name = strstr(buf + 20, NAME_PATTERN);
- if (!name) {
- fprintf(stderr,
- "cannot find 'root-cow.img' in savefile\n");
- exit(1);
- }
- *name = 0;
- slash = name - 1;
- while (slash[0] && slash[0] != '/')
- slash--;
- if (!*slash) {
- fprintf(stderr, "cannot find / in savefile\n");
- exit(1);
- }
- return slash + 1;
- }
- void fix_savefile_all(int fd, 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");
- 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));
- }
- void unpack_cows(char *name)
- {
- char vmdir[4096];
- char tarfile[4096];
- int status;
- snprintf(vmdir, sizeof(vmdir), "/var/lib/qubes/appvms/%s", name);
- snprintf(tarfile, sizeof(tarfile),
- "/var/lib/qubes/appvms/%s/saved_cows.tar", name);
- switch (fork()) {
- case -1:
- fprintf(stderr, "fork");
- exit(1);
- case 0:
- execl("/bin/tar", "tar", "-C", vmdir, "-Sxf",
- tarfile, NULL);
- perror("execl");
- exit(1);
- default:
- wait(&status);
- if (WEXITSTATUS(status)) {
- fprintf(stderr, "tar exited with status=0x%x\n",
- status);
- exit(1);
- }
- fprintf(stderr, "time=%s, cows restored\n", gettime());
- }
- }
- void write_xs_single(struct xs_handle *xs, int domid, char *name,
- char *val)
- {
- char key[256];
- snprintf(key, sizeof(key), "/local/domain/%d/%s", domid, name);
- if (!xs_write(xs, XBT_NULL, key, val, strlen(val))) {
- fprintf(stderr, "xs_write");
- exit(1);
- }
- }
- int get_netvm_id_from_name(char *name)
- {
- int fd, n;
- char netvm_id[256];
- char netvm_id_path[256];
- snprintf(netvm_id_path, sizeof(netvm_id_path),
- "/var/lib/qubes/appvms/%s/netvm_id.txt", name);
- fd = open(netvm_id_path, O_RDONLY);
- if (fd < 0) {
- perror("open netvm_id");
- exit(1);
- }
- n = read(fd, netvm_id, sizeof(netvm_id) - 1);
- close(fd);
- netvm_id[n] = 0;
- return atoi(netvm_id);
- }
- void setup_xenstore(int netvm_id, int domid, int dvmid, char *name)
- {
- char val[256];
- struct xs_handle *xs = xs_daemon_open();
- if (!xs) {
- perror("xs_daemon_open");
- exit(1);
- }
- write_xs_single(xs, domid, "qubes_ip",
- build_dvm_ip(netvm_id, dvmid));
- write_xs_single(xs, domid, "qubes_netmask", "255.255.0.0");
- snprintf(val, sizeof(val), "10.%d.0.1", netvm_id);
- write_xs_single(xs, domid, "qubes_gateway", val);
- snprintf(val, sizeof(val), "10.%d.255.254", netvm_id);
- 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");
- xs_daemon_close(xs);
- }
- int get_next_disposable_id()
- {
- int seq = 0;
- int fd = open("/var/run/qubes/dispVM_seq", O_RDWR);
- if (fd < 0) {
- perror("open dispVM_seq");
- exit(1);
- }
- read(fd, &seq, sizeof(seq));
- seq++;
- lseek(fd, 0, SEEK_SET);
- write(fd, &seq, sizeof(seq));
- close(fd);
- return seq;
- }
- void write_varrun_domid(int domid, char *dispname, char *orig)
- {
- FILE *f = fopen("/var/run/qubes/dispVM_xid", "w");
- if (!f) {
- perror("fopen dispVM_xid");
- exit(1);
- }
- fprintf(f, "%d\n%s\n%s\n", domid, dispname, orig);
- fclose(f);
- }
- void redirect_stderr()
- {
- int fd = open("/var/log/qubes/qubes_restore.log",
- O_CREAT | O_TRUNC | O_WRONLY, 0600);
- if (fd < 0) {
- syslog(LOG_DAEMON | LOG_ERR, "open qubes_restore.log");
- exit(1);
- }
- dup2(fd, 2);
- }
- int main(int argc, char **argv)
- {
- int fd, domid, dispid, netvm_id;
- char *resp;
- char *name;
- if (argc < 2) {
- fprintf(stderr,
- "usage: %s savefile [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");
- exit(1);
- }
- dispid = get_next_disposable_id();
- name = get_vmname_from_savefile(fd);
- netvm_id = get_netvm_id_from_name(name);
- fix_savefile_all(fd, dispid, netvm_id);
- // 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);
- write_varrun_domid(domid, dispname_by_dispid(dispid), name);
- fprintf(stderr,
- "time=%s, created domid=%d, creating xenstore entries\n",
- gettime(), domid);
- #if 0
- fd = xend_connect();
- send_req_setmem(fd, domid, 400);
- resp = recv_resp(fd);
- // printf("%s\n", resp);
- fprintf(stderr, "time=%s, creating xenstore entries\n", gettime());
- #endif
- setup_xenstore(netvm_id, domid, dispid, name);
- fprintf(stderr, "time=%s, starting qubes_guid\n", gettime());
- rm_fast_flag();
- start_guid(domid, argc, argv);
- return 0;
- }
|