]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved. | |
3 | * Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved. | |
4 | * | |
5 | * This file is part of LVM2. | |
6 | * | |
7 | * This copyrighted material is made available to anyone wishing to use, | |
8 | * modify, copy, or redistribute it subject to the terms and conditions | |
9 | * of the GNU Lesser General Public License v.2.1. | |
10 | * | |
11 | * You should have received a copy of the GNU Lesser General Public License | |
12 | * along with this program; if not, write to the Free Software Foundation, | |
13 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
14 | */ | |
15 | ||
16 | #include "tools.h" | |
17 | ||
18 | static int _get_vsn(struct cmd_context *cmd, uint16_t *version_int) | |
19 | { | |
20 | const char *vsn; | |
21 | unsigned int major, minor, patchlevel; | |
22 | ||
23 | if (!(vsn = arg_str_value(cmd, atversion_ARG, NULL)) && | |
24 | !(vsn = arg_str_value(cmd, sinceversion_ARG, NULL))) | |
25 | vsn = LVM_VERSION; | |
26 | ||
27 | if (sscanf(vsn, "%u.%u.%u", &major, &minor, &patchlevel) != 3) { | |
28 | log_error("Incorrect version format."); | |
29 | return 0; | |
30 | } | |
31 | ||
32 | *version_int = vsn(major, minor, patchlevel); | |
33 | return 1; | |
34 | } | |
35 | ||
36 | static int _do_def_check(struct config_def_tree_spec *spec, | |
37 | struct dm_config_tree *cft, | |
38 | struct cft_check_handle **cft_check_handle) | |
39 | { | |
40 | struct cft_check_handle *handle; | |
41 | ||
42 | if (!(handle = get_config_tree_check_handle(spec->cmd, cft))) | |
43 | return 0; | |
44 | ||
45 | handle->force_check = 1; | |
46 | handle->suppress_messages = 1; | |
47 | ||
48 | if (spec->type == CFG_DEF_TREE_DIFF) { | |
49 | if (!handle->check_diff) | |
50 | handle->skip_if_checked = 0; | |
51 | handle->check_diff = 1; | |
52 | } else { | |
53 | handle->skip_if_checked = 1; | |
54 | handle->check_diff = 0; | |
55 | } | |
56 | ||
57 | handle->ignoreunsupported = spec->ignoreunsupported; | |
58 | handle->ignoreadvanced = spec->ignoreadvanced; | |
59 | ||
60 | config_def_check(handle); | |
61 | *cft_check_handle = handle; | |
62 | ||
63 | return 1; | |
64 | } | |
65 | ||
66 | static int _merge_config_cascade(struct cmd_context *cmd, struct dm_config_tree *cft_cascaded, | |
67 | struct dm_config_tree **cft_merged) | |
68 | { | |
69 | if (!cft_cascaded) | |
70 | return 1; | |
71 | ||
72 | if (!*cft_merged && !(*cft_merged = config_open(CONFIG_MERGED_FILES, NULL, 0))) | |
73 | return_0; | |
74 | ||
75 | if (!_merge_config_cascade(cmd, cft_cascaded->cascade, cft_merged)) | |
76 | return_0; | |
77 | ||
78 | return merge_config_tree(cmd, *cft_merged, cft_cascaded, CONFIG_MERGE_TYPE_RAW); | |
79 | } | |
80 | ||
81 | static int _config_validate(struct cmd_context *cmd, struct dm_config_tree *cft) | |
82 | { | |
83 | struct cft_check_handle *handle; | |
84 | ||
85 | if (!(handle = get_config_tree_check_handle(cmd, cft))) | |
86 | return 1; | |
87 | ||
88 | handle->force_check = 1; | |
89 | handle->skip_if_checked = 1; | |
90 | handle->suppress_messages = 0; | |
91 | ||
92 | return config_def_check(handle); | |
93 | } | |
94 | ||
95 | int dumpconfig(struct cmd_context *cmd, int argc, char **argv) | |
96 | { | |
97 | const char *file = arg_str_value(cmd, file_ARG, NULL); | |
98 | const char *type = arg_str_value(cmd, configtype_ARG, arg_is_set(cmd, list_ARG) ? "list" : "current"); | |
99 | struct config_def_tree_spec tree_spec = {0}; | |
100 | struct dm_config_tree *cft = NULL; | |
101 | struct cft_check_handle *cft_check_handle = NULL; | |
102 | struct profile *profile = NULL; | |
103 | int r = ECMD_PROCESSED; | |
104 | ||
105 | tree_spec.cmd = cmd; | |
106 | ||
107 | if (arg_is_set(cmd, configtype_ARG) && arg_is_set(cmd, validate_ARG)) { | |
108 | log_error("Only one of --type and --validate permitted."); | |
109 | return EINVALID_CMD_LINE; | |
110 | } | |
111 | ||
112 | if (arg_is_set(cmd, configtype_ARG) && arg_is_set(cmd, list_ARG)) { | |
113 | log_error("Only one of --type and --list permitted."); | |
114 | return EINVALID_CMD_LINE; | |
115 | } | |
116 | ||
117 | if (arg_is_set(cmd, atversion_ARG)) { | |
118 | if (arg_is_set(cmd, sinceversion_ARG)) { | |
119 | log_error("Only one of --atversion and --sinceversion permitted."); | |
120 | return EINVALID_CMD_LINE; | |
121 | } | |
122 | ||
123 | if (!arg_is_set(cmd, configtype_ARG) && !arg_is_set(cmd, list_ARG)) { | |
124 | log_error("--atversion requires --type or --list"); | |
125 | return EINVALID_CMD_LINE; | |
126 | } | |
127 | } else if (arg_is_set(cmd, sinceversion_ARG)) { | |
128 | if (!arg_is_set(cmd, configtype_ARG) || strcmp(type, "new")) { | |
129 | log_error("--sinceversion requires --type new"); | |
130 | return EINVALID_CMD_LINE; | |
131 | } | |
132 | } | |
133 | ||
134 | if (arg_is_set(cmd, ignoreadvanced_ARG)) | |
135 | tree_spec.ignoreadvanced = 1; | |
136 | ||
137 | if (arg_is_set(cmd, ignoreunsupported_ARG)) { | |
138 | if (arg_is_set(cmd, showunsupported_ARG)) { | |
139 | log_error("Only one of --ignoreunsupported and --showunsupported permitted."); | |
140 | return EINVALID_CMD_LINE; | |
141 | } | |
142 | tree_spec.ignoreunsupported = 1; | |
143 | } else if (arg_is_set(cmd, showunsupported_ARG)) { | |
144 | tree_spec.ignoreunsupported = 0; | |
145 | } else if (strcmp(type, "current") && strcmp(type, "diff")) { | |
146 | /* | |
147 | * By default hide unsupported settings | |
148 | * for all display types except "current" | |
149 | * and "diff". | |
150 | */ | |
151 | tree_spec.ignoreunsupported = 1; | |
152 | } | |
153 | ||
154 | if (strcmp(type, "current") && strcmp(type, "diff")) { | |
155 | /* | |
156 | * By default hide deprecated settings | |
157 | * for all display types except "current" | |
158 | * and "diff" unless --showdeprecated is set. | |
159 | * | |
160 | * N.B. Deprecated settings are visible if | |
161 | * --atversion is used with a version that | |
162 | * is lower than the version in which the | |
163 | * setting was deprecated. | |
164 | */ | |
165 | if (!arg_is_set(cmd, showdeprecated_ARG)) | |
166 | tree_spec.ignoredeprecated = 1; | |
167 | } | |
168 | ||
169 | if (arg_is_set(cmd, ignorelocal_ARG)) | |
170 | tree_spec.ignorelocal = 1; | |
171 | ||
172 | if (!strcmp(type, "current") || !strcmp(type, "full")) { | |
173 | if (arg_is_set(cmd, atversion_ARG)) { | |
174 | log_error("--atversion has no effect with --type %s", type); | |
175 | return EINVALID_CMD_LINE; | |
176 | } | |
177 | ||
178 | if ((arg_is_set(cmd, ignoreunsupported_ARG) || | |
179 | arg_is_set(cmd, ignoreadvanced_ARG)) && | |
180 | !strcmp(type, "current")) { | |
181 | /* FIXME: allow these even for --type current */ | |
182 | log_error("--ignoreadvanced and --ignoreunsupported has " | |
183 | "no effect with --type current"); | |
184 | return EINVALID_CMD_LINE; | |
185 | } | |
186 | } else if (arg_is_set(cmd, mergedconfig_ARG)) { | |
187 | log_error("--mergedconfig has no effect without --type current or --type full"); | |
188 | return EINVALID_CMD_LINE; | |
189 | } | |
190 | ||
191 | if (!_get_vsn(cmd, &tree_spec.version)) | |
192 | return EINVALID_CMD_LINE; | |
193 | ||
194 | /* | |
195 | * The profile specified by --profile cmd arg is like --commandprofile, | |
196 | * but it is used just for dumping the profile content and not for | |
197 | * application. | |
198 | */ | |
199 | if (arg_is_set(cmd, profile_ARG) && | |
200 | (!(profile = add_profile(cmd, arg_str_value(cmd, profile_ARG, NULL), CONFIG_PROFILE_COMMAND)) || | |
201 | !override_config_tree_from_profile(cmd, profile))) { | |
202 | log_error("Failed to load profile %s.", arg_str_value(cmd, profile_ARG, NULL)); | |
203 | return ECMD_FAILED; | |
204 | } | |
205 | ||
206 | /* | |
207 | * Set the 'cft' to work with based on whether we need the plain | |
208 | * config tree or merged config tree cascade if --mergedconfig is used. | |
209 | */ | |
210 | if ((arg_is_set(cmd, mergedconfig_ARG) || !strcmp(type, "full") || !strcmp(type, "diff")) && cmd->cft->cascade) { | |
211 | if (!_merge_config_cascade(cmd, cmd->cft, &cft)) { | |
212 | log_error("Failed to merge configuration."); | |
213 | r = ECMD_FAILED; | |
214 | goto out; | |
215 | } | |
216 | } else | |
217 | cft = cmd->cft; | |
218 | tree_spec.current_cft = cft; | |
219 | ||
220 | if (arg_is_set(cmd, validate_ARG)) { | |
221 | if (_config_validate(cmd, cft)) { | |
222 | log_print("LVM configuration valid."); | |
223 | goto out; | |
224 | } else { | |
225 | log_error("LVM configuration invalid."); | |
226 | r = ECMD_FAILED; | |
227 | goto out; | |
228 | } | |
229 | } | |
230 | ||
231 | if (!strcmp(type, "list") || arg_is_set(cmd, list_ARG)) { | |
232 | tree_spec.type = CFG_DEF_TREE_LIST; | |
233 | if (arg_is_set(cmd, withcomments_ARG)) { | |
234 | log_error("--withcomments has no effect with --type list"); | |
235 | return EINVALID_CMD_LINE; | |
236 | } | |
237 | if (arg_is_set(cmd, withlocalpreamble_ARG)) { | |
238 | log_error("--withlocalpreamble has no effect with --type list"); | |
239 | return EINVALID_CMD_LINE; | |
240 | } | |
241 | if (arg_is_set(cmd, withgeneralpreamble_ARG)) { | |
242 | log_error("--withgeneralpreamble has no effect with --type list"); | |
243 | return EINVALID_CMD_LINE; | |
244 | } | |
245 | if (arg_is_set(cmd, valuesonly_ARG)) { | |
246 | log_err("--valuesonly has no effect with --type list"); | |
247 | return EINVALID_CMD_LINE; | |
248 | } | |
249 | /* list type does not require status check */ | |
250 | } else if (!strcmp(type, "full")) { | |
251 | tree_spec.type = CFG_DEF_TREE_FULL; | |
252 | if (!_do_def_check(&tree_spec, cft, &cft_check_handle)) { | |
253 | r = ECMD_FAILED; | |
254 | goto_out; | |
255 | } | |
256 | } else if (!strcmp(type, "current")) { | |
257 | tree_spec.type = CFG_DEF_TREE_CURRENT; | |
258 | if (!_do_def_check(&tree_spec, cft, &cft_check_handle)) { | |
259 | r = ECMD_FAILED; | |
260 | goto_out; | |
261 | } | |
262 | } | |
263 | else if (!strcmp(type, "missing")) { | |
264 | tree_spec.type = CFG_DEF_TREE_MISSING; | |
265 | if (!_do_def_check(&tree_spec, cft, &cft_check_handle)) { | |
266 | r = ECMD_FAILED; | |
267 | goto_out; | |
268 | } | |
269 | } | |
270 | else if (!strcmp(type, "default")) { | |
271 | tree_spec.type = CFG_DEF_TREE_DEFAULT; | |
272 | /* default type does not require check status */ | |
273 | } | |
274 | else if (!strcmp(type, "diff")) { | |
275 | tree_spec.type = CFG_DEF_TREE_DIFF; | |
276 | if (!_do_def_check(&tree_spec, cft, &cft_check_handle)) { | |
277 | r = ECMD_FAILED; | |
278 | goto_out; | |
279 | } | |
280 | } | |
281 | else if (!strcmp(type, "new")) { | |
282 | tree_spec.type = arg_is_set(cmd, sinceversion_ARG) ? CFG_DEF_TREE_NEW_SINCE | |
283 | : CFG_DEF_TREE_NEW; | |
284 | /* new type does not require check status */ | |
285 | } | |
286 | else if (!strcmp(type, "profilable")) { | |
287 | tree_spec.type = CFG_DEF_TREE_PROFILABLE; | |
288 | /* profilable type does not require check status */ | |
289 | } | |
290 | else if (!strcmp(type, "profilable-command")) { | |
291 | tree_spec.type = CFG_DEF_TREE_PROFILABLE_CMD; | |
292 | /* profilable-command type does not require check status */ | |
293 | } | |
294 | else if (!strcmp(type, "profilable-metadata")) { | |
295 | tree_spec.type = CFG_DEF_TREE_PROFILABLE_MDA; | |
296 | /* profilable-metadata type does not require check status */ | |
297 | } | |
298 | else { | |
299 | log_error("Incorrect type of configuration specified. " | |
300 | "Expected one of: current, default, diff, full, list, missing, " | |
301 | "new, profilable, profilable-command, profilable-metadata."); | |
302 | r = EINVALID_CMD_LINE; | |
303 | goto out; | |
304 | } | |
305 | ||
306 | if (arg_is_set(cmd, withsummary_ARG) || arg_is_set(cmd, list_ARG)) | |
307 | tree_spec.withsummary = 1; | |
308 | ||
309 | if (arg_is_set(cmd, withcomments_ARG)) | |
310 | tree_spec.withcomments = 1; | |
311 | ||
312 | if (arg_is_set(cmd, unconfigured_ARG)) | |
313 | tree_spec.unconfigured = 1; | |
314 | ||
315 | if (arg_is_set(cmd, withversions_ARG)) | |
316 | tree_spec.withversions = 1; | |
317 | ||
318 | if (arg_is_set(cmd, withgeneralpreamble_ARG)) | |
319 | tree_spec.withgeneralpreamble = 1; | |
320 | ||
321 | if (arg_is_set(cmd, withlocalpreamble_ARG)) | |
322 | tree_spec.withlocalpreamble = 1; | |
323 | ||
324 | if (arg_is_set(cmd, withspaces_ARG)) | |
325 | tree_spec.withspaces = 1; | |
326 | ||
327 | if (arg_is_set(cmd, valuesonly_ARG)) | |
328 | tree_spec.valuesonly = 1; | |
329 | ||
330 | if (cft_check_handle) | |
331 | tree_spec.check_status = cft_check_handle->status; | |
332 | ||
333 | if ((tree_spec.type != CFG_DEF_TREE_CURRENT) && | |
334 | (tree_spec.type != CFG_DEF_TREE_DIFF) && | |
335 | !(cft = config_def_create_tree(&tree_spec))) { | |
336 | r = ECMD_FAILED; | |
337 | goto_out; | |
338 | } | |
339 | ||
340 | if (!config_write(cft, &tree_spec, file, argc, argv)) { | |
341 | stack; | |
342 | r = ECMD_FAILED; | |
343 | } | |
344 | out: | |
345 | if (tree_spec.current_cft && (tree_spec.current_cft != cft) && | |
346 | (tree_spec.current_cft != cmd->cft)) | |
347 | /* | |
348 | * This happens in case of CFG_DEF_TREE_FULL where we | |
349 | * have merged explicitly defined config trees and also | |
350 | * we have used default tree. | |
351 | */ | |
352 | dm_config_destroy(tree_spec.current_cft); | |
353 | ||
354 | if (cft && (cft != cmd->cft)) | |
355 | dm_config_destroy(cft); | |
356 | else if (profile) | |
357 | remove_config_tree_by_source(cmd, CONFIG_PROFILE_COMMAND); | |
358 | ||
359 | /* | |
360 | * The cmd->cft (the "current" tree) is destroyed | |
361 | * together with cmd context destroy... | |
362 | */ | |
363 | ||
364 | return r; | |
365 | } | |
366 | ||
367 | int config(struct cmd_context *cmd, int argc, char **argv) | |
368 | { | |
369 | return dumpconfig(cmd, argc, argv); | |
370 | } | |
371 | ||
372 | int lvmconfig(struct cmd_context *cmd, int argc, char **argv) | |
373 | { | |
374 | return dumpconfig(cmd, argc, argv); | |
375 | } |