mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
Add the necessary logic in libxl to allow it to act as a listener for launching backends in a driver domain, replacing udev (like we already do on Dom0). This new functionality is acomplished by watching the domain backend path (/local/domain/<domid>/backend) and reacting to device creation/destruction. The way to launch this listener daemon is from xl, using the newly introduced "devd" command. The command will daemonize by default, using "xldevd.log" as it's logfile. Optionally the user can force the execution of the listener in the foreground by passing the "-F" option to the devd command. Current backends handled by this daemon include Qdisk, vbd and vif device types. Signed-off-by: Roger Pau Monné <roger.pau@citrix.com> Cc: Ian Campbell <ian.campbell@citrix.com> Acked-by: Ian Jackson <ian.jackson@eu.citrix.com>
7244 lines
207 KiB
C
7244 lines
207 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 <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 "libxl_json.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;
|
|
|
|
xlchild children[child_max];
|
|
|
|
#define INVALID_DOMID ~0
|
|
static const char *common_domname;
|
|
static int fd_lock = -1;
|
|
|
|
/* Stash for specific vcpu to pcpu mappping */
|
|
static int *vcpu_to_pcpu;
|
|
|
|
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)
|
|
|
|
struct domain_create {
|
|
int debug;
|
|
int daemonize;
|
|
int monitor; /* handle guest reboots etc */
|
|
int paused;
|
|
int dryrun;
|
|
int quiet;
|
|
int vnc;
|
|
int vncautopass;
|
|
int console_autoconnect;
|
|
int checkpointed_stream;
|
|
const char *config_file;
|
|
const char *extra_config; /* extra config string */
|
|
const char *restore_file;
|
|
int migrate_fd; /* -1 means none */
|
|
char **migration_domname_r; /* from malloc */
|
|
};
|
|
|
|
|
|
|
|
static int 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 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 uint32_t find_domain(const char *p) __attribute__((warn_unused_result));
|
|
static uint32_t find_domain(const char *p)
|
|
{
|
|
uint32_t domid;
|
|
int rc;
|
|
|
|
rc = libxl_domain_qualifier_to_domid(ctx, p, &domid);
|
|
if (rc) {
|
|
fprintf(stderr, "%s is an invalid domain identifier (rc=%d)\n", p, rc);
|
|
exit(2);
|
|
}
|
|
common_domname = libxl_domid_to_name(ctx, domid);
|
|
return domid;
|
|
}
|
|
|
|
static int vncviewer(uint32_t domid, int autopass)
|
|
{
|
|
libxl_vncviewer_exec(ctx, domid, autopass);
|
|
fprintf(stderr, "Unable to execute vncviewer\n");
|
|
return 1;
|
|
}
|
|
|
|
static void vncviewer_child_report(void)
|
|
{
|
|
if (xl_child_pid(child_vncviewer)) {
|
|
int status;
|
|
pid_t got = xl_waitpid(child_vncviewer, &status, 0);
|
|
if (got < 0)
|
|
perror("xl: warning, failed to waitpid for vncviewer child");
|
|
else if (status)
|
|
libxl_report_child_exitstatus(ctx, XTL_ERROR, "vncviewer child",
|
|
xl_child_pid(child_vncviewer), status);
|
|
}
|
|
}
|
|
|
|
static void autoconnect_vncviewer(uint32_t domid, int autopass)
|
|
{
|
|
vncviewer_child_report();
|
|
|
|
pid_t pid = xl_fork(child_vncviewer);
|
|
if (pid < 0) {
|
|
perror("unable to fork vncviewer");
|
|
return;
|
|
} else if (pid > 0)
|
|
return;
|
|
|
|
postfork();
|
|
|
|
sleep(1);
|
|
vncviewer(domid, autopass);
|
|
_exit(1);
|
|
}
|
|
|
|
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 yajl_gen_status printf_info_one_json(yajl_gen hand, int domid,
|
|
libxl_domain_config *d_config)
|
|
{
|
|
yajl_gen_status s;
|
|
|
|
s = yajl_gen_map_open(hand);
|
|
if (s != yajl_gen_status_ok)
|
|
goto out;
|
|
|
|
s = yajl_gen_string(hand, (const unsigned char *)"domid",
|
|
sizeof("domid")-1);
|
|
if (s != yajl_gen_status_ok)
|
|
goto out;
|
|
if (domid != -1)
|
|
s = yajl_gen_integer(hand, domid);
|
|
else
|
|
s = yajl_gen_null(hand);
|
|
if (s != yajl_gen_status_ok)
|
|
goto out;
|
|
|
|
s = yajl_gen_string(hand, (const unsigned char *)"config",
|
|
sizeof("config")-1);
|
|
if (s != yajl_gen_status_ok)
|
|
goto out;
|
|
s = libxl_domain_config_gen_json(hand, d_config);
|
|
if (s != yajl_gen_status_ok)
|
|
goto out;
|
|
|
|
s = yajl_gen_map_close(hand);
|
|
if (s != yajl_gen_status_ok)
|
|
goto out;
|
|
|
|
out:
|
|
return s;
|
|
}
|
|
static void printf_info(enum output_format output_format,
|
|
int domid,
|
|
libxl_domain_config *d_config)
|
|
{
|
|
if (output_format == OUTPUT_FORMAT_SXP)
|
|
return printf_info_sexp(domid, d_config);
|
|
|
|
const char *buf;
|
|
libxl_yajl_length len = 0;
|
|
yajl_gen_status s;
|
|
yajl_gen hand;
|
|
|
|
hand = libxl_yajl_gen_alloc(NULL);
|
|
if (!hand) {
|
|
fprintf(stderr, "unable to allocate JSON generator\n");
|
|
return;
|
|
}
|
|
|
|
s = printf_info_one_json(hand, domid, d_config);
|
|
if (s != yajl_gen_status_ok)
|
|
goto out;
|
|
|
|
s = yajl_gen_get_buf(hand, (const unsigned char **)&buf, &len);
|
|
if (s != yajl_gen_status_ok)
|
|
goto out;
|
|
|
|
puts(buf);
|
|
|
|
out:
|
|
yajl_gen_free(hand);
|
|
|
|
if (s != yajl_gen_status_ok)
|
|
fprintf(stderr,
|
|
"unable to format domain config as JSON (YAJL:%d)\n", s);
|
|
|
|
if (ferror(stdout) || fflush(stdout)) { perror("stdout"); exit(-1); }
|
|
}
|
|
|
|
static int do_daemonize(char *name)
|
|
{
|
|
char *fullname;
|
|
pid_t child1, got_child;
|
|
int nullfd, ret = 0;
|
|
int status = 0;
|
|
|
|
child1 = xl_fork(child_waitdaemon);
|
|
if (child1) {
|
|
for (;;) {
|
|
got_child = xl_waitpid(child_waitdaemon, &status, 0);
|
|
if (got_child == child1) break;
|
|
assert(got_child == -1);
|
|
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 = 1;
|
|
goto out;
|
|
}
|
|
|
|
postfork();
|
|
|
|
ret = libxl_create_logfile(ctx, name, &fullname);
|
|
if (ret) {
|
|
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);
|
|
|
|
CHK_ERRNO(( nullfd = open("/dev/null", O_RDONLY) )<0);
|
|
dup2(nullfd, 0);
|
|
dup2(logfile, 1);
|
|
dup2(logfile, 2);
|
|
|
|
CHK_ERRNO(daemon(0, 1) < 0);
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
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(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 parse_vif_rate(XLU_Config **config, const char *rate,
|
|
libxl_device_nic *nic)
|
|
{
|
|
int e;
|
|
|
|
e = xlu_vif_parse_rate(*config, rate, nic);
|
|
if (e == EINVAL || e == EOVERFLOW) exit(-1);
|
|
if (e) {
|
|
fprintf(stderr,"xlu_vif_parse_rate failed: %s\n",strerror(errno));
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
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 int vcpupin_parse(char *cpu, libxl_bitmap *cpumap)
|
|
{
|
|
libxl_bitmap exclude_cpumap;
|
|
uint32_t cpuida, cpuidb;
|
|
char *endptr, *toka, *tokb, *saveptr = NULL;
|
|
int i, rc = 0, rmcpu;
|
|
|
|
if (!strcmp(cpu, "all")) {
|
|
libxl_bitmap_set_any(cpumap);
|
|
return 0;
|
|
}
|
|
|
|
if (libxl_cpu_bitmap_alloc(ctx, &exclude_cpumap, 0)) {
|
|
fprintf(stderr, "Error: Failed to allocate cpumap.\n");
|
|
return ENOMEM;
|
|
}
|
|
|
|
for (toka = strtok_r(cpu, ",", &saveptr); toka;
|
|
toka = strtok_r(NULL, ",", &saveptr)) {
|
|
rmcpu = 0;
|
|
if (*toka == '^') {
|
|
/* This (These) Cpu(s) will be removed from the map */
|
|
toka++;
|
|
rmcpu = 1;
|
|
}
|
|
/* Extract a valid (range of) cpu(s) */
|
|
cpuida = cpuidb = strtoul(toka, &endptr, 10);
|
|
if (endptr == toka) {
|
|
fprintf(stderr, "Error: Invalid argument.\n");
|
|
rc = EINVAL;
|
|
goto vcpp_out;
|
|
}
|
|
if (*endptr == '-') {
|
|
tokb = endptr + 1;
|
|
cpuidb = strtoul(tokb, &endptr, 10);
|
|
if (endptr == tokb || cpuida > cpuidb) {
|
|
fprintf(stderr, "Error: Invalid argument.\n");
|
|
rc = EINVAL;
|
|
goto vcpp_out;
|
|
}
|
|
}
|
|
while (cpuida <= cpuidb) {
|
|
rmcpu == 0 ? libxl_bitmap_set(cpumap, cpuida) :
|
|
libxl_bitmap_set(&exclude_cpumap, cpuida);
|
|
cpuida++;
|
|
}
|
|
}
|
|
|
|
/* Clear all the cpus from the removal list */
|
|
libxl_for_each_set_bit(i, exclude_cpumap) {
|
|
libxl_bitmap_reset(cpumap, i);
|
|
}
|
|
|
|
vcpp_out:
|
|
libxl_bitmap_dispose(&exclude_cpumap);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void parse_config_data(const char *config_source,
|
|
const char *config_data,
|
|
int config_len,
|
|
libxl_domain_config *d_config,
|
|
struct domain_create *dom_info)
|
|
|
|
{
|
|
const char *buf;
|
|
long l;
|
|
XLU_Config *config;
|
|
XLU_ConfigList *cpus, *vbds, *nics, *pcis, *cvfbs, *cpuids, *vtpms;
|
|
XLU_ConfigList *ioports, *irqs, *iomem;
|
|
int num_ioports, num_irqs, num_iomem;
|
|
int pci_power_mgmt = 0;
|
|
int pci_msitranslate = 0;
|
|
int pci_permissive = 0;
|
|
int i, 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, config_source);
|
|
if (!config) {
|
|
fprintf(stderr, "Failed to allocate for configuration\n");
|
|
exit(1);
|
|
}
|
|
|
|
e= xlu_cfg_readdata(config, config_data, config_len);
|
|
if (e) {
|
|
fprintf(stderr, "Failed to parse config: %s\n", strerror(e));
|
|
exit(1);
|
|
}
|
|
|
|
if (!xlu_cfg_get_string (config, "init_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: init_seclabel not supported\n");
|
|
} else {
|
|
fprintf(stderr, "Invalid init_seclabel: %s\n", buf);
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_string (config, "seclabel", &buf, 0)) {
|
|
uint32_t ssidref;
|
|
e = libxl_flask_context_to_sid(ctx, (char *)buf, strlen(buf),
|
|
&ssidref);
|
|
if (e) {
|
|
if (errno == ENOSYS) {
|
|
fprintf(stderr, "XSM Disabled: seclabel not supported\n");
|
|
} else {
|
|
fprintf(stderr, "Invalid seclabel: %s\n", buf);
|
|
exit(1);
|
|
}
|
|
} else if (c_info->ssidref) {
|
|
b_info->exec_ssidref = ssidref;
|
|
} else {
|
|
c_info->ssidref = ssidref;
|
|
}
|
|
}
|
|
|
|
libxl_defbool_set(&c_info->run_hotplug_scripts, run_hotplug_scripts);
|
|
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;
|
|
|
|
xlu_cfg_get_defbool(config, "pvh", &c_info->pvh, 0);
|
|
xlu_cfg_get_defbool(config, "hap", &c_info->hap, 0);
|
|
|
|
if (xlu_cfg_replace_string (config, "name", &c_info->name, 0)) {
|
|
fprintf(stderr, "Domain name must be specified.\n");
|
|
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);
|
|
}
|
|
|
|
xlu_cfg_get_defbool(config, "oos", &c_info->oos, 0);
|
|
|
|
if (!xlu_cfg_get_string (config, "pool", &buf, 0)) {
|
|
c_info->poolid = -1;
|
|
cpupool_qualifier_to_cpupoolid(buf, &c_info->poolid, NULL);
|
|
}
|
|
if (!libxl_cpupoolid_is_valid(ctx, c_info->poolid)) {
|
|
fprintf(stderr, "Illegal pool specified\n");
|
|
exit(1);
|
|
}
|
|
|
|
libxl_domain_build_info_init_type(b_info, c_info->type);
|
|
if (blkdev_start)
|
|
b_info->blkdev_start = strdup(blkdev_start);
|
|
|
|
/* the following is the actual config parsing with overriding
|
|
* values in the structures */
|
|
if (!xlu_cfg_get_long (config, "cpu_weight", &l, 0))
|
|
b_info->sched_params.weight = l;
|
|
if (!xlu_cfg_get_long (config, "cap", &l, 0))
|
|
b_info->sched_params.cap = l;
|
|
if (!xlu_cfg_get_long (config, "period", &l, 0))
|
|
b_info->sched_params.period = l;
|
|
if (!xlu_cfg_get_long (config, "slice", &l, 0))
|
|
b_info->sched_params.slice = l;
|
|
if (!xlu_cfg_get_long (config, "latency", &l, 0))
|
|
b_info->sched_params.latency = l;
|
|
if (!xlu_cfg_get_long (config, "extratime", &l, 0))
|
|
b_info->sched_params.extratime = l;
|
|
|
|
if (!xlu_cfg_get_long (config, "vcpus", &l, 0)) {
|
|
b_info->max_vcpus = l;
|
|
|
|
if (libxl_cpu_bitmap_alloc(ctx, &b_info->avail_vcpus, l)) {
|
|
fprintf(stderr, "Unable to allocate cpumap\n");
|
|
exit(1);
|
|
}
|
|
libxl_bitmap_set_none(&b_info->avail_vcpus);
|
|
while (l-- > 0)
|
|
libxl_bitmap_set((&b_info->avail_vcpus), l);
|
|
}
|
|
|
|
if (!xlu_cfg_get_long (config, "maxvcpus", &l, 0))
|
|
b_info->max_vcpus = l;
|
|
|
|
if (!xlu_cfg_get_list (config, "cpus", &cpus, 0, 1)) {
|
|
int n_cpus = 0;
|
|
|
|
if (libxl_cpu_bitmap_alloc(ctx, &b_info->cpumap, 0)) {
|
|
fprintf(stderr, "Unable to allocate cpumap\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* Prepare the array for single vcpu to pcpu mappings */
|
|
vcpu_to_pcpu = xmalloc(sizeof(int) * b_info->max_vcpus);
|
|
memset(vcpu_to_pcpu, -1, sizeof(int) * b_info->max_vcpus);
|
|
|
|
/*
|
|
* Idea here is to let libxl think all the domain's vcpus
|
|
* have cpu affinity with all the pcpus on the list.
|
|
* It is then us, here in xl, that matches each single vcpu
|
|
* to its pcpu (and that's why we need to stash such info in
|
|
* the vcpu_to_pcpu array now) after the domain has been created.
|
|
* Doing it like this saves the burden of passing to libxl
|
|
* some big array hosting the single mappings. Also, using
|
|
* the cpumap derived from the list ensures memory is being
|
|
* allocated on the proper nodes anyway.
|
|
*/
|
|
libxl_bitmap_set_none(&b_info->cpumap);
|
|
while ((buf = xlu_cfg_get_listitem(cpus, n_cpus)) != NULL) {
|
|
i = atoi(buf);
|
|
if (!libxl_bitmap_cpu_valid(&b_info->cpumap, i)) {
|
|
fprintf(stderr, "cpu %d illegal\n", i);
|
|
exit(1);
|
|
}
|
|
libxl_bitmap_set(&b_info->cpumap, i);
|
|
if (n_cpus < b_info->max_vcpus)
|
|
vcpu_to_pcpu[n_cpus] = i;
|
|
n_cpus++;
|
|
}
|
|
|
|
/* We have a cpumap, disable automatic placement */
|
|
libxl_defbool_set(&b_info->numa_placement, false);
|
|
}
|
|
else if (!xlu_cfg_get_string (config, "cpus", &buf, 0)) {
|
|
char *buf2 = strdup(buf);
|
|
|
|
if (libxl_cpu_bitmap_alloc(ctx, &b_info->cpumap, 0)) {
|
|
fprintf(stderr, "Unable to allocate cpumap\n");
|
|
exit(1);
|
|
}
|
|
|
|
libxl_bitmap_set_none(&b_info->cpumap);
|
|
if (vcpupin_parse(buf2, &b_info->cpumap))
|
|
exit(1);
|
|
free(buf2);
|
|
|
|
libxl_defbool_set(&b_info->numa_placement, false);
|
|
}
|
|
|
|
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;
|
|
|
|
libxl_defbool_set(&b_info->claim_mode, claim_mode);
|
|
|
|
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);
|
|
|
|
xlu_cfg_get_defbool(config, "nomigrate", &b_info->disable_migrate, 0);
|
|
|
|
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, "rtc_timeoffset", &l, 0))
|
|
b_info->rtc_timeoffset = l;
|
|
|
|
if (dom_info && !xlu_cfg_get_long(config, "vncviewer", &l, 0)) {
|
|
/* Command line arguments must take precedence over what's
|
|
* specified in the configuration file. */
|
|
if (!dom_info->vnc)
|
|
dom_info->vnc = l;
|
|
}
|
|
|
|
xlu_cfg_get_defbool(config, "localtime", &b_info->localtime, 0);
|
|
|
|
if (!xlu_cfg_get_long (config, "videoram", &l, 0))
|
|
b_info->video_memkb = l * 1024;
|
|
|
|
if (!xlu_cfg_get_long(config, "max_event_channels", &l, 0))
|
|
b_info->event_channels = l;
|
|
|
|
xlu_cfg_get_defbool(config, "driver_domain", &c_info->driver_domain, 0);
|
|
|
|
switch(b_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_string(config, "bios", &buf, 0) &&
|
|
libxl_bios_type_from_string(buf, &b_info->u.hvm.bios)) {
|
|
fprintf(stderr, "ERROR: invalid value \"%s\" for \"bios\"\n",
|
|
buf);
|
|
exit (1);
|
|
}
|
|
|
|
xlu_cfg_get_defbool(config, "pae", &b_info->u.hvm.pae, 0);
|
|
xlu_cfg_get_defbool(config, "apic", &b_info->u.hvm.apic, 0);
|
|
xlu_cfg_get_defbool(config, "acpi", &b_info->u.hvm.acpi, 0);
|
|
xlu_cfg_get_defbool(config, "acpi_s3", &b_info->u.hvm.acpi_s3, 0);
|
|
xlu_cfg_get_defbool(config, "acpi_s4", &b_info->u.hvm.acpi_s4, 0);
|
|
xlu_cfg_get_defbool(config, "nx", &b_info->u.hvm.nx, 0);
|
|
xlu_cfg_get_defbool(config, "viridian", &b_info->u.hvm.viridian, 0);
|
|
xlu_cfg_get_defbool(config, "hpet", &b_info->u.hvm.hpet, 0);
|
|
xlu_cfg_get_defbool(config, "vpt_align", &b_info->u.hvm.vpt_align, 0);
|
|
|
|
if (!xlu_cfg_get_long(config, "timer_mode", &l, 1)) {
|
|
const char *s = libxl_timer_mode_to_string(l);
|
|
fprintf(stderr, "WARNING: specifying \"timer_mode\" as an integer is deprecated. "
|
|
"Please use the named parameter variant. %s%s%s\n",
|
|
s ? "e.g. timer_mode=\"" : "",
|
|
s ? s : "",
|
|
s ? "\"" : "");
|
|
|
|
if (l < LIBXL_TIMER_MODE_DELAY_FOR_MISSED_TICKS ||
|
|
l > LIBXL_TIMER_MODE_ONE_MISSED_TICK_PENDING) {
|
|
fprintf(stderr, "ERROR: invalid value %ld for \"timer_mode\"\n", l);
|
|
exit (1);
|
|
}
|
|
b_info->u.hvm.timer_mode = l;
|
|
} else if (!xlu_cfg_get_string(config, "timer_mode", &buf, 0)) {
|
|
if (libxl_timer_mode_from_string(buf, &b_info->u.hvm.timer_mode)) {
|
|
fprintf(stderr, "ERROR: invalid value \"%s\" for \"timer_mode\"\n",
|
|
buf);
|
|
exit (1);
|
|
}
|
|
}
|
|
|
|
xlu_cfg_get_defbool(config, "nestedhvm", &b_info->u.hvm.nested_hvm, 0);
|
|
|
|
xlu_cfg_replace_string(config, "smbios_firmware",
|
|
&b_info->u.hvm.smbios_firmware, 0);
|
|
xlu_cfg_replace_string(config, "acpi_firmware",
|
|
&b_info->u.hvm.acpi_firmware, 0);
|
|
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, 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) {
|
|
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, 0);
|
|
break;
|
|
}
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
if (!xlu_cfg_get_list(config, "ioports", &ioports, &num_ioports, 0)) {
|
|
b_info->num_ioports = num_ioports;
|
|
b_info->ioports = calloc(num_ioports, sizeof(*b_info->ioports));
|
|
if (b_info->ioports == NULL) {
|
|
fprintf(stderr, "unable to allocate memory for ioports\n");
|
|
exit(-1);
|
|
}
|
|
|
|
for (i = 0; i < num_ioports; i++) {
|
|
const char *buf2;
|
|
char *ep;
|
|
uint32_t start, end;
|
|
unsigned long ul;
|
|
|
|
buf = xlu_cfg_get_listitem (ioports, i);
|
|
if (!buf) {
|
|
fprintf(stderr,
|
|
"xl: Unable to get element #%d in ioport list\n", i);
|
|
exit(1);
|
|
}
|
|
ul = strtoul(buf, &ep, 16);
|
|
if (ep == buf) {
|
|
fprintf(stderr, "xl: Invalid argument parsing ioport: %s\n",
|
|
buf);
|
|
exit(1);
|
|
}
|
|
if (ul >= UINT32_MAX) {
|
|
fprintf(stderr, "xl: ioport %lx too big\n", ul);
|
|
exit(1);
|
|
}
|
|
start = end = ul;
|
|
|
|
if (*ep == '-') {
|
|
buf2 = ep + 1;
|
|
ul = strtoul(buf2, &ep, 16);
|
|
if (ep == buf2 || *ep != '\0' || start > end) {
|
|
fprintf(stderr,
|
|
"xl: Invalid argument parsing ioport: %s\n", buf);
|
|
exit(1);
|
|
}
|
|
if (ul >= UINT32_MAX) {
|
|
fprintf(stderr, "xl: ioport %lx too big\n", ul);
|
|
exit(1);
|
|
}
|
|
end = ul;
|
|
} else if ( *ep != '\0' )
|
|
fprintf(stderr,
|
|
"xl: Invalid argument parsing ioport: %s\n", buf);
|
|
b_info->ioports[i].first = start;
|
|
b_info->ioports[i].number = end - start + 1;
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_list(config, "irqs", &irqs, &num_irqs, 0)) {
|
|
b_info->num_irqs = num_irqs;
|
|
b_info->irqs = calloc(num_irqs, sizeof(*b_info->irqs));
|
|
if (b_info->irqs == NULL) {
|
|
fprintf(stderr, "unable to allocate memory for ioports\n");
|
|
exit(-1);
|
|
}
|
|
for (i = 0; i < num_irqs; i++) {
|
|
char *ep;
|
|
unsigned long ul;
|
|
buf = xlu_cfg_get_listitem (irqs, i);
|
|
if (!buf) {
|
|
fprintf(stderr,
|
|
"xl: Unable to get element %d in irq list\n", i);
|
|
exit(1);
|
|
}
|
|
ul = strtoul(buf, &ep, 10);
|
|
if (ep == buf) {
|
|
fprintf(stderr,
|
|
"xl: Invalid argument parsing irq: %s\n", buf);
|
|
exit(1);
|
|
}
|
|
if (ul >= UINT32_MAX) {
|
|
fprintf(stderr, "xl: irq %lx too big\n", ul);
|
|
exit(1);
|
|
}
|
|
b_info->irqs[i] = ul;
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_list(config, "iomem", &iomem, &num_iomem, 0)) {
|
|
b_info->num_iomem = num_iomem;
|
|
b_info->iomem = calloc(num_iomem, sizeof(*b_info->iomem));
|
|
if (b_info->iomem == NULL) {
|
|
fprintf(stderr, "unable to allocate memory for iomem\n");
|
|
exit(-1);
|
|
}
|
|
for (i = 0; i < num_iomem; i++) {
|
|
buf = xlu_cfg_get_listitem (iomem, i);
|
|
if (!buf) {
|
|
fprintf(stderr,
|
|
"xl: Unable to get element %d in iomem list\n", i);
|
|
exit(1);
|
|
}
|
|
if(sscanf(buf, "%" SCNx64",%" SCNx64,
|
|
&b_info->iomem[i].start,
|
|
&b_info->iomem[i].number)
|
|
!= 2) {
|
|
fprintf(stderr,
|
|
"xl: Invalid argument parsing iomem: %s\n", buf);
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
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, "vtpm", &vtpms, 0, 0)) {
|
|
d_config->num_vtpms = 0;
|
|
d_config->vtpms = NULL;
|
|
while ((buf = xlu_cfg_get_listitem (vtpms, d_config->num_vtpms)) != NULL) {
|
|
libxl_device_vtpm *vtpm;
|
|
char * buf2 = strdup(buf);
|
|
char *p, *p2;
|
|
bool got_backend = false;
|
|
|
|
d_config->vtpms = (libxl_device_vtpm *) realloc(d_config->vtpms,
|
|
sizeof(libxl_device_vtpm) * (d_config->num_vtpms+1));
|
|
vtpm = d_config->vtpms + d_config->num_vtpms;
|
|
libxl_device_vtpm_init(vtpm);
|
|
vtpm->devid = d_config->num_vtpms;
|
|
|
|
p = strtok(buf2, ",");
|
|
if(p) {
|
|
do {
|
|
while(*p == ' ')
|
|
++p;
|
|
if ((p2 = strchr(p, '=')) == NULL)
|
|
break;
|
|
*p2 = '\0';
|
|
if (!strcmp(p, "backend")) {
|
|
vtpm->backend_domname = strdup(p2 + 1);
|
|
got_backend = true;
|
|
} else if(!strcmp(p, "uuid")) {
|
|
if( libxl_uuid_from_string(&vtpm->uuid, p2 + 1) ) {
|
|
fprintf(stderr,
|
|
"Failed to parse vtpm UUID: %s\n", p2 + 1);
|
|
exit(1);
|
|
}
|
|
} else {
|
|
fprintf(stderr, "Unknown string `%s' in vtpm spec\n", p);
|
|
exit(1);
|
|
}
|
|
} while ((p = strtok(NULL, ",")) != NULL);
|
|
}
|
|
if(!got_backend) {
|
|
fprintf(stderr, "vtpm spec missing required backend field!\n");
|
|
exit(1);
|
|
}
|
|
free(buf2);
|
|
d_config->num_vtpms++;
|
|
}
|
|
}
|
|
|
|
if (!xlu_cfg_get_list (config, "vif", &nics, 0, 0)) {
|
|
d_config->num_nics = 0;
|
|
d_config->nics = NULL;
|
|
while ((buf = xlu_cfg_get_listitem (nics, d_config->num_nics)) != NULL) {
|
|
libxl_device_nic *nic;
|
|
char *buf2 = strdup(buf);
|
|
char *p, *p2;
|
|
|
|
d_config->nics = (libxl_device_nic *) realloc(d_config->nics, sizeof (libxl_device_nic) * (d_config->num_nics+1));
|
|
nic = d_config->nics + d_config->num_nics;
|
|
libxl_device_nic_init(nic);
|
|
nic->devid = d_config->num_nics;
|
|
|
|
if (default_vifscript) {
|
|
free(nic->script);
|
|
nic->script = strdup(default_vifscript);
|
|
}
|
|
|
|
if (default_bridge) {
|
|
free(nic->bridge);
|
|
nic->bridge = strdup(default_bridge);
|
|
}
|
|
|
|
if (default_gatewaydev) {
|
|
free(nic->gatewaydev);
|
|
nic->gatewaydev = strdup(default_gatewaydev);
|
|
}
|
|
|
|
if (default_vifbackend) {
|
|
free(nic->backend_domname);
|
|
nic->backend_domname = strdup(default_vifbackend);
|
|
}
|
|
|
|
p = strtok(buf2, ",");
|
|
if (!p)
|
|
goto skip_nic;
|
|
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_VIF_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")) {
|
|
free(nic->backend_domname);
|
|
nic->backend_domname = strdup(p2 + 1);
|
|
} else if (!strcmp(p, "rate")) {
|
|
parse_vif_rate(&config, (p2 + 1), nic);
|
|
} else if (!strcmp(p, "accel")) {
|
|
fprintf(stderr, "the accel parameter for vifs is currently not supported\n");
|
|
} else if (!strcmp(p, "netdev")) {
|
|
fprintf(stderr, "the netdev parameter is deprecated, "
|
|
"please use gatewaydev instead\n");
|
|
free(nic->gatewaydev);
|
|
nic->gatewaydev = strdup(p2 + 1);
|
|
} else if (!strcmp(p, "gatewaydev")) {
|
|
free(nic->gatewaydev);
|
|
nic->gatewaydev = strdup(p2 + 1);
|
|
}
|
|
} while ((p = strtok(NULL, ",")) != NULL);
|
|
skip_nic:
|
|
free(buf2);
|
|
d_config->num_nics++;
|
|
}
|
|
}
|
|
|
|
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(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(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")) {
|
|
libxl_defbool_set(&vfb->vnc.enable, atoi(p2 + 1));
|
|
} else if (!strcmp(p, "vnclisten")) {
|
|
free(vfb->vnc.listen);
|
|
vfb->vnc.listen = strdup(p2 + 1);
|
|
} else if (!strcmp(p, "vncpasswd")) {
|
|
free(vfb->vnc.passwd);
|
|
vfb->vnc.passwd = strdup(p2 + 1);
|
|
} else if (!strcmp(p, "vncdisplay")) {
|
|
vfb->vnc.display = atoi(p2 + 1);
|
|
} else if (!strcmp(p, "vncunused")) {
|
|
libxl_defbool_set(&vfb->vnc.findunused, atoi(p2 + 1));
|
|
} else if (!strcmp(p, "keymap")) {
|
|
free(vfb->keymap);
|
|
vfb->keymap = strdup(p2 + 1);
|
|
} else if (!strcmp(p, "sdl")) {
|
|
libxl_defbool_set(&vfb->sdl.enable, atoi(p2 + 1));
|
|
} else if (!strcmp(p, "opengl")) {
|
|
libxl_defbool_set(&vfb->sdl.opengl, atoi(p2 + 1));
|
|
} else if (!strcmp(p, "display")) {
|
|
free(vfb->sdl.display);
|
|
vfb->sdl.display = strdup(p2 + 1);
|
|
} else if (!strcmp(p, "xauthority")) {
|
|
free(vfb->sdl.xauthority);
|
|
vfb->sdl.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;
|
|
|
|
if (!xlu_cfg_get_long (config, "pci_permissive", &l, 0))
|
|
pci_permissive = l;
|
|
|
|
/* To be reworked (automatically enabled) once the auto ballooning
|
|
* after guest starts is done (with PCI devices passed in). */
|
|
if (c_info->type == LIBXL_DOMAIN_TYPE_PV) {
|
|
xlu_cfg_get_defbool(config, "e820_host", &b_info->u.pv.e820_host, 0);
|
|
}
|
|
|
|
if (!xlu_cfg_get_list (config, "pci", &pcis, 0, 0)) {
|
|
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;
|
|
libxl_device_pci_init(pcidev);
|
|
|
|
pcidev->msitranslate = pci_msitranslate;
|
|
pcidev->power_mgmt = pci_power_mgmt;
|
|
pcidev->permissive = pci_permissive;
|
|
if (!xlu_pci_parse_bdf(config, pcidev, buf))
|
|
d_config->num_pcidevs++;
|
|
}
|
|
if (d_config->num_pcidevs && c_info->type == LIBXL_DOMAIN_TYPE_PV)
|
|
libxl_defbool_set(&b_info->u.pv.e820_host, true);
|
|
}
|
|
|
|
switch (xlu_cfg_get_list(config, "cpuid", &cpuids, 0, 1)) {
|
|
case 0:
|
|
{
|
|
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;
|
|
}
|
|
|
|
/* 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",
|
|
&b_info->device_model, 0);
|
|
if (!xlu_cfg_get_string (config, "device_model_version", &buf, 0)) {
|
|
if (!strcmp(buf, "qemu-xen-traditional")) {
|
|
b_info->device_model_version
|
|
= LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
|
|
} else if (!strcmp(buf, "qemu-xen")) {
|
|
b_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 (b_info->device_model)
|
|
fprintf(stderr, "WARNING: device model override given without specific DM version\n");
|
|
xlu_cfg_get_defbool (config, "device_model_stubdomain_override",
|
|
&b_info->device_model_stubdomain, 0);
|
|
|
|
if (!xlu_cfg_get_string (config, "device_model_stubdomain_seclabel",
|
|
&buf, 0)) {
|
|
e = libxl_flask_context_to_sid(ctx, (char *)buf, strlen(buf),
|
|
&b_info->device_model_ssidref);
|
|
if (e) {
|
|
if (errno == ENOSYS) {
|
|
fprintf(stderr, "XSM Disabled:"
|
|
" device_model_stubdomain_seclabel not supported\n");
|
|
} else {
|
|
fprintf(stderr, "Invalid device_model_stubdomain_seclabel:"
|
|
" %s\n", buf);
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
#define parse_extra_args(type) \
|
|
e = xlu_cfg_get_list_as_string_list(config, "device_model_args"#type, \
|
|
&b_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_string (config, "vga", &buf, 0)) {
|
|
if (!strcmp(buf, "stdvga")) {
|
|
b_info->u.hvm.vga.kind = LIBXL_VGA_INTERFACE_TYPE_STD;
|
|
} else if (!strcmp(buf, "cirrus")) {
|
|
b_info->u.hvm.vga.kind = LIBXL_VGA_INTERFACE_TYPE_CIRRUS;
|
|
} else {
|
|
fprintf(stderr, "Unknown vga \"%s\" specified\n", buf);
|
|
exit(1);
|
|
}
|
|
} else if (!xlu_cfg_get_long(config, "stdvga", &l, 0))
|
|
b_info->u.hvm.vga.kind = l ? LIBXL_VGA_INTERFACE_TYPE_STD :
|
|
LIBXL_VGA_INTERFACE_TYPE_CIRRUS;
|
|
|
|
xlu_cfg_get_defbool(config, "vnc", &b_info->u.hvm.vnc.enable, 0);
|
|
xlu_cfg_replace_string (config, "vnclisten",
|
|
&b_info->u.hvm.vnc.listen, 0);
|
|
xlu_cfg_replace_string (config, "vncpasswd",
|
|
&b_info->u.hvm.vnc.passwd, 0);
|
|
if (!xlu_cfg_get_long (config, "vncdisplay", &l, 0))
|
|
b_info->u.hvm.vnc.display = l;
|
|
xlu_cfg_get_defbool(config, "vncunused",
|
|
&b_info->u.hvm.vnc.findunused, 0);
|
|
xlu_cfg_replace_string (config, "keymap", &b_info->u.hvm.keymap, 0);
|
|
xlu_cfg_get_defbool(config, "sdl", &b_info->u.hvm.sdl.enable, 0);
|
|
xlu_cfg_get_defbool(config, "opengl", &b_info->u.hvm.sdl.opengl, 0);
|
|
xlu_cfg_get_defbool (config, "spice", &b_info->u.hvm.spice.enable, 0);
|
|
if (!xlu_cfg_get_long (config, "spiceport", &l, 0))
|
|
b_info->u.hvm.spice.port = l;
|
|
if (!xlu_cfg_get_long (config, "spicetls_port", &l, 0))
|
|
b_info->u.hvm.spice.tls_port = l;
|
|
xlu_cfg_replace_string (config, "spicehost",
|
|
&b_info->u.hvm.spice.host, 0);
|
|
xlu_cfg_get_defbool(config, "spicedisable_ticketing",
|
|
&b_info->u.hvm.spice.disable_ticketing, 0);
|
|
xlu_cfg_replace_string (config, "spicepasswd",
|
|
&b_info->u.hvm.spice.passwd, 0);
|
|
xlu_cfg_get_defbool(config, "spiceagent_mouse",
|
|
&b_info->u.hvm.spice.agent_mouse, 0);
|
|
xlu_cfg_get_defbool(config, "spicevdagent",
|
|
&b_info->u.hvm.spice.vdagent, 0);
|
|
xlu_cfg_get_defbool(config, "spice_clipboard_sharing",
|
|
&b_info->u.hvm.spice.clipboard_sharing, 0);
|
|
xlu_cfg_get_defbool(config, "nographic", &b_info->u.hvm.nographic, 0);
|
|
xlu_cfg_get_defbool(config, "gfx_passthru",
|
|
&b_info->u.hvm.gfx_passthru, 0);
|
|
xlu_cfg_replace_string (config, "serial", &b_info->u.hvm.serial, 0);
|
|
xlu_cfg_replace_string (config, "boot", &b_info->u.hvm.boot, 0);
|
|
xlu_cfg_get_defbool(config, "usb", &b_info->u.hvm.usb, 0);
|
|
switch (xlu_cfg_get_list_as_string_list(config, "usbdevice",
|
|
&b_info->u.hvm.usbdevice_list,
|
|
1))
|
|
{
|
|
|
|
case 0: break; /* Success */
|
|
case ESRCH: break; /* Option not present */
|
|
case EINVAL:
|
|
/* If it's not a valid list, try reading it as an atom,
|
|
* falling through to an error if it fails */
|
|
if (!xlu_cfg_replace_string(config, "usbdevice",
|
|
&b_info->u.hvm.usbdevice, 0))
|
|
break;
|
|
/* FALLTHRU */
|
|
default:
|
|
fprintf(stderr,"xl: Unable to parse usbdevice.\n");
|
|
exit(-ERROR_FAIL);
|
|
}
|
|
xlu_cfg_replace_string (config, "soundhw", &b_info->u.hvm.soundhw, 0);
|
|
xlu_cfg_get_defbool(config, "xen_platform_pci",
|
|
&b_info->u.hvm.xen_platform_pci, 0);
|
|
|
|
if(b_info->u.hvm.vnc.listen
|
|
&& b_info->u.hvm.vnc.display
|
|
&& strchr(b_info->u.hvm.vnc.listen, ':') != NULL) {
|
|
fprintf(stderr,
|
|
"ERROR: Display specified both in vnclisten"
|
|
" and vncdisplay!\n");
|
|
exit (1);
|
|
|
|
}
|
|
|
|
if (!xlu_cfg_get_string (config, "vendor_device", &buf, 0)) {
|
|
libxl_vendor_device d;
|
|
|
|
e = libxl_vendor_device_from_string(buf, &d);
|
|
if (e) {
|
|
fprintf(stderr,
|
|
"xl: unknown vendor_device '%s'\n",
|
|
buf);
|
|
exit(-ERROR_FAIL);
|
|
}
|
|
|
|
b_info->u.hvm.vendor_device = d;
|
|
}
|
|
}
|
|
|
|
xlu_cfg_destroy(config);
|
|
}
|
|
|
|
static void reload_domain_config(uint32_t domid,
|
|
uint8_t **config_data, int *config_len)
|
|
{
|
|
uint8_t *t_data;
|
|
int ret, t_len;
|
|
|
|
ret = libxl_userdata_retrieve(ctx, domid, "xl", &t_data, &t_len);
|
|
if (ret) {
|
|
LOG("failed to retrieve guest configuration (rc=%d). "
|
|
"reusing old configuration", ret);
|
|
return;
|
|
}
|
|
|
|
free(*config_data);
|
|
*config_data = t_data;
|
|
*config_len = t_len;
|
|
}
|
|
|
|
/* Returns 1 if domain should be restarted,
|
|
* 2 if domain should be renamed then restarted, or 0
|
|
* Can update r_domid if domain is destroyed etc */
|
|
static int handle_domain_death(uint32_t *r_domid,
|
|
libxl_event *event,
|
|
uint8_t **config_data, int *config_len,
|
|
libxl_domain_config *d_config)
|
|
|
|
{
|
|
int restart = 0;
|
|
libxl_action_on_shutdown action;
|
|
|
|
switch (event->u.domain_shutdown.shutdown_reason) {
|
|
case LIBXL_SHUTDOWN_REASON_POWEROFF:
|
|
action = d_config->on_poweroff;
|
|
break;
|
|
case LIBXL_SHUTDOWN_REASON_REBOOT:
|
|
action = d_config->on_reboot;
|
|
break;
|
|
case LIBXL_SHUTDOWN_REASON_SUSPEND:
|
|
LOG("Domain has suspended.");
|
|
return 0;
|
|
case LIBXL_SHUTDOWN_REASON_CRASH:
|
|
action = d_config->on_crash;
|
|
break;
|
|
case LIBXL_SHUTDOWN_REASON_WATCHDOG:
|
|
action = d_config->on_watchdog;
|
|
break;
|
|
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, *r_domid, corefile, NULL);
|
|
if (rc) LOG("core dump failed (rc=%d).", rc);
|
|
free(corefile);
|
|
}
|
|
/* No point crying over spilled milk, continue on failure. */
|
|
|
|
if (action == LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_DESTROY)
|
|
action = LIBXL_ACTION_ON_SHUTDOWN_DESTROY;
|
|
else
|
|
action = LIBXL_ACTION_ON_SHUTDOWN_RESTART;
|
|
}
|
|
|
|
switch (action) {
|
|
case LIBXL_ACTION_ON_SHUTDOWN_PRESERVE:
|
|
break;
|
|
|
|
case LIBXL_ACTION_ON_SHUTDOWN_RESTART_RENAME:
|
|
reload_domain_config(*r_domid, config_data, config_len);
|
|
restart = 2;
|
|
break;
|
|
|
|
case LIBXL_ACTION_ON_SHUTDOWN_RESTART:
|
|
reload_domain_config(*r_domid, config_data, config_len);
|
|
|
|
restart = 1;
|
|
/* fall-through */
|
|
case LIBXL_ACTION_ON_SHUTDOWN_DESTROY:
|
|
LOG("Domain %d needs to be cleaned up: destroying the domain",
|
|
*r_domid);
|
|
libxl_domain_destroy(ctx, *r_domid, 0);
|
|
*r_domid = INVALID_DOMID;
|
|
break;
|
|
|
|
case LIBXL_ACTION_ON_SHUTDOWN_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);
|
|
}
|
|
|
|
|
|
/* Preserve a copy of a domain under a new name. Updates *r_domid */
|
|
static int preserve_domain(uint32_t *r_domid, libxl_event *event,
|
|
libxl_domain_config *d_config)
|
|
{
|
|
time_t now;
|
|
struct tm tm;
|
|
char strtime[24];
|
|
|
|
libxl_uuid new_uuid;
|
|
|
|
int rc;
|
|
|
|
now = time(NULL);
|
|
if (now == ((time_t) -1)) {
|
|
LOG("Failed to get current time for domain rename");
|
|
return 0;
|
|
}
|
|
|
|
tzset();
|
|
if (gmtime_r(&now, &tm) == NULL) {
|
|
LOG("Failed to convert time to UTC");
|
|
return 0;
|
|
}
|
|
|
|
if (!strftime(&strtime[0], sizeof(strtime), "-%Y%m%dT%H%MZ", &tm)) {
|
|
LOG("Failed to format time as a string");
|
|
return 0;
|
|
}
|
|
|
|
libxl_uuid_generate(&new_uuid);
|
|
|
|
LOG("Preserving domain %d %s with suffix%s",
|
|
*r_domid, d_config->c_info.name, strtime);
|
|
rc = libxl_domain_preserve(ctx, *r_domid, &d_config->c_info,
|
|
strtime, new_uuid);
|
|
|
|
/*
|
|
* Although the domain still exists it is no longer the one we are
|
|
* concerned with.
|
|
*/
|
|
*r_domid = INVALID_DOMID;
|
|
|
|
return rc == 0 ? 1 : 0;
|
|
}
|
|
|
|
static int freemem(uint32_t domid, libxl_domain_build_info *b_info)
|
|
{
|
|
int rc, retries = 3;
|
|
uint32_t need_memkb, free_memkb;
|
|
|
|
if (!autoballoon)
|
|
return 0;
|
|
|
|
rc = libxl_domain_need_memory(ctx, b_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 void console_child_report(void)
|
|
{
|
|
if (xl_child_pid(child_console)) {
|
|
int status;
|
|
pid_t got = xl_waitpid(child_console, &status, 0);
|
|
if (got < 0)
|
|
perror("xl: warning, failed to waitpid for console child");
|
|
else if (status)
|
|
libxl_report_child_exitstatus(ctx, XTL_ERROR, "console child",
|
|
xl_child_pid(child_console), status);
|
|
}
|
|
}
|
|
|
|
static void autoconnect_console(libxl_ctx *ctx_ignored,
|
|
libxl_event *ev, void *priv)
|
|
{
|
|
uint32_t bldomid = ev->domid;
|
|
|
|
libxl_event_free(ctx, ev);
|
|
|
|
console_child_report();
|
|
|
|
pid_t pid = xl_fork(child_console);
|
|
if (pid < 0) {
|
|
perror("unable to fork xenconsole");
|
|
return;
|
|
} else if (pid > 0)
|
|
return;
|
|
|
|
postfork();
|
|
|
|
sleep(1);
|
|
libxl_primary_console_exec(ctx, bldomid);
|
|
/* Do not return. xl continued in child process */
|
|
fprintf(stderr, "Unable to attach console\n");
|
|
_exit(1);
|
|
}
|
|
|
|
static int domain_wait_event(uint32_t domid, libxl_event **event_r)
|
|
{
|
|
int ret;
|
|
for (;;) {
|
|
ret = libxl_event_wait(ctx, event_r, LIBXL_EVENTMASK_ALL, 0,0);
|
|
if (ret) {
|
|
LOG("Domain %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 uint32_t create_domain(struct domain_create *dom_info)
|
|
{
|
|
uint32_t domid = INVALID_DOMID;
|
|
|
|
libxl_domain_config d_config;
|
|
|
|
int debug = dom_info->debug;
|
|
int daemonize = dom_info->daemonize;
|
|
int monitor = dom_info->monitor;
|
|
int paused = dom_info->paused;
|
|
int vncautopass = dom_info->vncautopass;
|
|
const char *config_file = dom_info->config_file;
|
|
const char *extra_config = dom_info->extra_config;
|
|
const char *restore_file = dom_info->restore_file;
|
|
const char *config_source = NULL;
|
|
const char *restore_source = NULL;
|
|
int migrate_fd = dom_info->migrate_fd;
|
|
|
|
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;
|
|
const libxl_asyncprogress_how *autoconnect_console_how;
|
|
struct save_file_header hdr;
|
|
|
|
int restoring = (restore_file || (migrate_fd >= 0));
|
|
|
|
libxl_domain_config_init(&d_config);
|
|
|
|
if (restoring) {
|
|
uint8_t *optdata_begin = 0;
|
|
const uint8_t *optdata_here = 0;
|
|
union { uint32_t u32; char b[4]; } u32buf;
|
|
uint32_t badflags;
|
|
|
|
if (migrate_fd >= 0) {
|
|
restore_source = "<incoming migration stream>";
|
|
restore_fd = migrate_fd;
|
|
} else {
|
|
restore_source = restore_file;
|
|
restore_fd = open(restore_file, O_RDONLY);
|
|
if (restore_fd == -1) {
|
|
fprintf(stderr, "Can't open restore file: %s\n", strerror(errno));
|
|
return ERROR_INVAL;
|
|
}
|
|
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_source, "header") );
|
|
if (memcmp(hdr.magic, savefileheader_magic, sizeof(hdr.magic))) {
|
|
fprintf(stderr, "File has wrong magic number -"
|
|
" corrupt or for a different tool?\n");
|
|
return ERROR_INVAL;
|
|
}
|
|
if (hdr.byteorder != SAVEFILE_BYTEORDER_VALUE) {
|
|
fprintf(stderr, "File has wrong byte order\n");
|
|
return ERROR_INVAL;
|
|
}
|
|
fprintf(stderr, "Loading new save file %s"
|
|
" (new xl fmt info"
|
|
" 0x%"PRIx32"/0x%"PRIx32"/%"PRIu32")\n",
|
|
restore_source, hdr.mandatory_flags, hdr.optional_flags,
|
|
hdr.optional_data_len);
|
|
|
|
badflags = hdr.mandatory_flags & ~( 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_source, "optdata") );
|
|
}
|
|
|
|
#define OPTDATA_LEFT (hdr.optional_data_len - (optdata_here - optdata_begin))
|
|
#define WITH_OPTDATA(amt, body) \
|
|
if (OPTDATA_LEFT < (amt)) { \
|
|
fprintf(stderr, "Savefile truncated.\n"); \
|
|
return ERROR_INVAL; \
|
|
} else { \
|
|
body; \
|
|
optdata_here += (amt); \
|
|
}
|
|
|
|
optdata_here = optdata_begin;
|
|
|
|
if (OPTDATA_LEFT) {
|
|
fprintf(stderr, " Savefile contains xl domain config\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;
|
|
/* /dev/null represents special case (read config. from command line) */
|
|
if (!strcmp(config_file, "/dev/null")) {
|
|
config_len = 0;
|
|
} else {
|
|
ret = libxl_read_file_contents(ctx, config_file,
|
|
&config_data, &config_len);
|
|
if (ret) { fprintf(stderr, "Failed to read config file: %s: %s\n",
|
|
config_file, strerror(errno)); return ERROR_FAIL; }
|
|
}
|
|
if (!restoring && extra_config && strlen(extra_config)) {
|
|
if (config_len > INT_MAX - (strlen(extra_config) + 2 + 1)) {
|
|
fprintf(stderr, "Failed to attach extra 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);
|
|
}
|
|
config_source=config_file;
|
|
} else {
|
|
if (!config_data) {
|
|
fprintf(stderr, "Config file not specified and"
|
|
" none in save file\n");
|
|
return ERROR_INVAL;
|
|
}
|
|
config_source = "<saved>";
|
|
}
|
|
|
|
if (!dom_info->quiet)
|
|
printf("Parsing config from %s\n", config_source);
|
|
|
|
parse_config_data(config_source, config_data, config_len, &d_config, dom_info);
|
|
|
|
if (migrate_fd >= 0) {
|
|
if (d_config.c_info.name) {
|
|
/* when we receive a domain we get its name from the config
|
|
* file; and we receive it to a temporary name */
|
|
assert(!common_domname);
|
|
|
|
common_domname = d_config.c_info.name;
|
|
d_config.c_info.name = 0; /* steals allocation from config */
|
|
|
|
if (asprintf(&d_config.c_info.name,
|
|
"%s--incoming", common_domname) < 0) {
|
|
fprintf(stderr, "Failed to allocate memory in asprintf\n");
|
|
exit(1);
|
|
}
|
|
*dom_info->migration_domname_r = strdup(d_config.c_info.name);
|
|
}
|
|
}
|
|
|
|
if (debug || dom_info->dryrun)
|
|
printf_info(default_output_format, -1, &d_config);
|
|
|
|
ret = 0;
|
|
if (dom_info->dryrun)
|
|
goto out;
|
|
|
|
start:
|
|
assert(domid == INVALID_DOMID);
|
|
|
|
rc = acquire_lock();
|
|
if (rc < 0)
|
|
goto error_out;
|
|
|
|
ret = freemem(domid, &d_config.b_info);
|
|
if (ret < 0) {
|
|
fprintf(stderr, "failed to free memory for the domain\n");
|
|
ret = ERROR_FAIL;
|
|
goto error_out;
|
|
}
|
|
|
|
libxl_asyncprogress_how autoconnect_console_how_buf;
|
|
if ( dom_info->console_autoconnect ) {
|
|
autoconnect_console_how_buf.callback = autoconnect_console;
|
|
autoconnect_console_how = &autoconnect_console_how_buf;
|
|
}else{
|
|
autoconnect_console_how = 0;
|
|
}
|
|
|
|
if ( restoring ) {
|
|
libxl_domain_restore_params params;
|
|
params.checkpointed_stream = dom_info->checkpointed_stream;
|
|
ret = libxl_domain_create_restore(ctx, &d_config,
|
|
&domid, restore_fd,
|
|
¶ms,
|
|
0, autoconnect_console_how);
|
|
/*
|
|
* On subsequent reboot etc we should create the domain, not
|
|
* restore/migrate-receive it again.
|
|
*/
|
|
restoring = 0;
|
|
}else{
|
|
ret = libxl_domain_create_new(ctx, &d_config, &domid,
|
|
0, autoconnect_console_how);
|
|
}
|
|
if ( ret )
|
|
goto error_out;
|
|
|
|
/* If single vcpu to pcpu mapping was requested, honour it */
|
|
if (vcpu_to_pcpu) {
|
|
libxl_bitmap vcpu_cpumap;
|
|
|
|
ret = libxl_cpu_bitmap_alloc(ctx, &vcpu_cpumap, 0);
|
|
if (ret)
|
|
goto error_out;
|
|
for (i = 0; i < d_config.b_info.max_vcpus; i++) {
|
|
|
|
if (vcpu_to_pcpu[i] != -1) {
|
|
libxl_bitmap_set_none(&vcpu_cpumap);
|
|
libxl_bitmap_set(&vcpu_cpumap, vcpu_to_pcpu[i]);
|
|
} else {
|
|
libxl_bitmap_set_any(&vcpu_cpumap);
|
|
}
|
|
if (libxl_set_vcpuaffinity(ctx, domid, i, &vcpu_cpumap)) {
|
|
fprintf(stderr, "setting affinity failed on vcpu `%d'.\n", i);
|
|
libxl_bitmap_dispose(&vcpu_cpumap);
|
|
free(vcpu_to_pcpu);
|
|
ret = ERROR_FAIL;
|
|
goto error_out;
|
|
}
|
|
}
|
|
libxl_bitmap_dispose(&vcpu_cpumap);
|
|
free(vcpu_to_pcpu); vcpu_to_pcpu = NULL;
|
|
}
|
|
|
|
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 (dom_info->vnc)
|
|
autoconnect_vncviewer(domid, vncautopass);
|
|
|
|
if (need_daemon) {
|
|
char *name;
|
|
|
|
if (asprintf(&name, "xl-%s", d_config.c_info.name) < 0) {
|
|
LOG("Failed to allocate memory in asprintf");
|
|
exit(1);
|
|
}
|
|
ret = do_daemonize(name);
|
|
free(name);
|
|
if (ret) {
|
|
ret = (ret == 1) ? domid : ret;
|
|
goto out;
|
|
}
|
|
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(domid, &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(&domid, event,
|
|
(uint8_t **)&config_data, &config_len,
|
|
&d_config)) {
|
|
case 2:
|
|
if (!preserve_domain(&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);
|
|
}
|
|
|
|
/* Reparse the configuration in case it has changed */
|
|
libxl_domain_config_dispose(&d_config);
|
|
libxl_domain_config_init(&d_config);
|
|
parse_config_data(config_source, config_data, config_len,
|
|
&d_config, dom_info);
|
|
|
|
/*
|
|
* 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");
|
|
libxl_event_free(ctx, event);
|
|
ret = 0;
|
|
goto out;
|
|
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
case LIBXL_EVENT_TYPE_DOMAIN_DEATH:
|
|
LOG("Domain %d has been destroyed.", domid);
|
|
libxl_event_free(ctx, event);
|
|
ret = 0;
|
|
goto out;
|
|
|
|
case LIBXL_EVENT_TYPE_DISK_EJECT:
|
|
/* XXX what is this for? */
|
|
libxl_cdrom_insert(ctx, domid, &event->u.disk_eject.disk, NULL);
|
|
break;
|
|
|
|
default:;
|
|
char *evstr = libxl_event_to_json(ctx, event);
|
|
LOG("warning, got unexpected event type %d, event=%s",
|
|
event->type, evstr);
|
|
free(evstr);
|
|
}
|
|
|
|
libxl_event_free(ctx, event);
|
|
}
|
|
|
|
error_out:
|
|
release_lock();
|
|
if (libxl_domid_valid_guest(domid)) {
|
|
libxl_domain_destroy(ctx, domid, 0);
|
|
domid = INVALID_DOMID;
|
|
}
|
|
|
|
out:
|
|
if (logfile != 2)
|
|
close(logfile);
|
|
|
|
libxl_domain_config_dispose(&d_config);
|
|
|
|
free(config_data);
|
|
|
|
console_child_report();
|
|
|
|
if (deathw)
|
|
libxl_evdisable_domain_death(ctx, deathw);
|
|
if (diskws) {
|
|
evdisable_disk_ejects(diskws, d_config.num_disks);
|
|
free(diskws);
|
|
}
|
|
|
|
/*
|
|
* If we have daemonized then do not return to the caller -- this has
|
|
* already happened in the parent.
|
|
*/
|
|
if ( daemonize && !need_daemon )
|
|
exit(ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void help(const char *command)
|
|
{
|
|
int i;
|
|
struct cmd_spec *cmd;
|
|
|
|
if (!command || !strcmp(command, "help")) {
|
|
printf("Usage xl [-vfN] <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 %s\n\n%s.\n\n",
|
|
cmd->modifies ? "f" : "",
|
|
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;
|
|
}
|
|
|
|
#define COMMON_LONG_OPTS {"help", 0, 0, 'h'}
|
|
|
|
/*
|
|
* Callers should use SWITCH_FOREACH_OPT in preference to calling this
|
|
* directly.
|
|
*/
|
|
static int def_getopt(int argc, char * const argv[],
|
|
const char *optstring,
|
|
const struct option *longopts,
|
|
const char* helpstr, int reqargs)
|
|
{
|
|
int opt;
|
|
const struct option def_options[] = {
|
|
COMMON_LONG_OPTS,
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
if (!longopts)
|
|
longopts = def_options;
|
|
|
|
opterr = 0;
|
|
while ((opt = getopt_long(argc, argv, optstring, longopts, NULL)) == '?') {
|
|
if (optopt == 'h') {
|
|
help(helpstr);
|
|
exit(0);
|
|
}
|
|
fprintf(stderr, "option `%c' not supported.\n", optopt);
|
|
}
|
|
if (opt == 'h') {
|
|
help(helpstr);
|
|
exit(0);
|
|
}
|
|
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);
|
|
exit(2);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Wraps def_getopt into a convenient loop+switch to process all
|
|
* arguments. This macro is intended to be called from main_XXX().
|
|
*
|
|
* SWITCH_FOREACH_OPT(int *opt, const char *opts,
|
|
* const struct option *longopts,
|
|
* const char *commandname,
|
|
* int num_opts_req) { ...
|
|
*
|
|
* opt: pointer to an int variable, holds the current option
|
|
* during processing.
|
|
* opts: short options, as per getopt_long(3)'s optstring argument.
|
|
* longopts: long options, as per getopt_long(3)'s longopts argument.
|
|
* May be null.
|
|
* commandname: name of this command, for usage string.
|
|
* num_required_opts: number of non-option command line parameters
|
|
* which are required.
|
|
*
|
|
* In addition the calling context is expected to contain variables
|
|
* "argc" and "argv" in the conventional C-style:
|
|
* main(int argc, char **argv)
|
|
* manner.
|
|
*
|
|
* Callers should treat SWITCH_FOREACH_OPT as they would a switch
|
|
* statement over the value of `opt`. Each option given in `opts` (or
|
|
* `lopts`) should be handled by a case statement as if it were inside
|
|
* a switch statement.
|
|
*
|
|
* In addition to the options provided in opts the macro will handle
|
|
* the "help" option and enforce a minimum number of non-option
|
|
* command line pearameters as follows:
|
|
* -- if the user passes a -h or --help option. help will be printed,
|
|
* and the macro will cause the process to exit with code 0.
|
|
* -- if the user does not provided `num_required_opts` non-option
|
|
* arguments, the macro will cause the process to exit with code 2.
|
|
*
|
|
* Example:
|
|
*
|
|
* int main_foo(int argc, char **argv) {
|
|
* int opt;
|
|
*
|
|
* SWITCH_FOREACH_OPT(opt, "blah", NULL, "foo", 0) {
|
|
* case 'b':
|
|
* ... handle b option...
|
|
* break;
|
|
* case 'l':
|
|
* ... handle l option ...
|
|
* break;
|
|
* case etc etc...
|
|
* }
|
|
* ... do something useful with the options ...
|
|
* }
|
|
*/
|
|
#define SWITCH_FOREACH_OPT(opt, opts, longopts, \
|
|
commandname, num_required_opts) \
|
|
while (((opt) = def_getopt(argc, argv, (opts), (longopts), \
|
|
(commandname), (num_required_opts))) != -1) \
|
|
switch (opt)
|
|
|
|
static int set_memory_max(uint32_t domid, const char *mem)
|
|
{
|
|
int64_t memorykb;
|
|
int rc;
|
|
|
|
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)
|
|
{
|
|
uint32_t domid;
|
|
int opt = 0;
|
|
char *mem;
|
|
int rc;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "mem-max", 2) {
|
|
/* No options */
|
|
}
|
|
|
|
domid = find_domain(argv[optind]);
|
|
mem = argv[optind + 1];
|
|
|
|
rc = set_memory_max(domid, 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(uint32_t domid, const char *mem)
|
|
{
|
|
long long int memorykb;
|
|
|
|
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)
|
|
{
|
|
uint32_t domid;
|
|
int opt = 0;
|
|
const char *mem;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "mem-set", 2) {
|
|
/* No options */
|
|
}
|
|
|
|
domid = find_domain(argv[optind]);
|
|
mem = argv[optind + 1];
|
|
|
|
set_memory_target(domid, mem);
|
|
return 0;
|
|
}
|
|
|
|
static int cd_insert(uint32_t domid, const char *virtdev, char *phys)
|
|
{
|
|
libxl_device_disk disk;
|
|
char *buf = NULL;
|
|
XLU_Config *config = 0;
|
|
struct stat b;
|
|
int rc = 0;
|
|
|
|
if (asprintf(&buf, "vdev=%s,access=r,devtype=cdrom,target=%s",
|
|
virtdev, phys ? phys : "") < 0) {
|
|
fprintf(stderr, "out of memory\n");
|
|
return 1;
|
|
}
|
|
|
|
parse_disk_config(&config, buf, &disk);
|
|
|
|
/* ATM the existence of the backing file is not checked for qdisk
|
|
* in libxl_cdrom_insert() because RAW is used for remote
|
|
* protocols as well as plain files. This will ideally be changed
|
|
* for 4.4, but this work-around fixes the problem of "cd-insert"
|
|
* returning success for non-existent files. */
|
|
if (disk.format != LIBXL_DISK_FORMAT_EMPTY
|
|
&& stat(disk.pdev_path, &b)) {
|
|
fprintf(stderr, "Cannot stat file: %s\n",
|
|
disk.pdev_path);
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
|
|
if (libxl_cdrom_insert(ctx, domid, &disk, NULL))
|
|
rc=1;
|
|
|
|
out:
|
|
libxl_device_disk_dispose(&disk);
|
|
free(buf);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int main_cd_eject(int argc, char **argv)
|
|
{
|
|
uint32_t domid;
|
|
int opt = 0;
|
|
const char *virtdev;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "cd-eject", 2) {
|
|
/* No options */
|
|
}
|
|
|
|
domid = find_domain(argv[optind]);
|
|
virtdev = argv[optind + 1];
|
|
|
|
return cd_insert(domid, virtdev, NULL);
|
|
}
|
|
|
|
int main_cd_insert(int argc, char **argv)
|
|
{
|
|
uint32_t domid;
|
|
int opt = 0;
|
|
const char *virtdev;
|
|
char *file = NULL; /* modified by cd_insert tokenising it */
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "cd-insert", 3) {
|
|
/* No options */
|
|
}
|
|
|
|
domid = find_domain(argv[optind]);
|
|
virtdev = argv[optind + 1];
|
|
file = argv[optind + 2];
|
|
|
|
cd_insert(domid, virtdev, file);
|
|
return 0;
|
|
}
|
|
|
|
int main_console(int argc, char **argv)
|
|
{
|
|
uint32_t domid;
|
|
int opt = 0, num = 0;
|
|
libxl_console_type type = 0;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "n:t:", NULL, "console", 1) {
|
|
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;
|
|
}
|
|
|
|
domid = 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;
|
|
}
|
|
|
|
int main_vncviewer(int argc, char **argv)
|
|
{
|
|
static const struct option opts[] = {
|
|
{"autopass", 0, 0, 'a'},
|
|
{"vncviewer-autopass", 0, 0, 'a'},
|
|
COMMON_LONG_OPTS,
|
|
{0, 0, 0, 0}
|
|
};
|
|
uint32_t domid;
|
|
int opt, autopass = 0;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "ah", opts, "vncviewer", 1) {
|
|
case 'a':
|
|
autopass = 1;
|
|
break;
|
|
}
|
|
|
|
domid = find_domain(argv[optind]);
|
|
|
|
if (vncviewer(domid, autopass))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static void pcilist(uint32_t domid)
|
|
{
|
|
libxl_device_pci *pcidevs;
|
|
int num, i;
|
|
|
|
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)
|
|
{
|
|
uint32_t domid;
|
|
int opt;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "pci-list", 1) {
|
|
/* No options */
|
|
}
|
|
|
|
domid = find_domain(argv[optind]);
|
|
|
|
pcilist(domid);
|
|
return 0;
|
|
}
|
|
|
|
static void pcidetach(uint32_t domid, const char *bdf, int force)
|
|
{
|
|
libxl_device_pci pcidev;
|
|
XLU_Config *config;
|
|
|
|
libxl_device_pci_init(&pcidev);
|
|
|
|
config = xlu_cfg_init(stderr, "command line");
|
|
if (!config) { perror("xlu_cfg_inig"); exit(-1); }
|
|
|
|
if (xlu_pci_parse_bdf(config, &pcidev, bdf)) {
|
|
fprintf(stderr, "pci-detach: malformed BDF specification \"%s\"\n", bdf);
|
|
exit(2);
|
|
}
|
|
if (force)
|
|
libxl_device_pci_destroy(ctx, domid, &pcidev, 0);
|
|
else
|
|
libxl_device_pci_remove(ctx, domid, &pcidev, 0);
|
|
|
|
libxl_device_pci_dispose(&pcidev);
|
|
xlu_cfg_destroy(config);
|
|
}
|
|
|
|
int main_pcidetach(int argc, char **argv)
|
|
{
|
|
uint32_t domid;
|
|
int opt;
|
|
int force = 0;
|
|
const char *bdf = NULL;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "f", NULL, "pci-detach", 2) {
|
|
case 'f':
|
|
force = 1;
|
|
break;
|
|
}
|
|
|
|
domid = find_domain(argv[optind]);
|
|
bdf = argv[optind + 1];
|
|
|
|
pcidetach(domid, bdf, force);
|
|
return 0;
|
|
}
|
|
static void pciattach(uint32_t domid, const char *bdf, const char *vs)
|
|
{
|
|
libxl_device_pci pcidev;
|
|
XLU_Config *config;
|
|
|
|
libxl_device_pci_init(&pcidev);
|
|
|
|
config = xlu_cfg_init(stderr, "command line");
|
|
if (!config) { perror("xlu_cfg_inig"); exit(-1); }
|
|
|
|
if (xlu_pci_parse_bdf(config, &pcidev, bdf)) {
|
|
fprintf(stderr, "pci-attach: malformed BDF specification \"%s\"\n", bdf);
|
|
exit(2);
|
|
}
|
|
libxl_device_pci_add(ctx, domid, &pcidev, 0);
|
|
|
|
libxl_device_pci_dispose(&pcidev);
|
|
xlu_cfg_destroy(config);
|
|
}
|
|
|
|
int main_pciattach(int argc, char **argv)
|
|
{
|
|
uint32_t domid;
|
|
int opt;
|
|
const char *bdf = NULL, *vs = NULL;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "pci-attach", 2) {
|
|
/* No options */
|
|
}
|
|
|
|
domid = find_domain(argv[optind]);
|
|
bdf = argv[optind + 1];
|
|
|
|
if (optind + 1 < argc)
|
|
vs = argv[optind + 2];
|
|
|
|
pciattach(domid, bdf, vs);
|
|
return 0;
|
|
}
|
|
|
|
static void pciassignable_list(void)
|
|
{
|
|
libxl_device_pci *pcidevs;
|
|
int num, i;
|
|
|
|
pcidevs = libxl_device_pci_assignable_list(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_pciassignable_list(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "pci-assignable-list", 0) {
|
|
/* No options */
|
|
}
|
|
|
|
pciassignable_list();
|
|
return 0;
|
|
}
|
|
|
|
static void pciassignable_add(const char *bdf, int rebind)
|
|
{
|
|
libxl_device_pci pcidev;
|
|
XLU_Config *config;
|
|
|
|
libxl_device_pci_init(&pcidev);
|
|
|
|
config = xlu_cfg_init(stderr, "command line");
|
|
if (!config) { perror("xlu_cfg_init"); exit(-1); }
|
|
|
|
if (xlu_pci_parse_bdf(config, &pcidev, bdf)) {
|
|
fprintf(stderr, "pci-assignable-add: malformed BDF specification \"%s\"\n", bdf);
|
|
exit(2);
|
|
}
|
|
libxl_device_pci_assignable_add(ctx, &pcidev, rebind);
|
|
|
|
libxl_device_pci_dispose(&pcidev);
|
|
xlu_cfg_destroy(config);
|
|
}
|
|
|
|
int main_pciassignable_add(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
const char *bdf = NULL;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "pci-assignable-add", 1) {
|
|
/* No options */
|
|
}
|
|
|
|
bdf = argv[optind];
|
|
|
|
pciassignable_add(bdf, 1);
|
|
return 0;
|
|
}
|
|
|
|
static void pciassignable_remove(const char *bdf, int rebind)
|
|
{
|
|
libxl_device_pci pcidev;
|
|
XLU_Config *config;
|
|
|
|
libxl_device_pci_init(&pcidev);
|
|
|
|
config = xlu_cfg_init(stderr, "command line");
|
|
if (!config) { perror("xlu_cfg_init"); exit(-1); }
|
|
|
|
if (xlu_pci_parse_bdf(config, &pcidev, bdf)) {
|
|
fprintf(stderr, "pci-assignable-remove: malformed BDF specification \"%s\"\n", bdf);
|
|
exit(2);
|
|
}
|
|
libxl_device_pci_assignable_remove(ctx, &pcidev, rebind);
|
|
|
|
libxl_device_pci_dispose(&pcidev);
|
|
xlu_cfg_destroy(config);
|
|
}
|
|
|
|
int main_pciassignable_remove(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
const char *bdf = NULL;
|
|
int rebind = 0;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "r", NULL, "pci-assignable-remove", 1) {
|
|
case 'r':
|
|
rebind=1;
|
|
break;
|
|
}
|
|
|
|
bdf = argv[optind];
|
|
|
|
pciassignable_remove(bdf, rebind);
|
|
return 0;
|
|
}
|
|
|
|
static void pause_domain(uint32_t domid)
|
|
{
|
|
libxl_domain_pause(ctx, domid);
|
|
}
|
|
|
|
static void unpause_domain(uint32_t domid)
|
|
{
|
|
libxl_domain_unpause(ctx, domid);
|
|
}
|
|
|
|
static void destroy_domain(uint32_t domid)
|
|
{
|
|
int rc;
|
|
|
|
if (domid == 0) {
|
|
fprintf(stderr, "Cannot destroy privileged domain 0.\n\n");
|
|
exit(-1);
|
|
}
|
|
rc = libxl_domain_destroy(ctx, domid, 0);
|
|
if (rc) { fprintf(stderr,"destroy failed (rc=%d)\n",rc); exit(-1); }
|
|
}
|
|
|
|
static void wait_for_domain_deaths(libxl_evgen_domain_death **deathws, int nr)
|
|
{
|
|
int rc, count = 0;
|
|
LOG("Waiting for %d domains", nr);
|
|
while(1 && count < nr) {
|
|
libxl_event *event;
|
|
rc = libxl_event_wait(ctx, &event, LIBXL_EVENTMASK_ALL, 0,0);
|
|
if (rc) {
|
|
LOG("Failed to get event, quitting (rc=%d)", rc);
|
|
exit(-1);
|
|
}
|
|
|
|
switch (event->type) {
|
|
case LIBXL_EVENT_TYPE_DOMAIN_DEATH:
|
|
LOG("Domain %d has been destroyed", event->domid);
|
|
libxl_evdisable_domain_death(ctx, deathws[event->for_user]);
|
|
count++;
|
|
break;
|
|
case LIBXL_EVENT_TYPE_DOMAIN_SHUTDOWN:
|
|
LOG("Domain %d has been shut down, reason code %d",
|
|
event->domid, event->u.domain_shutdown.shutdown_reason);
|
|
libxl_evdisable_domain_death(ctx, deathws[event->for_user]);
|
|
count++;
|
|
break;
|
|
default:
|
|
LOG("Unexpected event type %d", event->type);
|
|
break;
|
|
}
|
|
libxl_event_free(ctx, event);
|
|
}
|
|
}
|
|
|
|
static void shutdown_domain(uint32_t domid,
|
|
libxl_evgen_domain_death **deathw,
|
|
libxl_ev_user for_user,
|
|
int fallback_trigger)
|
|
{
|
|
int rc;
|
|
|
|
fprintf(stderr, "Shutting down domain %d\n", domid);
|
|
rc=libxl_domain_shutdown(ctx, domid);
|
|
if (rc == ERROR_NOPARAVIRT) {
|
|
if (fallback_trigger) {
|
|
fprintf(stderr, "PV control interface not available:"
|
|
" sending ACPI power button event.\n");
|
|
rc = libxl_send_trigger(ctx, domid, LIBXL_TRIGGER_POWER, 0);
|
|
} else {
|
|
fprintf(stderr, "PV control interface not available:"
|
|
" external graceful shutdown not possible.\n");
|
|
fprintf(stderr, "Use \"-F\" to fallback to ACPI power event.\n");
|
|
}
|
|
}
|
|
|
|
if (rc) {
|
|
fprintf(stderr,"shutdown failed (rc=%d)\n",rc);exit(-1);
|
|
}
|
|
|
|
if (deathw) {
|
|
rc = libxl_evenable_domain_death(ctx, domid, for_user, deathw);
|
|
if (rc) {
|
|
fprintf(stderr,"wait for death failed (evgen, rc=%d)\n",rc);
|
|
exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void reboot_domain(uint32_t domid, libxl_evgen_domain_death **deathw,
|
|
libxl_ev_user for_user, int fallback_trigger)
|
|
{
|
|
int rc;
|
|
|
|
fprintf(stderr, "Rebooting domain %d\n", domid);
|
|
rc=libxl_domain_reboot(ctx, domid);
|
|
if (rc == ERROR_NOPARAVIRT) {
|
|
if (fallback_trigger) {
|
|
fprintf(stderr, "PV control interface not available:"
|
|
" sending ACPI reset button event.\n");
|
|
rc = libxl_send_trigger(ctx, domid, LIBXL_TRIGGER_RESET, 0);
|
|
} else {
|
|
fprintf(stderr, "PV control interface not available:"
|
|
" external graceful reboot not possible.\n");
|
|
fprintf(stderr, "Use \"-F\" to fallback to ACPI reset event.\n");
|
|
}
|
|
}
|
|
if (rc) {
|
|
fprintf(stderr,"reboot failed (rc=%d)\n",rc);exit(-1);
|
|
}
|
|
|
|
if (deathw) {
|
|
rc = libxl_evenable_domain_death(ctx, domid, for_user, deathw);
|
|
if (rc) {
|
|
fprintf(stderr,"wait for death failed (evgen, rc=%d)\n",rc);
|
|
exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void list_domains_details(const libxl_dominfo *info, int nb_domain)
|
|
{
|
|
libxl_domain_config d_config;
|
|
|
|
char *config_source;
|
|
uint8_t *data;
|
|
int i, len, rc;
|
|
|
|
yajl_gen hand = NULL;
|
|
yajl_gen_status s;
|
|
const char *buf;
|
|
libxl_yajl_length yajl_len = 0;
|
|
|
|
if (default_output_format == OUTPUT_FORMAT_JSON) {
|
|
hand = libxl_yajl_gen_alloc(NULL);
|
|
if (!hand) {
|
|
fprintf(stderr, "unable to allocate JSON generator\n");
|
|
return;
|
|
}
|
|
|
|
s = yajl_gen_array_open(hand);
|
|
if (s != yajl_gen_status_ok)
|
|
goto out;
|
|
} else
|
|
s = yajl_gen_status_ok;
|
|
|
|
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_source, "<domid %d data>", info[i].domid));
|
|
libxl_domain_config_init(&d_config);
|
|
parse_config_data(config_source, (char *)data, len, &d_config, NULL);
|
|
if (default_output_format == OUTPUT_FORMAT_JSON)
|
|
s = printf_info_one_json(hand, info[i].domid, &d_config);
|
|
else
|
|
printf_info_sexp(info[i].domid, &d_config);
|
|
libxl_domain_config_dispose(&d_config);
|
|
free(data);
|
|
free(config_source);
|
|
if (s != yajl_gen_status_ok)
|
|
goto out;
|
|
}
|
|
|
|
if (default_output_format == OUTPUT_FORMAT_JSON) {
|
|
s = yajl_gen_array_close(hand);
|
|
if (s != yajl_gen_status_ok)
|
|
goto out;
|
|
|
|
s = yajl_gen_get_buf(hand, (const unsigned char **)&buf, &yajl_len);
|
|
if (s != yajl_gen_status_ok)
|
|
goto out;
|
|
|
|
puts(buf);
|
|
}
|
|
|
|
out:
|
|
if (default_output_format == OUTPUT_FORMAT_JSON) {
|
|
yajl_gen_free(hand);
|
|
if (s != yajl_gen_status_ok)
|
|
fprintf(stderr,
|
|
"unable to format domain config as JSON (YAJL:%d)\n", s);
|
|
}
|
|
}
|
|
|
|
/* If map is not full, prints it and returns 0. Returns 1 otherwise. */
|
|
static int 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)
|
|
return 1;
|
|
case 3:
|
|
fprintf(stream, "%s%d", state > 1 ? "," : "", firstset);
|
|
if (i - 1 > firstset)
|
|
fprintf(stream, "-%d", i - 1);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void print_cpumap(uint8_t *map, int maplen, FILE *stream)
|
|
{
|
|
if (print_bitmap(map, maplen, stream))
|
|
fprintf(stream, "any cpu");
|
|
}
|
|
|
|
static void print_nodemap(uint8_t *map, int maplen, FILE *stream)
|
|
{
|
|
if (print_bitmap(map, maplen, stream))
|
|
fprintf(stream, "any node");
|
|
}
|
|
|
|
static void list_domains(int verbose, int context, int claim, int numa,
|
|
const libxl_dominfo *info, int nb_domain)
|
|
{
|
|
int i;
|
|
static const char shutdown_reason_letters[]= "-rscw";
|
|
libxl_bitmap nodemap;
|
|
libxl_physinfo physinfo;
|
|
|
|
libxl_bitmap_init(&nodemap);
|
|
libxl_physinfo_init(&physinfo);
|
|
|
|
printf("Name ID Mem VCPUs\tState\tTime(s)");
|
|
if (verbose) printf(" UUID Reason-Code\tSecurity Label");
|
|
if (context && !verbose) printf(" Security Label");
|
|
if (claim) printf(" Claimed");
|
|
if (numa) {
|
|
if (libxl_node_bitmap_alloc(ctx, &nodemap, 0)) {
|
|
fprintf(stderr, "libxl_node_bitmap_alloc_failed.\n");
|
|
exit(1);
|
|
}
|
|
if (libxl_get_physinfo(ctx, &physinfo) != 0) {
|
|
fprintf(stderr, "libxl_physinfo failed.\n");
|
|
libxl_bitmap_dispose(&nodemap);
|
|
exit(1);
|
|
}
|
|
|
|
printf(" NODE Affinity");
|
|
}
|
|
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 +
|
|
info[i].outstanding_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 (claim)
|
|
printf(" %5lu", (unsigned long)info[i].outstanding_memkb / 1024);
|
|
if (verbose || context) {
|
|
int rc;
|
|
size_t size;
|
|
char *buf = NULL;
|
|
rc = libxl_flask_sid_to_context(ctx, info[i].ssidref, &buf,
|
|
&size);
|
|
printf(" %16s", rc < 0 ? "-" : buf);
|
|
free(buf);
|
|
}
|
|
if (numa) {
|
|
libxl_domain_get_nodeaffinity(ctx, info[i].domid, &nodemap);
|
|
|
|
putchar(' ');
|
|
print_nodemap(nodemap.map, physinfo.nr_nodes, stdout);
|
|
}
|
|
putchar('\n');
|
|
}
|
|
|
|
libxl_bitmap_dispose(&nodemap);
|
|
libxl_physinfo_dispose(&physinfo);
|
|
}
|
|
|
|
static void list_vm(void)
|
|
{
|
|
libxl_vminfo *info;
|
|
char *domname;
|
|
int nb_vm, i;
|
|
|
|
info = libxl_list_vm(ctx, &nb_vm);
|
|
|
|
if (!info) {
|
|
fprintf(stderr, "libxl_list_vm 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);
|
|
}
|
|
libxl_vminfo_list_free(info, nb_vm);
|
|
}
|
|
|
|
static void save_domain_core_begin(uint32_t domid,
|
|
const char *override_config_file,
|
|
uint8_t **config_data_r,
|
|
int *config_len_r)
|
|
{
|
|
int rc;
|
|
|
|
/* 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 *source,
|
|
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), source, "header") );
|
|
CHK_ERRNO( libxl_write_exactly(ctx, fd,
|
|
optdata_begin, hdr.optional_data_len, source, "header") );
|
|
|
|
fprintf(stderr, "Saving to %s new xl format (info"
|
|
" 0x%"PRIx32"/0x%"PRIx32"/%"PRIu32")\n",
|
|
source, hdr.mandatory_flags, hdr.optional_flags,
|
|
hdr.optional_data_len);
|
|
}
|
|
|
|
static int save_domain(uint32_t domid, const char *filename, int checkpoint,
|
|
int leavepaused, const char *override_config_file)
|
|
{
|
|
int fd;
|
|
uint8_t *config_data;
|
|
int config_len;
|
|
|
|
save_domain_core_begin(domid, 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);
|
|
|
|
int rc = libxl_domain_suspend(ctx, domid, fd, 0, NULL);
|
|
close(fd);
|
|
|
|
if (rc < 0) {
|
|
fprintf(stderr, "Failed to save domain, resuming domain\n");
|
|
libxl_domain_resume(ctx, domid, 1, 0);
|
|
}
|
|
else if (leavepaused || checkpoint) {
|
|
if (leavepaused)
|
|
libxl_domain_pause(ctx, domid);
|
|
libxl_domain_resume(ctx, domid, 1, 0);
|
|
}
|
|
else
|
|
libxl_domain_destroy(ctx, domid, 0);
|
|
|
|
exit(rc < 0 ? 1 : 0);
|
|
}
|
|
|
|
static pid_t create_migration_child(const char *rune, int *send_fd,
|
|
int *recv_fd)
|
|
{
|
|
int sendpipe[2], recvpipe[2];
|
|
pid_t child = -1;
|
|
|
|
if (!rune || !send_fd || !recv_fd)
|
|
return -1;
|
|
|
|
MUST( libxl_pipe(ctx, sendpipe) );
|
|
MUST( libxl_pipe(ctx, recvpipe) );
|
|
|
|
child = xl_fork(child_migration);
|
|
|
|
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];
|
|
|
|
/* if receiver dies, we get an error and can clean up
|
|
rather than just dying */
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
return child;
|
|
}
|
|
|
|
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(int recv_fd) {
|
|
pid_t child;
|
|
int status, sr;
|
|
struct timeval now, waituntil, timeout;
|
|
static const struct timeval pollinterval = { 0, 1000 }; /* 1ms */
|
|
|
|
if (!xl_child_pid(child_migration)) return;
|
|
|
|
CHK_ERRNO( gettimeofday(&waituntil, 0) );
|
|
waituntil.tv_sec += 2;
|
|
|
|
for (;;) {
|
|
pid_t migration_child = xl_child_pid(child_migration);
|
|
child = xl_waitpid(child_migration, &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) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void migrate_do_preamble(int send_fd, int recv_fd, pid_t child,
|
|
uint8_t *config_data, int config_len,
|
|
const char *rune)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (send_fd < 0 || recv_fd < 0) {
|
|
fprintf(stderr, "migrate_do_preamble: invalid file descriptors\n");
|
|
exit(1);
|
|
}
|
|
|
|
rc = migrate_read_fixedmessage(recv_fd, migrate_receiver_banner,
|
|
sizeof(migrate_receiver_banner)-1,
|
|
"banner", rune);
|
|
if (rc) {
|
|
close(send_fd);
|
|
migration_child_report(recv_fd);
|
|
exit(-rc);
|
|
}
|
|
|
|
save_domain_core_writeconfig(send_fd, "migration stream",
|
|
config_data, config_len);
|
|
|
|
}
|
|
|
|
static void migrate_domain(uint32_t domid, const char *rune, int debug,
|
|
const char *override_config_file)
|
|
{
|
|
pid_t child = -1;
|
|
int rc;
|
|
int send_fd = -1, recv_fd = -1;
|
|
char *away_domname;
|
|
char rc_buf;
|
|
uint8_t *config_data;
|
|
int config_len, flags = LIBXL_SUSPEND_LIVE;
|
|
|
|
save_domain_core_begin(domid, 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);
|
|
}
|
|
|
|
child = create_migration_child(rune, &send_fd, &recv_fd);
|
|
|
|
migrate_do_preamble(send_fd, recv_fd, child, config_data, config_len,
|
|
rune);
|
|
|
|
xtl_stdiostream_adjust_flags(logger, XTL_STDIOSTREAM_HIDE_PROGRESS, 0);
|
|
|
|
if (debug)
|
|
flags |= LIBXL_SUSPEND_DEBUG;
|
|
rc = libxl_domain_suspend(ctx, domid, send_fd, flags, NULL);
|
|
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, 0, 0);
|
|
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, 0); /* bang! */
|
|
fprintf(stderr, "Migration successful.\n");
|
|
exit(0);
|
|
|
|
failed_suspend:
|
|
close(send_fd);
|
|
migration_child_report(recv_fd);
|
|
fprintf(stderr, "Migration failed, failed to suspend at sender.\n");
|
|
exit(-ERROR_FAIL);
|
|
|
|
failed_resume:
|
|
close(send_fd);
|
|
migration_child_report(recv_fd);
|
|
fprintf(stderr, "Migration failed, resuming at sender.\n");
|
|
libxl_domain_resume(ctx, domid, 0, 0);
|
|
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(recv_fd);
|
|
exit(-ERROR_BADFAIL);
|
|
}
|
|
|
|
static void core_dump_domain(uint32_t domid, const char *filename)
|
|
{
|
|
int rc;
|
|
|
|
rc=libxl_domain_core_dump(ctx, domid, filename, NULL);
|
|
if (rc) { fprintf(stderr,"core dump failed (rc=%d)\n",rc);exit(-1); }
|
|
}
|
|
|
|
static void migrate_receive(int debug, int daemonize, int monitor,
|
|
int send_fd, int recv_fd, int remus)
|
|
{
|
|
uint32_t domid;
|
|
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, send_fd,
|
|
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.migrate_fd = recv_fd;
|
|
dom_info.migration_domname_r = &migration_domname;
|
|
dom_info.checkpointed_stream = remus;
|
|
|
|
rc = create_domain(&dom_info);
|
|
if (rc < 0) {
|
|
fprintf(stderr, "migration target: Domain creation failed"
|
|
" (code %d).\n", rc);
|
|
exit(-rc);
|
|
}
|
|
|
|
domid = rc;
|
|
|
|
if (remus) {
|
|
/* If we are here, it means that the sender (primary) has crashed.
|
|
* TODO: Split-Brain Check.
|
|
*/
|
|
fprintf(stderr, "migration target: Remus Failover for domain %u\n",
|
|
domid);
|
|
|
|
/*
|
|
* If domain renaming fails, lets just continue (as we need the domain
|
|
* to be up & dom names may not matter much, as long as its reachable
|
|
* over network).
|
|
*
|
|
* If domain unpausing fails, destroy domain ? Or is it better to have
|
|
* a consistent copy of the domain (memory, cpu state, disk)
|
|
* on atleast one physical host ? Right now, lets just leave the domain
|
|
* as is and let the Administrator decide (or troubleshoot).
|
|
*/
|
|
if (migration_domname) {
|
|
rc = libxl_domain_rename(ctx, domid, migration_domname,
|
|
common_domname);
|
|
if (rc)
|
|
fprintf(stderr, "migration target (Remus): "
|
|
"Failed to rename domain from %s to %s:%d\n",
|
|
migration_domname, common_domname, rc);
|
|
}
|
|
|
|
rc = libxl_domain_unpause(ctx, domid);
|
|
if (rc)
|
|
fprintf(stderr, "migration target (Remus): "
|
|
"Failed to unpause domain %s (id: %u):%d\n",
|
|
common_domname, domid, rc);
|
|
|
|
exit(rc ? -ERROR_FAIL: 0);
|
|
}
|
|
|
|
fprintf(stderr, "migration target: Transfer complete,"
|
|
" requesting permission to start domain.\n");
|
|
|
|
rc = libxl_write_exactly(ctx, send_fd,
|
|
migrate_receiver_ready,
|
|
sizeof(migrate_receiver_ready),
|
|
"migration ack stream", "ready message");
|
|
if (rc) exit(-rc);
|
|
|
|
rc = migrate_read_fixedmessage(recv_fd, 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, send_fd,
|
|
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, send_fd, &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, 0);
|
|
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, send_fd,
|
|
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, vnc = 0, vncautopass = 0;
|
|
int opt, rc;
|
|
static struct option opts[] = {
|
|
{"vncviewer", 0, 0, 'V'},
|
|
{"vncviewer-autopass", 0, 0, 'A'},
|
|
COMMON_LONG_OPTS,
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
SWITCH_FOREACH_OPT(opt, "FhcpdeVA", opts, "restore", 1) {
|
|
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;
|
|
case 'V':
|
|
vnc = 1;
|
|
break;
|
|
case 'A':
|
|
vnc = vncautopass = 1;
|
|
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.vnc = vnc;
|
|
dom_info.vncautopass = vncautopass;
|
|
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, remus = 0;
|
|
int opt;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "Fedr", NULL, "migrate-receive", 0) {
|
|
case 'F':
|
|
daemonize = 0;
|
|
break;
|
|
case 'e':
|
|
daemonize = 0;
|
|
monitor = 0;
|
|
break;
|
|
case 'd':
|
|
debug = 1;
|
|
break;
|
|
case 'r':
|
|
remus = 1;
|
|
break;
|
|
}
|
|
|
|
if (argc-optind != 0) {
|
|
help("migrate-receive");
|
|
return 2;
|
|
}
|
|
migrate_receive(debug, daemonize, monitor,
|
|
STDOUT_FILENO, STDIN_FILENO,
|
|
remus);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main_save(int argc, char **argv)
|
|
{
|
|
uint32_t domid;
|
|
const char *filename;
|
|
const char *config_filename = NULL;
|
|
int checkpoint = 0;
|
|
int leavepaused = 0;
|
|
int opt;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "cp", NULL, "save", 2) {
|
|
case 'c':
|
|
checkpoint = 1;
|
|
break;
|
|
case 'p':
|
|
leavepaused = 1;
|
|
break;
|
|
}
|
|
|
|
if (argc-optind > 3) {
|
|
help("save");
|
|
return 2;
|
|
}
|
|
|
|
domid = find_domain(argv[optind]);
|
|
filename = argv[optind + 1];
|
|
if ( argc - optind >= 3 )
|
|
config_filename = argv[optind + 2];
|
|
|
|
save_domain(domid, filename, checkpoint, leavepaused, config_filename);
|
|
return 0;
|
|
}
|
|
|
|
int main_migrate(int argc, char **argv)
|
|
{
|
|
uint32_t domid;
|
|
const char *config_filename = NULL;
|
|
const char *ssh_command = "ssh";
|
|
char *rune = NULL;
|
|
char *host;
|
|
int opt, daemonize = 1, monitor = 1, debug = 0;
|
|
static struct option opts[] = {
|
|
{"debug", 0, 0, 0x100},
|
|
COMMON_LONG_OPTS,
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
SWITCH_FOREACH_OPT(opt, "FC:s:e", opts, "migrate", 2) {
|
|
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 0x100:
|
|
debug = 1;
|
|
break;
|
|
}
|
|
|
|
domid = find_domain(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(domid, rune, debug, config_filename);
|
|
return 0;
|
|
}
|
|
|
|
int main_dump_core(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "dump-core", 2) {
|
|
/* No options */
|
|
}
|
|
|
|
core_dump_domain(find_domain(argv[optind]), argv[optind + 1]);
|
|
return 0;
|
|
}
|
|
|
|
int main_pause(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "pause", 1) {
|
|
/* No options */
|
|
}
|
|
|
|
pause_domain(find_domain(argv[optind]));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main_unpause(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "unpause", 1) {
|
|
/* No options */
|
|
}
|
|
|
|
unpause_domain(find_domain(argv[optind]));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main_destroy(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "destroy", 1) {
|
|
/* No options */
|
|
}
|
|
|
|
destroy_domain(find_domain(argv[optind]));
|
|
return 0;
|
|
}
|
|
|
|
static int main_shutdown_or_reboot(int do_reboot, int argc, char **argv)
|
|
{
|
|
const char *what = do_reboot ? "reboot" : "shutdown";
|
|
void (*fn)(uint32_t domid,
|
|
libxl_evgen_domain_death **, libxl_ev_user, int) =
|
|
do_reboot ? &reboot_domain : &shutdown_domain;
|
|
int opt, i, nb_domain;
|
|
int wait_for_it = 0, all =0;
|
|
int fallback_trigger = 0;
|
|
static struct option opts[] = {
|
|
{"all", 0, 0, 'a'},
|
|
{"wait", 0, 0, 'w'},
|
|
COMMON_LONG_OPTS,
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
SWITCH_FOREACH_OPT(opt, "awF", opts, what, 0) {
|
|
case 'a':
|
|
all = 1;
|
|
break;
|
|
case 'w':
|
|
wait_for_it = 1;
|
|
break;
|
|
case 'F':
|
|
fallback_trigger = 1;
|
|
break;
|
|
}
|
|
|
|
if (!argv[optind] && !all) {
|
|
fprintf(stderr, "You must specify -a or a domain id.\n\n");
|
|
return opt;
|
|
}
|
|
|
|
if (all) {
|
|
libxl_dominfo *dominfo;
|
|
libxl_evgen_domain_death **deathws = NULL;
|
|
if (!(dominfo = libxl_list_domain(ctx, &nb_domain))) {
|
|
fprintf(stderr, "libxl_list_domain failed.\n");
|
|
return -1;
|
|
}
|
|
|
|
if (wait_for_it)
|
|
deathws = calloc(nb_domain, sizeof(*deathws));
|
|
|
|
for (i = 0; i<nb_domain; i++) {
|
|
if (dominfo[i].domid == 0)
|
|
continue;
|
|
fn(dominfo[i].domid, deathws ? &deathws[i] : NULL, i,
|
|
fallback_trigger);
|
|
}
|
|
|
|
if (wait_for_it)
|
|
wait_for_domain_deaths(deathws, nb_domain - 1 /* not dom 0 */);
|
|
|
|
libxl_dominfo_list_free(dominfo, nb_domain);
|
|
} else {
|
|
libxl_evgen_domain_death *deathw = NULL;
|
|
uint32_t domid = find_domain(argv[optind]);
|
|
|
|
fn(domid, wait_for_it ? &deathw : NULL, 0, fallback_trigger);
|
|
|
|
if (wait_for_it)
|
|
wait_for_domain_deaths(&deathw, 1);
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main_shutdown(int argc, char **argv)
|
|
{
|
|
return main_shutdown_or_reboot(0, argc, argv);
|
|
}
|
|
|
|
int main_reboot(int argc, char **argv)
|
|
{
|
|
return main_shutdown_or_reboot(1, argc, argv);
|
|
}
|
|
|
|
int main_list(int argc, char **argv)
|
|
{
|
|
int opt, verbose = 0;
|
|
int context = 0;
|
|
int details = 0;
|
|
int numa = 0;
|
|
static struct option opts[] = {
|
|
{"long", 0, 0, 'l'},
|
|
{"verbose", 0, 0, 'v'},
|
|
{"context", 0, 0, 'Z'},
|
|
{"numa", 0, 0, 'n'},
|
|
COMMON_LONG_OPTS,
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
libxl_dominfo info_buf;
|
|
libxl_dominfo *info, *info_free=0;
|
|
int nb_domain, rc;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "lvhZn", opts, "list", 0) {
|
|
case 'l':
|
|
details = 1;
|
|
break;
|
|
case 'v':
|
|
verbose = 1;
|
|
break;
|
|
case 'Z':
|
|
context = 1;
|
|
break;
|
|
case 'n':
|
|
numa = 1;
|
|
break;
|
|
}
|
|
|
|
if (optind >= argc) {
|
|
info = libxl_list_domain(ctx, &nb_domain);
|
|
if (!info) {
|
|
fprintf(stderr, "libxl_list_domain failed.\n");
|
|
return 1;
|
|
}
|
|
info_free = info;
|
|
} else if (optind == argc-1) {
|
|
uint32_t domid = 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, 0 /* claim */, numa, info, nb_domain);
|
|
|
|
if (info_free)
|
|
libxl_dominfo_list_free(info, nb_domain);
|
|
else
|
|
libxl_dominfo_dispose(info);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main_vm_list(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "vm-list", 0) {
|
|
/* No options */
|
|
}
|
|
|
|
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, vnc = 0, vncautopass = 0;
|
|
int opt, rc;
|
|
static struct option opts[] = {
|
|
{"dryrun", 0, 0, 'n'},
|
|
{"quiet", 0, 0, 'q'},
|
|
{"defconfig", 1, 0, 'f'},
|
|
{"vncviewer", 0, 0, 'V'},
|
|
{"vncviewer-autopass", 0, 0, 'A'},
|
|
COMMON_LONG_OPTS,
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
if (argv[1] && argv[1][0] != '-' && !strchr(argv[1], '=')) {
|
|
filename = argv[1];
|
|
argc--; argv++;
|
|
}
|
|
|
|
SWITCH_FOREACH_OPT(opt, "Fhnqf:pcdeVA", opts, "create", 0) {
|
|
case 'f':
|
|
filename = optarg;
|
|
break;
|
|
case 'p':
|
|
paused = 1;
|
|
break;
|
|
case 'c':
|
|
console_autoconnect = 1;
|
|
break;
|
|
case 'd':
|
|
debug = 1;
|
|
break;
|
|
case 'F':
|
|
daemonize = 0;
|
|
break;
|
|
case 'e':
|
|
daemonize = 0;
|
|
monitor = 0;
|
|
break;
|
|
case 'n':
|
|
dryrun_only = 1;
|
|
break;
|
|
case 'q':
|
|
quiet = 1;
|
|
break;
|
|
case 'V':
|
|
vnc = 1;
|
|
break;
|
|
case 'A':
|
|
vnc = vncautopass = 1;
|
|
break;
|
|
}
|
|
|
|
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.vnc = vnc;
|
|
dom_info.vncautopass = vncautopass;
|
|
dom_info.console_autoconnect = console_autoconnect;
|
|
|
|
rc = create_domain(&dom_info);
|
|
if (rc < 0)
|
|
return -rc;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main_config_update(int argc, char **argv)
|
|
{
|
|
uint32_t domid;
|
|
const char *filename = NULL;
|
|
char *p;
|
|
char extra_config[1024];
|
|
void *config_data = 0;
|
|
int config_len = 0;
|
|
libxl_domain_config d_config;
|
|
int opt, rc;
|
|
int debug = 0;
|
|
static struct option opts[] = {
|
|
{"defconfig", 1, 0, 'f'},
|
|
COMMON_LONG_OPTS,
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
if (argc < 2) {
|
|
fprintf(stderr, "xl config-update requires a domain argument\n");
|
|
help("config-update");
|
|
exit(1);
|
|
}
|
|
|
|
domid = find_domain(argv[1]);
|
|
argc--; argv++;
|
|
|
|
if (argv[1] && argv[1][0] != '-' && !strchr(argv[1], '=')) {
|
|
filename = argv[1];
|
|
argc--; argv++;
|
|
}
|
|
|
|
SWITCH_FOREACH_OPT(opt, "dhqf:", opts, "config_update", 0) {
|
|
case 'd':
|
|
debug = 1;
|
|
break;
|
|
case 'f':
|
|
filename = optarg;
|
|
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;
|
|
}
|
|
}
|
|
if (filename) {
|
|
free(config_data); config_data = 0;
|
|
rc = libxl_read_file_contents(ctx, filename,
|
|
&config_data, &config_len);
|
|
if (rc) { 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 + 1)) {
|
|
fprintf(stderr, "Failed to attach extra configration\n");
|
|
exit(1);
|
|
}
|
|
/* 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");
|
|
exit(1);
|
|
}
|
|
config_len += sprintf(config_data + config_len, "\n%s\n",
|
|
extra_config);
|
|
}
|
|
} else {
|
|
fprintf(stderr, "Config file not specified\n");
|
|
exit(1);
|
|
}
|
|
|
|
libxl_domain_config_init(&d_config);
|
|
|
|
parse_config_data(filename, config_data, config_len, &d_config, NULL);
|
|
|
|
if (debug || dryrun_only)
|
|
printf_info(default_output_format, -1, &d_config);
|
|
|
|
if (!dryrun_only) {
|
|
fprintf(stderr, "setting dom%d configuration\n", domid);
|
|
rc = libxl_userdata_store(ctx, domid, "xl",
|
|
config_data, config_len);
|
|
if (rc) {
|
|
fprintf(stderr, "failed to update configuration\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
libxl_domain_config_dispose(&d_config);
|
|
|
|
free(config_data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void button_press(uint32_t domid, const char *b)
|
|
{
|
|
libxl_trigger trigger;
|
|
|
|
if (!strcmp(b, "power")) {
|
|
trigger = LIBXL_TRIGGER_POWER;
|
|
} else if (!strcmp(b, "sleep")) {
|
|
trigger = LIBXL_TRIGGER_SLEEP;
|
|
} else {
|
|
fprintf(stderr, "%s is an invalid button identifier\n", b);
|
|
exit(2);
|
|
}
|
|
|
|
libxl_send_trigger(ctx, domid, trigger, 0);
|
|
}
|
|
|
|
int main_button_press(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
fprintf(stderr, "WARNING: \"button-press\" is deprecated. "
|
|
"Please use \"trigger\"\n");
|
|
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "button-press", 2) {
|
|
/* No options */
|
|
}
|
|
|
|
button_press(find_domain(argv[optind]), argv[optind + 1]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
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_cpumap(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_list_free(vcpuinfo, nb_vcpu);
|
|
}
|
|
|
|
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);
|
|
|
|
libxl_dominfo_list_free(dominfo, nb_domain);
|
|
} else {
|
|
for (; argc > 0; ++argv, --argc) {
|
|
uint32_t domid = find_domain(*argv);
|
|
print_domain_vcpuinfo(domid, physinfo.nr_cpus);
|
|
}
|
|
}
|
|
vcpulist_out:
|
|
libxl_physinfo_dispose(&physinfo);
|
|
}
|
|
|
|
int main_vcpulist(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "vcpu-list", 0) {
|
|
/* No options */
|
|
}
|
|
|
|
vcpulist(argc - optind, argv + optind);
|
|
return 0;
|
|
}
|
|
|
|
static void vcpupin(uint32_t domid, const char *vcpu, char *cpu)
|
|
{
|
|
libxl_vcpuinfo *vcpuinfo;
|
|
libxl_bitmap cpumap;
|
|
|
|
uint32_t vcpuid;
|
|
char *endptr;
|
|
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;
|
|
}
|
|
|
|
if (libxl_cpu_bitmap_alloc(ctx, &cpumap, 0)) {
|
|
goto vcpupin_out;
|
|
}
|
|
|
|
if (vcpupin_parse(cpu, &cpumap))
|
|
goto vcpupin_out1;
|
|
|
|
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 (i = 0; i < nb_vcpu; i++) {
|
|
if (libxl_set_vcpuaffinity(ctx, domid, vcpuinfo[i].vcpuid,
|
|
&cpumap) == -1) {
|
|
fprintf(stderr, "libxl_set_vcpuaffinity failed"
|
|
" on vcpu `%u'.\n", vcpuinfo[i].vcpuid);
|
|
}
|
|
}
|
|
libxl_vcpuinfo_list_free(vcpuinfo, nb_vcpu);
|
|
}
|
|
vcpupin_out1:
|
|
libxl_bitmap_dispose(&cpumap);
|
|
vcpupin_out:
|
|
;
|
|
}
|
|
|
|
int main_vcpupin(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "vcpu-pin", 3) {
|
|
/* No options */
|
|
}
|
|
|
|
vcpupin(find_domain(argv[optind]), argv[optind+1] , argv[optind+2]);
|
|
return 0;
|
|
}
|
|
|
|
static void vcpuset(uint32_t domid, const char* nr_vcpus, int check_host)
|
|
{
|
|
char *endptr;
|
|
unsigned int max_vcpus, i;
|
|
libxl_bitmap cpumap;
|
|
|
|
max_vcpus = strtoul(nr_vcpus, &endptr, 10);
|
|
if (nr_vcpus == endptr) {
|
|
fprintf(stderr, "Error: Invalid argument.\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Maximum amount of vCPUS the guest is allowed to set is limited
|
|
* by the host's amount of pCPUs.
|
|
*/
|
|
if (check_host) {
|
|
unsigned int host_cpu = libxl_get_max_cpus(ctx);
|
|
if (max_vcpus > host_cpu) {
|
|
fprintf(stderr, "You are overcommmitting! You have %d physical " \
|
|
" CPUs and want %d vCPUs! Aborting, use --ignore-host to " \
|
|
" continue\n", host_cpu, max_vcpus);
|
|
return;
|
|
}
|
|
/* NB: This also limits how many are set in the bitmap */
|
|
max_vcpus = (max_vcpus > host_cpu ? host_cpu : max_vcpus);
|
|
}
|
|
if (libxl_cpu_bitmap_alloc(ctx, &cpumap, max_vcpus)) {
|
|
fprintf(stderr, "libxl_cpu_bitmap_alloc failed\n");
|
|
return;
|
|
}
|
|
for (i = 0; i < max_vcpus; i++)
|
|
libxl_bitmap_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_bitmap_dispose(&cpumap);
|
|
}
|
|
|
|
int main_vcpuset(int argc, char **argv)
|
|
{
|
|
static struct option opts[] = {
|
|
{"ignore-host", 0, 0, 'i'},
|
|
{0, 0, 0, 0}
|
|
};
|
|
int opt, check_host = 1;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "i", opts, "vcpu-set", 2) {
|
|
case 'i':
|
|
check_host = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
vcpuset(find_domain(argv[optind]), argv[optind + 1], check_host);
|
|
return 0;
|
|
}
|
|
|
|
static void output_xeninfo(void)
|
|
{
|
|
const libxl_version_info *info;
|
|
libxl_scheduler sched;
|
|
|
|
if (!(info = libxl_get_version_info(ctx))) {
|
|
fprintf(stderr, "libxl_get_version_info failed.\n");
|
|
return;
|
|
}
|
|
|
|
if ((sched = libxl_get_scheduler(ctx)) < 0) {
|
|
fprintf(stderr, "get_scheduler 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_version : %d.%d%s\n", info->xen_version_major,
|
|
info->xen_version_minor, info->xen_version_extra);
|
|
printf("xen_caps : %s\n", info->capabilities);
|
|
printf("xen_scheduler : %s\n", libxl_scheduler_to_string(sched));
|
|
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_bitmap 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("max_cpu_id : %d\n", info.max_cpu_id);
|
|
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.cap_hvm)
|
|
printf(" hvm");
|
|
if (info.cap_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 - info.outstanding_pages) / i);
|
|
printf("sharing_freed_memory : %"PRIu64"\n", info.sharing_freed_pages / i);
|
|
printf("sharing_used_memory : %"PRIu64"\n", info.sharing_used_frames / i);
|
|
printf("outstanding_claims : %"PRIu64"\n", info.outstanding_pages / i);
|
|
}
|
|
if (!libxl_get_freecpus(ctx, &cpumap)) {
|
|
libxl_for_each_bit(i, cpumap)
|
|
if (libxl_bitmap_test(&cpumap, i))
|
|
n++;
|
|
printf("free_cpus : %d\n", n);
|
|
free(cpumap.map);
|
|
}
|
|
libxl_physinfo_dispose(&info);
|
|
return;
|
|
}
|
|
|
|
static void output_numainfo(void)
|
|
{
|
|
libxl_numainfo *info;
|
|
int i, j, nr;
|
|
|
|
info = libxl_get_numainfo(ctx, &nr);
|
|
if (info == NULL) {
|
|
fprintf(stderr, "libxl_get_numainfo failed.\n");
|
|
return;
|
|
}
|
|
|
|
printf("numa_info :\n");
|
|
printf("node: memsize memfree distances\n");
|
|
|
|
for (i = 0; i < nr; i++) {
|
|
if (info[i].size != LIBXL_NUMAINFO_INVALID_ENTRY) {
|
|
printf("%4d: %6"PRIu64" %6"PRIu64" %d", i,
|
|
info[i].size >> 20, info[i].free >> 20,
|
|
info[i].dists[0]);
|
|
for (j = 1; j < info[i].num_dists; j++)
|
|
printf(",%d", info[i].dists[j]);
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
libxl_numainfo_list_free(info, nr);
|
|
|
|
return;
|
|
}
|
|
|
|
static void output_topologyinfo(void)
|
|
{
|
|
libxl_cputopology *info;
|
|
int i, nr;
|
|
|
|
info = libxl_get_cpu_topology(ctx, &nr);
|
|
if (info == NULL) {
|
|
fprintf(stderr, "libxl_get_topologyinfo failed.\n");
|
|
return;
|
|
}
|
|
|
|
printf("cpu_topology :\n");
|
|
printf("cpu: core socket node\n");
|
|
|
|
for (i = 0; i < nr; i++) {
|
|
if (info[i].core != LIBXL_CPUTOPOLOGY_INVALID_ENTRY)
|
|
printf("%3d: %4d %4d %4d\n", i,
|
|
info[i].core, info[i].socket, info[i].node);
|
|
}
|
|
|
|
libxl_cputopology_list_free(info, nr);
|
|
|
|
return;
|
|
}
|
|
|
|
static void print_info(int numa)
|
|
{
|
|
output_nodeinfo();
|
|
|
|
output_physinfo();
|
|
|
|
if (numa) {
|
|
output_topologyinfo();
|
|
output_numainfo();
|
|
}
|
|
output_xeninfo();
|
|
|
|
printf("xend_config_format : 4\n");
|
|
|
|
return;
|
|
}
|
|
|
|
int main_info(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
static struct option opts[] = {
|
|
{"numa", 0, 0, 'n'},
|
|
COMMON_LONG_OPTS,
|
|
{0, 0, 0, 0}
|
|
};
|
|
int numa = 0;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "hn", opts, "info", 0) {
|
|
case 'n':
|
|
numa = 1;
|
|
break;
|
|
}
|
|
|
|
print_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 +
|
|
info[i].outstanding_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;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "sharing", 0) {
|
|
/* No options */
|
|
}
|
|
|
|
if (optind >= argc) {
|
|
info = libxl_list_domain(ctx, &nb_domain);
|
|
if (!info) {
|
|
fprintf(stderr, "libxl_list_domain failed.\n");
|
|
return 1;
|
|
}
|
|
info_free = info;
|
|
} else if (optind == argc-1) {
|
|
uint32_t domid = 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)
|
|
libxl_dominfo_list_free(info_free, nb_domain);
|
|
else
|
|
libxl_dominfo_dispose(info);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sched_domain_get(libxl_scheduler sched, int domid,
|
|
libxl_domain_sched_params *scinfo)
|
|
{
|
|
int rc;
|
|
|
|
rc = libxl_domain_sched_params_get(ctx, domid, scinfo);
|
|
if (rc) {
|
|
fprintf(stderr, "libxl_domain_sched_params_get failed.\n");
|
|
return rc;
|
|
}
|
|
if (scinfo->sched != sched) {
|
|
fprintf(stderr, "libxl_domain_sched_params_get returned %s not %s.\n",
|
|
libxl_scheduler_to_string(scinfo->sched),
|
|
libxl_scheduler_to_string(sched));
|
|
return ERROR_INVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sched_domain_set(int domid, const libxl_domain_sched_params *scinfo)
|
|
{
|
|
int rc;
|
|
|
|
rc = libxl_domain_sched_params_set(ctx, domid, scinfo);
|
|
if (rc)
|
|
fprintf(stderr, "libxl_domain_sched_params_set failed.\n");
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int sched_credit_params_set(int poolid, libxl_sched_credit_params *scinfo)
|
|
{
|
|
int rc;
|
|
|
|
rc = libxl_sched_credit_params_set(ctx, poolid, scinfo);
|
|
if (rc)
|
|
fprintf(stderr, "libxl_sched_credit_params_set failed.\n");
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int sched_credit_params_get(int poolid, libxl_sched_credit_params *scinfo)
|
|
{
|
|
int rc;
|
|
|
|
rc = libxl_sched_credit_params_get(ctx, poolid, scinfo);
|
|
if (rc)
|
|
fprintf(stderr, "libxl_sched_credit_params_get failed.\n");
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int sched_credit_domain_output(int domid)
|
|
{
|
|
char *domname;
|
|
libxl_domain_sched_params scinfo;
|
|
int rc;
|
|
|
|
if (domid < 0) {
|
|
printf("%-33s %4s %6s %4s\n", "Name", "ID", "Weight", "Cap");
|
|
return 0;
|
|
}
|
|
rc = sched_domain_get(LIBXL_SCHEDULER_CREDIT, 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);
|
|
libxl_domain_sched_params_dispose(&scinfo);
|
|
return 0;
|
|
}
|
|
|
|
static int sched_credit_pool_output(uint32_t poolid)
|
|
{
|
|
libxl_sched_credit_params scparam;
|
|
char *poolname;
|
|
int rc;
|
|
|
|
poolname = libxl_cpupoolid_to_name(ctx, poolid);
|
|
rc = sched_credit_params_get(poolid, &scparam);
|
|
if (rc) {
|
|
printf("Cpupool %s: [sched params unavailable]\n",
|
|
poolname);
|
|
} else {
|
|
printf("Cpupool %s: tslice=%dms ratelimit=%dus\n",
|
|
poolname,
|
|
scparam.tslice_ms,
|
|
scparam.ratelimit_us);
|
|
}
|
|
free(poolname);
|
|
return 0;
|
|
}
|
|
|
|
static int sched_credit2_domain_output(
|
|
int domid)
|
|
{
|
|
char *domname;
|
|
libxl_domain_sched_params scinfo;
|
|
int rc;
|
|
|
|
if (domid < 0) {
|
|
printf("%-33s %4s %6s\n", "Name", "ID", "Weight");
|
|
return 0;
|
|
}
|
|
rc = sched_domain_get(LIBXL_SCHEDULER_CREDIT2, domid, &scinfo);
|
|
if (rc)
|
|
return rc;
|
|
domname = libxl_domid_to_name(ctx, domid);
|
|
printf("%-33s %4d %6d\n",
|
|
domname,
|
|
domid,
|
|
scinfo.weight);
|
|
free(domname);
|
|
libxl_domain_sched_params_dispose(&scinfo);
|
|
return 0;
|
|
}
|
|
|
|
static int sched_sedf_domain_output(
|
|
int domid)
|
|
{
|
|
char *domname;
|
|
libxl_domain_sched_params 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_domain_get(LIBXL_SCHEDULER_SEDF, 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);
|
|
libxl_domain_sched_params_dispose(&scinfo);
|
|
return 0;
|
|
}
|
|
|
|
static int sched_default_pool_output(uint32_t poolid)
|
|
{
|
|
char *poolname;
|
|
|
|
poolname = libxl_cpupoolid_to_name(ctx, poolid);
|
|
printf("Cpupool %s:\n",
|
|
poolname);
|
|
free(poolname);
|
|
return 0;
|
|
}
|
|
|
|
static int sched_domain_output(libxl_scheduler sched, int (*output)(int),
|
|
int (*pooloutput)(uint32_t), const char *cpupool)
|
|
{
|
|
libxl_dominfo *info;
|
|
libxl_cpupoolinfo *poolinfo = NULL;
|
|
uint32_t poolid;
|
|
int nb_domain, n_pools = 0, i, p;
|
|
int rc = 0;
|
|
|
|
if (cpupool) {
|
|
if (cpupool_qualifier_to_cpupoolid(cpupool, &poolid, NULL) ||
|
|
!libxl_cpupoolid_is_valid(ctx, poolid)) {
|
|
fprintf(stderr, "unknown cpupool \'%s\'\n", cpupool);
|
|
return -ERROR_FAIL;
|
|
}
|
|
}
|
|
|
|
info = libxl_list_domain(ctx, &nb_domain);
|
|
if (!info) {
|
|
fprintf(stderr, "libxl_list_domain 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 != sched) ||
|
|
(cpupool && (poolid != poolinfo[p].poolid)))
|
|
continue;
|
|
|
|
pooloutput(poolinfo[p].poolid);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
libxl_cpupoolinfo_list_free(poolinfo, n_pools);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* <nothing> : List all domain params and sched params from all pools
|
|
* -d [domid] : List domain params for domain
|
|
* -d [domid] [params] : Set domain params for domain
|
|
* -p [pool] : list all domains and sched params for pool
|
|
* -s : List sched params for poolid 0
|
|
* -s [params] : Set sched params for poolid 0
|
|
* -p [pool] -s : List sched params for pool
|
|
* -p [pool] -s [params] : Set sched params for pool
|
|
* -p [pool] -d... : Illegal
|
|
*/
|
|
int main_sched_credit(int argc, char **argv)
|
|
{
|
|
const char *dom = NULL;
|
|
const char *cpupool = NULL;
|
|
int weight = 256, cap = 0, opt_w = 0, opt_c = 0;
|
|
int opt_s = 0;
|
|
int tslice = 0, opt_t = 0, ratelimit = 0, opt_r = 0;
|
|
int opt, rc;
|
|
static struct option opts[] = {
|
|
{"domain", 1, 0, 'd'},
|
|
{"weight", 1, 0, 'w'},
|
|
{"cap", 1, 0, 'c'},
|
|
{"schedparam", 0, 0, 's'},
|
|
{"tslice_ms", 1, 0, 't'},
|
|
{"ratelimit_us", 1, 0, 'r'},
|
|
{"cpupool", 1, 0, 'p'},
|
|
COMMON_LONG_OPTS,
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
SWITCH_FOREACH_OPT(opt, "d:w:c:p:t:r:hs", opts, "sched-credit", 0) {
|
|
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 't':
|
|
tslice = strtol(optarg, NULL, 10);
|
|
opt_t = 1;
|
|
break;
|
|
case 'r':
|
|
ratelimit = strtol(optarg, NULL, 10);
|
|
opt_r = 1;
|
|
break;
|
|
case 's':
|
|
opt_s = 1;
|
|
break;
|
|
case 'p':
|
|
cpupool = optarg;
|
|
break;
|
|
}
|
|
|
|
if ((cpupool || opt_s) && (dom || opt_w || opt_c)) {
|
|
fprintf(stderr, "Specifying a cpupool or schedparam is not "
|
|
"allowed with domain options.\n");
|
|
return 1;
|
|
}
|
|
if (!dom && (opt_w || opt_c)) {
|
|
fprintf(stderr, "Must specify a domain.\n");
|
|
return 1;
|
|
}
|
|
if (!opt_s && (opt_t || opt_r)) {
|
|
fprintf(stderr, "Must specify schedparam to set schedule "
|
|
"parameter values.\n");
|
|
return 1;
|
|
}
|
|
|
|
if (opt_s) {
|
|
libxl_sched_credit_params scparam;
|
|
uint32_t poolid = 0;
|
|
|
|
if (cpupool) {
|
|
if (cpupool_qualifier_to_cpupoolid(cpupool, &poolid, NULL) ||
|
|
!libxl_cpupoolid_is_valid(ctx, poolid)) {
|
|
fprintf(stderr, "unknown cpupool \'%s\'\n", cpupool);
|
|
return -ERROR_FAIL;
|
|
}
|
|
}
|
|
|
|
if (!opt_t && !opt_r) { /* Output scheduling parameters */
|
|
return -sched_credit_pool_output(poolid);
|
|
} else { /* Set scheduling parameters*/
|
|
rc = sched_credit_params_get(poolid, &scparam);
|
|
if (rc)
|
|
return -rc;
|
|
|
|
if (opt_t)
|
|
scparam.tslice_ms = tslice;
|
|
|
|
if (opt_r)
|
|
scparam.ratelimit_us = ratelimit;
|
|
|
|
rc = sched_credit_params_set(poolid, &scparam);
|
|
if (rc)
|
|
return -rc;
|
|
}
|
|
} else if (!dom) { /* list all domain's credit scheduler info */
|
|
return -sched_domain_output(LIBXL_SCHEDULER_CREDIT,
|
|
sched_credit_domain_output,
|
|
sched_credit_pool_output,
|
|
cpupool);
|
|
} else {
|
|
uint32_t domid = find_domain(dom);
|
|
|
|
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 */
|
|
libxl_domain_sched_params scinfo;
|
|
libxl_domain_sched_params_init(&scinfo);
|
|
scinfo.sched = LIBXL_SCHEDULER_CREDIT;
|
|
if (opt_w)
|
|
scinfo.weight = weight;
|
|
if (opt_c)
|
|
scinfo.cap = cap;
|
|
rc = sched_domain_set(domid, &scinfo);
|
|
libxl_domain_sched_params_dispose(&scinfo);
|
|
if (rc)
|
|
return -rc;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main_sched_credit2(int argc, char **argv)
|
|
{
|
|
const char *dom = NULL;
|
|
const char *cpupool = NULL;
|
|
int weight = 256, opt_w = 0;
|
|
int opt, rc;
|
|
static struct option opts[] = {
|
|
{"domain", 1, 0, 'd'},
|
|
{"weight", 1, 0, 'w'},
|
|
{"cpupool", 1, 0, 'p'},
|
|
COMMON_LONG_OPTS,
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
SWITCH_FOREACH_OPT(opt, "d:w:p:h", opts, "sched-credit2", 0) {
|
|
case 'd':
|
|
dom = optarg;
|
|
break;
|
|
case 'w':
|
|
weight = strtol(optarg, NULL, 10);
|
|
opt_w = 1;
|
|
break;
|
|
case 'p':
|
|
cpupool = optarg;
|
|
break;
|
|
}
|
|
|
|
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(LIBXL_SCHEDULER_CREDIT2,
|
|
sched_credit2_domain_output,
|
|
sched_default_pool_output,
|
|
cpupool);
|
|
} else {
|
|
uint32_t domid = find_domain(dom);
|
|
|
|
if (!opt_w) { /* output credit2 scheduler info */
|
|
sched_credit2_domain_output(-1);
|
|
return -sched_credit2_domain_output(domid);
|
|
} else { /* set credit2 scheduler paramaters */
|
|
libxl_domain_sched_params scinfo;
|
|
libxl_domain_sched_params_init(&scinfo);
|
|
scinfo.sched = LIBXL_SCHEDULER_CREDIT2;
|
|
if (opt_w)
|
|
scinfo.weight = weight;
|
|
rc = sched_domain_set(domid, &scinfo);
|
|
libxl_domain_sched_params_dispose(&scinfo);
|
|
if (rc)
|
|
return -rc;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main_sched_sedf(int argc, char **argv)
|
|
{
|
|
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;
|
|
static struct option opts[] = {
|
|
{"period", 1, 0, 'p'},
|
|
{"slice", 1, 0, 's'},
|
|
{"latency", 1, 0, 'l'},
|
|
{"extra", 1, 0, 'e'},
|
|
{"weight", 1, 0, 'w'},
|
|
{"cpupool", 1, 0, 'c'},
|
|
COMMON_LONG_OPTS,
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
SWITCH_FOREACH_OPT(opt, "d:p:s:l:e:w:c:h", opts, "sched-sedf", 0) {
|
|
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;
|
|
}
|
|
|
|
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(LIBXL_SCHEDULER_SEDF,
|
|
sched_sedf_domain_output,
|
|
sched_default_pool_output,
|
|
cpupool);
|
|
} else {
|
|
uint32_t domid = find_domain(dom);
|
|
|
|
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 */
|
|
libxl_domain_sched_params scinfo;
|
|
libxl_domain_sched_params_init(&scinfo);
|
|
scinfo.sched = LIBXL_SCHEDULER_SEDF;
|
|
|
|
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_domain_set(domid, &scinfo);
|
|
libxl_domain_sched_params_dispose(&scinfo);
|
|
if (rc)
|
|
return -rc;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main_domid(int argc, char **argv)
|
|
{
|
|
uint32_t domid;
|
|
int opt;
|
|
const char *domname = NULL;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "domid", 1) {
|
|
/* No options */
|
|
}
|
|
|
|
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)
|
|
{
|
|
uint32_t domid;
|
|
int opt;
|
|
char *domname = NULL;
|
|
char *endptr = NULL;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "domname", 1) {
|
|
/* No options */
|
|
}
|
|
|
|
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)
|
|
{
|
|
uint32_t domid;
|
|
int opt;
|
|
const char *dom, *new_name;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "rename", 2) {
|
|
/* No options */
|
|
}
|
|
|
|
dom = argv[optind++];
|
|
new_name = argv[optind];
|
|
|
|
domid = find_domain(dom);
|
|
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)
|
|
{
|
|
uint32_t domid;
|
|
int opt;
|
|
char *endptr = NULL;
|
|
int vcpuid = 0;
|
|
const char *trigger_name = NULL;
|
|
libxl_trigger trigger;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "trigger", 2) {
|
|
/* No options */
|
|
}
|
|
|
|
domid = find_domain(argv[optind++]);
|
|
|
|
trigger_name = argv[optind++];
|
|
if (libxl_trigger_from_string(trigger_name, &trigger)) {
|
|
fprintf(stderr, "Invalid trigger \"%s\"\n", trigger_name);
|
|
return -1;
|
|
}
|
|
|
|
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, vcpuid);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int main_sysrq(int argc, char **argv)
|
|
{
|
|
uint32_t domid;
|
|
int opt;
|
|
const char *sysrq = NULL;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "sysrq", 2) {
|
|
/* No options */
|
|
}
|
|
|
|
domid = find_domain(argv[optind++]);
|
|
|
|
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;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "debug-keys", 1) {
|
|
/* No options */
|
|
}
|
|
|
|
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;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "c", NULL, "dmesg", 0) {
|
|
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:
|
|
if (cr)
|
|
libxl_xen_console_read_finish(ctx, cr);
|
|
return ret;
|
|
}
|
|
|
|
int main_top(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "top", 0) {
|
|
/* No options */
|
|
}
|
|
|
|
return system("xentop");
|
|
}
|
|
|
|
int main_networkattach(int argc, char **argv)
|
|
{
|
|
uint32_t domid;
|
|
int opt;
|
|
libxl_device_nic nic;
|
|
XLU_Config *config = 0;
|
|
char *endptr, *oparg;
|
|
const char *tok;
|
|
int i;
|
|
unsigned int val;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "network-attach", 1) {
|
|
/* No options */
|
|
}
|
|
|
|
if (argc-optind > 11) {
|
|
help("network-attach");
|
|
return 0;
|
|
}
|
|
|
|
domid = find_domain(argv[optind]);
|
|
|
|
config= xlu_cfg_init(stderr, "command line");
|
|
if (!config) {
|
|
fprintf(stderr, "Failed to allocate for configuration\n");
|
|
return 1;
|
|
}
|
|
|
|
libxl_device_nic_init(&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_VIF_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("netdev", *argv, oparg)) {
|
|
fprintf(stderr, "the netdev parameter is deprecated, "
|
|
"please use gatewaydev instead\n");
|
|
replace_string(&nic.gatewaydev, oparg);
|
|
} else if (MATCH_OPTION("gatewaydev", *argv, oparg)) {
|
|
replace_string(&nic.gatewaydev, 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)) {
|
|
replace_string(&nic.backend_domname, oparg);
|
|
} 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)) {
|
|
parse_vif_rate(&config, oparg, &nic);
|
|
} else if (MATCH_OPTION("accel", *argv, oparg)) {
|
|
} else {
|
|
fprintf(stderr, "unrecognized argument `%s'\n", *argv);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (dryrun_only) {
|
|
char *json = libxl_device_nic_to_json(ctx, &nic);
|
|
printf("vif: %s\n", json);
|
|
free(json);
|
|
libxl_device_nic_dispose(&nic);
|
|
if (ferror(stdout) || fflush(stdout)) { perror("stdout"); exit(-1); }
|
|
return 0;
|
|
}
|
|
|
|
if (libxl_device_nic_add(ctx, domid, &nic, 0)) {
|
|
fprintf(stderr, "libxl_device_nic_add failed.\n");
|
|
return 1;
|
|
}
|
|
libxl_device_nic_dispose(&nic);
|
|
xlu_cfg_destroy(config);
|
|
return 0;
|
|
}
|
|
|
|
int main_networklist(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
libxl_device_nic *nics;
|
|
libxl_nicinfo nicinfo;
|
|
int nb, i;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "network-list", 1) {
|
|
/* No options */
|
|
}
|
|
|
|
/* 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) {
|
|
uint32_t domid = find_domain(*argv);
|
|
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)
|
|
{
|
|
uint32_t domid;
|
|
int opt;
|
|
libxl_device_nic nic;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "network-detach", 2) {
|
|
/* No options */
|
|
}
|
|
|
|
domid = find_domain(argv[optind]);
|
|
|
|
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, 0)) {
|
|
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;
|
|
libxl_device_disk disk;
|
|
XLU_Config *config = 0;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "block-attach", 2) {
|
|
/* No options */
|
|
}
|
|
|
|
if (libxl_domain_qualifier_to_domid(ctx, argv[optind], &fe_domid) < 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);
|
|
|
|
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, 0)) {
|
|
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;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "block-list", 1) {
|
|
/* No options */
|
|
}
|
|
|
|
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) {
|
|
uint32_t domid;
|
|
if (libxl_domain_qualifier_to_domid(ctx, *argv, &domid) < 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)
|
|
{
|
|
uint32_t domid;
|
|
int opt, rc = 0;
|
|
libxl_device_disk disk;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "block-detach", 2) {
|
|
/* No options */
|
|
}
|
|
|
|
domid = find_domain(argv[optind]);
|
|
|
|
if (libxl_vdev_to_device_disk(ctx, domid, argv[optind+1], &disk)) {
|
|
fprintf(stderr, "Error: Device %s not connected.\n", argv[optind+1]);
|
|
return 1;
|
|
}
|
|
rc = libxl_device_disk_remove(ctx, domid, &disk, 0);
|
|
if (rc) {
|
|
fprintf(stderr, "libxl_device_disk_remove failed.\n");
|
|
}
|
|
libxl_device_disk_dispose(&disk);
|
|
return rc;
|
|
}
|
|
|
|
int main_vtpmattach(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
libxl_device_vtpm vtpm;
|
|
char *oparg;
|
|
uint32_t domid;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "vtpm-attach", 1) {
|
|
/* No options */
|
|
}
|
|
|
|
if (libxl_domain_qualifier_to_domid(ctx, argv[optind], &domid) < 0) {
|
|
fprintf(stderr, "%s is an invalid domain identifier\n", argv[optind]);
|
|
return 1;
|
|
}
|
|
++optind;
|
|
|
|
libxl_device_vtpm_init(&vtpm);
|
|
for (argv += optind, argc -= optind; argc > 0; ++argv, --argc) {
|
|
if (MATCH_OPTION("uuid", *argv, oparg)) {
|
|
if(libxl_uuid_from_string(&(vtpm.uuid), oparg)) {
|
|
fprintf(stderr, "Invalid uuid specified (%s)\n", oparg);
|
|
return 1;
|
|
}
|
|
} else if (MATCH_OPTION("backend", *argv, oparg)) {
|
|
replace_string(&vtpm.backend_domname, oparg);
|
|
} else {
|
|
fprintf(stderr, "unrecognized argument `%s'\n", *argv);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if(dryrun_only) {
|
|
char* json = libxl_device_vtpm_to_json(ctx, &vtpm);
|
|
printf("vtpm: %s\n", json);
|
|
free(json);
|
|
libxl_device_vtpm_dispose(&vtpm);
|
|
if (ferror(stdout) || fflush(stdout)) { perror("stdout"); exit(-1); }
|
|
return 0;
|
|
}
|
|
|
|
if (libxl_device_vtpm_add(ctx, domid, &vtpm, 0)) {
|
|
fprintf(stderr, "libxl_device_vtpm_add failed.\n");
|
|
return 1;
|
|
}
|
|
libxl_device_vtpm_dispose(&vtpm);
|
|
return 0;
|
|
}
|
|
|
|
int main_vtpmlist(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
libxl_device_vtpm *vtpms;
|
|
libxl_vtpminfo vtpminfo;
|
|
int nb, i;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "vtpm-list", 1) {
|
|
/* No options */
|
|
}
|
|
|
|
/* Idx BE UUID Hdl Sta evch rref BE-path */
|
|
printf("%-3s %-2s %-36s %-6s %-5s %-6s %-5s %-10s\n",
|
|
"Idx", "BE", "Uuid", "handle", "state", "evt-ch", "ring-ref", "BE-path");
|
|
for (argv += optind, argc -= optind; argc > 0; --argc, ++argv) {
|
|
uint32_t domid;
|
|
if (libxl_domain_qualifier_to_domid(ctx, *argv, &domid) < 0) {
|
|
fprintf(stderr, "%s is an invalid domain identifier\n", *argv);
|
|
continue;
|
|
}
|
|
if (!(vtpms = libxl_device_vtpm_list(ctx, domid, &nb))) {
|
|
continue;
|
|
}
|
|
for (i = 0; i < nb; ++i) {
|
|
if(!libxl_device_vtpm_getinfo(ctx, domid, &vtpms[i], &vtpminfo)) {
|
|
/* Idx BE UUID Hdl Sta evch rref BE-path*/
|
|
printf("%-3d %-2d " LIBXL_UUID_FMT " %6d %5d %6d %8d %-30s\n",
|
|
vtpminfo.devid, vtpminfo.backend_id,
|
|
LIBXL_UUID_BYTES(vtpminfo.uuid),
|
|
vtpminfo.devid, vtpminfo.state, vtpminfo.evtch,
|
|
vtpminfo.rref, vtpminfo.backend);
|
|
|
|
libxl_vtpminfo_dispose(&vtpminfo);
|
|
}
|
|
libxl_device_vtpm_dispose(&vtpms[i]);
|
|
}
|
|
free(vtpms);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int main_vtpmdetach(int argc, char **argv)
|
|
{
|
|
uint32_t domid;
|
|
int opt, rc=0;
|
|
libxl_device_vtpm vtpm;
|
|
libxl_uuid uuid;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "vtpm-detach", 2) {
|
|
/* No options */
|
|
}
|
|
|
|
domid = find_domain(argv[optind]);
|
|
|
|
if ( libxl_uuid_from_string(&uuid, argv[optind+1])) {
|
|
if (libxl_devid_to_device_vtpm(ctx, domid, atoi(argv[optind+1]), &vtpm)) {
|
|
fprintf(stderr, "Unknown device %s.\n", argv[optind+1]);
|
|
return 1;
|
|
}
|
|
} else {
|
|
if (libxl_uuid_to_device_vtpm(ctx, domid, &uuid, &vtpm)) {
|
|
fprintf(stderr, "Unknown device %s.\n", argv[optind+1]);
|
|
return 1;
|
|
}
|
|
}
|
|
rc = libxl_device_vtpm_remove(ctx, domid, &vtpm, 0);
|
|
if (rc) {
|
|
fprintf(stderr, "libxl_device_vtpm_remove failed.\n");
|
|
}
|
|
libxl_device_vtpm_dispose(&vtpm);
|
|
return rc;
|
|
}
|
|
|
|
|
|
static char *uptime_to_string(unsigned long uptime, int short_mode)
|
|
{
|
|
int sec, min, hour, day;
|
|
char *time_string;
|
|
int ret;
|
|
|
|
day = (int)(uptime / 86400);
|
|
uptime -= (day * 86400);
|
|
hour = (int)(uptime / 3600);
|
|
uptime -= (hour * 3600);
|
|
min = (int)(uptime / 60);
|
|
uptime -= (min * 60);
|
|
sec = uptime;
|
|
|
|
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;
|
|
}
|
|
|
|
int main_claims(int argc, char **argv)
|
|
{
|
|
libxl_dominfo *info;
|
|
int opt;
|
|
int nb_domain;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "claims", 0) {
|
|
/* No options */
|
|
}
|
|
|
|
if (!claim_mode)
|
|
fprintf(stderr, "claim_mode not enabled (see man xl.conf).\n");
|
|
|
|
info = libxl_list_domain(ctx, &nb_domain);
|
|
if (!info) {
|
|
fprintf(stderr, "libxl_list_domain failed.\n");
|
|
return 1;
|
|
}
|
|
|
|
list_domains(0 /* verbose */, 0 /* context */, 1 /* claim */,
|
|
0 /* numa */, info, nb_domain);
|
|
|
|
libxl_dominfo_list_free(info, nb_domain);
|
|
return 0;
|
|
}
|
|
|
|
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);
|
|
libxl_vminfo_list_free(info, nb_vm);
|
|
} 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;
|
|
int short_mode = 0;
|
|
uint32_t domains[100];
|
|
int nb_doms = 0;
|
|
int opt;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "s", NULL, "uptime", 1) {
|
|
case 's':
|
|
short_mode = 1;
|
|
break;
|
|
}
|
|
|
|
for (;(dom = argv[optind]) != NULL; nb_doms++,optind++)
|
|
domains[nb_doms] = find_domain(dom);
|
|
|
|
print_uptime(short_mode, domains, nb_doms);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main_tmem_list(int argc, char **argv)
|
|
{
|
|
uint32_t domid;
|
|
const char *dom = NULL;
|
|
char *buf = NULL;
|
|
int use_long = 0;
|
|
int all = 0;
|
|
int opt;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "al", NULL, "tmem-list", 0) {
|
|
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 = INVALID_DOMID;
|
|
else
|
|
domid = 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)
|
|
{
|
|
uint32_t domid;
|
|
const char *dom = NULL;
|
|
int all = 0;
|
|
int opt;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "a", NULL, "tmem-freeze", 0) {
|
|
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 = INVALID_DOMID;
|
|
else
|
|
domid = find_domain(dom);
|
|
|
|
libxl_tmem_freeze(ctx, domid);
|
|
return 0;
|
|
}
|
|
|
|
int main_tmem_thaw(int argc, char **argv)
|
|
{
|
|
uint32_t domid;
|
|
const char *dom = NULL;
|
|
int all = 0;
|
|
int opt;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "a", NULL, "tmem-thaw", 0) {
|
|
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 = INVALID_DOMID;
|
|
else
|
|
domid = find_domain(dom);
|
|
|
|
libxl_tmem_thaw(ctx, domid);
|
|
return 0;
|
|
}
|
|
|
|
int main_tmem_set(int argc, char **argv)
|
|
{
|
|
uint32_t domid;
|
|
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;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "aw:c:p:", NULL, "tmem-set", 0) {
|
|
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 = INVALID_DOMID;
|
|
else
|
|
domid = 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)
|
|
{
|
|
uint32_t domid;
|
|
const char *autharg = NULL;
|
|
char *endptr = NULL;
|
|
const char *dom = NULL;
|
|
char *uuid = NULL;
|
|
int auth = -1;
|
|
int all = 0;
|
|
int opt;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "au:A:", NULL, "tmem-shared-auth", 0) {
|
|
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 = INVALID_DOMID;
|
|
else
|
|
domid = 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;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "tmem-freeale", 0) {
|
|
/* No options */
|
|
}
|
|
|
|
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, *config_src=NULL;
|
|
const char *p;
|
|
char extra_config[1024];
|
|
int opt;
|
|
static struct option opts[] = {
|
|
{"defconfig", 1, 0, 'f'},
|
|
{"dryrun", 0, 0, 'n'},
|
|
COMMON_LONG_OPTS,
|
|
{0, 0, 0, 0}
|
|
};
|
|
int ret;
|
|
char *config_data = 0;
|
|
int config_len = 0;
|
|
XLU_Config *config;
|
|
const char *buf;
|
|
const char *name;
|
|
uint32_t poolid;
|
|
libxl_scheduler sched = 0;
|
|
XLU_ConfigList *cpus;
|
|
XLU_ConfigList *nodes;
|
|
int n_cpus, n_nodes, i, n;
|
|
libxl_bitmap freemap;
|
|
libxl_bitmap cpumap;
|
|
libxl_uuid uuid;
|
|
libxl_cputopology *topology;
|
|
int rc = -ERROR_FAIL;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "hnf:", opts, "cpupool-create", 0) {
|
|
case 'f':
|
|
filename = optarg;
|
|
break;
|
|
case 'n':
|
|
dryrun_only = 1;
|
|
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");
|
|
goto out;
|
|
}
|
|
optind++;
|
|
}
|
|
|
|
if (filename)
|
|
{
|
|
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));
|
|
goto out;
|
|
}
|
|
config_src=filename;
|
|
}
|
|
else
|
|
config_src="command line";
|
|
|
|
if (strlen(extra_config)) {
|
|
if (config_len > INT_MAX - (strlen(extra_config) + 2)) {
|
|
fprintf(stderr, "Failed to attach extra configration\n");
|
|
goto out;
|
|
}
|
|
config_data = xrealloc(config_data,
|
|
config_len + strlen(extra_config) + 2);
|
|
if (!config_data) {
|
|
fprintf(stderr, "Failed to realloc config_data\n");
|
|
goto out;
|
|
}
|
|
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, config_src);
|
|
if (!config) {
|
|
fprintf(stderr, "Failed to allocate for configuration\n");
|
|
goto out;
|
|
}
|
|
|
|
ret = xlu_cfg_readdata(config, config_data, config_len);
|
|
if (ret) {
|
|
fprintf(stderr, "Failed to parse config file: %s\n", strerror(ret));
|
|
goto out_cfg;
|
|
}
|
|
|
|
if (!xlu_cfg_get_string (config, "name", &buf, 0))
|
|
name = strdup(buf);
|
|
else if (filename)
|
|
name = libxl_basename(filename);
|
|
else {
|
|
fprintf(stderr, "Missing cpupool name!\n");
|
|
goto out_cfg;
|
|
}
|
|
if (!libxl_name_to_cpupoolid(ctx, name, &poolid)) {
|
|
fprintf(stderr, "Pool name \"%s\" already exists\n", name);
|
|
goto out_cfg;
|
|
}
|
|
|
|
if (!xlu_cfg_get_string (config, "sched", &buf, 0)) {
|
|
if ((libxl_scheduler_from_string(buf, &sched)) < 0) {
|
|
fprintf(stderr, "Unknown scheduler\n");
|
|
goto out_cfg;
|
|
}
|
|
} else {
|
|
if ((sched = libxl_get_scheduler(ctx)) < 0) {
|
|
fprintf(stderr, "get_scheduler sysctl failed.\n");
|
|
goto out_cfg;
|
|
}
|
|
}
|
|
|
|
if (libxl_get_freecpus(ctx, &freemap)) {
|
|
fprintf(stderr, "libxl_get_freecpus failed\n");
|
|
goto out_cfg;
|
|
}
|
|
if (libxl_cpu_bitmap_alloc(ctx, &cpumap, 0)) {
|
|
fprintf(stderr, "Failed to allocate cpumap\n");
|
|
goto out_cfg;
|
|
}
|
|
if (!xlu_cfg_get_list(config, "nodes", &nodes, 0, 0)) {
|
|
int nr;
|
|
n_cpus = 0;
|
|
n_nodes = 0;
|
|
topology = libxl_get_cpu_topology(ctx, &nr);
|
|
if (topology == NULL) {
|
|
fprintf(stderr, "libxl_get_topologyinfo failed\n");
|
|
goto out_cfg;
|
|
}
|
|
while ((buf = xlu_cfg_get_listitem(nodes, n_nodes)) != NULL) {
|
|
n = atoi(buf);
|
|
for (i = 0; i < nr; i++) {
|
|
if ((topology[i].node == n) &&
|
|
libxl_bitmap_test(&freemap, i)) {
|
|
libxl_bitmap_set(&cpumap, i);
|
|
n_cpus++;
|
|
}
|
|
}
|
|
n_nodes++;
|
|
}
|
|
|
|
libxl_cputopology_list_free(topology, nr);
|
|
|
|
if (n_cpus == 0) {
|
|
fprintf(stderr, "no free cpu found\n");
|
|
goto out_cfg;
|
|
}
|
|
} 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_bitmap_test(&freemap, i)) {
|
|
fprintf(stderr, "cpu %d illegal or not free\n", i);
|
|
goto out_cfg;
|
|
}
|
|
libxl_bitmap_set(&cpumap, i);
|
|
n_cpus++;
|
|
}
|
|
} else
|
|
n_cpus = 0;
|
|
|
|
libxl_uuid_generate(&uuid);
|
|
|
|
printf("Using config file \"%s\"\n", config_src);
|
|
printf("cpupool name: %s\n", name);
|
|
printf("scheduler: %s\n", libxl_scheduler_to_string(sched));
|
|
printf("number of cpus: %d\n", n_cpus);
|
|
|
|
if (!dryrun_only) {
|
|
poolid = 0;
|
|
if (libxl_cpupool_create(ctx, name, sched, cpumap, &uuid, &poolid)) {
|
|
fprintf(stderr, "error on creating cpupool\n");
|
|
goto out_cfg;
|
|
}
|
|
}
|
|
/* We made it! */
|
|
rc = 0;
|
|
|
|
out_cfg:
|
|
xlu_cfg_destroy(config);
|
|
out:
|
|
free(config_data);
|
|
return rc;
|
|
}
|
|
|
|
int main_cpupoollist(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
static struct option opts[] = {
|
|
{"cpus", 0, 0, 'c'},
|
|
COMMON_LONG_OPTS,
|
|
{0, 0, 0, 0}
|
|
};
|
|
int opt_cpus = 0;
|
|
const char *pool = NULL;
|
|
libxl_cpupoolinfo *poolinfo;
|
|
int n_pools, p, c, n;
|
|
uint32_t poolid;
|
|
char *name;
|
|
int ret = 0;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "hc", opts, "cpupool-list", 0) {
|
|
case 'c':
|
|
opt_cpus = 1;
|
|
break;
|
|
}
|
|
|
|
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_bit(c, poolinfo[p].cpumap)
|
|
if (libxl_bitmap_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_scheduler_to_string(poolinfo[p].sched),
|
|
poolinfo[p].n_dom);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
libxl_cpupoolinfo_list_free(poolinfo, n_pools);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int main_cpupooldestroy(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
const char *pool;
|
|
uint32_t poolid;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "cpupool-destroy", 1) {
|
|
/* No options */
|
|
}
|
|
|
|
pool = argv[optind];
|
|
|
|
if (cpupool_qualifier_to_cpupoolid(pool, &poolid, NULL) ||
|
|
!libxl_cpupoolid_is_valid(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;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "cpupool-rename", 2) {
|
|
/* No options */
|
|
}
|
|
|
|
pool = argv[optind++];
|
|
|
|
if (cpupool_qualifier_to_cpupoolid(pool, &poolid, NULL) ||
|
|
!libxl_cpupoolid_is_valid(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;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "cpupool-cpu-add", 2) {
|
|
/* No options */
|
|
}
|
|
|
|
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_is_valid(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;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "cpupool-cpu-remove", 2) {
|
|
/* No options */
|
|
}
|
|
|
|
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_is_valid(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;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "cpupool-migrate", 2) {
|
|
/* No options */
|
|
}
|
|
|
|
dom = argv[optind++];
|
|
pool = argv[optind];
|
|
|
|
if (libxl_domain_qualifier_to_domid(ctx, dom, &domid) ||
|
|
!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_is_valid(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;
|
|
libxl_scheduler sched;
|
|
int n_pools;
|
|
int node;
|
|
int n_cpus;
|
|
char name[16];
|
|
libxl_uuid uuid;
|
|
libxl_bitmap cpumap;
|
|
libxl_cpupoolinfo *poolinfo;
|
|
libxl_cputopology *topology;
|
|
libxl_dominfo info;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "", NULL, "cpupool-numa-split", 0) {
|
|
/* No options */
|
|
}
|
|
|
|
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;
|
|
sched = poolinfo[0].sched;
|
|
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;
|
|
}
|
|
|
|
topology = libxl_get_cpu_topology(ctx, &n_cpus);
|
|
if (topology == NULL) {
|
|
fprintf(stderr, "libxl_get_topologyinfo failed\n");
|
|
return -ERROR_FAIL;
|
|
}
|
|
|
|
if (libxl_cpu_bitmap_alloc(ctx, &cpumap, 0)) {
|
|
fprintf(stderr, "Failed to allocate cpumap\n");
|
|
libxl_cputopology_list_free(topology, n_cpus);
|
|
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[0].node;
|
|
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 < n_cpus; c++) {
|
|
if (topology[c].node == node) {
|
|
topology[c].node = LIBXL_CPUTOPOLOGY_INVALID_ENTRY;
|
|
libxl_bitmap_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;
|
|
}
|
|
libxl_bitmap_set_none(&cpumap);
|
|
|
|
for (c = 0; c < n_cpus; c++) {
|
|
if (topology[c].node == LIBXL_CPUTOPOLOGY_INVALID_ENTRY) {
|
|
continue;
|
|
}
|
|
|
|
node = topology[c].node;
|
|
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_cpupool_create(ctx, name, sched, 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 < n_cpus; p++) {
|
|
if (topology[p].node == node) {
|
|
topology[p].node = LIBXL_CPUTOPOLOGY_INVALID_ENTRY;
|
|
}
|
|
}
|
|
}
|
|
|
|
out:
|
|
libxl_cputopology_list_free(topology, n_cpus);
|
|
libxl_bitmap_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;
|
|
}
|
|
|
|
int main_remus(int argc, char **argv)
|
|
{
|
|
uint32_t domid;
|
|
int opt, rc, daemonize = 1;
|
|
const char *ssh_command = "ssh";
|
|
char *host = NULL, *rune = NULL;
|
|
libxl_domain_remus_info r_info;
|
|
int send_fd = -1, recv_fd = -1;
|
|
pid_t child = -1;
|
|
uint8_t *config_data;
|
|
int config_len;
|
|
|
|
memset(&r_info, 0, sizeof(libxl_domain_remus_info));
|
|
/* Defaults */
|
|
r_info.interval = 200;
|
|
r_info.blackhole = 0;
|
|
r_info.compression = 1;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "bui:s:e", NULL, "remus", 2) {
|
|
case 'i':
|
|
r_info.interval = atoi(optarg);
|
|
break;
|
|
case 'b':
|
|
r_info.blackhole = 1;
|
|
break;
|
|
case 'u':
|
|
r_info.compression = 0;
|
|
break;
|
|
case 's':
|
|
ssh_command = optarg;
|
|
break;
|
|
case 'e':
|
|
daemonize = 0;
|
|
break;
|
|
}
|
|
|
|
domid = find_domain(argv[optind]);
|
|
host = argv[optind + 1];
|
|
|
|
if (r_info.blackhole) {
|
|
send_fd = open("/dev/null", O_RDWR, 0644);
|
|
if (send_fd < 0) {
|
|
perror("failed to open /dev/null");
|
|
exit(-1);
|
|
}
|
|
} else {
|
|
|
|
if (!ssh_command[0]) {
|
|
rune = host;
|
|
} else {
|
|
if (asprintf(&rune, "exec %s %s xl migrate-receive -r %s",
|
|
ssh_command, host,
|
|
daemonize ? "" : " -e") < 0)
|
|
return 1;
|
|
}
|
|
|
|
save_domain_core_begin(domid, NULL, &config_data, &config_len);
|
|
|
|
if (!config_len) {
|
|
fprintf(stderr, "No config file stored for running domain and "
|
|
"none supplied - cannot start remus.\n");
|
|
exit(1);
|
|
}
|
|
|
|
child = create_migration_child(rune, &send_fd, &recv_fd);
|
|
|
|
migrate_do_preamble(send_fd, recv_fd, child, config_data, config_len,
|
|
rune);
|
|
|
|
if (ssh_command[0])
|
|
free(rune);
|
|
}
|
|
|
|
/* Point of no return */
|
|
rc = libxl_domain_remus_start(ctx, &r_info, domid, send_fd, recv_fd, 0);
|
|
|
|
/* If we are here, it means backup has failed/domain suspend failed.
|
|
* Try to resume the domain and exit gracefully.
|
|
* TODO: Split-Brain check.
|
|
*/
|
|
fprintf(stderr, "remus sender: libxl_domain_suspend failed"
|
|
" (rc=%d)\n", rc);
|
|
|
|
if (rc == ERROR_GUEST_TIMEDOUT)
|
|
fprintf(stderr, "Failed to suspend domain at primary.\n");
|
|
else {
|
|
fprintf(stderr, "Remus: Backup failed? resuming domain at primary.\n");
|
|
libxl_domain_resume(ctx, domid, 1, 0);
|
|
}
|
|
|
|
close(send_fd);
|
|
return -ERROR_FAIL;
|
|
}
|
|
|
|
int main_devd(int argc, char **argv)
|
|
{
|
|
int ret = 0, opt = 0, daemonize = 1;
|
|
|
|
SWITCH_FOREACH_OPT(opt, "F", NULL, "devd", 0) {
|
|
case 'F':
|
|
daemonize = 0;
|
|
break;
|
|
}
|
|
|
|
if (daemonize) {
|
|
ret = do_daemonize("xldevd");
|
|
if (ret) {
|
|
ret = (ret == 1) ? 0 : ret;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
libxl_device_events_handler(ctx, 0);
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Local variables:
|
|
* mode: C
|
|
* c-basic-offset: 4
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*/
|