summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Teigland <teigland@redhat.com>2014-10-10 15:46:35 -0500
committerDavid Teigland <teigland@redhat.com>2014-10-17 16:58:50 -0500
commit3e7915b20aa95759c7e4b92b8cf58de336ac6182 (patch)
tree681d2b3b8c4199d10b9af6008f5dee88e30ccdc5
parent4461c624bc59ddca60f4bbf1eb447d64c8d4aeb2 (diff)
downloadlvm2-dev-dct-systemid2.tar.gz
system_id: use for VG ownershipdev-dct-systemid2
See included lvmsystemid(7) for full description.
-rw-r--r--lib/commands/toolcontext.c139
-rw-r--r--lib/commands/toolcontext.h6
-rw-r--r--lib/config/config_settings.h8
-rw-r--r--lib/config/defaults.h1
-rw-r--r--lib/metadata/metadata-exported.h2
-rw-r--r--lib/metadata/metadata.c96
-rw-r--r--lib/metadata/vg.c16
-rw-r--r--lib/metadata/vg.h1
-rw-r--r--lib/misc/lvm-string.c35
-rw-r--r--lib/misc/lvm-string.h2
-rw-r--r--lib/report/columns.h3
-rw-r--r--lib/report/properties.c2
-rw-r--r--man/Makefile.in2
-rw-r--r--man/lvmsystemid.7.in200
-rw-r--r--tools/args.h2
-rw-r--r--tools/commands.h6
-rw-r--r--tools/toollib.c21
-rw-r--r--tools/vgchange.c69
-rw-r--r--tools/vgcreate.c1
-rw-r--r--tools/vgexport.c1
-rw-r--r--tools/vgimport.c1
21 files changed, 586 insertions, 28 deletions
diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c
index e38aa5634..924c7b217 100644
--- a/lib/commands/toolcontext.c
+++ b/lib/commands/toolcontext.c
@@ -55,6 +55,89 @@
static const size_t linebuffer_size = 4096;
+
+/* Copy the input string, removing invalid characters. */
+
+char *system_id_from_string(struct cmd_context *cmd, const char *str)
+{
+ char *system_id;
+
+ if (!(system_id = dm_pool_zalloc(cmd->mem, strlen(str) + 1)))
+ return NULL;
+
+ copy_valid_chars(str, system_id);
+
+ if (!system_id[0])
+ return NULL;
+
+ return system_id;
+}
+
+static char *_read_system_id_from_file(struct cmd_context *cmd, const char *file)
+{
+ char line[NAME_LEN + 1];
+ FILE *fp;
+
+ if (!file || !strlen(file) || !file[0])
+ return NULL;
+
+ fp = fopen(file, "r");
+ if (!fp)
+ return NULL;
+
+ memset(line, 0, sizeof(line));
+
+ while (fgets(line, NAME_LEN, fp)) {
+ if (line[0] == '#' || line[0] == '\n')
+ continue;
+
+ fclose(fp);
+ return system_id_from_string(cmd, line);
+ }
+
+ fclose(fp);
+ return NULL;
+}
+
+char *system_id_from_source(struct cmd_context *cmd, const char *source)
+{
+ struct utsname uts;
+ char filebuf[PATH_MAX];
+ const char *file;
+ const char *etc_str;
+ const char *str;
+ char *system_id = NULL;
+
+ if (!strcmp(source, "uname")) {
+ if (!uname(&uts) && strncmp(uts.nodename, "localhost", 9))
+ system_id = system_id_from_string(cmd, uts.nodename);
+ goto out;
+ }
+
+ /* lvm.conf and lvm-local.conf are merged into one config tree */
+ if (!strcmp(source, "lvmlocal")) {
+ if ((str = find_config_tree_str(cmd, local_system_id_CFG, NULL)))
+ system_id = system_id_from_string(cmd, str);
+ goto out;
+ }
+
+ if (!strcmp(source, "machineid")) {
+ memset(filebuf, 0, sizeof(filebuf));
+ etc_str = find_config_tree_str(cmd, global_etc_CFG, NULL);
+ if (dm_snprintf(filebuf, sizeof(filebuf), "%s/machine-id", etc_str) >= 0)
+ system_id = _read_system_id_from_file(cmd, filebuf);
+ goto out;
+ }
+
+ if (!strcmp(source, "file")) {
+ file = find_config_tree_str(cmd, global_system_id_file_CFG, NULL);
+ system_id = _read_system_id_from_file(cmd, file);
+ goto out;
+ }
+out:
+ return system_id;
+}
+
static int _get_env_vars(struct cmd_context *cmd)
{
const char *e;
@@ -561,7 +644,7 @@ static int _init_tags(struct cmd_context *cmd, struct dm_config_tree *cft)
return 1;
}
-static int _load_config_file(struct cmd_context *cmd, const char *tag)
+static int _load_config_file(struct cmd_context *cmd, const char *tag, int local)
{
static char config_file[PATH_MAX] = "";
const char *filler = "";
@@ -569,6 +652,10 @@ static int _load_config_file(struct cmd_context *cmd, const char *tag)
if (*tag)
filler = "_";
+ else if (local) {
+ filler = "-";
+ tag = "local";
+ }
if (dm_snprintf(config_file, sizeof(config_file), "%s/lvm%s%s.conf",
cmd->system_dir, filler, tag) < 0) {
@@ -596,7 +683,13 @@ static int _load_config_file(struct cmd_context *cmd, const char *tag)
return 1;
}
-/* Find and read first config file */
+/*
+ * Find and read lvm.conf and lvm-local.conf
+ *
+ * lvm-local.conf is independent of any tag-based configs
+ * because it uses "-" instead of "_".
+ */
+
static int _init_lvm_conf(struct cmd_context *cmd)
{
/* No config file if LVM_SYSTEM_DIR is empty */
@@ -608,9 +701,11 @@ static int _init_lvm_conf(struct cmd_context *cmd)
return 1;
}
- if (!_load_config_file(cmd, ""))
+ if (!_load_config_file(cmd, "", 0))
return_0;
+ _load_config_file(cmd, "", 1);
+
return 1;
}
@@ -621,7 +716,7 @@ static int _init_tag_configs(struct cmd_context *cmd)
/* Tag list may grow while inside this loop */
dm_list_iterate_items(sl, &cmd->tags) {
- if (!_load_config_file(cmd, sl->str))
+ if (!_load_config_file(cmd, sl->str, 0))
return_0;
}
@@ -1335,6 +1430,39 @@ static int _init_hostname(struct cmd_context *cmd)
return 1;
}
+static int _init_system_id(struct cmd_context *cmd)
+{
+ const char *source;
+ int local_set;
+
+ cmd->system_id = NULL;
+
+ local_set = !!find_config_tree_str(cmd, local_system_id_CFG, NULL);
+
+ source = find_config_tree_str(cmd, global_system_id_source_CFG, NULL);
+ if (!source)
+ source = "none";
+
+ /* Defining local system_id but not using it is probably a config mistake. */
+ if (local_set && strcmp(source, "lvmlocal"))
+ log_warn("Local system_id is not used by system_id_source %s.", source);
+
+ if (!strcmp(source, "none"))
+ return 1;
+
+ if ((cmd->system_id = system_id_from_source(cmd, source)))
+ return 1;
+
+ /*
+ * The source failed to resolve a system_id. In this case allow
+ * VGs with no system_id to be accessed, but not VGs with a system_id.
+ */
+
+ log_warn("No system_id found from system_id_source %s.", source);
+ cmd->unknown_system_id = 1;
+ return 1;
+}
+
static int _init_backup(struct cmd_context *cmd)
{
uint32_t days, min;
@@ -1563,6 +1691,9 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
if (!_init_profiles(cmd))
goto_out;
+ if (!_init_system_id(cmd))
+ goto_out;
+
if (!(cmd->dev_types = create_dev_types(cmd->proc_dir,
find_config_tree_node(cmd, devices_types_CFG, NULL))))
goto_out;
diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
index d99cc280b..4c2f89b24 100644
--- a/lib/commands/toolcontext.h
+++ b/lib/commands/toolcontext.h
@@ -71,6 +71,7 @@ struct cmd_context {
struct dm_list formats; /* Available formats */
struct dm_list segtypes; /* Available segment types */
+ const char *system_id;
const char *hostname;
const char *kernel_vsn;
@@ -95,6 +96,8 @@ struct cmd_context {
unsigned threaded:1; /* Set if running within a thread e.g. clvmd */
unsigned independent_metadata_areas:1; /* Active formats have MDAs outside PVs */
+ unsigned unknown_system_id:1;
+ unsigned skip_systemid_check:1;
struct dev_types *dev_types;
@@ -160,4 +163,7 @@ int init_lvmcache_orphans(struct cmd_context *cmd);
struct format_type *get_format_by_name(struct cmd_context *cmd, const char *format);
+char *system_id_from_source(struct cmd_context *cmd, const char *system_id_source);
+char *system_id_from_string(struct cmd_context *cmd, const char *system_id_string);
+
#endif
diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h
index a6c1d1b04..85974b78f 100644
--- a/lib/config/config_settings.h
+++ b/lib/config/config_settings.h
@@ -82,6 +82,7 @@ cfg_section(metadata_CFG_SECTION, "metadata", root_CFG_SECTION, CFG_ADVANCED, vs
cfg_section(report_CFG_SECTION, "report", root_CFG_SECTION, CFG_ADVANCED | CFG_PROFILABLE, vsn(1, 0, 0), NULL)
cfg_section(dmeventd_CFG_SECTION, "dmeventd", root_CFG_SECTION, 0, vsn(1, 2, 3), NULL)
cfg_section(tags_CFG_SECTION, "tags", root_CFG_SECTION, 0, vsn(1, 0, 18), NULL)
+cfg_section(local_CFG_SECTION, "local", root_CFG_SECTION, 0, vsn(2, 2, 112), NULL)
cfg(config_checks_CFG, "checks", config_CFG_SECTION, 0, CFG_TYPE_BOOL, 1, vsn(2, 2, 99), "Configuration tree check on each LVM command execution.")
cfg(config_abort_on_errors_CFG, "abort_on_errors", config_CFG_SECTION, 0, CFG_TYPE_BOOL, 0, vsn(2,2,99), "Abort LVM command execution if configuration is invalid.")
@@ -141,6 +142,7 @@ cfg(log_prefix_CFG, "prefix", log_CFG_SECTION, CFG_ALLOW_EMPTY, CFG_TYPE_STRING,
cfg(log_activation_CFG, "activation", log_CFG_SECTION, 0, CFG_TYPE_BOOL, 0, vsn(1, 0, 0), NULL)
cfg(log_activate_file_CFG, "activate_file", log_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL)
cfg_array(log_debug_classes_CFG, "debug_classes", log_CFG_SECTION, CFG_ALLOW_EMPTY, CFG_TYPE_STRING, "#Smemory#Sdevices#Sactivation#Sallocation#Slvmetad#Smetadata#Scache#Slocking", vsn(2, 2, 99), NULL)
+cfg(log_silent_other_systemid_CFG, "silent_other_systemid", log_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_SILENT, vsn(2, 2, 112), NULL)
cfg(backup_backup_CFG, "backup", backup_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_BACKUP_ENABLED, vsn(1, 0, 0), NULL)
cfg_runtime(backup_backup_dir_CFG, "backup_dir", backup_CFG_SECTION, 0, CFG_TYPE_STRING, vsn(1, 0, 0), NULL)
@@ -162,6 +164,7 @@ cfg(global_format_CFG, "format", global_CFG_SECTION, 0, CFG_TYPE_STRING, DEFAULT
cfg_array(global_format_libraries_CFG, "format_libraries", global_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL)
cfg_array(global_segment_libraries_CFG, "segment_libraries", global_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 18), NULL)
cfg(global_proc_CFG, "proc", global_CFG_SECTION, 0, CFG_TYPE_STRING, DEFAULT_PROC_DIR, vsn(1, 0, 0), NULL)
+cfg(global_etc_CFG, "etc", global_CFG_SECTION, 0, CFG_TYPE_STRING, DEFAULT_ETC_DIR, vsn(2, 2, 112), NULL)
cfg(global_locking_type_CFG, "locking_type", global_CFG_SECTION, 0, CFG_TYPE_INT, 1, vsn(1, 0, 0), NULL)
cfg(global_wait_for_locks_CFG, "wait_for_locks", global_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_WAIT_FOR_LOCKS, vsn(2, 2, 50), NULL)
cfg(global_fallback_to_clustered_locking_CFG, "fallback_to_clustered_locking", global_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_FALLBACK_TO_CLUSTERED_LOCKING, vsn(2, 2, 42), NULL)
@@ -188,6 +191,8 @@ cfg_array(global_cache_check_options_CFG, "cache_check_options", global_CFG_SECT
cfg(global_cache_dump_executable_CFG, "cache_dump_executable", global_CFG_SECTION, CFG_ALLOW_EMPTY, CFG_TYPE_STRING, CACHE_DUMP_CMD, vsn(2, 2, 108), NULL)
cfg(global_cache_repair_executable_CFG, "cache_repair_executable", global_CFG_SECTION, CFG_ALLOW_EMPTY, CFG_TYPE_STRING, CACHE_REPAIR_CMD, vsn(2, 2, 108), NULL)
cfg_array(global_cache_repair_options_CFG, "cache_repair_options", global_CFG_SECTION, 0, CFG_TYPE_STRING, "#S" DEFAULT_CACHE_REPAIR_OPTIONS, vsn(2, 2, 108), NULL)
+cfg(global_system_id_source_CFG, "system_id_source", global_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(2, 2, 112), NULL)
+cfg(global_system_id_file_CFG, "system_id_file", global_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(2, 2, 112), NULL)
cfg(activation_checks_CFG, "checks", activation_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_ACTIVATION_CHECKS, vsn(2, 2, 86), NULL)
cfg(activation_udev_sync_CFG, "udev_sync", activation_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_UDEV_SYNC, vsn(2, 2, 51), NULL)
@@ -272,4 +277,7 @@ cfg(tags_hosttags_CFG, "hosttags", tags_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_H
cfg_section(tag_CFG_SUBSECTION, "tag", tags_CFG_SECTION, CFG_NAME_VARIABLE | CFG_DEFAULT_UNDEFINED, vsn(1, 0, 18), NULL)
cfg(tag_host_list_CFG, "host_list", tag_CFG_SUBSECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 18), NULL)
+cfg(local_system_id_CFG, "system_id", local_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(2, 2, 112), NULL)
+cfg_array(local_allow_system_id_CFG, "allow_system_id", local_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(2, 2, 112), NULL)
+
cfg(CFG_COUNT, NULL, root_CFG_SECTION, 0, CFG_TYPE_INT, 0, vsn(0, 0, 0), NULL)
diff --git a/lib/config/defaults.h b/lib/config/defaults.h
index 2cb1e5aa9..30f09a369 100644
--- a/lib/config/defaults.h
+++ b/lib/config/defaults.h
@@ -29,6 +29,7 @@
#define DEFAULT_DEV_DIR "/dev"
#define DEFAULT_PROC_DIR "/proc"
+#define DEFAULT_ETC_DIR "/etc"
#define DEFAULT_OBTAIN_DEVICE_LIST_FROM_UDEV 1
#define DEFAULT_SYSFS_SCAN 1
#define DEFAULT_MD_COMPONENT_DETECTION 1
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h
index bf6f1c122..7c2dcefb2 100644
--- a/lib/metadata/metadata-exported.h
+++ b/lib/metadata/metadata-exported.h
@@ -161,6 +161,7 @@
#define FAILED_CLUSTERED 0x00000040U
#define FAILED_ALLOCATION 0x00000080U
#define FAILED_EXIST 0x00000100U
+#define FAILED_SYSTEMID 0x00000200U
#define SUCCESS 0x00000000U
#define VGMETADATACOPIES_ALL UINT32_MAX
@@ -1145,6 +1146,7 @@ struct vgcreate_params {
alloc_policy_t alloc;
int clustered; /* FIXME: put this into a 'status' variable instead? */
uint32_t vgmetadatacopies;
+ const char *system_id;
};
int validate_major_minor(const struct cmd_context *cmd,
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
index 9308cb739..2057ada66 100644
--- a/lib/metadata/metadata.c
+++ b/lib/metadata/metadata.c
@@ -4223,6 +4223,94 @@ static struct volume_group *_recover_vg(struct cmd_context *cmd,
return (struct volume_group *)vg;
}
+static int allow_system_id(struct cmd_context *cmd, const char *system_id)
+{
+ const struct dm_config_node *cn;
+ const struct dm_config_value *cv;
+ const char *str;
+
+ if (!(cn = find_config_tree_node(cmd, local_allow_system_id_CFG, NULL)))
+ return 0;
+
+ for (cv = cn->v; cv; cv = cv->next) {
+ if (cv->type == DM_CFG_EMPTY_ARRAY)
+ break;
+ if (cv->type != DM_CFG_STRING) {
+ log_error("Ignoring invalid string in allow_system_id list");
+ continue;
+ }
+ str = cv->v.str;
+ if (!*str) {
+ log_error("Ignoring empty string in config file");
+ continue;
+ }
+
+ if (!strcmp(str, system_id))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int _access_vg_clustered(struct cmd_context *cmd, struct volume_group *vg)
+{
+ if (vg_is_clustered(vg) && !locking_is_clustered()) {
+ if (!cmd->ignore_clustered_vgs)
+ log_error("Skipping clustered volume group %s", vg->name);
+ else
+ log_verbose("Skipping clustered volume group %s", vg->name);
+ return 0;
+ }
+ return 1;
+}
+
+static int _access_vg_systemid(struct cmd_context *cmd, struct volume_group *vg)
+{
+ if (cmd->skip_systemid_check)
+ return 1;
+
+ if (vg->system_id && vg->system_id[0] && cmd->unknown_system_id)
+ goto fail;
+
+ if (vg->system_id && vg->system_id[0] && cmd->system_id && cmd->system_id[0]) {
+ if (!strcmp(vg->system_id, cmd->system_id))
+ return 1;
+ if (allow_system_id(cmd, vg->system_id))
+ return 1;
+ goto fail;
+ }
+
+ return 1;
+
+fail:
+ if (find_config_tree_bool(cmd, log_silent_other_systemid_CFG, NULL))
+ return 0;
+ log_warn("Cannot access VG %s with system id \"%s\" from local system id \"%s\"",
+ vg->name, vg->system_id, cmd->system_id ?: "unknown");
+ return 0;
+}
+
+/*
+ * FIXME: move _vg_bad_status_bits() checks in here.
+ */
+static int _access_vg(struct cmd_context *cmd, struct volume_group *vg, uint32_t *failure)
+{
+ if (!is_real_vg(vg->name))
+ return 1;
+
+ if (!_access_vg_clustered(cmd, vg)) {
+ *failure |= FAILED_CLUSTERED;
+ return 0;
+ }
+
+ if (!_access_vg_systemid(cmd, vg)) {
+ *failure |= FAILED_SYSTEMID;
+ return 0;
+ }
+
+ return 1;
+}
+
/*
* Consolidated locking, reading, and status flag checking.
*
@@ -4282,14 +4370,8 @@ static struct volume_group *_vg_lock_and_read(struct cmd_context *cmd, const cha
goto bad;
}
- if (vg_is_clustered(vg) && !locking_is_clustered()) {
- if (!cmd->ignore_clustered_vgs)
- log_error("Skipping clustered volume group %s", vg->name);
- else
- log_verbose("Skipping clustered volume group %s", vg->name);
- failure |= FAILED_CLUSTERED;
+ if (!_access_vg(cmd, vg, &failure))
goto bad;
- }
/* consistent == 0 when VG is not found, but failed == FAILED_NOTFOUND */
if (!consistent && !failure) {
diff --git a/lib/metadata/vg.c b/lib/metadata/vg.c
index 71b894b8f..57c9acfd9 100644
--- a/lib/metadata/vg.c
+++ b/lib/metadata/vg.c
@@ -603,6 +603,22 @@ int vg_set_clustered(struct volume_group *vg, int clustered)
return 1;
}
+/* The input string has already been validated. */
+
+int vg_set_system_id(struct volume_group *vg, const char *system_id)
+{
+ if (!system_id) {
+ vg->system_id = NULL;
+ return 1;
+ }
+
+ if (!(vg->system_id = dm_pool_strdup(vg->vgmem, system_id))) {
+ log_error("vg_set_system_id no mem");
+ return 0;
+ }
+ return 1;
+}
+
char *vg_attr_dup(struct dm_pool *mem, const struct volume_group *vg)
{
char *repstr;
diff --git a/lib/metadata/vg.h b/lib/metadata/vg.h
index fffe9a7d5..23d60acdd 100644
--- a/lib/metadata/vg.h
+++ b/lib/metadata/vg.h
@@ -144,6 +144,7 @@ uint32_t vg_seqno(const struct volume_group *vg);
uint64_t vg_status(const struct volume_group *vg);
int vg_set_alloc_policy(struct volume_group *vg, alloc_policy_t alloc);
int vg_set_clustered(struct volume_group *vg, int clustered);
+int vg_set_system_id(struct volume_group *vg, const char *system_id);
uint64_t vg_size(const struct volume_group *vg);
uint64_t vg_free(const struct volume_group *vg);
uint64_t vg_extent_size(const struct volume_group *vg);
diff --git a/lib/misc/lvm-string.c b/lib/misc/lvm-string.c
index 84c870837..f4c7110a0 100644
--- a/lib/misc/lvm-string.c
+++ b/lib/misc/lvm-string.c
@@ -101,6 +101,41 @@ int validate_name(const char *n)
return (_validate_name(n) == NAME_VALID) ? 1 : 0;
}
+/*
+ * Copy valid characters from source to destination.
+ * Invalid characters are skipped. Copying is stopped
+ * when NAME_LEN characters have been copied.
+ */
+
+void copy_valid_chars(const char *src, char *dst)
+{
+ const char *s = src;
+ char *d = dst;
+ int len = 0;
+ int i;
+ char c;
+
+ if (!s || !*s)
+ return;
+
+ for (i = 0; i < strlen(src); i++) {
+ c = *s;
+
+ if (!isalnum(c) && c != '.' && c != '_' && c != '-' && c != '+') {
+ s++;
+ continue;
+ }
+
+ *d = *s;
+ d++;
+ s++;
+ len++;
+
+ if (len == NAME_LEN)
+ break;
+ }
+}
+
static const char *_lvname_has_reserved_prefix(const char *lvname)
{
static const char _prefixes[][12] = {
diff --git a/lib/misc/lvm-string.h b/lib/misc/lvm-string.h
index 6a13e9ea4..0615f41cd 100644
--- a/lib/misc/lvm-string.h
+++ b/lib/misc/lvm-string.h
@@ -44,6 +44,8 @@ int validate_name(const char *n);
name_error_t validate_name_detailed(const char *n);
int validate_tag(const char *n);
+void copy_valid_chars(const char *src, char *dst);
+
int apply_lvname_restrictions(const char *name);
int is_reserved_lvname(const char *name);
diff --git a/lib/report/columns.h b/lib/report/columns.h
index a2f34f928..a7cc82816 100644
--- a/lib/report/columns.h
+++ b/lib/report/columns.h
@@ -130,7 +130,8 @@ FIELD(VGS, vg, STR, "AllocPol", cmd, 10, vgallocationpolicy, vg_allocation_polic
FIELD(VGS, vg, BIN, "Clustered", cmd, 10, vgclustered, vg_clustered, "Set if VG is clustered.", 0)
FIELD(VGS, vg, SIZ, "VSize", cmd, 5, vgsize, vg_size, "Total size of VG in current units.", 0)
FIELD(VGS, vg, NUM, "VFree", cmd, 5, vgfree, vg_free, "Total amount of free space in current units.", 0)
-FIELD(VGS, vg, STR, "SYS ID", system_id, 6, string, vg_sysid, "System ID indicating when and where it was created.", 0)
+FIELD(VGS, vg, STR, "SYS ID", system_id, 6, string, vg_sysid, "System ID of the VG.", 0)
+FIELD(VGS, vg, STR, "System ID", system_id, 9, string, vg_systemid, "System ID of the VG.", 0)
FIELD(VGS, vg, SIZ, "Ext", extent_size, 3, size32, vg_extent_size, "Size of Physical Extents in current units.", 0)
FIELD(VGS, vg, NUM, "#Ext", extent_count, 4, uint32, vg_extent_count, "Total number of Physical Extents.", 0)
FIELD(VGS, vg, NUM, "Free", free_count, 4, uint32, vg_free_count, "Total number of unallocated Physical Extents.", 0)
diff --git a/lib/report/properties.c b/lib/report/properties.c
index 8f4f47250..e14b2d2c9 100644
--- a/lib/report/properties.c
+++ b/lib/report/properties.c
@@ -320,6 +320,8 @@ GET_VG_NUM_PROPERTY_FN(vg_free, (SECTOR_SIZE * vg_free(vg)))
#define _vg_free_set prop_not_implemented_set
GET_VG_STR_PROPERTY_FN(vg_sysid, vg_system_id_dup(vg))
#define _vg_sysid_set prop_not_implemented_set
+GET_VG_STR_PROPERTY_FN(vg_systemid, vg_system_id_dup(vg))
+#define _vg_systemid_set prop_not_implemented_set
GET_VG_NUM_PROPERTY_FN(vg_extent_size, (SECTOR_SIZE * vg->extent_size))
#define _vg_extent_size_set prop_not_implemented_set
GET_VG_NUM_PROPERTY_FN(vg_extent_count, vg->extent_count)
diff --git a/man/Makefile.in b/man/Makefile.in
index 5a11ae2cc..2258e536e 100644
--- a/man/Makefile.in
+++ b/man/Makefile.in
@@ -41,7 +41,7 @@ LVMETAD =
endif
MAN5=lvm.conf.5
-MAN7=
+MAN7=lvmsystemid.7
MAN8=lvm-dumpconfig.8 \
lvchange.8 lvconvert.8 lvcreate.8 lvdisplay.8 lvextend.8 lvm.8 \
lvmchange.8 lvmconf.8 lvmdiskscan.8 lvmdump.8 lvmsadc.8 lvmsar.8 \
diff --git a/man/lvmsystemid.7.in b/man/lvmsystemid.7.in
new file mode 100644
index 000000000..cb6c0b36e
--- /dev/null
+++ b/man/lvmsystemid.7.in
@@ -0,0 +1,200 @@
+.TH "LVMSYSTEMID" "7" "LVM TOOLS #VERSION#" "Red Hat, Inc" "\""
+
+.SH NAME
+lvmsystemid \(em LVM system id
+
+.SH DESCRIPTION
+
+The lvm system_id is a host identifier used by lvm to identify a VG owner.
+vgcreate includes the local system_id in the metadata of a new VG. This
+means the host that creates a VG becomes the VG's owner. lvm commands
+will not operate on a VG owned by another host.
+
+A VG without a system_id can be used by any host, and a host without a
+system_id can use any VG (even a VG with a system_id set). This can lead
+to mistakes if a VG is visible to multiple hosts at once.
+
+To benefit fully from system_id, all hosts must have system_id set, and
+VGs must have system_id set. Two hosts should not be assigned the same
+system_id. Doing so defeats the purpose of the system_id.
+
+Valid system_id characters are the same as valid VG name characters. If a
+system_id contains invalid characters, those characters are omitted and
+remaining characters are used. If a system_id is longer than the maximum
+name length, the characters up to the maximum length are used.
+
+.SS system_id_source
+
+A host's own system_id can be defined in a number of ways. lvm.conf
+global/system_id_source defines the method lvm should use to find the
+local system_id:
+
+.TP
+.B none
+
+lvm will not use a system_id. It will be allowed to access all VGs, and
+will create new VGs without a system_id. An undefined system_id_source is
+equivalent to system_id_source = "none".
+
+.nf
+lvm.conf
+global {
+ system_id_source = "none"
+}
+.fi
+
+.TP
+.B machineid
+
+The content of /etc/machine-id is used as the system_id.
+
+.nf
+lvm.conf
+global {
+ system_id_source = "machineid"
+}
+.fi
+
+.TP
+.B uname
+
+The string utsname.nodename from
+.BR uname (2)
+is used as the system_id. A uname of "localhost" is ignored.
+
+.nf
+lvm.conf
+global {
+ system_id_source = "uname"
+}
+.fi
+
+.TP
+.B lvmlocal
+
+The system_id is defined in lvm-local.conf local/system_id.
+
+.nf
+lvm.conf
+global {
+ system_id_source = "lvmlocal"
+}
+
+lvm-local.conf
+local {
+ system_id = "example_name"
+}
+.fi
+
+.TP
+.B file
+
+The system_id is defined in a file specified by lvm.conf
+global/system_id_file.
+
+.nf
+lvm.conf
+global {
+ system_id_source = "file"
+ system_id_file = "/path/to/file"
+}
+.fi
+
+.LP
+
+Changing system_id_source will often cause the system_id to change, which
+may prevent the host from using VGs that it previously used (see
+allow_system_id below to handle this.)
+
+If a system_id_source other than none fails to resolve a system_id, the
+host will be allowed to access VGs with no system_id, but will not be
+allowed to access VGs with a defined system_id.
+
+.SS allow_system_id
+
+In some cases, it may be useful for a host to access VGs with different
+system_id's, e.g. if a host's system_id changes, and it wants to use VGs
+that it created with its old system_id. To allow a host to access VGs
+with other system_id's, those other system_id's can be listed in
+lvm-local.conf local/allow_system_id.
+
+.nf
+lvm-local.conf
+local {
+ allow_system_id = [ "my_other_name" ]
+}
+.fi
+
+.SS vgcreate
+
+In vgcreate, a host sets its own system_id in the VG metadata.
+To override this and set another system id:
+
+.B vgcreate --systemid
+.I SystemID VG Devices
+
+.B vgcreate --systemidsource
+.I SystemIDSource VG Devices
+
+.I SystemIDSource
+can be a method recognized by lvm.conf system_id_source
+(none, file, lvmlocal, machineid, uname).
+
+Overriding the system_id in either way makes it possible for a host to
+create a VG that it may not be able to use.
+
+.SS vgchange
+
+If a VG has system_id set, changing it with vgchange requires --force.
+vgchange --systemid and --systemidsource are accepted just as with
+vgcreate.
+
+.SS vgexport/vgimport
+
+vgexport clears the system_id.
+
+vgimport sets the VG system_id to the local system_id as determined by
+lvm.conf system_id_sources.
+
+.SS display
+
+The system_id of a vg is displayed with the "systemid" reporting option:
+
+.B vgs -o+systemid
+
+.SS validation
+
+When lvm reads a VG and finds that the VG's system_id does not match
+the local system_id, it will print a warning and not access the VG
+further:
+
+.nf
+Cannot access VG "vg" with system id "x" from local system id "y"
+.fi
+
+To silence these warnings, set lvm.conf log/silent_other_systemid = 1
+(or use a filter to exclude the foreign system's VG.)
+
+.SS clustered VGs
+
+A "clustered" VG should have no system_id set, allowing multiple hosts to
+use it, or it should have a system_id set to a value that multiple hosts
+have added to their allow_system_id list.
+
+.SS creation_host
+
+In vgcreate, the VG metadata field creation_host is set by default to the
+host's uname. The creation_host cannot be changed, and is not used to
+control access. When system_id_source is "uname", the system_id and
+creation_host will be the same.
+
+.SH SEE ALSO
+.BR vgcreate (8),
+.BR vgchange (8),
+.BR vgimport (8),
+.BR vgexport (8),
+.BR lvm.conf (5),
+.BR machine-id (5),
+.BR uname (2),
+.BR vgs (8)
+
diff --git a/tools/args.h b/tools/args.h
index 531148c95..8540b9a40 100644
--- a/tools/args.h
+++ b/tools/args.h
@@ -114,6 +114,8 @@ arg(split_ARG, '\0', "split", NULL, 0)
arg(readonly_ARG, '\0', "readonly", NULL, 0)
arg(atomic_ARG, '\0', "atomic", NULL, 0)
arg(activationmode_ARG, '\0', "activationmode", string_arg, 0)
+arg(systemid_ARG, '\0', "systemid", string_arg, 0)
+arg(systemidsource_ARG, '\0', "systemidsource", string_arg, 0)
/* Allow some variations */
diff --git a/tools/commands.h b/tools/commands.h
index 2e99fb177..03a1f7bc6 100644
--- a/tools/commands.h
+++ b/tools/commands.h
@@ -954,7 +954,8 @@ xx(vgchange,
ignoreskippedcluster_ARG, logicalvolume_ARG, maxphysicalvolumes_ARG,
metadataprofile_ARG, monitor_ARG, noudevsync_ARG, metadatacopies_ARG,
vgmetadatacopies_ARG, partial_ARG, physicalextentsize_ARG, poll_ARG,
- refresh_ARG, resizeable_ARG, resizable_ARG, sysinit_ARG, test_ARG, uuid_ARG)
+ refresh_ARG, resizeable_ARG, resizable_ARG, sysinit_ARG, test_ARG, uuid_ARG,
+ systemid_ARG, systemidsource_ARG, force_ARG)
xx(vgck,
"Check the consistency of volume group(s)",
@@ -1016,7 +1017,8 @@ xx(vgcreate,
maxphysicalvolumes_ARG, metadataprofile_ARG, metadatatype_ARG,
physicalextentsize_ARG, test_ARG, force_ARG, zero_ARG, labelsector_ARG,
metadatasize_ARG, pvmetadatacopies_ARG, metadatacopies_ARG,
- vgmetadatacopies_ARG, dataalignment_ARG, dataalignmentoffset_ARG)
+ vgmetadatacopies_ARG, dataalignment_ARG, dataalignmentoffset_ARG,
+ systemid_ARG, systemidsource_ARG)
xx(vgdisplay,
"Display volume group information",
diff --git a/tools/toollib.c b/tools/toollib.c
index 7f656c6e3..397484961 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -17,6 +17,7 @@
#include <sys/stat.h>
#include <signal.h>
#include <sys/wait.h>
+#include <sys/utsname.h>
const char *command_name(struct cmd_context *cmd)
{
@@ -169,6 +170,9 @@ int ignore_vg(struct volume_group *vg, const char *vg_name, int allow_inconsiste
if ((read_error == FAILED_INCONSISTENT) && allow_inconsistent)
return 0;
+ if (read_error == FAILED_SYSTEMID)
+ return 1;
+
if (read_error == FAILED_NOTFOUND)
*ret = ECMD_FAILED;
else if (read_error == FAILED_CLUSTERED && vg->cmd->ignore_clustered_vgs)
@@ -633,6 +637,7 @@ int vgcreate_params_set_defaults(struct cmd_context *cmd,
vp_def->alloc = vg->alloc;
vp_def->clustered = vg_is_clustered(vg);
vp_def->vgmetadatacopies = vg->mda_copies;
+ vp_def->system_id = vg->system_id ? dm_pool_strdup(cmd->mem, vg->system_id) : NULL;
} else {
vp_def->vg_name = NULL;
extent_size = find_config_tree_int64(cmd,
@@ -647,6 +652,7 @@ int vgcreate_params_set_defaults(struct cmd_context *cmd,
vp_def->alloc = DEFAULT_ALLOC_POLICY;
vp_def->clustered = DEFAULT_CLUSTERED;
vp_def->vgmetadatacopies = DEFAULT_VGMETADATACOPIES;
+ vp_def->system_id = cmd->system_id ? dm_pool_strdup(cmd->mem, cmd->system_id) : NULL;
}
return 1;
@@ -662,6 +668,9 @@ int vgcreate_params_set_from_args(struct cmd_context *cmd,
struct vgcreate_params *vp_new,
struct vgcreate_params *vp_def)
{
+ const char *system_id;
+ const char *system_id_source;
+
vp_new->vg_name = skip_dev_dir(cmd, vp_def->vg_name, NULL);
vp_new->max_lv = arg_uint_value(cmd, maxlogicalvolumes_ARG,
vp_def->max_lv);
@@ -712,6 +721,18 @@ int vgcreate_params_set_from_args(struct cmd_context *cmd,
vp_new->vgmetadatacopies = find_config_tree_int(cmd, metadata_vgmetadatacopies_CFG, NULL);
}
+ if ((system_id = arg_str_value(cmd, systemid_ARG, NULL))) {
+ vp_new->system_id = system_id_from_string(cmd, system_id);
+ } else if ((system_id_source = arg_str_value(cmd, systemidsource_ARG, NULL))) {
+ vp_new->system_id = system_id_from_source(cmd, system_id_source);
+ } else {
+ vp_new->system_id = vp_def->system_id;
+ }
+
+ /* A clustered vg has no system_id unless overriden with the systemid arg. */
+ if (!system_id && vp_new->clustered)
+ vp_new->system_id = NULL;
+
return 1;
}
diff --git a/tools/vgchange.c b/tools/vgchange.c
index 9a9fe4866..5d4efd81e 100644
--- a/tools/vgchange.c
+++ b/tools/vgchange.c
@@ -336,6 +336,9 @@ static int _vgchange_clustered(struct cmd_context *cmd,
}
}
+ if (clustered)
+ vg->system_id = NULL;
+
if (!vg_set_clustered(vg, clustered))
return_0;
@@ -475,6 +478,39 @@ static int _vgchange_profile(struct cmd_context *cmd,
return 1;
}
+static int _vgchange_system_id(struct cmd_context *cmd, struct volume_group *vg)
+{
+ const char *system_id = arg_str_value(cmd, systemid_ARG, NULL);
+ const char *source = arg_str_value(cmd, systemidsource_ARG, NULL);
+
+ if (vg->system_id && vg->system_id[0] && !arg_is_set(cmd, force_ARG)) {
+ log_error("Volume group \"%s\" already has system id \"%s\"",
+ vg->name, vg->system_id);
+ return 0;
+ }
+
+ if (system_id) {
+ vg->system_id = system_id_from_string(cmd, system_id);
+ return 1;
+ }
+
+ if (!source)
+ return 1;
+
+ if (!strcmp(source, "none")) {
+ vg->system_id = NULL;
+ return 1;
+ }
+
+ if ((system_id = system_id_from_source(cmd, source))) {
+ vg->system_id = (char *)system_id;
+ return 1;
+ }
+
+ log_error("No system_id found from source %s", source);
+ return 0;
+}
+
static int vgchange_single(struct cmd_context *cmd, const char *vg_name,
struct volume_group *vg,
void *handle __attribute__((unused)))
@@ -498,8 +534,10 @@ static int vgchange_single(struct cmd_context *cmd, const char *vg_name,
{ clustered_ARG, &_vgchange_clustered },
{ vgmetadatacopies_ARG, &_vgchange_metadata_copies },
{ metadataprofile_ARG, &_vgchange_profile },
- { profile_ARG, &_vgchange_profile},
- { detachprofile_ARG, &_vgchange_profile},
+ { profile_ARG, &_vgchange_profile },
+ { detachprofile_ARG, &_vgchange_profile },
+ { systemid_ARG, &_vgchange_system_id },
+ { systemidsource_ARG, &_vgchange_system_id },
};
if (vg_is_exported(vg)) {
@@ -593,13 +631,19 @@ static int vgchange_single(struct cmd_context *cmd, const char *vg_name,
int vgchange(struct cmd_context *cmd, int argc, char **argv)
{
- /* Update commands that can be combined */
+ int noupdate =
+ arg_count(cmd, activate_ARG) ||
+ arg_count(cmd, monitor_ARG) ||
+ arg_count(cmd, poll_ARG) ||
+ arg_count(cmd, refresh_ARG);
+
int update_partial_safe =
arg_count(cmd, deltag_ARG) ||
arg_count(cmd, addtag_ARG) ||
arg_count(cmd, metadataprofile_ARG) ||
arg_count(cmd, profile_ARG) ||
arg_count(cmd, detachprofile_ARG);
+
int update_partial_unsafe =
arg_count(cmd, logicalvolume_ARG) ||
arg_count(cmd, maxphysicalvolumes_ARG) ||
@@ -608,18 +652,14 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv)
arg_count(cmd, physicalextentsize_ARG) ||
arg_count(cmd, clustered_ARG) ||
arg_count(cmd, alloc_ARG) ||
- arg_count(cmd, vgmetadatacopies_ARG);
+ arg_count(cmd, vgmetadatacopies_ARG) ||
+ arg_count(cmd, systemid_ARG) ||
+ arg_count(cmd, systemidsource_ARG);
+
int update = update_partial_safe || update_partial_unsafe;
- if (!update &&
- !arg_count(cmd, activate_ARG) &&
- !arg_count(cmd, monitor_ARG) &&
- !arg_count(cmd, poll_ARG) &&
- !arg_count(cmd, refresh_ARG)) {
- log_error("Need 1 or more of -a, -c, -l, -p, -s, -x, "
- "--refresh, --uuid, --alloc, --addtag, --deltag, "
- "--monitor, --poll, --vgmetadatacopies or "
- "--metadatacopies");
+ if (!update && !noupdate) {
+ log_error("Need one or more command options.");
return EINVALID_CMD_LINE;
}
@@ -709,6 +749,9 @@ int vgchange(struct cmd_context *cmd, int argc, char **argv)
if (!update || !update_partial_unsafe)
cmd->handles_missing_pvs = 1;
+ if (arg_is_set(cmd, systemid_ARG) && arg_is_set(cmd, force_ARG))
+ cmd->skip_systemid_check = 1;
+
return process_each_vg(cmd, argc, argv, update ? READ_FOR_UPDATE : 0,
NULL, &vgchange_single);
}
diff --git a/tools/vgcreate.c b/tools/vgcreate.c
index 01bf421f6..b49414852 100644
--- a/tools/vgcreate.c
+++ b/tools/vgcreate.c
@@ -71,6 +71,7 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv)
!vg_set_max_pv(vg, vp_new.max_pv) ||
!vg_set_alloc_policy(vg, vp_new.alloc) ||
!vg_set_clustered(vg, vp_new.clustered) ||
+ !vg_set_system_id(vg, vp_new.system_id) ||
!vg_set_mda_copies(vg, vp_new.vgmetadatacopies))
goto bad_orphan;
diff --git a/tools/vgexport.c b/tools/vgexport.c
index d9f8efadc..f40784a02 100644
--- a/tools/vgexport.c
+++ b/tools/vgexport.c
@@ -32,6 +32,7 @@ static int vgexport_single(struct cmd_context *cmd __attribute__((unused)),
goto_bad;
vg->status |= EXPORTED_VG;
+ vg->system_id = NULL;
dm_list_iterate_items(pvl, &vg->pvs)
pvl->pv->status |= EXPORTED_VG;
diff --git a/tools/vgimport.c b/tools/vgimport.c
index 7cb93378f..23f802eb2 100644
--- a/tools/vgimport.c
+++ b/tools/vgimport.c
@@ -37,6 +37,7 @@ static int vgimport_single(struct cmd_context *cmd __attribute__((unused)),
goto_bad;
vg->status &= ~EXPORTED_VG;
+ vg->system_id = dm_pool_strdup(cmd->mem, cmd->system_id);
dm_list_iterate_items(pvl, &vg->pvs) {
pv = pvl->pv;