qubes-restore.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  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 <sys/file.h>
  13. #include <syslog.h>
  14. #include <xs.h>
  15. int restore_domain(const char *restore_file, const char *conf_file, const char *name) {
  16. int pid, status, domid, ret;
  17. int pipe_fd[2];
  18. char buf[256];
  19. char *endptr;
  20. switch (pid = fork()) {
  21. case -1:
  22. perror("fork");
  23. exit(1);
  24. case 0:
  25. close(1);
  26. if (dup2(open("/dev/null", O_RDWR), 1)==-1) {
  27. perror("dup2 or open");
  28. exit(1);
  29. }
  30. execl("/usr/sbin/xl", "xl", "-v", "restore", "-d", conf_file, restore_file, NULL);
  31. perror("execl");
  32. exit(1);
  33. default:;
  34. }
  35. if (waitpid(pid, &status, 0) < 0) {
  36. perror("waitpid");
  37. exit(1);
  38. }
  39. if (status != 0) {
  40. fprintf(stderr, "Error starting VM\n");
  41. exit(1);
  42. }
  43. // read domid
  44. if (pipe(pipe_fd)==-1) {
  45. perror("pipe");
  46. exit(1);
  47. }
  48. switch (pid = fork()) {
  49. case -1:
  50. perror("fork");
  51. exit(1);
  52. case 0:
  53. close(1);
  54. if (dup2(pipe_fd[1], 1) == -1) {
  55. perror("dup2");
  56. exit(1);
  57. }
  58. execl("/usr/sbin/xl", "xl", "domid", name, NULL);
  59. perror("execl");
  60. exit(1);
  61. default:;
  62. }
  63. ret = read(pipe_fd[0], buf, sizeof(buf)-1);
  64. if (ret < 0) {
  65. perror("read");
  66. exit(1);
  67. }
  68. buf[ret] = 0;
  69. domid = strtoul(buf, &endptr, 10);
  70. if (domid <= 0 || *endptr != '\n') {
  71. fprintf(stderr, "Cannot get DispVM xid\n");
  72. exit(1);
  73. }
  74. if (waitpid(pid, &status, 0) < 0) {
  75. perror("waitpid");
  76. exit(1);
  77. }
  78. if (status != 0) {
  79. fprintf(stderr, "Error getting DispVM xid\n");
  80. exit(1);
  81. }
  82. return domid;
  83. }
  84. const char *gettime(void)
  85. {
  86. static char retbuf[60];
  87. struct timeval tv;
  88. gettimeofday(&tv, NULL);
  89. snprintf(retbuf, sizeof(retbuf), "%lld.%06lld",
  90. (long long) tv.tv_sec, (long long) tv.tv_usec);
  91. return retbuf;
  92. }
  93. int actually_do_unlink = 1;
  94. #define FAST_FLAG_PATH "/var/run/qubes/fast-block-attach"
  95. void set_fast_flag(void)
  96. {
  97. int fd = open(FAST_FLAG_PATH, O_CREAT | O_RDONLY, 0600);
  98. if (fd < 0) {
  99. perror("set_fast_flag");
  100. exit(1);
  101. }
  102. close(fd);
  103. }
  104. void rm_fast_flag(void)
  105. {
  106. if (actually_do_unlink)
  107. unlink(FAST_FLAG_PATH);
  108. }
  109. #define BUFSIZE (512*1024)
  110. void do_read(int fd)
  111. {
  112. static char buf[BUFSIZE];
  113. int n;
  114. while ((n = read(fd, buf, BUFSIZE))) {
  115. if (n < 0) {
  116. perror("read savefile");
  117. exit(1);
  118. }
  119. }
  120. }
  121. void preload_cache(int fd)
  122. {
  123. signal(SIGCHLD, SIG_IGN);
  124. switch (fork()) {
  125. case -1:
  126. perror("fork");
  127. exit(1);
  128. case 0:
  129. actually_do_unlink = 0;
  130. do_read(fd);
  131. fprintf(stderr, "time=%s, fs cache preload complete\n",
  132. gettime());
  133. exit(0);
  134. default:
  135. close(fd);
  136. }
  137. }
  138. void start_rexec(int domid, const char *domain_name, const char *default_user)
  139. {
  140. int pid, status;
  141. char dstr[40];
  142. snprintf(dstr, sizeof(dstr), "%d", domid);
  143. switch (pid = fork()) {
  144. case -1:
  145. perror("fork");
  146. exit(1);
  147. case 0:
  148. execl("/usr/lib/qubes/qrexec-daemon", "qrexec-daemon",
  149. dstr, domain_name, default_user, NULL);
  150. perror("execl");
  151. exit(1);
  152. default:;
  153. }
  154. if (waitpid(pid, &status, 0) < 0) {
  155. perror("waitpid");
  156. exit(1);
  157. }
  158. }
  159. void start_guid(int domid, int argc, char **argv)
  160. {
  161. int i;
  162. char dstr[40];
  163. char *guid_args[argc + 4];
  164. snprintf(dstr, sizeof(dstr), "%d", domid);
  165. guid_args[0] = "qubes-guid";
  166. guid_args[1] = "-d";
  167. guid_args[2] = dstr;
  168. for (i = 0; i < argc; i++)
  169. guid_args[i+3] = argv[i];
  170. guid_args[argc+3] = NULL;
  171. execv("/usr/bin/qubes-guid", guid_args);
  172. perror("execv");
  173. }
  174. const char *dispname_by_dispid(int dispid)
  175. {
  176. static char retbuf[16];
  177. snprintf(retbuf, sizeof(retbuf), "disp%d", dispid);
  178. return retbuf;
  179. }
  180. const char *build_dvm_ip(int netvm, int id)
  181. {
  182. static char buf[256];
  183. snprintf(buf, sizeof(buf), "10.138.%d.%d", netvm, (id % 254) + 1);
  184. return buf;
  185. }
  186. #define NAME_PATTERN "/volatile.img"
  187. // replaces the unique portions of the savefile with per-dvm values
  188. // returns the name of VM the savefile was taken for
  189. // by looking for /.../vmname/volatile.img
  190. // normally, it should be "templatename-dvm"
  191. const char *get_vmname_from_savefile(int fd)
  192. {
  193. int buflen;
  194. static char buf[4096];
  195. char *name;
  196. char *slash;
  197. if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
  198. perror("lseek vm conf");
  199. exit(1);
  200. }
  201. buflen = read(fd, buf, sizeof(buf) - 1);
  202. if (buflen < 0) {
  203. perror("read vm conf");
  204. exit(1);
  205. }
  206. buf[buflen] = 0;
  207. name = strstr(buf, NAME_PATTERN);
  208. if (!name) {
  209. fprintf(stderr,
  210. "cannot find '"NAME_PATTERN"' in savefile\n");
  211. exit(1);
  212. }
  213. *name = 0;
  214. slash = name - 1;
  215. while (slash >= buf && slash[0] && slash[0] != '/')
  216. slash--;
  217. if (slash < buf || !*slash) {
  218. fprintf(stderr, "cannot find / in savefile\n");
  219. exit(1);
  220. }
  221. return slash + 1;
  222. }
  223. void fill_field(FILE *conf, const char *field, int dispid, int netvm_id)
  224. {
  225. if (!strcmp(field, "NAME")) {
  226. fprintf(conf, "%s", dispname_by_dispid(dispid));
  227. } else if (!strcmp(field, "MAC")) {
  228. fprintf(conf, "00:16:3e:7c:8b:%02x", dispid);
  229. } else if (!strcmp(field, "IP")) {
  230. fprintf(conf, "%s", build_dvm_ip(netvm_id, dispid));
  231. } else if (!strcmp(field, "UUID")) {
  232. // currently not present in conf file
  233. fprintf(conf, "064cd14c-95ad-4fc2-a4c9-cf9f522e5b%02x", dispid);
  234. } else {
  235. fprintf(stderr, "unknown field in vm conf: %s\n", field);
  236. exit(1);
  237. }
  238. }
  239. // modify the config file. conf = FILE of the new config,
  240. // conf_templ - fd of config template
  241. // pattern - pattern to search for
  242. // val - string to replace pattern with
  243. void fix_conffile(FILE *conf, int conf_templ, int dispid, int netvm_id)
  244. {
  245. int buflen = 0, cur_len = 0;
  246. char buf[4096];
  247. char *bufpos = buf;
  248. char *pattern, *patternend;
  249. /* read config template */
  250. if (lseek(conf_templ, 0, SEEK_SET) == (off_t)-1) {
  251. perror("lseek vm conf");
  252. exit(1);
  253. }
  254. while ((cur_len = read(conf_templ, buf+buflen, sizeof(buf)-buflen)) > 0) {
  255. buflen+=cur_len;
  256. if (buflen >= (int)sizeof(buf)) {
  257. /* We'll false positive if the file is exactly sizeof(buf) bytes,
  258. as we don't know if there's any more stuff in the file */
  259. fprintf(stderr, "vm conf too large\n");
  260. exit(1);
  261. }
  262. }
  263. if (cur_len < 0) {
  264. perror("read vm conf");
  265. exit(1);
  266. }
  267. while ((pattern = index(bufpos, '%'))) {
  268. fwrite(bufpos, 1, pattern-bufpos, conf);
  269. if (ferror(conf)) {
  270. perror("write vm conf");
  271. exit(1);
  272. }
  273. patternend = index(pattern+1, '%');
  274. if (!patternend) {
  275. fprintf(stderr, "Unmatched '%%' in VM config\n");
  276. exit(1);
  277. }
  278. *patternend = '\0';
  279. fill_field(conf, pattern+1, dispid, netvm_id);
  280. bufpos = patternend+1;
  281. }
  282. while ((cur_len = fwrite(bufpos, 1, buflen-(bufpos-buf), conf)) > 0) {
  283. bufpos+=cur_len;
  284. }
  285. if (ferror(conf)) {
  286. perror("write vm conf");
  287. exit(1);
  288. }
  289. }
  290. void unpack_cows(const char *name)
  291. {
  292. char vmdir[4096];
  293. char tarfile[4096];
  294. int status;
  295. snprintf(vmdir, sizeof(vmdir), "/var/lib/qubes/appvms/%s", name);
  296. snprintf(tarfile, sizeof(tarfile),
  297. "/var/lib/qubes/appvms/%s/saved-cows.tar", name);
  298. switch (fork()) {
  299. case -1:
  300. perror("fork");
  301. exit(1);
  302. case 0:
  303. execl("/bin/tar", "tar", "-C", vmdir, "-Sxf",
  304. tarfile, NULL);
  305. perror("execl");
  306. exit(1);
  307. default:
  308. wait(&status);
  309. if (WEXITSTATUS(status)) {
  310. fprintf(stderr, "tar exited with status=0x%x\n",
  311. status);
  312. exit(1);
  313. }
  314. fprintf(stderr, "time=%s, cows restored\n", gettime());
  315. }
  316. }
  317. void write_xs_single(struct xs_handle *xs, int domid, const char *name,
  318. const char *val)
  319. {
  320. char key[256];
  321. snprintf(key, sizeof(key), "/local/domain/%d/%s", domid, name);
  322. if (!xs_write(xs, XBT_NULL, key, val, strlen(val))) {
  323. fprintf(stderr, "xs_write\n");
  324. exit(1);
  325. }
  326. }
  327. void perm_xs_single(struct xs_handle *xs, int domid, const char *name,
  328. struct xs_permissions *perms, int nperms)
  329. {
  330. char key[256];
  331. snprintf(key, sizeof(key), "/local/domain/%d/%s", domid, name);
  332. if (!xs_set_permissions(xs, XBT_NULL, key, perms, nperms)) {
  333. fprintf(stderr, "xs_set_permissions\n");
  334. exit(1);
  335. }
  336. }
  337. int get_netvm_id_from_name(const char *name)
  338. {
  339. int fd, n;
  340. char netvm_id[256];
  341. char netvm_id_path[256];
  342. snprintf(netvm_id_path, sizeof(netvm_id_path),
  343. "/var/lib/qubes/appvms/%s/netvm-id.txt", name);
  344. fd = open(netvm_id_path, O_RDONLY);
  345. if (fd < 0) {
  346. perror("open netvm_id");
  347. exit(1);
  348. }
  349. n = read(fd, netvm_id, sizeof(netvm_id) - 1);
  350. if (n < 0) {
  351. perror("read netvm_id");
  352. exit(1);
  353. }
  354. close(fd);
  355. netvm_id[n] = 0;
  356. n = atoi(netvm_id);
  357. return n;
  358. }
  359. void setup_xenstore(int netvm_id, int domid, int dvmid)
  360. {
  361. char val[256];
  362. struct xs_handle *xs = xs_daemon_open();
  363. struct xs_permissions perm[1];
  364. if (!xs) {
  365. perror("xs_daemon_open");
  366. exit(1);
  367. }
  368. write_xs_single(xs, domid, "qubes-ip",
  369. build_dvm_ip(netvm_id, dvmid));
  370. write_xs_single(xs, domid, "qubes-netmask", "255.255.0.0");
  371. snprintf(val, sizeof(val), "10.137.%d.1", netvm_id);
  372. write_xs_single(xs, domid, "qubes-gateway", val);
  373. snprintf(val, sizeof(val), "10.137.%d.254", netvm_id);
  374. write_xs_single(xs, domid, "qubes-secondary-dns", val);
  375. write_xs_single(xs, domid, "qubes-vm-type", "DisposableVM");
  376. write_xs_single(xs, domid, "qubes-restore-complete", "True");
  377. perm[0].id = domid;
  378. perm[0].perms = XS_PERM_NONE;
  379. perm_xs_single(xs, domid, "device", perm, 1);
  380. perm_xs_single(xs, domid, "memory", perm, 1);
  381. xs_daemon_close(xs);
  382. }
  383. int get_next_disposable_id(void)
  384. {
  385. int seq = 0;
  386. int fd = open("/var/run/qubes/dispVM.seq", O_RDWR);
  387. if (fd < 0) {
  388. perror("open dispVM.seq");
  389. exit(1);
  390. }
  391. if (flock(fd, LOCK_EX) < 0) {
  392. perror("lock dispVM.seq");
  393. exit(1);
  394. }
  395. if (read(fd, &seq, sizeof(seq)) != sizeof(seq)) {
  396. perror("read dispVM.seq");
  397. exit(1);
  398. }
  399. seq++;
  400. if (seq < 0) {
  401. fprintf(stderr, "Invalid data in dispVM.seq");
  402. exit(1);
  403. }
  404. if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
  405. perror("seek dispVM.seq");
  406. exit(1);
  407. }
  408. if (write(fd, &seq, sizeof(seq)) != sizeof(seq)) {
  409. perror("write dispVM.seq");
  410. exit(1);
  411. }
  412. flock(fd, LOCK_UN);
  413. close(fd);
  414. return seq;
  415. }
  416. void write_varrun_domid(int domid, const char *dispname, const char *orig)
  417. {
  418. FILE *f = fopen("/var/run/qubes/dispVM.xid", "w");
  419. if (!f) {
  420. perror("fopen dispVM.xid");
  421. exit(1);
  422. }
  423. fprintf(f, "%d\n%s\n%s\n", domid, dispname, orig);
  424. fclose(f);
  425. }
  426. void redirect_stderr(void)
  427. {
  428. int fd = open("/var/log/qubes/qubes-restore.log",
  429. O_CREAT | O_TRUNC | O_WRONLY, 0600);
  430. if (fd < 0) {
  431. syslog(LOG_DAEMON | LOG_ERR, "open qubes-restore.log");
  432. exit(1);
  433. }
  434. dup2(fd, 2);
  435. if (fd != 2)
  436. close(fd);
  437. }
  438. int main(int argc, char **argv)
  439. {
  440. int conf_templ, domid, dispid, netvm_id;
  441. FILE *conf;
  442. const char *name;
  443. char confname[256];
  444. char *default_user = NULL;
  445. int guid_args_start = 3;
  446. if (argc < 3) {
  447. fprintf(stderr,
  448. "usage: %s savefile conf_templ [-u default_user] [guid args] \n", argv[0]);
  449. exit(1);
  450. }
  451. redirect_stderr();
  452. fprintf(stderr, "time=%s, starting\n", gettime());
  453. set_fast_flag();
  454. atexit(rm_fast_flag);
  455. conf_templ = open(argv[2], O_RDONLY);
  456. if (conf_templ < 0) {
  457. perror("fopen vm conf");
  458. exit(1);
  459. }
  460. if (argc > 4 && strcmp(argv[3], "-u")==0) {
  461. default_user = argv[4];
  462. guid_args_start += 2;
  463. }
  464. dispid = get_next_disposable_id();
  465. name = get_vmname_from_savefile(conf_templ);
  466. netvm_id = get_netvm_id_from_name(name);
  467. snprintf(confname, sizeof(confname), "/tmp/qubes-dvm-%d.xl", dispid);
  468. conf = fopen(confname, "w");
  469. if (!conf) {
  470. perror("fopen new vm conf");
  471. exit(1);
  472. }
  473. fix_conffile(conf, conf_templ, dispid, netvm_id);
  474. close(conf_templ);
  475. fclose(conf);
  476. // printf("name=%s\n", name);
  477. unpack_cows(name);
  478. // no preloading for now, assume savefile in shm
  479. // preload_cache(fd);
  480. domid=restore_domain(argv[1], confname, dispname_by_dispid(dispid));
  481. write_varrun_domid(domid, dispname_by_dispid(dispid), name);
  482. fprintf(stderr,
  483. "time=%s, created domid=%d, creating xenstore entries\n",
  484. gettime(), domid);
  485. setup_xenstore(netvm_id, domid, dispid);
  486. rm_fast_flag();
  487. fprintf(stderr, "time=%s, starting qrexec\n", gettime());
  488. start_rexec(domid, dispname_by_dispid(dispid), default_user);
  489. fprintf(stderr, "time=%s, starting qubes-guid\n", gettime());
  490. start_guid(domid, argc-guid_args_start, argv+guid_args_start);
  491. fprintf(stderr, "time=%s, started qubes-guid\n", gettime());
  492. return 0;
  493. }