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