mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
In order to better reflect the contents of the header and to make it more appropriate to use it for different runtime environments like programs, libraries, and firmware, rename the libs.h include file to common-macros.h. Additionally add a comment pointing out the need to be self-contained. Suggested-by: Andrew Cooper <andrew.cooper3@citrix.com> Signed-off-by: Juergen Gross <jgross@suse.com> Acked-by: Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com> # tools/python/xen/lowlevel/xc/xc.c Acked-by: Christian Lindig <christian.lindig@cloud.com> Acked-by: Anthony PERARD <anthony.perard@citrix.com> Acked-by: Jan Beulich <jbeulich@suse.com>
639 lines
16 KiB
C
639 lines
16 KiB
C
/*
|
|
* 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 <time.h>
|
|
#include <unistd.h>
|
|
#include <xenctrl.h>
|
|
#include <xenstore.h>
|
|
|
|
#include <xen/errno.h>
|
|
#include <xen-tools/common-macros.h>
|
|
|
|
static xc_interface *xch;
|
|
|
|
void show_help(void)
|
|
{
|
|
fprintf(stderr,
|
|
"xen-livepatch: live patching tool\n"
|
|
"Usage: xen-livepatch <command> [args] [command-flags]\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> [flags] apply <name> patch.\n"
|
|
" Supported flags:\n"
|
|
" --nodeps Disable inter-module buildid dependency check.\n"
|
|
" Check only against hypervisor buildid.\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> [flags] upload and apply <file> with name as the <file> name\n"
|
|
" Supported flags:\n"
|
|
" --nodeps Disable inter-module buildid dependency check.\n"
|
|
" Check only against hypervisor buildid.\n",
|
|
XEN_LIVEPATCH_NAME_SIZE);
|
|
}
|
|
|
|
/* wrapper function */
|
|
static int help_func(int argc, char *argv[])
|
|
{
|
|
show_help();
|
|
return 0;
|
|
}
|
|
|
|
static const char *state2str(unsigned int state)
|
|
{
|
|
#define STATE(x) [LIVEPATCH_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];
|
|
}
|
|
|
|
static int list_func(int argc, char *argv[])
|
|
{
|
|
unsigned int nr, done, left, i;
|
|
xen_livepatch_status_t *info = NULL;
|
|
char *name = NULL;
|
|
char *metadata = NULL;
|
|
uint32_t *len = NULL;
|
|
uint32_t *metadata_len = NULL;
|
|
uint32_t name_total_size, metadata_total_size, name_off, metadata_off;
|
|
int rc = ENOMEM;
|
|
|
|
if ( argc )
|
|
{
|
|
show_help();
|
|
return -1;
|
|
}
|
|
done = left = 0;
|
|
|
|
rc = xc_livepatch_list_get_sizes(xch, &nr, &name_total_size, &metadata_total_size);
|
|
if ( rc )
|
|
{
|
|
rc = errno;
|
|
fprintf(stderr, "Failed to get list sizes.\n"
|
|
"Error %d: %s\n",
|
|
rc, strerror(rc));
|
|
return rc;
|
|
}
|
|
|
|
if ( nr == 0 )
|
|
{
|
|
fprintf(stdout, "Nothing to list\n");
|
|
return 0;
|
|
}
|
|
|
|
info = malloc(nr * sizeof(*info));
|
|
if ( !info )
|
|
return rc;
|
|
|
|
name = malloc(name_total_size * sizeof(*name));
|
|
if ( !name )
|
|
goto error_name;
|
|
|
|
len = malloc(nr * sizeof(*len));
|
|
if ( !len )
|
|
goto error_len;
|
|
|
|
metadata = malloc(metadata_total_size * sizeof(*metadata) + 1);
|
|
if ( !metadata )
|
|
goto error_metadata;
|
|
|
|
metadata_len = malloc(nr * sizeof(*metadata_len));
|
|
if ( !metadata_len )
|
|
goto error_metadata_len;
|
|
|
|
memset(info, 'A', nr * sizeof(*info));
|
|
memset(name, 'B', name_total_size * sizeof(*name));
|
|
memset(len, 'C', nr * sizeof(*len));
|
|
memset(metadata, 'D', metadata_total_size * sizeof(*metadata) + 1);
|
|
memset(metadata_len, 'E', nr * sizeof(*metadata_len));
|
|
name_off = metadata_off = 0;
|
|
|
|
rc = xc_livepatch_list(xch, nr, 0, info, name, len, name_total_size,
|
|
metadata, metadata_len, metadata_total_size, &done, &left);
|
|
if ( rc || done != nr || left > 0)
|
|
{
|
|
rc = errno;
|
|
fprintf(stderr, "Failed to list %d/%d.\n"
|
|
"Error %d: %s\n",
|
|
left, nr, rc, strerror(rc));
|
|
goto error;
|
|
}
|
|
|
|
fprintf(stdout," ID | status | metadata\n"
|
|
"----------------------------------------+------------+---------------\n");
|
|
|
|
for ( i = 0; i < done; i++ )
|
|
{
|
|
unsigned int j;
|
|
char *name_str = name + name_off;
|
|
char *metadata_str = metadata + metadata_off;
|
|
|
|
printf("%-40.*s| %s", len[i], name_str, state2str(info[i].state));
|
|
if ( info[i].rc )
|
|
printf(" (%d, %s) | ", -info[i].rc, strerror(-info[i].rc));
|
|
else
|
|
printf(" | ");
|
|
|
|
/* Replace all '\0' with semi-colons. */
|
|
for ( j = 0; metadata_len[i] && j < metadata_len[i] - 1; j++ )
|
|
metadata_str[j] = (metadata_str[j] ?: ';');
|
|
printf("%.*s\n", metadata_len[i], metadata_str);
|
|
|
|
name_off += len[i];
|
|
metadata_off += metadata_len[i];
|
|
}
|
|
|
|
error:
|
|
free(metadata_len);
|
|
error_metadata_len:
|
|
free(metadata);
|
|
error_metadata:
|
|
free(len);
|
|
error_len:
|
|
free(name);
|
|
error_name:
|
|
free(info);
|
|
return rc;
|
|
}
|
|
#undef MAX_LEN
|
|
|
|
static int get_name(int argc, char *argv[], char *name)
|
|
{
|
|
ssize_t len = strlen(argv[0]);
|
|
if ( len > XEN_LIVEPATCH_NAME_SIZE )
|
|
{
|
|
fprintf(stderr, "ID must be no more than %d characters.\n",
|
|
XEN_LIVEPATCH_NAME_SIZE);
|
|
errno = EINVAL;
|
|
return errno;
|
|
}
|
|
/* Don't want any funny strings from the stack. */
|
|
memset(name, 0, XEN_LIVEPATCH_NAME_SIZE);
|
|
strncpy(name, argv[0], len);
|
|
return 0;
|
|
}
|
|
|
|
static int upload_func(int argc, char *argv[])
|
|
{
|
|
char *filename;
|
|
char name[XEN_LIVEPATCH_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 )
|
|
{
|
|
int saved_errno = errno;
|
|
fprintf(stderr, "Could not open %s.\n"
|
|
"Error %d: %s\n",
|
|
filename, saved_errno, strerror(saved_errno));
|
|
return saved_errno;
|
|
}
|
|
if ( stat(filename, &buf) != 0 )
|
|
{
|
|
int saved_errno = errno;
|
|
fprintf(stderr, "Could not get size of %s.\n"
|
|
"Error %d: %s\n",
|
|
filename, saved_errno, strerror(saved_errno));
|
|
close(fd);
|
|
return saved_errno;
|
|
}
|
|
|
|
len = buf.st_size;
|
|
fbuf = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
if ( fbuf == MAP_FAILED )
|
|
{
|
|
int saved_errno = errno;
|
|
fprintf(stderr, "Could not map %s.\n"
|
|
"Error %d: %s\n",
|
|
filename, saved_errno, strerror(saved_errno));
|
|
close (fd);
|
|
return saved_errno;
|
|
}
|
|
printf("Uploading %s... ", filename);
|
|
rc = xc_livepatch_upload(xch, name, fbuf, len);
|
|
if ( rc )
|
|
{
|
|
rc = errno;
|
|
printf("failed\n");
|
|
fprintf(stderr, "Error %d: %s\n", rc, strerror(rc));
|
|
}
|
|
else
|
|
printf("completed\n");
|
|
|
|
|
|
if ( munmap( fbuf, len) )
|
|
{
|
|
fprintf(stderr, "Could not unmap %s.\n"
|
|
"Error %d: %s\n",
|
|
filename, errno, strerror(errno));
|
|
}
|
|
close(fd);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* These MUST match to the 'action_options[]' and 'flag_options[]' array slots. */
|
|
enum {
|
|
ACTION_APPLY = 0,
|
|
ACTION_REVERT = 1,
|
|
ACTION_UNLOAD = 2,
|
|
ACTION_REPLACE = 3,
|
|
ACTION_NUM
|
|
};
|
|
|
|
struct {
|
|
int allow; /* State it must be in to call function. */
|
|
int expected; /* The state to be in after the function. */
|
|
const char *name;
|
|
const char *verb;
|
|
int (*function)(xc_interface *xch, char *name, uint32_t timeout, uint32_t flags);
|
|
} action_options[] = {
|
|
{ .allow = LIVEPATCH_STATE_CHECKED,
|
|
.expected = LIVEPATCH_STATE_APPLIED,
|
|
.name = "apply",
|
|
.verb = "Applying",
|
|
.function = xc_livepatch_apply,
|
|
},
|
|
{ .allow = LIVEPATCH_STATE_APPLIED,
|
|
.expected = LIVEPATCH_STATE_CHECKED,
|
|
.name = "revert",
|
|
.verb = "Reverting",
|
|
.function = xc_livepatch_revert,
|
|
},
|
|
{ .allow = LIVEPATCH_STATE_CHECKED,
|
|
.expected = -XEN_ENOENT,
|
|
.name = "unload",
|
|
.verb = "Unloading",
|
|
.function = xc_livepatch_unload,
|
|
},
|
|
{ .allow = LIVEPATCH_STATE_CHECKED,
|
|
.expected = LIVEPATCH_STATE_APPLIED,
|
|
.name = "replace",
|
|
.verb = "Replacing all live patches with",
|
|
.function = xc_livepatch_replace,
|
|
},
|
|
};
|
|
|
|
/*
|
|
* This structure defines supported flag options for actions.
|
|
* It defines entries for each action and supports up to 32
|
|
* flags per action.
|
|
*/
|
|
struct {
|
|
const char *name;
|
|
const uint32_t flag;
|
|
} flag_options[ACTION_NUM][8 * sizeof(uint32_t)] = {
|
|
{ /* ACTION_APPLY */
|
|
{ .name = "--nodeps",
|
|
.flag = LIVEPATCH_ACTION_APPLY_NODEPS,
|
|
},
|
|
},
|
|
{ /* ACTION_REVERT */
|
|
},
|
|
{ /* ACTION_UNLOAD */
|
|
},
|
|
{ /* ACTION_REPLACE */
|
|
}
|
|
};
|
|
|
|
/*
|
|
* Parse user provided action flags.
|
|
* This function expects to only receive an array of input parameters being flags.
|
|
* Expected action is specified via idx paramater (index of flag_options[]).
|
|
*/
|
|
static int get_flags(int argc, char *argv[], unsigned int idx, uint32_t *flags)
|
|
{
|
|
int i, j;
|
|
|
|
if ( !flags || idx >= ARRAY_SIZE(flag_options) )
|
|
return -1;
|
|
|
|
*flags = 0;
|
|
for ( i = 0; i < argc; i++ )
|
|
{
|
|
for ( j = 0; j < ARRAY_SIZE(flag_options[idx]); j++ )
|
|
{
|
|
if ( !flag_options[idx][j].name )
|
|
goto error;
|
|
|
|
if ( !strcmp(flag_options[idx][j].name, argv[i]) )
|
|
{
|
|
*flags |= flag_options[idx][j].flag;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( j == ARRAY_SIZE(flag_options[idx]) )
|
|
goto error;
|
|
}
|
|
|
|
return 0;
|
|
error:
|
|
fprintf(stderr, "Unsupported flag: %s.\n", argv[i]);
|
|
errno = EINVAL;
|
|
return errno;
|
|
}
|
|
|
|
/* The hypervisor timeout for the live patching operation is 30 msec,
|
|
* but it could take some time for the operation to start, so wait twice
|
|
* that period. */
|
|
#define HYPERVISOR_TIMEOUT_NS 30000000
|
|
#define DELAY (2 * HYPERVISOR_TIMEOUT_NS)
|
|
|
|
static void nanosleep_retry(long ns)
|
|
{
|
|
struct timespec req, rem;
|
|
int rc;
|
|
|
|
rem.tv_sec = 0;
|
|
rem.tv_nsec = ns;
|
|
|
|
do {
|
|
req = rem;
|
|
rc = nanosleep(&req, &rem);
|
|
} while ( rc == -1 && errno == EINTR );
|
|
}
|
|
|
|
int action_func(int argc, char *argv[], unsigned int idx)
|
|
{
|
|
char name[XEN_LIVEPATCH_NAME_SIZE];
|
|
int rc;
|
|
xen_livepatch_status_t status;
|
|
uint32_t flags;
|
|
|
|
if ( argc < 1 )
|
|
{
|
|
show_help();
|
|
return -1;
|
|
}
|
|
|
|
if ( idx >= ARRAY_SIZE(action_options) )
|
|
return -1;
|
|
|
|
if ( get_name(argc--, argv++, name) )
|
|
return EINVAL;
|
|
|
|
if ( get_flags(argc, argv, idx, &flags) )
|
|
return EINVAL;
|
|
|
|
/* Check initial status. */
|
|
rc = xc_livepatch_get(xch, name, &status);
|
|
if ( rc )
|
|
{
|
|
int saved_errno = errno;
|
|
fprintf(stderr, "Failed to get status of %s.\n"
|
|
"Error %d: %s\n",
|
|
name, saved_errno, strerror(saved_errno));
|
|
return saved_errno;
|
|
}
|
|
if ( status.rc == -XEN_EAGAIN )
|
|
{
|
|
fprintf(stderr,
|
|
"Cannot execute %s.\n"
|
|
"Operation already in progress.\n", action_options[idx].name);
|
|
return EAGAIN;
|
|
}
|
|
|
|
if ( status.state == action_options[idx].expected )
|
|
{
|
|
printf("No action needed.\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Perform action. */
|
|
if ( action_options[idx].allow & status.state )
|
|
{
|
|
printf("%s %s... ", action_options[idx].verb, name);
|
|
rc = action_options[idx].function(xch, name, HYPERVISOR_TIMEOUT_NS, flags);
|
|
if ( rc )
|
|
{
|
|
int saved_errno = errno;
|
|
printf("failed\n");
|
|
fprintf(stderr, "Error %d: %s\n",
|
|
saved_errno, strerror(saved_errno));
|
|
return saved_errno;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "%s is in the wrong state.\n"
|
|
"Current state: %s\n"
|
|
"Expected state: %s\n",
|
|
name, state2str(status.state),
|
|
state2str(action_options[idx].allow));
|
|
return -1;
|
|
}
|
|
|
|
nanosleep_retry(DELAY);
|
|
rc = xc_livepatch_get(xch, name, &status);
|
|
|
|
if ( rc )
|
|
rc = -errno;
|
|
else if ( status.rc )
|
|
rc = status.rc;
|
|
|
|
if ( rc == -XEN_EAGAIN )
|
|
{
|
|
printf("failed\n");
|
|
fprintf(stderr, "Operation didn't complete.\n");
|
|
return EAGAIN;
|
|
}
|
|
|
|
if ( rc == 0 )
|
|
rc = status.state;
|
|
|
|
if ( action_options[idx].expected == rc )
|
|
printf("completed\n");
|
|
else if ( rc < 0 )
|
|
{
|
|
printf("failed\n");
|
|
fprintf(stderr, "Error %d: %s\n", -rc, strerror(-rc));
|
|
return -rc;
|
|
}
|
|
else
|
|
{
|
|
printf("failed\n");
|
|
fprintf(stderr, "%s is in the wrong state.\n"
|
|
"Current state: %s\n"
|
|
"Expected state: %s\n",
|
|
name, state2str(rc),
|
|
state2str(action_options[idx].expected));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int load_func(int argc, char *argv[])
|
|
{
|
|
int i, rc = ENOMEM;
|
|
char *upload_argv[2];
|
|
char **apply_argv, *path, *name, *lastdot;
|
|
|
|
if ( argc < 1 )
|
|
{
|
|
show_help();
|
|
return -1;
|
|
}
|
|
|
|
/* apply action has <id> [flags] input requirement, which must be constructed */
|
|
apply_argv = (char **) malloc(argc * sizeof(*apply_argv));
|
|
if ( !apply_argv )
|
|
return rc;
|
|
|
|
/* <file> */
|
|
upload_argv[1] = argv[0];
|
|
|
|
/* Synthesize the <id> */
|
|
path = strdup(argv[0]);
|
|
|
|
name = basename(path);
|
|
lastdot = strrchr(name, '.');
|
|
if ( lastdot != NULL )
|
|
*lastdot = '\0';
|
|
upload_argv[0] = name;
|
|
apply_argv[0] = name;
|
|
|
|
/* Fill in all user provided flags */
|
|
for ( i = 1; i < argc; i++ )
|
|
apply_argv[i] = argv[i];
|
|
|
|
rc = upload_func(2 /* <id> <file> */, upload_argv);
|
|
if ( rc )
|
|
goto error;
|
|
|
|
rc = action_func(argc, apply_argv, ACTION_APPLY);
|
|
if ( rc )
|
|
action_func(1 /* only <id> */, upload_argv, ACTION_UNLOAD);
|
|
|
|
error:
|
|
free(apply_argv);
|
|
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 = 0, ret;
|
|
|
|
/*
|
|
* Set stdout to be unbuffered to avoid having to fflush when
|
|
* printing without a newline.
|
|
*/
|
|
setvbuf(stdout, NULL, _IONBF, 0);
|
|
|
|
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-livepatch help'\n", argv[1]);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
/*
|
|
* Exitcode 0 for success.
|
|
* Exitcode 1 for an error.
|
|
* Exitcode 2 if the operation should be retried for any reason (e.g. a
|
|
* timeout or because another operation was in progress).
|
|
*/
|
|
#define EXIT_TIMEOUT (EXIT_FAILURE + 1)
|
|
|
|
BUILD_BUG_ON(EXIT_SUCCESS != 0);
|
|
BUILD_BUG_ON(EXIT_FAILURE != 1);
|
|
BUILD_BUG_ON(EXIT_TIMEOUT != 2);
|
|
|
|
switch ( ret )
|
|
{
|
|
case 0:
|
|
return EXIT_SUCCESS;
|
|
case EAGAIN:
|
|
case EBUSY:
|
|
return EXIT_TIMEOUT;
|
|
default:
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Local variables:
|
|
* mode: C
|
|
* c-file-style: "BSD"
|
|
* c-basic-offset: 4
|
|
* tab-width: 4
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*/
|