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-21 15:07:27 -0500
commit80f541d3cc31559bdfac042996785349634a7d5f (patch)
tree992402c11cbfe69f12ec74a12e00bf1dcabc5a66
parent4461c624bc59ddca60f4bbf1eb447d64c8d4aeb2 (diff)
downloadlvm2-dev-dct-systemid4.tar.gz
system_id: use for VG ownershipdev-dct-systemid4
See included lvmsystemid(7) for full description.
-rw-r--r--lib/commands/toolcontext.c136
-rw-r--r--lib/commands/toolcontext.h6
-rw-r--r--lib/config/config_settings.h7
-rw-r--r--lib/config/defaults.h1
-rw-r--r--lib/metadata/metadata-exported.h2
-rw-r--r--lib/metadata/metadata.c115
-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.in264
-rw-r--r--tools/args.h3
-rw-r--r--tools/commands.h8
-rw-r--r--tools/reporter.c11
-rw-r--r--tools/toollib.c32
-rw-r--r--tools/vgchange.c83
-rw-r--r--tools/vgcreate.c1
-rw-r--r--tools/vgexport.c1
-rw-r--r--tools/vgimport.c12
22 files changed, 714 insertions, 29 deletions
diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c
index e38aa5634..0bade9ac6 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 lvmlocal.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,10 @@ 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 lvmlocal.conf.
+ */
+
static int _init_lvm_conf(struct cmd_context *cmd)
{
/* No config file if LVM_SYSTEM_DIR is empty */
@@ -608,9 +698,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 +713,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 +1427,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 +1688,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..a3aaf7c28 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 include_foreign_vgs: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..a1f6d42df 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.")
@@ -162,6 +163,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 +190,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 +276,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..f77a322aa 100644
--- a/lib/metadata/metadata.c
+++ b/lib/metadata/metadata.c
@@ -4223,6 +4223,113 @@ 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)
+{
+ /*
+ * A VG without a system_id can be accessed by anyone.
+ */
+ if (!vg->system_id || !vg->system_id[0])
+ return 1;
+
+ /*
+ * We sometimes want to report foreign vgs.
+ */
+ if (cmd->include_foreign_vgs)
+ return 1;
+
+ /*
+ * A host without a system_id cannot access a VG with a system_id.
+ */
+ if (!cmd->system_id || cmd->unknown_system_id) {
+ log_warn("Cannot access VG %s with system id \"%s\" with unknown local system id.",
+ vg->name, vg->system_id);
+ return 0;
+ }
+
+ /*
+ * A host can access a VG with a matching system_id.
+ */
+ if (!strcmp(vg->system_id, cmd->system_id))
+ return 1;
+
+ /*
+ * A host can access a VG if the VG's system_id is in the allow list.
+ */
+ if (allow_system_id(cmd, vg->system_id))
+ return 1;
+
+ /*
+ * Silently ignore foreign VGs. They will only cause the
+ * command to fail if they were named as explicit command
+ * args, in which case the command will fail indicating the
+ * VG was not found.
+ */
+
+ 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 +4389,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..80e0a696d
--- /dev/null
+++ b/man/lvmsystemid.7.in
@@ -0,0 +1,264 @@
+.TH "LVMSYSTEMID" "7" "LVM TOOLS #VERSION#" "Red Hat, Inc" "\""
+
+.SH NAME
+lvmsystemid \(em LVM system id
+
+.SH DESCRIPTION
+
+Local VG's may exist on shared storage where they are visible to multiple
+hosts. These VG's are intended to be used by only a single machine, even
+though they are visible to many. A system_id identifying a single host
+can be assigned to a VG to indicate the VG's owner. The VG owner can use
+the VG as usual, and all other hosts will ignore it. This protects the VG
+from accidental use by other hosts.
+
+The system_id is not a dynamic property, and can only be changed in very
+limited circumstances (see vgexport and vgimport). Even limited changes
+to the VG system_id are not perfectly reflected across hosts. A more
+coherent view of shared storage requires using an inter-host locking
+system to coordinate access and update caches.
+
+The system_id is a string uniquely identifying a host. It can be manually
+set to a custom value or it can be assigned automatically by lvm using a
+unique identifier already available on the host, e.g. machine-id or uname.
+
+In vgcreate, the local system_id is saved in the new VG metadata. The
+local host owns the new VG, and other hosts will ignore it.
+
+A VG without a system_id can be used by any host, and a VG with a
+system_id can only be used by a host with a matching system_id. A
+"foreign VG" is defined as a VG with a system_id that a host cannot access
+because the host has an unmatching system_id (or no system_id).
+
+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 Types of VG access
+A "local VG" is meant to be used by a single host.
+.br
+A "shared VG" is meant to be used by multiple hosts.
+.br
+These can be further distinguished as:
+
+.B Unrestricted:
+A local VG that has no system_id. This VG type is unprotected and
+accessible to any host.
+
+.B Owned:
+A local VG that has a system_id set, as viewed from the one host with a
+matching system_id: the owner. This VG type is by definition acessible.
+
+.B Foreign:
+A local VG that has a system_id set, as viewed from any host with an
+unmatching system_id (or no system_id). It is owned by another host.
+This VG type is by definition not accessible.
+
+.B Exported:
+A local VG that has been exported with vgexport and has no system_id.
+This VG type can only be accessed by vgimport which will change it to
+owned.
+
+.B Clustered:
+A shared VG with the clustered flag set, and no system_id. This VG type
+is only accessible to hosts using clvmd.
+
+.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 will 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 none.
+
+.I lvm.conf
+.nf
+global {
+ system_id_source = "none"
+}
+.fi
+
+.TP
+.B machineid
+
+The content of /etc/machine-id is used as the system_id.
+
+.I lvm.conf
+.nf
+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.
+
+.I lvm.conf
+.nf
+global {
+ system_id_source = "uname"
+}
+.fi
+
+.TP
+.B lvmlocal
+
+The system_id is defined in lvmlocal.conf local/system_id.
+
+.I lvm.conf
+.nf
+global {
+ system_id_source = "lvmlocal"
+}
+.fi
+
+.I lvmlocal.conf
+.nf
+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.
+
+.I lvm.conf
+.nf
+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
+lvmlocal.conf local/allow_system_id.
+
+.I lvmlocal.conf
+.nf
+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. Another host with a system_id
+matching the one specified may not recognize the new VG without manually
+rescanning devices.
+
+.SS report/display
+
+The system_id of a VG is displayed with the "systemid" reporting option.
+
+Report/display commands will ignore foreign VGs by default. To report
+foreign VGs, the --foreign option must be used. This causes all VGs to be
+read from disk.
+
+.B vgs --foreign -o+systemid
+
+When a host with no system_id sees foreign VGs, it will warn about them as
+they are skipped. The host should be assigned a system_id, after which
+standard reporting commands will silently ignore foreign VGs.
+
+.SS vgexport/vgimport
+
+vgexport clears the system_id.
+
+Other hosts will consider a newly exported VG to be foreign because of
+local caching. Manually updating the local cache with pvscan --cache will
+cause a host to recognize the exported VG.
+
+vgimport sets the VG system_id to the local system_id as determined by
+lvm.conf system_id_sources. vgimport automatically scans storage for
+newly exported VGs.
+
+.SS vgchange
+
+If a VG has a system_id, changing it with vgchange requires --force.
+vgchange --systemid and --systemidsource are accepted just as with
+vgcreate.
+
+If a host's system_id is changed, the system_id of its own VG's can be
+changed to match. Using allow_system_id is a way to avoid using force
+with vgchange.
+
+To move a VG from one host to another, vgexport and vgimport should be
+used.
+
+.SS clustered VGs
+
+A "clustered" VG should have no system_id set, allowing multiple hosts to
+use it via clvm. Changing a VG to clustered will clear the existing
+system_id. Changing a VG to not clustered will set the system_id to the
+host running the vgchange command.
+
+.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.
+
+.SS orphans
+
+Orphan PVs are unused devices; they are not currently used in any VG.
+Because of this, they are not protected by a system_id, and any host can
+use them. Coodination of changes to orphan PVs is beyond the scope of
+system_id and requires the use of a distributed locking mechanism.
+
+.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..b7fc82948 100644
--- a/tools/args.h
+++ b/tools/args.h
@@ -114,6 +114,9 @@ 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)
+arg(foreign_ARG, '\0', "foreign", NULL, 0)
/* Allow some variations */
diff --git a/tools/commands.h b/tools/commands.h
index 2e99fb177..8e837f645 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)
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",
@@ -1233,7 +1235,7 @@ xx(vgs,
ignoreskippedcluster_ARG, nameprefixes_ARG, noheadings_ARG,
nolocking_ARG, nosuffix_ARG, options_ARG, partial_ARG,
readonly_ARG, rows_ARG, select_ARG, separator_ARG, sort_ARG,
- trustcache_ARG, unbuffered_ARG, units_ARG, unquoted_ARG)
+ trustcache_ARG, unbuffered_ARG, units_ARG, unquoted_ARG, foreign_ARG)
xx(vgscan,
"Search for all volume groups",
diff --git a/tools/reporter.c b/tools/reporter.c
index c55d653aa..c8a394023 100644
--- a/tools/reporter.c
+++ b/tools/reporter.c
@@ -265,6 +265,17 @@ static int _report(struct cmd_context *cmd, int argc, char **argv,
unsigned args_are_pvs, lv_info_needed;
int lock_global = 0;
+ /*
+ * When reporting foreign VGs we want to refresh our cached
+ * copy of them, since other hosts have probably made changes
+ * to their own VGs. We also want to override the default
+ * behavior which skips over foreign VGs.
+ */
+ if (arg_is_set(cmd, foreign_ARG) && lvmetad_used()) {
+ lvmetad_pvscan_all_devs(cmd, NULL);
+ cmd->include_foreign_vgs = 1;
+ }
+
aligned = find_config_tree_bool(cmd, report_aligned_CFG, NULL);
buffered = find_config_tree_bool(cmd, report_buffered_CFG, NULL);
headings = find_config_tree_bool(cmd, report_headings_CFG, NULL);
diff --git a/tools/toollib.c b/tools/toollib.c
index 7f656c6e3..f4227667a 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 *arg_str;
+ 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,29 @@ int vgcreate_params_set_from_args(struct cmd_context *cmd,
vp_new->vgmetadatacopies = find_config_tree_int(cmd, metadata_vgmetadatacopies_CFG, NULL);
}
+ if ((arg_str = arg_str_value(cmd, systemid_ARG, NULL))) {
+ vp_new->system_id = system_id_from_string(cmd, arg_str);
+ } 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;
+ }
+
+ if (arg_str || system_id_source) {
+ if (!vp_new->system_id)
+ log_warn("No local system id found, VG will not have a system id.");
+
+ if (vp_new->system_id && cmd->system_id &&
+ strcmp(vp_new->system_id, cmd->system_id)) {
+ log_warn("VG system id \"%s\" will not be accessible to local system id \"%s\"",
+ vp_new->system_id, cmd->system_id);
+ }
+ }
+
+ /* A clustered vg has no system_id. */
+ if (vp_new->clustered)
+ vp_new->system_id = NULL;
+
return 1;
}
diff --git a/tools/vgchange.c b/tools/vgchange.c
index 9a9fe4866..11ae8b42c 100644
--- a/tools/vgchange.c
+++ b/tools/vgchange.c
@@ -336,6 +336,11 @@ static int _vgchange_clustered(struct cmd_context *cmd,
}
}
+ if (clustered)
+ vg->system_id = NULL;
+ else if (cmd->system_id && cmd->system_id[0])
+ vg->system_id = dm_pool_strdup(cmd->mem, cmd->system_id);
+
if (!vg_set_clustered(vg, clustered))
return_0;
@@ -475,6 +480,54 @@ static int _vgchange_profile(struct cmd_context *cmd,
return 1;
}
+/*
+ * This function will not be called unless the local host is allowed to use the
+ * VG. Either the VG has no system_id, or the VG and host have matching
+ * system_ids, or the host has the VG's current system_id in its
+ * allow_system_id list. This function is not allowed to change the system_id
+ * of a foreign VG (VG owned by another host).
+ */
+
+static int _vgchange_system_id(struct cmd_context *cmd, struct volume_group *vg)
+{
+ const char *arg_str = arg_str_value(cmd, systemid_ARG, NULL);
+ const char *source = arg_str_value(cmd, systemidsource_ARG, NULL);
+ char *system_id;
+
+ if (!arg_str && !source)
+ return 0;
+
+ if (arg_str) {
+ system_id = system_id_from_string(cmd, arg_str);
+ goto change;
+ }
+
+ if (!strcmp(source, "none")) {
+ system_id = NULL;
+ goto change;
+ }
+
+ if ((system_id = system_id_from_source(cmd, source)))
+ goto change;
+
+ log_error("No system_id found from source %s", source);
+ return 0;
+
+change:
+ if (system_id && cmd->system_id && strcmp(system_id, cmd->system_id)) {
+ log_warn("VG \"%s\" system id \"%s\" does not match local system id \"%s\"",
+ vg->name, system_id, cmd->system_id);
+
+ if (yes_no_prompt("Change system id? [y/n]: ") == 'n') {
+ log_error("Volume group \"%s\" not changed.", vg->name);
+ return 0;
+ }
+ }
+
+ vg->system_id = system_id;
+ return 1;
+}
+
static int vgchange_single(struct cmd_context *cmd, const char *vg_name,
struct volume_group *vg,
void *handle __attribute__((unused)))
@@ -498,8 +551,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 +648,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 +669,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;
}
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..72fc3a1fb 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;
@@ -84,6 +85,17 @@ int vgimport(struct cmd_context *cmd, int argc, char **argv)
cmd->handles_missing_pvs = 1;
}
+ /*
+ * Rescan devices and update lvmetad. lvmetad may hold a copy of the
+ * VG from before it was exported, if it was exported by another host.
+ * We need to reread it to see that it's been exported before we can
+ * import it.
+ */
+ if (lvmetad_used() && !lvmetad_pvscan_all_devs(cmd, NULL)) {
+ log_error("Failed to scan devices.");
+ return ECMD_FAILED;
+ }
+
return process_each_vg(cmd, argc, argv,
READ_FOR_UPDATE | READ_ALLOW_EXPORTED,
NULL,