qubes-restore.c 11 KB

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