/* Hotplug scripts caller functions */ static int libxl__hotplug(libxl__gc *gc, libxl__device *dev, char ***args, libxl__device_action action) { char *be_path = libxl__device_backend_path(gc, dev); char *script; int nr = 0, rc = 0, arraysize = 4; script = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s", be_path, "script")); if (!script) { LOGEV(ERROR, errno, "unable to read script from %s", be_path); rc = ERROR_FAIL; goto out; } GCNEW_ARRAY(*args, arraysize); (*args)[nr++] = script; (*args)[nr++] = be_path; (*args)[nr++] = GCSPRINTF("%d", action == LIBXL__DEVICE_ACTION_ADD ? XenbusStateInitWait : XenbusStateClosed); (*args)[nr++] = NULL; assert(nr == arraysize); out: return rc; }
static int libxl__append_nic_list(libxl__gc *gc, uint32_t domid, libxl_device_nic **nics, int *nnics) { char *libxl_dir_path = NULL; char **dir = NULL; unsigned int n = 0; libxl_device_nic *pnic = NULL, *pnic_end = NULL; int rc; libxl_dir_path = GCSPRINTF("%s/device/vif", libxl__xs_libxl_path(gc, domid)); dir = libxl__xs_directory(gc, XBT_NULL, libxl_dir_path, &n); if (dir && n) { libxl_device_nic *tmp; tmp = realloc(*nics, sizeof (libxl_device_nic) * (*nnics + n)); if (tmp == NULL) return ERROR_NOMEM; *nics = tmp; pnic = *nics + *nnics; pnic_end = *nics + *nnics + n; for (; pnic < pnic_end; pnic++, dir++) { const char *p; p = GCSPRINTF("%s/%s", libxl_dir_path, *dir); rc = libxl__device_nic_from_xenstore(gc, p, pnic); if (rc) goto out; } *nnics += n; } return 0; out: return rc; }
/* out_target_memkb and out_max_memkb can be NULL */ int libxl__get_memory_target(libxl__gc *gc, uint32_t domid, uint64_t *out_target_memkb, uint64_t *out_max_memkb) { int rc; char *target = NULL, *static_max = NULL, *endptr = NULL; char *dompath = libxl__xs_get_dompath(gc, domid); uint64_t target_memkb, max_memkb; target = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/memory/target", dompath)); static_max = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/memory/static-max", dompath)); rc = ERROR_FAIL; if ((!target || !static_max) && !domid) { rc = libxl__fill_dom0_memory_info(gc, &target_memkb, &max_memkb); if (rc < 0) goto out; } else if (!target) { LOGED(ERROR, domid, "Cannot get target memory info from %s/memory/target", dompath); goto out; } else if (!static_max) { LOGED(ERROR, domid, "Cannot get target memory info from %s/memory/static-max", dompath); goto out; } else { target_memkb = strtoull(target, &endptr, 10); if (*endptr != '\0') { LOGED(ERROR, domid, "Invalid memory target %s from %s/memory/target\n", target, dompath); goto out; } max_memkb = strtoull(static_max, &endptr, 10); if (*endptr != '\0') { LOGED(ERROR, domid, "Invalid memory target %s from %s/memory/static-max\n", static_max, dompath); goto out; } } if (out_target_memkb) *out_target_memkb = target_memkb; if (out_max_memkb) *out_max_memkb = max_memkb; rc = 0; out: return rc; }
/* * the script needs the following env & args * $vifname * $XENBUS_PATH (/libxl/<domid>/remus/netbuf/<devid>/) * $REMUS_IFB (for teardown) * setup/teardown as command line arg. */ static void setup_async_exec(libxl__checkpoint_device *dev, char *op) { int arraysize, nr = 0; char **env = NULL, **args = NULL; libxl__remus_device_nic *remus_nic = dev->concrete_data; libxl__checkpoint_devices_state *cds = dev->cds; libxl__async_exec_state *aes = &dev->aodev.aes; libxl__remus_state *rs = cds->concrete_data; STATE_AO_GC(cds->ao); /* Convenience aliases */ char *const script = libxl__strdup(gc, rs->netbufscript); const uint32_t domid = cds->domid; const int dev_id = remus_nic->devid; const char *const vif = remus_nic->vif; const char *const ifb = remus_nic->ifb; arraysize = 7; GCNEW_ARRAY(env, arraysize); env[nr++] = "vifname"; env[nr++] = libxl__strdup(gc, vif); env[nr++] = "XENBUS_PATH"; env[nr++] = GCSPRINTF("%s/remus/netbuf/%d", libxl__xs_libxl_path(gc, domid), dev_id); if (!strcmp(op, "teardown") && ifb) { env[nr++] = "REMUS_IFB"; env[nr++] = libxl__strdup(gc, ifb); } env[nr++] = NULL; assert(nr <= arraysize); arraysize = 3; nr = 0; GCNEW_ARRAY(args, arraysize); args[nr++] = script; args[nr++] = op; args[nr++] = NULL; assert(nr == arraysize); aes->ao = dev->cds->ao; aes->what = GCSPRINTF("%s %s", args[0], args[1]); aes->env = env; aes->args = args; aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000; aes->stdfds[0] = -1; aes->stdfds[1] = -1; aes->stdfds[2] = -1; if (!strcmp(op, "teardown")) aes->callback = netbuf_teardown_script_cb; else aes->callback = netbuf_setup_script_cb; }
const char *libxl__device_nic_devname(libxl__gc *gc, uint32_t domid, uint32_t devid, libxl_nic_type type) { switch (type) { case LIBXL_NIC_TYPE_VIF: return GCSPRINTF(NETBACK_NIC_NAME, domid, devid); case LIBXL_NIC_TYPE_VIF_IOEMU: return GCSPRINTF(NETBACK_NIC_NAME TAP_DEVICE_SUFFIX, domid, devid); default: abort(); } }
int libxl__save_emulator_xenstore_data(libxl__domain_save_state *dss, char **callee_buf, uint32_t *callee_len) { STATE_AO_GC(dss->ao); const char *xs_root; char **entries, *buf = NULL; unsigned int nr_entries, i, j, len = 0; int rc; const uint32_t domid = dss->domid; const uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid); xs_root = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, ""); entries = libxl__xs_directory(gc, 0, GCSPRINTF("%s/physmap", xs_root), &nr_entries); if (!entries || nr_entries == 0) { rc = 0; goto out; } for (i = 0; i < nr_entries; ++i) { static const char *const physmap_subkeys[] = { "start_addr", "size", "name" }; for (j = 0; j < ARRAY_SIZE(physmap_subkeys); ++j) { const char *key = GCSPRINTF("physmap/%s/%s", entries[i], physmap_subkeys[j]); const char *val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s", xs_root, key)); if (!val) { rc = ERROR_FAIL; goto out; } append_string(gc, &buf, &len, key); append_string(gc, &buf, &len, val); } } rc = 0; out: if (!rc) { *callee_buf = buf; *callee_len = len; } return rc; }
static int libxl__hotplug_disk(libxl__gc *gc, libxl__device *dev, char ***args, char ***env, libxl__device_action action) { char *be_path = libxl__device_backend_path(gc, dev); char *script; int nr = 0, rc = 0; script = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s", be_path, "script")); if (!script) { LOGEV(ERROR, errno, "unable to read script from %s", be_path); rc = ERROR_FAIL; goto error; } *env = get_hotplug_env(gc, script, dev); if (!*env) { rc = ERROR_FAIL; goto error; } const int arraysize = 3; GCNEW_ARRAY(*args, arraysize); (*args)[nr++] = script; (*args)[nr++] = action == DEVICE_CONNECT ? "add" : "remove"; (*args)[nr++] = NULL; assert(nr == arraysize); rc = 1; error: return rc; }
static void helper_exited(libxl__egc *egc, libxl__ev_child *ch, pid_t pid, int status) { libxl__save_helper_state *shs = CONTAINER_OF(ch, *shs, child); STATE_AO_GC(shs->ao); /* Convenience aliases */ const uint32_t domid = shs->domid; const char *what = GCSPRINTF("domain %"PRIu32" save/restore helper", domid); if (status) { libxl_report_child_exitstatus(CTX, XTL_ERROR, what, pid, status); shs->rc = ERROR_FAIL; } if (shs->need_results) { if (!shs->rc) LOG(ERROR,"%s exited without providing results",what); shs->rc = ERROR_FAIL; } if (!shs->completed) { if (!shs->rc) LOG(ERROR,"%s exited without signaling completion",what); shs->rc = ERROR_FAIL; } helper_done(egc, shs); return; }
int init_subkind_drbd_disk(libxl__remus_devices_state *rds) { STATE_AO_GC(rds->ao); rds->drbd_probe_script = GCSPRINTF("%s/block-drbd-probe", libxl__xen_script_dir_path()); return 0; }
static int libxl__hotplug_nic(libxl__gc *gc, libxl__device *dev, char ***args, char ***env, libxl__device_action action, int num_exec) { char *be_path = libxl__device_backend_path(gc, dev); char *script; int nr = 0, rc = 0; libxl_nic_type nictype; script = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s", be_path, "script")); if (!script) { LOGE(ERROR, "unable to read script from %s", be_path); rc = ERROR_FAIL; goto out; } rc = libxl__nic_type(gc, dev, &nictype); if (rc) { LOG(ERROR, "error when fetching nic type"); rc = ERROR_FAIL; goto out; } if (nictype == LIBXL_NIC_TYPE_VIF && num_exec != 0) { rc = 0; goto out; } *env = get_hotplug_env(gc, script, dev); if (!env) { rc = ERROR_FAIL; goto out; } const int arraysize = 4; GCNEW_ARRAY(*args, arraysize); (*args)[nr++] = script; if (nictype == LIBXL_NIC_TYPE_VIF_IOEMU && num_exec) { (*args)[nr++] = action == DEVICE_CONNECT ? "add" : "remove"; (*args)[nr++] = "type_if=tap"; (*args)[nr++] = NULL; } else { (*args)[nr++] = action == DEVICE_CONNECT ? "online" : "offline"; (*args)[nr++] = "type_if=vif"; (*args)[nr++] = NULL; } assert(nr == arraysize); rc = 1; out: return rc; }
int libxl__restore_emulator_xenstore_data(libxl__domain_create_state *dcs, const char *ptr, uint32_t size) { STATE_AO_GC(dcs->ao); const char *next = ptr, *end = ptr + size, *key, *val; int rc; const uint32_t domid = dcs->guest_domid; const uint32_t dm_domid = libxl_get_stubdom_id(CTX, domid); const char *xs_root = DEVICE_MODEL_XS_PATH(gc, dm_domid, domid, ""); while (next < end) { key = next; next = next_string(next, end); /* Sanitise 'key'. */ if (!next) { rc = ERROR_FAIL; LOG(ERROR, "Key in xenstore data not NUL terminated"); goto out; } if (key[0] == '\0') { rc = ERROR_FAIL; LOG(ERROR, "empty key found in xenstore data"); goto out; } if (key[0] == '/') { rc = ERROR_FAIL; LOG(ERROR, "Key in xenstore data not relative"); goto out; } val = next; next = next_string(next, end); /* Sanitise 'val'. */ if (!next) { rc = ERROR_FAIL; LOG(ERROR, "Val in xenstore data not NUL terminated"); goto out; } libxl__xs_printf(gc, XBT_NULL, GCSPRINTF("%s/%s", xs_root, key), "%s", val); } rc = 0; out: return rc; }
char *libxl__device_model_xs_path(libxl__gc *gc, uint32_t dm_domid, uint32_t domid, const char *format, ...) { char *s, *fmt; va_list ap; fmt = GCSPRINTF("/local/domain/%u/device-model/%u%s", dm_domid, domid, format); va_start(ap, format); s = libxl__vsprintf(gc, fmt, ap); va_end(ap); return s; }
int libxl_get_stubdom_id(libxl_ctx *ctx, int guest_domid) { GC_INIT(ctx); char * stubdom_id_s; int ret; stubdom_id_s = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/image/device-model-domid", libxl__xs_get_dompath(gc, guest_domid))); if (stubdom_id_s) ret = atoi(stubdom_id_s); else ret = 0; GC_FREE; return ret; }
static void libxl__dm_vifs_from_hvm_guest_config(libxl__gc *gc, libxl_domain_config * const guest_config, libxl_domain_config *dm_config) { int i, nr = guest_config->num_nics; GCNEW_ARRAY(dm_config->nics, nr); for (i=0; i<nr; i++) { dm_config->nics[i] = guest_config->nics[i]; dm_config->nics[i].nictype = LIBXL_NIC_TYPE_VIF; if (dm_config->nics[i].ifname) dm_config->nics[i].ifname = GCSPRINTF("%s" TAP_DEVICE_SUFFIX, dm_config->nics[i].ifname); } dm_config->num_nics = nr; }
int init_subkind_nic(libxl__checkpoint_devices_state *cds) { int rc, ret; libxl__domain_save_state *dss = CONTAINER_OF(cds, *dss, cds); libxl__remus_state *rs = cds->concrete_data; STATE_AO_GC(cds->ao); rs->nlsock = nl_socket_alloc(); if (!rs->nlsock) { LOGD(ERROR, dss->domid, "cannot allocate nl socket"); rc = ERROR_FAIL; goto out; } ret = nl_connect(rs->nlsock, NETLINK_ROUTE); if (ret) { LOGD(ERROR, dss->domid, "failed to open netlink socket: %s", nl_geterror(ret)); rc = ERROR_FAIL; goto out; } /* get list of all qdiscs installed on network devs. */ ret = rtnl_qdisc_alloc_cache(rs->nlsock, &rs->qdisc_cache); if (ret) { LOGD(ERROR, dss->domid, "failed to allocate qdisc cache: %s", nl_geterror(ret)); rc = ERROR_FAIL; goto out; } if (dss->remus->netbufscript) { rs->netbufscript = libxl__strdup(gc, dss->remus->netbufscript); } else { rs->netbufscript = GCSPRINTF("%s/remus-netbuf-setup", libxl__xen_script_dir_path()); } rc = 0; out: return rc; }
int libxl__device_model_version_running(libxl__gc *gc, uint32_t domid) { char *path = NULL; char *dm_version = NULL; libxl_device_model_version value; path = libxl__xs_libxl_path(gc, domid); path = GCSPRINTF("%s/dm-version", path); dm_version = libxl__xs_read(gc, XBT_NULL, path); if (!dm_version) { return LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL; } if (libxl_device_model_version_from_string(dm_version, &value) < 0) { LOGD(ERROR, domid, "fatal: %s contain a wrong value (%s)", path, dm_version); return -1; } return value; }
int libxl_is_stubdom(libxl_ctx *ctx, uint32_t domid, uint32_t *target_domid) { GC_INIT(ctx); char *target, *endptr; uint32_t value; int ret = 0; target = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/target", libxl__xs_get_dompath(gc, domid))); if (!target) goto out; value = strtol(target, &endptr, 10); if (*endptr != '\0') goto out; if (target_domid) *target_domid = value; ret = 1; out: GC_FREE; return ret; }
int libxl_devid_to_device_nic(libxl_ctx *ctx, uint32_t domid, int devid, libxl_device_nic *nic) { GC_INIT(ctx); char *libxl_dom_path, *libxl_path; int rc = ERROR_FAIL; libxl_device_nic_init(nic); libxl_dom_path = libxl__xs_libxl_path(gc, domid); if (!libxl_dom_path) goto out; libxl_path = GCSPRINTF("%s/device/vif/%d", libxl_dom_path, devid); rc = libxl__device_nic_from_xenstore(gc, libxl_path, nic); if (rc) goto out; rc = 0; out: GC_FREE; return rc; }
static void match_async_exec(libxl__egc *egc, libxl__remus_device *dev) { int arraysize, nr = 0, rc; const libxl_device_disk *disk = dev->backend_dev; libxl__async_exec_state *aes = &dev->aodev.aes; STATE_AO_GC(dev->rds->ao); /* setup env & args */ arraysize = 1; GCNEW_ARRAY(aes->env, arraysize); aes->env[nr++] = NULL; assert(nr <= arraysize); arraysize = 3; nr = 0; GCNEW_ARRAY(aes->args, arraysize); aes->args[nr++] = dev->rds->drbd_probe_script; aes->args[nr++] = disk->pdev_path; aes->args[nr++] = NULL; assert(nr <= arraysize); aes->ao = dev->rds->ao; aes->what = GCSPRINTF("%s %s", aes->args[0], aes->args[1]); aes->timeout_ms = LIBXL_HOTPLUG_TIMEOUT * 1000; aes->callback = match_async_exec_cb; aes->stdfds[0] = -1; aes->stdfds[1] = -1; aes->stdfds[2] = -1; rc = libxl__async_exec_start(gc, aes); if (rc) goto out; return; out: dev->aodev.rc = rc; dev->aodev.callback(egc, &dev->aodev); }
int libxl_device_nic_getinfo(libxl_ctx *ctx, uint32_t domid, libxl_device_nic *nic, libxl_nicinfo *nicinfo) { GC_INIT(ctx); char *dompath, *nicpath, *libxl_path; char *val; int rc; dompath = libxl__xs_get_dompath(gc, domid); nicinfo->devid = nic->devid; nicpath = GCSPRINTF("%s/device/vif/%d", dompath, nicinfo->devid); libxl_path = GCSPRINTF("%s/device/vif/%d", libxl__xs_libxl_path(gc, domid), nicinfo->devid); nicinfo->backend = xs_read(ctx->xsh, XBT_NULL, GCSPRINTF("%s/backend", libxl_path), NULL); if (!nicinfo->backend) { GC_FREE; return ERROR_FAIL; } rc = libxl__backendpath_parse_domid(gc, nicinfo->backend, &nicinfo->backend_id); if (rc) goto out; val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", nicpath)); nicinfo->state = val ? strtoul(val, NULL, 10) : -1; val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", nicpath)); nicinfo->evtch = val ? strtoul(val, NULL, 10) : -1; val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/tx-ring-ref", nicpath)); nicinfo->rref_tx = val ? strtoul(val, NULL, 10) : -1; val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/rx-ring-ref", nicpath)); nicinfo->rref_rx = val ? strtoul(val, NULL, 10) : -1; nicinfo->frontend = libxl__strdup(NOGC, nicpath); nicinfo->frontend_id = domid; rc = 0; out: GC_FREE; return rc; }
/* * If the device has a vifname, then use that instead of * the vifX.Y format. * it must ONLY be used for remus because if driver domains * were in use it would constitute a security vulnerability. */ static const char *get_vifname(libxl__checkpoint_device *dev, const libxl_device_nic *nic) { const char *vifname = NULL; const char *path; int rc; STATE_AO_GC(dev->cds->ao); /* Convenience aliases */ const uint32_t domid = dev->cds->domid; path = GCSPRINTF("%s/backend/vif/%d/%d/vifname", libxl__xs_get_dompath(gc, 0), domid, nic->devid); rc = libxl__xs_read_checked(gc, XBT_NULL, path, &vifname); if (!rc && !vifname) { vifname = libxl__device_nic_devname(gc, domid, nic->devid, nic->nictype); } return vifname; }
static void bootloader_gotptys(libxl__egc *egc, libxl__openpty_state *op) { libxl__bootloader_state *bl = CONTAINER_OF(op, *bl, openpty); STATE_AO_GC(bl->ao); int rc, r; char *const env[] = { "TERM", "vt100", NULL }; if (bl->openpty.rc) { rc = bl->openpty.rc; goto out; } /* * We need to present the bootloader's tty as a pty slave that xenconsole * can access. Since the bootloader itself needs a pty slave, * we end up with a connection like this: * * xenconsole -- (slave pty1 master) <-> (master pty2 slave) -- bootloader * * where we copy characters between the two master fds, as well as * listening on the bootloader's fifo for the results. */ char *dom_console_xs_path; char dom_console_slave_tty_path[PATH_MAX]; rc = setup_xenconsoled_pty(egc, bl, &dom_console_slave_tty_path[0], sizeof(dom_console_slave_tty_path)); if (rc) goto out; char *dompath = libxl__xs_get_dompath(gc, bl->domid); if (!dompath) { rc = ERROR_FAIL; goto out; } dom_console_xs_path = GCSPRINTF("%s/console/tty", dompath); rc = libxl__xs_write(gc, XBT_NULL, dom_console_xs_path, "%s", dom_console_slave_tty_path); if (rc) { LOGE(ERROR,"xs write console path %s := %s failed", dom_console_xs_path, dom_console_slave_tty_path); rc = ERROR_FAIL; goto out; } bl->deathcheck.what = "stopping bootloader"; bl->deathcheck.domid = bl->domid; bl->deathcheck.callback = bootloader_domaindeath; rc = libxl__domaindeathcheck_start(gc, &bl->deathcheck); if (rc) goto out; if (bl->console_available) bl->console_available(egc, bl); int bootloader_master = libxl__carefd_fd(bl->ptys[0].master); int xenconsole_master = libxl__carefd_fd(bl->ptys[1].master); libxl_fd_set_nonblock(CTX, bootloader_master, 1); libxl_fd_set_nonblock(CTX, xenconsole_master, 1); bl->keystrokes.writefd = bl->display.readfd = bootloader_master; bl->keystrokes.writewhat = bl->display.readwhat = "bootloader pty"; bl->keystrokes.readfd = bl->display.writefd = xenconsole_master; bl->keystrokes.readwhat = bl->display.writewhat = "xenconsole client pty"; bl->keystrokes.ao = ao; bl->keystrokes.maxsz = BOOTLOADER_BUF_OUT; bl->keystrokes.copywhat = GCSPRINTF("bootloader input for domain %"PRIu32, bl->domid); bl->keystrokes.callback = bootloader_keystrokes_copyfail; bl->keystrokes.callback_pollhup = bootloader_keystrokes_copyfail; /* pollhup gets called with errnoval==-1 which is not otherwise * possible since errnos are nonnegative, so it's unambiguous */ rc = libxl__datacopier_start(&bl->keystrokes); if (rc) goto out; bl->display.ao = ao; bl->display.maxsz = BOOTLOADER_BUF_IN; bl->display.copywhat = GCSPRINTF("bootloader output for domain %"PRIu32, bl->domid); bl->display.callback = bootloader_display_copyfail; bl->display.callback_pollhup = bootloader_display_copyfail; rc = libxl__datacopier_start(&bl->display); if (rc) goto out; LOG(DEBUG, "executing bootloader: %s", bl->args[0]); for (const char **blarg = bl->args; *blarg; blarg++) LOG(DEBUG, " bootloader arg: %s", *blarg); struct termios termattr; pid_t pid = libxl__ev_child_fork(gc, &bl->child, bootloader_finished); if (pid == -1) { rc = ERROR_FAIL; goto out; } if (!pid) { /* child */ r = login_tty(libxl__carefd_fd(bl->ptys[0].slave)); if (r) { LOGE(ERROR, "login_tty failed"); exit(-1); } libxl__exec(gc, -1, -1, -1, bl->args[0], (char **) bl->args, env); exit(-1); } /* parent */ /* * On Solaris, the master pty side does not have terminal semantics, * so don't try to set any attributes, as it will fail. */ #if !defined(__sun__) tcgetattr(bootloader_master, &termattr); cfmakeraw(&termattr); tcsetattr(bootloader_master, TCSANOW, &termattr); #endif return; out: bootloader_callback(egc, bl, rc); }
void libxl__bootloader_run(libxl__egc *egc, libxl__bootloader_state *bl) { STATE_AO_GC(bl->ao); const libxl_domain_build_info *info = bl->info; uint32_t domid = bl->domid; char *logfile_tmp = NULL; int rc, r; libxl__bootloader_init(bl); if (info->type != LIBXL_DOMAIN_TYPE_PV) { LOG(DEBUG, "not a PV domain, skipping bootloader"); rc = 0; goto out_ok; } if (!info->u.pv.bootloader) { LOG(DEBUG, "no bootloader configured, using user supplied kernel"); bl->kernel->path = bl->info->u.pv.kernel; bl->ramdisk->path = bl->info->u.pv.ramdisk; bl->cmdline = bl->info->u.pv.cmdline; rc = 0; goto out_ok; } if (!bl->disk) { LOG(ERROR, "cannot run bootloader with no boot disk"); rc = ERROR_FAIL; goto out; } bootloader_setpaths(gc, bl); const char *logfile_leaf = GCSPRINTF("bootloader.%"PRIu32, domid); rc = libxl_create_logfile(CTX, logfile_leaf, &logfile_tmp); if (rc) goto out; /* Transfer ownership of log filename to bl and the gc */ bl->logfile = logfile_tmp; libxl__ptr_add(gc, logfile_tmp); logfile_tmp = NULL; bl->display.log = fopen(bl->logfile, "a"); if (!bl->display.log) { LOGE(ERROR, "failed to create bootloader logfile %s", bl->logfile); rc = ERROR_FAIL; goto out; } for (;;) { r = mkdir(bl->outputdir, 0600); if (!r) break; if (errno == EINTR) continue; if (errno == EEXIST) break; LOGE(ERROR, "failed to create bootloader dir %s", bl->outputdir); rc = ERROR_FAIL; goto out; } for (;;) { r = open(bl->outputpath, O_WRONLY|O_CREAT|O_TRUNC, 0600); if (r>=0) { close(r); break; } if (errno == EINTR) continue; LOGE(ERROR, "failed to precreate bootloader output %s", bl->outputpath); rc = ERROR_FAIL; goto out; } /* This sets the state of the dls struct from Undefined to Idle */ libxl__device_disk_local_init(&bl->dls); bl->dls.ao = ao; bl->dls.in_disk = bl->disk; bl->dls.blkdev_start = info->blkdev_start; bl->dls.callback = bootloader_disk_attached_cb; libxl__device_disk_local_initiate_attach(egc, &bl->dls); return; out: assert(rc); out_ok: free(logfile_tmp); bootloader_callback(egc, bl, rc); }
static void bootloader_setpaths(libxl__gc *gc, libxl__bootloader_state *bl) { uint32_t domid = bl->domid; bl->outputdir = GCSPRINTF(XEN_RUN_DIR "/bootloader.%"PRIu32".d", domid); bl->outputpath = GCSPRINTF(XEN_RUN_DIR "/bootloader.%"PRIu32".out", domid); }
void libxl__spawn_local_dm(libxl__egc *egc, libxl__dm_spawn_state *dmss) { /* convenience aliases */ const int domid = dmss->guest_domid; libxl__domain_build_state *const state = dmss->build_state; libxl__spawn_state *const spawn = &dmss->spawn; STATE_AO_GC(dmss->spawn.ao); libxl_ctx *ctx = CTX; libxl_domain_config *guest_config = dmss->guest_config; const libxl_domain_create_info *c_info = &guest_config->c_info; const libxl_domain_build_info *b_info = &guest_config->b_info; const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config); char *path, *logfile; int logfile_w, null; int rc; char **args, **arg; xs_transaction_t t; char *vm_path; char **pass_stuff; const char *dm; if (libxl_defbool_val(b_info->device_model_stubdomain)) { abort(); } dm = libxl__domain_device_model(gc, b_info); if (!dm) { rc = ERROR_FAIL; goto out; } if (access(dm, X_OK) < 0) { LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "device model %s is not executable", dm); rc = ERROR_FAIL; goto out; } args = libxl__build_device_model_args(gc, dm, domid, guest_config, state); if (!args) { rc = ERROR_FAIL; goto out; } if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) { path = xs_get_domain_path(ctx->xsh, domid); libxl__xs_write(gc, XBT_NULL, libxl__sprintf(gc, "%s/hvmloader/bios", path), "%s", libxl_bios_type_to_string(b_info->u.hvm.bios)); /* Disable relocating memory to make the MMIO hole larger * unless we're running qemu-traditional */ libxl__xs_write(gc, XBT_NULL, libxl__sprintf(gc, "%s/hvmloader/allow-memory-relocate", path), "%d", b_info->device_model_version==LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL); free(path); } path = libxl__sprintf(gc, "/local/domain/0/device-model/%d", domid); xs_mkdir(ctx->xsh, XBT_NULL, path); if (b_info->type == LIBXL_DOMAIN_TYPE_HVM && b_info->device_model_version == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL) libxl__xs_write(gc, XBT_NULL, libxl__sprintf(gc, "%s/disable_pf", path), "%d", !libxl_defbool_val(b_info->u.hvm.xen_platform_pci)); libxl_create_logfile(ctx, libxl__sprintf(gc, "qemu-dm-%s", c_info->name), &logfile); logfile_w = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0644); free(logfile); null = open("/dev/null", O_RDONLY); const char *dom_path = libxl__xs_get_dompath(gc, domid); spawn->pidpath = GCSPRINTF("%s/%s", dom_path, "image/device-model-pid"); if (vnc && vnc->passwd) { /* This xenstore key will only be used by qemu-xen-traditionnal. * The code to supply vncpasswd to qemu-xen is later. */ retry_transaction: /* Find uuid and the write the vnc password to xenstore for qemu. */ t = xs_transaction_start(ctx->xsh); vm_path = libxl__xs_read(gc,t,libxl__sprintf(gc, "%s/vm", dom_path)); if (vm_path) { /* Now write the vncpassword into it. */ pass_stuff = libxl__calloc(gc, 3, sizeof(char *)); pass_stuff[0] = "vncpasswd"; pass_stuff[1] = vnc->passwd; libxl__xs_writev(gc,t,vm_path,pass_stuff); if (!xs_transaction_end(ctx->xsh, t, 0)) if (errno == EAGAIN) goto retry_transaction; } } LIBXL__LOG(CTX, XTL_DEBUG, "Spawning device-model %s with arguments:", dm); for (arg = args; *arg; arg++) LIBXL__LOG(CTX, XTL_DEBUG, " %s", *arg); spawn->what = GCSPRINTF("domain %d device model", domid); spawn->xspath = GCSPRINTF("/local/domain/0/device-model/%d/state", domid); spawn->timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000; spawn->pidpath = GCSPRINTF("%s/image/device-model-pid", dom_path); spawn->midproc_cb = libxl__spawn_record_pid; spawn->confirm_cb = device_model_confirm; spawn->failure_cb = device_model_startup_failed; spawn->detached_cb = device_model_detached; rc = libxl__spawn_spawn(egc, spawn); if (rc < 0) goto out_close; if (!rc) { /* inner child */ setsid(); libxl__exec(gc, null, logfile_w, logfile_w, dm, args, NULL); } rc = 0; out_close: close(null); close(logfile_w); out: if (rc) device_model_spawn_outcome(egc, dmss, rc); }
static void run_helper(libxl__egc *egc, libxl__save_helper_state *shs, const char *mode_arg, int stream_fd, const int *preserve_fds, int num_preserve_fds, const unsigned long *argnums, int num_argnums) { STATE_AO_GC(shs->ao); const char *args[4 + num_argnums]; const char **arg = args; int i, rc; /* Resources we must free */ libxl__carefd *childs_pipes[2] = { 0,0 }; /* Convenience aliases */ const uint32_t domid = shs->domid; shs->rc = 0; shs->completed = 0; shs->pipes[0] = shs->pipes[1] = 0; libxl__ev_fd_init(&shs->readable); libxl__ev_child_init(&shs->child); shs->stdin_what = GCSPRINTF("domain %"PRIu32" save/restore helper" " stdin pipe", domid); shs->stdout_what = GCSPRINTF("domain %"PRIu32" save/restore helper" " stdout pipe", domid); *arg++ = getenv("LIBXL_SAVE_HELPER") ?: PRIVATE_BINDIR "/" "libxl-save-helper"; *arg++ = mode_arg; const char **stream_fd_arg = arg++; for (i=0; i<num_argnums; i++) *arg++ = GCSPRINTF("%lu", argnums[i]); *arg++ = 0; assert(arg == args + ARRAY_SIZE(args)); libxl__carefd_begin(); int childfd; for (childfd=0; childfd<2; childfd++) { /* Setting up the pipe for the child's fd childfd */ int fds[2]; if (libxl_pipe(CTX,fds)) { rc = ERROR_FAIL; libxl__carefd_unlock(); goto out; } int childs_end = childfd==0 ? 0 /*read*/ : 1 /*write*/; int our_end = childfd==0 ? 1 /*write*/ : 0 /*read*/; childs_pipes[childfd] = libxl__carefd_record(CTX, fds[childs_end]); shs->pipes[childfd] = libxl__carefd_record(CTX, fds[our_end]); } libxl__carefd_unlock(); pid_t pid = libxl__ev_child_fork(gc, &shs->child, helper_exited); if (!pid) { if (stream_fd <= 2) { stream_fd = dup(stream_fd); if (stream_fd < 0) { LOGE(ERROR,"dup migration stream fd"); exit(-1); } } libxl_fd_set_cloexec(CTX, stream_fd, 0); *stream_fd_arg = GCSPRINTF("%d", stream_fd); for (i=0; i<num_preserve_fds; i++) if (preserve_fds[i] >= 0) { assert(preserve_fds[i] > 2); libxl_fd_set_cloexec(CTX, preserve_fds[i], 0); } libxl__exec(gc, libxl__carefd_fd(childs_pipes[0]), libxl__carefd_fd(childs_pipes[1]), -1, args[0], (char**)args, 0); } libxl__carefd_close(childs_pipes[0]); libxl__carefd_close(childs_pipes[1]); rc = libxl__ev_fd_register(gc, &shs->readable, helper_stdout_readable, libxl__carefd_fd(shs->pipes[1]), POLLIN|POLLPRI); if (rc) goto out; return; out: libxl__carefd_close(childs_pipes[0]); libxl__carefd_close(childs_pipes[1]); helper_failed(egc, shs, rc);; }
int libxl__domain_rename(libxl__gc *gc, uint32_t domid, const char *old_name, const char *new_name, xs_transaction_t trans) { libxl_ctx *ctx = libxl__gc_owner(gc); char *dom_path = 0; const char *name_path; char *got_old_name; unsigned int got_old_len; xs_transaction_t our_trans = 0; uint32_t stub_dm_domid; const char *stub_dm_old_name = NULL, *stub_dm_new_name = NULL; int rc; libxl_dominfo info; char *uuid; const char *vm_name_path; libxl_dominfo_init(&info); dom_path = libxl__xs_get_dompath(gc, domid); if (!dom_path) goto x_nomem; name_path= GCSPRINTF("%s/name", dom_path); if (!name_path) goto x_nomem; stub_dm_domid = libxl_get_stubdom_id(CTX, domid); if (stub_dm_domid) { stub_dm_old_name = libxl__stub_dm_name(gc, old_name); stub_dm_new_name = libxl__stub_dm_name(gc, new_name); } retry_transaction: if (!trans) { trans = our_trans = xs_transaction_start(ctx->xsh); if (!our_trans) { LOGEVD(ERROR, errno, domid, "Create xs transaction for domain (re)name"); goto x_fail; } } if (!new_name) { LOGD(ERROR, domid, "New domain name not specified"); rc = ERROR_INVAL; goto x_rc; } if (new_name[0]) { /* nonempty names must be unique */ uint32_t domid_e; rc = libxl_name_to_domid(ctx, new_name, &domid_e); if (rc == ERROR_INVAL) { /* no such domain, good */ } else if (rc != 0) { LOGD(ERROR, domid, "Unexpected error checking for existing domain"); goto x_rc; } else if (domid_e == domid) { /* domain already has this name, ok (but we do still * need the rest of the code as we may need to check * old_name, for example). */ } else { LOGD(ERROR, domid, "Domain with name \"%s\" already exists.", new_name); rc = ERROR_INVAL; goto x_rc; } } if (old_name) { got_old_name = xs_read(ctx->xsh, trans, name_path, &got_old_len); if (!got_old_name) { LOGEVD(ERROR, errno, domid, "Check old name for domain allegedly named `%s'", old_name); goto x_fail; } if (strcmp(old_name, got_old_name)) { LOGD(ERROR, domid, "Allegedly named `%s' is actually named `%s' - racing ?", old_name, got_old_name); free(got_old_name); goto x_fail; } free(got_old_name); } if (!xs_write(ctx->xsh, trans, name_path, new_name, strlen(new_name))) { LOGD(ERROR, domid, "Failed to write new name `%s'" " for domain previously named `%s'", new_name, old_name); goto x_fail; } /* update /vm/<uuid>/name */ rc = libxl_domain_info(ctx, &info, domid); if (rc) goto x_rc; uuid = GCSPRINTF(LIBXL_UUID_FMT, LIBXL_UUID_BYTES(info.uuid)); vm_name_path = GCSPRINTF("/vm/%s/name", uuid); if (libxl__xs_write_checked(gc, trans, vm_name_path, new_name)) goto x_fail; if (stub_dm_domid) { rc = libxl__domain_rename(gc, stub_dm_domid, stub_dm_old_name, stub_dm_new_name, trans); if (rc) { LOGED(ERROR, domid, "Unable to rename stub-domain"); goto x_rc; } } if (our_trans) { if (!xs_transaction_end(ctx->xsh, our_trans, 0)) { trans = our_trans = 0; if (errno != EAGAIN) { LOGD(ERROR, domid, "Failed to commit new name `%s'" " for domain previously named `%s'", new_name, old_name); goto x_fail; } LOGD(DEBUG, domid, "Need to retry rename transaction" " for domain (name_path=\"%s\", new_name=\"%s\")", name_path, new_name); goto retry_transaction; } our_trans = 0; } rc = 0; x_rc: if (our_trans) xs_transaction_end(ctx->xsh, our_trans, 1); libxl_dominfo_dispose(&info); return rc; x_fail: rc = ERROR_FAIL; goto x_rc; x_nomem: rc = ERROR_NOMEM; goto x_rc; }
void libxl__colo_restore_setup(libxl__egc *egc, libxl__colo_restore_state *crs) { libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs); libxl__colo_restore_checkpoint_state *crcs; int rc = ERROR_FAIL; /* Convenience aliases */ libxl__srm_restore_autogen_callbacks *const callbacks = &dcs->srs.shs.callbacks.restore.a; const int domid = crs->domid; STATE_AO_GC(crs->ao); GCNEW(crcs); crs->crcs = crcs; crcs->crs = crs; crs->qdisk_setuped = false; crs->qdisk_used = false; if (dcs->colo_proxy_script) crs->colo_proxy_script = libxl__strdup(gc, dcs->colo_proxy_script); else crs->colo_proxy_script = GCSPRINTF("%s/colo-proxy-setup", libxl__xen_script_dir_path()); /* setup dsps */ crcs->dsps.ao = ao; crcs->dsps.domid = domid; if (init_dsps(&crcs->dsps)) goto out; callbacks->postcopy = libxl__colo_restore_domain_resume_callback; callbacks->wait_checkpoint = libxl__colo_restore_domain_wait_checkpoint_callback; callbacks->suspend = libxl__colo_restore_domain_suspend_callback; callbacks->checkpoint = libxl__colo_restore_domain_checkpoint_callback; /* * Secondary vm is running in colo mode, so we need to call * libxl__xc_domain_restore_done() to create secondary vm. * But we will exit in domain_create_cb(). So replace the * callback here. */ crs->saved_cb = dcs->callback; dcs->callback = libxl__colo_domain_create_cb; crcs->state_file = GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%d", domid); crcs->status = LIBXL_COLO_SETUPED; libxl__logdirty_init(&crcs->lds); crcs->lds.ao = ao; crcs->sws.fd = crs->send_back_fd; crcs->sws.ao = ao; crcs->sws.back_channel = true; dcs->cds.concrete_data = crs; libxl__stream_write_start(egc, &crcs->sws); rc = 0; out: crs->callback(egc, crs, rc); return; }
void libxl__xc_domain_restore_done(libxl__egc *egc, void *dcs_void, int ret, int retval, int errnoval) { libxl__domain_create_state *dcs = dcs_void; STATE_AO_GC(dcs->ao); libxl_ctx *ctx = libxl__gc_owner(gc); char **vments = NULL, **localents = NULL; struct timeval start_time; int i, esave, flags; /* convenience aliases */ const uint32_t domid = dcs->guest_domid; libxl_domain_config *const d_config = dcs->guest_config; libxl_domain_build_info *const info = &d_config->b_info; libxl__domain_build_state *const state = &dcs->build_state; const int fd = dcs->restore_fd; if (ret) goto out; if (retval) { LOGEV(ERROR, errnoval, "restoring domain"); ret = ERROR_FAIL; goto out; } gettimeofday(&start_time, NULL); switch (info->type) { case LIBXL_DOMAIN_TYPE_HVM: vments = libxl__calloc(gc, 7, sizeof(char *)); vments[0] = "rtc/timeoffset"; vments[1] = (info->u.hvm.timeoffset) ? info->u.hvm.timeoffset : ""; vments[2] = "image/ostype"; vments[3] = "hvm"; vments[4] = "start_time"; vments[5] = libxl__sprintf(gc, "%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000); break; case LIBXL_DOMAIN_TYPE_PV: vments = libxl__calloc(gc, 11, sizeof(char *)); i = 0; vments[i++] = "image/ostype"; vments[i++] = "linux"; vments[i++] = "image/kernel"; vments[i++] = (char *) state->pv_kernel.path; vments[i++] = "start_time"; vments[i++] = libxl__sprintf(gc, "%lu.%02d", start_time.tv_sec,(int)start_time.tv_usec/10000); if (state->pv_ramdisk.path) { vments[i++] = "image/ramdisk"; vments[i++] = (char *) state->pv_ramdisk.path; } if (state->pv_cmdline) { vments[i++] = "image/cmdline"; vments[i++] = (char *) state->pv_cmdline; } break; default: ret = ERROR_INVAL; goto out; } ret = libxl__build_post(gc, domid, info, state, vments, localents); if (ret) goto out; if (info->type == LIBXL_DOMAIN_TYPE_HVM) { state->saved_state = GCSPRINTF( XC_DEVICE_MODEL_RESTORE_FILE".%d", domid); } out: if (info->type == LIBXL_DOMAIN_TYPE_PV) { libxl__file_reference_unmap(&state->pv_kernel); libxl__file_reference_unmap(&state->pv_ramdisk); } esave = errno; flags = fcntl(fd, F_GETFL); if (flags == -1) { LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "unable to get flags on restore fd"); } else { flags &= ~O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) == -1) LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "unable to put restore fd" " back to blocking mode"); } errno = esave; domcreate_rebuild_done(egc, dcs, ret); }
/* * In return, the script writes the name of REMUS_IFB device (during setup) * to be used for output buffering into XENBUS_PATH/ifb */ static void netbuf_setup_script_cb(libxl__egc *egc, libxl__async_exec_state *aes, int rc, int status) { libxl__ao_device *aodev = CONTAINER_OF(aes, *aodev, aes); libxl__checkpoint_device *dev = CONTAINER_OF(aodev, *dev, aodev); libxl__remus_device_nic *remus_nic = dev->concrete_data; libxl__checkpoint_devices_state *cds = dev->cds; libxl__remus_state *rs = cds->concrete_data; const char *out_path_base, *hotplug_error = NULL; STATE_AO_GC(cds->ao); /* Convenience aliases */ const uint32_t domid = cds->domid; const int devid = remus_nic->devid; const char *const vif = remus_nic->vif; const char **const ifb = &remus_nic->ifb; if (status && !rc) rc = ERROR_FAIL; if (rc) goto out; /* * we need to get ifb first because it's needed for teardown */ rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/remus/netbuf/%d/ifb", libxl__xs_libxl_path(gc, domid), devid), ifb); if (rc) goto out; if (!(*ifb)) { LOGD(ERROR, domid, "Cannot get ifb dev name for domain %u dev %s", domid, vif); rc = ERROR_FAIL; goto out; } out_path_base = GCSPRINTF("%s/remus/netbuf/%d", libxl__xs_libxl_path(gc, domid), devid); rc = libxl__xs_read_checked(gc, XBT_NULL, GCSPRINTF("%s/hotplug-error", out_path_base), &hotplug_error); if (rc) goto out; if (hotplug_error) { LOGD(ERROR, domid, "netbuf script %s setup failed for vif %s: %s", rs->netbufscript, vif, hotplug_error); rc = ERROR_FAIL; goto out; } if (status) { rc = ERROR_FAIL; goto out; } LOGD(DEBUG, domid, "%s will buffer packets from vif %s", *ifb, vif); rc = init_qdisc(cds, remus_nic); out: aodev->rc = rc; aodev->callback(egc, aodev); }