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 = &notify_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(&params);
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(&params.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, &params,
932                                           0, autoconnect_console_how);
933 
934         libxl_domain_restore_params_dispose(&params);
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