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