Пример #1
0
int libxl_wait_for_memory_target(libxl_ctx *ctx, uint32_t domid, int wait_secs)
{
    int rc = 0;
    uint64_t target_memkb = 0;
    uint64_t current_memkb, prev_memkb;
    libxl_dominfo info;

    rc = libxl_get_memory_target(ctx, domid, &target_memkb);
    if (rc < 0)
        return rc;

    libxl_dominfo_init(&info);
    prev_memkb = UINT64_MAX;

    do {
        sleep(2);

        libxl_dominfo_dispose(&info);
        libxl_dominfo_init(&info);
        rc = libxl_domain_info(ctx, &info, domid);
        if (rc < 0)
            goto out;

        current_memkb = info.current_memkb + info.outstanding_memkb;

        if (current_memkb > prev_memkb)
        {
            rc = ERROR_FAIL;
            goto out;
        }
        else if (current_memkb == prev_memkb)
            wait_secs -= 2;
        /* if current_memkb < prev_memkb loop for free as progress has
         * been made */

        prev_memkb = current_memkb;
    } while (wait_secs > 0 && current_memkb > target_memkb);

    if (current_memkb <= target_memkb)
        rc = 0;
    else
        rc = ERROR_FAIL;

out:
    libxl_dominfo_dispose(&info);
    return rc;
}
Пример #2
0
int  hyperxl_domaim_check(libxl_ctx* ctx, uint32_t domid)
{
    return libxl_domain_info(ctx, NULL, domid);
}
Пример #3
0
/* Portability note: this lock utilises flock(2) so a proper implementation of
 * flock(2) is required.
 */
libxl__domain_userdata_lock *libxl__lock_domain_userdata(libxl__gc *gc,
                                                         uint32_t domid)
{
    libxl__domain_userdata_lock *lock = NULL;
    const char *lockfile;
    int fd;
    struct stat stab, fstab;

    lockfile = libxl__userdata_path(gc, domid, "domain-userdata-lock", "l");
    if (!lockfile) goto out;

    lock = libxl__zalloc(NOGC, sizeof(libxl__domain_userdata_lock));
    lock->path = libxl__strdup(NOGC, lockfile);

    while (true) {
        libxl__carefd_begin();
        fd = open(lockfile, O_RDWR|O_CREAT, 0666);
        if (fd < 0)
            LOGE(ERROR, "cannot open lockfile %s, errno=%d", lockfile, errno);
        lock->lock_carefd = libxl__carefd_opened(CTX, fd);
        if (fd < 0) goto out;

        /* Lock the file in exclusive mode, wait indefinitely to
         * acquire the lock
         */
        while (flock(fd, LOCK_EX)) {
            switch (errno) {
            case EINTR:
                /* Signal received, retry */
                continue;
            default:
                /* All other errno: EBADF, EINVAL, ENOLCK, EWOULDBLOCK */
                LOGE(ERROR,
                     "unexpected error while trying to lock %s, fd=%d, errno=%d",
                     lockfile, fd, errno);
                goto out;
            }
        }

        if (fstat(fd, &fstab)) {
            LOGE(ERROR, "cannot fstat %s, fd=%d, errno=%d",
                 lockfile, fd, errno);
            goto out;
        }
        if (stat(lockfile, &stab)) {
            if (errno != ENOENT) {
                LOGE(ERROR, "cannot stat %s, errno=%d", lockfile, errno);
                goto out;
            }
        } else {
            if (stab.st_dev == fstab.st_dev && stab.st_ino == fstab.st_ino)
                break;
        }

        libxl__carefd_close(lock->lock_carefd);
    }

    /* Check the domain is still there, if not we should release the
     * lock and clean up.
     */
    if (libxl_domain_info(CTX, NULL, domid))
        goto out;

    return lock;

out:
    if (lock) libxl__unlock_domain_userdata(lock);
    return NULL;
}
Пример #4
0
/* In general you should not add new output to this function since it
 * is intended only for legacy use.
 */
void printf_info_sexp(int domid, libxl_domain_config *d_config)
{
    int i;
    libxl_dominfo info;

    libxl_domain_create_info *c_info = &d_config->c_info;
    libxl_domain_build_info *b_info = &d_config->b_info;

    printf("(domain\n\t(domid %d)\n", domid);
    printf("\t(create_info)\n");
    printf("\t(hvm %d)\n", c_info->type == LIBXL_DOMAIN_TYPE_HVM);
    printf("\t(hap %s)\n", libxl_defbool_to_string(c_info->hap));
    printf("\t(oos %s)\n", libxl_defbool_to_string(c_info->oos));
    printf("\t(ssidref %d)\n", c_info->ssidref);
    printf("\t(name %s)\n", c_info->name);

    /* retrieve the UUID from dominfo, since it is probably generated
     * during parsing and thus does not match the real one
     */
    if (libxl_domain_info(ctx, &info, domid) == 0) {
        printf("\t(uuid " LIBXL_UUID_FMT ")\n", LIBXL_UUID_BYTES(info.uuid));
    } else {
        printf("\t(uuid <unknown>)\n");
    }
    if (c_info->pool_name)
        printf("\t(cpupool %s)\n", c_info->pool_name);
    if (c_info->xsdata)
        printf("\t(xsdata contains data)\n");
    else
        printf("\t(xsdata (null))\n");
    if (c_info->platformdata)
        printf("\t(platformdata contains data)\n");
    else
        printf("\t(platformdata (null))\n");


    printf("\t(build_info)\n");
    printf("\t(max_vcpus %d)\n", b_info->max_vcpus);
    printf("\t(tsc_mode %s)\n", libxl_tsc_mode_to_string(b_info->tsc_mode));
    printf("\t(max_memkb %"PRId64")\n", b_info->max_memkb);
    printf("\t(target_memkb %"PRId64")\n", b_info->target_memkb);
    printf("\t(nomigrate %s)\n",
           libxl_defbool_to_string(b_info->disable_migrate));

    if (c_info->type == LIBXL_DOMAIN_TYPE_PV && b_info->u.pv.bootloader) {
        printf("\t(bootloader %s)\n", b_info->u.pv.bootloader);
        if (b_info->u.pv.bootloader_args) {
            printf("\t(bootloader_args");
            for (i=0; b_info->u.pv.bootloader_args[i]; i++)
                printf(" %s", b_info->u.pv.bootloader_args[i]);
            printf(")\n");
        }
    }

    printf("\t(image\n");
    switch (c_info->type) {
    case LIBXL_DOMAIN_TYPE_HVM:
        printf("\t\t(hvm\n");
        printf("\t\t\t(firmware %s)\n", b_info->u.hvm.firmware);
        printf("\t\t\t(video_memkb %"PRId64")\n", b_info->video_memkb);
        printf("\t\t\t(shadow_memkb %"PRId64")\n", b_info->shadow_memkb);
        printf("\t\t\t(pae %s)\n", libxl_defbool_to_string(b_info->u.hvm.pae));
        printf("\t\t\t(apic %s)\n",
               libxl_defbool_to_string(b_info->u.hvm.apic));
        printf("\t\t\t(acpi %s)\n",
               libxl_defbool_to_string(b_info->u.hvm.acpi));
        printf("\t\t\t(nx %s)\n", libxl_defbool_to_string(b_info->u.hvm.nx));
        printf("\t\t\t(viridian %s)\n",
               libxl_defbool_to_string(b_info->u.hvm.viridian));
        printf("\t\t\t(hpet %s)\n",
               libxl_defbool_to_string(b_info->u.hvm.hpet));
        printf("\t\t\t(vpt_align %s)\n",
               libxl_defbool_to_string(b_info->u.hvm.vpt_align));
        printf("\t\t\t(timer_mode %s)\n",
               libxl_timer_mode_to_string(b_info->u.hvm.timer_mode));
        printf("\t\t\t(nestedhvm %s)\n",
               libxl_defbool_to_string(b_info->u.hvm.nested_hvm));
        printf("\t\t\t(stdvga %s)\n", b_info->u.hvm.vga.kind ==
                                      LIBXL_VGA_INTERFACE_TYPE_STD ?
                                      "True" : "False");
        printf("\t\t\t(vnc %s)\n",
               libxl_defbool_to_string(b_info->u.hvm.vnc.enable));
        printf("\t\t\t(vnclisten %s)\n", b_info->u.hvm.vnc.listen);
        printf("\t\t\t(vncdisplay %d)\n", b_info->u.hvm.vnc.display);
        printf("\t\t\t(vncunused %s)\n",
               libxl_defbool_to_string(b_info->u.hvm.vnc.findunused));
        printf("\t\t\t(keymap %s)\n", b_info->u.hvm.keymap);
        printf("\t\t\t(sdl %s)\n",
               libxl_defbool_to_string(b_info->u.hvm.sdl.enable));
        printf("\t\t\t(opengl %s)\n",
               libxl_defbool_to_string(b_info->u.hvm.sdl.opengl));
        printf("\t\t\t(nographic %s)\n",
               libxl_defbool_to_string(b_info->u.hvm.nographic));
        printf("\t\t\t(spice %s)\n",
               libxl_defbool_to_string(b_info->u.hvm.spice.enable));
        printf("\t\t\t(spiceport %d)\n", b_info->u.hvm.spice.port);
        printf("\t\t\t(spicetls_port %d)\n", b_info->u.hvm.spice.tls_port);
        printf("\t\t\t(spicehost %s)\n", b_info->u.hvm.spice.host);
        printf("\t\t\t(spicedisable_ticketing %s)\n",
               libxl_defbool_to_string(b_info->u.hvm.spice.disable_ticketing));
        printf("\t\t\t(spiceagent_mouse %s)\n",
               libxl_defbool_to_string(b_info->u.hvm.spice.agent_mouse));

        printf("\t\t\t(device_model %s)\n", b_info->device_model ? : "default");
        printf("\t\t\t(gfx_passthru %s)\n",
               libxl_defbool_to_string(b_info->u.hvm.gfx_passthru));
        printf("\t\t\t(serial %s)\n", b_info->u.hvm.serial);
        printf("\t\t\t(boot %s)\n", b_info->u.hvm.boot);
        printf("\t\t\t(usb %s)\n", libxl_defbool_to_string(b_info->u.hvm.usb));
        printf("\t\t\t(usbdevice %s)\n", b_info->u.hvm.usbdevice);
        printf("\t\t)\n");
        break;
    case LIBXL_DOMAIN_TYPE_PV:
        printf("\t\t(linux %d)\n", 0);
        printf("\t\t\t(kernel %s)\n", b_info->u.pv.kernel);
        printf("\t\t\t(cmdline %s)\n", b_info->u.pv.cmdline);
        printf("\t\t\t(ramdisk %s)\n", b_info->u.pv.ramdisk);
        printf("\t\t\t(e820_host %s)\n",
               libxl_defbool_to_string(b_info->u.pv.e820_host));
        printf("\t\t)\n");
        break;
    default:
        fprintf(stderr, "Unknown domain type %d\n", c_info->type);
        exit(1);
    }
    printf("\t)\n");

    for (i = 0; i < d_config->num_disks; i++) {
        printf("\t(device\n");
        printf("\t\t(tap\n");
        printf("\t\t\t(backend_domid %d)\n", d_config->disks[i].backend_domid);
        printf("\t\t\t(frontend_domid %d)\n", domid);
        printf("\t\t\t(physpath %s)\n", d_config->disks[i].pdev_path);
        printf("\t\t\t(phystype %d)\n", d_config->disks[i].backend);
        printf("\t\t\t(virtpath %s)\n", d_config->disks[i].vdev);
        printf("\t\t\t(unpluggable %d)\n", d_config->disks[i].removable);
        printf("\t\t\t(readwrite %d)\n", d_config->disks[i].readwrite);
        printf("\t\t\t(is_cdrom %d)\n", d_config->disks[i].is_cdrom);
        printf("\t\t)\n");
        printf("\t)\n");
    }

    for (i = 0; i < d_config->num_nics; i++) {
        printf("\t(device\n");
        printf("\t\t(vif\n");
        if (d_config->nics[i].ifname)
            printf("\t\t\t(vifname %s)\n", d_config->nics[i].ifname);
        printf("\t\t\t(backend_domid %d)\n", d_config->nics[i].backend_domid);
        printf("\t\t\t(frontend_domid %d)\n", domid);
        printf("\t\t\t(devid %d)\n", d_config->nics[i].devid);
        printf("\t\t\t(mtu %d)\n", d_config->nics[i].mtu);
        printf("\t\t\t(model %s)\n", d_config->nics[i].model);
        printf("\t\t\t(mac %02x%02x%02x%02x%02x%02x)\n",
               d_config->nics[i].mac[0], d_config->nics[i].mac[1],
               d_config->nics[i].mac[2], d_config->nics[i].mac[3],
               d_config->nics[i].mac[4], d_config->nics[i].mac[5]);
        printf("\t\t)\n");
        printf("\t)\n");
    }

    for (i = 0; i < d_config->num_pcidevs; i++) {
        printf("\t(device\n");
        printf("\t\t(pci\n");
        printf("\t\t\t(pci dev %04x:%02x:%02x.%01x@%02x)\n",
               d_config->pcidevs[i].domain, d_config->pcidevs[i].bus,
               d_config->pcidevs[i].dev, d_config->pcidevs[i].func,
               d_config->pcidevs[i].vdevfn);
        printf("\t\t\t(opts msitranslate %d power_mgmt %d)\n",
               d_config->pcidevs[i].msitranslate,
               d_config->pcidevs[i].power_mgmt);
        printf("\t\t)\n");
        printf("\t)\n");
    }

    for (i = 0; i < d_config->num_vfbs; i++) {
        printf("\t(device\n");
        printf("\t\t(vfb\n");
        printf("\t\t\t(backend_domid %d)\n", d_config->vfbs[i].backend_domid);
        printf("\t\t\t(frontend_domid %d)\n", domid);
        printf("\t\t\t(devid %d)\n", d_config->vfbs[i].devid);
        printf("\t\t\t(vnc %s)\n",
               libxl_defbool_to_string(d_config->vfbs[i].vnc.enable));
        printf("\t\t\t(vnclisten %s)\n", d_config->vfbs[i].vnc.listen);
        printf("\t\t\t(vncdisplay %d)\n", d_config->vfbs[i].vnc.display);
        printf("\t\t\t(vncunused %s)\n",
               libxl_defbool_to_string(d_config->vfbs[i].vnc.findunused));
        printf("\t\t\t(keymap %s)\n", d_config->vfbs[i].keymap);
        printf("\t\t\t(sdl %s)\n",
               libxl_defbool_to_string(d_config->vfbs[i].sdl.enable));
        printf("\t\t\t(opengl %s)\n",
               libxl_defbool_to_string(d_config->vfbs[i].sdl.opengl));
        printf("\t\t\t(display %s)\n", d_config->vfbs[i].sdl.display);
        printf("\t\t\t(xauthority %s)\n", d_config->vfbs[i].sdl.xauthority);
        printf("\t\t)\n");
        printf("\t)\n");
    }
    printf(")\n");
}
Пример #5
0
void *testcase(struct test *tc)
{
    int count;

    libxl_domain_config dc;
    uint32_t domid = -2;
    struct event ev;

    init_domain_config(&dc, "test_domain_suspend",
                       "resources/vmlinuz-4.0.4-301.fc22.x86_64",
                       "resources/initrd.xen-4.0.4-301.fc22.x86_64",
                       "resources/Fedora-Cloud-Base-22-20150521.x86_64.qcow2",
                       "resources/cloudinit.iso");

    do_domain_create(tc, &dc, &domid);
    wait_for(tc, EV_LIBXL_CALLBACK, &ev);
    assert(ev.u.callback_event.rc == 0);
    assert(domid != (uint32_t) -2);
    printf("domid: %d\n", domid);

    libxl_domain_unpause(tc->ctx, domid);
    printf("domain %d unpaused\n", domid);

    printf("waiting for domain to boot\n");
    wait_for_n(tc, EV_EVENTLOOP, 10, &ev);	

    /* Most of the work of suspending a domain is done by helper
       processes.   The helper processes generate hundreds of FD events,
       so the test tries to cancel after batches of 100 FD events, as well
       as after every non-FD event. */

    for (count = 1; count < 100; count++) {
        int rc;
        FILE *suspend_file;
        int suspend_fd;

        printf("\n****** Will cancel after %d events ******\n", count);

        suspend_file = tmpfile();
        if (!suspend_file) {
            perror("tmpfile");
            break;
        }
        suspend_fd = fileno(suspend_file);
        do_domain_suspend(tc, domid, suspend_fd);

        if (wait_until_n(tc, EV_LIBXL_CALLBACK, count, &ev, 50)) {
            /* The API call returned before we could cancel it.
               It should have returned successfully.
             */
            fclose(suspend_file);
            printf("libxl_domain_suspend returned %d\n",
                   ev.u.callback_event.rc);
            assert(ev.u.callback_event.rc == 0);

            /* No operation in progress - cancelling should return an error */
            rc = libxl_ao_cancel(tc->ctx, &tc->ao_how);
            printf("libxl_ao_cancel returned %d\n", rc);
            assert(rc == ERROR_NOTFOUND);

            break;
        }

        /* The wait_until_n() call did not receive a calback event,
           so we will try to cancel the asynchronous operation */

        printf("Cancelling asynchronous operation\n");
        rc = libxl_ao_cancel(tc->ctx, &tc->ao_how);

        /* Calling cancel on a cancellable operation should not return an
           error, unless the operation happened to complete in the meantime.
         */
        printf("libxl_ao_cancel returned %d\n", rc);
        assert(rc == ERROR_NOTFOUND || rc == 0);

        /* The API call's return code should indicate that it was cancelled */
        wait_for(tc, EV_LIBXL_CALLBACK, &ev);
        fclose(suspend_file);
        printf("libxl_domain_suspend returned %d\n",
               ev.u.callback_event.rc);
        assert(ev.u.callback_event.rc == ERROR_CANCELLED
               || ev.u.callback_event.rc == 0);

        /* Suspend was cancelled - the domain should still be running */
        assert(!libxl_domain_info(tc->ctx, NULL, domid));
    }

    libxl_domain_destroy(tc->ctx, domid, 0);
    libxl_domain_config_dispose(&dc);
    test_exit();
    return NULL;
}
Пример #6
0
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;
}
Пример #7
0
static int libxl__fill_dom0_memory_info(libxl__gc *gc, uint64_t *target_memkb,
                                        uint64_t *max_memkb)
{
    int rc;
    libxl_dominfo info;
    libxl_physinfo physinfo;
    char *target = NULL, *staticmax = NULL, *endptr = NULL;
    char *target_path = "/local/domain/0/memory/target";
    char *max_path = "/local/domain/0/memory/static-max";
    xs_transaction_t t;
    libxl_ctx *ctx = libxl__gc_owner(gc);

    libxl_dominfo_init(&info);

retry_transaction:
    t = xs_transaction_start(ctx->xsh);

    target = libxl__xs_read(gc, t, target_path);
    staticmax = libxl__xs_read(gc, t, max_path);
    if (target && staticmax) {
        rc = 0;
        goto out;
    }

    if (target) {
        *target_memkb = strtoull(target, &endptr, 10);
        if (*endptr != '\0') {
            LOGED(ERROR, 0, "Invalid memory target %s from %s\n", target,
                 target_path);
            rc = ERROR_FAIL;
            goto out;
        }
    }

    if (staticmax) {
        *max_memkb = strtoull(staticmax, &endptr, 10);
        if (*endptr != '\0') {
            LOGED(ERROR, 0, "Invalid memory static-max %s from %s\n",
                 staticmax,
                 max_path);
            rc = ERROR_FAIL;
            goto out;
        }
    }

    libxl_dominfo_dispose(&info);
    libxl_dominfo_init(&info);
    rc = libxl_domain_info(ctx, &info, 0);
    if (rc < 0)
        goto out;

    rc = libxl_get_physinfo(ctx, &physinfo);
    if (rc < 0)
        goto out;

    if (target == NULL) {
        libxl__xs_printf(gc, t, target_path, "%"PRIu64, info.current_memkb);
        *target_memkb = info.current_memkb;
    }
    if (staticmax == NULL) {
        libxl__xs_printf(gc, t, max_path, "%"PRIu64, info.max_memkb);
        *max_memkb = info.max_memkb;
    }

    rc = 0;

out:
    if (!xs_transaction_end(ctx->xsh, t, 0)) {
        if (errno == EAGAIN)
            goto retry_transaction;
        else
            rc = ERROR_FAIL;
    }

    libxl_dominfo_dispose(&info);
    return rc;
}