debuggers.hg

changeset 21193:4405b50cb183

xl: New savefile format. Save domain config when saving a domain.

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:
* 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.

Old format save files generated by old versions of xl are not
supported.

Signed-off-by: Ian Jackson <Ian.Jackson@eu.citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Mon Apr 12 17:46:10 2010 +0100 (2010-04-12)
parents 117c79b7066d
children f0c305aaa6a2
files tools/libxl/libxlu_cfg.c tools/libxl/libxlutil.h tools/libxl/xl.c
line diff
     1.1 --- a/tools/libxl/libxlu_cfg.c	Mon Apr 12 17:45:26 2010 +0100
     1.2 +++ b/tools/libxl/libxlu_cfg.c	Mon Apr 12 17:46:10 2010 +0100
     1.3 @@ -53,6 +53,43 @@ int xlu_cfg_readfile(XLU_Config *cfg, co
     1.4      return ctx.err;
     1.5  }
     1.6  
     1.7 +int xlu_cfg_readdata(XLU_Config *cfg, const char *data, int length) {
     1.8 +    CfgParseContext ctx;
     1.9 +    int e, r;
    1.10 +    YY_BUFFER_STATE buf= 0;
    1.11 +
    1.12 +    ctx.scanner= 0;
    1.13 +    ctx.cfg= cfg;
    1.14 +    ctx.err= 0;
    1.15 +    ctx.lexerrlineno= -1;
    1.16 +
    1.17 +    e= xlu__cfg_yylex_init_extra(&ctx, &ctx.scanner);
    1.18 +    if (e) {
    1.19 +        fprintf(cfg->report,"%s: unable to create scanner: %s\n",
    1.20 +                cfg->filename, strerror(e));
    1.21 +        ctx.err= e;
    1.22 +        ctx.scanner= 0;
    1.23 +        goto xe;
    1.24 +    }
    1.25 +
    1.26 +    buf = xlu__cfg_yy_scan_bytes(data, length, ctx.scanner);
    1.27 +    if (!buf) {
    1.28 +        fprintf(cfg->report,"%s: unable to allocate scanner buffer\n",
    1.29 +                cfg->filename);
    1.30 +        ctx.err= ENOMEM;
    1.31 +        goto xe;
    1.32 +    }
    1.33 +
    1.34 +    r= xlu__cfg_yyparse(&ctx);
    1.35 +    if (r) assert(ctx.err);
    1.36 +
    1.37 + xe:
    1.38 +    if (buf) xlu__cfg_yy_delete_buffer(buf, ctx.scanner);
    1.39 +    if (ctx.scanner) xlu__cfg_yylex_destroy(ctx.scanner);
    1.40 +
    1.41 +    return ctx.err;
    1.42 +}
    1.43 +
    1.44  void xlu__cfg_set_free(XLU_ConfigSetting *set) {
    1.45      free(set->name);
    1.46      free(set->values);
     2.1 --- a/tools/libxl/libxlutil.h	Mon Apr 12 17:45:26 2010 +0100
     2.2 +++ b/tools/libxl/libxlutil.h	Mon Apr 12 17:46:10 2010 +0100
     2.3 @@ -30,7 +30,8 @@ XLU_Config *xlu_cfg_init(FILE *report, c
     2.4     *  until the Config is destroyed. */
     2.5  
     2.6  int xlu_cfg_readfile(XLU_Config*, const char *real_filename);
     2.7 -  /* If this fails, then it is undefined behaviour to call xlu_cfg_get_...
     2.8 +int xlu_cfg_readdata(XLU_Config*, const char *data, int length);
     2.9 +  /* If these fail, then it is undefined behaviour to call xlu_cfg_get_...
    2.10     * functions.  You have to just xlu_cfg_destroy. */
    2.11   
    2.12  void xlu_cfg_destroy(XLU_Config*);
     3.1 --- a/tools/libxl/xl.c	Mon Apr 12 17:45:26 2010 +0100
     3.2 +++ b/tools/libxl/xl.c	Mon Apr 12 17:46:10 2010 +0100
     3.3 @@ -30,6 +30,7 @@
     3.4  #include <arpa/inet.h>
     3.5  #include <xenctrl.h>
     3.6  #include <ctype.h>
     3.7 +#include <inttypes.h>
     3.8  
     3.9  #include "libxl.h"
    3.10  #include "libxl_utils.h"
    3.11 @@ -46,6 +47,25 @@ static struct libxl_ctx ctx;
    3.12  static uint32_t domid;
    3.13  static const char *common_domname;
    3.14  
    3.15 +static const char savefileheader_magic[32]=
    3.16 +    "Xen saved domain, xl format\n \0 \r";
    3.17 +
    3.18 +struct save_file_header {
    3.19 +    char magic[32]; /* savefileheader_magic */
    3.20 +    /* All uint32_ts are in domain's byte order. */
    3.21 +    uint32_t byteorder; /* SAVEFILE_BYTEORDER_VALUE */
    3.22 +    uint32_t mandatory_flags; /* unknown flags => reject restore */
    3.23 +    uint32_t optional_flags; /* unknown flags => reject restore */
    3.24 +    uint32_t optional_data_len; /* skip, or skip tail, if not understood */
    3.25 +};
    3.26 +
    3.27 +/* Optional data, in order:
    3.28 + *   4 bytes uint32_t  config file size
    3.29 + *   n bytes           config file in Unix text file format
    3.30 + */
    3.31 +
    3.32 +#define SAVEFILE_BYTEORDER_VALUE ((uint32_t)0x01020304UL)
    3.33 +
    3.34  void log_callback(void *userdata, int loglevel, const char *file, int line, const char *func, char *s)
    3.35  {
    3.36      char str[1024];
    3.37 @@ -343,7 +363,9 @@ static void printf_info(libxl_domain_cre
    3.38      }
    3.39  }
    3.40  
    3.41 -static void parse_config_file(const char *filename,
    3.42 +static void parse_config_data(const char *configfile_filename_report,
    3.43 +                              const char *configfile_data,
    3.44 +                              int configfile_len,
    3.45                                libxl_domain_create_info *c_info,
    3.46                                libxl_domain_build_info *b_info,
    3.47                                libxl_device_disk **disks,
    3.48 @@ -366,13 +388,13 @@ static void parse_config_file(const char
    3.49      int pci_msitranslate = 1;
    3.50      int i, e;
    3.51  
    3.52 -    config= xlu_cfg_init(stderr, filename);
    3.53 +    config= xlu_cfg_init(stderr, configfile_filename_report);
    3.54      if (!config) {
    3.55          fprintf(stderr, "Failed to allocate for configuration\n");
    3.56          exit(1);
    3.57      }
    3.58  
    3.59 -    e= xlu_cfg_readfile (config, filename);
    3.60 +    e= xlu_cfg_readdata(config, configfile_data, configfile_len);
    3.61      if (e) {
    3.62          fprintf(stderr, "Failed to parse config file: %s\n", strerror(e));
    3.63          exit(1);
    3.64 @@ -692,6 +714,15 @@ skip_pci:
    3.65      xlu_cfg_destroy(config);
    3.66  }
    3.67  
    3.68 +#define CHK_ERRNO( call ) ({                                            \
    3.69 +        int chk_errno = (call);                                         \
    3.70 +        if (chk_errno) {                                                \
    3.71 +            fprintf(stderr,"xl: fatal error: %s:%d: %s: %s\n",          \
    3.72 +                    __FILE__,__LINE__, strerror(chk_errno), #call);     \
    3.73 +            exit(-ERROR_FAIL);                                          \
    3.74 +        }                                                               \
    3.75 +    })
    3.76 +
    3.77  #define MUST( call ) ({                                                 \
    3.78          int must_rc = (call);                                           \
    3.79          if (must_rc) {                                                  \
    3.80 @@ -701,6 +732,26 @@ skip_pci:
    3.81          }                                                               \
    3.82      })
    3.83  
    3.84 +static void *xmalloc(size_t sz) {
    3.85 +    void *r;
    3.86 +    r = malloc(sz);
    3.87 +    if (!r) { fprintf(stderr,"xl: Unable to malloc %lu bytes.\n",
    3.88 +                      (unsigned long)sz); exit(-ERROR_FAIL); }
    3.89 +    return r;
    3.90 +}
    3.91 +
    3.92 +static void *xrealloc(void *ptr, size_t sz) {
    3.93 +    void *r;
    3.94 +    if (!sz) { free(ptr); return 0; }
    3.95 +      /* realloc(non-0, 0) has a useless return value;
    3.96 +       * but xrealloc(anything, 0) is like free
    3.97 +       */
    3.98 +    r = realloc(ptr, sz);
    3.99 +    if (!r) { fprintf(stderr,"xl: Unable to realloc to %lu bytes.\n",
   3.100 +                      (unsigned long)sz); exit(-ERROR_FAIL); }
   3.101 +    return r;
   3.102 +}
   3.103 +
   3.104  static void create_domain(int debug, int daemonize, const char *config_file, const char *restore_file, int paused)
   3.105  {
   3.106      libxl_domain_create_info info1;
   3.107 @@ -719,10 +770,100 @@ static void create_domain(int debug, int
   3.108      int ret;
   3.109      libxl_device_model_starting *dm_starting = 0;
   3.110      libxl_waiter *w1 = NULL, *w2 = NULL;
   3.111 +    void *config_data = 0;
   3.112 +    int config_len = 0;
   3.113 +    int restore_fd = -1;
   3.114 +    struct save_file_header hdr;
   3.115 +
   3.116      memset(&dm_info, 0x00, sizeof(dm_info));
   3.117  
   3.118 +    if (libxl_ctx_init(&ctx, LIBXL_VERSION)) {
   3.119 +        fprintf(stderr, "cannot init xl context\n");
   3.120 +        exit(1);
   3.121 +    }
   3.122 +
   3.123 +    if (restore_file) {
   3.124 +        uint8_t *optdata_begin = 0;
   3.125 +        const uint8_t *optdata_here = 0;
   3.126 +        union { uint32_t u32; char b[4]; } u32buf;
   3.127 +        uint32_t badflags;
   3.128 +
   3.129 +        restore_fd = open(restore_file, O_RDONLY);
   3.130 +
   3.131 +        CHK_ERRNO( libxl_read_exactly(&ctx, restore_fd, &hdr,
   3.132 +                   sizeof(hdr), restore_file, "header") );
   3.133 +        if (memcmp(hdr.magic, savefileheader_magic, sizeof(hdr.magic))) {
   3.134 +            fprintf(stderr, "File has wrong magic number -"
   3.135 +                    " corrupt or for a different tool?\n");
   3.136 +            exit(2);
   3.137 +        }
   3.138 +        if (hdr.byteorder != SAVEFILE_BYTEORDER_VALUE) {
   3.139 +            fprintf(stderr, "File has wrong byte order\n");
   3.140 +            exit(2);
   3.141 +        }
   3.142 +        fprintf(stderr, "Loading new save file %s"
   3.143 +                " (new xl fmt info"
   3.144 +                " 0x%"PRIx32"/0x%"PRIx32"/%"PRIu32")\n",
   3.145 +                restore_file, hdr.mandatory_flags, hdr.optional_flags,
   3.146 +                hdr.optional_data_len);
   3.147 +
   3.148 +        badflags = hdr.mandatory_flags & ~( 0 /* none understood yet */ );
   3.149 +        if (badflags) {
   3.150 +            fprintf(stderr, "Savefile has mandatory flag(s) 0x%"PRIx32" "
   3.151 +                    "which are not supported; need newer xl\n",
   3.152 +                    badflags);
   3.153 +            exit(2);
   3.154 +        }
   3.155 +        if (hdr.optional_data_len) {
   3.156 +            optdata_begin = xmalloc(hdr.optional_data_len);
   3.157 +            CHK_ERRNO( libxl_read_exactly(&ctx, restore_fd, optdata_begin,
   3.158 +                   hdr.optional_data_len, restore_file, "optdata") );
   3.159 +        }
   3.160 +
   3.161 +#define OPTDATA_LEFT  (hdr.optional_data_len - (optdata_here - optdata_begin))
   3.162 +#define WITH_OPTDATA(amt, body)                                         \
   3.163 +            if (OPTDATA_LEFT < (amt)) {                                 \
   3.164 +                fprintf(stderr, "Savefile truncated.\n"); exit(2);      \
   3.165 +            } else {                                                    \
   3.166 +                body;                                                   \
   3.167 +                optdata_here += (amt);                                  \
   3.168 +            }
   3.169 +
   3.170 +        optdata_here = optdata_begin;
   3.171 +
   3.172 +        if (OPTDATA_LEFT) {
   3.173 +            fprintf(stderr, " Savefile contains xl domain config\n");
   3.174 +            WITH_OPTDATA(4, {
   3.175 +                memcpy(u32buf.b, optdata_here, 4);
   3.176 +                config_len = u32buf.u32;
   3.177 +            });
   3.178 +            WITH_OPTDATA(config_len, {
   3.179 +                config_data = xmalloc(config_len);
   3.180 +                memcpy(config_data, optdata_here, config_len);
   3.181 +            });
   3.182 +        }
   3.183 +
   3.184 +    }
   3.185 +
   3.186 +    if (config_file) {
   3.187 +        free(config_data);  config_data = 0;
   3.188 +        ret = libxl_read_file_contents(&ctx, config_file,
   3.189 +                                       &config_data, &config_len);
   3.190 +        if (ret) { fprintf(stderr, "Failed to read config file: %s: %s\n",
   3.191 +                           config_file, strerror(errno)); exit(1); }
   3.192 +    } else {
   3.193 +        if (!config_data) {
   3.194 +            fprintf(stderr, "Config file not specified and"
   3.195 +                    " none in save file\n");
   3.196 +            exit(1);
   3.197 +        }
   3.198 +        config_file = "<saved>";
   3.199 +    }
   3.200 +
   3.201      printf("Parsing config file %s\n", config_file);
   3.202 -    parse_config_file(config_file, &info1, &info2, &disks, &num_disks, &vifs, &num_vifs, &pcidevs, &num_pcidevs, &vfbs, &num_vfbs, &vkbs, &num_vkbs, &dm_info);
   3.203 +
   3.204 +    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);
   3.205 +
   3.206      if (debug)
   3.207          printf_info(&info1, &info2, disks, num_disks, vifs, num_vifs, pcidevs, num_pcidevs, vfbs, num_vfbs, vkbs, num_vkbs, &dm_info);
   3.208  
   3.209 @@ -732,7 +873,14 @@ start:
   3.210      ret = libxl_domain_make(&ctx, &info1, &domid);
   3.211      if (ret) {
   3.212          fprintf(stderr, "cannot make domain: %d\n", ret);
   3.213 -        return;
   3.214 +        exit(1);
   3.215 +    }
   3.216 +
   3.217 +    ret = libxl_userdata_store(&ctx, domid, "xl",
   3.218 +                                    config_data, config_len);
   3.219 +    if (ret) {
   3.220 +        perror("cannot save config file");
   3.221 +        exit(1);
   3.222      }
   3.223  
   3.224      if (!restore_file || !need_daemon) {
   3.225 @@ -742,16 +890,13 @@ start:
   3.226          }
   3.227          ret = libxl_domain_build(&ctx, &info2, domid, &state);
   3.228      } else {
   3.229 -        int restore_fd;
   3.230 -
   3.231 -        restore_fd = open(restore_file, O_RDONLY);
   3.232          ret = libxl_domain_restore(&ctx, &info2, domid, restore_fd, &state, &dm_info);
   3.233          close(restore_fd);
   3.234      }
   3.235  
   3.236      if (ret) {
   3.237          fprintf(stderr, "cannot (re-)build domain: %d\n", ret);
   3.238 -        return;
   3.239 +        exit(1);
   3.240      }
   3.241  
   3.242      for (i = 0; i < num_disks; i++) {
   3.243 @@ -759,7 +904,7 @@ start:
   3.244          ret = libxl_device_disk_add(&ctx, domid, &disks[i]);
   3.245          if (ret) {
   3.246              fprintf(stderr, "cannot add disk %d to domain: %d\n", i, ret);
   3.247 -            return;
   3.248 +            exit(1);
   3.249          }
   3.250      }
   3.251      for (i = 0; i < num_vifs; i++) {
   3.252 @@ -767,7 +912,7 @@ start:
   3.253          ret = libxl_device_nic_add(&ctx, domid, &vifs[i]);
   3.254          if (ret) {
   3.255              fprintf(stderr, "cannot add nic %d to domain: %d\n", i, ret);
   3.256 -            return;
   3.257 +            exit(1);
   3.258          }
   3.259      }
   3.260      if (info1.hvm) {
   3.261 @@ -814,8 +959,8 @@ start:
   3.262          need_daemon = 0;
   3.263      }
   3.264      LOG("Waiting for domain %s (domid %d) to die", info1.name, domid);
   3.265 -    w1 = (libxl_waiter*) malloc(sizeof(libxl_waiter) * num_disks);
   3.266 -    w2 = (libxl_waiter*) malloc(sizeof(libxl_waiter));
   3.267 +    w1 = (libxl_waiter*) xmalloc(sizeof(libxl_waiter) * num_disks);
   3.268 +    w2 = (libxl_waiter*) xmalloc(sizeof(libxl_waiter));
   3.269      libxl_wait_for_disk_ejects(&ctx, domid, disks, num_disks, w1);
   3.270      libxl_wait_for_domain_death(&ctx, domid, w2);
   3.271      libxl_get_wait_fd(&ctx, &fd);
   3.272 @@ -870,6 +1015,7 @@ start:
   3.273      free(vfbs);
   3.274      free(vkbs);
   3.275      free(pcidevs);
   3.276 +    free(config_data);
   3.277  }
   3.278  
   3.279  static void help(char *command)
   3.280 @@ -921,13 +1067,13 @@ static void help(char *command)
   3.281          printf("Usage: xl unpause <Domain>\n\n");
   3.282          printf("Unpause a paused domain.\n\n");
   3.283      } else if(!strcmp(command, "save")) {
   3.284 -        printf("Usage: xl save [options] <Domain> <CheckpointFile>\n\n");
   3.285 +        printf("Usage: xl save [options] <Domain> <CheckpointFile> [<ConfigFile>]\n\n");
   3.286          printf("Save a domain state to restore later.\n\n");
   3.287          printf("Options:\n\n");
   3.288          printf("-h                     Print this help.\n");
   3.289          printf("-c                     Leave domain running after creating the snapshot.\n");
   3.290      } else if(!strcmp(command, "restore")) {
   3.291 -        printf("Usage: xl restore [options] <ConfigFile> <CheckpointFile>\n\n");
   3.292 +        printf("Usage: xl restore [options] [<ConfigFile>] <CheckpointFile>\n\n");
   3.293          printf("Restore a domain from a saved state.\n\n");
   3.294          printf("Options:\n\n");
   3.295          printf("-h                     Print this help.\n");
   3.296 @@ -1337,9 +1483,15 @@ void list_vm(void)
   3.297      free(info);
   3.298  }
   3.299  
   3.300 -int save_domain(char *p, char *filename, int checkpoint)
   3.301 +int save_domain(char *p, char *filename, int checkpoint,
   3.302 +                const char *override_config_file)
   3.303  {
   3.304 -    int fd;
   3.305 +    int fd, rc;
   3.306 +    struct save_file_header hdr;
   3.307 +    uint8_t *config_data = 0;
   3.308 +    int config_len;
   3.309 +    uint8_t *optdata_begin;
   3.310 +    union { uint32_t u32; char b[4]; } u32buf;
   3.311  
   3.312      find_domain(p);
   3.313      fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
   3.314 @@ -1347,6 +1499,57 @@ int save_domain(char *p, char *filename,
   3.315          fprintf(stderr, "Failed to open temp file %s for writing\n", filename);
   3.316          exit(2);
   3.317      }
   3.318 +
   3.319 +    memset(&hdr, 0, sizeof(hdr));
   3.320 +    memcpy(hdr.magic, savefileheader_magic, sizeof(hdr.magic));
   3.321 +    hdr.byteorder = SAVEFILE_BYTEORDER_VALUE;
   3.322 +
   3.323 +    optdata_begin= 0;
   3.324 +
   3.325 +#define ADD_OPTDATA(ptr, len) ({                                            \
   3.326 +    if ((len)) {                                                        \
   3.327 +        hdr.optional_data_len += (len);                                 \
   3.328 +        optdata_begin = xrealloc(optdata_begin, hdr.optional_data_len); \
   3.329 +        memcpy(optdata_begin + hdr.optional_data_len - (len),           \
   3.330 +               (ptr), (len));                                           \
   3.331 +    }                                                                   \
   3.332 +                          })
   3.333 +
   3.334 +    /* configuration file in optional data: */
   3.335 +
   3.336 +    if (override_config_file) {
   3.337 +        void *config_v = 0;
   3.338 +        rc = libxl_read_file_contents(&ctx, override_config_file,
   3.339 +                                      &config_v, &config_len);
   3.340 +        config_data = config_v;
   3.341 +    } else {
   3.342 +        rc = libxl_userdata_retrieve(&ctx, domid, "xl",
   3.343 +                                          &config_data, &config_len);
   3.344 +    }
   3.345 +    if (rc) {
   3.346 +        fputs("Unable to get config file\n",stderr);
   3.347 +        exit(2);
   3.348 +    }
   3.349 +    if (!config_len) {
   3.350 +        fputs(" Savefile will not contain xl domain config\n", stderr);
   3.351 +    }
   3.352 +
   3.353 +    u32buf.u32 = config_len;
   3.354 +    ADD_OPTDATA(u32buf.b,    4);
   3.355 +    ADD_OPTDATA(config_data, config_len);
   3.356 +
   3.357 +    /* that's the optional data */
   3.358 +
   3.359 +    CHK_ERRNO( libxl_write_exactly(&ctx, fd,
   3.360 +        &hdr, sizeof(hdr), filename, "header") );
   3.361 +    CHK_ERRNO( libxl_write_exactly(&ctx, fd,
   3.362 +        optdata_begin, hdr.optional_data_len, filename, "header") );
   3.363 +
   3.364 +    fprintf(stderr, "Saving to %s new xl format (info"
   3.365 +            " 0x%"PRIx32"/0x%"PRIx32"/%"PRIu32")\n",
   3.366 +            filename, hdr.mandatory_flags, hdr.optional_flags,
   3.367 +            hdr.optional_data_len);
   3.368 +
   3.369      libxl_domain_suspend(&ctx, NULL, domid, fd);
   3.370      close(fd);
   3.371  
   3.372 @@ -1385,13 +1588,15 @@ int main_restore(int argc, char **argv)
   3.373          }
   3.374      }
   3.375  
   3.376 -    if (optind >= argc - 1) {
   3.377 +    if (argc-optind == 1) {
   3.378 +        checkpoint_file = argv[optind];
   3.379 +    } else if (argc-optind == 2) {
   3.380 +        config_file = argv[optind];
   3.381 +        checkpoint_file = argv[optind + 1];
   3.382 +    } else {
   3.383          help("restore");
   3.384          exit(2);
   3.385      }
   3.386 -
   3.387 -    config_file = argv[optind];
   3.388 -    checkpoint_file = argv[optind + 1];
   3.389      create_domain(debug, daemonize, config_file, checkpoint_file, paused);
   3.390      exit(0);
   3.391  }
   3.392 @@ -1399,6 +1604,7 @@ int main_restore(int argc, char **argv)
   3.393  int main_save(int argc, char **argv)
   3.394  {
   3.395      char *filename = NULL, *p = NULL;
   3.396 +    const char *config_filename;
   3.397      int checkpoint = 0;
   3.398      int opt;
   3.399  
   3.400 @@ -1416,14 +1622,15 @@ int main_save(int argc, char **argv)
   3.401          }
   3.402      }
   3.403  
   3.404 -    if (optind >= argc - 1) {
   3.405 +    if (argc-optind < 1 || argc-optind > 3) {
   3.406          help("save");
   3.407          exit(2);
   3.408      }
   3.409  
   3.410      p = argv[optind];
   3.411      filename = argv[optind + 1];
   3.412 -    save_domain(p, filename, checkpoint);
   3.413 +    config_filename = argv[optind + 2];
   3.414 +    save_domain(p, filename, checkpoint, config_filename);
   3.415      exit(0);
   3.416  }
   3.417