1/*	$NetBSD$	*/
2
3/*
4 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5 * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
6 *
7 * This file is part of LVM2.
8 *
9 * This copyrighted material is made available to anyone wishing to use,
10 * modify, copy, or redistribute it subject to the terms and conditions
11 * of the GNU Lesser General Public License v.2.1.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, write to the Free Software Foundation,
15 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16 */
17
18#include "tools.h"
19#include "report.h"
20
21static int _vgs_single(struct cmd_context *cmd __attribute((unused)),
22		       const char *vg_name, struct volume_group *vg,
23		       void *handle)
24{
25	if (!report_object(handle, vg, NULL, NULL, NULL, NULL)) {
26		stack;
27		return ECMD_FAILED;
28	}
29
30	check_current_backup(vg);
31
32	return ECMD_PROCESSED;
33}
34
35static int _lvs_single(struct cmd_context *cmd, struct logical_volume *lv,
36		       void *handle)
37{
38	if (!arg_count(cmd, all_ARG) && !lv_is_visible(lv))
39		return ECMD_PROCESSED;
40
41	if (!report_object(handle, lv->vg, lv, NULL, NULL, NULL)) {
42		stack;
43		return ECMD_FAILED;
44	}
45
46	return ECMD_PROCESSED;
47}
48
49static int _segs_single(struct cmd_context *cmd __attribute((unused)),
50			struct lv_segment *seg, void *handle)
51{
52	if (!report_object(handle, seg->lv->vg, seg->lv, NULL, seg, NULL)) {
53		stack;
54		return ECMD_FAILED;
55	}
56
57	return ECMD_PROCESSED;
58}
59
60static int _pvsegs_sub_single(struct cmd_context *cmd,
61			      struct volume_group *vg,
62			      struct pv_segment *pvseg, void *handle)
63{
64	int ret = ECMD_PROCESSED;
65	struct lv_segment *seg = pvseg->lvseg;
66
67	struct volume_group _free_vg = {
68		.cmd = cmd,
69		.name = (char *)"",
70	};
71
72	struct logical_volume _free_logical_volume = {
73		.vg = vg ?: &_free_vg,
74		.name = (char *) "",
75	        .snapshot = NULL,
76		.status = VISIBLE_LV,
77		.major = -1,
78		.minor = -1,
79	};
80
81	struct lv_segment _free_lv_segment = {
82        	.lv = &_free_logical_volume,
83        	.le = 0,
84        	.status = 0,
85        	.stripe_size = 0,
86        	.area_count = 0,
87        	.area_len = 0,
88        	.origin = NULL,
89        	.cow = NULL,
90        	.chunk_size = 0,
91        	.region_size = 0,
92        	.extents_copied = 0,
93        	.log_lv = NULL,
94        	.areas = NULL,
95	};
96
97        _free_lv_segment.segtype = get_segtype_from_string(cmd, "free");
98	_free_lv_segment.len = pvseg->len;
99	dm_list_init(&_free_vg.pvs);
100	dm_list_init(&_free_vg.lvs);
101	dm_list_init(&_free_vg.tags);
102	dm_list_init(&_free_lv_segment.tags);
103	dm_list_init(&_free_lv_segment.origin_list);
104	dm_list_init(&_free_logical_volume.tags);
105	dm_list_init(&_free_logical_volume.segments);
106	dm_list_init(&_free_logical_volume.segs_using_this_lv);
107	dm_list_init(&_free_logical_volume.snapshot_segs);
108
109	if (!report_object(handle, vg, seg ? seg->lv : &_free_logical_volume, pvseg->pv,
110			   seg ? : &_free_lv_segment, pvseg)) {
111		stack;
112		ret = ECMD_FAILED;
113	}
114
115	return ret;
116}
117
118static int _lvsegs_single(struct cmd_context *cmd, struct logical_volume *lv,
119			  void *handle)
120{
121	if (!arg_count(cmd, all_ARG) && !lv_is_visible(lv))
122		return ECMD_PROCESSED;
123
124	return process_each_segment_in_lv(cmd, lv, handle, _segs_single);
125}
126
127static int _pvsegs_single(struct cmd_context *cmd, struct volume_group *vg,
128			  struct physical_volume *pv, void *handle)
129{
130	return process_each_segment_in_pv(cmd, vg, pv, handle,
131					  _pvsegs_sub_single);
132}
133
134static int _pvs_single(struct cmd_context *cmd, struct volume_group *vg,
135		       struct physical_volume *pv, void *handle)
136{
137	struct pv_list *pvl;
138	int ret = ECMD_PROCESSED;
139	const char *vg_name = NULL;
140	struct volume_group *old_vg = vg;
141
142	if (is_pv(pv) && !is_orphan(pv) && !vg) {
143		vg_name = pv_vg_name(pv);
144
145		vg = vg_read(cmd, vg_name, (char *)&pv->vgid, 0);
146		if (vg_read_error(vg)) {
147			log_error("Skipping volume group %s", vg_name);
148			vg_release(vg);
149			return ECMD_FAILED;
150		}
151
152		/*
153		 * Replace possibly incomplete PV structure with new one
154		 * allocated in vg_read_internal() path.
155		*/
156		if (!(pvl = find_pv_in_vg(vg, pv_dev_name(pv)))) {
157			log_error("Unable to find \"%s\" in volume group \"%s\"",
158				  pv_dev_name(pv), vg->name);
159			ret = ECMD_FAILED;
160			goto out;
161		}
162
163		 pv = pvl->pv;
164	}
165
166	if (!report_object(handle, vg, NULL, pv, NULL, NULL)) {
167		stack;
168		ret = ECMD_FAILED;
169	}
170
171out:
172	if (vg_name)
173		unlock_vg(cmd, vg_name);
174
175	if (!old_vg)
176		vg_release(vg);
177
178	return ret;
179}
180
181static int _label_single(struct cmd_context *cmd, struct volume_group *vg,
182		       struct physical_volume *pv, void *handle)
183{
184	if (!report_object(handle, vg, NULL, pv, NULL, NULL)) {
185		stack;
186		return ECMD_FAILED;
187	}
188
189	return ECMD_PROCESSED;
190}
191
192static int _pvs_in_vg(struct cmd_context *cmd, const char *vg_name,
193		      struct volume_group *vg,
194		      void *handle)
195{
196	if (vg_read_error(vg)) {
197		stack;
198		return ECMD_FAILED;
199	}
200
201	return process_each_pv_in_vg(cmd, vg, NULL, handle, &_pvs_single);
202}
203
204static int _pvsegs_in_vg(struct cmd_context *cmd, const char *vg_name,
205			 struct volume_group *vg,
206			 void *handle)
207{
208	if (vg_read_error(vg)) {
209		stack;
210		return ECMD_FAILED;
211	}
212
213	return process_each_pv_in_vg(cmd, vg, NULL, handle, &_pvsegs_single);
214}
215
216static int _report(struct cmd_context *cmd, int argc, char **argv,
217		   report_type_t report_type)
218{
219	void *report_handle;
220	const char *opts;
221	char *str;
222	const char *keys = NULL, *options = NULL, *separator;
223	int r = ECMD_PROCESSED;
224	int aligned, buffered, headings, field_prefixes, quoted;
225	int columns_as_rows;
226	unsigned args_are_pvs;
227
228	aligned = find_config_tree_int(cmd, "report/aligned",
229				  DEFAULT_REP_ALIGNED);
230	buffered = find_config_tree_int(cmd, "report/buffered",
231				   DEFAULT_REP_BUFFERED);
232	headings = find_config_tree_int(cmd, "report/headings",
233				   DEFAULT_REP_HEADINGS);
234	separator = find_config_tree_str(cmd, "report/separator",
235				    DEFAULT_REP_SEPARATOR);
236	field_prefixes = find_config_tree_int(cmd, "report/prefixes",
237					      DEFAULT_REP_PREFIXES);
238	quoted = find_config_tree_int(cmd, "report/quoted",
239				     DEFAULT_REP_QUOTED);
240	columns_as_rows = find_config_tree_int(cmd, "report/columns_as_rows",
241					       DEFAULT_REP_COLUMNS_AS_ROWS);
242
243	args_are_pvs = (report_type == PVS ||
244			report_type == LABEL ||
245			report_type == PVSEGS) ? 1 : 0;
246
247	switch (report_type) {
248	case LVS:
249		keys = find_config_tree_str(cmd, "report/lvs_sort",
250				       DEFAULT_LVS_SORT);
251		if (!arg_count(cmd, verbose_ARG))
252			options = find_config_tree_str(cmd,
253						  "report/lvs_cols",
254						  DEFAULT_LVS_COLS);
255		else
256			options = find_config_tree_str(cmd,
257						  "report/lvs_cols_verbose",
258						  DEFAULT_LVS_COLS_VERB);
259		break;
260	case VGS:
261		keys = find_config_tree_str(cmd, "report/vgs_sort",
262				       DEFAULT_VGS_SORT);
263		if (!arg_count(cmd, verbose_ARG))
264			options = find_config_tree_str(cmd,
265						  "report/vgs_cols",
266						  DEFAULT_VGS_COLS);
267		else
268			options = find_config_tree_str(cmd,
269						  "report/vgs_cols_verbose",
270						  DEFAULT_VGS_COLS_VERB);
271		break;
272	case LABEL:
273	case PVS:
274		keys = find_config_tree_str(cmd, "report/pvs_sort",
275				       DEFAULT_PVS_SORT);
276		if (!arg_count(cmd, verbose_ARG))
277			options = find_config_tree_str(cmd,
278						  "report/pvs_cols",
279						  DEFAULT_PVS_COLS);
280		else
281			options = find_config_tree_str(cmd,
282						  "report/pvs_cols_verbose",
283						  DEFAULT_PVS_COLS_VERB);
284		break;
285	case SEGS:
286		keys = find_config_tree_str(cmd, "report/segs_sort",
287				       DEFAULT_SEGS_SORT);
288		if (!arg_count(cmd, verbose_ARG))
289			options = find_config_tree_str(cmd,
290						  "report/segs_cols",
291						  DEFAULT_SEGS_COLS);
292		else
293			options = find_config_tree_str(cmd,
294						  "report/segs_cols_verbose",
295						  DEFAULT_SEGS_COLS_VERB);
296		break;
297	case PVSEGS:
298		keys = find_config_tree_str(cmd, "report/pvsegs_sort",
299				       DEFAULT_PVSEGS_SORT);
300		if (!arg_count(cmd, verbose_ARG))
301			options = find_config_tree_str(cmd,
302						  "report/pvsegs_cols",
303						  DEFAULT_PVSEGS_COLS);
304		else
305			options = find_config_tree_str(cmd,
306						  "report/pvsegs_cols_verbose",
307						  DEFAULT_PVSEGS_COLS_VERB);
308		break;
309	}
310
311	/* If -o supplied use it, else use default for report_type */
312	if (arg_count(cmd, options_ARG)) {
313		opts = arg_str_value(cmd, options_ARG, "");
314		if (!opts || !*opts) {
315			log_error("Invalid options string: %s", opts);
316			return EINVALID_CMD_LINE;
317		}
318		if (*opts == '+') {
319			if (!(str = dm_pool_alloc(cmd->mem,
320					 strlen(options) + strlen(opts) + 1))) {
321				log_error("options string allocation failed");
322				return ECMD_FAILED;
323			}
324			strcpy(str, options);
325			strcat(str, ",");
326			strcat(str, opts + 1);
327			options = str;
328		} else
329			options = opts;
330	}
331
332	/* -O overrides default sort settings */
333	keys = arg_str_value(cmd, sort_ARG, keys);
334
335	separator = arg_str_value(cmd, separator_ARG, separator);
336	if (arg_count(cmd, separator_ARG))
337		aligned = 0;
338	if (arg_count(cmd, aligned_ARG))
339		aligned = 1;
340	if (arg_count(cmd, unbuffered_ARG) && !arg_count(cmd, sort_ARG))
341		buffered = 0;
342	if (arg_count(cmd, noheadings_ARG))
343		headings = 0;
344	if (arg_count(cmd, nameprefixes_ARG)) {
345		aligned = 0;
346		field_prefixes = 1;
347	}
348	if (arg_count(cmd, unquoted_ARG))
349		quoted = 0;
350	if (arg_count(cmd, rows_ARG))
351		columns_as_rows = 1;
352
353	if (!(report_handle = report_init(cmd, options, keys, &report_type,
354					  separator, aligned, buffered,
355					  headings, field_prefixes, quoted,
356					  columns_as_rows))) {
357		stack;
358		return ECMD_FAILED;
359	}
360
361	/* Ensure options selected are compatible */
362	if (report_type & SEGS)
363		report_type |= LVS;
364	if (report_type & PVSEGS)
365		report_type |= PVS;
366	if ((report_type & LVS) && (report_type & (PVS | LABEL)) && !args_are_pvs) {
367		log_error("Can't report LV and PV fields at the same time");
368		dm_report_free(report_handle);
369		return ECMD_FAILED;
370	}
371
372	/* Change report type if fields specified makes this necessary */
373	if ((report_type & PVSEGS) ||
374	    ((report_type & (PVS | LABEL)) && (report_type & LVS)))
375		report_type = PVSEGS;
376	else if ((report_type & LABEL) && (report_type & VGS))
377		report_type = PVS;
378	else if (report_type & PVS)
379		report_type = PVS;
380	else if (report_type & SEGS)
381		report_type = SEGS;
382	else if (report_type & LVS)
383		report_type = LVS;
384
385	switch (report_type) {
386	case LVS:
387		r = process_each_lv(cmd, argc, argv, 0, report_handle,
388				    &_lvs_single);
389		break;
390	case VGS:
391		r = process_each_vg(cmd, argc, argv, 0,
392				    report_handle, &_vgs_single);
393		break;
394	case LABEL:
395		r = process_each_pv(cmd, argc, argv, NULL, READ_WITHOUT_LOCK,
396				    1, report_handle, &_label_single);
397		break;
398	case PVS:
399		if (args_are_pvs)
400			r = process_each_pv(cmd, argc, argv, NULL, 0,
401					    0, report_handle, &_pvs_single);
402		else
403			r = process_each_vg(cmd, argc, argv, 0,
404					    report_handle, &_pvs_in_vg);
405		break;
406	case SEGS:
407		r = process_each_lv(cmd, argc, argv, 0, report_handle,
408				    &_lvsegs_single);
409		break;
410	case PVSEGS:
411		if (args_are_pvs)
412			r = process_each_pv(cmd, argc, argv, NULL, 0,
413					    0, report_handle, &_pvsegs_single);
414		else
415			r = process_each_vg(cmd, argc, argv, 0,
416					    report_handle, &_pvsegs_in_vg);
417		break;
418	}
419
420	dm_report_output(report_handle);
421
422	dm_report_free(report_handle);
423	return r;
424}
425
426int lvs(struct cmd_context *cmd, int argc, char **argv)
427{
428	report_type_t type;
429
430	if (arg_count(cmd, segments_ARG))
431		type = SEGS;
432	else
433		type = LVS;
434
435	return _report(cmd, argc, argv, type);
436}
437
438int vgs(struct cmd_context *cmd, int argc, char **argv)
439{
440	return _report(cmd, argc, argv, VGS);
441}
442
443int pvs(struct cmd_context *cmd, int argc, char **argv)
444{
445	report_type_t type;
446
447	if (arg_count(cmd, segments_ARG))
448		type = PVSEGS;
449	else
450		type = LABEL;
451
452	return _report(cmd, argc, argv, type);
453}
454