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