qubes_restore.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. #define _GNU_SOURCE
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5. #include <fcntl.h>
  6. #include <sys/socket.h>
  7. #include <sys/un.h>
  8. #include <signal.h>
  9. #include <unistd.h>
  10. #include <sys/time.h>
  11. #include <sys/wait.h>
  12. #include <syslog.h>
  13. #include <xs.h>
  14. char xmlrpc_header[] =
  15. "POST /RPC2 HTTP/1.0\r\n"
  16. "Host: \r\n"
  17. "User-Agent: xmlrpclib.py/1.0.1 (by www.pythonware.com)\r\n"
  18. "Content-Type: text/xml\r\n" "Content-Length: %d\r\n" "\r\n";
  19. char xmlrpc_body_restore[] =
  20. "<?xml version='1.0'?>\n"
  21. "<methodCall>\n"
  22. "<methodName>xend.domain.restore</methodName>\n"
  23. "<params>\n"
  24. "<param>\n"
  25. "<value><string>%s</string></value>\n"
  26. "</param>\n"
  27. "<param>\n"
  28. "<value><boolean>0</boolean></value>\n"
  29. "</param>\n" "</params>\n" "</methodCall>\n";
  30. char xmlrpc_body_setmem[] =
  31. "<?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";
  32. void send_raw(int fd, char *body)
  33. {
  34. char *header;
  35. asprintf(&header, xmlrpc_header, strlen(body));
  36. if (write(fd, header, strlen(header)) != strlen(header)) {
  37. perror("write xend");
  38. exit(1);
  39. }
  40. if (write(fd, body, strlen(body)) != strlen(body)) {
  41. perror("write xend");
  42. exit(1);
  43. }
  44. shutdown(fd, SHUT_WR);
  45. }
  46. void send_req_restore(int fd, char *name)
  47. {
  48. char *body;
  49. asprintf(&body, xmlrpc_body_restore, name);
  50. send_raw(fd, body);
  51. }
  52. void send_req_setmem(int fd, int domid, int mem)
  53. {
  54. char *body;
  55. asprintf(&body, xmlrpc_body_setmem, domid, mem);
  56. send_raw(fd, body);
  57. }
  58. char *recv_resp(int fd)
  59. {
  60. #define RESPSIZE 65536
  61. static char buf[RESPSIZE];
  62. int total = 0;
  63. int n;
  64. for (;;) {
  65. n = read(fd, buf + total, RESPSIZE - total);
  66. if (n == 0) {
  67. buf[total] = 0;
  68. close(fd);
  69. return buf;
  70. }
  71. if (n < 0) {
  72. perror("xend read");
  73. exit(1);
  74. }
  75. total += n;
  76. }
  77. }
  78. void bad_resp(char *resp)
  79. {
  80. fprintf(stderr, "Error; Xend response:\n%s\n", resp);
  81. exit(1);
  82. }
  83. int parse_resp(char *resp)
  84. {
  85. char *domid;
  86. if (strstr(resp, "<fault>"))
  87. bad_resp(resp);
  88. if (!strstr(resp, "domid"))
  89. bad_resp(resp);
  90. domid = strstr(resp, "<int>");
  91. if (!domid)
  92. bad_resp(resp);
  93. return strtoul(domid + 5, NULL, 0);
  94. }
  95. char *gettime()
  96. {
  97. static char retbuf[60];
  98. struct timeval tv;
  99. gettimeofday(&tv, NULL);
  100. snprintf(retbuf, sizeof(retbuf), "%lld.%lld",
  101. (long long) tv.tv_sec, (long long) tv.tv_usec);
  102. return retbuf;
  103. }
  104. int actually_do_unlink = 1;
  105. #define FAST_FLAG_PATH "/var/run/qubes/fast_block_attach"
  106. void set_fast_flag()
  107. {
  108. int fd = open(FAST_FLAG_PATH, O_CREAT | O_RDONLY, 0600);
  109. if (fd < 0) {
  110. perror("set_fast_flag");
  111. exit(1);
  112. }
  113. close(fd);
  114. }
  115. void rm_fast_flag()
  116. {
  117. if (actually_do_unlink)
  118. unlink(FAST_FLAG_PATH);
  119. }
  120. #define BUFSIZE (512*1024)
  121. void do_read(int fd)
  122. {
  123. static char buf[BUFSIZE];
  124. int n;
  125. while ((n = read(fd, buf, BUFSIZE))) {
  126. if (n < 0) {
  127. perror("read savefile");
  128. exit(1);
  129. }
  130. }
  131. }
  132. void preload_cache(int fd)
  133. {
  134. signal(SIGCHLD, SIG_IGN);
  135. switch (fork()) {
  136. case -1:
  137. perror("fork");
  138. exit(1);
  139. case 0:
  140. actually_do_unlink = 0;
  141. do_read(fd);
  142. fprintf(stderr, "time=%s, fs cache preload complete\n",
  143. gettime());
  144. exit(0);
  145. default:
  146. close(fd);
  147. }
  148. }
  149. int xend_connect()
  150. {
  151. struct sockaddr_un server;
  152. int s;
  153. s = socket(AF_UNIX, SOCK_STREAM, 0);
  154. if (s < 0) {
  155. perror("socket af_unix");
  156. exit(1);
  157. }
  158. server.sun_family = AF_UNIX;
  159. strcpy(server.sun_path, "/var/run/xend/xmlrpc.sock");
  160. if (connect
  161. (s, (struct sockaddr *) &server,
  162. strlen(server.sun_path) + sizeof(server.sun_family))) {
  163. perror("connext xend");
  164. exit(1);
  165. }
  166. return s;
  167. }
  168. void start_guid(int domid, int argc, char **argv)
  169. {
  170. int i;
  171. char dstr[40];
  172. char *guid_args[argc + 2];
  173. snprintf(dstr, sizeof(dstr), "%d", domid);
  174. guid_args[0] = "qubes_guid";
  175. guid_args[1] = "-d";
  176. guid_args[2] = dstr;
  177. for (i = 2; i < argc; i++)
  178. guid_args[i + 1] = argv[i];
  179. guid_args[argc + 1] = NULL;
  180. execv("/usr/bin/qubes_guid", guid_args);
  181. perror("execv");
  182. }
  183. // modify the savefile. fd = fd to the open savefile,
  184. // buf - already read 1st page of the savefile
  185. // pattern - pattern to search for
  186. // val - string to replace pattern with
  187. void fix_savefile(int fd, char *buf, char *pattern, char *val)
  188. {
  189. int i, len = strlen(val), origlen;
  190. char *bracket;
  191. char *loc = strstr(buf + 20, pattern) + strlen(pattern);
  192. if (!loc)
  193. return;
  194. bracket = index(loc, ')');
  195. if (!bracket)
  196. return;
  197. origlen = (long) bracket - (long) loc;
  198. if (origlen < len) {
  199. fprintf(stderr, "too long string %s\n", val);
  200. exit(1);
  201. }
  202. for (i = 0; i < origlen - len; i++)
  203. loc[i] = ' ';
  204. memcpy(loc + i, val, strlen(val));
  205. lseek(fd, (long) loc - (long) buf, SEEK_SET);
  206. write(fd, loc, origlen);
  207. }
  208. char *dispname_by_dispid(int dispid)
  209. {
  210. static char retbuf[16];
  211. snprintf(retbuf, sizeof(retbuf), "disp%d", dispid);
  212. return retbuf;
  213. }
  214. char *build_dvm_ip(int netvm, int id)
  215. {
  216. static char buf[256];
  217. snprintf(buf, sizeof(buf), "10.%d.%d.%d", netvm, id / 254 + 200,
  218. (id % 254) + 1);
  219. return buf;
  220. }
  221. #define NAME_PATTERN "/root-cow.img"
  222. // replaces the unique portions of the savefile with per-dvm values
  223. // returns the name of VM the savefile was taken for
  224. // by looking for /.../vmname/root-cow.img
  225. // normally, it should be "templatename-dvm"
  226. char *get_vmname_from_savefile(int fd)
  227. {
  228. static char buf[4096];
  229. char *name;
  230. char *slash;
  231. lseek(fd, 0, SEEK_SET);
  232. if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
  233. perror("read savefile");
  234. exit(1);
  235. }
  236. buf[sizeof(buf) - 1] = 0;
  237. name = strstr(buf + 20, NAME_PATTERN);
  238. if (!name) {
  239. fprintf(stderr,
  240. "cannot find 'root-cow.img' in savefile\n");
  241. exit(1);
  242. }
  243. *name = 0;
  244. slash = name - 1;
  245. while (slash[0] && slash[0] != '/')
  246. slash--;
  247. if (!*slash) {
  248. fprintf(stderr, "cannot find / in savefile\n");
  249. exit(1);
  250. }
  251. return slash + 1;
  252. }
  253. void fix_savefile_all(int fd, int dispid, int netvm_id)
  254. {
  255. char val[256];
  256. char buf[4096];
  257. lseek(fd, 0, SEEK_SET);
  258. if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
  259. perror("read savefile");
  260. exit(1);
  261. }
  262. buf[sizeof(buf) - 1] = 0;
  263. snprintf(val, sizeof(val),
  264. "064cd14c-95ad-4fc2-a4c9-cf9f522e5b%02x", dispid);
  265. fix_savefile(fd, buf, "(uuid ", val);
  266. fix_savefile(fd, buf, "(name ", dispname_by_dispid(dispid));
  267. snprintf(val, sizeof(val), "00:16:3e:7c:8b:%02x", dispid);
  268. fix_savefile(fd, buf, "(mac ", val);
  269. fix_savefile(fd, buf, "(ip ", build_dvm_ip(netvm_id, dispid));
  270. }
  271. void unpack_cows(char *name)
  272. {
  273. char vmdir[4096];
  274. char tarfile[4096];
  275. int status;
  276. snprintf(vmdir, sizeof(vmdir), "/var/lib/qubes/appvms/%s", name);
  277. snprintf(tarfile, sizeof(tarfile),
  278. "/var/lib/qubes/appvms/%s/saved_cows.tar", name);
  279. switch (fork()) {
  280. case -1:
  281. fprintf(stderr, "fork");
  282. exit(1);
  283. case 0:
  284. execl("/bin/tar", "tar", "-C", vmdir, "-Sxf",
  285. tarfile, NULL);
  286. perror("execl");
  287. exit(1);
  288. default:
  289. wait(&status);
  290. if (WEXITSTATUS(status)) {
  291. fprintf(stderr, "tar exited with status=0x%x\n",
  292. status);
  293. exit(1);
  294. }
  295. fprintf(stderr, "time=%s, cows restored\n", gettime());
  296. }
  297. }
  298. void write_xs_single(struct xs_handle *xs, int domid, char *name,
  299. char *val)
  300. {
  301. char key[256];
  302. snprintf(key, sizeof(key), "/local/domain/%d/%s", domid, name);
  303. if (!xs_write(xs, XBT_NULL, key, val, strlen(val))) {
  304. fprintf(stderr, "xs_write");
  305. exit(1);
  306. }
  307. }
  308. int get_netvm_id_from_name(char *name)
  309. {
  310. int fd, n;
  311. char netvm_id[256];
  312. char netvm_id_path[256];
  313. snprintf(netvm_id_path, sizeof(netvm_id_path),
  314. "/var/lib/qubes/appvms/%s/netvm_id.txt", name);
  315. fd = open(netvm_id_path, O_RDONLY);
  316. if (fd < 0) {
  317. perror("open netvm_id");
  318. exit(1);
  319. }
  320. n = read(fd, netvm_id, sizeof(netvm_id) - 1);
  321. close(fd);
  322. netvm_id[n] = 0;
  323. return atoi(netvm_id);
  324. }
  325. void setup_xenstore(int netvm_id, int domid, int dvmid, char *name)
  326. {
  327. char val[256];
  328. struct xs_handle *xs = xs_daemon_open();
  329. if (!xs) {
  330. perror("xs_daemon_open");
  331. exit(1);
  332. }
  333. write_xs_single(xs, domid, "qubes_ip",
  334. build_dvm_ip(netvm_id, dvmid));
  335. write_xs_single(xs, domid, "qubes_netmask", "255.255.0.0");
  336. snprintf(val, sizeof(val), "10.%d.0.1", netvm_id);
  337. write_xs_single(xs, domid, "qubes_gateway", val);
  338. snprintf(val, sizeof(val), "10.%d.255.254", netvm_id);
  339. write_xs_single(xs, domid, "qubes_secondary_dns", val);
  340. write_xs_single(xs, domid, "qubes_vm_type", "AppVM");
  341. write_xs_single(xs, domid, "qubes_restore_complete", "True");
  342. xs_daemon_close(xs);
  343. }
  344. int get_next_disposable_id()
  345. {
  346. int seq = 0;
  347. int fd = open("/var/run/qubes/dispVM_seq", O_RDWR);
  348. if (fd < 0) {
  349. perror("open dispVM_seq");
  350. exit(1);
  351. }
  352. read(fd, &seq, sizeof(seq));
  353. seq++;
  354. lseek(fd, 0, SEEK_SET);
  355. write(fd, &seq, sizeof(seq));
  356. close(fd);
  357. return seq;
  358. }
  359. void write_varrun_domid(int domid, char *dispname, char *orig)
  360. {
  361. FILE *f = fopen("/var/run/qubes/dispVM_xid", "w");
  362. if (!f) {
  363. perror("fopen dispVM_xid");
  364. exit(1);
  365. }
  366. fprintf(f, "%d\n%s\n%s\n", domid, dispname, orig);
  367. fclose(f);
  368. }
  369. void redirect_stderr()
  370. {
  371. int fd = open("/var/log/qubes/qubes_restore.log",
  372. O_CREAT | O_TRUNC | O_WRONLY, 0600);
  373. if (fd < 0) {
  374. syslog(LOG_DAEMON | LOG_ERR, "open qubes_restore.log");
  375. exit(1);
  376. }
  377. dup2(fd, 2);
  378. }
  379. int main(int argc, char **argv)
  380. {
  381. int fd, domid, dispid, netvm_id;
  382. char *resp;
  383. char *name;
  384. if (argc < 2) {
  385. fprintf(stderr,
  386. "usage: %s savefile [guid args] \n", argv[0]);
  387. exit(1);
  388. }
  389. redirect_stderr();
  390. fprintf(stderr, "time=%s, starting\n", gettime());
  391. set_fast_flag();
  392. atexit(rm_fast_flag);
  393. fd = open(argv[1], O_RDWR);
  394. if (fd < 0) {
  395. perror("open savefile");
  396. exit(1);
  397. }
  398. dispid = get_next_disposable_id();
  399. name = get_vmname_from_savefile(fd);
  400. netvm_id = get_netvm_id_from_name(name);
  401. fix_savefile_all(fd, dispid, netvm_id);
  402. // printf("name=%s\n", name);
  403. unpack_cows(name);
  404. // no preloading for now, assume savefile in shm
  405. // preload_cache(fd);
  406. fd = xend_connect();
  407. send_req_restore(fd, argv[1]);
  408. resp = recv_resp(fd);
  409. domid = parse_resp(resp);
  410. write_varrun_domid(domid, dispname_by_dispid(dispid), name);
  411. fprintf(stderr,
  412. "time=%s, created domid=%d, creating xenstore entries\n",
  413. gettime(), domid);
  414. #if 0
  415. fd = xend_connect();
  416. send_req_setmem(fd, domid, 400);
  417. resp = recv_resp(fd);
  418. // printf("%s\n", resp);
  419. fprintf(stderr, "time=%s, creating xenstore entries\n", gettime());
  420. #endif
  421. setup_xenstore(netvm_id, domid, dispid, name);
  422. fprintf(stderr, "time=%s, starting qubes_guid\n", gettime());
  423. rm_fast_flag();
  424. start_guid(domid, argc, argv);
  425. return 0;
  426. }