diff --git a/doc/example.conf.in b/doc/example.conf.in index 162072178..3bc8c3cbf 100644 --- a/doc/example.conf.in +++ b/doc/example.conf.in @@ -23,6 +23,9 @@ config { # If enabled, any configuration mismatch aborts the LVM2 process. abort_on_errors = 0 + + # Directory where LVM looks for configuration profiles. + profile_dir = "@DEFAULT_SYS_DIR@/@DEFAULT_PROFILE_SUBDIR" } # This section allows you to configure which block devices should diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c index 718fa65f3..f8b861bd6 100644 --- a/lib/commands/toolcontext.c +++ b/lib/commands/toolcontext.c @@ -595,6 +595,35 @@ static int _init_tag_configs(struct cmd_context *cmd) return 1; } +static int _init_profiles(struct cmd_context *cmd) +{ + static char default_dir[PATH_MAX]; + const char *dir; + struct profile_params *pp; + + if (!(pp = dm_pool_zalloc(cmd->libmem, sizeof(*pp)))) { + log_error("profile_params alloc failed"); + return 0; + } + + if (!(dir = find_config_tree_str(cmd, config_profile_dir_CFG))) { + if (dm_snprintf(default_dir, sizeof(default_dir), "%s/%s", + cmd->system_dir, DEFAULT_PROFILE_SUBDIR) == -1) { + log_error("Couldn't create default profile path '%s/%s'.", + cmd->system_dir, DEFAULT_PROFILE_SUBDIR); + return 0; + } + dir = default_dir; + } + + pp->dir = dm_pool_strdup(cmd->libmem, dir); + dm_list_init(&pp->profiles_to_load); + dm_list_init(&pp->profiles); + + cmd->profile_params = pp; + return 1; +} + static struct dm_config_tree *_merge_config_files(struct cmd_context *cmd, struct dm_config_tree *cft) { struct config_tree_list *cfl; @@ -641,10 +670,11 @@ static void _destroy_config(struct cmd_context *cmd) { struct config_tree_list *cfl; struct dm_config_tree *cft; + struct profile *profile; /* * Configuration cascade: - * CONFIG_STRING -> CONFIG_FILE/CONFIG_MERGED_FILES + * CONFIG_STRING -> CONFIG_PROFILE -> CONFIG_FILE/CONFIG_MERGED_FILES */ /* CONFIG_FILE/CONFIG_MERGED_FILES */ @@ -657,6 +687,17 @@ static void _destroy_config(struct cmd_context *cmd) config_destroy(cfl->cft); dm_list_init(&cmd->config_files); + /* CONFIG_PROFILE */ + if (cmd->profile_params) { + remove_config_tree_by_source(cmd, CONFIG_PROFILE); + dm_list_iterate_items(profile, &cmd->profile_params->profiles_to_load) + config_destroy(profile->cft); + dm_list_iterate_items(profile, &cmd->profile_params->profiles) + config_destroy(profile->cft); + dm_list_init(&cmd->profile_params->profiles_to_load); + dm_list_init(&cmd->profile_params->profiles); + } + /* CONFIG_STRING */ if ((cft = remove_config_tree_by_source(cmd, CONFIG_STRING))) config_destroy(cft); @@ -1427,6 +1468,9 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived, if (!_process_config(cmd)) goto_out; + if (!_init_profiles(cmd)) + goto_out; + if (!(cmd->dev_types = create_dev_types(cmd->proc_dir, find_config_tree_node(cmd, devices_types_CFG)))) goto_out; @@ -1470,6 +1514,7 @@ out: cmd = NULL; } + return cmd; } diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h index f6a09a71c..7551245cb 100644 --- a/lib/commands/toolcontext.h +++ b/lib/commands/toolcontext.h @@ -51,6 +51,7 @@ struct config_info { }; struct dm_config_tree; +struct profile_params; struct archive_params; struct backup_params; struct arg_values; @@ -104,6 +105,8 @@ struct cmd_context { struct config_info current_settings; struct dm_hash_table *cft_def_hash; /* cft definition hash used for validity check */ + struct profile_params *profile_params; + struct archive_params *archive_params; struct backup_params *backup_params; const char *stripe_filler; diff --git a/lib/config/config.c b/lib/config/config.c index df65d38a7..5711ead13 100644 --- a/lib/config/config.c +++ b/lib/config/config.c @@ -22,6 +22,7 @@ #include "str_list.h" #include "toolcontext.h" #include "lvm-file.h" +#include "memlock.h" #include #include @@ -1302,3 +1303,77 @@ struct dm_config_tree *config_def_create_tree(struct config_def_tree_spec *spec) cft->root = root; return cft; } + +struct profile *add_profile(struct cmd_context *cmd, const char *profile_name) +{ + struct profile *profile; + + /* Do some sanity checks first. */ + if (!profile_name || !*profile_name) { + log_error("Undefined profile name."); + return NULL; + } + + if (strchr(profile_name, '/')) { + log_error("%s: bad profile name, it contains '/'.", profile_name); + return NULL; + } + + /* Check if the profile is added already... */ + dm_list_iterate_items(profile, &cmd->profile_params->profiles_to_load) { + if (!strcmp(profile->name, profile_name)) + return profile; + } + dm_list_iterate_items(profile, &cmd->profile_params->profiles) { + if (!strcmp(profile->name, profile_name)) + return profile; + } + + if (!(profile = dm_pool_zalloc(cmd->libmem, sizeof(*profile)))) { + log_error("profile allocation failed"); + return NULL; + } + + profile->name = dm_pool_strdup(cmd->libmem, profile_name); + dm_list_add(&cmd->profile_params->profiles_to_load, &profile->list); + + return profile; +} + +int load_profile(struct cmd_context *cmd, struct profile *profile) { + static char profile_path[PATH_MAX]; + + if (critical_section()) { + log_error(INTERNAL_ERROR "trying to load profile %s " + "in critical section.", profile->name); + return 0; + } + + if (profile->cft) + return 1; + + if (dm_snprintf(profile_path, sizeof(profile_path), "%s/%s.profile", + cmd->profile_params->dir, profile->name) < 0) { + log_error("LVM_SYSTEM_DIR or profile name too long"); + return 0; + } + + if (!(profile->cft = config_file_open_and_read(profile_path, CONFIG_PROFILE))) + return 0; + + dm_list_move(&cmd->profile_params->profiles, &profile->list); + + return 1; +} + +int load_pending_profiles(struct cmd_context *cmd) +{ + struct profile *profile, *temp_profile; + + dm_list_iterate_items_safe(profile, temp_profile, &cmd->profile_params->profiles_to_load) { + if (!load_profile(cmd, profile)) + return 0; + } + + return 1; +} diff --git a/lib/config/config.h b/lib/config/config.h index f4c77edb8..ef7558070 100644 --- a/lib/config/config.h +++ b/lib/config/config.h @@ -34,6 +34,19 @@ typedef enum { CONFIG_PROFILE /* profile config */ } config_source_t; +struct profile { + struct dm_list list; + const char *name; + struct dm_config_tree *cft; +}; + +struct profile_params { + const char *dir; /* subdir in LVM_SYSTEM_DIR where LVM looks for profiles */ + struct profile *global_profile; /* profile that overrides any other VG/LV-based profile ('--profile' cmd line arg) */ + struct dm_list profiles_to_load;/* list of profiles which are only added, but still need to be loaded for any use */ + struct dm_list profiles; /* list of profiles which are loaded already and which are ready for use */ +}; + #define CFG_PATH_MAX_LEN 64 /* @@ -114,6 +127,10 @@ enum { #undef cfg_array }; +struct profile *add_profile(struct cmd_context *cmd, const char *profile_name); +int load_profile(struct cmd_context *cmd, struct profile *profile); +int load_pending_profiles(struct cmd_context *cmd); + int config_def_get_path(char *buf, size_t buf_size, int id); int config_def_check(struct cmd_context *cmd, int force, int skip, int suppress_messages); diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h index 6229f2e6d..65d869b7f 100644 --- a/lib/config/config_settings.h +++ b/lib/config/config_settings.h @@ -68,6 +68,7 @@ cfg_section(tags_CFG_SECTION, "tags", root_CFG_SECTION, 0, vsn(1, 0, 18), 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.") +cfg(config_profile_dir_CFG, "profile_dir", config_CFG_SECTION, 0, CFG_TYPE_STRING, 0, vsn(2, 2, 99), "Directory with configuration profiles.") cfg(devices_dir_CFG, "dir", devices_CFG_SECTION, 0, CFG_TYPE_STRING, DEFAULT_DEV_DIR, vsn(1, 0, 0), NULL) cfg_array(devices_scan_CFG, "scan", devices_CFG_SECTION, 0, CFG_TYPE_STRING, "#S/dev", vsn(1, 0, 0), NULL) diff --git a/lib/mm/memlock.c b/lib/mm/memlock.c index 08876b0e6..fda3bb81b 100644 --- a/lib/mm/memlock.c +++ b/lib/mm/memlock.c @@ -406,6 +406,13 @@ static void _unlock_mem_if_possible(struct cmd_context *cmd) void critical_section_inc(struct cmd_context *cmd, const char *reason) { + /* + * Profiles are loaded on-demand so make sure that before + * entering the critical section all needed profiles are + * loaded to avoid the disk access later. + */ + load_pending_profiles(cmd); + if (!_critical_section) { _critical_section = 1; log_debug_mem("Entering critical section (%s).", reason); diff --git a/tools/args.h b/tools/args.h index 2fed97ef1..cadd2112d 100644 --- a/tools/args.h +++ b/tools/args.h @@ -91,6 +91,7 @@ arg(writemostly_ARG, '\0', "writemostly", string_arg, ARG_GROUPABLE) arg(writebehind_ARG, '\0', "writebehind", int_arg, 0) arg(minrecoveryrate_ARG, '\0', "minrecoveryrate", size_kb_arg, 0) arg(maxrecoveryrate_ARG, '\0', "maxrecoveryrate", size_kb_arg, 0) +arg(profile_ARG, '\0', "profile", string_arg, 0) /* Allow some variations */ arg(resizable_ARG, '\0', "resizable", yes_no_arg, 0) diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c index 037243494..1111df6a0 100644 --- a/tools/lvmcmdline.c +++ b/tools/lvmcmdline.c @@ -620,7 +620,8 @@ void lvm_register_commands(void) debug_ARG, help_ARG, help2_ARG, \ version_ARG, verbose_ARG, \ yes_ARG, \ - quiet_ARG, config_ARG, -1); + quiet_ARG, config_ARG, \ + profile_ARG, -1); #include "commands.h" #undef xx } @@ -1051,6 +1052,7 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv) int locking_type; int monitoring; struct dm_config_tree *old_cft; + struct profile *profile; init_error_message_produced(0); @@ -1089,6 +1091,16 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv) } } + if (arg_count(cmd, profile_ARG)) { + if (!(profile = add_profile(cmd, arg_str_value(cmd, profile_ARG, NULL)))) { + log_error("Failed to add configuration profile."); + return ECMD_FAILED; + } + log_debug("Setting global configuration profile \"%s\".", profile->name); + /* This profile will override any VG/LV-based profile if present */ + cmd->profile_params->global_profile = profile; + } + if ((ret = _get_settings(cmd))) goto_out; _apply_settings(cmd);