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