uint32_t min_retains;
char *dir;
+ char *desc;
/*
* An ordered list of previous archives. Each list
static int _vg_write(struct format_instance *fi, struct volume_group *vg)
{
+ struct archive_c *bc = (struct archive_c *) fi->private;
+
int r = 0, i, fd;
unsigned int index = 0;
- struct archive_c *bc = (struct archive_c *) fi->private;
struct archive_file *last;
FILE *fp = NULL;
char temp_file[PATH_MAX], archive_name[PATH_MAX];
return 0;
}
- if (!text_vg_export(fp, vg)) {
+ if (!text_vg_export(fp, vg, bc->desc)) {
stack;
fclose(fp);
return 0;
destroy: _destroy
};
+/*
+ * FIXME: format_instances shouldn't be allocated from the pool.
+ */
struct format_instance *archive_format_create(struct cmd_context *cmd,
- const char *dir,
- uint32_t retain_days,
- uint32_t min_retains)
+ const char *dir,
+ uint32_t retain_days,
+ uint32_t min_retains,
+ const char *desc)
{
struct format_instance *fi;
struct archive_c *bc = NULL;
goto bad;
}
+ if (!(bc->desc = pool_strdup(mem, desc))) {
+ stack;
+ goto bad;
+ }
+
bc->retain_days = retain_days;
bc->min_retains = min_retains;
va_end(ap);
}
-static int _print_header(struct formatter *f, struct volume_group *vg)
+static int _print_header(struct formatter *f,
+ struct volume_group *vg, const char *desc)
{
time_t t;
_out(f,
"# This file was originally generated by the LVM2 library\n"
"# Generated: %s\n", ctime(&t));
+
+ _out(f, "description = \"%s\"", desc);
+ _out(f, "creation_time = %lu\n", t);
+
return 1;
}
static inline const char *
_get_pv_name(struct formatter *f, struct physical_volume *pv)
{
- return (pv) ? (const char *)
+ return (pv) ? (const char *)
hash_lookup(f->pv_names, dev_name(pv->dev)) :
"Missing";
}
return 0;
}
-int text_vg_export(FILE *fp, struct volume_group *vg)
+int text_vg_export(FILE *fp, struct volume_group *vg, const char *desc)
{
int r = 0;
struct formatter *f;
#define fail do {stack; goto out;} while(0)
- if (!_print_header(f, vg))
+ if (!_print_header(f, vg, desc))
fail;
_out(f, "%s {", vg->name);
struct text_c {
char *path;
+ char *desc;
struct uuid_map *um;
};
return 0;
}
- if (!text_vg_export(fp, vg)) {
+ if (!text_vg_export(fp, vg, tc->desc)) {
log_error("Failed to write metadata to %s.", temp_file);
fclose(fp);
return 0;
struct format_instance *text_format_create(struct cmd_context *cmd,
const char *file,
- struct uuid_map *um)
+ struct uuid_map *um,
+ const char *desc)
{
- const char *no_alloc = "Couldn't allocate text format object.";
-
struct format_instance *fi;
- char *path;
+ char *path, *d;
struct text_c *tc;
if (!(fi = dbg_malloc(sizeof(*fi)))) {
- log_err(no_alloc);
- return NULL;
+ stack;
+ goto no_mem;
}
if (!(path = dbg_strdup(file))) {
- dbg_free(fi);
- log_err(no_alloc);
- return NULL;
+ stack;
+ goto no_mem;
+ }
+
+ if (!(d = dbg_strdup(desc))) {
+ stack;
+ goto no_mem;
}
if (!(tc = dbg_malloc(sizeof(*tc)))) {
- dbg_free(fi);
- dbg_free(path);
- log_err(no_alloc);
- return NULL;
+ stack;
+ goto no_mem;
}
tc->path = path;
+ tc->desc = d;
tc->um = um;
fi->cmd = cmd;
fi->private = tc;
return fi;
+
+ no_mem:
+ if (fi)
+ dbg_free(fi);
+
+ if (path)
+ dbg_free(path);
+
+ if (d)
+ dbg_free(path);
+
+ log_err("Couldn't allocate text format object.");
+ return NULL;
}
#include "uuid-map.h"
/*
- * The archive format is used to maintain a set of metadata backup files
- * in an archive directory.
- * 'retain_days' is the minimum number of days that an archive file must
- * be held for.
- *
- * 'min_archives' is the minimum number of archives required to be kept
- * for each volume group.
+ * The archive format is used to maintain a set of metadata
+ * backup files in an archive directory. 'retain_days' is the
+ * minimum number of days that an archive file must be held for.
+ * 'min_archives' is the minimum number of archives required to
+ * be kept for each volume group.
*/
struct format_instance *archive_format_create(struct cmd_context *cmd,
- const char *dir,
- uint32_t retain_days,
- uint32_t min_archives);
+ const char *dir,
+ uint32_t retain_days,
+ uint32_t min_archives,
+ const char *desc);
void backup_expire(struct format_instance *fi);
*/
struct format_instance *text_format_create(struct cmd_context *cmd,
const char *file,
- struct uuid_map *um);
+ struct uuid_map *um,
+ const char *desc);
#endif
int read_flags(uint32_t *status, int type, struct config_value *cv);
-int text_vg_export(FILE *fp, struct volume_group *vg);
+int text_vg_export(FILE *fp, struct volume_group *vg, const char *desc);
struct volume_group *text_vg_import(struct cmd_context *cmd, const char *file,
struct uuid_map *um);
struct pool *mem;
/* misc. vars needed by format handler */
+ char *cmd_line;
char *dev_dir;
struct dev_filter *filter;
struct config_file *cf;
struct volume_group *vg,
struct list *acceptable_pvs);
-int lv_reduce(struct format_instance *fi,
+int lv_reduce(struct format_instance *fi,
struct logical_volume *lv, uint32_t extents);
int lv_extend(struct format_instance *fi,
_archive_params.enabled = flag;
}
+static char *_build_desc(struct pool *mem, const char *line, int before)
+{
+ size_t len = strlen(line) + 32;
+ char *buffer;
+
+ if (!(buffer = pool_zalloc(mem, strlen(line) + 32))) {
+ stack;
+ return NULL;
+ }
+
+ if (snprintf(buffer, len,
+ "Created %s executing '%s'",
+ before ? "*before*" : "*after*", line) < 0) {
+ stack;
+ return NULL;
+ }
+
+ return buffer;
+}
+
static int __archive(struct volume_group *vg)
{
int r;
struct format_instance *archiver;
+ char *desc;
+
+ if (!(desc = _build_desc(vg->cmd->mem, vg->cmd->cmd_line, 1))) {
+ stack;
+ return 0;
+ }
if (!(archiver = archive_format_create(vg->cmd,
- _archive_params.dir,
- _archive_params.keep_days,
- _archive_params.keep_number))) {
+ _archive_params.dir,
+ _archive_params.keep_days,
+ _archive_params.keep_number,
+ desc))) {
log_error("Couldn't create archiver object.");
return 0;
}
int r;
struct format_instance *tf;
char name[PATH_MAX];
+ char *desc;
+
+ if (!(desc = _build_desc(vg->cmd->mem, vg->cmd->cmd_line, 0))) {
+ stack;
+ return 0;
+ }
if (lvm_snprintf(name, sizeof(name), "%s/%s",
_backup_params.dir, vg->name) < 0) {
log_verbose("Creating volume group backup \"%s\"", name);
- if (!(tf = text_format_create(vg->cmd, name, the_um))) {
+ if (!(tf = text_format_create(vg->cmd, name, the_um, desc))) {
stack;
return 0;
}
struct volume_group *vg;
struct format_instance *tf;
- if (!(tf = text_format_create(cmd, file, the_um))) {
+ if (!(tf = text_format_create(cmd, file, the_um, cmd->cmd_line))) {
log_error("Couldn't create text format object.");
- return 0;
+ return NULL;
}
if (!(vg = tf->ops->vg_read(tf, vg_name)))
backup_enable(settings->backup);
}
+static char *_copy_command_line(struct pool *mem, int argc, char **argv)
+{
+ int i;
+
+ /*
+ * Build up the complete command line, used as a
+ * description for backups.
+ */
+ if (!pool_begin_object(cmd->mem, 128))
+ goto bad;
+
+ for (i = 0; i < argc; i++) {
+ if (!pool_grow_object(cmd->mem, argv[i], strlen(argv[i])))
+ goto bad;
+
+ if (i < (argc - 1))
+ if (!pool_grow_object(cmd->mem, " ", 1));
+ }
+
+ /*
+ * Terminate.
+ */
+ if (!pool_grow_object(mem, "\0", 1))
+ goto bad;
+
+ return pool_end_object(mem);
+
+ bad:
+ log_err("Couldn't copy command line.");
+ pool_abandon_object(mem);
+ return NULL;
+}
+
static int run_command(int argc, char **argv)
{
int ret = 0;
+ if (!(cmd->cmd_line = _copy_command_line(cmd->mem, argc, argv)))
+ return ECMD_FAILED;
+
if (!(the_command = find_command(argv[0])))
return ENO_SUCH_CMD;
init_cmd_name(find_config_int(cf->root, "log/command_names", '/', 0));
- _default_settings.test = find_config_int(cf->root, "global/test",
+ _default_settings.test = find_config_int(cf->root, "global/test",
'/', 0);
if (find_config_int(cf->root, "log/overwrite", '/', 0))
open_mode = "w";
int r;
struct format_instance *tf;
- if (!(tf = text_format_create(vg->cmd, file, the_um))) {
+ if (!(tf = text_format_create(vg->cmd, file, the_um,
+ vg->cmd->cmd_line))) {
log_error("Couldn't create backup object.");
return 0;
}