We introduce a new format for saved domains. The new format, in
contrast to the old:
* Has a magic number which can distinguish it from other kinds of file
* Is extensible
* Can contains the domain configuration file
On domain creation we remember the actual config file used (using the
toolstack data feature of libxl, just introduced), and by default save
it to the save file.
However, options are provided for the following:
* Reading and writing old-style xl saved domain files. (These will
become deprecated at some point in the future.)
* When saving a domain, supplying an alternative config file to
store in the savefile.
* When restoring a domain, supplying an alternative config file.
If a domain is restored with a different config file, it is the
responsibility of the xl user to ensure that the two configs are
"compatible". Changing the targets of virtual devices is supported;
changing other features of the domain is not recommended. Bad changes
may lead to undefined behaviour in the domain, and are in practice
likely to cause resume failures or crashes.
Signed-off-by: Ian Jackson <Ian.Jackson@xxxxxxxxxxxxx>
---
tools/libxl/libxlu_cfg.c | 37 ++++++
tools/libxl/libxlutil.h | 3 +-
tools/libxl/xl.c | 280 +++++++++++++++++++++++++++++++++++++++++-----
3 files changed, 289 insertions(+), 31 deletions(-)
diff --git a/tools/libxl/libxlu_cfg.c b/tools/libxl/libxlu_cfg.c
index 632c371..0a5ccef 100644
--- a/tools/libxl/libxlu_cfg.c
+++ b/tools/libxl/libxlu_cfg.c
@@ -53,6 +53,43 @@ int xlu_cfg_readfile(XLU_Config *cfg, const char
*real_filename) {
return ctx.err;
}
+int xlu_cfg_readdata(XLU_Config *cfg, const char *data, int length) {
+ CfgParseContext ctx;
+ int e, r;
+ YY_BUFFER_STATE buf= 0;
+
+ ctx.scanner= 0;
+ ctx.cfg= cfg;
+ ctx.err= 0;
+ ctx.lexerrlineno= -1;
+
+ e= xlu__cfg_yylex_init_extra(&ctx, &ctx.scanner);
+ if (e) {
+ fprintf(cfg->report,"%s: unable to create scanner: %s\n",
+ cfg->filename, strerror(e));
+ ctx.err= e;
+ ctx.scanner= 0;
+ goto xe;
+ }
+
+ buf = xlu__cfg_yy_scan_bytes(data, length, ctx.scanner);
+ if (!buf) {
+ fprintf(cfg->report,"%s: unable to allocate scanner buffer\n",
+ cfg->filename);
+ ctx.err= ENOMEM;
+ goto xe;
+ }
+
+ r= xlu__cfg_yyparse(&ctx);
+ if (r) assert(ctx.err);
+
+ xe:
+ if (buf) xlu__cfg_yy_delete_buffer(buf, ctx.scanner);
+ if (ctx.scanner) xlu__cfg_yylex_destroy(ctx.scanner);
+
+ return ctx.err;
+}
+
void xlu__cfg_set_free(XLU_ConfigSetting *set) {
free(set->name);
free(set->values);
diff --git a/tools/libxl/libxlutil.h b/tools/libxl/libxlutil.h
index 97b16aa..0262e55 100644
--- a/tools/libxl/libxlutil.h
+++ b/tools/libxl/libxlutil.h
@@ -30,7 +30,8 @@ XLU_Config *xlu_cfg_init(FILE *report, const char
*report_filename);
* until the Config is destroyed. */
int xlu_cfg_readfile(XLU_Config*, const char *real_filename);
- /* If this fails, then it is undefined behaviour to call xlu_cfg_get_...
+int xlu_cfg_readdata(XLU_Config*, const char *data, int length);
+ /* If these fail, then it is undefined behaviour to call xlu_cfg_get_...
* functions. You have to just xlu_cfg_destroy. */
void xlu_cfg_destroy(XLU_Config*);
diff --git a/tools/libxl/xl.c b/tools/libxl/xl.c
index 2e23da3..f3729f3 100644
--- a/tools/libxl/xl.c
+++ b/tools/libxl/xl.c
@@ -30,6 +30,7 @@
#include <arpa/inet.h>
#include <xenctrl.h>
#include <ctype.h>
+#include <inttypes.h>
#include "libxl.h"
#include "libxl_utils.h"
@@ -39,6 +40,27 @@
int logfile = 2;
+static int suspend_old_xl_format = 0;
+
+static const char savefileheader_magic[32]=
+ "Xen saved domain, xl format\n \0 \r";
+
+typedef struct {
+ 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 */
+} SaveFileHeader;
+
+/* 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)
+
void log_callback(void *userdata, int loglevel, const char *file, int line,
const char *func, char *s)
{
char str[1024];
@@ -320,7 +342,9 @@ static void printf_info(libxl_domain_create_info *c_info,
}
}
-static void parse_config_file(const char *filename,
+static void parse_config_data(const char *configfile_filename_report,
+ const char *configfile_data,
+ int configfile_len,
libxl_domain_create_info *c_info,
libxl_domain_build_info *b_info,
libxl_device_disk **disks,
@@ -343,13 +367,13 @@ static void parse_config_file(const char *filename,
int pci_msitranslate = 1;
int i, e;
- config= xlu_cfg_init(stderr, filename);
+ config= xlu_cfg_init(stderr, configfile_filename_report);
if (!config) {
fprintf(stderr, "Failed to allocate for configuration\n");
exit(1);
}
- e= xlu_cfg_readfile (config, filename);
+ e= xlu_cfg_readdata(config, configfile_data, configfile_len);
if (e) {
fprintf(stderr, "Failed to parse config file: %s\n", strerror(e));
exit(1);
@@ -664,6 +688,15 @@ skip_pci:
xlu_cfg_destroy(config);
}
+#define CHK_ERRNO( call ) ({ \
+ int chk_errno = (call); \
+ if (chk_errno) { \
+ 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) { \
@@ -673,6 +706,26 @@ skip_pci:
} \
})
+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;
+}
+
static void create_domain(int debug, int daemonize, const char *config_file,
const char *restore_file, int paused)
{
struct libxl_ctx ctx;
@@ -693,27 +746,124 @@ static void create_domain(int debug, int daemonize,
const char *config_file, con
int ret;
libxl_device_model_starting *dm_starting = 0;
libxl_waiter *w1 = NULL, *w2 = NULL;
+ void *config_data = 0;
+ int config_len = 0;
+ int restore_fd = -1;
+ SaveFileHeader hdr;
+
memset(&dm_info, 0x00, sizeof(dm_info));
+ if (libxl_ctx_init(&ctx, LIBXL_VERSION)) {
+ fprintf(stderr, "cannot init xl context\n");
+ exit(1);
+ }
+
+ if (restore_file) {
+ uint8_t *optdata_begin = 0;
+ const uint8_t *optdata_here = 0;
+ union { uint32_t u32; char b[4]; } u32buf;
+
+ restore_fd = open(restore_file, O_RDONLY);
+
+ if (suspend_old_xl_format) {
+ memset(&hdr,0,sizeof(hdr));
+ } else {
+ uint32_t badflags;
+
+ CHK_ERRNO( libxl_read_exactly(&ctx, restore_fd, &hdr,
+ sizeof(hdr), restore_file, "header") );
+ if (memcmp(hdr.magic, savefileheader_magic, sizeof(hdr.magic))) {
+ fprintf(stderr, "File has wrong magic number -"
+ " corrupt or needs -O?\n");
+ exit(2);
+ }
+ if (hdr.byteorder != SAVEFILE_BYTEORDER_VALUE) {
+ fprintf(stderr, "File has wrong byte order\n");
+ exit(2);
+ }
+ fprintf(stderr, "Loading new save file %s"
+ " (new xl fmt info"
+ " 0x%"PRIx32"/0x%"PRIx32"/%"PRIu32")\n",
+ restore_file, hdr.mandatory_flags, hdr.optional_flags,
+ hdr.optional_data_len);
+
+ badflags = hdr.mandatory_flags & ~( 0 /* none understood yet */ );
+ if (badflags) {
+ fprintf(stderr, "Savefile has mandatory flag(s) 0x%"PRIx32" "
+ "which are not supported; need newer xl\n",
+ badflags);
+ exit(2);
+ }
+ }
+ if (hdr.optional_data_len) {
+ optdata_begin = xmalloc(hdr.optional_data_len);
+ CHK_ERRNO( libxl_read_exactly(&ctx, restore_fd, optdata_begin,
+ hdr.optional_data_len, restore_file, "optdata") );
+ }
+
+#define OPTDATA_LEFT (hdr.optional_data_len - (optdata_here - optdata_begin))
+#define WITH_OPTDATA(amt, body) \
+ if (OPTDATA_LEFT < (amt)) { \
+ fprintf(stderr, "Savefile truncated.\n"); exit(2); \
+ } else { \
+ body; \
+ optdata_here += (amt); \
+ }
+
+ optdata_here = optdata_begin;
+
+ if (OPTDATA_LEFT) {
+ fprintf(stderr, " Savefile contains xl domain config\n");
+ WITH_OPTDATA(4, {
+ memcpy(u32buf.b, optdata_here, 4);
+ config_len = u32buf.u32;
+ });
+ WITH_OPTDATA(config_len, {
+ config_data = xmalloc(config_len);
+ memcpy(config_data, optdata_here, config_len);
+ });
+ }
+
+ }
+
+ if (config_file) {
+ free(config_data); config_data = 0;
+ ret = libxl_read_file_contents(&ctx, config_file,
+ &config_data, &config_len);
+ if (ret) { fprintf(stderr, "Failed to read config file: %s: %s\n",
+ config_file, strerror(errno)); exit(1); }
+ } else {
+ if (!config_data) {
+ fprintf(stderr, "Config file not specified and"
+ " none in save file\n");
+ exit(1);
+ }
+ config_file = "<saved>";
+ }
+
printf("Parsing config file %s\n", config_file);
- parse_config_file(config_file, &info1, &info2, &disks, &num_disks, &vifs,
&num_vifs, &pcidevs, &num_pcidevs, &vfbs, &num_vfbs, &vkbs, &num_vkbs,
&dm_info);
+
+ parse_config_data(config_file, config_data, config_len, &info1, &info2,
&disks, &num_disks, &vifs, &num_vifs, &pcidevs, &num_pcidevs, &vfbs, &num_vfbs,
&vkbs, &num_vkbs, &dm_info);
+
if (debug)
printf_info(&info1, &info2, disks, num_disks, vifs, num_vifs, pcidevs,
num_pcidevs, vfbs, num_vfbs, vkbs, num_vkbs, &dm_info);
start:
domid = 0;
- if (libxl_ctx_init(&ctx, LIBXL_VERSION)) {
- fprintf(stderr, "cannot init xl context\n");
- return;
- }
-
libxl_ctx_set_log(&ctx, log_callback, NULL);
ret = libxl_domain_make(&ctx, &info1, &domid);
if (ret) {
fprintf(stderr, "cannot make domain: %d\n", ret);
- return;
+ exit(1);
+ }
+
+ ret = libxl_userdata_store(&ctx, domid, "xl",
+ config_data, config_len);
+ if (ret) {
+ perror("cannot save config file");
+ exit(1);
}
if (!restore_file || !need_daemon) {
@@ -723,16 +873,13 @@ start:
}
ret = libxl_domain_build(&ctx, &info2, domid, &state);
} else {
- int restore_fd;
-
- restore_fd = open(restore_file, O_RDONLY);
ret = libxl_domain_restore(&ctx, &info2, domid, restore_fd, &state,
&dm_info);
close(restore_fd);
}
if (ret) {
fprintf(stderr, "cannot (re-)build domain: %d\n", ret);
- return;
+ exit(1);
}
for (i = 0; i < num_disks; i++) {
@@ -740,7 +887,7 @@ start:
ret = libxl_device_disk_add(&ctx, domid, &disks[i]);
if (ret) {
fprintf(stderr, "cannot add disk %d to domain: %d\n", i, ret);
- return;
+ exit(1);
}
}
for (i = 0; i < num_vifs; i++) {
@@ -748,7 +895,7 @@ start:
ret = libxl_device_nic_add(&ctx, domid, &vifs[i]);
if (ret) {
fprintf(stderr, "cannot add nic %d to domain: %d\n", i, ret);
- return;
+ exit(1);
}
}
if (info1.hvm) {
@@ -795,8 +942,8 @@ start:
need_daemon = 0;
}
LOG("Waiting for domain %s (domid %d) to die", info1.name, domid);
- w1 = (libxl_waiter*) malloc(sizeof(libxl_waiter) * num_disks);
- w2 = (libxl_waiter*) malloc(sizeof(libxl_waiter));
+ w1 = (libxl_waiter*) xmalloc(sizeof(libxl_waiter) * num_disks);
+ w2 = (libxl_waiter*) xmalloc(sizeof(libxl_waiter));
libxl_wait_for_disk_ejects(&ctx, domid, disks, num_disks, w1);
libxl_wait_for_domain_death(&ctx, domid, w2);
libxl_get_wait_fd(&ctx, &fd);
@@ -852,6 +999,7 @@ start:
free(vfbs);
free(vkbs);
free(pcidevs);
+ free(config_data);
}
static void help(char *command)
@@ -900,16 +1048,18 @@ static void help(char *command)
printf("Usage: xl unpause <Domain>\n\n");
printf("Unpause a paused domain.\n\n");
} else if(!strcmp(command, "save")) {
- printf("Usage: xl save [options] <Domain> <CheckpointFile>\n\n");
+ printf("Usage: xl save [options] <Domain> <CheckpointFile>
[<ConfigFile>]\n\n");
printf("Save a domain state to restore later.\n\n");
printf("Options:\n\n");
printf("-h Print this help.\n");
+ printf("-O Old (configless) xl save format.\n");
printf("-c Leave domain running after creating the
snapshot.\n");
} else if(!strcmp(command, "restore")) {
- printf("Usage: xl restore [options] <ConfigFile>
<CheckpointFile>\n\n");
+ printf("Usage: xl restore [options] [<ConfigFile>]
<CheckpointFile>\n\n");
printf("Restore a domain from a saved state.\n\n");
printf("Options:\n\n");
printf("-h Print this help.\n");
+ printf("-O Old (configless) xl save format.\n");
printf("-p Do not unpause domain after restoring
it.\n");
printf("-e Do not wait in the background for the
death of the domain.\n");
} else if(!strcmp(command, "destroy")) {
@@ -1413,11 +1563,12 @@ void list_vm(void)
free(info);
}
-int save_domain(char *p, char *filename, int checkpoint)
+int save_domain(char *p, char *filename, int checkpoint,
+ const char *override_config_file)
{
struct libxl_ctx ctx;
uint32_t domid;
- int fd;
+ int fd, rc;
if (libxl_ctx_init(&ctx, LIBXL_VERSION)) {
fprintf(stderr, "cannot init xl context\n");
@@ -1434,6 +1585,65 @@ int save_domain(char *p, char *filename, int checkpoint)
fprintf(stderr, "Failed to open temp file %s for writing\n", filename);
exit(2);
}
+
+ if (!suspend_old_xl_format) {
+ SaveFileHeader hdr;
+ uint8_t *config_data = 0;
+ int config_len;
+ 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)); \
+ } \
+ })
+
+ /* 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);
+ config_data = config_v;
+ } else {
+ rc = libxl_userdata_retrieve(&ctx, domid, "xl",
+ &config_data, &config_len);
+ }
+ if (rc) {
+ fputs("Unable to get config file\n",stderr);
+ exit(2);
+ }
+ if (!config_len) {
+ fputs(" Savefile will not contain xl domain config\n", stderr);
+ }
+
+ u32buf.u32 = config_len;
+ ADD_OPTDATA(u32buf.b, 4);
+ ADD_OPTDATA(config_data, config_len);
+
+ /* that's the optional data */
+
+ CHK_ERRNO( libxl_write_exactly(&ctx, fd,
+ &hdr, sizeof(hdr), filename, "header") );
+ CHK_ERRNO( libxl_write_exactly(&ctx, fd,
+ optdata_begin, hdr.optional_data_len, filename, "header") );
+
+ fprintf(stderr, "Saving to %s new xl format (info"
+ " 0x%"PRIx32"/0x%"PRIx32"/%"PRIu32")\n",
+ filename, hdr.mandatory_flags, hdr.optional_flags,
+ hdr.optional_data_len);
+ }
+
libxl_domain_suspend(&ctx, NULL, domid, fd);
close(fd);
@@ -1452,7 +1662,7 @@ int main_restore(int argc, char **argv)
int paused = 0, debug = 0, daemonize = 1;
int opt;
- while ((opt = getopt(argc, argv, "hpde")) != -1) {
+ while ((opt = getopt(argc, argv, "hpdeO")) != -1) {
switch (opt) {
case 'p':
paused = 1;
@@ -1466,19 +1676,24 @@ int main_restore(int argc, char **argv)
case 'h':
help("restore");
exit(0);
+ case 'O':
+ suspend_old_xl_format = 1;
+ break;
default:
fprintf(stderr, "option not supported\n");
break;
}
}
- if (optind >= argc - 1) {
+ 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");
exit(2);
}
-
- config_file = argv[optind];
- checkpoint_file = argv[optind + 1];
create_domain(debug, daemonize, config_file, checkpoint_file, paused);
exit(0);
}
@@ -1486,10 +1701,11 @@ int main_restore(int argc, char **argv)
int main_save(int argc, char **argv)
{
char *filename = NULL, *p = NULL;
+ const char *config_filename;
int checkpoint = 0;
int opt;
- while ((opt = getopt(argc, argv, "hc")) != -1) {
+ while ((opt = getopt(argc, argv, "hcO")) != -1) {
switch (opt) {
case 'c':
checkpoint = 1;
@@ -1497,20 +1713,24 @@ int main_save(int argc, char **argv)
case 'h':
help("save");
exit(0);
+ case 'O':
+ suspend_old_xl_format = 1;
+ break;
default:
fprintf(stderr, "option not supported\n");
break;
}
}
- if (optind >= argc - 1) {
+ if (argc-optind < 1 || argc-optind > 3) {
help("save");
exit(2);
}
p = argv[optind];
filename = argv[optind + 1];
- save_domain(p, filename, checkpoint);
+ config_filename = argv[optind + 2];
+ save_domain(p, filename, checkpoint, config_filename);
exit(0);
}
--
1.5.6.5
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|