From 3e7915b20aa95759c7e4b92b8cf58de336ac6182 Mon Sep 17 00:00:00 2001 From: David Teigland Date: Fri, 10 Oct 2014 15:46:35 -0500 Subject: system_id: use for VG ownership See included lvmsystemid(7) for full description. --- lib/commands/toolcontext.c | 139 ++++++++++++++++++++++++++- lib/commands/toolcontext.h | 6 ++ lib/config/config_settings.h | 8 ++ lib/config/defaults.h | 1 + lib/metadata/metadata-exported.h | 2 + lib/metadata/metadata.c | 96 +++++++++++++++++-- lib/metadata/vg.c | 16 ++++ lib/metadata/vg.h | 1 + lib/misc/lvm-string.c | 35 +++++++ lib/misc/lvm-string.h | 2 + lib/report/columns.h | 3 +- lib/report/properties.c | 2 + man/Makefile.in | 2 +- man/lvmsystemid.7.in | 200 +++++++++++++++++++++++++++++++++++++++ tools/args.h | 2 + tools/commands.h | 6 +- tools/toollib.c | 21 ++++ tools/vgchange.c | 69 +++++++++++--- tools/vgcreate.c | 1 + tools/vgexport.c | 1 + tools/vgimport.c | 1 + 21 files changed, 586 insertions(+), 28 deletions(-) create mode 100644 man/lvmsystemid.7.in 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 #include #include +#include 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; -- cgit v1.2.1