| /* |
| * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved. |
| * Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved. |
| * |
| * This file is part of LVM2. |
| * |
| * This copyrighted material is made available to anyone wishing to use, |
| * modify, copy, or redistribute it subject to the terms and conditions |
| * of the GNU Lesser General Public License v.2.1. |
| * |
| * You should have received a copy of the GNU Lesser General Public License |
| * along with this program; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include "tools.h" |
| |
| static int _get_vsn(struct cmd_context *cmd, uint16_t *version_int) |
| { |
| const char *vsn; |
| unsigned int major, minor, patchlevel; |
| |
| if (!(vsn = arg_str_value(cmd, atversion_ARG, NULL)) && |
| !(vsn = arg_str_value(cmd, sinceversion_ARG, NULL))) |
| vsn = LVM_VERSION; |
| |
| if (sscanf(vsn, "%u.%u.%u", &major, &minor, &patchlevel) != 3) { |
| log_error("Incorrect version format."); |
| return 0; |
| } |
| |
| *version_int = vsn(major, minor, patchlevel); |
| return 1; |
| } |
| |
| static int _do_def_check(struct config_def_tree_spec *spec, |
| struct dm_config_tree *cft, |
| struct cft_check_handle **cft_check_handle) |
| { |
| struct cft_check_handle *handle; |
| |
| if (!(handle = get_config_tree_check_handle(spec->cmd, cft))) |
| return 0; |
| |
| handle->force_check = 1; |
| handle->suppress_messages = 1; |
| |
| if (spec->type == CFG_DEF_TREE_DIFF) { |
| if (!handle->check_diff) |
| handle->skip_if_checked = 0; |
| handle->check_diff = 1; |
| } else { |
| handle->skip_if_checked = 1; |
| handle->check_diff = 0; |
| } |
| |
| handle->ignoreunsupported = spec->ignoreunsupported; |
| handle->ignoreadvanced = spec->ignoreadvanced; |
| |
| config_def_check(handle); |
| *cft_check_handle = handle; |
| |
| return 1; |
| } |
| |
| static int _merge_config_cascade(struct cmd_context *cmd, struct dm_config_tree *cft_cascaded, |
| struct dm_config_tree **cft_merged) |
| { |
| if (!cft_cascaded) |
| return 1; |
| |
| if (!*cft_merged && !(*cft_merged = config_open(CONFIG_MERGED_FILES, NULL, 0))) |
| return_0; |
| |
| if (!_merge_config_cascade(cmd, cft_cascaded->cascade, cft_merged)) |
| return_0; |
| |
| return merge_config_tree(cmd, *cft_merged, cft_cascaded, CONFIG_MERGE_TYPE_RAW); |
| } |
| |
| static int _config_validate(struct cmd_context *cmd, struct dm_config_tree *cft) |
| { |
| struct cft_check_handle *handle; |
| |
| if (!(handle = get_config_tree_check_handle(cmd, cft))) |
| return 1; |
| |
| handle->force_check = 1; |
| handle->skip_if_checked = 1; |
| handle->suppress_messages = 0; |
| |
| return config_def_check(handle); |
| } |
| |
| int dumpconfig(struct cmd_context *cmd, int argc, char **argv) |
| { |
| const char *file = arg_str_value(cmd, file_ARG, NULL); |
| const char *type = arg_str_value(cmd, configtype_ARG, arg_is_set(cmd, list_ARG) ? "list" : "current"); |
| struct config_def_tree_spec tree_spec = {0}; |
| struct dm_config_tree *cft = NULL; |
| struct cft_check_handle *cft_check_handle = NULL; |
| struct profile *profile = NULL; |
| int r = ECMD_PROCESSED; |
| |
| tree_spec.cmd = cmd; |
| |
| if (arg_is_set(cmd, configtype_ARG) && arg_is_set(cmd, validate_ARG)) { |
| log_error("Only one of --type and --validate permitted."); |
| return EINVALID_CMD_LINE; |
| } |
| |
| if (arg_is_set(cmd, configtype_ARG) && arg_is_set(cmd, list_ARG)) { |
| log_error("Only one of --type and --list permitted."); |
| return EINVALID_CMD_LINE; |
| } |
| |
| if (arg_is_set(cmd, atversion_ARG)) { |
| if (arg_is_set(cmd, sinceversion_ARG)) { |
| log_error("Only one of --atversion and --sinceversion permitted."); |
| return EINVALID_CMD_LINE; |
| } |
| |
| if (!arg_is_set(cmd, configtype_ARG) && !arg_is_set(cmd, list_ARG)) { |
| log_error("--atversion requires --type or --list"); |
| return EINVALID_CMD_LINE; |
| } |
| } else if (arg_is_set(cmd, sinceversion_ARG)) { |
| if (!arg_is_set(cmd, configtype_ARG) || strcmp(type, "new")) { |
| log_error("--sinceversion requires --type new"); |
| return EINVALID_CMD_LINE; |
| } |
| } |
| |
| if (arg_is_set(cmd, ignoreadvanced_ARG)) |
| tree_spec.ignoreadvanced = 1; |
| |
| if (arg_is_set(cmd, ignoreunsupported_ARG)) { |
| if (arg_is_set(cmd, showunsupported_ARG)) { |
| log_error("Only one of --ignoreunsupported and --showunsupported permitted."); |
| return EINVALID_CMD_LINE; |
| } |
| tree_spec.ignoreunsupported = 1; |
| } else if (arg_is_set(cmd, showunsupported_ARG)) { |
| tree_spec.ignoreunsupported = 0; |
| } else if (strcmp(type, "current") && strcmp(type, "diff")) { |
| /* |
| * By default hide unsupported settings |
| * for all display types except "current" |
| * and "diff". |
| */ |
| tree_spec.ignoreunsupported = 1; |
| } |
| |
| if (strcmp(type, "current") && strcmp(type, "diff")) { |
| /* |
| * By default hide deprecated settings |
| * for all display types except "current" |
| * and "diff" unless --showdeprecated is set. |
| * |
| * N.B. Deprecated settings are visible if |
| * --atversion is used with a version that |
| * is lower than the version in which the |
| * setting was deprecated. |
| */ |
| if (!arg_is_set(cmd, showdeprecated_ARG)) |
| tree_spec.ignoredeprecated = 1; |
| } |
| |
| if (arg_is_set(cmd, ignorelocal_ARG)) |
| tree_spec.ignorelocal = 1; |
| |
| if (!strcmp(type, "current") || !strcmp(type, "full")) { |
| if (arg_is_set(cmd, atversion_ARG)) { |
| log_error("--atversion has no effect with --type %s", type); |
| return EINVALID_CMD_LINE; |
| } |
| |
| if ((arg_is_set(cmd, ignoreunsupported_ARG) || |
| arg_is_set(cmd, ignoreadvanced_ARG)) && |
| !strcmp(type, "current")) { |
| /* FIXME: allow these even for --type current */ |
| log_error("--ignoreadvanced and --ignoreunsupported has " |
| "no effect with --type current"); |
| return EINVALID_CMD_LINE; |
| } |
| } else if (arg_is_set(cmd, mergedconfig_ARG)) { |
| log_error("--mergedconfig has no effect without --type current or --type full"); |
| return EINVALID_CMD_LINE; |
| } |
| |
| if (!_get_vsn(cmd, &tree_spec.version)) |
| return EINVALID_CMD_LINE; |
| |
| /* |
| * The profile specified by --profile cmd arg is like --commandprofile, |
| * but it is used just for dumping the profile content and not for |
| * application. |
| */ |
| if (arg_is_set(cmd, profile_ARG) && |
| (!(profile = add_profile(cmd, arg_str_value(cmd, profile_ARG, NULL), CONFIG_PROFILE_COMMAND)) || |
| !override_config_tree_from_profile(cmd, profile))) { |
| log_error("Failed to load profile %s.", arg_str_value(cmd, profile_ARG, NULL)); |
| return ECMD_FAILED; |
| } |
| |
| /* |
| * Set the 'cft' to work with based on whether we need the plain |
| * config tree or merged config tree cascade if --mergedconfig is used. |
| */ |
| if ((arg_is_set(cmd, mergedconfig_ARG) || !strcmp(type, "full") || !strcmp(type, "diff")) && cmd->cft->cascade) { |
| if (!_merge_config_cascade(cmd, cmd->cft, &cft)) { |
| log_error("Failed to merge configuration."); |
| r = ECMD_FAILED; |
| goto out; |
| } |
| } else |
| cft = cmd->cft; |
| tree_spec.current_cft = cft; |
| |
| if (arg_is_set(cmd, validate_ARG)) { |
| if (_config_validate(cmd, cft)) { |
| log_print("LVM configuration valid."); |
| goto out; |
| } else { |
| log_error("LVM configuration invalid."); |
| r = ECMD_FAILED; |
| goto out; |
| } |
| } |
| |
| if (!strcmp(type, "list") || arg_is_set(cmd, list_ARG)) { |
| tree_spec.type = CFG_DEF_TREE_LIST; |
| if (arg_is_set(cmd, withcomments_ARG)) { |
| log_error("--withcomments has no effect with --type list"); |
| return EINVALID_CMD_LINE; |
| } |
| /* list type does not require status check */ |
| } else if (!strcmp(type, "full")) { |
| tree_spec.type = CFG_DEF_TREE_FULL; |
| if (!_do_def_check(&tree_spec, cft, &cft_check_handle)) { |
| r = ECMD_FAILED; |
| goto_out; |
| } |
| } else if (!strcmp(type, "current")) { |
| tree_spec.type = CFG_DEF_TREE_CURRENT; |
| if (!_do_def_check(&tree_spec, cft, &cft_check_handle)) { |
| r = ECMD_FAILED; |
| goto_out; |
| } |
| } |
| else if (!strcmp(type, "missing")) { |
| tree_spec.type = CFG_DEF_TREE_MISSING; |
| if (!_do_def_check(&tree_spec, cft, &cft_check_handle)) { |
| r = ECMD_FAILED; |
| goto_out; |
| } |
| } |
| else if (!strcmp(type, "default")) { |
| tree_spec.type = CFG_DEF_TREE_DEFAULT; |
| /* default type does not require check status */ |
| } |
| else if (!strcmp(type, "diff")) { |
| tree_spec.type = CFG_DEF_TREE_DIFF; |
| if (!_do_def_check(&tree_spec, cft, &cft_check_handle)) { |
| r = ECMD_FAILED; |
| goto_out; |
| } |
| } |
| else if (!strcmp(type, "new")) { |
| tree_spec.type = arg_is_set(cmd, sinceversion_ARG) ? CFG_DEF_TREE_NEW_SINCE |
| : CFG_DEF_TREE_NEW; |
| /* new type does not require check status */ |
| } |
| else if (!strcmp(type, "profilable")) { |
| tree_spec.type = CFG_DEF_TREE_PROFILABLE; |
| /* profilable type does not require check status */ |
| } |
| else if (!strcmp(type, "profilable-command")) { |
| tree_spec.type = CFG_DEF_TREE_PROFILABLE_CMD; |
| /* profilable-command type does not require check status */ |
| } |
| else if (!strcmp(type, "profilable-metadata")) { |
| tree_spec.type = CFG_DEF_TREE_PROFILABLE_MDA; |
| /* profilable-metadata type does not require check status */ |
| } |
| else { |
| log_error("Incorrect type of configuration specified. " |
| "Expected one of: current, default, diff, full, list, missing, " |
| "new, profilable, profilable-command, profilable-metadata."); |
| r = EINVALID_CMD_LINE; |
| goto out; |
| } |
| |
| if (arg_is_set(cmd, withsummary_ARG) || arg_is_set(cmd, list_ARG)) |
| tree_spec.withsummary = 1; |
| if (arg_is_set(cmd, withcomments_ARG)) |
| tree_spec.withcomments = 1; |
| if (arg_is_set(cmd, unconfigured_ARG)) |
| tree_spec.unconfigured = 1; |
| |
| if (arg_is_set(cmd, withversions_ARG)) |
| tree_spec.withversions = 1; |
| |
| if (arg_is_set(cmd, withspaces_ARG)) |
| tree_spec.withspaces = 1; |
| |
| if (cft_check_handle) |
| tree_spec.check_status = cft_check_handle->status; |
| |
| if ((tree_spec.type != CFG_DEF_TREE_CURRENT) && |
| (tree_spec.type != CFG_DEF_TREE_DIFF) && |
| !(cft = config_def_create_tree(&tree_spec))) { |
| r = ECMD_FAILED; |
| goto_out; |
| } |
| |
| if (!config_write(cft, &tree_spec, file, argc, argv)) { |
| stack; |
| r = ECMD_FAILED; |
| } |
| out: |
| if (tree_spec.current_cft && (tree_spec.current_cft != cft) && |
| (tree_spec.current_cft != cmd->cft)) |
| /* |
| * This happens in case of CFG_DEF_TREE_FULL where we |
| * have merged explicitly defined config trees and also |
| * we have used default tree. |
| */ |
| dm_config_destroy(tree_spec.current_cft); |
| |
| if (cft && (cft != cmd->cft)) |
| dm_config_destroy(cft); |
| else if (profile) |
| remove_config_tree_by_source(cmd, CONFIG_PROFILE_COMMAND); |
| |
| /* |
| * The cmd->cft (the "current" tree) is destroyed |
| * together with cmd context destroy... |
| */ |
| |
| return r; |
| } |
| |
| int config(struct cmd_context *cmd, int argc, char **argv) |
| { |
| return dumpconfig(cmd, argc, argv); |
| } |
| |
| int lvmconfig(struct cmd_context *cmd, int argc, char **argv) |
| { |
| return dumpconfig(cmd, argc, argv); |
| } |