1 /*
2 * Copyright 2009-2017 Citrix Ltd and other contributors
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published
6 * by the Free Software Foundation; version 2.1 only. with the special
7 * exception on linking described in file LICENSE.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 */
14
15 #include <fcntl.h>
16 #include <inttypes.h>
17 #include <limits.h>
18 #include <stdlib.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <sys/utsname.h>
22 #include <time.h>
23 #include <unistd.h>
24
25 #include <libxl.h>
26 #include <libxl_utils.h>
27 #include <libxlutil.h>
28
29 #include "xl.h"
30 #include "xl_utils.h"
31 #include "xl_parse.h"
32
33 static int fd_lock = -1;
34
pause_domain(uint32_t domid)35 static void pause_domain(uint32_t domid)
36 {
37 libxl_domain_pause(ctx, domid);
38 }
39
unpause_domain(uint32_t domid)40 static void unpause_domain(uint32_t domid)
41 {
42 libxl_domain_unpause(ctx, domid);
43 }
44
destroy_domain(uint32_t domid,int force)45 static void destroy_domain(uint32_t domid, int force)
46 {
47 int rc;
48
49 if (domid == 0 && !force) {
50 fprintf(stderr, "Not destroying domain 0; use -f to force.\n"
51 "This can only be done when using a disaggregated "
52 "hardware domain and toolstack.\n\n");
53 exit(EXIT_FAILURE);
54 }
55 rc = libxl_domain_destroy(ctx, domid, 0);
56 if (rc) { fprintf(stderr,"destroy failed (rc=%d)\n",rc); exit(EXIT_FAILURE); }
57 }
58
main_pause(int argc,char ** argv)59 int main_pause(int argc, char **argv)
60 {
61 int opt;
62
63 SWITCH_FOREACH_OPT(opt, "", NULL, "pause", 1) {
64 /* No options */
65 }
66
67 pause_domain(find_domain(argv[optind]));
68
69 return EXIT_SUCCESS;
70 }
71
main_unpause(int argc,char ** argv)72 int main_unpause(int argc, char **argv)
73 {
74 int opt;
75
76 SWITCH_FOREACH_OPT(opt, "", NULL, "unpause", 1) {
77 /* No options */
78 }
79
80 unpause_domain(find_domain(argv[optind]));
81
82 return EXIT_SUCCESS;
83 }
84
main_destroy(int argc,char ** argv)85 int main_destroy(int argc, char **argv)
86 {
87 int opt;
88 int force = 0;
89
90 SWITCH_FOREACH_OPT(opt, "f", NULL, "destroy", 1) {
91 case 'f':
92 force = 1;
93 break;
94 }
95
96 destroy_domain(find_domain(argv[optind]), force);
97 return EXIT_SUCCESS;
98 }
99
reboot_domain(uint32_t domid,libxl_evgen_domain_death ** deathw,libxl_ev_user for_user,int fallback_trigger)100 static void reboot_domain(uint32_t domid, libxl_evgen_domain_death **deathw,
101 libxl_ev_user for_user, int fallback_trigger)
102 {
103 int rc;
104
105 fprintf(stderr, "Rebooting domain %u\n", domid);
106 rc=libxl_domain_reboot(ctx, domid);
107 if (rc == ERROR_NOPARAVIRT) {
108 if (fallback_trigger) {
109 fprintf(stderr, "PV control interface not available:"
110 " sending ACPI reset button event.\n");
111 rc = libxl_send_trigger(ctx, domid, LIBXL_TRIGGER_RESET, 0);
112 } else {
113 fprintf(stderr, "PV control interface not available:"
114 " external graceful reboot not possible.\n");
115 fprintf(stderr, "Use \"-F\" to fallback to ACPI reset event.\n");
116 }
117 }
118 if (rc) {
119 fprintf(stderr,"reboot failed (rc=%d)\n",rc);exit(EXIT_FAILURE);
120 }
121
122 if (deathw) {
123 rc = libxl_evenable_domain_death(ctx, domid, for_user, deathw);
124 if (rc) {
125 fprintf(stderr,"wait for death failed (evgen, rc=%d)\n",rc);
126 exit(EXIT_FAILURE);
127 }
128 }
129 }
130
shutdown_domain(uint32_t domid,libxl_evgen_domain_death ** deathw,libxl_ev_user for_user,int fallback_trigger)131 static void shutdown_domain(uint32_t domid,
132 libxl_evgen_domain_death **deathw,
133 libxl_ev_user for_user,
134 int fallback_trigger)
135 {
136 int rc;
137
138 fprintf(stderr, "Shutting down domain %u\n", domid);
139 rc=libxl_domain_shutdown(ctx, domid);
140 if (rc == ERROR_NOPARAVIRT) {
141 if (fallback_trigger) {
142 fprintf(stderr, "PV control interface not available:"
143 " sending ACPI power button event.\n");
144 rc = libxl_send_trigger(ctx, domid, LIBXL_TRIGGER_POWER, 0);
145 } else {
146 fprintf(stderr, "PV control interface not available:"
147 " external graceful shutdown not possible.\n");
148 fprintf(stderr, "Use \"-F\" to fallback to ACPI power event.\n");
149 }
150 }
151
152 if (rc) {
153 fprintf(stderr,"shutdown failed (rc=%d)\n",rc);exit(EXIT_FAILURE);
154 }
155
156 if (deathw) {
157 rc = libxl_evenable_domain_death(ctx, domid, for_user, deathw);
158 if (rc) {
159 fprintf(stderr,"wait for death failed (evgen, rc=%d)\n",rc);
160 exit(EXIT_FAILURE);
161 }
162 }
163 }
164
wait_for_domain_deaths(libxl_evgen_domain_death ** deathws,int nr)165 static void wait_for_domain_deaths(libxl_evgen_domain_death **deathws, int nr)
166 {
167 int rc, count = 0;
168 LOG("Waiting for %d domains", nr);
169 while(1 && count < nr) {
170 libxl_event *event;
171 rc = libxl_event_wait(ctx, &event, LIBXL_EVENTMASK_ALL, 0,0);
172 if (rc) {
173 LOG("Failed to get event, quitting (rc=%d)", rc);
174 exit(EXIT_FAILURE);
175 }
176
177 switch (event->type) {
178 case LIBXL_EVENT_TYPE_DOMAIN_DEATH:
179 LOG("Domain %d has been destroyed", event->domid);
180 libxl_evdisable_domain_death(ctx, deathws[event->for_user]);
181 count++;
182 break;
183 case LIBXL_EVENT_TYPE_DOMAIN_SHUTDOWN:
184 LOG("Domain %d has been shut down, reason code %d",
185 event->domid, event->u.domain_shutdown.shutdown_reason);
186 libxl_evdisable_domain_death(ctx, deathws[event->for_user]);
187 count++;
188 break;
189 default:
190 LOG("Unexpected event type %d", event->type);
191 break;
192 }
193 libxl_event_free(ctx, event);
194 }
195 }
196
main_shutdown_or_reboot(int do_reboot,int argc,char ** argv)197 static int main_shutdown_or_reboot(int do_reboot, int argc, char **argv)
198 {
199 const char *what = do_reboot ? "reboot" : "shutdown";
200 void (*fn)(uint32_t domid,
201 libxl_evgen_domain_death **, libxl_ev_user, int) =
202 do_reboot ? &reboot_domain : &shutdown_domain;
203 int opt, i, nb_domain;
204 int wait_for_it = 0, all = 0, nrdeathws = 0;
205 int fallback_trigger = 0;
206 static struct option opts[] = {
207 {"all", 0, 0, 'a'},
208 {"wait", 0, 0, 'w'},
209 COMMON_LONG_OPTS
210 };
211
212 SWITCH_FOREACH_OPT(opt, "awF", opts, what, 0) {
213 case 'a':
214 all = 1;
215 break;
216 case 'w':
217 wait_for_it = 1;
218 break;
219 case 'F':
220 fallback_trigger = 1;
221 break;
222 }
223
224 if (!argv[optind] && !all) {
225 fprintf(stderr, "You must specify -a or a domain id.\n\n");
226 return EXIT_FAILURE;
227 }
228
229 if (all) {
230 libxl_dominfo *dominfo;
231 libxl_evgen_domain_death **deathws = NULL;
232 if (!(dominfo = libxl_list_domain(ctx, &nb_domain))) {
233 fprintf(stderr, "libxl_list_domain failed.\n");
234 return EXIT_FAILURE;
235 }
236
237 if (wait_for_it)
238 deathws = calloc(nb_domain, sizeof(*deathws));
239
240 for (i = 0; i<nb_domain; i++) {
241 if (dominfo[i].domid == 0 || dominfo[i].never_stop)
242 continue;
243 fn(dominfo[i].domid, deathws ? &deathws[i] : NULL, i,
244 fallback_trigger);
245 nrdeathws++;
246 }
247
248 if (deathws) {
249 wait_for_domain_deaths(deathws, nrdeathws);
250 free(deathws);
251 }
252
253 libxl_dominfo_list_free(dominfo, nb_domain);
254 } else {
255 libxl_evgen_domain_death *deathw = NULL;
256 uint32_t domid = find_domain(argv[optind]);
257
258 fn(domid, wait_for_it ? &deathw : NULL, 0, fallback_trigger);
259
260 if (wait_for_it)
261 wait_for_domain_deaths(&deathw, 1);
262 }
263
264
265 return EXIT_SUCCESS;
266 }
267
main_shutdown(int argc,char ** argv)268 int main_shutdown(int argc, char **argv)
269 {
270 return main_shutdown_or_reboot(0, argc, argv);
271 }
272
main_reboot(int argc,char ** argv)273 int main_reboot(int argc, char **argv)
274 {
275 return main_shutdown_or_reboot(1, argc, argv);
276 }
277
evdisable_disk_ejects(libxl_evgen_disk_eject ** diskws,int num_disks)278 static void evdisable_disk_ejects(libxl_evgen_disk_eject **diskws,
279 int num_disks)
280 {
281 int i;
282
283 for (i = 0; i < num_disks; i++) {
284 if (diskws[i])
285 libxl_evdisable_disk_eject(ctx, diskws[i]);
286 diskws[i] = NULL;
287 }
288 }
289
domain_wait_event(uint32_t domid,libxl_event ** event_r)290 static int domain_wait_event(uint32_t domid, libxl_event **event_r)
291 {
292 int ret;
293 for (;;) {
294 ret = libxl_event_wait(ctx, event_r, LIBXL_EVENTMASK_ALL, 0,0);
295 if (ret) {
296 LOG("Domain %u, failed to get event, quitting (rc=%d)", domid, ret);
297 return ret;
298 }
299 if ((*event_r)->domid != domid) {
300 char *evstr = libxl_event_to_json(ctx, *event_r);
301 LOG("INTERNAL PROBLEM - ignoring unexpected event for"
302 " domain %d (expected %d): event=%s",
303 (*event_r)->domid, domid, evstr);
304 free(evstr);
305 libxl_event_free(ctx, *event_r);
306 continue;
307 }
308 return ret;
309 }
310 }
311
312 /*
313 * Returns false if memory can't be freed, but also if we encounter errors.
314 * Returns true in case there is already, or we manage to free it, enough
315 * memory, but also if autoballoon is false.
316 */
freemem(uint32_t domid,libxl_domain_build_info * b_info)317 static bool freemem(uint32_t domid, libxl_domain_build_info *b_info)
318 {
319 int rc, retries = 3;
320 uint64_t need_memkb, free_memkb;
321
322 if (!autoballoon)
323 return true;
324
325 rc = libxl_domain_need_memory(ctx, b_info, &need_memkb);
326 if (rc < 0)
327 return false;
328
329 do {
330 rc = libxl_get_free_memory(ctx, &free_memkb);
331 if (rc < 0)
332 return false;
333
334 if (free_memkb >= need_memkb)
335 return true;
336
337 rc = libxl_set_memory_target(ctx, 0, free_memkb - need_memkb, 1, 0);
338 if (rc < 0)
339 return false;
340
341 /* wait until dom0 reaches its target, as long as we are making
342 * progress */
343 rc = libxl_wait_for_memory_target(ctx, 0, 10);
344 if (rc < 0)
345 return false;
346
347 retries--;
348 } while (retries > 0);
349
350 return false;
351 }
352
reload_domain_config(uint32_t domid,libxl_domain_config * d_config)353 static void reload_domain_config(uint32_t domid,
354 libxl_domain_config *d_config)
355 {
356 int rc;
357 uint8_t *t_data;
358 int ret, t_len;
359 libxl_domain_config d_config_new;
360
361 /* In case user has used "config-update" to store a new config
362 * file.
363 */
364 ret = libxl_userdata_retrieve(ctx, domid, "xl", &t_data, &t_len);
365 if (ret && errno != ENOENT) {
366 LOG("\"xl\" configuration found but failed to load\n");
367 }
368 if (t_len > 0) {
369 LOG("\"xl\" configuration found, using it\n");
370 libxl_domain_config_dispose(d_config);
371 libxl_domain_config_init(d_config);
372 parse_config_data("<updated>", (const char *)t_data,
373 t_len, d_config);
374 free(t_data);
375 libxl_userdata_unlink(ctx, domid, "xl");
376 return;
377 }
378
379 libxl_domain_config_init(&d_config_new);
380 rc = libxl_retrieve_domain_configuration(ctx, domid, &d_config_new);
381 if (rc) {
382 LOG("failed to retrieve guest configuration (rc=%d). "
383 "reusing old configuration", rc);
384 libxl_domain_config_dispose(&d_config_new);
385 } else {
386 libxl_domain_config_dispose(d_config);
387 /* Steal allocations */
388 memcpy(d_config, &d_config_new, sizeof(libxl_domain_config));
389 }
390 }
391
392 /* Can update r_domid if domain is destroyed */
handle_domain_death(uint32_t * r_domid,libxl_event * event,libxl_domain_config * d_config)393 static domain_restart_type handle_domain_death(uint32_t *r_domid,
394 libxl_event *event,
395 libxl_domain_config *d_config)
396 {
397 domain_restart_type restart = DOMAIN_RESTART_NONE;
398 libxl_action_on_shutdown action;
399
400 switch (event->u.domain_shutdown.shutdown_reason) {
401 case LIBXL_SHUTDOWN_REASON_POWEROFF:
402 action = d_config->on_poweroff;
403 break;
404 case LIBXL_SHUTDOWN_REASON_REBOOT:
405 action = d_config->on_reboot;
406 break;
407 case LIBXL_SHUTDOWN_REASON_SUSPEND:
408 LOG("Domain has suspended.");
409 return 0;
410 case LIBXL_SHUTDOWN_REASON_CRASH:
411 action = d_config->on_crash;
412 break;
413 case LIBXL_SHUTDOWN_REASON_WATCHDOG:
414 action = d_config->on_watchdog;
415 break;
416 case LIBXL_SHUTDOWN_REASON_SOFT_RESET:
417 action = d_config->on_soft_reset;
418 break;
419 default:
420 LOG("Unknown shutdown reason code %d. Destroying domain.",
421 event->u.domain_shutdown.shutdown_reason);
422 action = LIBXL_ACTION_ON_SHUTDOWN_DESTROY;
423 }
424
425 LOG("Action for shutdown reason code %d is %s",
426 event->u.domain_shutdown.shutdown_reason,
427 get_action_on_shutdown_name(action));
428
429 if (action == LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_DESTROY || action == LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_RESTART) {
430 char *corefile;
431 int rc;
432
433 xasprintf(&corefile, XEN_DUMP_DIR "/%s", d_config->c_info.name);
434 LOG("dumping core to %s", corefile);
435 rc = libxl_domain_core_dump(ctx, *r_domid, corefile, NULL);
436 if (rc) LOG("core dump failed (rc=%d).", rc);
437 free(corefile);
438 /* No point crying over spilled milk, continue on failure. */
439
440 if (action == LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_DESTROY)
441 action = LIBXL_ACTION_ON_SHUTDOWN_DESTROY;
442 else
443 action = LIBXL_ACTION_ON_SHUTDOWN_RESTART;
444 }
445
446 switch (action) {
447 case LIBXL_ACTION_ON_SHUTDOWN_PRESERVE:
448 break;
449
450 case LIBXL_ACTION_ON_SHUTDOWN_RESTART_RENAME:
451 reload_domain_config(*r_domid, d_config);
452 restart = DOMAIN_RESTART_RENAME;
453 break;
454
455 case LIBXL_ACTION_ON_SHUTDOWN_RESTART:
456 reload_domain_config(*r_domid, d_config);
457 restart = DOMAIN_RESTART_NORMAL;
458 /* fall-through */
459 case LIBXL_ACTION_ON_SHUTDOWN_DESTROY:
460 LOG("Domain %d needs to be cleaned up: destroying the domain",
461 *r_domid);
462 libxl_domain_destroy(ctx, *r_domid, 0);
463 *r_domid = INVALID_DOMID;
464 break;
465
466 case LIBXL_ACTION_ON_SHUTDOWN_SOFT_RESET:
467 reload_domain_config(*r_domid, d_config);
468 restart = DOMAIN_RESTART_SOFT_RESET;
469 break;
470
471 case LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_DESTROY:
472 case LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_RESTART:
473 /* Already handled these above. */
474 abort();
475 }
476
477 return restart;
478 }
479
480 /* Preserve a copy of a domain under a new name. Updates *r_domid */
preserve_domain(uint32_t * r_domid,libxl_event * event,libxl_domain_config * d_config)481 static int preserve_domain(uint32_t *r_domid, libxl_event *event,
482 libxl_domain_config *d_config)
483 {
484 time_t now;
485 struct tm tm;
486 char strtime[24];
487
488 libxl_uuid new_uuid;
489
490 int rc;
491
492 now = time(NULL);
493 if (now == ((time_t) -1)) {
494 LOG("Failed to get current time for domain rename");
495 return 0;
496 }
497
498 tzset();
499 if (gmtime_r(&now, &tm) == NULL) {
500 LOG("Failed to convert time to UTC");
501 return 0;
502 }
503
504 if (!strftime(&strtime[0], sizeof(strtime), "-%Y%m%dT%H%MZ", &tm)) {
505 LOG("Failed to format time as a string");
506 return 0;
507 }
508
509 libxl_uuid_generate(&new_uuid);
510
511 LOG("Preserving domain %u %s with suffix%s",
512 *r_domid, d_config->c_info.name, strtime);
513 rc = libxl_domain_preserve(ctx, *r_domid, &d_config->c_info,
514 strtime, new_uuid);
515
516 /*
517 * Although the domain still exists it is no longer the one we are
518 * concerned with.
519 */
520 *r_domid = INVALID_DOMID;
521
522 return rc == 0 ? 1 : 0;
523 }
524
console_child_report(xlchildnum child)525 static void console_child_report(xlchildnum child)
526 {
527 if (xl_child_pid(child))
528 child_report(child);
529 }
530
vncviewer(uint32_t domid,int autopass)531 static int vncviewer(uint32_t domid, int autopass)
532 {
533 libxl_vncviewer_exec(ctx, domid, autopass);
534 fprintf(stderr, "Unable to execute vncviewer\n");
535 return 1;
536 }
537
autoconnect_vncviewer(uint32_t domid,int autopass)538 static void autoconnect_vncviewer(uint32_t domid, int autopass)
539 {
540 console_child_report(child_vncviewer);
541
542 pid_t pid = xl_fork(child_vncviewer, "vncviewer child");
543 if (pid)
544 return;
545
546 postfork();
547
548 sleep(1);
549 vncviewer(domid, autopass);
550 _exit(EXIT_FAILURE);
551 }
552
acquire_lock(void)553 static int acquire_lock(void)
554 {
555 int rc;
556 struct flock fl;
557
558 /* lock already acquired */
559 if (fd_lock >= 0)
560 return ERROR_INVAL;
561
562 fl.l_type = F_WRLCK;
563 fl.l_whence = SEEK_SET;
564 fl.l_start = 0;
565 fl.l_len = 0;
566 fd_lock = open(lockfile, O_WRONLY|O_CREAT, S_IWUSR);
567 if (fd_lock < 0) {
568 fprintf(stderr, "cannot open the lockfile %s errno=%d\n", lockfile, errno);
569 return ERROR_FAIL;
570 }
571 if (fcntl(fd_lock, F_SETFD, FD_CLOEXEC) < 0) {
572 close(fd_lock);
573 fprintf(stderr, "cannot set cloexec to lockfile %s errno=%d\n", lockfile, errno);
574 return ERROR_FAIL;
575 }
576 get_lock:
577 rc = fcntl(fd_lock, F_SETLKW, &fl);
578 if (rc < 0 && errno == EINTR)
579 goto get_lock;
580 if (rc < 0) {
581 fprintf(stderr, "cannot acquire lock %s errno=%d\n", lockfile, errno);
582 rc = ERROR_FAIL;
583 } else
584 rc = 0;
585 return rc;
586 }
587
release_lock(void)588 static int release_lock(void)
589 {
590 int rc;
591 struct flock fl;
592
593 /* lock not acquired */
594 if (fd_lock < 0)
595 return ERROR_INVAL;
596
597 release_lock:
598 fl.l_type = F_UNLCK;
599 fl.l_whence = SEEK_SET;
600 fl.l_start = 0;
601 fl.l_len = 0;
602
603 rc = fcntl(fd_lock, F_SETLKW, &fl);
604 if (rc < 0 && errno == EINTR)
605 goto release_lock;
606 if (rc < 0) {
607 fprintf(stderr, "cannot release lock %s, errno=%d\n", lockfile, errno);
608 rc = ERROR_FAIL;
609 } else
610 rc = 0;
611 close(fd_lock);
612 fd_lock = -1;
613
614 return rc;
615 }
616
617
autoconnect_console(libxl_ctx * ctx_ignored,libxl_event * ev,void * priv)618 static void autoconnect_console(libxl_ctx *ctx_ignored,
619 libxl_event *ev, void *priv)
620 {
621 uint32_t bldomid = ev->domid;
622 int notify_fd = *(int*)priv; /* write end of the notification pipe */
623
624 libxl_event_free(ctx, ev);
625
626 console_child_report(child_console);
627
628 pid_t pid = xl_fork(child_console, "console child");
629 if (pid)
630 return;
631
632 postfork();
633
634 sleep(1);
635 libxl_primary_console_exec(ctx, bldomid, notify_fd);
636 /* Do not return. xl continued in child process */
637 perror("xl: unable to exec console client");
638 _exit(1);
639 }
640
create_domain(struct domain_create * dom_info)641 int create_domain(struct domain_create *dom_info)
642 {
643 uint32_t domid = INVALID_DOMID;
644
645 libxl_domain_config d_config;
646
647 int debug = dom_info->debug;
648 int daemonize = dom_info->daemonize;
649 int monitor = dom_info->monitor;
650 int paused = dom_info->paused;
651 int vncautopass = dom_info->vncautopass;
652 const char *config_file = dom_info->config_file;
653 const char *extra_config = dom_info->extra_config;
654 const char *restore_file = dom_info->restore_file;
655 const char *config_source = NULL;
656 const char *restore_source = NULL;
657 int migrate_fd = dom_info->migrate_fd;
658 bool config_in_json;
659
660 int i;
661 int need_daemon = daemonize;
662 int ret, rc;
663 libxl_evgen_domain_death *deathw = NULL;
664 libxl_evgen_disk_eject **diskws = NULL; /* one per disk */
665 unsigned int num_diskws = 0;
666 void *config_data = 0;
667 int config_len = 0;
668 int restore_fd = -1;
669 int restore_fd_to_close = -1;
670 int send_back_fd = -1;
671 const libxl_asyncprogress_how *autoconnect_console_how;
672 int notify_pipe[2] = { -1, -1 };
673 struct save_file_header hdr;
674 uint32_t domid_soft_reset = INVALID_DOMID;
675
676 int restoring = (restore_file || (migrate_fd >= 0));
677
678 libxl_domain_config_init(&d_config);
679
680 if (restoring) {
681 uint8_t *optdata_begin = 0;
682 const uint8_t *optdata_here = 0;
683 union { uint32_t u32; char b[4]; } u32buf;
684 uint32_t badflags;
685
686 if (migrate_fd >= 0) {
687 restore_source = "<incoming migration stream>";
688 restore_fd = migrate_fd;
689 send_back_fd = dom_info->send_back_fd;
690 } else {
691 restore_source = restore_file;
692 restore_fd = open(restore_file, O_RDONLY);
693 if (restore_fd == -1) {
694 fprintf(stderr, "Can't open restore file: %s\n", strerror(errno));
695 return ERROR_INVAL;
696 }
697 restore_fd_to_close = restore_fd;
698 rc = libxl_fd_set_cloexec(ctx, restore_fd, 1);
699 if (rc) return rc;
700 }
701
702 CHK_ERRNOVAL(libxl_read_exactly(
703 ctx, restore_fd, &hdr, sizeof(hdr),
704 restore_source, "header"));
705 if (memcmp(hdr.magic, savefileheader_magic, sizeof(hdr.magic))) {
706 fprintf(stderr, "File has wrong magic number -"
707 " corrupt or for a different tool?\n");
708 return ERROR_INVAL;
709 }
710 if (hdr.byteorder != SAVEFILE_BYTEORDER_VALUE) {
711 fprintf(stderr, "File has wrong byte order\n");
712 return ERROR_INVAL;
713 }
714 fprintf(stderr, "Loading new save file %s"
715 " (new xl fmt info"
716 " 0x%"PRIx32"/0x%"PRIx32"/%"PRIu32")\n",
717 restore_source, hdr.mandatory_flags, hdr.optional_flags,
718 hdr.optional_data_len);
719
720 badflags = hdr.mandatory_flags & ~XL_MANDATORY_FLAG_ALL;
721 if (badflags) {
722 fprintf(stderr, "Savefile has mandatory flag(s) 0x%"PRIx32" "
723 "which are not supported; need newer xl\n",
724 badflags);
725 return ERROR_INVAL;
726 }
727 if (hdr.optional_data_len) {
728 optdata_begin = xmalloc(hdr.optional_data_len);
729 CHK_ERRNOVAL(libxl_read_exactly(
730 ctx, restore_fd, optdata_begin,
731 hdr.optional_data_len, restore_source,
732 "optdata"));
733 }
734
735 #define OPTDATA_LEFT (hdr.optional_data_len - (optdata_here - optdata_begin))
736 #define WITH_OPTDATA(amt, body) \
737 if (OPTDATA_LEFT < (amt)) { \
738 fprintf(stderr, "Savefile truncated.\n"); \
739 return ERROR_INVAL; \
740 } else { \
741 body; \
742 optdata_here += (amt); \
743 }
744
745 optdata_here = optdata_begin;
746
747 if (OPTDATA_LEFT) {
748 fprintf(stderr, " Savefile contains xl domain config%s\n",
749 !!(hdr.mandatory_flags & XL_MANDATORY_FLAG_JSON)
750 ? " in JSON format" : "");
751 WITH_OPTDATA(4, {
752 memcpy(u32buf.b, optdata_here, 4);
753 config_len = u32buf.u32;
754 });
755 WITH_OPTDATA(config_len, {
756 config_data = xmalloc(config_len);
757 memcpy(config_data, optdata_here, config_len);
758 });
759 }
760
761 }
762
763 if (config_file) {
764 free(config_data); config_data = 0;
765 /* /dev/null represents special case (read config. from command line) */
766 if (!strcmp(config_file, "/dev/null")) {
767 config_len = 0;
768 } else {
769 ret = libxl_read_file_contents(ctx, config_file,
770 &config_data, &config_len);
771 if (ret) { fprintf(stderr, "Failed to read config file: %s: %s\n",
772 config_file, strerror(errno)); return ERROR_FAIL; }
773 }
774 if (!restoring && extra_config && strlen(extra_config)) {
775 if (config_len > INT_MAX - (strlen(extra_config) + 2 + 1)) {
776 fprintf(stderr, "Failed to attach extra configuration\n");
777 return ERROR_FAIL;
778 }
779 /* allocate space for the extra config plus two EOLs plus \0 */
780 config_data = xrealloc(config_data, config_len
781 + strlen(extra_config) + 2 + 1);
782 config_len += sprintf(config_data + config_len, "\n%s\n",
783 extra_config);
784 }
785 config_source=config_file;
786 config_in_json = false;
787 } else {
788 if (!config_data) {
789 fprintf(stderr, "Config file not specified and"
790 " none in save file\n");
791 return ERROR_INVAL;
792 }
793 config_source = "<saved>";
794 config_in_json = !!(hdr.mandatory_flags & XL_MANDATORY_FLAG_JSON);
795 }
796
797 if (!dom_info->quiet)
798 fprintf(stderr, "Parsing config from %s\n", config_source);
799
800 if (config_in_json) {
801 libxl_domain_config_from_json(ctx, &d_config,
802 (const char *)config_data);
803 } else {
804 parse_config_data(config_source, config_data, config_len, &d_config);
805 }
806
807 if (migrate_fd >= 0) {
808 if (d_config.c_info.name) {
809 /* when we receive a domain we get its name from the config
810 * file; and we receive it to a temporary name */
811 assert(!common_domname);
812
813 common_domname = d_config.c_info.name;
814 d_config.c_info.name = 0; /* steals allocation from config */
815
816 xasprintf(&d_config.c_info.name, "%s--incoming", common_domname);
817 *dom_info->migration_domname_r = strdup(d_config.c_info.name);
818 }
819 }
820
821 if (debug || dom_info->dryrun) {
822 FILE *cfg_print_fh = (debug && !dom_info->dryrun) ? stderr : stdout;
823 if (default_output_format == OUTPUT_FORMAT_SXP) {
824 printf_info_sexp(-1, &d_config, cfg_print_fh);
825 } else {
826 char *json = libxl_domain_config_to_json(ctx, &d_config);
827 if (!json) {
828 fprintf(stderr,
829 "Failed to convert domain configuration to JSON\n");
830 exit(1);
831 }
832 fputs(json, cfg_print_fh);
833 free(json);
834 flush_stream(cfg_print_fh);
835 }
836 }
837
838
839 ret = 0;
840 if (dom_info->dryrun)
841 goto out;
842
843 start:
844 assert(domid == INVALID_DOMID);
845
846 rc = acquire_lock();
847 if (rc < 0)
848 goto error_out;
849
850 if (domid_soft_reset == INVALID_DOMID) {
851 if (!freemem(domid, &d_config.b_info)) {
852 fprintf(stderr, "failed to free memory for the domain\n");
853 ret = ERROR_FAIL;
854 goto error_out;
855 }
856 }
857
858 libxl_asyncprogress_how autoconnect_console_how_buf;
859 if ( dom_info->console_autoconnect ) {
860 if (libxl_pipe(ctx, notify_pipe)) {
861 ret = ERROR_FAIL;
862 goto error_out;
863 }
864 autoconnect_console_how_buf.callback = autoconnect_console;
865 autoconnect_console_how_buf.for_callback = ¬ify_pipe[1];
866 autoconnect_console_how = &autoconnect_console_how_buf;
867 }else{
868 autoconnect_console_how = 0;
869 }
870
871 if ( restoring ) {
872 libxl_domain_restore_params params;
873
874 libxl_domain_restore_params_init(¶ms);
875
876 params.checkpointed_stream = dom_info->checkpointed_stream;
877 params.stream_version =
878 (hdr.mandatory_flags & XL_MANDATORY_FLAG_STREAMv2) ? 2 : 1;
879 params.colo_proxy_script = dom_info->colo_proxy_script;
880 libxl_defbool_set(¶ms.userspace_colo_proxy,
881 dom_info->userspace_colo_proxy);
882
883 ret = libxl_domain_create_restore(ctx, &d_config,
884 &domid, restore_fd,
885 send_back_fd, ¶ms,
886 0, autoconnect_console_how);
887
888 libxl_domain_restore_params_dispose(¶ms);
889
890 /*
891 * On subsequent reboot etc we should create the domain, not
892 * restore/migrate-receive it again.
893 */
894 restoring = 0;
895 } else if (domid_soft_reset != INVALID_DOMID) {
896 /* Do soft reset. */
897 ret = libxl_domain_soft_reset(ctx, &d_config, domid_soft_reset,
898 0, autoconnect_console_how);
899 domid = domid_soft_reset;
900 domid_soft_reset = INVALID_DOMID;
901 } else {
902 ret = libxl_domain_create_new(ctx, &d_config, &domid,
903 0, autoconnect_console_how);
904 }
905 if ( ret )
906 goto error_out;
907
908 release_lock();
909
910 if (restore_fd_to_close >= 0) {
911 if (close(restore_fd_to_close))
912 fprintf(stderr, "Failed to close restoring file, fd %d, errno %d\n",
913 restore_fd_to_close, errno);
914 restore_fd_to_close = -1;
915 }
916
917 if (autoconnect_console_how) {
918 char buf[1];
919 int r;
920
921 /* Try to get notification from xenconsole. Just move on if
922 * error occurs -- it's only minor annoyance if console
923 * doesn't show up.
924 */
925 do {
926 r = read(notify_pipe[0], buf, 1);
927 } while (r == -1 && errno == EINTR);
928
929 if (r == -1)
930 fprintf(stderr,
931 "Failed to get notification from xenconsole: %s\n",
932 strerror(errno));
933 else if (r == 0)
934 fprintf(stderr, "Got EOF from xenconsole notification fd\n");
935 else if (r == 1 && buf[0] != 0x00)
936 fprintf(stderr, "Got unexpected response from xenconsole: %#x\n",
937 buf[0]);
938
939 close(notify_pipe[0]);
940 close(notify_pipe[1]);
941 notify_pipe[0] = notify_pipe[1] = -1;
942 }
943
944 if (!paused)
945 libxl_domain_unpause(ctx, domid);
946
947 ret = domid; /* caller gets success in parent */
948 if (!daemonize && !monitor)
949 goto out;
950
951 if (dom_info->vnc)
952 autoconnect_vncviewer(domid, vncautopass);
953
954 if (need_daemon) {
955 char *name;
956
957 xasprintf(&name, "xl-%s", d_config.c_info.name);
958 ret = do_daemonize(name, NULL);
959 free(name);
960 if (ret) {
961 ret = (ret == 1) ? domid : ret;
962 goto out;
963 }
964 need_daemon = 0;
965 }
966 LOG("Waiting for domain %s (domid %u) to die [pid %ld]",
967 d_config.c_info.name, domid, (long)getpid());
968
969 ret = libxl_evenable_domain_death(ctx, domid, 0, &deathw);
970 if (ret) goto out;
971
972 if (!diskws) {
973 diskws = xmalloc(sizeof(*diskws) * d_config.num_disks);
974 for (i = 0; i < d_config.num_disks; i++)
975 diskws[i] = NULL;
976 num_diskws = d_config.num_disks;
977 }
978 for (i = 0; i < num_diskws; i++) {
979 if (d_config.disks[i].removable) {
980 ret = libxl_evenable_disk_eject(ctx, domid, d_config.disks[i].vdev,
981 0, &diskws[i]);
982 if (ret) goto out;
983 }
984 }
985 while (1) {
986 libxl_event *event;
987 ret = domain_wait_event(domid, &event);
988 if (ret) goto out;
989
990 switch (event->type) {
991
992 case LIBXL_EVENT_TYPE_DOMAIN_SHUTDOWN:
993 LOG("Domain %u has shut down, reason code %d 0x%x", domid,
994 event->u.domain_shutdown.shutdown_reason,
995 event->u.domain_shutdown.shutdown_reason);
996 switch (handle_domain_death(&domid, event, &d_config)) {
997 case DOMAIN_RESTART_SOFT_RESET:
998 domid_soft_reset = domid;
999 domid = INVALID_DOMID;
1000 /* fall through */
1001 case DOMAIN_RESTART_RENAME:
1002 if (domid_soft_reset == INVALID_DOMID &&
1003 !preserve_domain(&domid, event, &d_config)) {
1004 libxl_event_free(ctx, event);
1005 /* If we fail then exit leaving the old domain in place. */
1006 ret = -1;
1007 goto out;
1008 }
1009
1010 /* Otherwise fall through and restart. */
1011 case DOMAIN_RESTART_NORMAL:
1012 libxl_event_free(ctx, event);
1013 libxl_evdisable_domain_death(ctx, deathw);
1014 deathw = NULL;
1015 evdisable_disk_ejects(diskws, num_diskws);
1016 free(diskws);
1017 diskws = NULL;
1018 num_diskws = 0;
1019 /* discard any other events which may have been generated */
1020 while (!(ret = libxl_event_check(ctx, &event,
1021 LIBXL_EVENTMASK_ALL, 0,0))) {
1022 libxl_event_free(ctx, event);
1023 }
1024 if (ret != ERROR_NOT_READY) {
1025 LOG("warning, libxl_event_check (cleanup) failed (rc=%d)",
1026 ret);
1027 }
1028
1029 /*
1030 * Do not attempt to reconnect if we come round again due to a
1031 * guest reboot -- the stdin/out will be disconnected by then.
1032 */
1033 dom_info->console_autoconnect = 0;
1034
1035 /* Some settings only make sense on first boot. */
1036 paused = 0;
1037 if (common_domname
1038 && strcmp(d_config.c_info.name, common_domname)) {
1039 d_config.c_info.name = strdup(common_domname);
1040 }
1041
1042 /*
1043 * XXX FIXME: If this sleep is not there then domain
1044 * re-creation fails sometimes.
1045 */
1046 LOG("Done. Rebooting now");
1047 sleep(2);
1048 goto start;
1049
1050 case DOMAIN_RESTART_NONE:
1051 LOG("Done. Exiting now");
1052 libxl_event_free(ctx, event);
1053 ret = 0;
1054 goto out;
1055
1056 default:
1057 abort();
1058 }
1059
1060 case LIBXL_EVENT_TYPE_DOMAIN_DEATH:
1061 LOG("Domain %u has been destroyed.", domid);
1062 libxl_event_free(ctx, event);
1063 ret = 0;
1064 goto out;
1065
1066 case LIBXL_EVENT_TYPE_DISK_EJECT:
1067 /* XXX what is this for? */
1068 libxl_cdrom_insert(ctx, domid, &event->u.disk_eject.disk, NULL);
1069 break;
1070
1071 default:;
1072 char *evstr = libxl_event_to_json(ctx, event);
1073 LOG("warning, got unexpected event type %d, event=%s",
1074 event->type, evstr);
1075 free(evstr);
1076 }
1077
1078 libxl_event_free(ctx, event);
1079 }
1080
1081 error_out:
1082 release_lock();
1083 if (libxl_domid_valid_guest(domid)) {
1084 libxl_domain_destroy(ctx, domid, 0);
1085 domid = INVALID_DOMID;
1086 }
1087
1088 out:
1089 if (restore_fd_to_close >= 0) {
1090 if (close(restore_fd_to_close))
1091 fprintf(stderr, "Failed to close restoring file, fd %d, errno %d\n",
1092 restore_fd_to_close, errno);
1093 restore_fd_to_close = -1;
1094 }
1095
1096 if (logfile != 2)
1097 close(logfile);
1098
1099 libxl_domain_config_dispose(&d_config);
1100
1101 free(config_data);
1102
1103 console_child_report(child_console);
1104
1105 if (deathw)
1106 libxl_evdisable_domain_death(ctx, deathw);
1107 if (diskws) {
1108 evdisable_disk_ejects(diskws, d_config.num_disks);
1109 free(diskws);
1110 }
1111
1112 /*
1113 * If we have daemonized then do not return to the caller -- this has
1114 * already happened in the parent.
1115 */
1116 if ( daemonize && !need_daemon )
1117 exit(ret);
1118
1119 return ret;
1120 }
1121
main_create(int argc,char ** argv)1122 int main_create(int argc, char **argv)
1123 {
1124 const char *filename = NULL;
1125 struct domain_create dom_info;
1126 int paused = 0, debug = 0, daemonize = 1, console_autoconnect = 0,
1127 quiet = 0, monitor = 1, vnc = 0, vncautopass = 0;
1128 int opt, rc;
1129 static struct option opts[] = {
1130 {"dryrun", 0, 0, 'n'},
1131 {"quiet", 0, 0, 'q'},
1132 {"defconfig", 1, 0, 'f'},
1133 {"vncviewer", 0, 0, 'V'},
1134 {"vncviewer-autopass", 0, 0, 'A'},
1135 COMMON_LONG_OPTS
1136 };
1137
1138 dom_info.extra_config = NULL;
1139
1140 if (argv[1] && argv[1][0] != '-' && !strchr(argv[1], '=')) {
1141 filename = argv[1];
1142 argc--; argv++;
1143 }
1144
1145 SWITCH_FOREACH_OPT(opt, "Fnqf:pcdeVA", opts, "create", 0) {
1146 case 'f':
1147 filename = optarg;
1148 break;
1149 case 'p':
1150 paused = 1;
1151 break;
1152 case 'c':
1153 console_autoconnect = 1;
1154 break;
1155 case 'd':
1156 debug = 1;
1157 break;
1158 case 'F':
1159 daemonize = 0;
1160 break;
1161 case 'e':
1162 daemonize = 0;
1163 monitor = 0;
1164 break;
1165 case 'n':
1166 dryrun_only = 1;
1167 break;
1168 case 'q':
1169 quiet = 1;
1170 break;
1171 case 'V':
1172 vnc = 1;
1173 break;
1174 case 'A':
1175 vnc = vncautopass = 1;
1176 break;
1177 }
1178
1179 memset(&dom_info, 0, sizeof(dom_info));
1180
1181 for (; optind < argc; optind++) {
1182 if (strchr(argv[optind], '=') != NULL) {
1183 string_realloc_append(&dom_info.extra_config, argv[optind]);
1184 string_realloc_append(&dom_info.extra_config, "\n");
1185 } else if (!filename) {
1186 filename = argv[optind];
1187 } else {
1188 help("create");
1189 free(dom_info.extra_config);
1190 return 2;
1191 }
1192 }
1193
1194 dom_info.debug = debug;
1195 dom_info.daemonize = daemonize;
1196 dom_info.monitor = monitor;
1197 dom_info.paused = paused;
1198 dom_info.dryrun = dryrun_only;
1199 dom_info.quiet = quiet;
1200 dom_info.config_file = filename;
1201 dom_info.migrate_fd = -1;
1202 dom_info.send_back_fd = -1;
1203 dom_info.vnc = vnc;
1204 dom_info.vncautopass = vncautopass;
1205 dom_info.console_autoconnect = console_autoconnect;
1206
1207 rc = create_domain(&dom_info);
1208 if (rc < 0) {
1209 free(dom_info.extra_config);
1210 return -rc;
1211 }
1212
1213 free(dom_info.extra_config);
1214 return 0;
1215 }
1216
1217 /*
1218 * Local variables:
1219 * mode: C
1220 * c-basic-offset: 4
1221 * indent-tabs-mode: nil
1222 * End:
1223 */
1224