/* * Copyright (C) 2009 Citrix Ltd. * Author Stefano Stabellini * Author Vincent Hanquez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; version 2.1 only. with the special * exception on linking described in file LICENSE. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. */ #include "libxl_osdeps.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for utsname in xl info */ #include #include #include #include "libxl.h" #include "libxl_utils.h" #include "libxlutil.h" #include "xl.h" #define CHK_ERRNO( call ) ({ \ int chk_errno = (call); \ if (chk_errno < 0) { \ fprintf(stderr,"xl: fatal error: %s:%d: %s: %s\n", \ __FILE__,__LINE__, strerror(chk_errno), #call); \ exit(-ERROR_FAIL); \ } \ }) #define MUST( call ) ({ \ int must_rc = (call); \ if (must_rc < 0) { \ fprintf(stderr,"xl: fatal error: %s:%d, rc=%d: %s\n", \ __FILE__,__LINE__, must_rc, #call); \ exit(-must_rc); \ } \ }) int logfile = 2; /* every libxl action in xl uses this same libxl context */ libxl_ctx ctx; /* when we operate on a domain, it is this one: */ static uint32_t domid; static const char *common_domname; static int fd_lock = -1; static const char savefileheader_magic[32]= "Xen saved domain, xl format\n \0 \r"; 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"; /* 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 */ 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 */ }; static const char *action_on_shutdown_names[] = { [LIBXL_ACTION_DESTROY] = "destroy", [LIBXL_ACTION_RESTART] = "restart", [LIBXL_ACTION_RESTART_RENAME] = "rename-restart", [LIBXL_ACTION_PRESERVE] = "preserve", [LIBXL_ACTION_COREDUMP_DESTROY] = "coredump-destroy", [LIBXL_ACTION_COREDUMP_RESTART] = "coredump-restart", }; /* 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) static int qualifier_to_id(const char *p, uint32_t *id_r) { int i, alldigit; alldigit = 1; for (i = 0; p[i]; i++) { if (!isdigit((uint8_t)p[i])) { alldigit = 0; break; } } if (i > 0 && alldigit) { *id_r = strtoul(p, NULL, 10); return 0; } else { /* check here if it's a uuid and do proper conversion */ } return 1; } static int domain_qualifier_to_domid(const char *p, uint32_t *domid_r, int *was_name_r) { libxl_dominfo dominfo; int was_name, rc; was_name = qualifier_to_id(p, domid_r); if (was_name_r) *was_name_r = was_name; if (was_name) { rc = libxl_name_to_domid(&ctx, p, domid_r); if (rc) return rc; } else { rc = libxl_domain_info(&ctx, &dominfo, *domid_r); /* error only if domain does not exist */ if (rc == ERROR_INVAL) return rc; } return 0; } static int cpupool_qualifier_to_cpupoolid(const char *p, uint32_t *poolid_r, int *was_name_r) { int was_name; was_name = qualifier_to_id(p, poolid_r); if (was_name_r) *was_name_r = was_name; return was_name ? libxl_name_to_cpupoolid(&ctx, p, poolid_r) : 0; } static void find_domain(const char *p) { int rc, was_name; rc = domain_qualifier_to_domid(p, &domid, &was_name); if (rc) { fprintf(stderr, "%s is an invalid domain identifier (rc=%d)\n", p, rc); exit(2); } common_domname = was_name ? p : libxl_domid_to_name(&ctx, domid); } 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; } 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 *xmalloc(size_t sz) { void *r; r = malloc(sz); if (!r) { fprintf(stderr,"xl: Unable to malloc %lu bytes.\n", (unsigned long)sz); exit(-ERROR_FAIL); } return r; } static void *xrealloc(void *ptr, size_t sz) { void *r; if (!sz) { free(ptr); return 0; } /* realloc(non-0, 0) has a useless return value; * but xrealloc(anything, 0) is like free */ r = realloc(ptr, sz); if (!r) { fprintf(stderr,"xl: Unable to realloc to %lu bytes.\n", (unsigned long)sz); exit(-ERROR_FAIL); } return r; } #define LOG(_f, _a...) dolog(__FILE__, __LINE__, __func__, _f "\n", ##_a) static void dolog(const char *file, int line, const char *func, char *fmt, ...) __attribute__((format(printf,4,5))); static void dolog(const char *file, int line, const char *func, char *fmt, ...) { va_list ap; char *s; int rc; va_start(ap, fmt); rc = vasprintf(&s, fmt, ap); va_end(ap); if (rc >= 0) libxl_write_exactly(NULL, logfile, s, rc, NULL, NULL); } static void printf_info(int domid, libxl_domain_config *d_config, libxl_device_model_info *dm_info) { 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->hvm); printf("\t(hap %d)\n", c_info->hap); printf("\t(oos %d)\n", 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 )\n"); } printf("\t(cpupool %s)\n", c_info->poolname); 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 %d)\n", b_info->tsc_mode); printf("\t(max_memkb %d)\n", b_info->max_memkb); printf("\t(target_memkb %d)\n", b_info->target_memkb); printf("\t(nomigrate %d)\n", b_info->disable_migrate); if (!c_info->hvm && 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 %s)\n", b_info->u.pv.bootloader_args); } printf("\t(image\n"); if (c_info->hvm) { printf("\t\t(hvm\n"); printf("\t\t\t(loader %s)\n", b_info->kernel.path); printf("\t\t\t(video_memkb %d)\n", b_info->video_memkb); printf("\t\t\t(shadow_memkb %d)\n", b_info->shadow_memkb); printf("\t\t\t(pae %d)\n", b_info->u.hvm.pae); printf("\t\t\t(apic %d)\n", b_info->u.hvm.apic); printf("\t\t\t(acpi %d)\n", b_info->u.hvm.acpi); printf("\t\t\t(nx %d)\n", b_info->u.hvm.nx); printf("\t\t\t(viridian %d)\n", b_info->u.hvm.viridian); printf("\t\t\t(hpet %d)\n", b_info->u.hvm.hpet); printf("\t\t\t(vpt_align %d)\n", b_info->u.hvm.vpt_align); printf("\t\t\t(timer_mode %d)\n", b_info->u.hvm.timer_mode); printf("\t\t\t(device_model %s)\n", dm_info->device_model); printf("\t\t\t(videoram %d)\n", dm_info->videoram); printf("\t\t\t(stdvga %d)\n", dm_info->stdvga); printf("\t\t\t(vnc %d)\n", dm_info->vnc); printf("\t\t\t(vnclisten %s)\n", dm_info->vnclisten); printf("\t\t\t(vncdisplay %d)\n", dm_info->vncdisplay); printf("\t\t\t(vncunused %d)\n", dm_info->vncunused); printf("\t\t\t(keymap %s)\n", dm_info->keymap); printf("\t\t\t(sdl %d)\n", dm_info->sdl); printf("\t\t\t(gfx_passthru %d)\n", dm_info->gfx_passthru); printf("\t\t\t(opengl %d)\n", dm_info->opengl); printf("\t\t\t(nographic %d)\n", dm_info->nographic); printf("\t\t\t(serial %s)\n", dm_info->serial); printf("\t\t\t(boot %s)\n", dm_info->boot); printf("\t\t\t(usb %d)\n", dm_info->usb); printf("\t\t\t(usbdevice %s)\n", dm_info->usbdevice); printf("\t\t\t(apic %d)\n", dm_info->apic); printf("\t\t)\n"); } else { printf("\t\t(linux %d)\n", b_info->hvm); printf("\t\t\t(kernel %s)\n", b_info->kernel.path); printf("\t\t\t(cmdline %s)\n", b_info->u.pv.cmdline); printf("\t\t\t(ramdisk %s)\n", b_info->u.pv.ramdisk.path); printf("\t\t)\n"); } 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", d_config->disks[i].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].unpluggable); 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_vifs; i++) { printf("\t(device\n"); printf("\t\t(vif\n"); if (d_config->vifs[i].ifname) printf("\t\t\t(vifname %s)\n", d_config->vifs[i].ifname); printf("\t\t\t(backend_domid %d)\n", d_config->vifs[i].backend_domid); printf("\t\t\t(frontend_domid %d)\n", d_config->vifs[i].domid); printf("\t\t\t(devid %d)\n", d_config->vifs[i].devid); printf("\t\t\t(mtu %d)\n", d_config->vifs[i].mtu); printf("\t\t\t(model %s)\n", d_config->vifs[i].model); printf("\t\t\t(mac %02x%02x%02x%02x%02x%02x)\n", d_config->vifs[i].mac[0], d_config->vifs[i].mac[1], d_config->vifs[i].mac[2], d_config->vifs[i].mac[3], d_config->vifs[i].mac[4], d_config->vifs[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", d_config->vfbs[i].domid); printf("\t\t\t(devid %d)\n", d_config->vfbs[i].devid); printf("\t\t\t(vnc %d)\n", d_config->vfbs[i].vnc); printf("\t\t\t(vnclisten %s)\n", d_config->vfbs[i].vnclisten); printf("\t\t\t(vncdisplay %d)\n", d_config->vfbs[i].vncdisplay); printf("\t\t\t(vncunused %d)\n", d_config->vfbs[i].vncunused); printf("\t\t\t(keymap %s)\n", d_config->vfbs[i].keymap); printf("\t\t\t(sdl %d)\n", d_config->vfbs[i].sdl); printf("\t\t\t(opengl %d)\n", d_config->vfbs[i].opengl); printf("\t\t\t(display %s)\n", d_config->vfbs[i].display); printf("\t\t\t(xauthority %s)\n", d_config->vfbs[i].xauthority); printf("\t\t)\n"); printf("\t)\n"); } printf(")\n"); } static int parse_action_on_shutdown(const char *buf, enum libxl_action_on_shutdown *a) { int i; const char *n; for (i = 0; i < sizeof(action_on_shutdown_names) / sizeof(action_on_shutdown_names[0]); i++) { n = action_on_shutdown_names[i]; if (strcmp(buf, n) == 0) { *a = i; return 1; } } return 0; } #define DSTATE_INITIAL 0 #define DSTATE_TAP 1 #define DSTATE_PHYSPATH 2 #define DSTATE_VIRTPATH 3 #define DSTATE_VIRTTYPE 4 #define DSTATE_RW 5 #define DSTATE_TERMINAL 6 static int parse_disk_config(libxl_device_disk *disk, char *buf2) { int state = DSTATE_INITIAL; char *p, *end, *tok; memset(disk, 0, sizeof(*disk)); for(tok = p = buf2, end = buf2 + strlen(buf2) + 1; p < end; p++) { switch(state){ case DSTATE_INITIAL: if ( *p == ':' ) { *p = '\0'; if ( !strcmp(tok, "phy") ) { state = DSTATE_PHYSPATH; disk->format = DISK_FORMAT_RAW; disk->backend = DISK_BACKEND_PHY; }else if ( !strcmp(tok, "file") ) { state = DSTATE_PHYSPATH; disk->format = DISK_FORMAT_RAW; disk->backend = DISK_BACKEND_TAP; }else if ((!strcmp(tok, "tap")) || (!strcmp(tok, "tap2"))) { state = DSTATE_TAP; }else{ fprintf(stderr, "Unknown disk type: %s\n", tok); return 0; } tok = p + 1; } else if (*p == ',') { state = DSTATE_VIRTPATH; disk->format = DISK_FORMAT_EMPTY; disk->backend = DISK_BACKEND_TAP; disk->pdev_path = strdup(""); tok = p + 1; } break; case DSTATE_TAP: if (*p == ',') { disk->format = DISK_FORMAT_RAW; disk->backend = DISK_BACKEND_TAP; state = DSTATE_PHYSPATH; } else if ( *p == ':' ) { *p = '\0'; if (!strcmp(tok, "aio")) { tok = p + 1; break; } if (!strcmp(tok, "vhd")) { disk->format = DISK_FORMAT_VHD; disk->backend = DISK_BACKEND_TAP; }else if ( !strcmp(tok, "qcow") ) { disk->format = DISK_FORMAT_QCOW; disk->backend = DISK_BACKEND_QDISK; }else if ( !strcmp(tok, "qcow2") ) { disk->format = DISK_FORMAT_QCOW2; disk->backend = DISK_BACKEND_QDISK; }else if (!strcmp(tok, "raw")) { disk->format = DISK_FORMAT_RAW; disk->backend = DISK_BACKEND_TAP; } else { fprintf(stderr, "Unknown tapdisk type: %s\n", tok); return 0; } tok = p + 1; state = DSTATE_PHYSPATH; break; } else { break; } case DSTATE_PHYSPATH: if ( *p == ',' ) { int ioemu_len; *p = '\0'; disk->pdev_path = (*tok) ? strdup(tok) : NULL; tok = p + 1; /* hack for ioemu disk spec */ ioemu_len = strlen("ioemu:"); state = DSTATE_VIRTPATH; if ( tok + ioemu_len < end && !strncmp(tok, "ioemu:", ioemu_len)) { tok += ioemu_len; p += ioemu_len; } } break; case DSTATE_VIRTPATH: if ( *p == ',' || *p == ':' || *p == '\0' ) { switch(*p) { case ':': state = DSTATE_VIRTTYPE; break; case ',': state = DSTATE_RW; break; case '\0': state = DSTATE_TERMINAL; break; } if ( tok == p ) goto out; *p = '\0'; disk->vdev = (*tok) ? strdup(tok) : NULL; tok = p + 1; } break; case DSTATE_VIRTTYPE: if ( *p == ',' || *p == '\0' ) { *p = '\0'; if ( !strcmp(tok, "cdrom") ) { disk->is_cdrom = 1; disk->unpluggable = 1; }else{ fprintf(stderr, "Unknown virtual disk type: %s\n", tok); return 0; } tok = p + 1; state = (*p == ',') ? DSTATE_RW : DSTATE_TERMINAL; } break; case DSTATE_RW: if ( *p == '\0' ) { disk->readwrite = (tok[0] == 'w'); tok = p + 1; state = DSTATE_TERMINAL; } break; case DSTATE_TERMINAL: goto out; } } out: if ( tok != p || state != DSTATE_TERMINAL ) { fprintf(stderr, "parse error in disk config near '%s'\n", tok); return 0; } return 1; } static void parse_config_data(const char *configfile_filename_report, const char *configfile_data, int configfile_len, libxl_domain_config *d_config, libxl_device_model_info *dm_info) { const char *buf; long l; XLU_Config *config; XLU_ConfigList *vbds, *nics, *pcis, *cvfbs, *net2s, *cpuids; int pci_power_mgmt = 0; int pci_msitranslate = 1; int e; libxl_domain_create_info *c_info = &d_config->c_info; libxl_domain_build_info *b_info = &d_config->b_info; config= xlu_cfg_init(stderr, configfile_filename_report); if (!config) { fprintf(stderr, "Failed to allocate for configuration\n"); exit(1); } e= xlu_cfg_readdata(config, configfile_data, configfile_len); if (e) { fprintf(stderr, "Failed to parse config file: %s\n", strerror(e)); exit(1); } libxl_init_create_info(c_info); c_info->hvm = 0; if (!xlu_cfg_get_string (config, "builder", &buf) && !strncmp(buf, "hvm", strlen(buf))) c_info->hvm = 1; if (!xlu_cfg_get_long (config, "hap", &l)) c_info->hap = l; if (xlu_cfg_replace_string (config, "name", &c_info->name)) c_info->name = strdup("test"); if (!xlu_cfg_get_string (config, "uuid", &buf) ) { if ( libxl_uuid_from_string(&c_info->uuid, buf) ) { fprintf(stderr, "Failed to parse UUID: %s\n", buf); exit(1); } }else{ libxl_uuid_generate(&c_info->uuid); } if (!xlu_cfg_get_long(config, "oos", &l)) c_info->oos = l; if (!xlu_cfg_get_string (config, "pool", &buf)) { c_info->poolid = -1; cpupool_qualifier_to_cpupoolid(buf, &c_info->poolid, NULL); } c_info->poolname = libxl_cpupoolid_to_name(&ctx, c_info->poolid); if (!c_info->poolname) { fprintf(stderr, "Illegal pool specified\n"); exit(1); } libxl_init_build_info(b_info, c_info); /* the following is the actual config parsing with overriding values in the structures */ if (!xlu_cfg_get_long (config, "vcpus", &l)) { b_info->max_vcpus = l; b_info->cur_vcpus = (1 << l) - 1; } if (!xlu_cfg_get_long (config, "maxvcpus", &l)) b_info->max_vcpus = l; if (!xlu_cfg_get_long (config, "memory", &l)) { b_info->max_memkb = l * 1024; b_info->target_memkb = b_info->max_memkb; } if (!xlu_cfg_get_long (config, "maxmem", &l)) b_info->max_memkb = l * 1024; if (xlu_cfg_get_string (config, "on_poweroff", &buf)) buf = "destroy"; if (!parse_action_on_shutdown(buf, &d_config->on_poweroff)) { fprintf(stderr, "Unknown on_poweroff action \"%s\" specified\n", buf); exit(1); } if (xlu_cfg_get_string (config, "on_reboot", &buf)) buf = "restart"; if (!parse_action_on_shutdown(buf, &d_config->on_reboot)) { fprintf(stderr, "Unknown on_reboot action \"%s\" specified\n", buf); exit(1); } if (xlu_cfg_get_string (config, "on_watchdog", &buf)) buf = "destroy"; if (!parse_action_on_shutdown(buf, &d_config->on_watchdog)) { fprintf(stderr, "Unknown on_watchdog action \"%s\" specified\n", buf); exit(1); } if (xlu_cfg_get_string (config, "on_crash", &buf)) buf = "destroy"; if (!parse_action_on_shutdown(buf, &d_config->on_crash)) { fprintf(stderr, "Unknown on_crash action \"%s\" specified\n", buf); exit(1); } /* libxl_get_required_shadow_memory() must be called after final values * (default or specified) for vcpus and memory are set, because the * calculation depends on those values. */ b_info->shadow_memkb = !xlu_cfg_get_long(config, "shadow_memory", &l) ? l * 1024 : libxl_get_required_shadow_memory(b_info->max_memkb, b_info->max_vcpus); if (!xlu_cfg_get_long (config, "nomigrate", &l)) b_info->disable_migrate = l; if (!xlu_cfg_get_long(config, "tsc_mode", &l)) b_info->tsc_mode = l; b_info->rtc_timeoffset = !xlu_cfg_get_long(config, "rtc_timeoffset", &l) ? l : 0; b_info->localtime = !xlu_cfg_get_long(config, "localtime", &l) ? l : 0; if (b_info->localtime) { time_t t; struct tm *tm; t = time(NULL); tm = localtime(&t); b_info->rtc_timeoffset += tm->tm_gmtoff; } if (!xlu_cfg_get_long (config, "videoram", &l)) b_info->video_memkb = l * 1024; xlu_cfg_replace_string (config, "kernel", &b_info->kernel.path); if (!xlu_cfg_get_long (config, "gfx_passthru", &l)) dm_info->gfx_passthru = l; if (c_info->hvm == 1) { if (!xlu_cfg_get_long (config, "pae", &l)) b_info->u.hvm.pae = l; if (!xlu_cfg_get_long (config, "apic", &l)) b_info->u.hvm.apic = l; if (!xlu_cfg_get_long (config, "acpi", &l)) b_info->u.hvm.acpi = l; if (!xlu_cfg_get_long (config, "nx", &l)) b_info->u.hvm.nx = l; if (!xlu_cfg_get_long (config, "viridian", &l)) b_info->u.hvm.viridian = l; if (!xlu_cfg_get_long (config, "hpet", &l)) b_info->u.hvm.hpet = l; if (!xlu_cfg_get_long (config, "vpt_align", &l)) b_info->u.hvm.vpt_align = l; if (!xlu_cfg_get_long (config, "timer_mode", &l)) b_info->u.hvm.timer_mode = l; } else { char *cmdline = NULL; const char *root = NULL, *extra = ""; xlu_cfg_get_string (config, "root", &root); xlu_cfg_get_string (config, "extra", &extra); if (root) { if (asprintf(&cmdline, "root=%s %s", root, extra) == -1) cmdline = NULL; } else { cmdline = strdup(extra); } if ((root || extra) && !cmdline) { fprintf(stderr, "Failed to allocate memory for cmdline\n"); exit(1); } xlu_cfg_replace_string (config, "bootloader", &b_info->u.pv.bootloader); xlu_cfg_replace_string (config, "bootloader_args", &b_info->u.pv.bootloader_args); if (!b_info->u.pv.bootloader && !b_info->kernel.path) { fprintf(stderr, "Neither kernel nor bootloader specified\n"); exit(1); } b_info->u.pv.cmdline = cmdline; xlu_cfg_replace_string (config, "ramdisk", &b_info->u.pv.ramdisk.path); } if (!xlu_cfg_get_list (config, "disk", &vbds, 0, 0)) { d_config->num_disks = 0; d_config->disks = NULL; while ((buf = xlu_cfg_get_listitem (vbds, d_config->num_disks)) != NULL) { libxl_device_disk *disk; char *buf2 = strdup(buf); d_config->disks = (libxl_device_disk *) realloc(d_config->disks, sizeof (libxl_device_disk) * (d_config->num_disks + 1)); disk = d_config->disks + d_config->num_disks; if ( !parse_disk_config(disk, buf2) ) { exit(1); } free(buf2); d_config->num_disks++; } } if (!xlu_cfg_get_list (config, "vif", &nics, 0, 0)) { d_config->num_vifs = 0; d_config->vifs = NULL; while ((buf = xlu_cfg_get_listitem (nics, d_config->num_vifs)) != NULL) { libxl_device_nic *nic; char *buf2 = strdup(buf); char *p, *p2; d_config->vifs = (libxl_device_nic *) realloc(d_config->vifs, sizeof (libxl_device_nic) * (d_config->num_vifs+1)); nic = d_config->vifs + d_config->num_vifs; CHK_ERRNO( libxl_device_nic_init(nic, d_config->num_vifs) ); if (default_vifscript) { free(nic->script); nic->script = strdup(default_vifscript); } p = strtok(buf2, ","); if (!p) goto skip; do { while (*p == ' ') p++; if ((p2 = strchr(p, '=')) == NULL) break; *p2 = '\0'; if (!strcmp(p, "model")) { free(nic->model); nic->model = strdup(p2 + 1); } else if (!strcmp(p, "mac")) { char *p3 = p2 + 1; *(p3 + 2) = '\0'; nic->mac[0] = strtol(p3, NULL, 16); p3 = p3 + 3; *(p3 + 2) = '\0'; nic->mac[1] = strtol(p3, NULL, 16); p3 = p3 + 3; *(p3 + 2) = '\0'; nic->mac[2] = strtol(p3, NULL, 16); p3 = p3 + 3; *(p3 + 2) = '\0'; nic->mac[3] = strtol(p3, NULL, 16); p3 = p3 + 3; *(p3 + 2) = '\0'; nic->mac[4] = strtol(p3, NULL, 16); p3 = p3 + 3; *(p3 + 2) = '\0'; nic->mac[5] = strtol(p3, NULL, 16); } else if (!strcmp(p, "bridge")) { free(nic->bridge); nic->bridge = strdup(p2 + 1); } else if (!strcmp(p, "type")) { if (!strcmp(p2 + 1, "ioemu")) nic->nictype = NICTYPE_IOEMU; else nic->nictype = NICTYPE_VIF; } else if (!strcmp(p, "ip")) { free(nic->ip); nic->ip = strdup(p2 + 1); } else if (!strcmp(p, "script")) { free(nic->script); nic->script = strdup(p2 + 1); } else if (!strcmp(p, "vifname")) { free(nic->ifname); nic->ifname = strdup(p2 + 1); } else if (!strcmp(p, "backend")) { if(libxl_name_to_domid(&ctx, (p2 + 1), &(nic->backend_domid))) { fprintf(stderr, "Specified backend domain does not exist, defaulting to Dom0\n"); nic->backend_domid = 0; } } else if (!strcmp(p, "rate")) { fprintf(stderr, "the rate parameter for vifs is currently not supported\n"); } else if (!strcmp(p, "accel")) { fprintf(stderr, "the accel parameter for vifs is currently not supported\n"); } } while ((p = strtok(NULL, ",")) != NULL); skip: free(buf2); d_config->num_vifs++; } } if (!xlu_cfg_get_list(config, "vif2", &net2s, 0, 0)) { d_config->num_vif2s = 0; d_config->vif2s = NULL; while ((buf = xlu_cfg_get_listitem(net2s, d_config->num_vif2s))) { libxl_device_net2 *net2; char *buf2 = strdup(buf); char *p; d_config->vif2s = realloc(d_config->vif2s, sizeof (libxl_device_net2) * (d_config->num_vif2s + 1)); net2 = d_config->vif2s + d_config->num_vif2s; libxl_device_net2_init(net2, d_config->num_vif2s); for (p = strtok(buf2, ","); p; p = strtok(NULL, ",")) { char* val; while (isblank(*p)) p++; val = strchr(p, '='); if (val == NULL) continue; *val++ = 0; if (!strcmp("front_mac", p)) { libxl_strtomac(val, net2->front_mac); } else if (!strcmp("back_mac", p)) { libxl_strtomac(val, net2->back_mac); } else if (!strcmp("backend", p)) { domain_qualifier_to_domid(val, &net2->backend_domid, 0); } else if (!strcmp("trusted", p)) { net2->trusted = (*val == '1'); } else if (!strcmp("back_trusted", p)) { net2->back_trusted = (*val == '1'); } else if (!strcmp("bridge", p)) { free(net2->bridge); net2->bridge = strdup(val); } else if (!strcmp("filter_mac", p)) { net2->filter_mac = (*val == '1'); } else if (!strcmp("front_filter_mac", p)) { net2->front_filter_mac = (*val == '1'); } else if (!strcmp("pdev", p)) { net2->pdev = strtoul(val, NULL, 10); } else if (!strcmp("max_bypasses", p)) { net2->max_bypasses = strtoul(val, NULL, 10); } } free(buf2); d_config->num_vif2s++; } } if (!xlu_cfg_get_list (config, "vfb", &cvfbs, 0, 0)) { d_config->num_vfbs = 0; d_config->num_vkbs = 0; d_config->vfbs = NULL; d_config->vkbs = NULL; while ((buf = xlu_cfg_get_listitem (cvfbs, d_config->num_vfbs)) != NULL) { libxl_device_vfb *vfb; libxl_device_vkb *vkb; char *buf2 = strdup(buf); char *p, *p2; d_config->vfbs = (libxl_device_vfb *) realloc(d_config->vfbs, sizeof(libxl_device_vfb) * (d_config->num_vfbs + 1)); vfb = d_config->vfbs + d_config->num_vfbs; libxl_device_vfb_init(vfb, d_config->num_vfbs); d_config->vkbs = (libxl_device_vkb *) realloc(d_config->vkbs, sizeof(libxl_device_vkb) * (d_config->num_vkbs + 1)); vkb = d_config->vkbs + d_config->num_vkbs; libxl_device_vkb_init(vkb, d_config->num_vkbs); p = strtok(buf2, ","); if (!p) goto skip_vfb; do { while (*p == ' ') p++; if ((p2 = strchr(p, '=')) == NULL) break; *p2 = '\0'; if (!strcmp(p, "vnc")) { vfb->vnc = atoi(p2 + 1); } else if (!strcmp(p, "vnclisten")) { free(vfb->vnclisten); vfb->vnclisten = strdup(p2 + 1); } else if (!strcmp(p, "vncpasswd")) { free(vfb->vncpasswd); vfb->vncpasswd = strdup(p2 + 1); } else if (!strcmp(p, "vncdisplay")) { vfb->vncdisplay = atoi(p2 + 1); } else if (!strcmp(p, "vncunused")) { vfb->vncunused = atoi(p2 + 1); } else if (!strcmp(p, "keymap")) { free(vfb->keymap); vfb->keymap = strdup(p2 + 1); } else if (!strcmp(p, "sdl")) { vfb->sdl = atoi(p2 + 1); } else if (!strcmp(p, "opengl")) { vfb->opengl = atoi(p2 + 1); } else if (!strcmp(p, "display")) { free(vfb->display); vfb->display = strdup(p2 + 1); } else if (!strcmp(p, "xauthority")) { free(vfb->xauthority); vfb->xauthority = strdup(p2 + 1); } } while ((p = strtok(NULL, ",")) != NULL); skip_vfb: free(buf2); d_config->num_vfbs++; d_config->num_vkbs++; } } if (!xlu_cfg_get_long (config, "pci_msitranslate", &l)) pci_msitranslate = l; if (!xlu_cfg_get_long (config, "pci_power_mgmt", &l)) pci_power_mgmt = l; if (!xlu_cfg_get_list (config, "pci", &pcis, 0, 0)) { int i; d_config->num_pcidevs = 0; d_config->pcidevs = NULL; for(i = 0; (buf = xlu_cfg_get_listitem (pcis, i)) != NULL; i++) { libxl_device_pci *pcidev; d_config->pcidevs = (libxl_device_pci *) realloc(d_config->pcidevs, sizeof (libxl_device_pci) * (d_config->num_pcidevs + 1)); pcidev = d_config->pcidevs + d_config->num_pcidevs; memset(pcidev, 0x00, sizeof(libxl_device_pci)); pcidev->msitranslate = pci_msitranslate; pcidev->power_mgmt = pci_power_mgmt; if (!libxl_device_pci_parse_bdf(&ctx, pcidev, buf)) d_config->num_pcidevs++; } } switch (xlu_cfg_get_list(config, "cpuid", &cpuids, 0, 1)) { case 0: { int i; const char *errstr; for (i = 0; (buf = xlu_cfg_get_listitem(cpuids, i)) != NULL; i++) { e = libxl_cpuid_parse_config_xend(&b_info->cpuid, buf); switch (e) { case 0: continue; case 1: errstr = "illegal leaf number"; break; case 2: errstr = "illegal subleaf number"; break; case 3: errstr = "missing colon"; break; case 4: errstr = "invalid register name (must be e[abcd]x)"; break; case 5: errstr = "policy string must be exactly 32 characters long"; break; default: errstr = "unknown error"; break; } fprintf(stderr, "while parsing CPUID line: \"%s\":\n", buf); fprintf(stderr, " error #%i: %s\n", e, errstr); } } break; case EINVAL: /* config option is not a list, parse as a string */ if (!xlu_cfg_get_string(config, "cpuid", &buf)) { char *buf2, *p, *strtok_ptr = NULL; const char *errstr; buf2 = strdup(buf); p = strtok_r(buf2, ",", &strtok_ptr); if (p == NULL) { free(buf2); break; } if (strcmp(p, "host")) { fprintf(stderr, "while parsing CPUID string: \"%s\":\n", buf); fprintf(stderr, " error: first word must be \"host\"\n"); free(buf2); break; } for (p = strtok_r(NULL, ",", &strtok_ptr); p != NULL; p = strtok_r(NULL, ",", &strtok_ptr)) { e = libxl_cpuid_parse_config(&b_info->cpuid, p); switch (e) { case 0: continue; case 1: errstr = "missing \"=\" in key=value"; break; case 2: errstr = "unknown CPUID flag name"; break; case 3: errstr = "illegal CPUID value (must be: [0|1|x|k|s])"; break; default: errstr = "unknown error"; break; } fprintf(stderr, "while parsing CPUID flag: \"%s\":\n", p); fprintf(stderr, " error #%i: %s\n", e, errstr); } free(buf2); } break; default: break; } if (c_info->hvm == 1) { XLU_ConfigList *dmargs; int nr_dmargs = 0; /* init dm from c and b */ libxl_init_dm_info(dm_info, c_info, b_info); /* then process config related to dm */ xlu_cfg_replace_string (config, "device_model", &dm_info->device_model); if (!xlu_cfg_get_long (config, "stdvga", &l)) dm_info->stdvga = l; if (!xlu_cfg_get_long (config, "vnc", &l)) dm_info->vnc = l; xlu_cfg_replace_string (config, "vnclisten", &dm_info->vnclisten); xlu_cfg_replace_string (config, "vncpasswd", &dm_info->vncpasswd); if (!xlu_cfg_get_long (config, "vncdisplay", &l)) dm_info->vncdisplay = l; if (!xlu_cfg_get_long (config, "vncunused", &l)) dm_info->vncunused = l; xlu_cfg_replace_string (config, "keymap", &dm_info->keymap); if (!xlu_cfg_get_long (config, "sdl", &l)) dm_info->sdl = l; if (!xlu_cfg_get_long (config, "opengl", &l)) dm_info->opengl = l; if (!xlu_cfg_get_long (config, "nographic", &l)) dm_info->nographic = l; if (!xlu_cfg_get_long (config, "gfx_passthru", &l)) dm_info->gfx_passthru = l; xlu_cfg_replace_string (config, "serial", &dm_info->serial); xlu_cfg_replace_string (config, "boot", &dm_info->boot); if (!xlu_cfg_get_long (config, "usb", &l)) dm_info->usb = l; xlu_cfg_replace_string (config, "usbdevice", &dm_info->usbdevice); xlu_cfg_replace_string (config, "soundhw", &dm_info->soundhw); if (!xlu_cfg_get_long (config, "xen_platform_pci", &l)) dm_info->xen_platform_pci = l; if (!xlu_cfg_get_list(config, "device_model_args", &dmargs, &nr_dmargs, 0)) { int i; dm_info->extra = xmalloc(sizeof(char *) * (nr_dmargs + 1)); dm_info->extra[nr_dmargs] = NULL; for (i=0; iextra[i] = a ? strdup(a) : NULL; } } } dm_info->type = c_info->hvm ? XENFV : XENPV; xlu_cfg_destroy(config); } /* Returns 1 if domain should be restarted, 2 if domain should be renamed then restarted */ static int handle_domain_death(libxl_ctx *ctx, uint32_t domid, libxl_event *event, libxl_domain_config *d_config, libxl_dominfo *info) { int restart = 0; enum libxl_action_on_shutdown action; switch (info->shutdown_reason) { case SHUTDOWN_poweroff: action = d_config->on_poweroff; break; case SHUTDOWN_reboot: action = d_config->on_reboot; break; case SHUTDOWN_suspend: return 0; case SHUTDOWN_crash: action = d_config->on_crash; break; case SHUTDOWN_watchdog: action = d_config->on_watchdog; break; default: LOG("Unknown shutdown reason code %d. Destroying domain.", info->shutdown_reason); action = LIBXL_ACTION_DESTROY; } LOG("Action for shutdown reason code %d is %s", info->shutdown_reason, action_on_shutdown_names[action]); if (action == LIBXL_ACTION_COREDUMP_DESTROY || action == LIBXL_ACTION_COREDUMP_RESTART) { char *corefile; int rc; if (asprintf(&corefile, "/var/xen/dump/%s", d_config->c_info.name) < 0) { LOG("failed to construct core dump path"); } else { LOG("dumping core to %s", corefile); rc=libxl_domain_core_dump(ctx, domid, corefile); if (rc) LOG("core dump failed (rc=%d).", rc); } /* No point crying over spilled milk, continue on failure. */ if (action == LIBXL_ACTION_COREDUMP_DESTROY) action = LIBXL_ACTION_DESTROY; else action = LIBXL_ACTION_RESTART; } switch (action) { case LIBXL_ACTION_PRESERVE: break; case LIBXL_ACTION_RESTART_RENAME: restart = 2; break; case LIBXL_ACTION_RESTART: restart = 1; /* fall-through */ case LIBXL_ACTION_DESTROY: LOG("Domain %d needs to be cleaned up: destroying the domain", domid); libxl_domain_destroy(ctx, domid, 0); break; case LIBXL_ACTION_COREDUMP_DESTROY: case LIBXL_ACTION_COREDUMP_RESTART: /* Already handled these above. */ abort(); } return restart; } static int preserve_domain(libxl_ctx *ctx, uint32_t domid, libxl_event *event, libxl_domain_config *d_config, libxl_dominfo *info) { time_t now; struct tm tm; char stime[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(&stime[0], sizeof(stime), "-%Y%m%dT%H%MZ", &tm)) { LOG("Failed to format time as a string"); return 0; } libxl_uuid_generate(&new_uuid); LOG("Preserving domain %d %s with suffix%s", domid, d_config->c_info.name, stime); rc = libxl_domain_preserve(ctx, domid, &d_config->c_info, stime, new_uuid); return rc == 0 ? 1 : 0; } struct domain_create { int debug; int daemonize; int paused; int dryrun; int quiet; int console_autoconnect; const char *config_file; const char *extra_config; /* extra config string */ const char *restore_file; int migrate_fd; /* -1 means none */ char **migration_domname_r; /* from malloc */ }; static int freemem(libxl_domain_build_info *b_info, libxl_device_model_info *dm_info) { int rc, retries = 3; uint32_t need_memkb, free_memkb; if (!autoballoon) return 0; rc = libxl_domain_need_memory(&ctx, b_info, dm_info, &need_memkb); if (rc < 0) return rc; do { rc = libxl_get_free_memory(&ctx, &free_memkb); if (rc < 0) return rc; if (free_memkb >= need_memkb) return 0; rc = libxl_set_memory_target(&ctx, 0, free_memkb - need_memkb, 1, 0); if (rc < 0) return rc; rc = libxl_wait_for_free_memory(&ctx, domid, need_memkb, 10); if (!rc) return 0; else if (rc != ERROR_NOMEM) return rc; /* the memory target has been reached but the free memory is still * not enough: loop over again */ rc = libxl_wait_for_memory_target(&ctx, 0, 1); if (rc < 0) return rc; retries--; } while (retries > 0); return ERROR_NOMEM; } static int autoconnect_console(libxl_ctx *ctx, uint32_t domid, void *priv) { pid_t *pid = priv; *pid = fork(); if (*pid < 0) { perror("unable to fork xenconsole"); return ERROR_FAIL; } else if (*pid > 0) return 0; libxl_ctx_postfork(ctx); sleep(1); libxl_primary_console_exec(ctx, domid); /* Do not return. xl continued in child process */ fprintf(stderr, "Unable to attach console\n"); _exit(1); } static int create_domain(struct domain_create *dom_info) { libxl_domain_config d_config; int debug = dom_info->debug; int daemonize = dom_info->daemonize; int paused = dom_info->paused; const char *config_file = dom_info->config_file; const char *extra_config = dom_info->extra_config; const char *restore_file = dom_info->restore_file; int migrate_fd = dom_info->migrate_fd; int fd, i; int need_daemon = 1; int ret, rc; libxl_waiter *w1 = NULL, *w2 = NULL; void *config_data = 0; int config_len = 0; int restore_fd = -1; int status = 0; libxl_console_ready cb; pid_t child_console_pid = -1; struct save_file_header hdr; memset(&d_config, 0x00, sizeof(d_config)); if (restore_file) { uint8_t *optdata_begin = 0; const uint8_t *optdata_here = 0; union { uint32_t u32; char b[4]; } u32buf; uint32_t badflags; restore_fd = migrate_fd >= 0 ? migrate_fd : open(restore_file, O_RDONLY); CHK_ERRNO( libxl_read_exactly(&ctx, restore_fd, &hdr, sizeof(hdr), restore_file, "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_file, hdr.mandatory_flags, hdr.optional_flags, hdr.optional_data_len); badflags = hdr.mandatory_flags & ~( 0 /* none understood yet */ ); 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_ERRNO( libxl_read_exactly(&ctx, restore_fd, optdata_begin, hdr.optional_data_len, restore_file, "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\n"); 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; 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 (!restore_file && extra_config && strlen(extra_config)) { if (config_len > INT_MAX - (strlen(extra_config) + 2 + 1)) { fprintf(stderr, "Failed to attach extra configration\n"); return ERROR_FAIL; } /* allocate space for the extra config plus two EOLs plus \0 */ config_data = realloc(config_data, config_len + strlen(extra_config) + 2 + 1); if (!config_data) { fprintf(stderr, "Failed to realloc config_data\n"); return ERROR_FAIL; } config_len += sprintf(config_data + config_len, "\n%s\n", extra_config); } } else { if (!config_data) { fprintf(stderr, "Config file not specified and" " none in save file\n"); return ERROR_INVAL; } config_file = ""; } if (!dom_info->quiet) printf("Parsing config file %s\n", config_file); parse_config_data(config_file, config_data, config_len, &d_config, &d_config.dm_info); 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 */ if (asprintf(&d_config.c_info.name, "%s--incoming", common_domname) < 0) { fprintf(stderr, "Failed to allocate memory in asprintf\n"); exit(1); } *dom_info->migration_domname_r = strdup(d_config.c_info.name); } } if (debug || dom_info->dryrun) printf_info(-1, &d_config, &d_config.dm_info); ret = 0; if (dom_info->dryrun) goto out; start: domid = -1; rc = acquire_lock(); if (rc < 0) goto error_out; ret = freemem(&d_config.b_info, &d_config.dm_info); if (ret < 0) { fprintf(stderr, "failed to free memory for the domain\n"); ret = ERROR_FAIL; goto error_out; } if ( dom_info->console_autoconnect ) { cb = autoconnect_console; }else{ cb = NULL; } if ( restore_file ) { ret = libxl_domain_create_restore(&ctx, &d_config, cb, &child_console_pid, &domid, restore_fd); }else{ ret = libxl_domain_create_new(&ctx, &d_config, cb, &child_console_pid, &domid); } if ( ret ) goto error_out; ret = libxl_userdata_store(&ctx, domid, "xl", config_data, config_len); if (ret) { perror("cannot save config file"); ret = ERROR_FAIL; goto error_out; } release_lock(); if (!paused) libxl_domain_unpause(&ctx, domid); ret = domid; /* caller gets success in parent */ if (!daemonize) goto out; if (need_daemon) { char *fullname, *name; pid_t child1, got_child; int nullfd; child1 = libxl_fork(&ctx); if (child1) { printf("Daemon running with PID %d\n", child1); for (;;) { got_child = waitpid(child1, &status, 0); if (got_child == child1) break; assert(got_child == -1); if (errno != EINTR) { perror("failed to wait for daemonizing child"); ret = ERROR_FAIL; goto error_out; } } if (status) { libxl_report_child_exitstatus(&ctx, XTL_ERROR, "daemonizing child", child1, status); ret = ERROR_FAIL; goto error_out; } ret = domid; goto out; } rc = libxl_ctx_postfork(&ctx); if (rc) { LOG("failed to reinitialise context after fork"); exit(-1); } if (asprintf(&name, "xl-%s", d_config.c_info.name) < 0) { LOG("Failed to allocate memory in asprintf"); exit(1); } rc = libxl_create_logfile(&ctx, name, &fullname); if (rc) { LOG("failed to open logfile %s: %s",fullname,strerror(errno)); exit(-1); } CHK_ERRNO(( logfile = open(fullname, O_WRONLY|O_CREAT, 0644) )<0); free(fullname); free(name); CHK_ERRNO(( nullfd = open("/dev/null", O_RDONLY) )<0); dup2(nullfd, 0); dup2(logfile, 1); dup2(logfile, 2); CHK_ERRNO(daemon(0, 1) < 0); need_daemon = 0; } LOG("Waiting for domain %s (domid %d) to die [pid %ld]", d_config.c_info.name, domid, (long)getpid()); w1 = (libxl_waiter*) xmalloc(sizeof(libxl_waiter) * d_config.num_disks); w2 = (libxl_waiter*) xmalloc(sizeof(libxl_waiter)); libxl_wait_for_disk_ejects(&ctx, domid, d_config.disks, d_config.num_disks, w1); libxl_wait_for_domain_death(&ctx, domid, w2); libxl_get_wait_fd(&ctx, &fd); while (1) { int ret; fd_set rfds; libxl_dominfo info; libxl_event event; libxl_device_disk disk; FD_ZERO(&rfds); FD_SET(fd, &rfds); ret = select(fd + 1, &rfds, NULL, NULL, NULL); if (!ret) continue; libxl_get_event(&ctx, &event); switch (event.type) { case LIBXL_EVENT_DOMAIN_DEATH: ret = libxl_event_get_domain_death_info(&ctx, domid, &event, &info); if (ret < 0) { libxl_free_event(&event); continue; } LOG("Domain %d is dead", domid); if (ret) { switch (handle_domain_death(&ctx, domid, &event, &d_config, &info)) { case 2: if (!preserve_domain(&ctx, domid, &event, &d_config, &info)) { /* If we fail then exit leaving the old domain in place. */ ret = -1; goto out; } /* Otherwise fall through and restart. */ case 1: for (i = 0; i < d_config.num_disks; i++) libxl_free_waiter(&w1[i]); libxl_free_waiter(w2); free(w1); free(w2); /* * 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 0: LOG("Done. Exiting now"); ret = 0; goto out; } } else { LOG("Unable to get domain death info, quitting"); goto out; } break; case LIBXL_EVENT_DISK_EJECT: if (libxl_event_get_disk_eject_info(&ctx, domid, &event, &disk)) { libxl_cdrom_insert(&ctx, domid, &disk); libxl_device_disk_destroy(&disk); } break; } libxl_free_event(&event); } error_out: release_lock(); if (libxl_domid_valid_guest(domid)) libxl_domain_destroy(&ctx, domid, 0); out: if (logfile != 2) close(logfile); libxl_domain_config_destroy(&d_config); free(config_data); waitpid_out: if (child_console_pid > 0 && waitpid(child_console_pid, &status, 0) < 0 && errno == EINTR) goto waitpid_out; /* * If we have daemonized then do not return to the caller -- this has * already happened in the parent. */ if ( !need_daemon ) exit(ret); return ret; } void help(const char *command) { int i; struct cmd_spec *cmd; if (!command || !strcmp(command, "help")) { printf("Usage xl [-v] [args]\n\n"); printf("xl full list of subcommands:\n\n"); for (i = 0; i < cmdtable_len; i++) printf(" %-20s%s\n", cmd_table[i].cmd_name, cmd_table[i].cmd_desc); } else { cmd = cmdtable_lookup(command); if (cmd) { printf("Usage: xl [-v] %s %s\n\n%s.\n\n", cmd->cmd_name, cmd->cmd_usage, cmd->cmd_desc); if (cmd->cmd_option) printf("Options:\n\n%s\n", cmd->cmd_option); } else { printf("command \"%s\" not implemented\n", command); } } } static int64_t parse_mem_size_kb(const char *mem) { char *endptr; int64_t kbytes; kbytes = strtoll(mem, &endptr, 10); if (strlen(endptr) > 1) return -1; switch (tolower((uint8_t)*endptr)) { case 't': kbytes <<= 10; case 'g': kbytes <<= 10; case '\0': case 'm': kbytes <<= 10; case 'k': break; case 'b': kbytes >>= 10; break; default: return -1; } return kbytes; } static int set_memory_max(const char *p, const char *mem) { int64_t memorykb; int rc; find_domain(p); memorykb = parse_mem_size_kb(mem); if (memorykb == -1) { fprintf(stderr, "invalid memory size: %s\n", mem); exit(3); } rc = libxl_domain_setmaxmem(&ctx, domid, memorykb); return rc; } int main_memmax(int argc, char **argv) { int opt = 0; char *p = NULL, *mem; int rc; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("mem-max"); exit(0); default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (optind >= argc - 1) { help("mem-max"); return 2; } p = argv[optind]; mem = argv[optind + 1]; rc = set_memory_max(p, mem); if (rc) { fprintf(stderr, "cannot set domid %d static max memory to : %s\n", domid, mem); return 1; } return 0; } static void set_memory_target(const char *p, const char *mem) { long long int memorykb; find_domain(p); memorykb = parse_mem_size_kb(mem); if (memorykb == -1) { fprintf(stderr, "invalid memory size: %s\n", mem); exit(3); } libxl_set_memory_target(&ctx, domid, memorykb, 0, /* enforce */ 1); } int main_memset(int argc, char **argv) { int opt = 0; const char *p = NULL, *mem; while ((opt = getopt(argc, argv, "h:")) != -1) { switch (opt) { case 'h': help("mem-set"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (optind >= argc - 1) { help("mem-set"); return 2; } p = argv[optind]; mem = argv[optind + 1]; set_memory_target(p, mem); return 0; } static void cd_insert(const char *dom, const char *virtdev, char *phys) { libxl_device_disk disk; /* we don't free disk's contents */ char *buf = NULL; find_domain(dom); if (asprintf(&buf, "%s,%s:cdrom,r", phys ? phys : "", virtdev) < 0) { fprintf(stderr, "out of memory\n"); return; } if (!parse_disk_config(&disk, buf)) { fprintf(stderr, "format error\n"); return; } disk.backend_domid = 0; disk.domid = domid; libxl_cdrom_insert(&ctx, domid, &disk); free(buf); } int main_cd_eject(int argc, char **argv) { int opt = 0; const char *p = NULL, *virtdev; while ((opt = getopt(argc, argv, "hn:")) != -1) { switch (opt) { case 'h': help("cd-eject"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (optind >= argc - 1) { help("cd-eject"); return 2; } p = argv[optind]; virtdev = argv[optind + 1]; cd_insert(p, virtdev, NULL); return 0; } int main_cd_insert(int argc, char **argv) { int opt = 0; const char *p = NULL, *virtdev; char *file = NULL; /* modified by cd_insert tokenising it */ while ((opt = getopt(argc, argv, "hn:")) != -1) { switch (opt) { case 'h': help("cd-insert"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (optind >= argc - 2) { help("cd-insert"); return 2; } p = argv[optind]; virtdev = argv[optind + 1]; file = argv[optind + 2]; cd_insert(p, virtdev, file); return 0; } int main_console(int argc, char **argv) { int opt = 0, num = 0; libxl_console_constype type = 0; while ((opt = getopt(argc, argv, "hn:t:")) != -1) { switch (opt) { case 'h': help("console"); return 0; case 't': if (!strcmp(optarg, "pv")) type = LIBXL_CONSTYPE_PV; else if (!strcmp(optarg, "serial")) type = LIBXL_CONSTYPE_SERIAL; else { fprintf(stderr, "console type supported are: pv, serial\n"); return 2; } break; case 'n': num = atoi(optarg); break; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (optind >= argc) { help("console"); return 2; } find_domain(argv[optind]); if (!type) libxl_primary_console_exec(&ctx, domid); else libxl_console_exec(&ctx, domid, num, type); fprintf(stderr, "Unable to attach console\n"); return 1; } static int vncviewer(const char *domain_spec, int autopass) { find_domain(domain_spec); libxl_vncviewer_exec(&ctx, domid, autopass); fprintf(stderr, "Unable to execute vncviewer\n"); return 1; } int main_vncviewer(int argc, char **argv) { static const struct option long_options[] = { {"autopass", 0, 0, 'a'}, {"vncviewer-autopass", 0, 0, 'a'}, {"help", 0, 0, 'h'}, {0, 0, 0, 0} }; int opt, autopass = 0; while (1) { opt = getopt_long(argc, argv, "ah", long_options, NULL); if (opt == -1) break; switch (opt) { case 'a': autopass = 1; break; case 'h': help("vncviewer"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (argc - optind != 1) { help("vncviewer"); return 2; } if (vncviewer(argv[optind], autopass)) return 1; return 0; } static void pcilist_assignable(void) { libxl_device_pci *pcidevs; int num, i; if ( libxl_device_pci_list_assignable(&ctx, &pcidevs, &num) ) return; for (i = 0; i < num; i++) { printf("%04x:%02x:%02x.%01x\n", pcidevs[i].domain, pcidevs[i].bus, pcidevs[i].dev, pcidevs[i].func); } free(pcidevs); } int main_pcilist_assignable(int argc, char **argv) { int opt; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("pci-list-assignable-devices"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } pcilist_assignable(); return 0; } static void pcilist(const char *dom) { libxl_device_pci *pcidevs; int num, i; find_domain(dom); if (libxl_device_pci_list_assigned(&ctx, &pcidevs, domid, &num)) return; printf("Vdev Device\n"); for (i = 0; i < num; i++) { printf("%02x.%01x %04x:%02x:%02x.%01x\n", (pcidevs[i].vdevfn >> 3) & 0x1f, pcidevs[i].vdevfn & 0x7, pcidevs[i].domain, pcidevs[i].bus, pcidevs[i].dev, pcidevs[i].func); libxl_device_pci_destroy(&pcidevs[i]); } free(pcidevs); } int main_pcilist(int argc, char **argv) { int opt; const char *domname = NULL; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("pci-list"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (optind >= argc) { help("pci-list"); return 2; } domname = argv[optind]; pcilist(domname); return 0; } static void pcidetach(const char *dom, const char *bdf, int force) { libxl_device_pci pcidev; find_domain(dom); memset(&pcidev, 0x00, sizeof(pcidev)); if (libxl_device_pci_parse_bdf(&ctx, &pcidev, bdf)) { fprintf(stderr, "pci-detach: malformed BDF specification \"%s\"\n", bdf); exit(2); } libxl_device_pci_remove(&ctx, domid, &pcidev, force); libxl_device_pci_destroy(&pcidev); } int main_pcidetach(int argc, char **argv) { int opt; int force = 0; const char *domname = NULL, *bdf = NULL; while ((opt = getopt(argc, argv, "hf")) != -1) { switch (opt) { case 'h': help("pci-detach"); return 0; case 'f': force = 1; break; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (optind >= argc - 1) { help("pci-detach"); return 2; } domname = argv[optind]; bdf = argv[optind + 1]; pcidetach(domname, bdf, force); return 0; } static void pciattach(const char *dom, const char *bdf, const char *vs) { libxl_device_pci pcidev; find_domain(dom); memset(&pcidev, 0x00, sizeof(pcidev)); if (libxl_device_pci_parse_bdf(&ctx, &pcidev, bdf)) { fprintf(stderr, "pci-attach: malformed BDF specification \"%s\"\n", bdf); exit(2); } libxl_device_pci_add(&ctx, domid, &pcidev); libxl_device_pci_destroy(&pcidev); } int main_pciattach(int argc, char **argv) { int opt; const char *domname = NULL, *bdf = NULL, *vs = NULL; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("pci-attach"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (optind >= argc - 1) { help("pci-attach"); return 2; } domname = argv[optind]; bdf = argv[optind + 1]; if (optind + 1 < argc) vs = argv[optind + 2]; pciattach(domname, bdf, vs); return 0; } static void pause_domain(const char *p) { find_domain(p); libxl_domain_pause(&ctx, domid); } static void unpause_domain(const char *p) { find_domain(p); libxl_domain_unpause(&ctx, domid); } static void destroy_domain(const char *p) { int rc; find_domain(p); if (domid == 0) { fprintf(stderr, "Cannot destroy privileged domain 0.\n\n"); exit(-1); } rc = libxl_domain_destroy(&ctx, domid, 0); if (rc) { fprintf(stderr,"destroy failed (rc=%d)\n",rc); exit(-1); } } static void shutdown_domain(const char *p, int wait) { int rc; find_domain(p); rc=libxl_domain_shutdown(&ctx, domid, 0); if (rc) { fprintf(stderr,"shutdown failed (rc=%d)\n",rc);exit(-1); } if (wait) { libxl_waiter waiter; int fd; libxl_wait_for_domain_death(&ctx, domid, &waiter); libxl_get_wait_fd(&ctx, &fd); while (wait) { fd_set rfds; libxl_event event; libxl_dominfo info; FD_ZERO(&rfds); FD_SET(fd, &rfds); if (!select(fd + 1, &rfds, NULL, NULL, NULL)) continue; libxl_get_event(&ctx, &event); if (event.type == LIBXL_EVENT_DOMAIN_DEATH) { if (libxl_event_get_domain_death_info(&ctx, domid, &event, &info) < 0) continue; LOG("Domain %d is dead", domid); wait = 0; } libxl_free_event(&event); } libxl_free_waiter(&waiter); } } static void reboot_domain(const char *p) { int rc; find_domain(p); rc=libxl_domain_shutdown(&ctx, domid, 1); if (rc) { fprintf(stderr,"reboot failed (rc=%d)\n",rc);exit(-1); } } static void list_domains_details(const libxl_dominfo *info, int nb_domain) { libxl_domain_config d_config; char *config_file; uint8_t *data; int i, len, rc; libxl_device_model_info dm_info; for (i = 0; i < nb_domain; i++) { /* no detailed info available on dom0 */ if (info[i].domid == 0) continue; rc = libxl_userdata_retrieve(&ctx, info[i].domid, "xl", &data, &len); if (rc) continue; CHK_ERRNO(asprintf(&config_file, "", info[i].domid)); memset(&d_config, 0x00, sizeof(d_config)); parse_config_data(config_file, (char *)data, len, &d_config, &dm_info); printf_info(info[i].domid, &d_config, &dm_info); libxl_domain_config_destroy(&d_config); free(data); free(config_file); } } static void list_domains(int verbose, const libxl_dominfo *info, int nb_domain) { int i; static const char shutdown_reason_letters[]= "-rscw"; printf("Name ID Mem VCPUs\tState\tTime(s)"); if (verbose) printf(" UUID Reason-Code"); printf("\n"); for (i = 0; i < nb_domain; i++) { char *domname; unsigned shutdown_reason; domname = libxl_domid_to_name(&ctx, info[i].domid); shutdown_reason = info[i].shutdown ? info[i].shutdown_reason : 0; printf("%-40s %5d %5lu %5d %c%c%c%c%c%c %8.1f", domname, info[i].domid, (unsigned long) (info[i].current_memkb / 1024), info[i].vcpu_online, info[i].running ? 'r' : '-', info[i].blocked ? 'b' : '-', info[i].paused ? 'p' : '-', info[i].shutdown ? 's' : '-', (shutdown_reason >= 0 && shutdown_reason < sizeof(shutdown_reason_letters)-1 ? shutdown_reason_letters[shutdown_reason] : '?'), info[i].dying ? 'd' : '-', ((float)info[i].cpu_time / 1e9)); free(domname); if (verbose) { printf(" " LIBXL_UUID_FMT, LIBXL_UUID_BYTES(info[i].uuid)); if (info[i].shutdown) printf(" %8x", shutdown_reason); else printf(" %8s", "-"); } putchar('\n'); } } static void list_vm(void) { libxl_vminfo *info; char *domname; int nb_vm, i; info = libxl_list_vm(&ctx, &nb_vm); if (info < 0) { fprintf(stderr, "libxl_domain_infolist failed.\n"); exit(1); } printf("UUID ID name\n"); for (i = 0; i < nb_vm; i++) { domname = libxl_domid_to_name(&ctx, info[i].domid); printf(LIBXL_UUID_FMT " %d %-30s\n", LIBXL_UUID_BYTES(info[i].uuid), info[i].domid, domname); free(domname); } free(info); } static void save_domain_core_begin(const char *domain_spec, const char *override_config_file, uint8_t **config_data_r, int *config_len_r) { int rc; find_domain(domain_spec); /* configuration file in optional data: */ if (override_config_file) { void *config_v = 0; rc = libxl_read_file_contents(&ctx, override_config_file, &config_v, config_len_r); *config_data_r = config_v; } else { rc = libxl_userdata_retrieve(&ctx, domid, "xl", config_data_r, config_len_r); } if (rc) { fputs("Unable to get config file\n",stderr); exit(2); } } static void save_domain_core_writeconfig(int fd, const char *filename, const uint8_t *config_data, int config_len) { struct save_file_header hdr; uint8_t *optdata_begin; union { uint32_t u32; char b[4]; } u32buf; memset(&hdr, 0, sizeof(hdr)); memcpy(hdr.magic, savefileheader_magic, sizeof(hdr.magic)); hdr.byteorder = SAVEFILE_BYTEORDER_VALUE; optdata_begin= 0; #define ADD_OPTDATA(ptr, len) ({ \ if ((len)) { \ hdr.optional_data_len += (len); \ optdata_begin = xrealloc(optdata_begin, hdr.optional_data_len); \ memcpy(optdata_begin + hdr.optional_data_len - (len), \ (ptr), (len)); \ } \ }) u32buf.u32 = config_len; ADD_OPTDATA(u32buf.b, 4); ADD_OPTDATA(config_data, config_len); /* that's the optional data */ CHK_ERRNO( libxl_write_exactly(&ctx, fd, &hdr, sizeof(hdr), filename, "header") ); CHK_ERRNO( libxl_write_exactly(&ctx, fd, optdata_begin, hdr.optional_data_len, filename, "header") ); fprintf(stderr, "Saving to %s new xl format (info" " 0x%"PRIx32"/0x%"PRIx32"/%"PRIu32")\n", filename, hdr.mandatory_flags, hdr.optional_flags, hdr.optional_data_len); } static int save_domain(const char *p, const char *filename, int checkpoint, const char *override_config_file) { int fd; uint8_t *config_data; int config_len; save_domain_core_begin(p, override_config_file, &config_data, &config_len); if (!config_len) { fputs(" Savefile will not contain xl domain config\n", stderr); } fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644); if (fd < 0) { fprintf(stderr, "Failed to open temp file %s for writing\n", filename); exit(2); } save_domain_core_writeconfig(fd, filename, config_data, config_len); CHK_ERRNO(libxl_domain_suspend(&ctx, NULL, domid, fd)); close(fd); if (checkpoint) libxl_domain_unpause(&ctx, domid); else libxl_domain_destroy(&ctx, domid, 0); exit(0); } static int migrate_read_fixedmessage(int fd, const void *msg, int msgsz, const char *what, const char *rune) { char buf[msgsz]; const char *stream; int rc; stream = rune ? "migration receiver stream" : "migration stream"; rc = libxl_read_exactly(&ctx, fd, buf, msgsz, stream, what); if (rc) return ERROR_FAIL; if (memcmp(buf, msg, msgsz)) { fprintf(stderr, "%s contained unexpected data instead of %s\n", stream, what); if (rune) fprintf(stderr, "(command run was: %s )\n", rune); return ERROR_FAIL; } return 0; } static void migration_child_report(pid_t migration_child, int recv_fd) { pid_t child; int status, sr; struct timeval now, waituntil, timeout; static const struct timeval pollinterval = { 0, 1000 }; /* 1ms */ if (!migration_child) return; CHK_ERRNO( gettimeofday(&waituntil, 0) ); waituntil.tv_sec += 2; for (;;) { child = waitpid(migration_child, &status, WNOHANG); if (child == migration_child) { if (status) libxl_report_child_exitstatus(&ctx, XTL_INFO, "migration target process", migration_child, status); break; } if (child == -1) { if (errno == EINTR) continue; fprintf(stderr, "wait for migration child [%ld] failed: %s\n", (long)migration_child, strerror(errno)); break; } assert(child == 0); CHK_ERRNO( gettimeofday(&now, 0) ); if (timercmp(&now, &waituntil, >)) { fprintf(stderr, "migration child [%ld] not exiting, no longer" " waiting (exit status will be unreported)\n", (long)migration_child); break; } timersub(&waituntil, &now, &timeout); if (recv_fd >= 0) { fd_set readfds, exceptfds; FD_ZERO(&readfds); FD_ZERO(&exceptfds); FD_SET(recv_fd, &readfds); FD_SET(recv_fd, &exceptfds); sr = select(recv_fd+1, &readfds,0,&exceptfds, &timeout); } else { if (timercmp(&timeout, &pollinterval, >)) timeout = pollinterval; sr = select(0,0,0,0, &timeout); } if (sr > 0) { recv_fd = -1; } else if (sr == 0) { } else if (sr == -1) { if (errno != EINTR) { fprintf(stderr, "migration child [%ld] exit wait select" " failed unexpectedly: %s\n", (long)migration_child, strerror(errno)); break; } } } migration_child = 0; } static void migrate_domain(const char *domain_spec, const char *rune, const char *override_config_file) { pid_t child = -1; int rc; int sendpipe[2], recvpipe[2]; int send_fd, recv_fd; libxl_domain_suspend_info suspinfo; char *away_domname; char rc_buf; uint8_t *config_data; int config_len; save_domain_core_begin(domain_spec, override_config_file, &config_data, &config_len); if (!config_len) { fprintf(stderr, "No config file stored for running domain and " "none supplied - cannot migrate.\n"); exit(1); } MUST( libxl_pipe(&ctx, sendpipe) ); MUST( libxl_pipe(&ctx, recvpipe) ); child = libxl_fork(&ctx); if (child==-1) exit(1); if (!child) { dup2(sendpipe[0], 0); dup2(recvpipe[1], 1); close(sendpipe[0]); close(sendpipe[1]); close(recvpipe[0]); close(recvpipe[1]); execlp("sh","sh","-c",rune,(char*)0); perror("failed to exec sh"); exit(-1); } close(sendpipe[0]); close(recvpipe[1]); send_fd = sendpipe[1]; recv_fd = recvpipe[0]; signal(SIGPIPE, SIG_IGN); /* if receiver dies, we get an error and can clean up rather than just dying */ rc = migrate_read_fixedmessage(recv_fd, migrate_receiver_banner, sizeof(migrate_receiver_banner)-1, "banner", rune); if (rc) { close(send_fd); migration_child_report(child, recv_fd); exit(-rc); } save_domain_core_writeconfig(send_fd, "migration stream", config_data, config_len); xtl_stdiostream_adjust_flags(logger, XTL_STDIOSTREAM_HIDE_PROGRESS, 0); memset(&suspinfo, 0, sizeof(suspinfo)); suspinfo.flags |= XL_SUSPEND_LIVE; rc = libxl_domain_suspend(&ctx, &suspinfo, domid, send_fd); if (rc) { fprintf(stderr, "migration sender: libxl_domain_suspend failed" " (rc=%d)\n", rc); if (rc == ERROR_GUEST_TIMEDOUT) goto failed_suspend; else goto failed_resume; } //fprintf(stderr, "migration sender: Transfer complete.\n"); // Should only be printed when debugging as it's a bit messy with // progress indication. rc = migrate_read_fixedmessage(recv_fd, migrate_receiver_ready, sizeof(migrate_receiver_ready), "ready message", rune); if (rc) goto failed_resume; xtl_stdiostream_adjust_flags(logger, 0, XTL_STDIOSTREAM_HIDE_PROGRESS); /* right, at this point we are about give the destination * permission to rename and resume, so we must first rename the * domain away ourselves */ fprintf(stderr, "migration sender: Target has acknowledged transfer.\n"); if (common_domname) { if (asprintf(&away_domname, "%s--migratedaway", common_domname) < 0) goto failed_resume; rc = libxl_domain_rename(&ctx, domid, common_domname, away_domname, 0); if (rc) goto failed_resume; } /* point of no return - as soon as we have tried to say * "go" to the receiver, it's not safe to carry on. We leave * the domain renamed to %s--migratedaway in case that's helpful. */ fprintf(stderr, "migration sender: Giving target permission to start.\n"); rc = libxl_write_exactly(&ctx, send_fd, migrate_permission_to_go, sizeof(migrate_permission_to_go), "migration stream", "GO message"); if (rc) goto failed_badly; rc = migrate_read_fixedmessage(recv_fd, migrate_report, sizeof(migrate_report), "success/failure report message", rune); if (rc) goto failed_badly; rc = libxl_read_exactly(&ctx, recv_fd, &rc_buf, 1, "migration ack stream", "success/failure status"); if (rc) goto failed_badly; if (rc_buf) { fprintf(stderr, "migration sender: Target reports startup failure" " (status code %d).\n", rc_buf); rc = migrate_read_fixedmessage(recv_fd, migrate_permission_to_go, sizeof(migrate_permission_to_go), "permission for sender to resume", rune); if (rc) goto failed_badly; fprintf(stderr, "migration sender: Trying to resume at our end.\n"); if (common_domname) { libxl_domain_rename(&ctx, domid, away_domname, common_domname, 0); } rc = libxl_domain_resume(&ctx, domid); if (!rc) fprintf(stderr, "migration sender: Resumed OK.\n"); fprintf(stderr, "Migration failed due to problems at target.\n"); exit(-ERROR_FAIL); } fprintf(stderr, "migration sender: Target reports successful startup.\n"); libxl_domain_destroy(&ctx, domid, 1); /* bang! */ fprintf(stderr, "Migration successful.\n"); exit(0); failed_suspend: close(send_fd); migration_child_report(child, recv_fd); fprintf(stderr, "Migration failed, failed to suspend at sender.\n"); exit(-ERROR_FAIL); failed_resume: close(send_fd); migration_child_report(child, recv_fd); fprintf(stderr, "Migration failed, resuming at sender.\n"); libxl_domain_resume(&ctx, domid); exit(-ERROR_FAIL); failed_badly: fprintf(stderr, "** Migration failed during final handshake **\n" "Domain state is now undefined !\n" "Please CHECK AT BOTH ENDS for running instances, before renaming and\n" " resuming at most one instance. Two simultaneous instances of the domain\n" " would probably result in SEVERE DATA LOSS and it is now your\n" " responsibility to avoid that. Sorry.\n"); close(send_fd); migration_child_report(child, recv_fd); exit(-ERROR_BADFAIL); } static void core_dump_domain(const char *domain_spec, const char *filename) { int rc; find_domain(domain_spec); rc=libxl_domain_core_dump(&ctx, domid, filename); if (rc) { fprintf(stderr,"core dump failed (rc=%d)\n",rc);exit(-1); } } static void migrate_receive(int debug, int daemonize) { int rc, rc2; char rc_buf; char *migration_domname; struct domain_create dom_info; signal(SIGPIPE, SIG_IGN); /* if we get SIGPIPE we'd rather just have it as an error */ fprintf(stderr, "migration target: Ready to receive domain.\n"); CHK_ERRNO( libxl_write_exactly(&ctx, 1, migrate_receiver_banner, sizeof(migrate_receiver_banner)-1, "migration ack stream", "banner") ); memset(&dom_info, 0, sizeof(dom_info)); dom_info.debug = debug; dom_info.daemonize = daemonize; dom_info.paused = 1; dom_info.restore_file = "incoming migration stream"; dom_info.migrate_fd = 0; /* stdin */ dom_info.migration_domname_r = &migration_domname; rc = create_domain(&dom_info); if (rc < 0) { fprintf(stderr, "migration target: Domain creation failed" " (code %d).\n", rc); exit(-rc); } fprintf(stderr, "migration target: Transfer complete," " requesting permission to start domain.\n"); rc = libxl_write_exactly(&ctx, 1, migrate_receiver_ready, sizeof(migrate_receiver_ready), "migration ack stream", "ready message"); if (rc) exit(-rc); rc = migrate_read_fixedmessage(0, migrate_permission_to_go, sizeof(migrate_permission_to_go), "GO message", 0); if (rc) goto perhaps_destroy_notify_rc; fprintf(stderr, "migration target: Got permission, starting domain.\n"); if (migration_domname) { rc = libxl_domain_rename(&ctx, domid, migration_domname, common_domname, 0); if (rc) goto perhaps_destroy_notify_rc; } rc = libxl_domain_unpause(&ctx, domid); if (rc) goto perhaps_destroy_notify_rc; fprintf(stderr, "migration target: Domain started successsfully.\n"); rc = 0; perhaps_destroy_notify_rc: rc2 = libxl_write_exactly(&ctx, 1, migrate_report, sizeof(migrate_report), "migration ack stream", "success/failure report"); if (rc2) exit(-ERROR_BADFAIL); rc_buf = -rc; assert(!!rc_buf == !!rc); rc2 = libxl_write_exactly(&ctx, 1, &rc_buf, 1, "migration ack stream", "success/failure code"); if (rc2) exit(-ERROR_BADFAIL); if (rc) { fprintf(stderr, "migration target: Failure, destroying our copy.\n"); rc2 = libxl_domain_destroy(&ctx, domid, 1); if (rc2) { fprintf(stderr, "migration target: Failed to destroy our copy" " (code %d).\n", rc2); exit(-ERROR_BADFAIL); } fprintf(stderr, "migration target: Cleanup OK, granting sender" " permission to resume.\n"); rc2 = libxl_write_exactly(&ctx, 1, migrate_permission_to_go, sizeof(migrate_permission_to_go), "migration ack stream", "permission to sender to have domain back"); if (rc2) exit(-ERROR_BADFAIL); } exit(0); } int main_restore(int argc, char **argv) { const char *checkpoint_file = NULL; const char *config_file = NULL; struct domain_create dom_info; int paused = 0, debug = 0, daemonize = 1, console_autoconnect = 0; int opt, rc; while ((opt = getopt(argc, argv, "chpde")) != -1) { switch (opt) { case 'c': console_autoconnect = 1; break; case 'p': paused = 1; break; case 'd': debug = 1; break; case 'e': daemonize = 0; break; case 'h': help("restore"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (argc-optind == 1) { checkpoint_file = argv[optind]; } else if (argc-optind == 2) { config_file = argv[optind]; checkpoint_file = argv[optind + 1]; } else { help("restore"); return 2; } memset(&dom_info, 0, sizeof(dom_info)); dom_info.debug = debug; dom_info.daemonize = daemonize; dom_info.paused = paused; dom_info.config_file = config_file; dom_info.restore_file = checkpoint_file; dom_info.migrate_fd = -1; dom_info.console_autoconnect = console_autoconnect; rc = create_domain(&dom_info); if (rc < 0) return -rc; return 0; } int main_migrate_receive(int argc, char **argv) { int debug = 0, daemonize = 1; int opt; while ((opt = getopt(argc, argv, "hed")) != -1) { switch (opt) { case 'h': help("migrate-receive"); return 2; break; case 'e': daemonize = 0; break; case 'd': debug = 1; break; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (argc-optind != 0) { help("migrate-receive"); return 2; } migrate_receive(debug, daemonize); return 0; } int main_save(int argc, char **argv) { const char *filename = NULL, *p = NULL; const char *config_filename; int checkpoint = 0; int opt; while ((opt = getopt(argc, argv, "hc")) != -1) { switch (opt) { case 'c': checkpoint = 1; break; case 'h': help("save"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (argc-optind < 1 || argc-optind > 3) { help("save"); return 2; } p = argv[optind]; filename = argv[optind + 1]; config_filename = argv[optind + 2]; save_domain(p, filename, checkpoint, config_filename); return 0; } int main_migrate(int argc, char **argv) { const char *p = NULL; const char *config_filename = NULL; const char *ssh_command = "ssh"; char *rune = NULL; char *host; int opt, daemonize = 1, debug = 0; while ((opt = getopt(argc, argv, "hC:s:ed")) != -1) { switch (opt) { case 'h': help("migrate"); return 0; case 'C': config_filename = optarg; break; case 's': ssh_command = optarg; break; case 'e': daemonize = 0; break; case 'd': debug = 1; break; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (argc-optind < 2 || argc-optind > 2) { help("migrate"); return 2; } p = argv[optind]; host = argv[optind + 1]; if (!ssh_command[0]) { rune= host; } else { if (asprintf(&rune, "exec %s %s xl migrate-receive%s%s", ssh_command, host, daemonize ? "" : " -e", debug ? " -d" : "") < 0) return 1; } migrate_domain(p, rune, config_filename); return 0; } int main_dump_core(int argc, char **argv) { int opt; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("dump-core"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if ( argc-optind < 2 ) { help("dump-core"); return 2; } core_dump_domain(argv[optind], argv[optind + 1]); return 0; } int main_pause(int argc, char **argv) { int opt; const char *p; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("pause"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (optind >= argc) { help("pause"); return 2; } p = argv[optind]; pause_domain(p); return 0; } int main_unpause(int argc, char **argv) { int opt; const char *p; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("unpause"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (optind >= argc) { help("unpause"); return 2; } p = argv[optind]; unpause_domain(p); return 0; } int main_destroy(int argc, char **argv) { int opt; const char *p; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("destroy"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (optind >= argc) { help("destroy"); return 2; } p = argv[optind]; destroy_domain(p); return 0; } int main_shutdown(int argc, char **argv) { int opt; int wait = 0; const char *p; while ((opt = getopt(argc, argv, "hw")) != -1) { switch (opt) { case 'h': help("shutdown"); return 0; case 'w': wait = 1; break; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (optind >= argc) { help("shutdown"); return 2; } p = argv[optind]; shutdown_domain(p, wait); return 0; } int main_reboot(int argc, char **argv) { int opt; const char *p; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("reboot"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (optind >= argc) { help("reboot"); return 2; } p = argv[optind]; reboot_domain(p); return 0; } int main_list(int argc, char **argv) { int opt, verbose = 0; int details = 0; int option_index = 0; static struct option long_options[] = { {"long", 0, 0, 'l'}, {"help", 0, 0, 'h'}, {"verbose", 0, 0, 'v'}, {0, 0, 0, 0} }; libxl_dominfo info_buf; libxl_dominfo *info, *info_free=0; int nb_domain, rc; while (1) { opt = getopt_long(argc, argv, "lvh", long_options, &option_index); if (opt == -1) break; switch (opt) { case 'l': details = 1; break; case 'h': help("list"); return 0; case 'v': verbose = 1; break; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (optind >= argc) { info = libxl_list_domain(&ctx, &nb_domain); if (!info) { fprintf(stderr, "libxl_domain_infolist failed.\n"); return 1; } info_free = info; } else if (optind == argc-1) { find_domain(argv[optind]); rc = libxl_domain_info(&ctx, &info_buf, domid); if (rc == ERROR_INVAL) { fprintf(stderr, "Error: Domain \'%s\' does not exist.\n", argv[optind]); return -rc; } if (rc) { fprintf(stderr, "libxl_domain_info failed (code %d).\n", rc); return -rc; } info = &info_buf; nb_domain = 1; } else { help("list"); return 2; } if (details) list_domains_details(info, nb_domain); else list_domains(verbose, info, nb_domain); free(info_free); return 0; } int main_list_vm(int argc, char **argv) { int opt; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("list-vm"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } list_vm(); return 0; } int main_create(int argc, char **argv) { const char *filename = NULL; char *p; char extra_config[1024]; struct domain_create dom_info; int paused = 0, debug = 0, daemonize = 1, console_autoconnect = 0, dryrun = 0, quiet = 0; int opt, rc; int option_index = 0; static struct option long_options[] = { {"dryrun", 0, 0, 'n'}, {"quiet", 0, 0, 'q'}, {"help", 0, 0, 'h'}, {"defconfig", 1, 0, 'f'}, {0, 0, 0, 0} }; if (argv[1] && argv[1][0] != '-' && !strchr(argv[1], '=')) { filename = argv[1]; argc--; argv++; } while (1) { opt = getopt_long(argc, argv, "hnqf:pcde", long_options, &option_index); if (opt == -1) break; switch (opt) { case 'f': filename = optarg; break; case 'p': paused = 1; break; case 'c': console_autoconnect = 1; break; case 'd': debug = 1; break; case 'e': daemonize = 0; break; case 'h': help("create"); return 0; case 'n': dryrun = 1; break; case 'q': quiet = 1; break; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } extra_config[0] = '\0'; for (p = extra_config; optind < argc; optind++) { if (strchr(argv[optind], '=') != NULL) { p += snprintf(p, sizeof(extra_config) - (p - extra_config), "%s\n", argv[optind]); } else if (!filename) { filename = argv[optind]; } else { help("create"); return 2; } } memset(&dom_info, 0, sizeof(dom_info)); dom_info.debug = debug; dom_info.daemonize = daemonize; dom_info.paused = paused; dom_info.dryrun = dryrun; dom_info.quiet = quiet; dom_info.config_file = filename; dom_info.extra_config = extra_config; dom_info.migrate_fd = -1; dom_info.console_autoconnect = console_autoconnect; rc = create_domain(&dom_info); if (rc < 0) return -rc; return 0; } static void button_press(const char *p, const char *b) { libxl_button button; find_domain(p); if (!strcmp(b, "power")) { button = POWER_BUTTON; } else if (!strcmp(b, "sleep")) { button = SLEEP_BUTTON; } else { fprintf(stderr, "%s is an invalid button identifier\n", b); exit(2); } libxl_button_press(&ctx, domid, button); } int main_button_press(int argc, char **argv) { int opt; const char *p; const char *b; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("button-press"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (optind >= argc - 1) { help("button-press"); return 2; } p = argv[optind]; b = argv[optind + 1]; button_press(p, b); return 0; } static void print_bitmap(uint8_t *map, int maplen, FILE *stream) { int i; uint8_t pmap = 0, bitmask = 0; int firstset = 0, state = 0; for (i = 0; i < maplen; i++) { if (i % 8 == 0) { pmap = *map++; bitmask = 1; } else bitmask <<= 1; switch (state) { case 0: case 2: if ((pmap & bitmask) != 0) { firstset = i; state++; } continue; case 1: case 3: if ((pmap & bitmask) == 0) { fprintf(stream, "%s%d", state > 1 ? "," : "", firstset); if (i - 1 > firstset) fprintf(stream, "-%d", i - 1); state = 2; } continue; } } switch (state) { case 0: fprintf(stream, "none"); break; case 2: break; case 1: if (firstset == 0) { fprintf(stream, "any cpu"); break; } case 3: fprintf(stream, "%s%d", state > 1 ? "," : "", firstset); if (i - 1 > firstset) fprintf(stream, "-%d", i - 1); break; } } static void print_vcpuinfo(uint32_t tdomid, const libxl_vcpuinfo *vcpuinfo, uint32_t nr_cpus) { char *domname; /* NAME ID VCPU */ domname = libxl_domid_to_name(&ctx, tdomid); printf("%-32s %5u %5u", domname, tdomid, vcpuinfo->vcpuid); free(domname); if (!vcpuinfo->online) { /* CPU STA */ printf("%5c %3c%cp ", '-', '-', '-'); } else { /* CPU STA */ printf("%5u %3c%c- ", vcpuinfo->cpu, vcpuinfo->running ? 'r' : '-', vcpuinfo->blocked ? 'b' : '-'); } /* TIM */ printf("%9.1f ", ((float)vcpuinfo->vcpu_time / 1e9)); /* CPU AFFINITY */ print_bitmap(vcpuinfo->cpumap.map, nr_cpus, stdout); printf("\n"); } static void print_domain_vcpuinfo(uint32_t domid, uint32_t nr_cpus) { libxl_vcpuinfo *vcpuinfo; int i, nb_vcpu, nrcpus; vcpuinfo = libxl_list_vcpu(&ctx, domid, &nb_vcpu, &nrcpus); if (!vcpuinfo) { fprintf(stderr, "libxl_list_vcpu failed.\n"); return; } for (i = 0; i < nb_vcpu; i++) { print_vcpuinfo(domid, &vcpuinfo[i], nr_cpus); libxl_vcpuinfo_destroy(&vcpuinfo[i]); } free(vcpuinfo); } static void vcpulist(int argc, char **argv) { libxl_dominfo *dominfo; libxl_physinfo physinfo; int i, nb_domain; if (libxl_get_physinfo(&ctx, &physinfo) != 0) { fprintf(stderr, "libxl_physinfo failed.\n"); goto vcpulist_out; } printf("%-32s %5s %5s %5s %5s %9s %s\n", "Name", "ID", "VCPU", "CPU", "State", "Time(s)", "CPU Affinity"); if (!argc) { if (!(dominfo = libxl_list_domain(&ctx, &nb_domain))) { fprintf(stderr, "libxl_list_domain failed.\n"); goto vcpulist_out; } for (i = 0; i 0; ++argv, --argc) { if (domain_qualifier_to_domid(*argv, &domid, 0) < 0) { fprintf(stderr, "%s is an invalid domain identifier\n", *argv); goto vcpulist_out; } print_domain_vcpuinfo(domid, physinfo.nr_cpus); } } vcpulist_out: ; } int main_vcpulist(int argc, char **argv) { int opt; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("vcpu-list"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } vcpulist(argc - optind, argv + optind); return 0; } static void vcpupin(const char *d, const char *vcpu, char *cpu) { libxl_vcpuinfo *vcpuinfo; libxl_cpumap cpumap; uint32_t vcpuid, cpuida, cpuidb; char *endptr, *toka, *tokb; int i, nb_vcpu; vcpuid = strtoul(vcpu, &endptr, 10); if (vcpu == endptr) { if (strcmp(vcpu, "all")) { fprintf(stderr, "Error: Invalid argument.\n"); return; } vcpuid = -1; } find_domain(d); if (libxl_cpumap_alloc(&ctx, &cpumap)) { goto vcpupin_out; } if (strcmp(cpu, "all")) { for (toka = strtok(cpu, ","), i = 0; toka; toka = strtok(NULL, ","), ++i) { cpuida = strtoul(toka, &endptr, 10); if (toka == endptr) { fprintf(stderr, "Error: Invalid argument.\n"); goto vcpupin_out1; } if (*endptr == '-') { tokb = endptr + 1; cpuidb = strtoul(tokb, &endptr, 10); if ((tokb == endptr) || (cpuida > cpuidb)) { fprintf(stderr, "Error: Invalid argument.\n"); goto vcpupin_out1; } while (cpuida <= cpuidb) { libxl_cpumap_set(&cpumap, cpuida); ++cpuida; } } else { libxl_cpumap_set(&cpumap, cpuida); } } } else { memset(cpumap.map, -1, cpumap.size); } if (vcpuid != -1) { if (libxl_set_vcpuaffinity(&ctx, domid, vcpuid, &cpumap) == -1) { fprintf(stderr, "Could not set affinity for vcpu `%u'.\n", vcpuid); } } else { if (!(vcpuinfo = libxl_list_vcpu(&ctx, domid, &nb_vcpu, &i))) { fprintf(stderr, "libxl_list_vcpu failed.\n"); goto vcpupin_out1; } for (; nb_vcpu > 0; --nb_vcpu, ++vcpuinfo) { if (libxl_set_vcpuaffinity(&ctx, domid, vcpuinfo->vcpuid, &cpumap) == -1) { fprintf(stderr, "libxl_set_vcpuaffinity failed on vcpu `%u'.\n", vcpuinfo->vcpuid); } } } vcpupin_out1: libxl_cpumap_destroy(&cpumap); vcpupin_out: ; } int main_vcpupin(int argc, char **argv) { int opt; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("vcpu-pin"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (optind != argc - 3) { help("vcpu-pin"); return 2; } vcpupin(argv[optind], argv[optind+1] , argv[optind+2]); return 0; } static void vcpuset(const char *d, const char* nr_vcpus) { char *endptr; unsigned int max_vcpus, i; libxl_cpumap cpumap; max_vcpus = strtoul(nr_vcpus, &endptr, 10); if (nr_vcpus == endptr) { fprintf(stderr, "Error: Invalid argument.\n"); return; } find_domain(d); if (libxl_cpumap_alloc(&ctx, &cpumap)) { fprintf(stderr, "libxl_cpumap_alloc failed\n"); return; } for (i = 0; i < max_vcpus; i++) libxl_cpumap_set(&cpumap, i); if (libxl_set_vcpuonline(&ctx, domid, &cpumap) < 0) fprintf(stderr, "libxl_set_vcpuonline failed domid=%d max_vcpus=%d\n", domid, max_vcpus); libxl_cpumap_destroy(&cpumap); } int main_vcpuset(int argc, char **argv) { int opt; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("vcpu-set"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (optind >= argc - 1) { help("vcpu-set"); return 2; } vcpuset(argv[optind], argv[optind+1]); return 0; } static void output_xeninfo(void) { const libxl_version_info *info; int sched_id; if (!(info = libxl_get_version_info(&ctx))) { fprintf(stderr, "libxl_get_version_info failed.\n"); return; } if ((sched_id = libxl_get_sched_id(&ctx)) < 0) { fprintf(stderr, "get_sched_id sysctl failed.\n"); return; } printf("xen_major : %d\n", info->xen_version_major); printf("xen_minor : %d\n", info->xen_version_minor); printf("xen_extra : %s\n", info->xen_version_extra); printf("xen_caps : %s\n", info->capabilities); printf("xen_scheduler : %s\n", libxl_schedid_to_name(&ctx, sched_id)); printf("xen_pagesize : %lu\n", info->pagesize); printf("platform_params : virt_start=0x%lx\n", info->virt_start); printf("xen_changeset : %s\n", info->changeset); printf("xen_commandline : %s\n", info->commandline); printf("cc_compiler : %s\n", info->compiler); printf("cc_compile_by : %s\n", info->compile_by); printf("cc_compile_domain : %s\n", info->compile_domain); printf("cc_compile_date : %s\n", info->compile_date); return; } static void output_nodeinfo(void) { struct utsname utsbuf; if (uname(&utsbuf) < 0) return; printf("host : %s\n", utsbuf.nodename); printf("release : %s\n", utsbuf.release); printf("version : %s\n", utsbuf.version); printf("machine : %s\n", utsbuf.machine); } static void output_physinfo(void) { libxl_physinfo info; const libxl_version_info *vinfo; unsigned int i; libxl_cpumap cpumap; int n = 0; if (libxl_get_physinfo(&ctx, &info) != 0) { fprintf(stderr, "libxl_physinfo failed.\n"); return; } printf("nr_cpus : %d\n", info.nr_cpus); printf("nr_nodes : %d\n", info.nr_nodes); printf("cores_per_socket : %d\n", info.cores_per_socket); printf("threads_per_core : %d\n", info.threads_per_core); printf("cpu_mhz : %d\n", info.cpu_khz / 1000); printf("hw_caps : "); for (i = 0; i < 8; i++) printf("%08x%c", info.hw_cap[i], i < 7 ? ':' : '\n'); printf("virt_caps :"); if (info.phys_cap & XEN_SYSCTL_PHYSCAP_hvm) printf(" hvm"); if (info.phys_cap & XEN_SYSCTL_PHYSCAP_hvm_directio) printf(" hvm_directio"); printf("\n"); vinfo = libxl_get_version_info(&ctx); if (vinfo) { i = (1 << 20) / vinfo->pagesize; printf("total_memory : %"PRIu64"\n", info.total_pages / i); printf("free_memory : %"PRIu64"\n", info.free_pages / i); } if (!libxl_get_freecpus(&ctx, &cpumap)) { libxl_for_each_cpu(i, cpumap) if (libxl_cpumap_test(&cpumap, i)) n++; printf("free_cpus : %d\n", n); free(cpumap.map); } return; } static void output_topologyinfo(void) { libxl_topologyinfo info; int i; if (libxl_get_topologyinfo(&ctx, &info)) { fprintf(stderr, "libxl_get_topologyinfo failed.\n"); return; } printf("cpu_topology :\n"); printf("cpu: core socket node\n"); for (i = 0; i < info.coremap.entries; i++) { if (info.coremap.array[i] != LIBXL_CPUARRAY_INVALID_ENTRY) printf("%3d: %4d %4d %4d\n", i, info.coremap.array[i], info.socketmap.array[i], info.nodemap.array[i]); } printf("numa_info : none\n"); libxl_topologyinfo_destroy(&info); return; } static void info(int numa) { output_nodeinfo(); output_physinfo(); if (numa) output_topologyinfo(); output_xeninfo(); printf("xend_config_format : 4\n"); return; } int main_info(int argc, char **argv) { int opt; int option_index = 0; static struct option long_options[] = { {"help", 0, 0, 'h'}, {"numa", 0, 0, 'n'}, {0, 0, 0, 0} }; int numa = 0; while ((opt = getopt_long(argc, argv, "hn", long_options, &option_index)) != -1) { switch (opt) { case 'h': help("info"); return 0; case 'n': numa = 1; break; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } info(numa); return 0; } static int sched_credit_domain_get( int domid, libxl_sched_credit *scinfo) { int rc; rc = libxl_sched_credit_domain_get(&ctx, domid, scinfo); if (rc) fprintf(stderr, "libxl_sched_credit_domain_get failed.\n"); return rc; } static int sched_credit_domain_set( int domid, libxl_sched_credit *scinfo) { int rc; rc = libxl_sched_credit_domain_set(&ctx, domid, scinfo); if (rc) fprintf(stderr, "libxl_sched_credit_domain_set failed.\n"); return rc; } static void sched_credit_domain_output( int domid, libxl_sched_credit *scinfo) { char *domname; domname = libxl_domid_to_name(&ctx, domid); printf("%-33s %4d %6d %4d\n", domname, domid, scinfo->weight, scinfo->cap); free(domname); } int main_sched_credit(int argc, char **argv) { libxl_dominfo *info; libxl_sched_credit scinfo; int nb_domain, i; const char *dom = NULL; int weight = 256, cap = 0, opt_w = 0, opt_c = 0; int opt, rc; while ((opt = getopt(argc, argv, "hd:w:c:")) != -1) { switch (opt) { case 'd': dom = optarg; break; case 'w': weight = strtol(optarg, NULL, 10); opt_w = 1; break; case 'c': cap = strtol(optarg, NULL, 10); opt_c = 1; break; case 'h': help("sched-credit"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (!dom && (opt_w || opt_c)) { fprintf(stderr, "Must specify a domain.\n"); return 1; } if (!dom) { /* list all domain's credit scheduler info */ info = libxl_list_domain(&ctx, &nb_domain); if (!info) { fprintf(stderr, "libxl_domain_infolist failed.\n"); return 1; } printf("%-33s %4s %6s %4s\n", "Name", "ID", "Weight", "Cap"); for (i = 0; i < nb_domain; i++) { rc = sched_credit_domain_get(info[i].domid, &scinfo); if (rc) return -rc; sched_credit_domain_output(info[i].domid, &scinfo); } } else { find_domain(dom); rc = sched_credit_domain_get(domid, &scinfo); if (rc) return -rc; if (!opt_w && !opt_c) { /* output credit scheduler info */ printf("%-33s %4s %6s %4s\n", "Name", "ID", "Weight", "Cap"); sched_credit_domain_output(domid, &scinfo); } else { /* set credit scheduler paramaters */ if (opt_w) scinfo.weight = weight; if (opt_c) scinfo.cap = cap; rc = sched_credit_domain_set(domid, &scinfo); if (rc) return -rc; } } return 0; } int main_domid(int argc, char **argv) { int opt; const char *domname = NULL; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("domid"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } domname = argv[optind]; if (!domname) { fprintf(stderr, "Must specify a domain name.\n\n"); help("domid"); return 1; } if (libxl_name_to_domid(&ctx, domname, &domid)) { fprintf(stderr, "Can't get domid of domain name '%s', maybe this domain does not exist.\n", domname); return 1; } printf("%d\n", domid); return 0; } int main_domname(int argc, char **argv) { int opt; char *domname = NULL; char *endptr = NULL; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("domname"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (!argv[optind]) { fprintf(stderr, "Must specify a domain id.\n\n"); help("domname"); return 1; } domid = strtol(argv[optind], &endptr, 10); if (domid == 0 && !strcmp(endptr, argv[optind])) { /*no digits at all*/ fprintf(stderr, "Invalid domain id.\n\n"); return 1; } domname = libxl_domid_to_name(&ctx, domid); if (!domname) { fprintf(stderr, "Can't get domain name of domain id '%d', maybe this domain does not exist.\n", domid); return 1; } printf("%s\n", domname); free(domname); return 0; } int main_rename(int argc, char **argv) { int opt; const char *dom; const char *new_name; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("rename"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } dom = argv[optind++]; if (!dom || !argv[optind]) { fprintf(stderr, "'xl rename' requires 2 arguments.\n\n"); help("rename"); return 1; } find_domain(dom); new_name = argv[optind]; if (libxl_domain_rename(&ctx, domid, common_domname, new_name, 0)) { fprintf(stderr, "Can't rename domain '%s'.\n", dom); return 1; } return 0; } int main_trigger(int argc, char **argv) { int opt; char *trigger_name = NULL; char *endptr = NULL; const char *dom = NULL; int vcpuid = 0; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("trigger"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } dom = argv[optind++]; if (!dom || !argv[optind]) { fprintf(stderr, "'xl trigger' requires between 2 and 3 arguments.\n\n"); help("trigger"); return 1; } find_domain(dom); trigger_name = argv[optind++]; if (argv[optind]) { vcpuid = strtol(argv[optind], &endptr, 10); if (vcpuid == 0 && !strcmp(endptr, argv[optind])) { fprintf(stderr, "Invalid vcpuid, using default vcpuid=0.\n\n"); } } libxl_send_trigger(&ctx, domid, trigger_name, vcpuid); return 0; } int main_sysrq(int argc, char **argv) { int opt; const char *sysrq = NULL; const char *dom = NULL; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("sysrq"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } dom = argv[optind++]; if (!dom || !argv[optind]) { fprintf(stderr, "'xl sysrq' requires 2 arguments.\n\n"); help("sysrq"); return 1; } find_domain(dom); sysrq = argv[optind]; if (sysrq[1] != '\0') { fprintf(stderr, "Invalid sysrq.\n\n"); help("sysrq"); return 1; } libxl_send_sysrq(&ctx, domid, sysrq[0]); return 0; } int main_debug_keys(int argc, char **argv) { int opt; char *keys; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("debug-keys"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (optind >= argc) { help("debug-keys"); return 2; } keys = argv[optind]; if (libxl_send_debug_keys(&ctx, keys)) { fprintf(stderr, "cannot send debug keys: %s\n", keys); return 1; } return 0; } int main_dmesg(int argc, char **argv) { unsigned int clear = 0; libxl_xen_console_reader *cr; char *line; int opt, ret = 1; while ((opt = getopt(argc, argv, "hc")) != -1) { switch (opt) { case 'c': clear = 1; break; case 'h': help("dmesg"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } cr = libxl_xen_console_read_start(&ctx, clear); if (!cr) goto finish; while ((ret = libxl_xen_console_read_line(&ctx, cr, &line)) > 0) printf("%s", line); finish: libxl_xen_console_read_finish(&ctx, cr); return ret; } int main_top(int argc, char **argv) { int opt; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("top"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } return system("xentop"); } int main_networkattach(int argc, char **argv) { int opt; libxl_device_nic nic; char *endptr; const char *tok; int i; unsigned int val; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("network-attach"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if ((argc-optind < 2) || (argc-optind > 11)) { help("network-attach"); return 0; } if (domain_qualifier_to_domid(argv[optind], &domid, 0) < 0) { fprintf(stderr, "%s is an invalid domain identifier\n", argv[optind]); return 1; } libxl_device_nic_init(&nic, -1); for (argv += optind+1, argc -= optind+1; argc > 0; ++argv, --argc) { if (!strncmp("type=", *argv, 5)) { if (!strncmp("vif", (*argv) + 5, 4)) { nic.nictype = NICTYPE_VIF; } else if (!strncmp("ioemu", (*argv) + 5, 5)) { nic.nictype = NICTYPE_IOEMU; } else { fprintf(stderr, "Invalid parameter `type'.\n"); return 1; } } else if (!strncmp("mac=", *argv, 4)) { tok = strtok((*argv) + 4, ":"); for (i = 0; tok && i < 6; tok = strtok(NULL, ":"), ++i) { val = strtoul(tok, &endptr, 16); if ((tok == endptr) || (val > 255)) { fprintf(stderr, "Invalid parameter `mac'.\n"); return 1; } nic.mac[i] = val; } } else if (!strncmp("bridge=", *argv, 7)) { nic.bridge = (*argv) + 7; } else if (!strncmp("ip=", *argv, 3)) { free(nic.ip); nic.ip = strdup((*argv) + 3); } else if (!strncmp("script=", *argv, 6)) { nic.script = (*argv) + 6; } else if (!strncmp("backend=", *argv, 8)) { if(libxl_name_to_domid(&ctx, ((*argv) + 8), &val)) { fprintf(stderr, "Specified backend domain does not exist, defaulting to Dom0\n"); val = 0; } nic.backend_domid = val; } else if (!strncmp("vifname=", *argv, 8)) { nic.ifname = (*argv) + 8; } else if (!strncmp("model=", *argv, 6)) { nic.model = (*argv) + 6; } else if (!strncmp("rate=", *argv, 5)) { } else if (!strncmp("accel=", *argv, 6)) { } else { fprintf(stderr, "unrecognized argument `%s'\n", *argv); return 1; } } nic.domid = domid; if (libxl_device_nic_add(&ctx, domid, &nic)) { fprintf(stderr, "libxl_device_nic_add failed.\n"); return 1; } libxl_device_nic_destroy(&nic); return 0; } int main_networklist(int argc, char **argv) { int opt; libxl_nicinfo *nics; unsigned int nb, i; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("network-list"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (argc-optind < 1) { help("network-list"); return 1; } /* Idx BE MAC Hdl Sta evch txr/rxr BE-path */ printf("%-3s %-2s %-17s %-6s %-5s %-6s %5s/%-5s %-30s\n", "Idx", "BE", "Mac Addr.", "handle", "state", "evt-ch", "tx-", "rx-ring-ref", "BE-path"); for (argv += optind, argc -= optind; argc > 0; --argc, ++argv) { if (domain_qualifier_to_domid(*argv, &domid, 0) < 0) { fprintf(stderr, "%s is an invalid domain identifier\n", *argv); continue; } if (!(nics = libxl_list_nics(&ctx, domid, &nb))) { continue; } for (i = 0; i < nb; ++i) { /* Idx BE */ printf("%-3d %-2d ", nics[i].devid, nics[i].backend_id); /* MAC */ printf("%02x:%02x:%02x:%02x:%02x:%02x ", nics[i].mac[0], nics[i].mac[1], nics[i].mac[2], nics[i].mac[3], nics[i].mac[4], nics[i].mac[5]); /* Hdl Sta evch txr/rxr BE-path */ printf("%6d %5d %6d %5d/%-11d %-30s\n", nics[i].devid, nics[i].state, nics[i].evtch, nics[i].rref_tx, nics[i].rref_rx, nics[i].backend); libxl_nicinfo_destroy(&nics[i]); } free(nics); } return 0; } int main_networkdetach(int argc, char **argv) { int opt; libxl_device_nic nic; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("network-detach"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (argc-optind != 2) { help("network-detach"); return 0; } if (domain_qualifier_to_domid(argv[optind], &domid, 0) < 0) { fprintf(stderr, "%s is an invalid domain identifier\n", argv[optind]); return 1; } if (!strchr(argv[optind+1], ':')) { if (libxl_devid_to_device_nic(&ctx, domid, argv[optind+1], &nic)) { fprintf(stderr, "Unknown device %s.\n", argv[optind+1]); return 1; } } else { if (libxl_mac_to_device_nic(&ctx, domid, argv[optind+1], &nic)) { fprintf(stderr, "Unknown device %s.\n", argv[optind+1]); return 1; } } if (libxl_device_nic_del(&ctx, &nic, 1)) { fprintf(stderr, "libxl_device_nic_del failed.\n"); return 1; } libxl_device_nic_destroy(&nic); return 0; } int main_blockattach(int argc, char **argv) { int opt; const char *tok; uint32_t fe_domid, be_domid = 0; libxl_device_disk disk = { 0 }; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("block-attach"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if ((argc-optind < 3) || (argc-optind > 5)) { help("block-attach"); return 0; } tok = strtok(argv[optind+1], ":"); if (!strcmp(tok, "phy")) { disk.backend = DISK_BACKEND_PHY; } else if (!strcmp(tok, "file")) { disk.backend = DISK_BACKEND_TAP; } else if (!strcmp(tok, "tap")) { tok = strtok(NULL, ":"); if (!strcmp(tok, "aio")) { disk.backend = DISK_BACKEND_TAP; } else if (!strcmp(tok, "vhd")) { disk.format = DISK_FORMAT_VHD; disk.backend = DISK_BACKEND_TAP; } else if (!strcmp(tok, "qcow")) { disk.format = DISK_FORMAT_QCOW; disk.backend = DISK_BACKEND_QDISK; } else if (!strcmp(tok, "qcow2")) { disk.format = DISK_FORMAT_QCOW2; disk.backend = DISK_BACKEND_QDISK; } else { fprintf(stderr, "Error: `%s' is not a valid disk image.\n", tok); return 1; } } else { fprintf(stderr, "Error: `%s' is not a valid block device.\n", tok); return 1; } disk.pdev_path = strtok(NULL, "\0"); if (!disk.pdev_path) { fprintf(stderr, "Error: missing path to disk image.\n"); return 1; } disk.vdev = argv[optind+2]; disk.unpluggable = 1; disk.readwrite = ((argc-optind <= 3) || (argv[optind+3][0] == 'w')); if (domain_qualifier_to_domid(argv[optind], &fe_domid, 0) < 0) { fprintf(stderr, "%s is an invalid domain identifier\n", argv[optind]); return 1; } if (argc-optind == 5) { if (domain_qualifier_to_domid(argv[optind+4], &be_domid, 0) < 0) { fprintf(stderr, "%s is an invalid domain identifier\n", argv[optind+4]); return 1; } } disk.domid = fe_domid; disk.backend_domid = be_domid; if (libxl_device_disk_add(&ctx, fe_domid, &disk)) { fprintf(stderr, "libxl_device_disk_add failed.\n"); } return 0; } int main_blocklist(int argc, char **argv) { int opt; int i, nb; libxl_device_disk *disks; libxl_diskinfo diskinfo; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("block-list"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (argc-optind < 1) { help("block-list"); return 0; } printf("%-5s %-3s %-6s %-5s %-6s %-8s %-30s\n", "Vdev", "BE", "handle", "state", "evt-ch", "ring-ref", "BE-path"); for (argv += optind, argc -= optind; argc > 0; --argc, ++argv) { if (domain_qualifier_to_domid(*argv, &domid, 0) < 0) { fprintf(stderr, "%s is an invalid domain identifier\n", *argv); continue; } disks = libxl_device_disk_list(&ctx, domid, &nb); if (!disks) { continue; } for (i=0; i 10)) { help("network2-attach"); return 0; } if (domain_qualifier_to_domid(argv[optind], &domid, 0) < 0) { fprintf(stderr, "%s is an invalid domain identifier\n", argv[optind]); return 1; } libxl_device_net2_init(&net2, -1); for (argv += optind+1, argc -= optind+1; argc > 0; --argc, ++argv) { if (!strncmp("front_mac=", *argv, 10)) { tok = strtok((*argv) + 10, ":"); for (i = 0; tok && i < 6; tok = strtok(NULL, ":"), ++i) { val = strtoul(tok, &endptr, 16); if ((tok == endptr) || (val > 255)) { fprintf(stderr, "Invalid parameter `front_mac'.\n"); return 1; } net2.front_mac[i] = val; } } else if (!strncmp("back_mac=", *argv, 9)) { tok = strtok((*argv) + 10, ":"); for (i = 0; tok && i < 6; tok = strtok(NULL, ":"), ++i) { val = strtoul(tok, &endptr, 16); if ((tok == endptr) || (val > 255)) { fprintf(stderr, "Invalid parameter back_mac=%s.\n", *argv + 9); return 1; } net2.back_mac[i] = val; } } else if (!strncmp("backend=", *argv, 8)) { back_dom = *argv; } else if (!strncmp("trusted=", *argv, 8)) { net2.trusted = (*((*argv) + 8) == '1'); } else if (!strncmp("back_trusted=", *argv, 13)) { net2.back_trusted = (*((*argv) + 13) == '1'); } else if (!strncmp("bridge=", *argv, 7)) { net2.bridge = *argv + 13; } else if (!strncmp("filter_mac=", *argv, 11)) { net2.filter_mac = (*((*argv) + 11) == '1'); } else if (!strncmp("front_filter_mac=", *argv, 17)) { net2.front_filter_mac = (*((*argv) + 17) == '1'); } else if (!strncmp("pdev=", *argv, 5)) { val = strtoul(*argv + 5, &endptr, 10); if (endptr == (*argv + 5)) { fprintf(stderr, "Invalid parameter pdev=%s.\n", *argv + 5); return 1; } net2.pdev = val; } else if (!strncmp("max_bypasses=", *argv, 13)) { val = strtoul(*argv + 13, &endptr, 10); if (endptr == (*argv + 13)) { fprintf(stderr, "Invalid parameter max_bypasses=%s.\n", *argv + 13); return 1; } net2.max_bypasses = val; } else { fprintf(stderr, "unrecognized argument `%s'\n", *argv); return 1; } } if (back_dom) { if (domain_qualifier_to_domid(back_dom, &back_domid, 0) < 0) { fprintf(stderr, "%s is an invalid domain identifier\n", back_dom); return 1; } } net2.domid = domid; net2.backend_domid = back_domid; if (libxl_device_net2_add(&ctx, domid, &net2)) { fprintf(stderr, "libxl_device_net2_add failed.\n"); } return 0; } int main_network2list(int argc, char **argv) { int opt; unsigned int nb; libxl_net2info *net2s; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("network2-list"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (argc - optind < 1) { help("network2-list"); return 0; } printf("%-3s %-2s %-5s %-17s %-17s %-7s %-6s %-30s\n", "Idx", "BE", "state", "Mac Addr.", "Remote Mac Addr.", "trusted", "filter", "backend"); for (argv += optind, argc -=optind; argc > 0; --argc, ++argv) { if (domain_qualifier_to_domid(*argv, &domid, 0) < 0) { fprintf(stderr, "%s is an invalid domain identifier\n", *argv); continue; } if ((net2s = libxl_device_net2_list(&ctx, domid, &nb))) { for (; nb > 0; --nb, ++net2s) { printf("%3d %2d %5d ", net2s->devid, net2s->backend_id, net2s->state); printf("%02x:%02x:%02x:%02x:%02x:%02x ", net2s->mac[0], net2s->mac[1], net2s->mac[2], net2s->mac[3], net2s->mac[4], net2s->mac[5]); printf("%02x:%02x:%02x:%02x:%02x:%02x ", net2s->back_mac[0], net2s->back_mac[1], net2s->back_mac[2], net2s->back_mac[3], net2s->back_mac[4], net2s->back_mac[5]); printf("%-7d %-6d %-30s\n", net2s->trusted, net2s->filter_mac, net2s->backend); } } } return 0; } int main_network2detach(int argc, char **argv) { int opt; libxl_device_net2 net2; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("network2-detach"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if (argc-optind != 2) { help("network2-detach"); return 0; } if (domain_qualifier_to_domid(argv[optind], &domid, 0) < 0) { fprintf(stderr, "%s is an invalid domain identifier\n", argv[optind]); return 1; } if (libxl_devid_to_device_net2(&ctx, domid, argv[optind+1], &net2)) { fprintf(stderr, "Error: Device %s not connected.\n", argv[optind+1]); return 1; } if (libxl_device_net2_del(&ctx, &net2, 1)) { fprintf(stderr, "libxl_device_net2_del failed.\n"); return 1; } return 0; } static char *uptime_to_string(unsigned long time, int short_mode) { int sec, min, hour, day; char *time_string; int ret; day = (int)(time / 86400); time -= (day * 86400); hour = (int)(time / 3600); time -= (hour * 3600); min = (int)(time / 60); time -= (min * 60); sec = time; if (short_mode) if (day > 1) ret = asprintf(&time_string, "%d days, %2d:%02d", day, hour, min); else if (day == 1) ret = asprintf(&time_string, "%d day, %2d:%02d", day, hour, min); else ret = asprintf(&time_string, "%2d:%02d", hour, min); else if (day > 1) ret = asprintf(&time_string, "%d days, %2d:%02d:%02d", day, hour, min, sec); else if (day == 1) ret = asprintf(&time_string, "%d day, %2d:%02d:%02d", day, hour, min, sec); else ret = asprintf(&time_string, "%2d:%02d:%02d", hour, min, sec); if (ret < 0) return NULL; return time_string; } static char *current_time_to_string(time_t now) { char now_str[100]; struct tm *tmp; tmp = localtime(&now); if (tmp == NULL) { fprintf(stderr, "Get localtime error"); exit(-1); } if (strftime(now_str, sizeof(now_str), "%H:%M:%S", tmp) == 0) { fprintf(stderr, "strftime returned 0"); exit(-1); } return strdup(now_str); } static void print_dom0_uptime(int short_mode, time_t now) { int fd; char buf[512]; uint32_t uptime = 0; char *uptime_str = NULL; char *now_str = NULL; char *domname; fd = open("/proc/uptime", O_RDONLY); if (fd == -1) goto err; if (read(fd, buf, sizeof(buf)) == -1) { close(fd); goto err; } close(fd); strtok(buf, " "); uptime = strtoul(buf, NULL, 10); domname = libxl_domid_to_name(&ctx, 0); if (short_mode) { now_str = current_time_to_string(now); uptime_str = uptime_to_string(uptime, 1); printf(" %s up %s, %s (%d)\n", now_str, uptime_str, domname, 0); } else { now_str = NULL; uptime_str = uptime_to_string(uptime, 0); printf("%-33s %4d %s\n", domname, 0, uptime_str); } free(now_str); free(uptime_str); free(domname); return; err: fprintf(stderr, "Can not get Dom0 uptime.\n"); exit(-1); } static void print_domU_uptime(uint32_t domuid, int short_mode, time_t now) { uint32_t s_time = 0; uint32_t uptime = 0; char *uptime_str = NULL; char *now_str = NULL; char *domname; s_time = libxl_vm_get_start_time(&ctx, domuid); if (s_time == -1) return; uptime = now - s_time; domname = libxl_domid_to_name(&ctx, domuid); if (short_mode) { now_str = current_time_to_string(now); uptime_str = uptime_to_string(uptime, 1); printf(" %s up %s, %s (%d)\n", now_str, uptime_str, domname, domuid); } else { now_str = NULL; uptime_str = uptime_to_string(uptime, 0); printf("%-33s %4d %s\n", domname, domuid, uptime_str); } free(domname); free(now_str); free(uptime_str); return; } static void print_uptime(int short_mode, uint32_t doms[], int nb_doms) { libxl_vminfo *info; time_t now; int nb_vm, i; now = time(NULL); if (!short_mode) printf("%-33s %4s %s\n", "Name", "ID", "Uptime"); if (nb_doms == 0) { print_dom0_uptime(short_mode, now); info = libxl_list_vm(&ctx, &nb_vm); for (i = 0; i < nb_vm; i++) print_domU_uptime(info[i].domid, short_mode, now); } else { for (i = 0; i < nb_doms; i++) { if (doms[i] == 0) print_dom0_uptime(short_mode, now); else print_domU_uptime(doms[i], short_mode, now); } } } int main_uptime(int argc, char **argv) { const char *dom = NULL; int short_mode = 0; uint32_t domains[100]; int nb_doms = 0; int opt; while ((opt = getopt(argc, argv, "hs")) != -1) { switch (opt) { case 's': short_mode = 1; break; case 'h': help("uptime"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } for (;(dom = argv[optind]) != NULL; nb_doms++,optind++) { find_domain(dom); domains[nb_doms] = domid; } print_uptime(short_mode, domains, nb_doms); return 0; } int main_tmem_list(int argc, char **argv) { const char *dom = NULL; char *buf = NULL; int use_long = 0; int all = 0; int opt; while ((opt = getopt(argc, argv, "alh")) != -1) { switch (opt) { case 'l': use_long = 1; break; case 'a': all = 1; break; case 'h': help("tmem-list"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } dom = argv[optind]; if (!dom && all == 0) { fprintf(stderr, "You must specify -a or a domain id.\n\n"); help("tmem-list"); return 1; } if (all) domid = -1; else find_domain(dom); buf = libxl_tmem_list(&ctx, domid, use_long); if (buf == NULL) return -1; printf("%s\n", buf); free(buf); return 0; } int main_tmem_freeze(int argc, char **argv) { const char *dom = NULL; int all = 0; int opt; while ((opt = getopt(argc, argv, "ah")) != -1) { switch (opt) { case 'a': all = 1; break; case 'h': help("tmem-freeze"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } dom = argv[optind]; if (!dom && all == 0) { fprintf(stderr, "You must specify -a or a domain id.\n\n"); help("tmem-freeze"); return 1; } if (all) domid = -1; else find_domain(dom); libxl_tmem_freeze(&ctx, domid); return 0; } int main_tmem_destroy(int argc, char **argv) { const char *dom = NULL; int all = 0; int opt; while ((opt = getopt(argc, argv, "ah")) != -1) { switch (opt) { case 'a': all = 1; break; case 'h': help("tmem-destroy"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } dom = argv[optind]; if (!dom && all == 0) { fprintf(stderr, "You must specify -a or a domain id.\n\n"); help("tmem-destroy"); return 1; } if (all) domid = -1; else find_domain(dom); libxl_tmem_destroy(&ctx, domid); return 0; } int main_tmem_thaw(int argc, char **argv) { const char *dom = NULL; int all = 0; int opt; while ((opt = getopt(argc, argv, "ah")) != -1) { switch (opt) { case 'a': all = 1; break; case 'h': help("tmem-thaw"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } dom = argv[optind]; if (!dom && all == 0) { fprintf(stderr, "You must specify -a or a domain id.\n\n"); help("tmem-thaw"); return 1; } if (all) domid = -1; else find_domain(dom); libxl_tmem_thaw(&ctx, domid); return 0; } int main_tmem_set(int argc, char **argv) { const char *dom = NULL; uint32_t weight = 0, cap = 0, compress = 0; int opt_w = 0, opt_c = 0, opt_p = 0; int all = 0; int opt; while ((opt = getopt(argc, argv, "aw:c:p:h")) != -1) { switch (opt) { case 'a': all = 1; break; case 'w': weight = strtol(optarg, NULL, 10); opt_w = 1; break; case 'c': cap = strtol(optarg, NULL, 10); opt_c = 1; break; case 'p': compress = strtol(optarg, NULL, 10); opt_p = 1; break; case 'h': help("tmem-set"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } dom = argv[optind]; if (!dom && all == 0) { fprintf(stderr, "You must specify -a or a domain id.\n\n"); help("tmem-set"); return 1; } if (all) domid = -1; else find_domain(dom); if (!opt_w && !opt_c && !opt_p) { fprintf(stderr, "No set value specified.\n\n"); help("tmem-set"); return 1; } if (opt_w) libxl_tmem_set(&ctx, domid, "weight", weight); if (opt_c) libxl_tmem_set(&ctx, domid, "cap", cap); if (opt_p) libxl_tmem_set(&ctx, domid, "compress", compress); return 0; } int main_tmem_shared_auth(int argc, char **argv) { const char *autharg = NULL; char *endptr = NULL; const char *dom = NULL; char *uuid = NULL; int auth = -1; int all = 0; int opt; while ((opt = getopt(argc, argv, "au:A:h")) != -1) { switch (opt) { case 'a': all = 1; break; case 'u': uuid = optarg; break; case 'A': autharg = optarg; break; case 'h': help("tmem-shared-auth"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } dom = argv[optind]; if (!dom && all == 0) { fprintf(stderr, "You must specify -a or a domain id.\n\n"); help("tmem-shared-auth"); return 1; } if (all) domid = -1; else find_domain(dom); if (uuid == NULL || autharg == NULL) { fprintf(stderr, "No uuid or auth specified.\n\n"); help("tmem-shared-auth"); return 1; } auth = strtol(autharg, &endptr, 10); if (*endptr != '\0') { fprintf(stderr, "Invalid auth, valid auth are <0|1>.\n\n"); return 1; } libxl_tmem_shared_auth(&ctx, domid, uuid, auth); return 0; } int main_tmem_freeable(int argc, char **argv) { int opt; int mb; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("tmem-freeable"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } mb = libxl_tmem_freeable(&ctx); if (mb == -1) return -1; printf("%d\n", mb); return 0; } int main_cpupoolcreate(int argc, char **argv) { const char *filename = NULL; const char *p; char extra_config[1024]; int dryrun = 0; int opt; int option_index = 0; static struct option long_options[] = { {"help", 0, 0, 'h'}, {"defconfig", 1, 0, 'f'}, {"dryrun", 0, 0, 'n'}, {0, 0, 0, 0} }; int ret; char *config_data = 0; int config_len = 0; XLU_Config *config; const char *buf; const char *name; const char *sched; uint32_t poolid; int schedid = -1; XLU_ConfigList *cpus; XLU_ConfigList *nodes; int n_cpus, n_nodes, i, n; libxl_cpumap freemap; libxl_cpumap cpumap; libxl_uuid uuid; libxl_topologyinfo topology; while (1) { opt = getopt_long(argc, argv, "hnf:", long_options, &option_index); if (opt == -1) break; switch (opt) { case 'f': filename = optarg; break; case 'h': help("cpupool-create"); return 0; case 'n': dryrun = 1; break; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } memset(extra_config, 0, sizeof(extra_config)); while (optind < argc) { if ((p = strchr(argv[optind], '='))) { if (strlen(extra_config) + 1 + strlen(argv[optind]) < sizeof(extra_config)) { strcat(extra_config, "\n"); strcat(extra_config, argv[optind]); } } else if (!filename) { filename = argv[optind]; } else { help("cpupool-create"); return -ERROR_FAIL; } optind++; } if (!filename) { help("cpupool-create"); return -ERROR_FAIL; } if (libxl_read_file_contents(&ctx, filename, (void **)&config_data, &config_len)) { fprintf(stderr, "Failed to read config file: %s: %s\n", filename, strerror(errno)); return -ERROR_FAIL; } if (strlen(extra_config)) { if (config_len > INT_MAX - (strlen(extra_config) + 2)) { fprintf(stderr, "Failed to attach extra configration\n"); return -ERROR_FAIL; } config_data = xrealloc(config_data, config_len + strlen(extra_config) + 2); if (!config_data) { fprintf(stderr, "Failed to realloc config_data\n"); return -ERROR_FAIL; } config_data[config_len] = 0; strcat(config_data, extra_config); strcat(config_data, "\n"); config_len += strlen(extra_config) + 1; } config = xlu_cfg_init(stderr, filename); if (!config) { fprintf(stderr, "Failed to allocate for configuration\n"); return -ERROR_FAIL; } ret = xlu_cfg_readdata(config, config_data, config_len); if (ret) { fprintf(stderr, "Failed to parse config file: %s\n", strerror(ret)); return -ERROR_FAIL; } if (!xlu_cfg_get_string (config, "name", &buf)) name = strdup(buf); else name = libxl_basename(filename); if (!libxl_name_to_cpupoolid(&ctx, name, &poolid)) { fprintf(stderr, "Pool name \"%s\" already exists\n", name); return -ERROR_FAIL; } if (!xlu_cfg_get_string (config, "sched", &buf)) { if ((schedid = libxl_name_to_schedid(&ctx, buf)) < 0) { fprintf(stderr, "Unknown scheduler\n"); return -ERROR_FAIL; } } else { if ((schedid = libxl_get_sched_id(&ctx)) < 0) { fprintf(stderr, "get_sched_id sysctl failed.\n"); return -ERROR_FAIL; } } sched = libxl_schedid_to_name(&ctx, schedid); if (libxl_get_freecpus(&ctx, &freemap)) { fprintf(stderr, "libxl_get_freecpus failed\n"); return -ERROR_FAIL; } if (libxl_cpumap_alloc(&ctx, &cpumap)) { fprintf(stderr, "Failed to allocate cpumap\n"); return -ERROR_FAIL; } if (!xlu_cfg_get_list(config, "nodes", &nodes, 0, 0)) { n_cpus = 0; n_nodes = 0; if (libxl_get_topologyinfo(&ctx, &topology)) { fprintf(stderr, "libxl_get_topologyinfo failed\n"); return -ERROR_FAIL; } while ((buf = xlu_cfg_get_listitem(nodes, n_nodes)) != NULL) { n = atoi(buf); for (i = 0; i < topology.nodemap.entries; i++) { if ((topology.nodemap.array[i] == n) && libxl_cpumap_test(&freemap, i)) { libxl_cpumap_set(&cpumap, i); n_cpus++; } } n_nodes++; } libxl_topologyinfo_destroy(&topology); if (n_cpus == 0) { fprintf(stderr, "no free cpu found\n"); return -ERROR_FAIL; } } else if (!xlu_cfg_get_list(config, "cpus", &cpus, 0, 0)) { n_cpus = 0; while ((buf = xlu_cfg_get_listitem(cpus, n_cpus)) != NULL) { i = atoi(buf); if ((i < 0) || (i >= freemap.size * 8) || !libxl_cpumap_test(&freemap, i)) { fprintf(stderr, "cpu %d illegal or not free\n", i); return -ERROR_FAIL; } libxl_cpumap_set(&cpumap, i); n_cpus++; } } else { n_cpus = 1; n = 0; libxl_for_each_cpu(i, freemap) if (libxl_cpumap_test(&freemap, i)) { n++; libxl_cpumap_set(&cpumap, i); break; } if (n < n_cpus) { fprintf(stderr, "no free cpu found\n"); return -ERROR_FAIL; } } libxl_uuid_generate(&uuid); printf("Using config file \"%s\"\n", filename); printf("cpupool name: %s\n", name); printf("scheduler: %s\n", sched); printf("number of cpus: %d\n", n_cpus); if (dryrun) return 0; poolid = 0; if (libxl_create_cpupool(&ctx, name, schedid, cpumap, &uuid, &poolid)) { fprintf(stderr, "error on creating cpupool\n"); return -ERROR_FAIL; } return 0; } int main_cpupoollist(int argc, char **argv) { int opt; int option_index = 0; static struct option long_options[] = { {"help", 0, 0, 'h'}, {"cpus", 0, 0, 'c'}, {0, 0, 0, 0} }; int opt_cpus = 0; const char *pool = NULL; libxl_cpupoolinfo *poolinfo; int n_pools, p, c, n; uint32_t poolid; char *name; int ret = 0; while (1) { opt = getopt_long(argc, argv, "hc", long_options, &option_index); if (opt == -1) break; switch (opt) { case 'h': help("cpupool-list"); return 0; case 'c': opt_cpus = 1; break; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } if ((optind + 1) < argc) { help("cpupool-list"); return -ERROR_FAIL; } if (optind < argc) { pool = argv[optind]; if (libxl_name_to_cpupoolid(&ctx, pool, &poolid)) { fprintf(stderr, "Pool \'%s\' does not exist\n", pool); return -ERROR_FAIL; } } poolinfo = libxl_list_cpupool(&ctx, &n_pools); if (!poolinfo) { fprintf(stderr, "error getting cpupool info\n"); return -ERROR_NOMEM; } printf("%-19s", "Name"); if (opt_cpus) printf("CPU list\n"); else printf("CPUs Sched Active Domain count\n"); for (p = 0; p < n_pools; p++) { if (!ret && (!pool || (poolinfo[p].poolid == poolid))) { name = libxl_cpupoolid_to_name(&ctx, poolinfo[p].poolid); if (!name) { fprintf(stderr, "error getting cpupool info\n"); ret = -ERROR_NOMEM; } else { printf("%-19s", name); free(name); n = 0; libxl_for_each_cpu(c, poolinfo[p].cpumap) if (libxl_cpumap_test(&poolinfo[p].cpumap, c)) { if (n && opt_cpus) printf(","); if (opt_cpus) printf("%d", c); n++; } if (!opt_cpus) { printf("%3d %9s y %4d", n, libxl_schedid_to_name(&ctx, poolinfo[p].sched_id), poolinfo[p].n_dom); } printf("\n"); } } libxl_cpupoolinfo_destroy(poolinfo + p); } return ret; } int main_cpupooldestroy(int argc, char **argv) { int opt; const char *pool; uint32_t poolid; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("cpupool-destroy"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } pool = argv[optind]; if (!pool) { fprintf(stderr, "no cpupool specified\n"); help("cpupool-destroy"); return -ERROR_FAIL; } if (cpupool_qualifier_to_cpupoolid(pool, &poolid, NULL) || !libxl_cpupoolid_to_name(&ctx, poolid)) { fprintf(stderr, "unknown cpupool \'%s\'\n", pool); return -ERROR_FAIL; } return -libxl_destroy_cpupool(&ctx, poolid); } int main_cpupoolrename(int argc, char **argv) { int opt; const char *pool; const char *new_name; uint32_t poolid; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("cpupool-rename"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } pool = argv[optind++]; if (!pool || !argv[optind]) { fprintf(stderr, "'xl cpupool-rename' requires 2 arguments.\n\n"); help("cpupool-rename"); return 1; } if (cpupool_qualifier_to_cpupoolid(pool, &poolid, NULL) || !libxl_cpupoolid_to_name(&ctx, poolid)) { fprintf(stderr, "unknown cpupool \'%s\'\n", pool); return -ERROR_FAIL; } new_name = argv[optind]; if (libxl_cpupool_rename(&ctx, new_name, poolid)) { fprintf(stderr, "Can't rename cpupool '%s'.\n", pool); return 1; } return 0; } int main_cpupoolcpuadd(int argc, char **argv) { int opt; const char *pool; uint32_t poolid; int cpu; int node; int n; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("cpupool-cpu-add"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } pool = argv[optind++]; if (!pool) { fprintf(stderr, "no cpupool specified\n"); help("cpupool-cpu-add"); return -ERROR_FAIL; } if (!argv[optind]) { fprintf(stderr, "no cpu specified\n"); help("cpupool-cpu-add"); return -ERROR_FAIL; } node = -1; cpu = -1; if (strncmp(argv[optind], "node:", 5) == 0) { node = atoi(argv[optind] + 5); } else { cpu = atoi(argv[optind]); } if (cpupool_qualifier_to_cpupoolid(pool, &poolid, NULL) || !libxl_cpupoolid_to_name(&ctx, poolid)) { fprintf(stderr, "unknown cpupool \'%s\'\n", pool); return -ERROR_FAIL; } if (cpu >= 0) { return -libxl_cpupool_cpuadd(&ctx, poolid, cpu); } if (libxl_cpupool_cpuadd_node(&ctx, poolid, node, &n)) { fprintf(stderr, "libxl_cpupool_cpuadd_node failed\n"); return -ERROR_FAIL; } if (n > 0) { return 0; } fprintf(stderr, "no free cpu found\n"); return -ERROR_FAIL; } int main_cpupoolcpuremove(int argc, char **argv) { int opt; const char *pool; uint32_t poolid; int cpu; int node; int n; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("cpupool-cpu-remove"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } pool = argv[optind++]; if (!pool) { fprintf(stderr, "no cpupool specified\n"); help("cpupool-cpu-remove"); return -ERROR_FAIL; } if (!argv[optind]) { fprintf(stderr, "no cpu specified\n"); help("cpupool-cpu-remove"); return -ERROR_FAIL; } node = -1; cpu = -1; if (strncmp(argv[optind], "node:", 5) == 0) { node = atoi(argv[optind] + 5); } else { cpu = atoi(argv[optind]); } if (cpupool_qualifier_to_cpupoolid(pool, &poolid, NULL) || !libxl_cpupoolid_to_name(&ctx, poolid)) { fprintf(stderr, "unknown cpupool \'%s\'\n", pool); return -ERROR_FAIL; } if (cpu >= 0) { return -libxl_cpupool_cpuremove(&ctx, poolid, cpu); } if (libxl_cpupool_cpuremove_node(&ctx, poolid, node, &n)) { fprintf(stderr, "libxl_cpupool_cpuremove_node failed\n"); return -ERROR_FAIL; } if (n == 0) { fprintf(stderr, "no cpu of node found in cpupool\n"); return -ERROR_FAIL; } return 0; } int main_cpupoolmigrate(int argc, char **argv) { int opt; const char *pool; uint32_t poolid; const char *dom; uint32_t domid; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("cpupool-migrate"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } dom = argv[optind++]; if (!dom) { fprintf(stderr, "no domain specified\n"); help("cpupool-migrate"); return -ERROR_FAIL; } pool = argv[optind++]; if (!pool) { fprintf(stderr, "no cpupool specified\n"); help("cpupool-migrate"); return -ERROR_FAIL; } if (domain_qualifier_to_domid(dom, &domid, NULL) || !libxl_domid_to_name(&ctx, domid)) { fprintf(stderr, "unknown domain \'%s\'\n", dom); return -ERROR_FAIL; } if (cpupool_qualifier_to_cpupoolid(pool, &poolid, NULL) || !libxl_cpupoolid_to_name(&ctx, poolid)) { fprintf(stderr, "unknown cpupool \'%s\'\n", pool); return -ERROR_FAIL; } return -libxl_cpupool_movedomain(&ctx, poolid, domid); } int main_cpupoolnumasplit(int argc, char **argv) { int ret; int opt; int p; int c; int n; uint32_t poolid; int schedid; int n_pools; int node; char name[16]; libxl_uuid uuid; libxl_cpumap cpumap; libxl_cpupoolinfo *poolinfo; libxl_topologyinfo topology; libxl_dominfo info; while ((opt = getopt(argc, argv, "h")) != -1) { switch (opt) { case 'h': help("cpupool-numa-split"); return 0; default: fprintf(stderr, "option `%c' not supported.\n", optopt); break; } } ret = 0; poolinfo = libxl_list_cpupool(&ctx, &n_pools); if (!poolinfo) { fprintf(stderr, "error getting cpupool info\n"); return -ERROR_NOMEM; } poolid = poolinfo[0].poolid; schedid = poolinfo[0].sched_id; for (p = 0; p < n_pools; p++) { libxl_cpupoolinfo_destroy(poolinfo + p); } if (n_pools > 1) { fprintf(stderr, "splitting not possible, already cpupools in use\n"); return -ERROR_FAIL; } if (libxl_get_topologyinfo(&ctx, &topology)) { fprintf(stderr, "libxl_get_topologyinfo failed\n"); return -ERROR_FAIL; } if (libxl_cpumap_alloc(&ctx, &cpumap)) { fprintf(stderr, "Failed to allocate cpumap\n"); libxl_topologyinfo_destroy(&topology); return -ERROR_FAIL; } /* Reset Pool-0 to 1st node: first add cpus, then remove cpus to avoid a cpupool without cpus in between */ node = topology.nodemap.array[0]; if (libxl_cpupool_cpuadd_node(&ctx, 0, node, &n)) { fprintf(stderr, "error on adding cpu to Pool 0\n"); return -ERROR_FAIL; } snprintf(name, 15, "Pool-node%d", node); ret = -libxl_cpupool_rename(&ctx, name, 0); if (ret) { fprintf(stderr, "error on renaming Pool 0\n"); goto out; } n = 0; for (c = 0; c < topology.nodemap.entries; c++) { if (topology.nodemap.array[c] == node) { topology.nodemap.array[c] = LIBXL_CPUARRAY_INVALID_ENTRY; libxl_cpumap_set(&cpumap, n); n++; } } if (libxl_set_vcpuonline(&ctx, 0, &cpumap)) { fprintf(stderr, "error on removing vcpus for Domain-0\n"); goto out; } for (c = 0; c < 10; c++) { if (libxl_domain_info(&ctx, &info, 0)) { fprintf(stderr, "error on getting info for Domain-0\n"); goto out; } if (info.vcpu_online == n) { break; } sleep(1); } if (info.vcpu_online > n) { fprintf(stderr, "failed to offline vcpus\n"); goto out; } memset(cpumap.map, 0, cpumap.size); for (c = 0; c < topology.nodemap.entries; c++) { if (topology.nodemap.array[c] == LIBXL_CPUARRAY_INVALID_ENTRY) { continue; } node = topology.nodemap.array[c]; ret = -libxl_cpupool_cpuremove_node(&ctx, 0, node, &n); if (ret) { fprintf(stderr, "error on removing cpu from Pool 0\n"); goto out; } snprintf(name, 15, "Pool-node%d", node); libxl_uuid_generate(&uuid); poolid = 0; ret = -libxl_create_cpupool(&ctx, name, schedid, cpumap, &uuid, &poolid); if (ret) { fprintf(stderr, "error on creating cpupool\n"); goto out; } ret = -libxl_cpupool_cpuadd_node(&ctx, poolid, node, &n); if (ret) { fprintf(stderr, "error on adding cpus to cpupool\n"); goto out; } for (p = c; p < topology.nodemap.entries; p++) { if (topology.nodemap.array[p] == node) { topology.nodemap.array[p] = LIBXL_CPUARRAY_INVALID_ENTRY; } } } out: libxl_topologyinfo_destroy(&topology); libxl_cpumap_destroy(&cpumap); return ret; }