From patchwork Fri Feb 24 16:13:11 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wei Liu X-Patchwork-Id: 9590755 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 1AF86604A2 for ; Fri, 24 Feb 2017 16:19:44 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 15774286EB for ; Fri, 24 Feb 2017 16:19:44 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 093442871D; Fri, 24 Feb 2017 16:19:44 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id D6BE128728 for ; Fri, 24 Feb 2017 16:19:40 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1chIY6-00077i-D7; Fri, 24 Feb 2017 16:16:50 +0000 Received: from mail6.bemta6.messagelabs.com ([193.109.254.103]) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1chIY4-00070J-9R for xen-devel@lists.xenproject.org; Fri, 24 Feb 2017 16:16:48 +0000 Received: from [193.109.254.147] by server-7.bemta-6.messagelabs.com id C7/B2-24539-F6C50B85; Fri, 24 Feb 2017 16:16:47 +0000 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFprBIsWRWlGSWpSXmKPExsXitHSDvW5uzIY Ig+er2S2+b5nM5MDocfjDFZYAxijWzLyk/IoE1ow5J+awF/z6z1Jx7G4XSwPj853MXYycHBIC /hIXdi9kAbHZBJQlfnb2soHYIgJ6Ek0HnjOC2MwCXhI/VvSwg9jCAs4S185+ZwWxWQRUJV4fO Q5Uw8HBK2ApceNyHMRIeYldbRfBSjiBwpsefAVrFRKwkHjxfTMjhK0g0TH9GBOIzSsgKHFy5h MWiFUSEgdfvGCewMg7C0lqFpLUAkamVYwaxalFZalFukZGeklFmekZJbmJmTm6hgZmermpxcW J6ak5iUnFesn5uZsYgeHDAAQ7GNfMDzzEKMnBpCTKGxq8IUKILyk/pTIjsTgjvqg0J7X4EKMM B4eSBK91NFBOsCg1PbUiLTMHGMgwaQkOHiUR3jlRQGne4oLE3OLMdIjUKUZjjjmzd79h4rjVs OcNkxBLXn5eqpQ4bwjIJAGQ0ozSPLhBsAi7xCgrJczLCHSaEE9BalFuZgmq/CtGcQ5GJaAKkC k8mXklcPteAZ3CBHSKpfNakFNKEhFSUg2MS9g7M9aFHe895h8bMTP62JmeRyGxv+dqH8jk3cU lGj757va/LjkbwzT7Jm1M0znqGdxelfs7PyvP1sYnYs9S/RlGW/9mmMQ5i+maZ2ybvklqTqPu z2e2evt6/RTSJO/4vrEv1rt51PCH42v7X/PMLnNpHfh/9ccJ24Vv3W+U9l9/nSHDLrJXiaU4I 9FQi7moOBEA2m3e4KsCAAA= X-Env-Sender: prvs=22177af8c=wei.liu2@citrix.com X-Msg-Ref: server-8.tower-27.messagelabs.com!1487953001!78466922!2 X-Originating-IP: [66.165.176.63] X-SpamReason: No, hits=0.0 required=7.0 tests=sa_preprocessor: VHJ1c3RlZCBJUDogNjYuMTY1LjE3Ni42MyA9PiAzMDYwNDg=\n, received_headers: No Received headers X-StarScan-Received: X-StarScan-Version: 9.2.3; banners=-,-,- X-VirusChecked: Checked Received: (qmail 36893 invoked from network); 24 Feb 2017 16:16:44 -0000 Received: from smtp02.citrix.com (HELO SMTP02.CITRIX.COM) (66.165.176.63) by server-8.tower-27.messagelabs.com with RC4-SHA encrypted SMTP; 24 Feb 2017 16:16:44 -0000 X-IronPort-AV: E=Sophos;i="5.35,201,1484006400"; d="scan'208";a="418411343" From: Wei Liu To: Xen-devel Date: Fri, 24 Feb 2017 16:13:11 +0000 Message-ID: <20170224161314.22154-27-wei.liu2@citrix.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170224161314.22154-1-wei.liu2@citrix.com> References: <20170224161314.22154-1-wei.liu2@citrix.com> MIME-Version: 1.0 Cc: Wei Liu , Ian Jackson Subject: [Xen-devel] [PATCH 26/29] xl: split out vm lifecycle control functions X-BeenThere: xen-devel@lists.xen.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xen.org Sender: "Xen-devel" X-Virus-Scanned: ClamAV using ClamSMTP Including create, reboot, shutdown, pause, unpause and destroy. Lift a bunch of core data structures and function declarations to xl.h because they are needed in both xl_cmdimpl.c and xl_vmcontrol.c. Signed-off-by: Wei Liu Acked-by: Ian Jackson --- tools/xl/Makefile | 1 + tools/xl/xl.h | 67 +++ tools/xl/xl_cmdimpl.c | 1243 ----------------------------------------------- tools/xl/xl_vmcontrol.c | 1225 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1293 insertions(+), 1243 deletions(-) create mode 100644 tools/xl/xl_vmcontrol.c diff --git a/tools/xl/Makefile b/tools/xl/Makefile index 3e8361b121..0f966684be 100644 --- a/tools/xl/Makefile +++ b/tools/xl/Makefile @@ -20,6 +20,7 @@ XL_OBJS += xl_tmem.o xl_parse.o xl_cpupool.o xl_flask.o XL_OBJS += xl_vtpm.o xl_block.o xl_nic.o xl_usb.o XL_OBJS += xl_sched.o xl_pci.o xl_vcpu.o xl_cd.o xl_mem.o XL_OBJS += xl_psr.o xl_info.o xl_console.o xl_misc.o +XL_OBJS += xl_vmcontrol.o $(XL_OBJS): CFLAGS += $(CFLAGS_libxentoollog) $(XL_OBJS): CFLAGS += $(CFLAGS_XL) diff --git a/tools/xl/xl.h b/tools/xl/xl.h index a8b6264c59..62d010076b 100644 --- a/tools/xl/xl.h +++ b/tools/xl/xl.h @@ -30,6 +30,73 @@ struct cmd_spec { char *cmd_option; }; +struct domain_create { + int debug; + int daemonize; + int monitor; /* handle guest reboots etc */ + int paused; + int dryrun; + int quiet; + int vnc; + int vncautopass; + int console_autoconnect; + int checkpointed_stream; + const char *config_file; + char *extra_config; /* extra config string */ + const char *restore_file; + char *colo_proxy_script; + int migrate_fd; /* -1 means none */ + int send_back_fd; /* -1 means none */ + char **migration_domname_r; /* from malloc */ +}; + +int create_domain(struct domain_create *dom_info); + + +static const char savefileheader_magic[32]= + "Xen saved domain, xl format\n \0 \r"; + +#ifndef LIBXL_HAVE_NO_SUSPEND_RESUME +static const char migrate_receiver_banner[]= + "xl migration receiver ready, send binary domain data.\n"; +static const char migrate_receiver_ready[]= + "domain received, ready to unpause"; +static const char migrate_permission_to_go[]= + "domain is yours, you are cleared to unpause"; +static const char migrate_report[]= + "my copy unpause results are as follows"; +#endif + + /* followed by one byte: + * 0: everything went well, domain is running + * next thing is we all exit + * non-0: things went badly + * next thing should be a migrate_permission_to_go + * from target to source + */ + +#define XL_MANDATORY_FLAG_JSON (1U << 0) /* config data is in JSON format */ +#define XL_MANDATORY_FLAG_STREAMv2 (1U << 1) /* stream is v2 */ +#define XL_MANDATORY_FLAG_ALL (XL_MANDATORY_FLAG_JSON | \ + XL_MANDATORY_FLAG_STREAMv2) + +struct save_file_header { + char magic[32]; /* savefileheader_magic */ + /* All uint32_ts are in domain's byte order. */ + uint32_t byteorder; /* SAVEFILE_BYTEORDER_VALUE */ + uint32_t mandatory_flags; /* unknown flags => reject restore */ + uint32_t optional_flags; /* unknown flags => reject restore */ + uint32_t optional_data_len; /* skip, or skip tail, if not understood */ +}; + +/* Optional data, in order: + * 4 bytes uint32_t config file size + * n bytes config file in Unix text file format + */ + +#define SAVEFILE_BYTEORDER_VALUE ((uint32_t)0x01020304UL) + + /* * The xl process should always return either EXIT_SUCCESS or * EXIT_FAILURE. main_* functions, implementing the various xl diff --git a/tools/xl/xl_cmdimpl.c b/tools/xl/xl_cmdimpl.c index 7851fc03d4..b3a17d5fa6 100644 --- a/tools/xl/xl_cmdimpl.c +++ b/tools/xl/xl_cmdimpl.c @@ -49,70 +49,6 @@ libxl_ctx *ctx; xlchild children[child_max]; const char *common_domname; -static int fd_lock = -1; - -static const char savefileheader_magic[32]= - "Xen saved domain, xl format\n \0 \r"; - -#ifndef LIBXL_HAVE_NO_SUSPEND_RESUME -static const char migrate_receiver_banner[]= - "xl migration receiver ready, send binary domain data.\n"; -static const char migrate_receiver_ready[]= - "domain received, ready to unpause"; -static const char migrate_permission_to_go[]= - "domain is yours, you are cleared to unpause"; -static const char migrate_report[]= - "my copy unpause results are as follows"; -#endif - - /* followed by one byte: - * 0: everything went well, domain is running - * next thing is we all exit - * non-0: things went badly - * next thing should be a migrate_permission_to_go - * from target to source - */ - -#define XL_MANDATORY_FLAG_JSON (1U << 0) /* config data is in JSON format */ -#define XL_MANDATORY_FLAG_STREAMv2 (1U << 1) /* stream is v2 */ -#define XL_MANDATORY_FLAG_ALL (XL_MANDATORY_FLAG_JSON | \ - XL_MANDATORY_FLAG_STREAMv2) - -struct save_file_header { - char magic[32]; /* savefileheader_magic */ - /* All uint32_ts are in domain's byte order. */ - uint32_t byteorder; /* SAVEFILE_BYTEORDER_VALUE */ - uint32_t mandatory_flags; /* unknown flags => reject restore */ - uint32_t optional_flags; /* unknown flags => reject restore */ - uint32_t optional_data_len; /* skip, or skip tail, if not understood */ -}; - -/* Optional data, in order: - * 4 bytes uint32_t config file size - * n bytes config file in Unix text file format - */ - -#define SAVEFILE_BYTEORDER_VALUE ((uint32_t)0x01020304UL) - -struct domain_create { - int debug; - int daemonize; - int monitor; /* handle guest reboots etc */ - int paused; - int dryrun; - int quiet; - int vnc; - int vncautopass; - int console_autoconnect; - int checkpointed_stream; - const char *config_file; - char *extra_config; /* extra config string */ - const char *restore_file; - char *colo_proxy_script; - int migrate_fd; /* -1 means none */ - int send_back_fd; /* -1 means none */ - char **migration_domname_r; /* from malloc */ -}; int child_report(xlchildnum child) { @@ -130,847 +66,6 @@ int child_report(xlchildnum child) } } -static void console_child_report(xlchildnum child) -{ - if (xl_child_pid(child)) - child_report(child); -} - -static int vncviewer(uint32_t domid, int autopass) -{ - libxl_vncviewer_exec(ctx, domid, autopass); - fprintf(stderr, "Unable to execute vncviewer\n"); - return 1; -} - -static void autoconnect_vncviewer(uint32_t domid, int autopass) -{ - console_child_report(child_vncviewer); - - pid_t pid = xl_fork(child_vncviewer, "vncviewer child"); - if (pid) - return; - - postfork(); - - sleep(1); - vncviewer(domid, autopass); - _exit(EXIT_FAILURE); -} - -static int acquire_lock(void) -{ - int rc; - struct flock fl; - - /* lock already acquired */ - if (fd_lock >= 0) - return ERROR_INVAL; - - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - fd_lock = open(lockfile, O_WRONLY|O_CREAT, S_IWUSR); - if (fd_lock < 0) { - fprintf(stderr, "cannot open the lockfile %s errno=%d\n", lockfile, errno); - return ERROR_FAIL; - } - if (fcntl(fd_lock, F_SETFD, FD_CLOEXEC) < 0) { - close(fd_lock); - fprintf(stderr, "cannot set cloexec to lockfile %s errno=%d\n", lockfile, errno); - return ERROR_FAIL; - } -get_lock: - rc = fcntl(fd_lock, F_SETLKW, &fl); - if (rc < 0 && errno == EINTR) - goto get_lock; - if (rc < 0) { - fprintf(stderr, "cannot acquire lock %s errno=%d\n", lockfile, errno); - rc = ERROR_FAIL; - } else - rc = 0; - return rc; -} - -static int release_lock(void) -{ - int rc; - struct flock fl; - - /* lock not acquired */ - if (fd_lock < 0) - return ERROR_INVAL; - -release_lock: - fl.l_type = F_UNLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - - rc = fcntl(fd_lock, F_SETLKW, &fl); - if (rc < 0 && errno == EINTR) - goto release_lock; - if (rc < 0) { - fprintf(stderr, "cannot release lock %s, errno=%d\n", lockfile, errno); - rc = ERROR_FAIL; - } else - rc = 0; - close(fd_lock); - fd_lock = -1; - - return rc; -} - -static void reload_domain_config(uint32_t domid, - libxl_domain_config *d_config) -{ - int rc; - uint8_t *t_data; - int ret, t_len; - libxl_domain_config d_config_new; - - /* In case user has used "config-update" to store a new config - * file. - */ - ret = libxl_userdata_retrieve(ctx, domid, "xl", &t_data, &t_len); - if (ret && errno != ENOENT) { - LOG("\"xl\" configuration found but failed to load\n"); - } - if (t_len > 0) { - LOG("\"xl\" configuration found, using it\n"); - libxl_domain_config_dispose(d_config); - libxl_domain_config_init(d_config); - parse_config_data("", (const char *)t_data, - t_len, d_config); - free(t_data); - libxl_userdata_unlink(ctx, domid, "xl"); - return; - } - - libxl_domain_config_init(&d_config_new); - rc = libxl_retrieve_domain_configuration(ctx, domid, &d_config_new); - if (rc) { - LOG("failed to retrieve guest configuration (rc=%d). " - "reusing old configuration", rc); - libxl_domain_config_dispose(&d_config_new); - } else { - libxl_domain_config_dispose(d_config); - /* Steal allocations */ - memcpy(d_config, &d_config_new, sizeof(libxl_domain_config)); - } -} - -/* Can update r_domid if domain is destroyed */ -static domain_restart_type handle_domain_death(uint32_t *r_domid, - libxl_event *event, - libxl_domain_config *d_config) -{ - domain_restart_type restart = DOMAIN_RESTART_NONE; - libxl_action_on_shutdown action; - - switch (event->u.domain_shutdown.shutdown_reason) { - case LIBXL_SHUTDOWN_REASON_POWEROFF: - action = d_config->on_poweroff; - break; - case LIBXL_SHUTDOWN_REASON_REBOOT: - action = d_config->on_reboot; - break; - case LIBXL_SHUTDOWN_REASON_SUSPEND: - LOG("Domain has suspended."); - return 0; - case LIBXL_SHUTDOWN_REASON_CRASH: - action = d_config->on_crash; - break; - case LIBXL_SHUTDOWN_REASON_WATCHDOG: - action = d_config->on_watchdog; - break; - case LIBXL_SHUTDOWN_REASON_SOFT_RESET: - action = d_config->on_soft_reset; - break; - default: - LOG("Unknown shutdown reason code %d. Destroying domain.", - event->u.domain_shutdown.shutdown_reason); - action = LIBXL_ACTION_ON_SHUTDOWN_DESTROY; - } - - LOG("Action for shutdown reason code %d is %s", - event->u.domain_shutdown.shutdown_reason, - get_action_on_shutdown_name(action)); - - if (action == LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_DESTROY || action == LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_RESTART) { - char *corefile; - int rc; - - xasprintf(&corefile, XEN_DUMP_DIR "/%s", d_config->c_info.name); - LOG("dumping core to %s", corefile); - rc = libxl_domain_core_dump(ctx, *r_domid, corefile, NULL); - if (rc) LOG("core dump failed (rc=%d).", rc); - free(corefile); - /* No point crying over spilled milk, continue on failure. */ - - if (action == LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_DESTROY) - action = LIBXL_ACTION_ON_SHUTDOWN_DESTROY; - else - action = LIBXL_ACTION_ON_SHUTDOWN_RESTART; - } - - switch (action) { - case LIBXL_ACTION_ON_SHUTDOWN_PRESERVE: - break; - - case LIBXL_ACTION_ON_SHUTDOWN_RESTART_RENAME: - reload_domain_config(*r_domid, d_config); - restart = DOMAIN_RESTART_RENAME; - break; - - case LIBXL_ACTION_ON_SHUTDOWN_RESTART: - reload_domain_config(*r_domid, d_config); - restart = DOMAIN_RESTART_NORMAL; - /* fall-through */ - case LIBXL_ACTION_ON_SHUTDOWN_DESTROY: - LOG("Domain %d needs to be cleaned up: destroying the domain", - *r_domid); - libxl_domain_destroy(ctx, *r_domid, 0); - *r_domid = INVALID_DOMID; - break; - - case LIBXL_ACTION_ON_SHUTDOWN_SOFT_RESET: - reload_domain_config(*r_domid, d_config); - restart = DOMAIN_RESTART_SOFT_RESET; - break; - - case LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_DESTROY: - case LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_RESTART: - /* Already handled these above. */ - abort(); - } - - return restart; -} - -/* Preserve a copy of a domain under a new name. Updates *r_domid */ -static int preserve_domain(uint32_t *r_domid, libxl_event *event, - libxl_domain_config *d_config) -{ - time_t now; - struct tm tm; - char strtime[24]; - - libxl_uuid new_uuid; - - int rc; - - now = time(NULL); - if (now == ((time_t) -1)) { - LOG("Failed to get current time for domain rename"); - return 0; - } - - tzset(); - if (gmtime_r(&now, &tm) == NULL) { - LOG("Failed to convert time to UTC"); - return 0; - } - - if (!strftime(&strtime[0], sizeof(strtime), "-%Y%m%dT%H%MZ", &tm)) { - LOG("Failed to format time as a string"); - return 0; - } - - libxl_uuid_generate(&new_uuid); - - LOG("Preserving domain %u %s with suffix%s", - *r_domid, d_config->c_info.name, strtime); - rc = libxl_domain_preserve(ctx, *r_domid, &d_config->c_info, - strtime, new_uuid); - - /* - * Although the domain still exists it is no longer the one we are - * concerned with. - */ - *r_domid = INVALID_DOMID; - - return rc == 0 ? 1 : 0; -} - -/* - * Returns false if memory can't be freed, but also if we encounter errors. - * Returns true in case there is already, or we manage to free it, enough - * memory, but also if autoballoon is false. - */ -static bool freemem(uint32_t domid, libxl_domain_build_info *b_info) -{ - int rc, retries = 3; - uint64_t need_memkb, free_memkb; - - if (!autoballoon) - return true; - - rc = libxl_domain_need_memory(ctx, b_info, &need_memkb); - if (rc < 0) - return false; - - do { - rc = libxl_get_free_memory(ctx, &free_memkb); - if (rc < 0) - return false; - - if (free_memkb >= need_memkb) - return true; - - rc = libxl_set_memory_target(ctx, 0, free_memkb - need_memkb, 1, 0); - if (rc < 0) - return false; - - /* wait until dom0 reaches its target, as long as we are making - * progress */ - rc = libxl_wait_for_memory_target(ctx, 0, 10); - if (rc < 0) - return false; - - retries--; - } while (retries > 0); - - return false; -} - -static void autoconnect_console(libxl_ctx *ctx_ignored, - libxl_event *ev, void *priv) -{ - uint32_t bldomid = ev->domid; - int notify_fd = *(int*)priv; /* write end of the notification pipe */ - - libxl_event_free(ctx, ev); - - console_child_report(child_console); - - pid_t pid = xl_fork(child_console, "console child"); - if (pid) - return; - - postfork(); - - sleep(1); - libxl_primary_console_exec(ctx, bldomid, notify_fd); - /* Do not return. xl continued in child process */ - perror("xl: unable to exec console client"); - _exit(1); -} - -static int domain_wait_event(uint32_t domid, libxl_event **event_r) -{ - int ret; - for (;;) { - ret = libxl_event_wait(ctx, event_r, LIBXL_EVENTMASK_ALL, 0,0); - if (ret) { - LOG("Domain %u, failed to get event, quitting (rc=%d)", domid, ret); - return ret; - } - if ((*event_r)->domid != domid) { - char *evstr = libxl_event_to_json(ctx, *event_r); - LOG("INTERNAL PROBLEM - ignoring unexpected event for" - " domain %d (expected %d): event=%s", - (*event_r)->domid, domid, evstr); - free(evstr); - libxl_event_free(ctx, *event_r); - continue; - } - return ret; - } -} - -static void evdisable_disk_ejects(libxl_evgen_disk_eject **diskws, - int num_disks) -{ - int i; - - for (i = 0; i < num_disks; i++) { - if (diskws[i]) - libxl_evdisable_disk_eject(ctx, diskws[i]); - diskws[i] = NULL; - } -} - -static int create_domain(struct domain_create *dom_info) -{ - uint32_t domid = INVALID_DOMID; - - libxl_domain_config d_config; - - int debug = dom_info->debug; - int daemonize = dom_info->daemonize; - int monitor = dom_info->monitor; - int paused = dom_info->paused; - int vncautopass = dom_info->vncautopass; - const char *config_file = dom_info->config_file; - const char *extra_config = dom_info->extra_config; - const char *restore_file = dom_info->restore_file; - const char *config_source = NULL; - const char *restore_source = NULL; - int migrate_fd = dom_info->migrate_fd; - bool config_in_json; - - int i; - int need_daemon = daemonize; - int ret, rc; - libxl_evgen_domain_death *deathw = NULL; - libxl_evgen_disk_eject **diskws = NULL; /* one per disk */ - unsigned int num_diskws = 0; - void *config_data = 0; - int config_len = 0; - int restore_fd = -1; - int restore_fd_to_close = -1; - int send_back_fd = -1; - const libxl_asyncprogress_how *autoconnect_console_how; - int notify_pipe[2] = { -1, -1 }; - struct save_file_header hdr; - uint32_t domid_soft_reset = INVALID_DOMID; - - int restoring = (restore_file || (migrate_fd >= 0)); - - libxl_domain_config_init(&d_config); - - if (restoring) { - uint8_t *optdata_begin = 0; - const uint8_t *optdata_here = 0; - union { uint32_t u32; char b[4]; } u32buf; - uint32_t badflags; - - if (migrate_fd >= 0) { - restore_source = ""; - restore_fd = migrate_fd; - send_back_fd = dom_info->send_back_fd; - } else { - restore_source = restore_file; - restore_fd = open(restore_file, O_RDONLY); - if (restore_fd == -1) { - fprintf(stderr, "Can't open restore file: %s\n", strerror(errno)); - return ERROR_INVAL; - } - restore_fd_to_close = restore_fd; - rc = libxl_fd_set_cloexec(ctx, restore_fd, 1); - if (rc) return rc; - } - - CHK_ERRNOVAL(libxl_read_exactly( - ctx, restore_fd, &hdr, sizeof(hdr), - restore_source, "header")); - if (memcmp(hdr.magic, savefileheader_magic, sizeof(hdr.magic))) { - fprintf(stderr, "File has wrong magic number -" - " corrupt or for a different tool?\n"); - return ERROR_INVAL; - } - if (hdr.byteorder != SAVEFILE_BYTEORDER_VALUE) { - fprintf(stderr, "File has wrong byte order\n"); - return ERROR_INVAL; - } - fprintf(stderr, "Loading new save file %s" - " (new xl fmt info" - " 0x%"PRIx32"/0x%"PRIx32"/%"PRIu32")\n", - restore_source, hdr.mandatory_flags, hdr.optional_flags, - hdr.optional_data_len); - - badflags = hdr.mandatory_flags & ~XL_MANDATORY_FLAG_ALL; - if (badflags) { - fprintf(stderr, "Savefile has mandatory flag(s) 0x%"PRIx32" " - "which are not supported; need newer xl\n", - badflags); - return ERROR_INVAL; - } - if (hdr.optional_data_len) { - optdata_begin = xmalloc(hdr.optional_data_len); - CHK_ERRNOVAL(libxl_read_exactly( - ctx, restore_fd, optdata_begin, - hdr.optional_data_len, restore_source, - "optdata")); - } - -#define OPTDATA_LEFT (hdr.optional_data_len - (optdata_here - optdata_begin)) -#define WITH_OPTDATA(amt, body) \ - if (OPTDATA_LEFT < (amt)) { \ - fprintf(stderr, "Savefile truncated.\n"); \ - return ERROR_INVAL; \ - } else { \ - body; \ - optdata_here += (amt); \ - } - - optdata_here = optdata_begin; - - if (OPTDATA_LEFT) { - fprintf(stderr, " Savefile contains xl domain config%s\n", - !!(hdr.mandatory_flags & XL_MANDATORY_FLAG_JSON) - ? " in JSON format" : ""); - WITH_OPTDATA(4, { - memcpy(u32buf.b, optdata_here, 4); - config_len = u32buf.u32; - }); - WITH_OPTDATA(config_len, { - config_data = xmalloc(config_len); - memcpy(config_data, optdata_here, config_len); - }); - } - - } - - if (config_file) { - free(config_data); config_data = 0; - /* /dev/null represents special case (read config. from command line) */ - if (!strcmp(config_file, "/dev/null")) { - config_len = 0; - } else { - ret = libxl_read_file_contents(ctx, config_file, - &config_data, &config_len); - if (ret) { fprintf(stderr, "Failed to read config file: %s: %s\n", - config_file, strerror(errno)); return ERROR_FAIL; } - } - if (!restoring && extra_config && strlen(extra_config)) { - if (config_len > INT_MAX - (strlen(extra_config) + 2 + 1)) { - fprintf(stderr, "Failed to attach extra configuration\n"); - return ERROR_FAIL; - } - /* allocate space for the extra config plus two EOLs plus \0 */ - config_data = xrealloc(config_data, config_len - + strlen(extra_config) + 2 + 1); - config_len += sprintf(config_data + config_len, "\n%s\n", - extra_config); - } - config_source=config_file; - config_in_json = false; - } else { - if (!config_data) { - fprintf(stderr, "Config file not specified and" - " none in save file\n"); - return ERROR_INVAL; - } - config_source = ""; - config_in_json = !!(hdr.mandatory_flags & XL_MANDATORY_FLAG_JSON); - } - - if (!dom_info->quiet) - fprintf(stderr, "Parsing config from %s\n", config_source); - - if (config_in_json) { - libxl_domain_config_from_json(ctx, &d_config, - (const char *)config_data); - } else { - parse_config_data(config_source, config_data, config_len, &d_config); - } - - if (migrate_fd >= 0) { - if (d_config.c_info.name) { - /* when we receive a domain we get its name from the config - * file; and we receive it to a temporary name */ - assert(!common_domname); - - common_domname = d_config.c_info.name; - d_config.c_info.name = 0; /* steals allocation from config */ - - xasprintf(&d_config.c_info.name, "%s--incoming", common_domname); - *dom_info->migration_domname_r = strdup(d_config.c_info.name); - } - } - - if (debug || dom_info->dryrun) { - FILE *cfg_print_fh = (debug && !dom_info->dryrun) ? stderr : stdout; - if (default_output_format == OUTPUT_FORMAT_SXP) { - printf_info_sexp(-1, &d_config, cfg_print_fh); - } else { - char *json = libxl_domain_config_to_json(ctx, &d_config); - if (!json) { - fprintf(stderr, - "Failed to convert domain configuration to JSON\n"); - exit(1); - } - fputs(json, cfg_print_fh); - free(json); - xflush_stream(cfg_print_fh); - } - } - - - ret = 0; - if (dom_info->dryrun) - goto out; - -start: - assert(domid == INVALID_DOMID); - - rc = acquire_lock(); - if (rc < 0) - goto error_out; - - if (domid_soft_reset == INVALID_DOMID) { - if (!freemem(domid, &d_config.b_info)) { - fprintf(stderr, "failed to free memory for the domain\n"); - ret = ERROR_FAIL; - goto error_out; - } - } - - libxl_asyncprogress_how autoconnect_console_how_buf; - if ( dom_info->console_autoconnect ) { - if (libxl_pipe(ctx, notify_pipe)) { - ret = ERROR_FAIL; - goto error_out; - } - autoconnect_console_how_buf.callback = autoconnect_console; - autoconnect_console_how_buf.for_callback = ¬ify_pipe[1]; - autoconnect_console_how = &autoconnect_console_how_buf; - }else{ - autoconnect_console_how = 0; - } - - if ( restoring ) { - libxl_domain_restore_params params; - - libxl_domain_restore_params_init(¶ms); - - params.checkpointed_stream = dom_info->checkpointed_stream; - params.stream_version = - (hdr.mandatory_flags & XL_MANDATORY_FLAG_STREAMv2) ? 2 : 1; - params.colo_proxy_script = dom_info->colo_proxy_script; - - ret = libxl_domain_create_restore(ctx, &d_config, - &domid, restore_fd, - send_back_fd, ¶ms, - 0, autoconnect_console_how); - - libxl_domain_restore_params_dispose(¶ms); - - /* - * On subsequent reboot etc we should create the domain, not - * restore/migrate-receive it again. - */ - restoring = 0; - } else if (domid_soft_reset != INVALID_DOMID) { - /* Do soft reset. */ - ret = libxl_domain_soft_reset(ctx, &d_config, domid_soft_reset, - 0, autoconnect_console_how); - domid = domid_soft_reset; - domid_soft_reset = INVALID_DOMID; - } else { - ret = libxl_domain_create_new(ctx, &d_config, &domid, - 0, autoconnect_console_how); - } - if ( ret ) - goto error_out; - - release_lock(); - - if (restore_fd_to_close >= 0) { - if (close(restore_fd_to_close)) - fprintf(stderr, "Failed to close restoring file, fd %d, errno %d\n", - restore_fd_to_close, errno); - restore_fd_to_close = -1; - } - - if (autoconnect_console_how) { - char buf[1]; - int r; - - /* Try to get notification from xenconsole. Just move on if - * error occurs -- it's only minor annoyance if console - * doesn't show up. - */ - do { - r = read(notify_pipe[0], buf, 1); - } while (r == -1 && errno == EINTR); - - if (r == -1) - fprintf(stderr, - "Failed to get notification from xenconsole: %s\n", - strerror(errno)); - else if (r == 0) - fprintf(stderr, "Got EOF from xenconsole notification fd\n"); - else if (r == 1 && buf[0] != 0x00) - fprintf(stderr, "Got unexpected response from xenconsole: %#x\n", - buf[0]); - - close(notify_pipe[0]); - close(notify_pipe[1]); - notify_pipe[0] = notify_pipe[1] = -1; - } - - if (!paused) - libxl_domain_unpause(ctx, domid); - - ret = domid; /* caller gets success in parent */ - if (!daemonize && !monitor) - goto out; - - if (dom_info->vnc) - autoconnect_vncviewer(domid, vncautopass); - - if (need_daemon) { - char *name; - - xasprintf(&name, "xl-%s", d_config.c_info.name); - ret = do_daemonize(name, NULL); - free(name); - if (ret) { - ret = (ret == 1) ? domid : ret; - goto out; - } - need_daemon = 0; - } - LOG("Waiting for domain %s (domid %u) to die [pid %ld]", - d_config.c_info.name, domid, (long)getpid()); - - ret = libxl_evenable_domain_death(ctx, domid, 0, &deathw); - if (ret) goto out; - - if (!diskws) { - diskws = xmalloc(sizeof(*diskws) * d_config.num_disks); - for (i = 0; i < d_config.num_disks; i++) - diskws[i] = NULL; - num_diskws = d_config.num_disks; - } - for (i = 0; i < num_diskws; i++) { - if (d_config.disks[i].removable) { - ret = libxl_evenable_disk_eject(ctx, domid, d_config.disks[i].vdev, - 0, &diskws[i]); - if (ret) goto out; - } - } - while (1) { - libxl_event *event; - ret = domain_wait_event(domid, &event); - if (ret) goto out; - - switch (event->type) { - - case LIBXL_EVENT_TYPE_DOMAIN_SHUTDOWN: - LOG("Domain %u has shut down, reason code %d 0x%x", domid, - event->u.domain_shutdown.shutdown_reason, - event->u.domain_shutdown.shutdown_reason); - switch (handle_domain_death(&domid, event, &d_config)) { - case DOMAIN_RESTART_SOFT_RESET: - domid_soft_reset = domid; - domid = INVALID_DOMID; - /* fall through */ - case DOMAIN_RESTART_RENAME: - if (domid_soft_reset == INVALID_DOMID && - !preserve_domain(&domid, event, &d_config)) { - libxl_event_free(ctx, event); - /* If we fail then exit leaving the old domain in place. */ - ret = -1; - goto out; - } - - /* Otherwise fall through and restart. */ - case DOMAIN_RESTART_NORMAL: - libxl_event_free(ctx, event); - libxl_evdisable_domain_death(ctx, deathw); - deathw = NULL; - evdisable_disk_ejects(diskws, num_diskws); - free(diskws); - diskws = NULL; - num_diskws = 0; - /* discard any other events which may have been generated */ - while (!(ret = libxl_event_check(ctx, &event, - LIBXL_EVENTMASK_ALL, 0,0))) { - libxl_event_free(ctx, event); - } - if (ret != ERROR_NOT_READY) { - LOG("warning, libxl_event_check (cleanup) failed (rc=%d)", - ret); - } - - /* - * Do not attempt to reconnect if we come round again due to a - * guest reboot -- the stdin/out will be disconnected by then. - */ - dom_info->console_autoconnect = 0; - - /* Some settings only make sense on first boot. */ - paused = 0; - if (common_domname - && strcmp(d_config.c_info.name, common_domname)) { - d_config.c_info.name = strdup(common_domname); - } - - /* - * XXX FIXME: If this sleep is not there then domain - * re-creation fails sometimes. - */ - LOG("Done. Rebooting now"); - sleep(2); - goto start; - - case DOMAIN_RESTART_NONE: - LOG("Done. Exiting now"); - libxl_event_free(ctx, event); - ret = 0; - goto out; - - default: - abort(); - } - - case LIBXL_EVENT_TYPE_DOMAIN_DEATH: - LOG("Domain %u has been destroyed.", domid); - libxl_event_free(ctx, event); - ret = 0; - goto out; - - case LIBXL_EVENT_TYPE_DISK_EJECT: - /* XXX what is this for? */ - libxl_cdrom_insert(ctx, domid, &event->u.disk_eject.disk, NULL); - break; - - default:; - char *evstr = libxl_event_to_json(ctx, event); - LOG("warning, got unexpected event type %d, event=%s", - event->type, evstr); - free(evstr); - } - - libxl_event_free(ctx, event); - } - -error_out: - release_lock(); - if (libxl_domid_valid_guest(domid)) { - libxl_domain_destroy(ctx, domid, 0); - domid = INVALID_DOMID; - } - -out: - if (restore_fd_to_close >= 0) { - if (close(restore_fd_to_close)) - fprintf(stderr, "Failed to close restoring file, fd %d, errno %d\n", - restore_fd_to_close, errno); - restore_fd_to_close = -1; - } - - if (logfile != 2) - close(logfile); - - libxl_domain_config_dispose(&d_config); - - free(config_data); - - console_child_report(child_console); - - if (deathw) - libxl_evdisable_domain_death(ctx, deathw); - if (diskws) { - evdisable_disk_ejects(diskws, d_config.num_disks); - free(diskws); - } - - /* - * If we have daemonized then do not return to the caller -- this has - * already happened in the parent. - */ - if ( daemonize && !need_daemon ) - exit(ret); - - return ret; -} - void help(const char *command) { int i; @@ -1003,127 +98,6 @@ void help(const char *command) } } -static void pause_domain(uint32_t domid) -{ - libxl_domain_pause(ctx, domid); -} - -static void unpause_domain(uint32_t domid) -{ - libxl_domain_unpause(ctx, domid); -} - -static void destroy_domain(uint32_t domid, int force) -{ - int rc; - - if (domid == 0 && !force) { - fprintf(stderr, "Not destroying domain 0; use -f to force.\n" - "This can only be done when using a disaggregated " - "hardware domain and toolstack.\n\n"); - exit(EXIT_FAILURE); - } - rc = libxl_domain_destroy(ctx, domid, 0); - if (rc) { fprintf(stderr,"destroy failed (rc=%d)\n",rc); exit(EXIT_FAILURE); } -} - -static void wait_for_domain_deaths(libxl_evgen_domain_death **deathws, int nr) -{ - int rc, count = 0; - LOG("Waiting for %d domains", nr); - while(1 && count < nr) { - libxl_event *event; - rc = libxl_event_wait(ctx, &event, LIBXL_EVENTMASK_ALL, 0,0); - if (rc) { - LOG("Failed to get event, quitting (rc=%d)", rc); - exit(EXIT_FAILURE); - } - - switch (event->type) { - case LIBXL_EVENT_TYPE_DOMAIN_DEATH: - LOG("Domain %d has been destroyed", event->domid); - libxl_evdisable_domain_death(ctx, deathws[event->for_user]); - count++; - break; - case LIBXL_EVENT_TYPE_DOMAIN_SHUTDOWN: - LOG("Domain %d has been shut down, reason code %d", - event->domid, event->u.domain_shutdown.shutdown_reason); - libxl_evdisable_domain_death(ctx, deathws[event->for_user]); - count++; - break; - default: - LOG("Unexpected event type %d", event->type); - break; - } - libxl_event_free(ctx, event); - } -} - -static void shutdown_domain(uint32_t domid, - libxl_evgen_domain_death **deathw, - libxl_ev_user for_user, - int fallback_trigger) -{ - int rc; - - fprintf(stderr, "Shutting down domain %u\n", domid); - rc=libxl_domain_shutdown(ctx, domid); - if (rc == ERROR_NOPARAVIRT) { - if (fallback_trigger) { - fprintf(stderr, "PV control interface not available:" - " sending ACPI power button event.\n"); - rc = libxl_send_trigger(ctx, domid, LIBXL_TRIGGER_POWER, 0); - } else { - fprintf(stderr, "PV control interface not available:" - " external graceful shutdown not possible.\n"); - fprintf(stderr, "Use \"-F\" to fallback to ACPI power event.\n"); - } - } - - if (rc) { - fprintf(stderr,"shutdown failed (rc=%d)\n",rc);exit(EXIT_FAILURE); - } - - if (deathw) { - rc = libxl_evenable_domain_death(ctx, domid, for_user, deathw); - if (rc) { - fprintf(stderr,"wait for death failed (evgen, rc=%d)\n",rc); - exit(EXIT_FAILURE); - } - } -} - -static void reboot_domain(uint32_t domid, libxl_evgen_domain_death **deathw, - libxl_ev_user for_user, int fallback_trigger) -{ - int rc; - - fprintf(stderr, "Rebooting domain %u\n", domid); - rc=libxl_domain_reboot(ctx, domid); - if (rc == ERROR_NOPARAVIRT) { - if (fallback_trigger) { - fprintf(stderr, "PV control interface not available:" - " sending ACPI reset button event.\n"); - rc = libxl_send_trigger(ctx, domid, LIBXL_TRIGGER_RESET, 0); - } else { - fprintf(stderr, "PV control interface not available:" - " external graceful reboot not possible.\n"); - fprintf(stderr, "Use \"-F\" to fallback to ACPI reset event.\n"); - } - } - if (rc) { - fprintf(stderr,"reboot failed (rc=%d)\n",rc);exit(EXIT_FAILURE); - } - - if (deathw) { - rc = libxl_evenable_domain_death(ctx, domid, for_user, deathw); - if (rc) { - fprintf(stderr,"wait for death failed (evgen, rc=%d)\n",rc); - exit(EXIT_FAILURE); - } - } -} - #ifndef LIBXL_HAVE_NO_SUSPEND_RESUME static void save_domain_core_begin(uint32_t domid, const char *override_config_file, @@ -1915,223 +889,6 @@ int main_migrate(int argc, char **argv) } #endif -int main_pause(int argc, char **argv) -{ - int opt; - - SWITCH_FOREACH_OPT(opt, "", NULL, "pause", 1) { - /* No options */ - } - - pause_domain(xfind_domain(argv[optind])); - - return EXIT_SUCCESS; -} - -int main_unpause(int argc, char **argv) -{ - int opt; - - SWITCH_FOREACH_OPT(opt, "", NULL, "unpause", 1) { - /* No options */ - } - - unpause_domain(xfind_domain(argv[optind])); - - return EXIT_SUCCESS; -} - -int main_destroy(int argc, char **argv) -{ - int opt; - int force = 0; - - SWITCH_FOREACH_OPT(opt, "f", NULL, "destroy", 1) { - case 'f': - force = 1; - break; - } - - destroy_domain(xfind_domain(argv[optind]), force); - return EXIT_SUCCESS; -} - -static int main_shutdown_or_reboot(int do_reboot, int argc, char **argv) -{ - const char *what = do_reboot ? "reboot" : "shutdown"; - void (*fn)(uint32_t domid, - libxl_evgen_domain_death **, libxl_ev_user, int) = - do_reboot ? &reboot_domain : &shutdown_domain; - int opt, i, nb_domain; - int wait_for_it = 0, all = 0, nrdeathws = 0; - int fallback_trigger = 0; - static struct option opts[] = { - {"all", 0, 0, 'a'}, - {"wait", 0, 0, 'w'}, - COMMON_LONG_OPTS - }; - - SWITCH_FOREACH_OPT(opt, "awF", opts, what, 0) { - case 'a': - all = 1; - break; - case 'w': - wait_for_it = 1; - break; - case 'F': - fallback_trigger = 1; - break; - } - - if (!argv[optind] && !all) { - fprintf(stderr, "You must specify -a or a domain id.\n\n"); - return EXIT_FAILURE; - } - - if (all) { - libxl_dominfo *dominfo; - libxl_evgen_domain_death **deathws = NULL; - if (!(dominfo = libxl_list_domain(ctx, &nb_domain))) { - fprintf(stderr, "libxl_list_domain failed.\n"); - return EXIT_FAILURE; - } - - if (wait_for_it) - deathws = calloc(nb_domain, sizeof(*deathws)); - - for (i = 0; itype) { + case LIBXL_EVENT_TYPE_DOMAIN_DEATH: + LOG("Domain %d has been destroyed", event->domid); + libxl_evdisable_domain_death(ctx, deathws[event->for_user]); + count++; + break; + case LIBXL_EVENT_TYPE_DOMAIN_SHUTDOWN: + LOG("Domain %d has been shut down, reason code %d", + event->domid, event->u.domain_shutdown.shutdown_reason); + libxl_evdisable_domain_death(ctx, deathws[event->for_user]); + count++; + break; + default: + LOG("Unexpected event type %d", event->type); + break; + } + libxl_event_free(ctx, event); + } +} + +static int main_shutdown_or_reboot(int do_reboot, int argc, char **argv) +{ + const char *what = do_reboot ? "reboot" : "shutdown"; + void (*fn)(uint32_t domid, + libxl_evgen_domain_death **, libxl_ev_user, int) = + do_reboot ? &reboot_domain : &shutdown_domain; + int opt, i, nb_domain; + int wait_for_it = 0, all = 0, nrdeathws = 0; + int fallback_trigger = 0; + static struct option opts[] = { + {"all", 0, 0, 'a'}, + {"wait", 0, 0, 'w'}, + COMMON_LONG_OPTS + }; + + SWITCH_FOREACH_OPT(opt, "awF", opts, what, 0) { + case 'a': + all = 1; + break; + case 'w': + wait_for_it = 1; + break; + case 'F': + fallback_trigger = 1; + break; + } + + if (!argv[optind] && !all) { + fprintf(stderr, "You must specify -a or a domain id.\n\n"); + return EXIT_FAILURE; + } + + if (all) { + libxl_dominfo *dominfo; + libxl_evgen_domain_death **deathws = NULL; + if (!(dominfo = libxl_list_domain(ctx, &nb_domain))) { + fprintf(stderr, "libxl_list_domain failed.\n"); + return EXIT_FAILURE; + } + + if (wait_for_it) + deathws = calloc(nb_domain, sizeof(*deathws)); + + for (i = 0; idomid != domid) { + char *evstr = libxl_event_to_json(ctx, *event_r); + LOG("INTERNAL PROBLEM - ignoring unexpected event for" + " domain %d (expected %d): event=%s", + (*event_r)->domid, domid, evstr); + free(evstr); + libxl_event_free(ctx, *event_r); + continue; + } + return ret; + } +} + +/* + * Returns false if memory can't be freed, but also if we encounter errors. + * Returns true in case there is already, or we manage to free it, enough + * memory, but also if autoballoon is false. + */ +static bool freemem(uint32_t domid, libxl_domain_build_info *b_info) +{ + int rc, retries = 3; + uint64_t need_memkb, free_memkb; + + if (!autoballoon) + return true; + + rc = libxl_domain_need_memory(ctx, b_info, &need_memkb); + if (rc < 0) + return false; + + do { + rc = libxl_get_free_memory(ctx, &free_memkb); + if (rc < 0) + return false; + + if (free_memkb >= need_memkb) + return true; + + rc = libxl_set_memory_target(ctx, 0, free_memkb - need_memkb, 1, 0); + if (rc < 0) + return false; + + /* wait until dom0 reaches its target, as long as we are making + * progress */ + rc = libxl_wait_for_memory_target(ctx, 0, 10); + if (rc < 0) + return false; + + retries--; + } while (retries > 0); + + return false; +} + +static void reload_domain_config(uint32_t domid, + libxl_domain_config *d_config) +{ + int rc; + uint8_t *t_data; + int ret, t_len; + libxl_domain_config d_config_new; + + /* In case user has used "config-update" to store a new config + * file. + */ + ret = libxl_userdata_retrieve(ctx, domid, "xl", &t_data, &t_len); + if (ret && errno != ENOENT) { + LOG("\"xl\" configuration found but failed to load\n"); + } + if (t_len > 0) { + LOG("\"xl\" configuration found, using it\n"); + libxl_domain_config_dispose(d_config); + libxl_domain_config_init(d_config); + parse_config_data("", (const char *)t_data, + t_len, d_config); + free(t_data); + libxl_userdata_unlink(ctx, domid, "xl"); + return; + } + + libxl_domain_config_init(&d_config_new); + rc = libxl_retrieve_domain_configuration(ctx, domid, &d_config_new); + if (rc) { + LOG("failed to retrieve guest configuration (rc=%d). " + "reusing old configuration", rc); + libxl_domain_config_dispose(&d_config_new); + } else { + libxl_domain_config_dispose(d_config); + /* Steal allocations */ + memcpy(d_config, &d_config_new, sizeof(libxl_domain_config)); + } +} + +/* Can update r_domid if domain is destroyed */ +static domain_restart_type handle_domain_death(uint32_t *r_domid, + libxl_event *event, + libxl_domain_config *d_config) +{ + domain_restart_type restart = DOMAIN_RESTART_NONE; + libxl_action_on_shutdown action; + + switch (event->u.domain_shutdown.shutdown_reason) { + case LIBXL_SHUTDOWN_REASON_POWEROFF: + action = d_config->on_poweroff; + break; + case LIBXL_SHUTDOWN_REASON_REBOOT: + action = d_config->on_reboot; + break; + case LIBXL_SHUTDOWN_REASON_SUSPEND: + LOG("Domain has suspended."); + return 0; + case LIBXL_SHUTDOWN_REASON_CRASH: + action = d_config->on_crash; + break; + case LIBXL_SHUTDOWN_REASON_WATCHDOG: + action = d_config->on_watchdog; + break; + case LIBXL_SHUTDOWN_REASON_SOFT_RESET: + action = d_config->on_soft_reset; + break; + default: + LOG("Unknown shutdown reason code %d. Destroying domain.", + event->u.domain_shutdown.shutdown_reason); + action = LIBXL_ACTION_ON_SHUTDOWN_DESTROY; + } + + LOG("Action for shutdown reason code %d is %s", + event->u.domain_shutdown.shutdown_reason, + get_action_on_shutdown_name(action)); + + if (action == LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_DESTROY || action == LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_RESTART) { + char *corefile; + int rc; + + xasprintf(&corefile, XEN_DUMP_DIR "/%s", d_config->c_info.name); + LOG("dumping core to %s", corefile); + rc = libxl_domain_core_dump(ctx, *r_domid, corefile, NULL); + if (rc) LOG("core dump failed (rc=%d).", rc); + free(corefile); + /* No point crying over spilled milk, continue on failure. */ + + if (action == LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_DESTROY) + action = LIBXL_ACTION_ON_SHUTDOWN_DESTROY; + else + action = LIBXL_ACTION_ON_SHUTDOWN_RESTART; + } + + switch (action) { + case LIBXL_ACTION_ON_SHUTDOWN_PRESERVE: + break; + + case LIBXL_ACTION_ON_SHUTDOWN_RESTART_RENAME: + reload_domain_config(*r_domid, d_config); + restart = DOMAIN_RESTART_RENAME; + break; + + case LIBXL_ACTION_ON_SHUTDOWN_RESTART: + reload_domain_config(*r_domid, d_config); + restart = DOMAIN_RESTART_NORMAL; + /* fall-through */ + case LIBXL_ACTION_ON_SHUTDOWN_DESTROY: + LOG("Domain %d needs to be cleaned up: destroying the domain", + *r_domid); + libxl_domain_destroy(ctx, *r_domid, 0); + *r_domid = INVALID_DOMID; + break; + + case LIBXL_ACTION_ON_SHUTDOWN_SOFT_RESET: + reload_domain_config(*r_domid, d_config); + restart = DOMAIN_RESTART_SOFT_RESET; + break; + + case LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_DESTROY: + case LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_RESTART: + /* Already handled these above. */ + abort(); + } + + return restart; +} + +/* Preserve a copy of a domain under a new name. Updates *r_domid */ +static int preserve_domain(uint32_t *r_domid, libxl_event *event, + libxl_domain_config *d_config) +{ + time_t now; + struct tm tm; + char strtime[24]; + + libxl_uuid new_uuid; + + int rc; + + now = time(NULL); + if (now == ((time_t) -1)) { + LOG("Failed to get current time for domain rename"); + return 0; + } + + tzset(); + if (gmtime_r(&now, &tm) == NULL) { + LOG("Failed to convert time to UTC"); + return 0; + } + + if (!strftime(&strtime[0], sizeof(strtime), "-%Y%m%dT%H%MZ", &tm)) { + LOG("Failed to format time as a string"); + return 0; + } + + libxl_uuid_generate(&new_uuid); + + LOG("Preserving domain %u %s with suffix%s", + *r_domid, d_config->c_info.name, strtime); + rc = libxl_domain_preserve(ctx, *r_domid, &d_config->c_info, + strtime, new_uuid); + + /* + * Although the domain still exists it is no longer the one we are + * concerned with. + */ + *r_domid = INVALID_DOMID; + + return rc == 0 ? 1 : 0; +} + +static void console_child_report(xlchildnum child) +{ + if (xl_child_pid(child)) + child_report(child); +} + +static int vncviewer(uint32_t domid, int autopass) +{ + libxl_vncviewer_exec(ctx, domid, autopass); + fprintf(stderr, "Unable to execute vncviewer\n"); + return 1; +} + +static void autoconnect_vncviewer(uint32_t domid, int autopass) +{ + console_child_report(child_vncviewer); + + pid_t pid = xl_fork(child_vncviewer, "vncviewer child"); + if (pid) + return; + + postfork(); + + sleep(1); + vncviewer(domid, autopass); + _exit(EXIT_FAILURE); +} + +static int acquire_lock(void) +{ + int rc; + struct flock fl; + + /* lock already acquired */ + if (fd_lock >= 0) + return ERROR_INVAL; + + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + fd_lock = open(lockfile, O_WRONLY|O_CREAT, S_IWUSR); + if (fd_lock < 0) { + fprintf(stderr, "cannot open the lockfile %s errno=%d\n", lockfile, errno); + return ERROR_FAIL; + } + if (fcntl(fd_lock, F_SETFD, FD_CLOEXEC) < 0) { + close(fd_lock); + fprintf(stderr, "cannot set cloexec to lockfile %s errno=%d\n", lockfile, errno); + return ERROR_FAIL; + } +get_lock: + rc = fcntl(fd_lock, F_SETLKW, &fl); + if (rc < 0 && errno == EINTR) + goto get_lock; + if (rc < 0) { + fprintf(stderr, "cannot acquire lock %s errno=%d\n", lockfile, errno); + rc = ERROR_FAIL; + } else + rc = 0; + return rc; +} + +static int release_lock(void) +{ + int rc; + struct flock fl; + + /* lock not acquired */ + if (fd_lock < 0) + return ERROR_INVAL; + +release_lock: + fl.l_type = F_UNLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + + rc = fcntl(fd_lock, F_SETLKW, &fl); + if (rc < 0 && errno == EINTR) + goto release_lock; + if (rc < 0) { + fprintf(stderr, "cannot release lock %s, errno=%d\n", lockfile, errno); + rc = ERROR_FAIL; + } else + rc = 0; + close(fd_lock); + fd_lock = -1; + + return rc; +} + + +static void autoconnect_console(libxl_ctx *ctx_ignored, + libxl_event *ev, void *priv) +{ + uint32_t bldomid = ev->domid; + int notify_fd = *(int*)priv; /* write end of the notification pipe */ + + libxl_event_free(ctx, ev); + + console_child_report(child_console); + + pid_t pid = xl_fork(child_console, "console child"); + if (pid) + return; + + postfork(); + + sleep(1); + libxl_primary_console_exec(ctx, bldomid, notify_fd); + /* Do not return. xl continued in child process */ + perror("xl: unable to exec console client"); + _exit(1); +} + +int create_domain(struct domain_create *dom_info) +{ + uint32_t domid = INVALID_DOMID; + + libxl_domain_config d_config; + + int debug = dom_info->debug; + int daemonize = dom_info->daemonize; + int monitor = dom_info->monitor; + int paused = dom_info->paused; + int vncautopass = dom_info->vncautopass; + const char *config_file = dom_info->config_file; + const char *extra_config = dom_info->extra_config; + const char *restore_file = dom_info->restore_file; + const char *config_source = NULL; + const char *restore_source = NULL; + int migrate_fd = dom_info->migrate_fd; + bool config_in_json; + + int i; + int need_daemon = daemonize; + int ret, rc; + libxl_evgen_domain_death *deathw = NULL; + libxl_evgen_disk_eject **diskws = NULL; /* one per disk */ + unsigned int num_diskws = 0; + void *config_data = 0; + int config_len = 0; + int restore_fd = -1; + int restore_fd_to_close = -1; + int send_back_fd = -1; + const libxl_asyncprogress_how *autoconnect_console_how; + int notify_pipe[2] = { -1, -1 }; + struct save_file_header hdr; + uint32_t domid_soft_reset = INVALID_DOMID; + + int restoring = (restore_file || (migrate_fd >= 0)); + + libxl_domain_config_init(&d_config); + + if (restoring) { + uint8_t *optdata_begin = 0; + const uint8_t *optdata_here = 0; + union { uint32_t u32; char b[4]; } u32buf; + uint32_t badflags; + + if (migrate_fd >= 0) { + restore_source = ""; + restore_fd = migrate_fd; + send_back_fd = dom_info->send_back_fd; + } else { + restore_source = restore_file; + restore_fd = open(restore_file, O_RDONLY); + if (restore_fd == -1) { + fprintf(stderr, "Can't open restore file: %s\n", strerror(errno)); + return ERROR_INVAL; + } + restore_fd_to_close = restore_fd; + rc = libxl_fd_set_cloexec(ctx, restore_fd, 1); + if (rc) return rc; + } + + CHK_ERRNOVAL(libxl_read_exactly( + ctx, restore_fd, &hdr, sizeof(hdr), + restore_source, "header")); + if (memcmp(hdr.magic, savefileheader_magic, sizeof(hdr.magic))) { + fprintf(stderr, "File has wrong magic number -" + " corrupt or for a different tool?\n"); + return ERROR_INVAL; + } + if (hdr.byteorder != SAVEFILE_BYTEORDER_VALUE) { + fprintf(stderr, "File has wrong byte order\n"); + return ERROR_INVAL; + } + fprintf(stderr, "Loading new save file %s" + " (new xl fmt info" + " 0x%"PRIx32"/0x%"PRIx32"/%"PRIu32")\n", + restore_source, hdr.mandatory_flags, hdr.optional_flags, + hdr.optional_data_len); + + badflags = hdr.mandatory_flags & ~XL_MANDATORY_FLAG_ALL; + if (badflags) { + fprintf(stderr, "Savefile has mandatory flag(s) 0x%"PRIx32" " + "which are not supported; need newer xl\n", + badflags); + return ERROR_INVAL; + } + if (hdr.optional_data_len) { + optdata_begin = xmalloc(hdr.optional_data_len); + CHK_ERRNOVAL(libxl_read_exactly( + ctx, restore_fd, optdata_begin, + hdr.optional_data_len, restore_source, + "optdata")); + } + +#define OPTDATA_LEFT (hdr.optional_data_len - (optdata_here - optdata_begin)) +#define WITH_OPTDATA(amt, body) \ + if (OPTDATA_LEFT < (amt)) { \ + fprintf(stderr, "Savefile truncated.\n"); \ + return ERROR_INVAL; \ + } else { \ + body; \ + optdata_here += (amt); \ + } + + optdata_here = optdata_begin; + + if (OPTDATA_LEFT) { + fprintf(stderr, " Savefile contains xl domain config%s\n", + !!(hdr.mandatory_flags & XL_MANDATORY_FLAG_JSON) + ? " in JSON format" : ""); + WITH_OPTDATA(4, { + memcpy(u32buf.b, optdata_here, 4); + config_len = u32buf.u32; + }); + WITH_OPTDATA(config_len, { + config_data = xmalloc(config_len); + memcpy(config_data, optdata_here, config_len); + }); + } + + } + + if (config_file) { + free(config_data); config_data = 0; + /* /dev/null represents special case (read config. from command line) */ + if (!strcmp(config_file, "/dev/null")) { + config_len = 0; + } else { + ret = libxl_read_file_contents(ctx, config_file, + &config_data, &config_len); + if (ret) { fprintf(stderr, "Failed to read config file: %s: %s\n", + config_file, strerror(errno)); return ERROR_FAIL; } + } + if (!restoring && extra_config && strlen(extra_config)) { + if (config_len > INT_MAX - (strlen(extra_config) + 2 + 1)) { + fprintf(stderr, "Failed to attach extra configuration\n"); + return ERROR_FAIL; + } + /* allocate space for the extra config plus two EOLs plus \0 */ + config_data = xrealloc(config_data, config_len + + strlen(extra_config) + 2 + 1); + config_len += sprintf(config_data + config_len, "\n%s\n", + extra_config); + } + config_source=config_file; + config_in_json = false; + } else { + if (!config_data) { + fprintf(stderr, "Config file not specified and" + " none in save file\n"); + return ERROR_INVAL; + } + config_source = ""; + config_in_json = !!(hdr.mandatory_flags & XL_MANDATORY_FLAG_JSON); + } + + if (!dom_info->quiet) + fprintf(stderr, "Parsing config from %s\n", config_source); + + if (config_in_json) { + libxl_domain_config_from_json(ctx, &d_config, + (const char *)config_data); + } else { + parse_config_data(config_source, config_data, config_len, &d_config); + } + + if (migrate_fd >= 0) { + if (d_config.c_info.name) { + /* when we receive a domain we get its name from the config + * file; and we receive it to a temporary name */ + assert(!common_domname); + + common_domname = d_config.c_info.name; + d_config.c_info.name = 0; /* steals allocation from config */ + + xasprintf(&d_config.c_info.name, "%s--incoming", common_domname); + *dom_info->migration_domname_r = strdup(d_config.c_info.name); + } + } + + if (debug || dom_info->dryrun) { + FILE *cfg_print_fh = (debug && !dom_info->dryrun) ? stderr : stdout; + if (default_output_format == OUTPUT_FORMAT_SXP) { + printf_info_sexp(-1, &d_config, cfg_print_fh); + } else { + char *json = libxl_domain_config_to_json(ctx, &d_config); + if (!json) { + fprintf(stderr, + "Failed to convert domain configuration to JSON\n"); + exit(1); + } + fputs(json, cfg_print_fh); + free(json); + xflush_stream(cfg_print_fh); + } + } + + + ret = 0; + if (dom_info->dryrun) + goto out; + +start: + assert(domid == INVALID_DOMID); + + rc = acquire_lock(); + if (rc < 0) + goto error_out; + + if (domid_soft_reset == INVALID_DOMID) { + if (!freemem(domid, &d_config.b_info)) { + fprintf(stderr, "failed to free memory for the domain\n"); + ret = ERROR_FAIL; + goto error_out; + } + } + + libxl_asyncprogress_how autoconnect_console_how_buf; + if ( dom_info->console_autoconnect ) { + if (libxl_pipe(ctx, notify_pipe)) { + ret = ERROR_FAIL; + goto error_out; + } + autoconnect_console_how_buf.callback = autoconnect_console; + autoconnect_console_how_buf.for_callback = ¬ify_pipe[1]; + autoconnect_console_how = &autoconnect_console_how_buf; + }else{ + autoconnect_console_how = 0; + } + + if ( restoring ) { + libxl_domain_restore_params params; + + libxl_domain_restore_params_init(¶ms); + + params.checkpointed_stream = dom_info->checkpointed_stream; + params.stream_version = + (hdr.mandatory_flags & XL_MANDATORY_FLAG_STREAMv2) ? 2 : 1; + params.colo_proxy_script = dom_info->colo_proxy_script; + + ret = libxl_domain_create_restore(ctx, &d_config, + &domid, restore_fd, + send_back_fd, ¶ms, + 0, autoconnect_console_how); + + libxl_domain_restore_params_dispose(¶ms); + + /* + * On subsequent reboot etc we should create the domain, not + * restore/migrate-receive it again. + */ + restoring = 0; + } else if (domid_soft_reset != INVALID_DOMID) { + /* Do soft reset. */ + ret = libxl_domain_soft_reset(ctx, &d_config, domid_soft_reset, + 0, autoconnect_console_how); + domid = domid_soft_reset; + domid_soft_reset = INVALID_DOMID; + } else { + ret = libxl_domain_create_new(ctx, &d_config, &domid, + 0, autoconnect_console_how); + } + if ( ret ) + goto error_out; + + release_lock(); + + if (restore_fd_to_close >= 0) { + if (close(restore_fd_to_close)) + fprintf(stderr, "Failed to close restoring file, fd %d, errno %d\n", + restore_fd_to_close, errno); + restore_fd_to_close = -1; + } + + if (autoconnect_console_how) { + char buf[1]; + int r; + + /* Try to get notification from xenconsole. Just move on if + * error occurs -- it's only minor annoyance if console + * doesn't show up. + */ + do { + r = read(notify_pipe[0], buf, 1); + } while (r == -1 && errno == EINTR); + + if (r == -1) + fprintf(stderr, + "Failed to get notification from xenconsole: %s\n", + strerror(errno)); + else if (r == 0) + fprintf(stderr, "Got EOF from xenconsole notification fd\n"); + else if (r == 1 && buf[0] != 0x00) + fprintf(stderr, "Got unexpected response from xenconsole: %#x\n", + buf[0]); + + close(notify_pipe[0]); + close(notify_pipe[1]); + notify_pipe[0] = notify_pipe[1] = -1; + } + + if (!paused) + libxl_domain_unpause(ctx, domid); + + ret = domid; /* caller gets success in parent */ + if (!daemonize && !monitor) + goto out; + + if (dom_info->vnc) + autoconnect_vncviewer(domid, vncautopass); + + if (need_daemon) { + char *name; + + xasprintf(&name, "xl-%s", d_config.c_info.name); + ret = do_daemonize(name, NULL); + free(name); + if (ret) { + ret = (ret == 1) ? domid : ret; + goto out; + } + need_daemon = 0; + } + LOG("Waiting for domain %s (domid %u) to die [pid %ld]", + d_config.c_info.name, domid, (long)getpid()); + + ret = libxl_evenable_domain_death(ctx, domid, 0, &deathw); + if (ret) goto out; + + if (!diskws) { + diskws = xmalloc(sizeof(*diskws) * d_config.num_disks); + for (i = 0; i < d_config.num_disks; i++) + diskws[i] = NULL; + num_diskws = d_config.num_disks; + } + for (i = 0; i < num_diskws; i++) { + if (d_config.disks[i].removable) { + ret = libxl_evenable_disk_eject(ctx, domid, d_config.disks[i].vdev, + 0, &diskws[i]); + if (ret) goto out; + } + } + while (1) { + libxl_event *event; + ret = domain_wait_event(domid, &event); + if (ret) goto out; + + switch (event->type) { + + case LIBXL_EVENT_TYPE_DOMAIN_SHUTDOWN: + LOG("Domain %u has shut down, reason code %d 0x%x", domid, + event->u.domain_shutdown.shutdown_reason, + event->u.domain_shutdown.shutdown_reason); + switch (handle_domain_death(&domid, event, &d_config)) { + case DOMAIN_RESTART_SOFT_RESET: + domid_soft_reset = domid; + domid = INVALID_DOMID; + /* fall through */ + case DOMAIN_RESTART_RENAME: + if (domid_soft_reset == INVALID_DOMID && + !preserve_domain(&domid, event, &d_config)) { + libxl_event_free(ctx, event); + /* If we fail then exit leaving the old domain in place. */ + ret = -1; + goto out; + } + + /* Otherwise fall through and restart. */ + case DOMAIN_RESTART_NORMAL: + libxl_event_free(ctx, event); + libxl_evdisable_domain_death(ctx, deathw); + deathw = NULL; + evdisable_disk_ejects(diskws, num_diskws); + free(diskws); + diskws = NULL; + num_diskws = 0; + /* discard any other events which may have been generated */ + while (!(ret = libxl_event_check(ctx, &event, + LIBXL_EVENTMASK_ALL, 0,0))) { + libxl_event_free(ctx, event); + } + if (ret != ERROR_NOT_READY) { + LOG("warning, libxl_event_check (cleanup) failed (rc=%d)", + ret); + } + + /* + * Do not attempt to reconnect if we come round again due to a + * guest reboot -- the stdin/out will be disconnected by then. + */ + dom_info->console_autoconnect = 0; + + /* Some settings only make sense on first boot. */ + paused = 0; + if (common_domname + && strcmp(d_config.c_info.name, common_domname)) { + d_config.c_info.name = strdup(common_domname); + } + + /* + * XXX FIXME: If this sleep is not there then domain + * re-creation fails sometimes. + */ + LOG("Done. Rebooting now"); + sleep(2); + goto start; + + case DOMAIN_RESTART_NONE: + LOG("Done. Exiting now"); + libxl_event_free(ctx, event); + ret = 0; + goto out; + + default: + abort(); + } + + case LIBXL_EVENT_TYPE_DOMAIN_DEATH: + LOG("Domain %u has been destroyed.", domid); + libxl_event_free(ctx, event); + ret = 0; + goto out; + + case LIBXL_EVENT_TYPE_DISK_EJECT: + /* XXX what is this for? */ + libxl_cdrom_insert(ctx, domid, &event->u.disk_eject.disk, NULL); + break; + + default:; + char *evstr = libxl_event_to_json(ctx, event); + LOG("warning, got unexpected event type %d, event=%s", + event->type, evstr); + free(evstr); + } + + libxl_event_free(ctx, event); + } + +error_out: + release_lock(); + if (libxl_domid_valid_guest(domid)) { + libxl_domain_destroy(ctx, domid, 0); + domid = INVALID_DOMID; + } + +out: + if (restore_fd_to_close >= 0) { + if (close(restore_fd_to_close)) + fprintf(stderr, "Failed to close restoring file, fd %d, errno %d\n", + restore_fd_to_close, errno); + restore_fd_to_close = -1; + } + + if (logfile != 2) + close(logfile); + + libxl_domain_config_dispose(&d_config); + + free(config_data); + + console_child_report(child_console); + + if (deathw) + libxl_evdisable_domain_death(ctx, deathw); + if (diskws) { + evdisable_disk_ejects(diskws, d_config.num_disks); + free(diskws); + } + + /* + * If we have daemonized then do not return to the caller -- this has + * already happened in the parent. + */ + if ( daemonize && !need_daemon ) + exit(ret); + + return ret; +} + +int main_create(int argc, char **argv) +{ + const char *filename = NULL; + struct domain_create dom_info; + int paused = 0, debug = 0, daemonize = 1, console_autoconnect = 0, + quiet = 0, monitor = 1, vnc = 0, vncautopass = 0; + int opt, rc; + static struct option opts[] = { + {"dryrun", 0, 0, 'n'}, + {"quiet", 0, 0, 'q'}, + {"defconfig", 1, 0, 'f'}, + {"vncviewer", 0, 0, 'V'}, + {"vncviewer-autopass", 0, 0, 'A'}, + COMMON_LONG_OPTS + }; + + dom_info.extra_config = NULL; + + if (argv[1] && argv[1][0] != '-' && !strchr(argv[1], '=')) { + filename = argv[1]; + argc--; argv++; + } + + SWITCH_FOREACH_OPT(opt, "Fnqf:pcdeVA", opts, "create", 0) { + case 'f': + filename = optarg; + break; + case 'p': + paused = 1; + break; + case 'c': + console_autoconnect = 1; + break; + case 'd': + debug = 1; + break; + case 'F': + daemonize = 0; + break; + case 'e': + daemonize = 0; + monitor = 0; + break; + case 'n': + dryrun_only = 1; + break; + case 'q': + quiet = 1; + break; + case 'V': + vnc = 1; + break; + case 'A': + vnc = vncautopass = 1; + break; + } + + memset(&dom_info, 0, sizeof(dom_info)); + + for (; optind < argc; optind++) { + if (strchr(argv[optind], '=') != NULL) { + xstring_realloc_append(&dom_info.extra_config, argv[optind]); + xstring_realloc_append(&dom_info.extra_config, "\n"); + } else if (!filename) { + filename = argv[optind]; + } else { + help("create"); + free(dom_info.extra_config); + return 2; + } + } + + dom_info.debug = debug; + dom_info.daemonize = daemonize; + dom_info.monitor = monitor; + dom_info.paused = paused; + dom_info.dryrun = dryrun_only; + dom_info.quiet = quiet; + dom_info.config_file = filename; + dom_info.migrate_fd = -1; + dom_info.send_back_fd = -1; + dom_info.vnc = vnc; + dom_info.vncautopass = vncautopass; + dom_info.console_autoconnect = console_autoconnect; + + rc = create_domain(&dom_info); + if (rc < 0) { + free(dom_info.extra_config); + return -rc; + } + + free(dom_info.extra_config); + return 0; +} + +/* + * Local variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */