mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
We want a function for setting fds to nonblocking, so introduce one. This is a very similar requirement to that for libxl_fd_set_cloexec, so make it common with that. While we're at it, fix a few deficiences that make this latter function less desirable than it could be: * Change the return from 0/-1 (like a syscall) to a libxl error code * Take a boolean parameter for turning the flag on and off * Log on error (and so, take a ctx for this purpose) Change callers of libxl_fd_set_cloexec to notice errors. (Although, such errors are highly unlikely.) Signed-off-by: Ian Jackson <ian.jackson@eu.citrix.com> Acked-by: Ian Campbell <ian.campbell@citrix.com> Committed-by: Ian Jackson <Ian.Jackson@eu.citrix.com>
5994 lines
169 KiB
C
5994 lines
169 KiB
C
/*
|
|
* Copyright (C) 2009 Citrix Ltd.
|
|
* Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
|
|
* Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
|
|
*
|
|
* 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 <stdio.h>
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <getopt.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/select.h>
|
|
#include <sys/utsname.h> /* for utsname in xl info */
|
|
#include <xentoollog.h>
|
|
#include <ctype.h>
|
|
#include <inttypes.h>
|
|
#include <limits.h>
|
|
|
|
#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_ON_SHUTDOWN_DESTROY] = "destroy",
|
|
|
|
[LIBXL_ACTION_ON_SHUTDOWN_RESTART] = "restart",
|
|
[LIBXL_ACTION_ON_SHUTDOWN_RESTART_RENAME] = "rename-restart",
|
|
|
|
[LIBXL_ACTION_ON_SHUTDOWN_PRESERVE] = "preserve",
|
|
|
|
[LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_DESTROY] = "coredump-destroy",
|
|
[LIBXL_ACTION_ON_SHUTDOWN_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;
|
|
}
|
|
if (fcntl(fd_lock, F_SETFD, FD_CLOEXEC) < 0) {
|
|
close(fd_lock);
|
|
fprintf(stderr, "cannot set cloexec to lockfile %s errno=%d\n", lockfile, errno);
|
|
return ERROR_FAIL;
|
|
}
|
|
get_lock:
|
|
rc = fcntl(fd_lock, F_SETLKW, &fl);
|
|
if (rc < 0 && errno == EINTR)
|
|
goto get_lock;
|
|
if (rc < 0) {
|
|
fprintf(stderr, "cannot acquire lock %s errno=%d\n", lockfile, errno);
|
|
rc = ERROR_FAIL;
|
|
} else
|
|
rc = 0;
|
|
return rc;
|
|
}
|
|
|
|
static int release_lock(void)
|
|
{
|
|
int rc;
|
|
struct flock fl;
|
|
|
|
/* lock not acquired */
|
|
if (fd_lock < 0)
|
|
return ERROR_INVAL;
|
|
|
|
release_lock:
|
|
fl.l_type = F_UNLCK;
|
|
fl.l_whence = SEEK_SET;
|
|
fl.l_start = 0;
|
|
fl.l_len = 0;
|
|
|
|
rc = fcntl(fd_lock, F_SETLKW, &fl);
|
|
if (rc < 0 && errno == EINTR)
|
|
goto release_lock;
|
|
if (rc < 0) {
|
|
fprintf(stderr, "cannot release lock %s, errno=%d\n", lockfile, errno);
|
|
rc = ERROR_FAIL;
|
|
} else
|
|
rc = 0;
|
|
close(fd_lock);
|
|
fd_lock = -1;
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void *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 = NULL;
|
|
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);
|
|
free(s);
|
|
}
|
|
|
|
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->type == LIBXL_DOMAIN_TYPE_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 <unknown>)\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 %s)\n", libxl_tsc_mode_to_string(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->type == LIBXL_DOMAIN_TYPE_PV && b_info->u.pv.bootloader) {
|
|
int i;
|
|
printf("\t(bootloader %s)\n", b_info->u.pv.bootloader);
|
|
if (b_info->u.pv.bootloader_args) {
|
|
printf("\t(bootloader_args");
|
|
for (i=0; b_info->u.pv.bootloader_args[i]; i++)
|
|
printf(" %s", b_info->u.pv.bootloader_args[i]);
|
|
printf(")\n");
|
|
}
|
|
}
|
|
|
|
printf("\t(image\n");
|
|
switch (c_info->type) {
|
|
case LIBXL_DOMAIN_TYPE_HVM:
|
|
printf("\t\t(hvm\n");
|
|
printf("\t\t\t(firmware %s)\n", b_info->u.hvm.firmware);
|
|
printf("\t\t\t(video_memkb %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(nestedhvm %d)\n", b_info->u.hvm.nested_hvm);
|
|
printf("\t\t\t(no_incr_generationid %d)\n",
|
|
b_info->u.hvm.no_incr_generationid);
|
|
|
|
printf("\t\t\t(device_model %s)\n", dm_info->device_model ? : "default");
|
|
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(acpi %d)\n", dm_info->acpi);
|
|
printf("\t\t\t(spice %d)\n", dm_info->spice);
|
|
printf("\t\t\t(spiceport %d)\n", dm_info->spiceport);
|
|
printf("\t\t\t(spicetls_port %d)\n", dm_info->spicetls_port);
|
|
printf("\t\t\t(spicehost %s)\n", dm_info->spicehost);
|
|
printf("\t\t\t(spicedisable_ticketing %d)\n",
|
|
dm_info->spicedisable_ticketing);
|
|
printf("\t\t\t(spiceagent_mouse %d)\n", dm_info->spiceagent_mouse);
|
|
printf("\t\t)\n");
|
|
break;
|
|
case LIBXL_DOMAIN_TYPE_PV:
|
|
printf("\t\t(linux %d)\n", 0);
|
|
printf("\t\t\t(kernel %s)\n", b_info->u.pv.kernel.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\t(e820_host %d)\n", b_info->u.pv.e820_host);
|
|
printf("\t\t)\n");
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Unknown domain type %d\n", c_info->type);
|
|
exit(1);
|
|
}
|
|
printf("\t)\n");
|
|
|
|
for (i = 0; i < d_config->num_disks; i++) {
|
|
printf("\t(device\n");
|
|
printf("\t\t(tap\n");
|
|
printf("\t\t\t(backend_domid %d)\n", d_config->disks[i].backend_domid);
|
|
printf("\t\t\t(frontend_domid %d)\n", domid);
|
|
printf("\t\t\t(physpath %s)\n", d_config->disks[i].pdev_path);
|
|
printf("\t\t\t(phystype %d)\n", d_config->disks[i].backend);
|
|
printf("\t\t\t(virtpath %s)\n", d_config->disks[i].vdev);
|
|
printf("\t\t\t(unpluggable %d)\n", d_config->disks[i].removable);
|
|
printf("\t\t\t(readwrite %d)\n", d_config->disks[i].readwrite);
|
|
printf("\t\t\t(is_cdrom %d)\n", d_config->disks[i].is_cdrom);
|
|
printf("\t\t)\n");
|
|
printf("\t)\n");
|
|
}
|
|
|
|
for (i = 0; i < d_config->num_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", 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", 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, 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 (!n) continue;
|
|
|
|
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 void parse_disk_config_multistring(XLU_Config **config,
|
|
int nspecs, const char *const *specs,
|
|
libxl_device_disk *disk)
|
|
{
|
|
int e;
|
|
|
|
libxl_device_disk_init(ctx, disk);
|
|
|
|
if (!*config) {
|
|
*config = xlu_cfg_init(stderr, "command line");
|
|
if (!*config) { perror("xlu_cfg_init"); exit(-1); }
|
|
}
|
|
|
|
e = xlu_disk_parse(*config, nspecs, specs, disk);
|
|
if (e == EINVAL) exit(-1);
|
|
if (e) {
|
|
fprintf(stderr,"xlu_disk_parse failed: %s\n",strerror(errno));
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
static void parse_disk_config(XLU_Config **config, const char *spec,
|
|
libxl_device_disk *disk)
|
|
{
|
|
parse_disk_config_multistring(config, 1, &spec, disk);
|
|
}
|
|
|
|
static void split_string_into_string_list(const char *str,
|
|
const char *delim,
|
|
libxl_string_list *psl)
|
|
{
|
|
char *s, *saveptr;
|
|
const char *p;
|
|
libxl_string_list sl;
|
|
|
|
int i = 0, nr = 0;
|
|
|
|
s = strdup(str);
|
|
if (s == NULL) {
|
|
fprintf(stderr, "unable to allocate memory to parse bootloader args\n");
|
|
exit(-1);
|
|
}
|
|
|
|
/* Count number of entries */
|
|
p = strtok_r(s, delim, &saveptr);
|
|
do {
|
|
nr++;
|
|
} while ((p = strtok_r(NULL, delim, &saveptr)));
|
|
|
|
free(s);
|
|
|
|
s = strdup(str);
|
|
|
|
sl = malloc((nr+1) * sizeof (char *));
|
|
if (sl == NULL) {
|
|
fprintf(stderr, "unable to allocate memory for bootloader args\n");
|
|
exit(-1);
|
|
}
|
|
|
|
p = strtok_r(s, delim, &saveptr);
|
|
do {
|
|
assert(i < nr);
|
|
sl[i] = strdup(p);
|
|
i++;
|
|
} while ((p = strtok_r(NULL, delim, &saveptr)));
|
|
sl[i] = NULL;
|
|
|
|
*psl = sl;
|
|
|
|
free(s);
|
|
}
|
|
|
|
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, *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);
|
|
}
|
|
|
|
if (libxl_init_create_info(ctx, c_info))
|
|
exit(1);
|
|
|
|
if (!xlu_cfg_get_string (config, "seclabel", &buf, 0)) {
|
|
e = libxl_flask_context_to_sid(ctx, (char *)buf, strlen(buf),
|
|
&c_info->ssidref);
|
|
if (e) {
|
|
if (errno == ENOSYS) {
|
|
fprintf(stderr, "XSM Disabled: seclabel not supported\n");
|
|
} else {
|
|
fprintf(stderr, "Invalid seclabel: %s\n", buf);
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
c_info->type = LIBXL_DOMAIN_TYPE_PV;
|
|
if (!xlu_cfg_get_string (config, "builder", &buf, 0) &&
|
|
!strncmp(buf, "hvm", strlen(buf)))
|
|
c_info->type = LIBXL_DOMAIN_TYPE_HVM;
|
|
|
|
if (!xlu_cfg_get_long (config, "hap", &l, 0))
|
|
c_info->hap = l;
|
|
|
|
if (xlu_cfg_replace_string (config, "name", &c_info->name, 0)) {
|
|
fprintf(stderr, "Domain name must be specified.");
|
|
exit(1);
|
|
}
|
|
|
|
if (!xlu_cfg_get_string (config, "uuid", &buf, 0) ) {
|
|
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, 0))
|
|
c_info->oos = l;
|
|
|
|
if (!xlu_cfg_get_string (config, "pool", &buf, 0)) {
|
|
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);
|
|
}
|
|
|
|
if (libxl_init_build_info(ctx, b_info, c_info))
|
|
exit(1);
|
|
|
|
/* the following is the actual config parsing with overriding values in the structures */
|
|
if (!xlu_cfg_get_long (config, "vcpus", &l, 0)) {
|
|
b_info->max_vcpus = l;
|
|
b_info->cur_vcpus = (1 << l) - 1;
|
|
}
|
|
|
|
if (!xlu_cfg_get_long (config, "maxvcpus", &l, 0))
|
|
b_info->max_vcpus = l;
|
|
|
|
if (!xlu_cfg_get_long (config, "memory", &l, 0)) {
|
|
b_info->max_memkb = l * 1024;
|
|
b_info->target_memkb = b_info->max_memkb;
|
|
}
|
|
|
|
if (!xlu_cfg_get_long (config, "maxmem", &l, 0))
|
|
b_info->max_memkb = l * 1024;
|
|
|
|
if (xlu_cfg_get_string (config, "on_poweroff", &buf, 0))
|
|
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, 0))
|
|
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, 0))
|
|
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, 0))
|
|
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, 0)
|
|
? l * 1024
|
|
: libxl_get_required_shadow_memory(b_info->max_memkb,
|
|
b_info->max_vcpus);
|
|
|
|
if (!xlu_cfg_get_long (config, "nomigrate", &l, 0))
|
|
b_info->disable_migrate = l;
|
|
|
|
if (!xlu_cfg_get_long(config, "tsc_mode", &l, 1)) {
|
|
const char *s = libxl_tsc_mode_to_string(l);
|
|
fprintf(stderr, "WARNING: specifying \"tsc_mode\" as an integer is deprecated. "
|
|
"Please use the named parameter variant. %s%s%s\n",
|
|
s ? "e.g. tsc_mode=\"" : "",
|
|
s ? s : "",
|
|
s ? "\"" : "");
|
|
|
|
if (l < LIBXL_TSC_MODE_DEFAULT ||
|
|
l > LIBXL_TSC_MODE_NATIVE_PARAVIRT) {
|
|
fprintf(stderr, "ERROR: invalid value %ld for \"tsc_mode\"\n", l);
|
|
exit (1);
|
|
}
|
|
b_info->tsc_mode = l;
|
|
} else if (!xlu_cfg_get_string(config, "tsc_mode", &buf, 0)) {
|
|
fprintf(stderr, "got a tsc mode string: \"%s\"\n", buf);
|
|
if (libxl_tsc_mode_from_string(buf, &b_info->tsc_mode)) {
|
|
fprintf(stderr, "ERROR: invalid value \"%s\" for \"tsc_mode\"\n",
|
|
buf);
|
|
exit (1);
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_long (config, "videoram", &l, 0))
|
|
b_info->video_memkb = l * 1024;
|
|
|
|
if (!xlu_cfg_get_long (config, "gfx_passthru", &l, 0))
|
|
dm_info->gfx_passthru = l;
|
|
|
|
switch(c_info->type) {
|
|
case LIBXL_DOMAIN_TYPE_HVM:
|
|
if (!xlu_cfg_get_string (config, "kernel", &buf, 0))
|
|
fprintf(stderr, "WARNING: ignoring \"kernel\" directive for HVM guest. "
|
|
"Use \"firmware_override\" instead if you really want a non-default firmware\n");
|
|
|
|
xlu_cfg_replace_string (config, "firmware_override",
|
|
&b_info->u.hvm.firmware, 0);
|
|
if (!xlu_cfg_get_long (config, "pae", &l, 0))
|
|
b_info->u.hvm.pae = l;
|
|
if (!xlu_cfg_get_long (config, "apic", &l, 0))
|
|
b_info->u.hvm.apic = l;
|
|
if (!xlu_cfg_get_long (config, "acpi", &l, 0))
|
|
b_info->u.hvm.acpi = l;
|
|
if (!xlu_cfg_get_long (config, "acpi_s3", &l, 0))
|
|
b_info->u.hvm.acpi_s3 = l;
|
|
if (!xlu_cfg_get_long (config, "acpi_s4", &l, 0))
|
|
b_info->u.hvm.acpi_s4 = l;
|
|
if (!xlu_cfg_get_long (config, "nx", &l, 0))
|
|
b_info->u.hvm.nx = l;
|
|
if (!xlu_cfg_get_long (config, "viridian", &l, 0))
|
|
b_info->u.hvm.viridian = l;
|
|
if (!xlu_cfg_get_long (config, "hpet", &l, 0))
|
|
b_info->u.hvm.hpet = l;
|
|
if (!xlu_cfg_get_long (config, "vpt_align", &l, 0))
|
|
b_info->u.hvm.vpt_align = l;
|
|
if (!xlu_cfg_get_long (config, "timer_mode", &l, 0))
|
|
b_info->u.hvm.timer_mode = l;
|
|
if (!xlu_cfg_get_long (config, "nestedhvm", &l, 0))
|
|
b_info->u.hvm.nested_hvm = l;
|
|
break;
|
|
case LIBXL_DOMAIN_TYPE_PV:
|
|
{
|
|
char *cmdline = NULL;
|
|
const char *root = NULL, *extra = "";
|
|
|
|
xlu_cfg_replace_string (config, "kernel", &b_info->u.pv.kernel.path, 0);
|
|
|
|
xlu_cfg_replace_string (config, "kernel", &b_info->u.pv.kernel.path, 0);
|
|
|
|
xlu_cfg_get_string (config, "root", &root, 0);
|
|
xlu_cfg_get_string (config, "extra", &extra, 0);
|
|
|
|
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, 0);
|
|
switch (xlu_cfg_get_list_as_string_list(config, "bootloader_args",
|
|
&b_info->u.pv.bootloader_args, 1))
|
|
{
|
|
|
|
case 0: break; /* Success */
|
|
case ESRCH: break; /* Option not present */
|
|
case EINVAL:
|
|
if (!xlu_cfg_get_string(config, "bootloader_args", &buf, 0)) {
|
|
|
|
fprintf(stderr, "WARNING: Specifying \"bootloader_args\""
|
|
" as a string is deprecated. "
|
|
"Please use a list of arguments.\n");
|
|
split_string_into_string_list(buf, " \t\n",
|
|
&b_info->u.pv.bootloader_args);
|
|
}
|
|
break;
|
|
default:
|
|
fprintf(stderr,"xl: Unable to parse bootloader_args.\n");
|
|
exit(-ERROR_FAIL);
|
|
}
|
|
|
|
if (!b_info->u.pv.bootloader && !b_info->u.pv.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, 0);
|
|
break;
|
|
}
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
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;
|
|
parse_disk_config(&config, buf2, disk);
|
|
|
|
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(ctx, nic) );
|
|
nic->devid = 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 = LIBXL_NIC_TYPE_IOEMU;
|
|
else
|
|
nic->nictype = LIBXL_NIC_TYPE_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", NULL, 0, 0)) {
|
|
fprintf(stderr, "WARNING: vif2: netchannel2 is deprecated and not supported by xl\n");
|
|
}
|
|
|
|
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(ctx, vfb);
|
|
vfb->devid = 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(ctx, vkb);
|
|
vkb->devid = 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, 0))
|
|
pci_msitranslate = l;
|
|
|
|
if (!xlu_cfg_get_long (config, "pci_power_mgmt", &l, 0))
|
|
pci_power_mgmt = l;
|
|
|
|
/* To be reworked (automatically enabled) once the auto ballooning
|
|
* after guest starts is done (with PCI devices passed in). */
|
|
if (!xlu_cfg_get_long (config, "e820_host", &l, 0)) {
|
|
switch (c_info->type) {
|
|
case LIBXL_DOMAIN_TYPE_HVM:
|
|
fprintf(stderr, "Can't do e820_host in HVM mode!");
|
|
break;
|
|
case LIBXL_DOMAIN_TYPE_PV:
|
|
if (l)
|
|
b_info->u.pv.e820_host = true;
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
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++;
|
|
}
|
|
if (d_config->num_pcidevs && c_info->type == LIBXL_DOMAIN_TYPE_PV)
|
|
b_info->u.pv.e820_host = true;
|
|
}
|
|
|
|
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, 0)) {
|
|
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;
|
|
}
|
|
|
|
/* init dm from c and b */
|
|
if (libxl_init_dm_info(ctx, dm_info, c_info, b_info))
|
|
exit(1);
|
|
/* parse device model arguments, this works for pv, hvm and stubdom */
|
|
if (!xlu_cfg_get_string (config, "device_model", &buf, 0)) {
|
|
fprintf(stderr,
|
|
"WARNING: ignoring device_model directive.\n"
|
|
"WARNING: Use \"device_model_override\" instead if you"
|
|
" really want a non-default device_model\n");
|
|
if (strstr(buf, "stubdom-dm")) {
|
|
if (c_info->type == LIBXL_DOMAIN_TYPE_HVM)
|
|
fprintf(stderr, "WARNING: Or use"
|
|
" \"device_model_stubdomain_override\" if you "
|
|
" want to enable stubdomains\n");
|
|
else
|
|
fprintf(stderr, "WARNING: ignoring"
|
|
" \"device_model_stubdomain_override\" directive"
|
|
" for pv guest\n");
|
|
}
|
|
}
|
|
|
|
|
|
xlu_cfg_replace_string (config, "device_model_override",
|
|
&dm_info->device_model, 0);
|
|
if (!xlu_cfg_get_string (config, "device_model_version", &buf, 0)) {
|
|
if (!strcmp(buf, "qemu-xen-traditional")) {
|
|
dm_info->device_model_version
|
|
= LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
|
|
} else if (!strcmp(buf, "qemu-xen")) {
|
|
dm_info->device_model_version
|
|
= LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
|
|
} else {
|
|
fprintf(stderr,
|
|
"Unknown device_model_version \"%s\" specified\n", buf);
|
|
exit(1);
|
|
}
|
|
} else if (dm_info->device_model)
|
|
fprintf(stderr, "WARNING: device model override given without specific DM version\n");
|
|
if (!xlu_cfg_get_long (config, "device_model_stubdomain_override", &l, 0))
|
|
dm_info->device_model_stubdomain = l;
|
|
|
|
#define parse_extra_args(type) \
|
|
e = xlu_cfg_get_list_as_string_list(config, "device_model_args"#type, \
|
|
&dm_info->extra##type, 0); \
|
|
if (e && e != ESRCH) { \
|
|
fprintf(stderr,"xl: Unable to parse device_model_args"#type".\n");\
|
|
exit(-ERROR_FAIL); \
|
|
}
|
|
|
|
/* parse extra args for qemu, common to both pv, hvm */
|
|
parse_extra_args();
|
|
|
|
/* parse extra args dedicated to pv */
|
|
parse_extra_args(_pv);
|
|
|
|
/* parse extra args dedicated to hvm */
|
|
parse_extra_args(_hvm);
|
|
|
|
#undef parse_extra_args
|
|
|
|
if (c_info->type == LIBXL_DOMAIN_TYPE_HVM) {
|
|
if (!xlu_cfg_get_long (config, "stdvga", &l, 0))
|
|
dm_info->stdvga = l;
|
|
if (!xlu_cfg_get_long (config, "vnc", &l, 0))
|
|
dm_info->vnc = l;
|
|
xlu_cfg_replace_string (config, "vnclisten", &dm_info->vnclisten, 0);
|
|
xlu_cfg_replace_string (config, "vncpasswd", &dm_info->vncpasswd, 0);
|
|
if (!xlu_cfg_get_long (config, "vncdisplay", &l, 0))
|
|
dm_info->vncdisplay = l;
|
|
if (!xlu_cfg_get_long (config, "vncunused", &l, 0))
|
|
dm_info->vncunused = l;
|
|
xlu_cfg_replace_string (config, "keymap", &dm_info->keymap, 0);
|
|
if (!xlu_cfg_get_long (config, "sdl", &l, 0))
|
|
dm_info->sdl = l;
|
|
if (!xlu_cfg_get_long (config, "opengl", &l, 0))
|
|
dm_info->opengl = l;
|
|
if (!xlu_cfg_get_long (config, "spice", &l, 0))
|
|
dm_info->spice = l;
|
|
if (!xlu_cfg_get_long (config, "spiceport", &l, 0))
|
|
dm_info->spiceport = l;
|
|
if (!xlu_cfg_get_long (config, "spicetls_port", &l, 0))
|
|
dm_info->spicetls_port = l;
|
|
xlu_cfg_replace_string (config, "spicehost", &dm_info->spicehost, 0);
|
|
if (!xlu_cfg_get_long (config, "spicedisable_ticketing", &l, 0))
|
|
dm_info->spicedisable_ticketing = l;
|
|
xlu_cfg_replace_string (config, "spicepasswd", &dm_info->spicepasswd, 0);
|
|
if (!xlu_cfg_get_long (config, "spiceagent_mouse", &l, 0))
|
|
dm_info->spiceagent_mouse = l;
|
|
else
|
|
dm_info->spiceagent_mouse = 1;
|
|
if (!xlu_cfg_get_long (config, "nographic", &l, 0))
|
|
dm_info->nographic = l;
|
|
if (!xlu_cfg_get_long (config, "gfx_passthru", &l, 0))
|
|
dm_info->gfx_passthru = l;
|
|
xlu_cfg_replace_string (config, "serial", &dm_info->serial, 0);
|
|
xlu_cfg_replace_string (config, "boot", &dm_info->boot, 0);
|
|
if (!xlu_cfg_get_long (config, "usb", &l, 0))
|
|
dm_info->usb = l;
|
|
xlu_cfg_replace_string (config, "usbdevice", &dm_info->usbdevice, 0);
|
|
xlu_cfg_replace_string (config, "soundhw", &dm_info->soundhw, 0);
|
|
if (!xlu_cfg_get_long (config, "xen_platform_pci", &l, 0))
|
|
dm_info->xen_platform_pci = l;
|
|
}
|
|
|
|
dm_info->type = c_info->type;
|
|
|
|
xlu_cfg_destroy(config);
|
|
}
|
|
|
|
/* Returns 1 if domain should be restarted,
|
|
* 2 if domain should be renamed then restarted, or 0 */
|
|
static int handle_domain_death(libxl_ctx *ctx, uint32_t domid,
|
|
libxl_event *event,
|
|
libxl_domain_config *d_config)
|
|
{
|
|
int restart = 0;
|
|
libxl_action_on_shutdown action;
|
|
|
|
switch (event->u.domain_shutdown.shutdown_reason) {
|
|
case SHUTDOWN_poweroff:
|
|
action = d_config->on_poweroff;
|
|
break;
|
|
case SHUTDOWN_reboot:
|
|
action = d_config->on_reboot;
|
|
break;
|
|
case SHUTDOWN_suspend:
|
|
LOG("Domain has suspended.");
|
|
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.",
|
|
event->u.domain_shutdown.shutdown_reason);
|
|
action = LIBXL_ACTION_ON_SHUTDOWN_DESTROY;
|
|
}
|
|
|
|
LOG("Action for shutdown reason code %d is %s",
|
|
event->u.domain_shutdown.shutdown_reason,
|
|
action_on_shutdown_names[action]);
|
|
|
|
if (action == LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_DESTROY || action == LIBXL_ACTION_ON_SHUTDOWN_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_ON_SHUTDOWN_COREDUMP_DESTROY)
|
|
action = LIBXL_ACTION_ON_SHUTDOWN_DESTROY;
|
|
else
|
|
action = LIBXL_ACTION_ON_SHUTDOWN_RESTART;
|
|
}
|
|
|
|
switch (action) {
|
|
case LIBXL_ACTION_ON_SHUTDOWN_PRESERVE:
|
|
break;
|
|
|
|
case LIBXL_ACTION_ON_SHUTDOWN_RESTART_RENAME:
|
|
restart = 2;
|
|
break;
|
|
|
|
case LIBXL_ACTION_ON_SHUTDOWN_RESTART:
|
|
restart = 1;
|
|
/* fall-through */
|
|
case LIBXL_ACTION_ON_SHUTDOWN_DESTROY:
|
|
LOG("Domain %d needs to be cleaned up: destroying the domain", domid);
|
|
libxl_domain_destroy(ctx, domid);
|
|
break;
|
|
|
|
case LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_DESTROY:
|
|
case LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_RESTART:
|
|
/* Already handled these above. */
|
|
abort();
|
|
}
|
|
|
|
return restart;
|
|
}
|
|
|
|
/* for now used only by main_networkattach, but can be reused elsewhere */
|
|
static int match_option_size(const char *prefix, size_t len,
|
|
char *arg, char **argopt)
|
|
{
|
|
int rc = strncmp(prefix, arg, len);
|
|
if (!rc) *argopt = arg+len;
|
|
return !rc;
|
|
}
|
|
#define MATCH_OPTION(prefix, arg, oparg) \
|
|
match_option_size((prefix "="), sizeof((prefix)), (arg), &(oparg))
|
|
|
|
static void replace_string(char **str, const char *val)
|
|
{
|
|
free(*str);
|
|
*str = strdup(val);
|
|
}
|
|
|
|
|
|
static int preserve_domain(libxl_ctx *ctx, uint32_t domid, libxl_event *event,
|
|
libxl_domain_config *d_config)
|
|
{
|
|
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 monitor; /* handle guest reboots etc */
|
|
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 */
|
|
int no_incr_generationid;
|
|
};
|
|
|
|
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 domain_wait_event(libxl_event **event_r)
|
|
{
|
|
int ret;
|
|
for (;;) {
|
|
ret = libxl_event_wait(ctx, event_r, LIBXL_EVENTMASK_ALL, 0,0);
|
|
if (ret) {
|
|
LOG("Domain %d, failed to get event, quitting (rc=%d)", domid, ret);
|
|
return ret;
|
|
}
|
|
if ((*event_r)->domid != domid) {
|
|
char *evstr = libxl_event_to_json(ctx, *event_r);
|
|
LOG("INTERNAL PROBLEM - ignoring unexpected event for"
|
|
" domain %d (expected %d): event=%s",
|
|
(*event_r)->domid, domid, evstr);
|
|
free(evstr);
|
|
libxl_event_free(ctx, *event_r);
|
|
continue;
|
|
}
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
static void evdisable_disk_ejects(libxl_evgen_disk_eject **diskws,
|
|
int num_disks)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < num_disks; i++) {
|
|
if (diskws[i])
|
|
libxl_evdisable_disk_eject(ctx, diskws[i]);
|
|
diskws[i] = NULL;
|
|
}
|
|
}
|
|
|
|
static int create_domain(struct domain_create *dom_info)
|
|
{
|
|
libxl_domain_config d_config;
|
|
|
|
int debug = dom_info->debug;
|
|
int daemonize = dom_info->daemonize;
|
|
int monitor = dom_info->monitor;
|
|
int paused = dom_info->paused;
|
|
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 i;
|
|
int need_daemon = daemonize;
|
|
int ret, rc;
|
|
libxl_evgen_domain_death *deathw = NULL;
|
|
libxl_evgen_disk_eject **diskws = NULL; /* one per disk */
|
|
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;
|
|
|
|
if (migrate_fd >= 0) {
|
|
restore_fd = migrate_fd;
|
|
} else {
|
|
restore_fd = open(restore_file, O_RDONLY);
|
|
rc = libxl_fd_set_cloexec(ctx, restore_fd, 1);
|
|
if (rc) return rc;
|
|
}
|
|
|
|
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 = "<saved>";
|
|
}
|
|
|
|
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 (d_config.c_info.type == LIBXL_DOMAIN_TYPE_HVM)
|
|
d_config.b_info.u.hvm.no_incr_generationid =
|
|
dom_info->no_incr_generationid;
|
|
|
|
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);
|
|
/*
|
|
* On subsequent reboot etc we should create the domain, not
|
|
* restore/migrate-receive it again.
|
|
*/
|
|
restore_file = NULL;
|
|
}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 && !monitor)
|
|
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 out;
|
|
}
|
|
}
|
|
if (status) {
|
|
libxl_report_child_exitstatus(ctx, XTL_ERROR,
|
|
"daemonizing child", child1, status);
|
|
ret = ERROR_FAIL;
|
|
goto 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|O_APPEND,
|
|
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());
|
|
|
|
ret = libxl_evenable_domain_death(ctx, domid, 0, &deathw);
|
|
if (ret) goto out;
|
|
|
|
if (!diskws) {
|
|
diskws = xmalloc(sizeof(*diskws) * d_config.num_disks);
|
|
for (i = 0; i < d_config.num_disks; i++)
|
|
diskws[i] = NULL;
|
|
}
|
|
for (i = 0; i < d_config.num_disks; i++) {
|
|
if (d_config.disks[i].removable) {
|
|
ret = libxl_evenable_disk_eject(ctx, domid, d_config.disks[i].vdev,
|
|
0, &diskws[i]);
|
|
if (ret) goto out;
|
|
}
|
|
}
|
|
while (1) {
|
|
libxl_event *event;
|
|
ret = domain_wait_event(&event);
|
|
if (ret) goto out;
|
|
|
|
switch (event->type) {
|
|
|
|
case LIBXL_EVENT_TYPE_DOMAIN_SHUTDOWN:
|
|
LOG("Domain %d has shut down, reason code %d 0x%x", domid,
|
|
event->u.domain_shutdown.shutdown_reason,
|
|
event->u.domain_shutdown.shutdown_reason);
|
|
switch (handle_domain_death(ctx, domid, event, &d_config)) {
|
|
case 2:
|
|
if (!preserve_domain(ctx, domid, event, &d_config)) {
|
|
/* If we fail then exit leaving the old domain in place. */
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
/* Otherwise fall through and restart. */
|
|
case 1:
|
|
libxl_event_free(ctx, event);
|
|
libxl_evdisable_domain_death(ctx, deathw);
|
|
deathw = NULL;
|
|
evdisable_disk_ejects(diskws, d_config.num_disks);
|
|
/* discard any other events which may have been generated */
|
|
while (!(ret = libxl_event_check(ctx, &event,
|
|
LIBXL_EVENTMASK_ALL, 0,0))) {
|
|
libxl_event_free(ctx, event);
|
|
}
|
|
if (ret != ERROR_NOT_READY) {
|
|
LOG("warning, libxl_event_check (cleanup) failed (rc=%d)",
|
|
ret);
|
|
}
|
|
|
|
/*
|
|
* Do not attempt to reconnect if we come round again due to a
|
|
* guest reboot -- the stdin/out will be disconnected by then.
|
|
*/
|
|
dom_info->console_autoconnect = 0;
|
|
|
|
/* Some settings only make sense on first boot. */
|
|
paused = 0;
|
|
if (common_domname
|
|
&& strcmp(d_config.c_info.name, common_domname)) {
|
|
d_config.c_info.name = strdup(common_domname);
|
|
}
|
|
|
|
/*
|
|
* XXX FIXME: If this sleep is not there then domain
|
|
* re-creation fails sometimes.
|
|
*/
|
|
LOG("Done. Rebooting now");
|
|
sleep(2);
|
|
goto start;
|
|
|
|
case 0:
|
|
LOG("Done. Exiting now");
|
|
ret = 0;
|
|
goto out;
|
|
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
case LIBXL_EVENT_TYPE_DOMAIN_DESTROY:
|
|
LOG("Domain %d has been destroyed.", domid);
|
|
ret = 0;
|
|
goto out;
|
|
|
|
case LIBXL_EVENT_TYPE_DISK_EJECT:
|
|
/* XXX what is this for? */
|
|
libxl_cdrom_insert(ctx, domid, &event->u.disk_eject.disk);
|
|
break;
|
|
|
|
default:;
|
|
char *evstr = libxl_event_to_json(ctx, event);
|
|
LOG("warning, got unexpected event type %d, event=%s",
|
|
event->type, evstr);
|
|
free(evstr);
|
|
}
|
|
|
|
libxl_event_free(ctx, event);
|
|
}
|
|
|
|
error_out:
|
|
release_lock();
|
|
if (libxl_domid_valid_guest(domid))
|
|
libxl_domain_destroy(ctx, domid);
|
|
|
|
out:
|
|
if (logfile != 2)
|
|
close(logfile);
|
|
|
|
libxl_domain_config_dispose(&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 (deathw)
|
|
libxl_evdisable_domain_death(ctx, deathw);
|
|
if (diskws) {
|
|
evdisable_disk_ejects(diskws, d_config.num_disks);
|
|
free(diskws);
|
|
}
|
|
|
|
/*
|
|
* If we have daemonized then do not return to the caller -- this has
|
|
* already happened in the parent.
|
|
*/
|
|
if ( !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 [-vN] <subcommand> [args]\n\n");
|
|
printf("xl full list of subcommands:\n\n");
|
|
for (i = 0; i < cmdtable_len; i++) {
|
|
printf(" %-19s ", cmd_table[i].cmd_name);
|
|
if (strlen(cmd_table[i].cmd_name) > 19)
|
|
printf("\n %-19s ", "");
|
|
printf("%s\n", cmd_table[i].cmd_desc);
|
|
}
|
|
} else {
|
|
cmd = cmdtable_lookup(command);
|
|
if (cmd) {
|
|
printf("Usage: xl [-v%s] %s %s\n\n%s.\n\n",
|
|
cmd->can_dryrun ? "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 def_getopt(int argc, char * const argv[], const char *optstring,
|
|
const char* helpstr, int reqargs)
|
|
{
|
|
int opt;
|
|
|
|
opterr = 0;
|
|
while ((opt = getopt(argc, argv, optstring)) == '?') {
|
|
if (optopt == 'h') {
|
|
help(helpstr);
|
|
return 0;
|
|
}
|
|
fprintf(stderr, "option `%c' not supported.\n", optopt);
|
|
}
|
|
if (opt != -1)
|
|
return opt;
|
|
|
|
if (argc - optind <= reqargs - 1) {
|
|
fprintf(stderr, "'xl %s' requires at least %d argument%s.\n\n",
|
|
helpstr, reqargs, reqargs > 1 ? "s" : "");
|
|
help(helpstr);
|
|
return 2;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
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;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "mem-max", 2)) != -1)
|
|
return opt;
|
|
|
|
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;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "mem-set", 2)) != -1)
|
|
return opt;
|
|
|
|
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;
|
|
XLU_Config *config = 0;
|
|
|
|
find_domain(dom);
|
|
|
|
if (asprintf(&buf, "%s,%s:cdrom,r", phys ? phys : "", virtdev) < 0) {
|
|
fprintf(stderr, "out of memory\n");
|
|
return;
|
|
}
|
|
|
|
parse_disk_config(&config, buf, &disk);
|
|
|
|
disk.backend_domid = 0;
|
|
|
|
libxl_cdrom_insert(ctx, domid, &disk);
|
|
|
|
libxl_device_disk_dispose(&disk);
|
|
free(buf);
|
|
}
|
|
|
|
int main_cd_eject(int argc, char **argv)
|
|
{
|
|
int opt = 0;
|
|
const char *p = NULL, *virtdev;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "cd-eject", 2)) != -1)
|
|
return opt;
|
|
|
|
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 */
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "cd-insert", 3)) != -1)
|
|
return opt;
|
|
|
|
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_type type = 0;
|
|
|
|
while ((opt = def_getopt(argc, argv, "n:t:", "console", 1)) != -1) {
|
|
switch (opt) {
|
|
case 0: case 2:
|
|
return opt;
|
|
case 't':
|
|
if (!strcmp(optarg, "pv"))
|
|
type = LIBXL_CONSOLE_TYPE_PV;
|
|
else if (!strcmp(optarg, "serial"))
|
|
type = LIBXL_CONSOLE_TYPE_SERIAL;
|
|
else {
|
|
fprintf(stderr, "console type supported are: pv, serial\n");
|
|
return 2;
|
|
}
|
|
break;
|
|
case 'n':
|
|
num = atoi(optarg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
pcidevs = libxl_device_pci_list_assignable(ctx, &num);
|
|
|
|
if ( pcidevs == NULL )
|
|
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);
|
|
libxl_device_pci_dispose(&pcidevs[i]);
|
|
}
|
|
free(pcidevs);
|
|
}
|
|
|
|
int main_pcilist_assignable(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "pci-list-assignable-devices", 0)) != -1)
|
|
return opt;
|
|
|
|
pcilist_assignable();
|
|
return 0;
|
|
}
|
|
|
|
static void pcilist(const char *dom)
|
|
{
|
|
libxl_device_pci *pcidevs;
|
|
int num, i;
|
|
|
|
find_domain(dom);
|
|
|
|
pcidevs = libxl_device_pci_list(ctx, domid, &num);
|
|
if (pcidevs == NULL)
|
|
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_dispose(&pcidevs[i]);
|
|
}
|
|
free(pcidevs);
|
|
}
|
|
|
|
int main_pcilist(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
const char *domname = NULL;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "pci-list", 1)) != -1)
|
|
return opt;
|
|
|
|
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);
|
|
}
|
|
if (force)
|
|
libxl_device_pci_destroy(ctx, domid, &pcidev);
|
|
else
|
|
libxl_device_pci_remove(ctx, domid, &pcidev);
|
|
libxl_device_pci_dispose(&pcidev);
|
|
}
|
|
|
|
int main_pcidetach(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
int force = 0;
|
|
const char *domname = NULL, *bdf = NULL;
|
|
|
|
while ((opt = def_getopt(argc, argv, "f", "pci-detach", 2)) != -1) {
|
|
switch (opt) {
|
|
case 0: case 2:
|
|
return opt;
|
|
case 'f':
|
|
force = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
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_dispose(&pcidev);
|
|
}
|
|
|
|
int main_pciattach(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
const char *domname = NULL, *bdf = NULL, *vs = NULL;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "pci-attach", 2)) != -1)
|
|
return opt;
|
|
|
|
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);
|
|
if (rc) { fprintf(stderr,"destroy failed (rc=%d)\n",rc); exit(-1); }
|
|
}
|
|
|
|
static void shutdown_domain(const char *p, int wait)
|
|
{
|
|
int rc;
|
|
libxl_event *event;
|
|
|
|
find_domain(p);
|
|
rc=libxl_domain_shutdown(ctx, domid);
|
|
if (rc) {
|
|
if (rc == ERROR_NOPARAVIRT) {
|
|
fprintf(stderr, "PV control interface not available:"
|
|
" external graceful shutdown not possible.\n");
|
|
fprintf(stderr, "Use \"xl button-press <dom> power\" or"
|
|
" \"xl destroy <dom>\".\n");
|
|
}
|
|
fprintf(stderr,"shutdown failed (rc=%d)\n",rc);exit(-1);
|
|
}
|
|
|
|
if (wait) {
|
|
libxl_evgen_domain_death *deathw;
|
|
|
|
rc = libxl_evenable_domain_death(ctx, domid, 0, &deathw);
|
|
if (rc) {
|
|
fprintf(stderr,"wait for death failed (evgen, rc=%d)\n",rc);
|
|
exit(-1);
|
|
}
|
|
|
|
for (;;) {
|
|
rc = domain_wait_event(&event);
|
|
if (rc) exit(-1);
|
|
|
|
switch (event->type) {
|
|
|
|
case LIBXL_EVENT_TYPE_DOMAIN_DESTROY:
|
|
LOG("Domain %d has been destroyed", domid);
|
|
goto done;
|
|
|
|
case LIBXL_EVENT_TYPE_DOMAIN_SHUTDOWN:
|
|
LOG("Domain %d has been shut down, reason code %d %x", domid,
|
|
event->u.domain_shutdown.shutdown_reason,
|
|
event->u.domain_shutdown.shutdown_reason);
|
|
goto done;
|
|
|
|
default:
|
|
LOG("Unexpected event type %d", event->type);
|
|
break;
|
|
}
|
|
libxl_event_free(ctx, event);
|
|
}
|
|
done:
|
|
libxl_event_free(ctx, event);
|
|
libxl_evdisable_domain_death(ctx, deathw);
|
|
}
|
|
}
|
|
|
|
static void reboot_domain(const char *p)
|
|
{
|
|
int rc;
|
|
find_domain(p);
|
|
rc=libxl_domain_reboot(ctx, domid);
|
|
if (rc) {
|
|
if (rc == ERROR_NOPARAVIRT) {
|
|
fprintf(stderr, "PV control interface not available:"
|
|
" external graceful reboot not possible.\n");
|
|
fprintf(stderr, "Use \"xl button-press <dom> power\" or"
|
|
" \"xl destroy <dom>\".\n");
|
|
}
|
|
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, "<domid %d data>", 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_dispose(&d_config);
|
|
free(data);
|
|
free(config_file);
|
|
}
|
|
}
|
|
|
|
static void list_domains(int verbose, int context, 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\tSecurity Label");
|
|
if (context && !verbose) printf(" Security Label");
|
|
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", "-");
|
|
}
|
|
if (verbose || context) {
|
|
int rc;
|
|
size_t size;
|
|
char *buf;
|
|
rc = libxl_flask_sid_to_context(ctx, info[i].ssidref, &buf,
|
|
&size);
|
|
if (rc < 0)
|
|
printf(" -");
|
|
else {
|
|
printf(" %s", buf);
|
|
free(buf);
|
|
}
|
|
}
|
|
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);
|
|
|
|
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);
|
|
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);
|
|
}
|
|
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); /* 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 monitor)
|
|
{
|
|
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.monitor = monitor;
|
|
dom_info.paused = 1;
|
|
dom_info.restore_file = "incoming migration stream";
|
|
dom_info.migrate_fd = 0; /* stdin */
|
|
dom_info.migration_domname_r = &migration_domname;
|
|
dom_info.no_incr_generationid = 1;
|
|
|
|
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);
|
|
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);
|
|
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, monitor = 1,
|
|
console_autoconnect = 0;
|
|
int opt, rc;
|
|
|
|
while ((opt = def_getopt(argc, argv, "Fcpde", "restore", 1)) != -1) {
|
|
switch (opt) {
|
|
case 0: case 2:
|
|
return opt;
|
|
case 'c':
|
|
console_autoconnect = 1;
|
|
break;
|
|
case 'p':
|
|
paused = 1;
|
|
break;
|
|
case 'd':
|
|
debug = 1;
|
|
break;
|
|
case 'F':
|
|
daemonize = 0;
|
|
break;
|
|
case 'e':
|
|
daemonize = 0;
|
|
monitor = 0;
|
|
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.monitor = monitor;
|
|
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, monitor = 1;
|
|
int opt;
|
|
|
|
while ((opt = def_getopt(argc, argv, "Fed", "migrate-receive", 0)) != -1) {
|
|
switch (opt) {
|
|
case 0: case 2:
|
|
return opt;
|
|
case 'F':
|
|
daemonize = 0;
|
|
break;
|
|
case 'e':
|
|
daemonize = 0;
|
|
monitor = 0;
|
|
break;
|
|
case 'd':
|
|
debug = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (argc-optind != 0) {
|
|
help("migrate-receive");
|
|
return 2;
|
|
}
|
|
migrate_receive(debug, daemonize, monitor);
|
|
return 0;
|
|
}
|
|
|
|
int main_save(int argc, char **argv)
|
|
{
|
|
const char *filename, *p;
|
|
const char *config_filename = NULL;
|
|
int checkpoint = 0;
|
|
int opt;
|
|
|
|
while ((opt = def_getopt(argc, argv, "c", "save", 2)) != -1) {
|
|
switch (opt) {
|
|
case 0: case 2:
|
|
return opt;
|
|
case 'c':
|
|
checkpoint = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (argc-optind > 3) {
|
|
help("save");
|
|
return 2;
|
|
}
|
|
|
|
p = argv[optind];
|
|
filename = argv[optind + 1];
|
|
if ( argc - optind >= 3 )
|
|
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, monitor = 1, debug = 0;
|
|
|
|
while ((opt = def_getopt(argc, argv, "FC:s:ed", "migrate", 2)) != -1) {
|
|
switch (opt) {
|
|
case 0: case 2:
|
|
return opt;
|
|
case 'C':
|
|
config_filename = optarg;
|
|
break;
|
|
case 's':
|
|
ssh_command = optarg;
|
|
break;
|
|
case 'F':
|
|
daemonize = 0;
|
|
break;
|
|
case 'e':
|
|
daemonize = 0;
|
|
monitor = 0;
|
|
break;
|
|
case 'd':
|
|
debug = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "dump-core", 2)) != -1)
|
|
return opt;
|
|
|
|
core_dump_domain(argv[optind], argv[optind + 1]);
|
|
return 0;
|
|
}
|
|
|
|
int main_pause(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "pause", 1)) != -1)
|
|
return opt;
|
|
|
|
pause_domain(argv[optind]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main_unpause(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "unpause", 1)) != -1)
|
|
return opt;
|
|
|
|
unpause_domain(argv[optind]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main_destroy(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "destroy", 1)) != -1)
|
|
return opt;
|
|
|
|
destroy_domain(argv[optind]);
|
|
return 0;
|
|
}
|
|
|
|
int main_shutdown(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
int wait = 0;
|
|
|
|
while ((opt = def_getopt(argc, argv, "w", "shutdown", 1)) != -1) {
|
|
switch (opt) {
|
|
case 0: case 2:
|
|
return opt;
|
|
case 'w':
|
|
wait = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
shutdown_domain(argv[optind], wait);
|
|
return 0;
|
|
}
|
|
|
|
int main_reboot(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "reboot", 1)) != -1)
|
|
return opt;
|
|
|
|
reboot_domain(argv[optind]);
|
|
return 0;
|
|
}
|
|
|
|
int main_list(int argc, char **argv)
|
|
{
|
|
int opt, verbose = 0;
|
|
int context = 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'},
|
|
{"context", 0, 0, 'Z'},
|
|
{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, "lvhZ", 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;
|
|
case 'Z':
|
|
context = 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, context, info, nb_domain);
|
|
|
|
free(info_free);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main_list_vm(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "list-vm", 0)) != -1)
|
|
return opt;
|
|
|
|
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,
|
|
quiet = 0, monitor = 1;
|
|
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, "Fhnqf: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 'F':
|
|
daemonize = 0;
|
|
break;
|
|
case 'e':
|
|
daemonize = 0;
|
|
monitor = 0;
|
|
break;
|
|
case 'h':
|
|
help("create");
|
|
return 0;
|
|
case 'n':
|
|
dryrun_only = 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.monitor = monitor;
|
|
dom_info.paused = paused;
|
|
dom_info.dryrun = dryrun_only;
|
|
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 = LIBXL_BUTTON_POWER;
|
|
} else if (!strcmp(b, "sleep")) {
|
|
button = LIBXL_BUTTON_SLEEP;
|
|
} 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;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "button-press", 2)) != -1)
|
|
return opt;
|
|
|
|
button_press(argv[optind], argv[optind + 1]);
|
|
|
|
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_dispose(&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<nb_domain; i++)
|
|
print_domain_vcpuinfo(dominfo[i].domid, physinfo.nr_cpus);
|
|
|
|
free(dominfo);
|
|
|
|
} else {
|
|
for (; argc > 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;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "cpu-list", 0)) != -1)
|
|
return opt;
|
|
|
|
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_dispose(&cpumap);
|
|
vcpupin_out:
|
|
;
|
|
}
|
|
|
|
int main_vcpupin(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "vcpu-pin", 3)) != -1)
|
|
return opt;
|
|
|
|
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_dispose(&cpumap);
|
|
}
|
|
|
|
int main_vcpuset(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "vcpu-set", 2)) != -1)
|
|
return opt;
|
|
|
|
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 : %u\n", info->pagesize);
|
|
printf("platform_params : virt_start=0x%"PRIx64"\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);
|
|
printf("sharing_freed_memory : %"PRIu64"\n", info.sharing_freed_pages / i);
|
|
printf("sharing_used_memory : %"PRIu64"\n", info.sharing_used_frames / 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_dispose(&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 void sharing(const libxl_dominfo *info, int nb_domain)
|
|
{
|
|
int i;
|
|
|
|
printf("Name ID Mem Shared\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 %5lu\n",
|
|
domname,
|
|
info[i].domid,
|
|
(unsigned long) (info[i].current_memkb / 1024),
|
|
(unsigned long) (info[i].shared_memkb / 1024));
|
|
free(domname);
|
|
}
|
|
}
|
|
|
|
int main_sharing(int argc, char **argv)
|
|
{
|
|
int opt = 0;
|
|
libxl_dominfo info_buf;
|
|
libxl_dominfo *info, *info_free = NULL;
|
|
int nb_domain, rc;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "sharing", 0)) != -1)
|
|
return opt;
|
|
|
|
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("sharing");
|
|
return 2;
|
|
}
|
|
|
|
sharing(info, nb_domain);
|
|
|
|
if (info_free)
|
|
free(info_free);
|
|
|
|
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 int sched_credit_domain_output(
|
|
int domid)
|
|
{
|
|
char *domname;
|
|
libxl_sched_credit scinfo;
|
|
int rc;
|
|
|
|
if (domid < 0) {
|
|
printf("%-33s %4s %6s %4s\n", "Name", "ID", "Weight", "Cap");
|
|
return 0;
|
|
}
|
|
rc = sched_credit_domain_get(domid, &scinfo);
|
|
if (rc)
|
|
return rc;
|
|
domname = libxl_domid_to_name(ctx, domid);
|
|
printf("%-33s %4d %6d %4d\n",
|
|
domname,
|
|
domid,
|
|
scinfo.weight,
|
|
scinfo.cap);
|
|
free(domname);
|
|
return 0;
|
|
}
|
|
|
|
static int sched_credit2_domain_get(
|
|
int domid, libxl_sched_credit2 *scinfo)
|
|
{
|
|
int rc;
|
|
|
|
rc = libxl_sched_credit2_domain_get(ctx, domid, scinfo);
|
|
if (rc)
|
|
fprintf(stderr, "libxl_sched_credit2_domain_get failed.\n");
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int sched_credit2_domain_set(
|
|
int domid, libxl_sched_credit2 *scinfo)
|
|
{
|
|
int rc;
|
|
|
|
rc = libxl_sched_credit2_domain_set(ctx, domid, scinfo);
|
|
if (rc)
|
|
fprintf(stderr, "libxl_sched_credit2_domain_set failed.\n");
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int sched_credit2_domain_output(
|
|
int domid)
|
|
{
|
|
char *domname;
|
|
libxl_sched_credit2 scinfo;
|
|
int rc;
|
|
|
|
if (domid < 0) {
|
|
printf("%-33s %4s %6s\n", "Name", "ID", "Weight");
|
|
return 0;
|
|
}
|
|
rc = sched_credit2_domain_get(domid, &scinfo);
|
|
if (rc)
|
|
return rc;
|
|
domname = libxl_domid_to_name(ctx, domid);
|
|
printf("%-33s %4d %6d\n",
|
|
domname,
|
|
domid,
|
|
scinfo.weight);
|
|
free(domname);
|
|
return 0;
|
|
}
|
|
|
|
static int sched_sedf_domain_get(
|
|
int domid, libxl_sched_sedf *scinfo)
|
|
{
|
|
int rc;
|
|
|
|
rc = libxl_sched_sedf_domain_get(ctx, domid, scinfo);
|
|
if (rc)
|
|
fprintf(stderr, "libxl_sched_sedf_domain_get failed.\n");
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int sched_sedf_domain_set(
|
|
int domid, libxl_sched_sedf *scinfo)
|
|
{
|
|
int rc;
|
|
|
|
rc = libxl_sched_sedf_domain_set(ctx, domid, scinfo);
|
|
if (rc)
|
|
fprintf(stderr, "libxl_sched_sedf_domain_set failed.\n");
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int sched_sedf_domain_output(
|
|
int domid)
|
|
{
|
|
char *domname;
|
|
libxl_sched_sedf scinfo;
|
|
int rc;
|
|
|
|
if (domid < 0) {
|
|
printf("%-33s %4s %6s %-6s %7s %5s %6s\n", "Name", "ID", "Period",
|
|
"Slice", "Latency", "Extra", "Weight");
|
|
return 0;
|
|
}
|
|
rc = sched_sedf_domain_get(domid, &scinfo);
|
|
if (rc)
|
|
return rc;
|
|
domname = libxl_domid_to_name(ctx, domid);
|
|
printf("%-33s %4d %6d %6d %7d %5d %6d\n",
|
|
domname,
|
|
domid,
|
|
scinfo.period,
|
|
scinfo.slice,
|
|
scinfo.latency,
|
|
scinfo.extratime,
|
|
scinfo.weight);
|
|
free(domname);
|
|
return 0;
|
|
}
|
|
|
|
static int sched_domain_output(
|
|
uint32_t sched, int (*output)(int), const char *cpupool)
|
|
{
|
|
libxl_dominfo *info;
|
|
libxl_cpupoolinfo *poolinfo = NULL;
|
|
uint32_t poolid;
|
|
char *poolname;
|
|
int nb_domain, n_pools = 0, i, p;
|
|
int rc = 0;
|
|
|
|
if (cpupool) {
|
|
if (cpupool_qualifier_to_cpupoolid(cpupool, &poolid, NULL) ||
|
|
!libxl_cpupoolid_to_name(ctx, poolid)) {
|
|
fprintf(stderr, "unknown cpupool \'%s\'\n", cpupool);
|
|
return -ERROR_FAIL;
|
|
}
|
|
}
|
|
|
|
info = libxl_list_domain(ctx, &nb_domain);
|
|
if (!info) {
|
|
fprintf(stderr, "libxl_domain_infolist failed.\n");
|
|
return 1;
|
|
}
|
|
poolinfo = libxl_list_cpupool(ctx, &n_pools);
|
|
if (!poolinfo) {
|
|
fprintf(stderr, "error getting cpupool info\n");
|
|
return -ERROR_NOMEM;
|
|
}
|
|
|
|
for (p = 0; !rc && (p < n_pools); p++) {
|
|
if ((poolinfo[p].sched_id != sched) ||
|
|
(cpupool && (poolid != poolinfo[p].poolid)))
|
|
continue;
|
|
|
|
poolname = libxl_cpupoolid_to_name(ctx, poolinfo[p].poolid);
|
|
printf("Cpupool %s:\n", poolname);
|
|
free(poolname);
|
|
|
|
output(-1);
|
|
for (i = 0; i < nb_domain; i++) {
|
|
if (info[i].cpupool != poolinfo[p].poolid)
|
|
continue;
|
|
rc = output(info[i].domid);
|
|
if (rc)
|
|
break;
|
|
}
|
|
}
|
|
if (poolinfo) {
|
|
for (p = 0; p < n_pools; p++) {
|
|
libxl_cpupoolinfo_dispose(poolinfo + p);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int main_sched_credit(int argc, char **argv)
|
|
{
|
|
libxl_sched_credit scinfo;
|
|
const char *dom = NULL;
|
|
const char *cpupool = NULL;
|
|
int weight = 256, cap = 0, opt_w = 0, opt_c = 0;
|
|
int opt, rc;
|
|
int option_index = 0;
|
|
static struct option long_options[] = {
|
|
{"domain", 1, 0, 'd'},
|
|
{"weight", 1, 0, 'w'},
|
|
{"cap", 1, 0, 'c'},
|
|
{"cpupool", 1, 0, 'p'},
|
|
{"help", 0, 0, 'h'},
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
while (1) {
|
|
opt = getopt_long(argc, argv, "d:w:c:p:h", long_options,
|
|
&option_index);
|
|
if (opt == -1)
|
|
break;
|
|
switch (opt) {
|
|
case 0: case 2:
|
|
return 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 'p':
|
|
cpupool = optarg;
|
|
break;
|
|
case 'h':
|
|
help("sched-credit");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (cpupool && (dom || opt_w || opt_c)) {
|
|
fprintf(stderr, "Specifying a cpupool is not allowed with other "
|
|
"options.\n");
|
|
return 1;
|
|
}
|
|
if (!dom && (opt_w || opt_c)) {
|
|
fprintf(stderr, "Must specify a domain.\n");
|
|
return 1;
|
|
}
|
|
|
|
if (!dom) { /* list all domain's credit scheduler info */
|
|
return -sched_domain_output(XEN_SCHEDULER_CREDIT,
|
|
sched_credit_domain_output, cpupool);
|
|
} else {
|
|
find_domain(dom);
|
|
|
|
rc = sched_credit_domain_get(domid, &scinfo);
|
|
if (rc)
|
|
return -rc;
|
|
|
|
if (!opt_w && !opt_c) { /* output credit scheduler info */
|
|
sched_credit_domain_output(-1);
|
|
return -sched_credit_domain_output(domid);
|
|
} 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_sched_credit2(int argc, char **argv)
|
|
{
|
|
libxl_sched_credit2 scinfo;
|
|
const char *dom = NULL;
|
|
const char *cpupool = NULL;
|
|
int weight = 256, opt_w = 0;
|
|
int opt, rc;
|
|
int option_index = 0;
|
|
static struct option long_options[] = {
|
|
{"domain", 1, 0, 'd'},
|
|
{"weight", 1, 0, 'w'},
|
|
{"cpupool", 1, 0, 'p'},
|
|
{"help", 0, 0, 'h'},
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
while (1) {
|
|
opt = getopt_long(argc, argv, "d:w:p:h", long_options, &option_index);
|
|
if (opt == -1)
|
|
break;
|
|
switch (opt) {
|
|
case 0: case 2:
|
|
return opt;
|
|
case 'd':
|
|
dom = optarg;
|
|
break;
|
|
case 'w':
|
|
weight = strtol(optarg, NULL, 10);
|
|
opt_w = 1;
|
|
break;
|
|
case 'p':
|
|
cpupool = optarg;
|
|
break;
|
|
case 'h':
|
|
help("sched-credit");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (cpupool && (dom || opt_w)) {
|
|
fprintf(stderr, "Specifying a cpupool is not allowed with other "
|
|
"options.\n");
|
|
return 1;
|
|
}
|
|
if (!dom && opt_w) {
|
|
fprintf(stderr, "Must specify a domain.\n");
|
|
return 1;
|
|
}
|
|
|
|
if (!dom) { /* list all domain's credit scheduler info */
|
|
return -sched_domain_output(XEN_SCHEDULER_CREDIT2,
|
|
sched_credit2_domain_output, cpupool);
|
|
} else {
|
|
find_domain(dom);
|
|
|
|
rc = sched_credit2_domain_get(domid, &scinfo);
|
|
if (rc)
|
|
return -rc;
|
|
|
|
if (!opt_w) { /* output credit2 scheduler info */
|
|
sched_credit2_domain_output(-1);
|
|
return -sched_credit2_domain_output(domid);
|
|
} else { /* set credit2 scheduler paramaters */
|
|
if (opt_w)
|
|
scinfo.weight = weight;
|
|
rc = sched_credit2_domain_set(domid, &scinfo);
|
|
if (rc)
|
|
return -rc;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main_sched_sedf(int argc, char **argv)
|
|
{
|
|
libxl_sched_sedf scinfo;
|
|
const char *dom = NULL;
|
|
const char *cpupool = NULL;
|
|
int period = 0, opt_p = 0;
|
|
int slice = 0, opt_s = 0;
|
|
int latency = 0, opt_l = 0;
|
|
int extra = 0, opt_e = 0;
|
|
int weight = 0, opt_w = 0;
|
|
int opt, rc;
|
|
int option_index = 0;
|
|
static struct option long_options[] = {
|
|
{"period", 1, 0, 'p'},
|
|
{"slice", 1, 0, 's'},
|
|
{"latency", 1, 0, 'l'},
|
|
{"extra", 1, 0, 'e'},
|
|
{"weight", 1, 0, 'w'},
|
|
{"cpupool", 1, 0, 'c'},
|
|
{"help", 0, 0, 'h'},
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
while (1) {
|
|
opt = getopt_long(argc, argv, "d:p:s:l:e:w:c:h", long_options,
|
|
&option_index);
|
|
if (opt == -1)
|
|
break;
|
|
switch (opt) {
|
|
case 0: case 2:
|
|
return opt;
|
|
case 'd':
|
|
dom = optarg;
|
|
break;
|
|
case 'p':
|
|
period = strtol(optarg, NULL, 10);
|
|
opt_p = 1;
|
|
break;
|
|
case 's':
|
|
slice = strtol(optarg, NULL, 10);
|
|
opt_s = 1;
|
|
break;
|
|
case 'l':
|
|
latency = strtol(optarg, NULL, 10);
|
|
opt_l = 1;
|
|
break;
|
|
case 'e':
|
|
extra = strtol(optarg, NULL, 10);
|
|
opt_e = 1;
|
|
break;
|
|
case 'w':
|
|
weight = strtol(optarg, NULL, 10);
|
|
opt_w = 1;
|
|
break;
|
|
case 'c':
|
|
cpupool = optarg;
|
|
break;
|
|
case 'h':
|
|
help("sched-sedf");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (cpupool && (dom || opt_p || opt_s || opt_l || opt_e || opt_w)) {
|
|
fprintf(stderr, "Specifying a cpupool is not allowed with other "
|
|
"options.\n");
|
|
return 1;
|
|
}
|
|
if (!dom && (opt_p || opt_s || opt_l || opt_e || opt_w)) {
|
|
fprintf(stderr, "Must specify a domain.\n");
|
|
return 1;
|
|
}
|
|
if (opt_w && (opt_p || opt_s)) {
|
|
fprintf(stderr, "Specifying a weight AND period or slice is not "
|
|
"allowed.\n");
|
|
}
|
|
|
|
if (!dom) { /* list all domain's credit scheduler info */
|
|
return -sched_domain_output(XEN_SCHEDULER_SEDF,
|
|
sched_sedf_domain_output, cpupool);
|
|
} else {
|
|
find_domain(dom);
|
|
|
|
rc = sched_sedf_domain_get(domid, &scinfo);
|
|
if (rc)
|
|
return -rc;
|
|
|
|
if (!opt_p && !opt_s && !opt_l && !opt_e && !opt_w) {
|
|
/* output sedf scheduler info */
|
|
sched_sedf_domain_output(-1);
|
|
return -sched_sedf_domain_output(domid);
|
|
} else { /* set sedf scheduler paramaters */
|
|
if (opt_p) {
|
|
scinfo.period = period;
|
|
scinfo.weight = 0;
|
|
}
|
|
if (opt_s) {
|
|
scinfo.slice = slice;
|
|
scinfo.weight = 0;
|
|
}
|
|
if (opt_l)
|
|
scinfo.latency = latency;
|
|
if (opt_e)
|
|
scinfo.extratime = extra;
|
|
if (opt_w) {
|
|
scinfo.weight = weight;
|
|
scinfo.period = 0;
|
|
scinfo.slice = 0;
|
|
}
|
|
rc = sched_sedf_domain_set(domid, &scinfo);
|
|
if (rc)
|
|
return -rc;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main_domid(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
const char *domname = NULL;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "domid", 1)) != -1)
|
|
return opt;
|
|
|
|
domname = argv[optind];
|
|
|
|
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;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "domname", 1)) != -1)
|
|
return opt;
|
|
|
|
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;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "rename", 2)) != -1)
|
|
return opt;
|
|
|
|
dom = argv[optind++];
|
|
|
|
find_domain(dom);
|
|
new_name = argv[optind];
|
|
|
|
if (libxl_domain_rename(ctx, domid, common_domname, new_name)) {
|
|
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;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "trigger", 2)) != -1)
|
|
return opt;
|
|
|
|
dom = argv[optind++];
|
|
|
|
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;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "sysrq", 2)) != -1)
|
|
return opt;
|
|
|
|
dom = argv[optind++];
|
|
|
|
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;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "debug-keys", 1)) != -1)
|
|
return opt;
|
|
|
|
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 = def_getopt(argc, argv, "c", "dmesg", 0)) != -1) {
|
|
switch (opt) {
|
|
case 0: case 2:
|
|
return opt;
|
|
case 'c':
|
|
clear = 1;
|
|
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;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "top", 0)) != -1)
|
|
return opt;
|
|
|
|
return system("xentop");
|
|
}
|
|
|
|
int main_networkattach(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
libxl_device_nic nic;
|
|
char *endptr, *oparg;
|
|
const char *tok;
|
|
int i;
|
|
unsigned int val;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "network-attach", 1)) != -1)
|
|
return opt;
|
|
|
|
if (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(ctx, &nic);
|
|
for (argv += optind+1, argc -= optind+1; argc > 0; ++argv, --argc) {
|
|
if (MATCH_OPTION("type", *argv, oparg)) {
|
|
if (!strcmp("vif", oparg)) {
|
|
nic.nictype = LIBXL_NIC_TYPE_VIF;
|
|
} else if (!strcmp("ioemu", oparg)) {
|
|
nic.nictype = LIBXL_NIC_TYPE_IOEMU;
|
|
} else {
|
|
fprintf(stderr, "Invalid parameter `type'.\n");
|
|
return 1;
|
|
}
|
|
} else if (MATCH_OPTION("mac", *argv, oparg)) {
|
|
tok = strtok(oparg, ":");
|
|
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 (MATCH_OPTION("bridge", *argv, oparg)) {
|
|
replace_string(&nic.bridge, oparg);
|
|
} else if (MATCH_OPTION("ip", *argv, oparg)) {
|
|
replace_string(&nic.ip, oparg);
|
|
} else if (MATCH_OPTION("script", *argv, oparg)) {
|
|
replace_string(&nic.script, oparg);
|
|
} else if (MATCH_OPTION("backend", *argv, oparg)) {
|
|
if(libxl_name_to_domid(ctx, oparg, &val)) {
|
|
fprintf(stderr, "Specified backend domain does not exist, defaulting to Dom0\n");
|
|
val = 0;
|
|
}
|
|
nic.backend_domid = val;
|
|
} else if (MATCH_OPTION("vifname", *argv, oparg)) {
|
|
replace_string(&nic.ifname, oparg);
|
|
} else if (MATCH_OPTION("model", *argv, oparg)) {
|
|
replace_string(&nic.model, oparg);
|
|
} else if (MATCH_OPTION("rate", *argv, oparg)) {
|
|
} else if (MATCH_OPTION("accel", *argv, oparg)) {
|
|
} else {
|
|
fprintf(stderr, "unrecognized argument `%s'\n", *argv);
|
|
return 1;
|
|
}
|
|
}
|
|
if (libxl_device_nic_add(ctx, domid, &nic)) {
|
|
fprintf(stderr, "libxl_device_nic_add failed.\n");
|
|
return 1;
|
|
}
|
|
libxl_device_nic_dispose(&nic);
|
|
return 0;
|
|
}
|
|
|
|
int main_networklist(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
libxl_device_nic *nics;
|
|
libxl_nicinfo nicinfo;
|
|
int nb, i;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "network-list", 1)) != -1)
|
|
return opt;
|
|
|
|
/* 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;
|
|
}
|
|
nics = libxl_device_nic_list(ctx, domid, &nb);
|
|
if (!nics) {
|
|
continue;
|
|
}
|
|
for (i = 0; i < nb; ++i) {
|
|
if (!libxl_device_nic_getinfo(ctx, domid, &nics[i], &nicinfo)) {
|
|
/* Idx BE */
|
|
printf("%-3d %-2d ", nicinfo.devid, nicinfo.backend_id);
|
|
/* MAC */
|
|
printf(LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nics[i].mac));
|
|
/* Hdl Sta evch txr/rxr BE-path */
|
|
printf("%6d %5d %6d %5d/%-11d %-30s\n",
|
|
nicinfo.devid, nicinfo.state, nicinfo.evtch,
|
|
nicinfo.rref_tx, nicinfo.rref_rx, nicinfo.backend);
|
|
libxl_nicinfo_dispose(&nicinfo);
|
|
}
|
|
libxl_device_nic_dispose(&nics[i]);
|
|
}
|
|
free(nics);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int main_networkdetach(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
libxl_device_nic nic;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "network-detach", 2)) != -1)
|
|
return opt;
|
|
|
|
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, atoi(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_remove(ctx, domid, &nic)) {
|
|
fprintf(stderr, "libxl_device_nic_del failed.\n");
|
|
return 1;
|
|
}
|
|
libxl_device_nic_dispose(&nic);
|
|
return 0;
|
|
}
|
|
|
|
int main_blockattach(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
uint32_t fe_domid, be_domid = 0;
|
|
libxl_device_disk disk = { 0 };
|
|
XLU_Config *config = 0;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "block-attach", 2)) != -1)
|
|
return opt;
|
|
|
|
if (domain_qualifier_to_domid(argv[optind], &fe_domid, 0) < 0) {
|
|
fprintf(stderr, "%s is an invalid domain identifier\n", argv[optind]);
|
|
return 1;
|
|
}
|
|
optind++;
|
|
|
|
parse_disk_config_multistring
|
|
(&config, argc-optind, (const char* const*)argv + optind, &disk);
|
|
|
|
disk.backend_domid = be_domid;
|
|
|
|
if (dryrun_only) {
|
|
char *json = libxl_device_disk_to_json(ctx, &disk);
|
|
printf("disk: %s\n", json);
|
|
free(json);
|
|
if (ferror(stdout) || fflush(stdout)) { perror("stdout"); exit(-1); }
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "block-list", 1)) != -1)
|
|
return opt;
|
|
|
|
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<nb; i++) {
|
|
if (!libxl_device_disk_getinfo(ctx, domid, &disks[i], &diskinfo)) {
|
|
/* Vdev BE hdl st evch rref BE-path*/
|
|
printf("%-5d %-3d %-6d %-5d %-6d %-8d %-30s\n",
|
|
diskinfo.devid, diskinfo.backend_id, diskinfo.frontend_id,
|
|
diskinfo.state, diskinfo.evtch, diskinfo.rref, diskinfo.backend);
|
|
libxl_diskinfo_dispose(&diskinfo);
|
|
}
|
|
libxl_device_disk_dispose(&disks[i]);
|
|
}
|
|
free(disks);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int main_blockdetach(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
libxl_device_disk disk;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "block-detach", 2)) != -1)
|
|
return opt;
|
|
|
|
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_disk(ctx, domid, atoi(argv[optind+1]), &disk)) {
|
|
fprintf(stderr, "Error: Device %s not connected.\n", argv[optind+1]);
|
|
return 1;
|
|
}
|
|
if (libxl_device_disk_remove(ctx, domid, &disk)) {
|
|
fprintf(stderr, "libxl_device_disk_remove failed.\n");
|
|
}
|
|
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 = def_getopt(argc, argv, "s", "uptime", 1)) != -1) {
|
|
switch (opt) {
|
|
case 0: case 2:
|
|
return opt;
|
|
case 's':
|
|
short_mode = 1;
|
|
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 = def_getopt(argc, argv, "al", "tmem-list", 0)) != -1) {
|
|
switch (opt) {
|
|
case 0: case 2:
|
|
return opt;
|
|
case 'l':
|
|
use_long = 1;
|
|
break;
|
|
case 'a':
|
|
all = 1;
|
|
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 = def_getopt(argc, argv, "a", "tmem-freeze", 0)) != -1) {
|
|
switch (opt) {
|
|
case 0: case 2:
|
|
return opt;
|
|
case 'a':
|
|
all = 1;
|
|
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 = def_getopt(argc, argv, "a", "tmem-destroy", 0)) != -1) {
|
|
switch (opt) {
|
|
case 0: case 2:
|
|
return opt;
|
|
case 'a':
|
|
all = 1;
|
|
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 = def_getopt(argc, argv, "a", "tmem-thaw", 0)) != -1) {
|
|
switch (opt) {
|
|
case 0: case 2:
|
|
return opt;
|
|
case 'a':
|
|
all = 1;
|
|
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 = def_getopt(argc, argv, "aw:c:p:", "tmem-set", 0)) != -1) {
|
|
switch (opt) {
|
|
case 0: case 2:
|
|
return 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;
|
|
}
|
|
}
|
|
|
|
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 = def_getopt(argc, argv, "au:A:", "tmem-shared-auth", 0)) != -1) {
|
|
switch (opt) {
|
|
case 0: case 2:
|
|
return opt;
|
|
case 'a':
|
|
all = 1;
|
|
break;
|
|
case 'u':
|
|
uuid = optarg;
|
|
break;
|
|
case 'A':
|
|
autharg = optarg;
|
|
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;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "tmem-freeable", 0)) != -1)
|
|
return opt;
|
|
|
|
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 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_only = 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, 0))
|
|
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, 0)) {
|
|
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_dispose(&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_only)
|
|
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'},
|
|
{"long", 0, 0, 'l'},
|
|
{"cpus", 0, 0, 'c'},
|
|
{0, 0, 0, 0}
|
|
};
|
|
int opt_long = 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, "hlc", long_options, &option_index);
|
|
if (opt == -1)
|
|
break;
|
|
|
|
switch (opt) {
|
|
case 'h':
|
|
help("cpupool-list");
|
|
return 0;
|
|
case 'l':
|
|
opt_long = 1;
|
|
break;
|
|
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_dispose(poolinfo + p);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int main_cpupooldestroy(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
const char *pool;
|
|
uint32_t poolid;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "cpupool-destroy", 1)) != -1)
|
|
return opt;
|
|
|
|
pool = 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;
|
|
}
|
|
|
|
return -libxl_cpupool_destroy(ctx, poolid);
|
|
}
|
|
|
|
int main_cpupoolrename(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
const char *pool;
|
|
const char *new_name;
|
|
uint32_t poolid;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "cpupool-rename", 2)) != -1)
|
|
return opt;
|
|
|
|
pool = 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;
|
|
}
|
|
|
|
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;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "cpupool-cpu-add", 2)) != -1)
|
|
return opt;
|
|
|
|
pool = argv[optind++];
|
|
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;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "cpupool-cpu-remove", 2)) != -1)
|
|
return opt;
|
|
|
|
pool = argv[optind++];
|
|
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;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "cpupool-migrate", 2)) != -1)
|
|
return opt;
|
|
|
|
dom = argv[optind++];
|
|
pool = argv[optind];
|
|
|
|
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;
|
|
|
|
if ((opt = def_getopt(argc, argv, "", "cpupool-numa-split", 0)) != -1)
|
|
return opt;
|
|
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_dispose(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_dispose(&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_dispose(&topology);
|
|
libxl_cpumap_dispose(&cpumap);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int main_getenforce(int argc, char **argv)
|
|
{
|
|
int ret;
|
|
|
|
ret = libxl_flask_getenforce(ctx);
|
|
|
|
if (ret < 0) {
|
|
if (errno == ENOSYS)
|
|
printf("Flask XSM Disabled\n");
|
|
else
|
|
fprintf(stderr, "Failed to get enforcing mode\n");
|
|
}
|
|
else if (ret == 1)
|
|
printf("Enforcing\n");
|
|
else if (ret == 0)
|
|
printf("Permissive\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
int main_setenforce(int argc, char **argv)
|
|
{
|
|
int ret, mode = -1;
|
|
const char *p = NULL;
|
|
|
|
if (optind >= argc) {
|
|
help("setenforce");
|
|
return 2;
|
|
}
|
|
|
|
p = argv[optind];
|
|
|
|
if (!strcmp(p, "0"))
|
|
mode = 0;
|
|
else if (!strcmp(p, "1"))
|
|
mode = 1;
|
|
else if (!strcasecmp(p, "permissive"))
|
|
mode = 0;
|
|
else if (!strcasecmp(p, "enforcing"))
|
|
mode = 1;
|
|
else {
|
|
help("setenforce");
|
|
return 2;
|
|
}
|
|
|
|
ret = libxl_flask_setenforce(ctx, mode);
|
|
|
|
if (ret) {
|
|
if (errno == ENOSYS) {
|
|
fprintf(stderr, "Flask XSM disabled\n");
|
|
}
|
|
else
|
|
fprintf(stderr, "error occured while setting enforcing mode (%i)\n", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int main_loadpolicy(int argc, char **argv)
|
|
{
|
|
const char *polFName;
|
|
int polFd = 0;
|
|
void *polMemCp = NULL;
|
|
struct stat info;
|
|
int ret;
|
|
|
|
if (optind >= argc) {
|
|
help("loadpolicy");
|
|
return 2;
|
|
}
|
|
|
|
polFName = argv[optind];
|
|
polFd = open(polFName, O_RDONLY);
|
|
if ( polFd < 0 ) {
|
|
fprintf(stderr, "Error occurred opening policy file '%s': %s\n",
|
|
polFName, strerror(errno));
|
|
ret = -1;
|
|
goto done;
|
|
}
|
|
|
|
ret = stat(polFName, &info);
|
|
if ( ret < 0 ) {
|
|
fprintf(stderr, "Error occurred retrieving information about"
|
|
"policy file '%s': %s\n", polFName, strerror(errno));
|
|
goto done;
|
|
}
|
|
|
|
polMemCp = malloc(info.st_size);
|
|
|
|
ret = read(polFd, polMemCp, info.st_size);
|
|
if ( ret < 0 ) {
|
|
fprintf(stderr, "Unable to read new Flask policy file: %s\n",
|
|
strerror(errno));
|
|
goto done;
|
|
}
|
|
|
|
ret = libxl_flask_loadpolicy(ctx, polMemCp, info.st_size);
|
|
|
|
if (ret < 0) {
|
|
if (errno == ENOSYS) {
|
|
fprintf(stderr, "Flask XSM disabled\n");
|
|
} else {
|
|
errno = -ret;
|
|
fprintf(stderr, "Unable to load new Flask policy: %s\n",
|
|
strerror(errno));
|
|
ret = -1;
|
|
}
|
|
} else {
|
|
printf("Successfully loaded policy.\n");
|
|
}
|
|
|
|
done:
|
|
free(polMemCp);
|
|
if ( polFd > 0 )
|
|
close(polFd);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Local variables:
|
|
* mode: C
|
|
* c-basic-offset: 4
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*/
|