mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
xen-xsplice: Tool to manipulate xsplice payloads
A simple tool that allows an system admin to perform basic xsplice operations: - Upload a xsplice file (with an unique name) - List all the xsplice payloads loaded. - Apply, revert, replace, or unload the payload using the unique name. - Do all two - upload, and apply the payload in one go (load). Also will use the name of the file as the <name> Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com> Acked-by: Wei Liu <wei.liu2@citrix.com> Release-acked-by: Wei Liu <wei.liu2@citrix.com>
This commit is contained in:
1 parent
7dca2ba85f
commit
05bb8afede
3 files changed
+468
No files matched your search
@@ -182,6 +182,7 @@ tools/misc/xen_cpuperf
|
||||
tools/misc/xen-cpuid
|
||||
tools/misc/xen-detect
|
||||
tools/misc/xen-tmem-list-parse
|
||||
tools/misc/xen-xsplice
|
||||
tools/misc/xenperf
|
||||
tools/misc/xenpm
|
||||
tools/misc/xen-hvmctx
|
||||
|
||||
@@ -32,6 +32,7 @@ INSTALL_SBIN += xenlockprof
|
||||
INSTALL_SBIN += xenperf
|
||||
INSTALL_SBIN += xenpm
|
||||
INSTALL_SBIN += xenwatchdogd
|
||||
INSTALL_SBIN += xen-xsplice
|
||||
INSTALL_SBIN += $(INSTALL_SBIN-y)
|
||||
|
||||
# Everything to be installed in a private bin/
|
||||
@@ -103,6 +104,9 @@ xen-mfndump: xen-mfndump.o
|
||||
xenwatchdogd: xenwatchdogd.o
|
||||
$(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS)
|
||||
|
||||
xen-xsplice: xen-xsplice.o
|
||||
$(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS)
|
||||
|
||||
xen-lowmemd: xen-lowmemd.o
|
||||
$(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenevtchn) $(LDLIBS_libxenctrl) $(LDLIBS_libxenstore) $(APPEND_LDFLAGS)
|
||||
|
||||
|
||||
@@ -0,0 +1,463 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <xenctrl.h>
|
||||
#include <xenstore.h>
|
||||
|
||||
static xc_interface *xch;
|
||||
|
||||
void show_help(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"xen-xsplice: Xsplice test tool\n"
|
||||
"Usage: xen-xsplice <command> [args]\n"
|
||||
" <name> An unique name of payload. Up to %d characters.\n"
|
||||
"Commands:\n"
|
||||
" help display this help\n"
|
||||
" upload <name> <file> upload file <file> with <name> name\n"
|
||||
" list list payloads uploaded.\n"
|
||||
" apply <name> apply <name> patch.\n"
|
||||
" revert <name> revert name <name> patch.\n"
|
||||
" replace <name> apply <name> patch and revert all others.\n"
|
||||
" unload <name> unload name <name> patch.\n"
|
||||
" load <file> upload and apply <file>.\n"
|
||||
" name is the <file> name\n",
|
||||
XEN_XSPLICE_NAME_SIZE);
|
||||
}
|
||||
|
||||
/* wrapper function */
|
||||
static int help_func(int argc, char *argv[])
|
||||
{
|
||||
show_help();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
|
||||
|
||||
static const char *state2str(unsigned int state)
|
||||
{
|
||||
#define STATE(x) [XSPLICE_STATE_##x] = #x
|
||||
static const char *const names[] = {
|
||||
STATE(CHECKED),
|
||||
STATE(APPLIED),
|
||||
};
|
||||
#undef STATE
|
||||
if (state >= ARRAY_SIZE(names) || !names[state])
|
||||
return "unknown";
|
||||
|
||||
return names[state];
|
||||
}
|
||||
|
||||
/* This value was choosen adhoc. It could be 42 too. */
|
||||
#define MAX_LEN 11
|
||||
static int list_func(int argc, char *argv[])
|
||||
{
|
||||
unsigned int idx, done, left, i;
|
||||
xen_xsplice_status_t *info = NULL;
|
||||
char *name = NULL;
|
||||
uint32_t *len = NULL;
|
||||
int rc = ENOMEM;
|
||||
|
||||
if ( argc )
|
||||
{
|
||||
show_help();
|
||||
return -1;
|
||||
}
|
||||
idx = left = 0;
|
||||
info = malloc(sizeof(*info) * MAX_LEN);
|
||||
if ( !info )
|
||||
return rc;
|
||||
name = malloc(sizeof(*name) * XEN_XSPLICE_NAME_SIZE * MAX_LEN);
|
||||
if ( !name )
|
||||
{
|
||||
free(info);
|
||||
return rc;
|
||||
}
|
||||
len = malloc(sizeof(*len) * MAX_LEN);
|
||||
if ( !len ) {
|
||||
free(name);
|
||||
free(info);
|
||||
return rc;
|
||||
}
|
||||
|
||||
fprintf(stdout," ID | status\n"
|
||||
"----------------------------------------+------------\n");
|
||||
do {
|
||||
done = 0;
|
||||
/* The memset is done to catch errors. */
|
||||
memset(info, 'A', sizeof(*info) * MAX_LEN);
|
||||
memset(name, 'B', sizeof(*name * MAX_LEN * XEN_XSPLICE_NAME_SIZE));
|
||||
memset(len, 'C', sizeof(*len) * MAX_LEN);
|
||||
rc = xc_xsplice_list(xch, MAX_LEN, idx, info, name, len, &done, &left);
|
||||
if ( rc )
|
||||
{
|
||||
fprintf(stderr, "Failed to list %d/%d: %d(%s)!\n",
|
||||
idx, left, errno, strerror(errno));
|
||||
break;
|
||||
}
|
||||
for ( i = 0; i < done; i++ )
|
||||
{
|
||||
unsigned int j;
|
||||
uint32_t sz;
|
||||
char *str;
|
||||
|
||||
sz = len[i];
|
||||
str = name + (i * XEN_XSPLICE_NAME_SIZE);
|
||||
for ( j = sz; j < XEN_XSPLICE_NAME_SIZE; j++ )
|
||||
str[j] = '\0';
|
||||
|
||||
printf("%-40s| %s", str, state2str(info[i].state));
|
||||
if ( info[i].rc )
|
||||
printf(" (%d, %s)\n", -info[i].rc, strerror(-info[i].rc));
|
||||
else
|
||||
puts("");
|
||||
}
|
||||
idx += done;
|
||||
} while ( left );
|
||||
|
||||
free(name);
|
||||
free(info);
|
||||
free(len);
|
||||
return rc;
|
||||
}
|
||||
#undef MAX_LEN
|
||||
|
||||
static int get_name(int argc, char *argv[], char *name)
|
||||
{
|
||||
ssize_t len = strlen(argv[0]);
|
||||
if ( len > XEN_XSPLICE_NAME_SIZE )
|
||||
{
|
||||
fprintf(stderr, "ID MUST be %d characters!\n", XEN_XSPLICE_NAME_SIZE);
|
||||
errno = EINVAL;
|
||||
return errno;
|
||||
}
|
||||
/* Don't want any funny strings from the stack. */
|
||||
memset(name, 0, XEN_XSPLICE_NAME_SIZE);
|
||||
strncpy(name, argv[0], len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int upload_func(int argc, char *argv[])
|
||||
{
|
||||
char *filename;
|
||||
char name[XEN_XSPLICE_NAME_SIZE];
|
||||
int fd = 0, rc;
|
||||
struct stat buf;
|
||||
unsigned char *fbuf;
|
||||
ssize_t len;
|
||||
|
||||
if ( argc != 2 )
|
||||
{
|
||||
show_help();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( get_name(argc, argv, name) )
|
||||
return EINVAL;
|
||||
|
||||
filename = argv[1];
|
||||
fd = open(filename, O_RDONLY);
|
||||
if ( fd < 0 )
|
||||
{
|
||||
fprintf(stderr, "Could not open %s, error: %d(%s)\n",
|
||||
filename, errno, strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
if ( stat(filename, &buf) != 0 )
|
||||
{
|
||||
fprintf(stderr, "Could not get right size %s, error: %d(%s)\n",
|
||||
filename, errno, strerror(errno));
|
||||
close(fd);
|
||||
return errno;
|
||||
}
|
||||
|
||||
len = buf.st_size;
|
||||
fbuf = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if ( fbuf == MAP_FAILED )
|
||||
{
|
||||
fprintf(stderr,"Could not map: %s, error: %d(%s)\n",
|
||||
filename, errno, strerror(errno));
|
||||
close (fd);
|
||||
return errno;
|
||||
}
|
||||
printf("Uploading %s (%zu bytes)\n", filename, len);
|
||||
rc = xc_xsplice_upload(xch, name, fbuf, len);
|
||||
if ( rc )
|
||||
fprintf(stderr, "Upload failed: %s, error: %d(%s)!\n",
|
||||
filename, errno, strerror(errno));
|
||||
|
||||
if ( munmap( fbuf, len) )
|
||||
{
|
||||
fprintf(stderr, "Could not unmap!? error: %d(%s)!\n",
|
||||
errno, strerror(errno));
|
||||
if ( !rc )
|
||||
rc = errno;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* These MUST match to the 'action_options[]' array slots. */
|
||||
enum {
|
||||
ACTION_APPLY = 0,
|
||||
ACTION_REVERT = 1,
|
||||
ACTION_UNLOAD = 2,
|
||||
ACTION_REPLACE = 3,
|
||||
};
|
||||
|
||||
struct {
|
||||
int allow; /* State it must be in to call function. */
|
||||
int expected; /* The state to be in after the function. */
|
||||
const char *name;
|
||||
int (*function)(xc_interface *xch, char *name, uint32_t timeout);
|
||||
unsigned int executed; /* Has the function been called?. */
|
||||
} action_options[] = {
|
||||
{ .allow = XSPLICE_STATE_CHECKED,
|
||||
.expected = XSPLICE_STATE_APPLIED,
|
||||
.name = "apply",
|
||||
.function = xc_xsplice_apply,
|
||||
},
|
||||
{ .allow = XSPLICE_STATE_APPLIED,
|
||||
.expected = XSPLICE_STATE_CHECKED,
|
||||
.name = "revert",
|
||||
.function = xc_xsplice_revert,
|
||||
},
|
||||
{ .allow = XSPLICE_STATE_CHECKED,
|
||||
.expected = -ENOENT,
|
||||
.name = "unload",
|
||||
.function = xc_xsplice_unload,
|
||||
},
|
||||
{ .allow = XSPLICE_STATE_CHECKED,
|
||||
.expected = XSPLICE_STATE_APPLIED,
|
||||
.name = "replace",
|
||||
.function = xc_xsplice_replace,
|
||||
},
|
||||
};
|
||||
|
||||
/* Go around 300 * 0.1 seconds = 30 seconds. */
|
||||
#define RETRIES 300
|
||||
/* aka 0.1 second */
|
||||
#define DELAY 100000
|
||||
|
||||
int action_func(int argc, char *argv[], unsigned int idx)
|
||||
{
|
||||
char name[XEN_XSPLICE_NAME_SIZE];
|
||||
int rc, original_state;
|
||||
xen_xsplice_status_t status;
|
||||
unsigned int retry = 0;
|
||||
|
||||
if ( argc != 1 )
|
||||
{
|
||||
show_help();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( idx >= ARRAY_SIZE(action_options) )
|
||||
return -1;
|
||||
|
||||
if ( get_name(argc, argv, name) )
|
||||
return EINVAL;
|
||||
|
||||
/* Check initial status. */
|
||||
rc = xc_xsplice_get(xch, name, &status);
|
||||
if ( rc )
|
||||
{
|
||||
fprintf(stderr, "%s failed to get status (rc=%d, %s)!\n",
|
||||
name, -rc, strerror(-rc));
|
||||
return -1;
|
||||
}
|
||||
if ( status.rc == -EAGAIN )
|
||||
{
|
||||
fprintf(stderr, "%s failed. Operation already in progress\n", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( status.state == action_options[idx].expected )
|
||||
{
|
||||
printf("No action needed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Perform action. */
|
||||
if ( action_options[idx].allow & status.state )
|
||||
{
|
||||
printf("Performing %s:", action_options[idx].name);
|
||||
rc = action_options[idx].function(xch, name, 0);
|
||||
if ( rc )
|
||||
{
|
||||
fprintf(stderr, "%s failed with %d(%s)\n", name, -rc, strerror(-rc));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%s: in wrong state (%s), expected (%s)\n",
|
||||
name, state2str(status.state),
|
||||
state2str(action_options[idx].expected));
|
||||
return -1;
|
||||
}
|
||||
|
||||
original_state = status.state;
|
||||
do {
|
||||
rc = xc_xsplice_get(xch, name, &status);
|
||||
if ( rc )
|
||||
{
|
||||
rc = -errno;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( status.state != original_state )
|
||||
break;
|
||||
if ( status.rc && status.rc != -EAGAIN )
|
||||
{
|
||||
rc = status.rc;
|
||||
break;
|
||||
}
|
||||
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
usleep(DELAY);
|
||||
} while ( ++retry < RETRIES );
|
||||
|
||||
if ( retry >= RETRIES )
|
||||
{
|
||||
fprintf(stderr, "%s: Operation didn't complete after 30 seconds.\n", name);
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( rc == 0 )
|
||||
rc = status.state;
|
||||
|
||||
if ( action_options[idx].expected == rc )
|
||||
printf(" completed\n");
|
||||
else if ( rc < 0 )
|
||||
{
|
||||
fprintf(stderr, "%s failed with %d(%s)\n", name, -rc, strerror(-rc));
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: in wrong state (%s), expected (%s)\n",
|
||||
name, state2str(rc),
|
||||
state2str(action_options[idx].expected));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_func(int argc, char *argv[])
|
||||
{
|
||||
int rc;
|
||||
char *new_argv[2];
|
||||
char *path, *name, *lastdot;
|
||||
|
||||
if ( argc != 1 )
|
||||
{
|
||||
show_help();
|
||||
return -1;
|
||||
}
|
||||
/* <file> */
|
||||
new_argv[1] = argv[0];
|
||||
|
||||
/* Synthesize the <id> */
|
||||
path = strdup(argv[0]);
|
||||
|
||||
name = basename(path);
|
||||
lastdot = strrchr(name, '.');
|
||||
if ( lastdot != NULL )
|
||||
*lastdot = '\0';
|
||||
new_argv[0] = name;
|
||||
|
||||
rc = upload_func(2 /* <id> <file> */, new_argv);
|
||||
if ( rc )
|
||||
return rc;
|
||||
|
||||
rc = action_func(1 /* only <id> */, new_argv, ACTION_APPLY);
|
||||
if ( rc )
|
||||
action_func(1, new_argv, ACTION_UNLOAD);
|
||||
|
||||
free(path);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* These are also functions in action_options that are called in case
|
||||
* none of the ones in main_options match.
|
||||
*/
|
||||
struct {
|
||||
const char *name;
|
||||
int (*function)(int argc, char *argv[]);
|
||||
} main_options[] = {
|
||||
{ "help", help_func },
|
||||
{ "list", list_func },
|
||||
{ "upload", upload_func },
|
||||
{ "load", load_func },
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i, j, ret;
|
||||
|
||||
if ( argc <= 1 )
|
||||
{
|
||||
show_help();
|
||||
return 0;
|
||||
}
|
||||
for ( i = 0; i < ARRAY_SIZE(main_options); i++ )
|
||||
if (!strncmp(main_options[i].name, argv[1], strlen(argv[1])))
|
||||
break;
|
||||
|
||||
if ( i == ARRAY_SIZE(main_options) )
|
||||
{
|
||||
for ( j = 0; j < ARRAY_SIZE(action_options); j++ )
|
||||
if (!strncmp(action_options[j].name, argv[1], strlen(argv[1])))
|
||||
break;
|
||||
|
||||
if ( j == ARRAY_SIZE(action_options) )
|
||||
{
|
||||
fprintf(stderr, "Unrecognised command '%s' -- try "
|
||||
"'xen-xsplice help'\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
} else
|
||||
j = ARRAY_SIZE(action_options);
|
||||
|
||||
xch = xc_interface_open(0,0,0);
|
||||
if ( !xch )
|
||||
{
|
||||
fprintf(stderr, "failed to get the handler\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( i == ARRAY_SIZE(main_options) )
|
||||
ret = action_func(argc -2, argv + 2, j);
|
||||
else
|
||||
ret = main_options[i].function(argc -2, argv + 2);
|
||||
|
||||
xc_interface_close(xch);
|
||||
|
||||
return !!ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "BSD"
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||