/* * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. * Copyright (C) 2004-2009 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "tools.h" #include "report.h" static int _process_each_devtype(struct cmd_context *cmd, int argc, struct processing_handle *handle) { if (argc) log_warn("WARNING: devtypes currently ignores command line arguments."); if (!report_devtypes(handle->custom_handle)) return_ECMD_FAILED; return ECMD_PROCESSED; } static int _vgs_single(struct cmd_context *cmd __attribute__((unused)), const char *vg_name, struct volume_group *vg, struct processing_handle *handle) { struct selection_handle *sh = handle->selection_handle; if (!report_object(sh ? : handle->custom_handle, sh != NULL, vg, NULL, NULL, NULL, NULL, NULL, NULL)) return_ECMD_FAILED; check_current_backup(vg); return ECMD_PROCESSED; } static void _choose_lv_segment_for_status_report(const struct logical_volume *lv, const struct lv_segment **lv_seg) { /* * By default, take the first LV segment to report status for. * If there's any other specific segment that needs to be * reported instead for the LV, choose it here and assign it * to lvdm->seg_status->seg. This is the segment whose * status line will be used for report exactly. */ *lv_seg = first_seg(lv); } static int _do_info_and_status(struct cmd_context *cmd, const struct logical_volume *lv, const struct lv_segment *lv_seg, struct lv_with_info_and_seg_status *status, int do_info, int do_status) { unsigned use_layer = lv_is_thin_pool(lv) ? 1 : 0; status->lv = lv; if (do_status) { if (!(status->seg_status.mem = dm_pool_create("reporter_pool", 1024))) return_0; if (!lv_seg) _choose_lv_segment_for_status_report(lv, &lv_seg); if (do_info) { /* both info and status */ status->info_ok = lv_info_with_seg_status(cmd, lv, lv_seg, use_layer, status, 1, 1); /* for inactive thin-pools reset lv info struct */ if (use_layer && status->info_ok && !lv_info(cmd, lv, 0, NULL, 0, 0)) memset(&status->info, 0, sizeof(status->info)); } else /* status only */ status->info_ok = lv_status(cmd, lv_seg, use_layer, &status->seg_status); } else if (do_info) /* info only */ status->info_ok = lv_info(cmd, lv, use_layer, &status->info, 1, 1); return 1; } static int _do_lvs_with_info_and_status_single(struct cmd_context *cmd, const struct logical_volume *lv, int do_info, int do_status, struct processing_handle *handle) { struct selection_handle *sh = handle->selection_handle; struct lv_with_info_and_seg_status status = { .seg_status.type = SEG_STATUS_NONE }; int r = ECMD_FAILED; if (!_do_info_and_status(cmd, lv, NULL, &status, do_info, do_status)) goto_out; if (!report_object(sh ? : handle->custom_handle, sh != NULL, lv->vg, lv, NULL, NULL, NULL, &status, NULL)) goto out; r = ECMD_PROCESSED; out: if (status.seg_status.mem) dm_pool_destroy(status.seg_status.mem); return r; } static int _lvs_single(struct cmd_context *cmd, struct logical_volume *lv, struct processing_handle *handle) { return _do_lvs_with_info_and_status_single(cmd, lv, 0, 0, handle); } static int _lvs_with_info_single(struct cmd_context *cmd, struct logical_volume *lv, struct processing_handle *handle) { return _do_lvs_with_info_and_status_single(cmd, lv, 1, 0, handle); } static int _lvs_with_status_single(struct cmd_context *cmd, struct logical_volume *lv, struct processing_handle *handle) { return _do_lvs_with_info_and_status_single(cmd, lv, 0, 1, handle); } static int _lvs_with_info_and_status_single(struct cmd_context *cmd, struct logical_volume *lv, struct processing_handle *handle) { return _do_lvs_with_info_and_status_single(cmd, lv, 1, 1, handle); } static int _do_segs_with_info_and_status_single(struct cmd_context *cmd, const struct lv_segment *seg, int do_info, int do_status, struct processing_handle *handle) { struct selection_handle *sh = handle->selection_handle; struct lv_with_info_and_seg_status status = { .seg_status.type = SEG_STATUS_NONE }; int r = ECMD_FAILED; if (!_do_info_and_status(cmd, seg->lv, seg, &status, do_info, do_status)) goto_out; if (!report_object(sh ? : handle->custom_handle, sh != NULL, seg->lv->vg, seg->lv, NULL, seg, NULL, &status, NULL)) goto_out; r = ECMD_PROCESSED; out: if (status.seg_status.mem) dm_pool_destroy(status.seg_status.mem); return r; } static int _segs_single(struct cmd_context *cmd, struct lv_segment *seg, struct processing_handle *handle) { return _do_segs_with_info_and_status_single(cmd, seg, 0, 0, handle); } static int _segs_with_info_single(struct cmd_context *cmd, struct lv_segment *seg, struct processing_handle *handle) { return _do_segs_with_info_and_status_single(cmd, seg, 1, 0, handle); } static int _segs_with_status_single(struct cmd_context *cmd, struct lv_segment *seg, struct processing_handle *handle) { return _do_segs_with_info_and_status_single(cmd, seg, 0, 1, handle); } static int _segs_with_info_and_status_single(struct cmd_context *cmd, struct lv_segment *seg, struct processing_handle *handle) { return _do_segs_with_info_and_status_single(cmd, seg, 1, 1, handle); } static int _lvsegs_single(struct cmd_context *cmd, struct logical_volume *lv, struct processing_handle *handle) { if (!arg_count(cmd, all_ARG) && !lv_is_visible(lv)) return ECMD_PROCESSED; return process_each_segment_in_lv(cmd, lv, handle, _segs_single); } static int _lvsegs_with_info_single(struct cmd_context *cmd, struct logical_volume *lv, struct processing_handle *handle) { if (!arg_count(cmd, all_ARG) && !lv_is_visible(lv)) return ECMD_PROCESSED; return process_each_segment_in_lv(cmd, lv, handle, _segs_with_info_single); } static int _lvsegs_with_status_single(struct cmd_context *cmd, struct logical_volume *lv, struct processing_handle *handle) { if (!arg_count(cmd, all_ARG) && !lv_is_visible(lv)) return ECMD_PROCESSED; return process_each_segment_in_lv(cmd, lv, handle, _segs_with_status_single); } static int _lvsegs_with_info_and_status_single(struct cmd_context *cmd, struct logical_volume *lv, struct processing_handle *handle) { if (!arg_count(cmd, all_ARG) && !lv_is_visible(lv)) return ECMD_PROCESSED; return process_each_segment_in_lv(cmd, lv, handle, _segs_with_info_and_status_single); } static int _do_pvsegs_sub_single(struct cmd_context *cmd, struct volume_group *vg, struct pv_segment *pvseg, int do_info, int do_status, struct processing_handle *handle) { struct selection_handle *sh = handle->selection_handle; int ret = ECMD_PROCESSED; struct lv_segment *seg = pvseg->lvseg; struct segment_type _freeseg_type = { .name = "free", .flags = SEG_VIRTUAL | SEG_CANNOT_BE_ZEROED, }; struct volume_group _free_vg = { .cmd = cmd, .name = "", .pvs = DM_LIST_HEAD_INIT(_free_vg.pvs), .lvs = DM_LIST_HEAD_INIT(_free_vg.lvs), .tags = DM_LIST_HEAD_INIT(_free_vg.tags), }; struct logical_volume _free_logical_volume = { .vg = vg ?: &_free_vg, .name = "", .status = VISIBLE_LV, .major = -1, .minor = -1, .tags = DM_LIST_HEAD_INIT(_free_logical_volume.tags), .segments = DM_LIST_HEAD_INIT(_free_logical_volume.segments), .segs_using_this_lv = DM_LIST_HEAD_INIT(_free_logical_volume.segs_using_this_lv), .snapshot_segs = DM_LIST_HEAD_INIT(_free_logical_volume.snapshot_segs), }; struct lv_segment _free_lv_segment = { .lv = &_free_logical_volume, .segtype = &_freeseg_type, .len = pvseg->len, .tags = DM_LIST_HEAD_INIT(_free_lv_segment.tags), .origin_list = DM_LIST_HEAD_INIT(_free_lv_segment.origin_list), }; struct lv_with_info_and_seg_status status = { .seg_status.type = SEG_STATUS_NONE, .lv = &_free_logical_volume }; if (seg && !_do_info_and_status(cmd, seg->lv, seg, &status, do_info, do_status)) goto_out; if (!report_object(sh ? : handle->custom_handle, sh != NULL, vg, seg ? seg->lv : &_free_logical_volume, pvseg->pv, seg ? : &_free_lv_segment, pvseg, &status, pv_label(pvseg->pv))) { ret = ECMD_FAILED; goto_out; } out: if (status.seg_status.mem) dm_pool_destroy(status.seg_status.mem); return ret; } static int _pvsegs_sub_single(struct cmd_context *cmd, struct volume_group *vg, struct pv_segment *pvseg, struct processing_handle *handle) { return _do_pvsegs_sub_single(cmd, vg, pvseg, 0, 0, handle); } static int _pvsegs_with_lv_info_sub_single(struct cmd_context *cmd, struct volume_group *vg, struct pv_segment *pvseg, struct processing_handle *handle) { return _do_pvsegs_sub_single(cmd, vg, pvseg, 1, 0, handle); } static int _pvsegs_with_lv_status_sub_single(struct cmd_context *cmd, struct volume_group *vg, struct pv_segment *pvseg, struct processing_handle *handle) { return _do_pvsegs_sub_single(cmd, vg, pvseg, 0, 1, handle); } static int _pvsegs_with_lv_info_and_status_sub_single(struct cmd_context *cmd, struct volume_group *vg, struct pv_segment *pvseg, struct processing_handle *handle) { return _do_pvsegs_sub_single(cmd, vg, pvseg, 1, 1, handle); } static int _pvsegs_single(struct cmd_context *cmd, struct volume_group *vg, struct physical_volume *pv, struct processing_handle *handle) { return process_each_segment_in_pv(cmd, vg, pv, handle, _pvsegs_sub_single); } static int _pvsegs_with_lv_info_single(struct cmd_context *cmd, struct volume_group *vg, struct physical_volume *pv, struct processing_handle *handle) { return process_each_segment_in_pv(cmd, vg, pv, handle, _pvsegs_with_lv_info_sub_single); } static int _pvsegs_with_lv_status_single(struct cmd_context *cmd, struct volume_group *vg, struct physical_volume *pv, struct processing_handle *handle) { return process_each_segment_in_pv(cmd, vg, pv, handle, _pvsegs_with_lv_status_sub_single); } static int _pvsegs_with_lv_info_and_status_single(struct cmd_context *cmd, struct volume_group *vg, struct physical_volume *pv, struct processing_handle *handle) { return process_each_segment_in_pv(cmd, vg, pv, handle, _pvsegs_with_lv_info_and_status_sub_single); } static int _pvs_single(struct cmd_context *cmd, struct volume_group *vg, struct physical_volume *pv, struct processing_handle *handle) { struct selection_handle *sh = handle->selection_handle; if (!report_object(sh ? : handle->custom_handle, sh != NULL, vg, NULL, pv, NULL, NULL, NULL, NULL)) return_ECMD_FAILED; return ECMD_PROCESSED; } static int _label_single(struct cmd_context *cmd, struct label *label, struct processing_handle *handle) { struct selection_handle *sh = handle->selection_handle; if (!report_object(sh ? : handle->custom_handle, sh != NULL, NULL, NULL, NULL, NULL, NULL, NULL, label)) return_ECMD_FAILED; return ECMD_PROCESSED; } static int _pvs_in_vg(struct cmd_context *cmd, const char *vg_name, struct volume_group *vg, struct processing_handle *handle) { return process_each_pv_in_vg(cmd, vg, handle, &_pvs_single); } static int _pvsegs_in_vg(struct cmd_context *cmd, const char *vg_name, struct volume_group *vg, struct processing_handle *handle) { return process_each_pv_in_vg(cmd, vg, handle, &_pvsegs_single); } static int _get_final_report_type(int args_are_pvs, report_type_t report_type, int *lv_info_needed, int *lv_segment_status_needed, report_type_t *final_report_type) { /* Do we need to acquire LV device info in addition? */ *lv_info_needed = (report_type & (LVSINFO | LVSINFOSTATUS)) ? 1 : 0; /* Do we need to acquire LV device status in addition? */ *lv_segment_status_needed = (report_type & (SEGSSTATUS | LVSSTATUS | LVSINFOSTATUS)) ? 1 : 0; /* Ensure options selected are compatible */ if (report_type & (SEGS | SEGSSTATUS)) report_type |= LVS; if (report_type & PVSEGS) report_type |= PVS; if ((report_type & (LVS | LVSINFO | LVSSTATUS | LVSINFOSTATUS)) && (report_type & (PVS | LABEL)) && !args_are_pvs) { log_error("Can't report LV and PV fields at the same time"); return 0; } /* Change report type if fields specified makes this necessary */ if ((report_type & PVSEGS) || ((report_type & (PVS | LABEL)) && (report_type & (LVS | LVSINFO | LVSSTATUS | LVSINFOSTATUS)))) report_type = PVSEGS; else if ((report_type & PVS) || ((report_type & LABEL) && (report_type & VGS))) report_type = PVS; else if (report_type & (SEGS | SEGSSTATUS)) report_type = SEGS; else if (report_type & (LVS | LVSINFO | LVSSTATUS | LVSINFOSTATUS)) report_type = LVS; *final_report_type = report_type; return 1; } int report_for_selection(struct cmd_context *cmd, struct selection_handle *sh, struct physical_volume *pv, struct volume_group *vg, struct logical_volume *lv) { static const char *incorrect_report_type_msg = "report_for_selection: incorrect report type"; int args_are_pvs = sh->orig_report_type == PVS; int do_lv_info, do_lv_seg_status; struct processing_handle *handle; int r = 0; if (!_get_final_report_type(args_are_pvs, sh->orig_report_type | sh->report_type, &do_lv_info, &do_lv_seg_status, &sh->report_type)) return_0; if (!(handle = init_processing_handle(cmd))) return_0; /* * We're already reporting for select so override * internal_report_for_select to 0 as we can call * process_each_* functions again and we could * end up in an infinite loop if we didn't stop * internal reporting for select right here. * * So the overall call trace from top to bottom looks like this: * * process_each_* (top-level one, using processing_handle with internal reporting enabled and selection_handle) -> * select_match_*(processing_handle with selection_handle) -> * report for selection -> * (creating new processing_handle here with internal reporting disabled!!!) * reporting_fn OR process_each_* (using *new* processing_handle with original selection_handle) * * The selection_handle is still reused so we can track * whether any of the items the top-level one is composed * of are still selected or not unerneath. Do not destroy * this selection handle - it needs to be passed to upper * layers to check the overall selection status. */ handle->internal_report_for_select = 0; handle->selection_handle = sh; /* * Remember: * sh->orig_report_type is the original report type requested (what are we selecting? PV/VG/LV?) * sh->report_type is the report type actually used (it counts with all types of fields used in selection criteria) */ switch (sh->orig_report_type) { case LVS: switch (sh->report_type) { case LVS: r = _do_lvs_with_info_and_status_single(vg->cmd, lv, do_lv_info, do_lv_seg_status, handle); break; case SEGS: r = process_each_segment_in_lv(vg->cmd, lv, handle, do_lv_info && !do_lv_seg_status ? &_segs_with_info_single : !do_lv_info && do_lv_seg_status ? &_segs_with_status_single : do_lv_info && do_lv_seg_status ? &_segs_with_info_and_status_single : &_segs_single); break; default: log_error(INTERNAL_ERROR "%s for LVS", incorrect_report_type_msg); break; } break; case VGS: switch (sh->report_type) { case VGS: r = _vgs_single(vg->cmd, vg->name, vg, handle); break; case LVS: r = process_each_lv_in_vg(vg->cmd, vg, NULL, NULL, 0, handle, do_lv_info && !do_lv_seg_status ? &_lvs_with_info_single : !do_lv_info && do_lv_seg_status ? &_lvs_with_status_single : do_lv_info && do_lv_seg_status ? &_lvs_with_info_and_status_single : &_lvs_single); break; case SEGS: r = process_each_lv_in_vg(vg->cmd, vg, NULL, NULL, 0, handle, do_lv_info && !do_lv_seg_status ? &_lvsegs_with_info_single : !do_lv_info && do_lv_seg_status ? &_lvsegs_with_status_single : do_lv_info && do_lv_seg_status ? &_lvsegs_with_info_and_status_single : &_lvsegs_single); break; case PVS: r = process_each_pv_in_vg(vg->cmd, vg, handle, &_pvs_single); break; case PVSEGS: r = process_each_pv_in_vg(vg->cmd, vg, handle, do_lv_info && !do_lv_seg_status ? &_pvsegs_with_lv_info_single : !do_lv_info && do_lv_seg_status ? &_pvsegs_with_lv_status_single : do_lv_info && do_lv_seg_status ? &_pvsegs_with_lv_info_and_status_single : &_pvsegs_single); break; default: log_error(INTERNAL_ERROR "%s for VGS", incorrect_report_type_msg); break; } break; case PVS: switch (sh->report_type) { case PVS: r = _pvs_single(vg->cmd, vg, pv, handle); break; case PVSEGS: r = process_each_segment_in_pv(vg->cmd, vg, pv, handle, do_lv_info && !do_lv_seg_status ? &_pvsegs_with_lv_info_sub_single : !do_lv_info && do_lv_seg_status ? &_pvsegs_with_lv_status_sub_single : do_lv_info && do_lv_seg_status ? &_pvsegs_with_lv_info_and_status_sub_single : &_pvsegs_sub_single); break; default: log_error(INTERNAL_ERROR "%s for PVS", incorrect_report_type_msg); break; } break; default: log_error(INTERNAL_ERROR "%s", incorrect_report_type_msg); break; } /* * Keep the selection handle provided from the caller - * do not destroy it - the caller will still use it to * pass the result through it to layers above. */ handle->selection_handle = NULL; destroy_processing_handle(cmd, handle); return r; } static void _check_pv_list(struct cmd_context *cmd, int argc, char **argv, report_type_t *report_type, unsigned *args_are_pvs) { unsigned i; int rescan_done = 0; *args_are_pvs = (*report_type == PVS || *report_type == LABEL || *report_type == PVSEGS) ? 1 : 0; if (*args_are_pvs && argc) { for (i = 0; i < argc; i++) { if (!rescan_done && !dev_cache_get(argv[i], cmd->full_filter)) { cmd->filter->wipe(cmd->filter); /* FIXME scan only one device */ lvmcache_label_scan(cmd); rescan_done = 1; } if (*argv[i] == '@') { /* * Tags are metadata related, not label * related, change report type accordingly! */ if (*report_type == LABEL) *report_type = PVS; /* * If we changed the report_type and we did rescan, * no need to iterate over dev list further - nothing * else would change. */ if (rescan_done) break; } } } } static void _del_option_from_list(struct dm_list *sll, const char *prefix, size_t prefix_len, const char *str) { struct dm_list *slh; struct dm_str_list *sl; const char *a = str, *b; prefix_len--; dm_list_uniterate(slh, sll, sll) { sl = dm_list_item(slh, struct dm_str_list); /* exact match */ if (!strcmp(str, sl->str)) { dm_list_del(slh); return; } /* also try to match with known prefix */ b = sl->str; if (!strncmp(prefix, a, prefix_len)) { a += prefix_len; if (*a == '_') a++; } if (!strncmp(prefix, b, prefix_len)) { b += prefix_len; if (*b == '_') b++; } if (!strcmp(a, b)) { dm_list_del(slh); return; } } } static int _get_report_options(struct cmd_context *cmd, report_type_t report_type, const char **options, const char **fields_to_compact) { const char *prefix = report_get_field_prefix(report_type); size_t prefix_len = strlen(prefix); struct arg_value_group_list *current_group; struct dm_list *final_opts_list; struct dm_list *final_compact_list = NULL; struct dm_list *opts_list = NULL; struct dm_str_list *sl; const char *opts; struct dm_pool *mem; int r = ECMD_PROCESSED; if (!(mem = dm_pool_create("report_options", 128))) { log_error("Failed to create temporary mempool to process report options."); return ECMD_FAILED; } if (!(final_opts_list = str_to_str_list(mem, *options, ",", 1))) { r = ECMD_FAILED; goto_out; } dm_list_iterate_items(current_group, &cmd->arg_value_groups) { if (!grouped_arg_is_set(current_group->arg_values, options_ARG)) continue; opts = grouped_arg_str_value(current_group->arg_values, options_ARG, NULL); if (!opts || !*opts) { log_error("Invalid options string: %s", opts); r = EINVALID_CMD_LINE; goto out; } switch (*opts) { case '+': /* fall through */ case '-': /* fall through */ case '#': if (!(opts_list = str_to_str_list(mem, opts + 1, ",", 1))) { r = ECMD_FAILED; goto_out; } if (*opts == '+') { dm_list_splice(final_opts_list, opts_list); } else if (*opts == '-') { dm_list_iterate_items(sl, opts_list) _del_option_from_list(final_opts_list, prefix, prefix_len, sl->str); } else if (*opts == '#') { if (!final_compact_list) final_compact_list = opts_list; else dm_list_splice(final_compact_list, opts_list); } break; default: if (!(final_opts_list = str_to_str_list(mem, opts, ",", 1))) { r = ECMD_FAILED; goto_out; } } } if (!(*options = str_list_to_str(cmd->mem, final_opts_list, ","))) { r = ECMD_FAILED; goto_out; } if (final_compact_list && !(*fields_to_compact = str_list_to_str(cmd->mem, final_compact_list, ","))) { dm_pool_free(cmd->mem, (char *) *options); r = ECMD_FAILED; goto_out; } out: dm_pool_destroy(mem); return r; } static int _report(struct cmd_context *cmd, int argc, char **argv, report_type_t report_type) { void *report_handle; struct processing_handle handle = {0}; const char *keys = NULL, *options = NULL, *selection = NULL, *separator; int r = ECMD_PROCESSED; int aligned, buffered, headings, field_prefixes, quoted; int columns_as_rows; unsigned args_are_pvs; int lv_info_needed, lv_segment_status_needed; int lock_global = 0; const char *fields_to_compact = NULL; aligned = find_config_tree_bool(cmd, report_aligned_CFG, NULL); buffered = find_config_tree_bool(cmd, report_buffered_CFG, NULL); headings = find_config_tree_bool(cmd, report_headings_CFG, NULL); separator = find_config_tree_str(cmd, report_separator_CFG, NULL); field_prefixes = find_config_tree_bool(cmd, report_prefixes_CFG, NULL); quoted = find_config_tree_bool(cmd, report_quoted_CFG, NULL); columns_as_rows = find_config_tree_bool(cmd, report_colums_as_rows_CFG, NULL); /* * Include foreign VGs that contain active LVs. * That shouldn't happen in general, but if it does by some * mistake, then we want to display those VGs and allow the * LVs to be deactivated. */ cmd->include_active_foreign_vgs = 1; /* Check PV specifics and do extra changes/actions if needed. */ _check_pv_list(cmd, argc, argv, &report_type, &args_are_pvs); switch (report_type) { case DEVTYPES: keys = find_config_tree_str(cmd, report_devtypes_sort_CFG, NULL); if (!arg_count(cmd, verbose_ARG)) options = find_config_tree_str(cmd, report_devtypes_cols_CFG, NULL); else options = find_config_tree_str(cmd, report_devtypes_cols_verbose_CFG, NULL); break; case LVS: keys = find_config_tree_str(cmd, report_lvs_sort_CFG, NULL); if (!arg_count(cmd, verbose_ARG)) options = find_config_tree_str(cmd, report_lvs_cols_CFG, NULL); else options = find_config_tree_str(cmd, report_lvs_cols_verbose_CFG, NULL); break; case VGS: keys = find_config_tree_str(cmd, report_vgs_sort_CFG, NULL); if (!arg_count(cmd, verbose_ARG)) options = find_config_tree_str(cmd, report_vgs_cols_CFG, NULL); else options = find_config_tree_str(cmd, report_vgs_cols_verbose_CFG, NULL); break; case LABEL: case PVS: keys = find_config_tree_str(cmd, report_pvs_sort_CFG, NULL); if (!arg_count(cmd, verbose_ARG)) options = find_config_tree_str(cmd, report_pvs_cols_CFG, NULL); else options = find_config_tree_str(cmd, report_pvs_cols_verbose_CFG, NULL); break; case SEGS: keys = find_config_tree_str(cmd, report_segs_sort_CFG, NULL); if (!arg_count(cmd, verbose_ARG)) options = find_config_tree_str(cmd, report_segs_cols_CFG, NULL); else options = find_config_tree_str(cmd, report_segs_cols_verbose_CFG, NULL); break; case PVSEGS: keys = find_config_tree_str(cmd, report_pvsegs_sort_CFG, NULL); if (!arg_count(cmd, verbose_ARG)) options = find_config_tree_str(cmd, report_pvsegs_cols_CFG, NULL); else options = find_config_tree_str(cmd, report_pvsegs_cols_verbose_CFG, NULL); break; default: log_error(INTERNAL_ERROR "Unknown report type."); return ECMD_FAILED; } /* If -o supplied use it, else use default for report_type */ if (arg_count(cmd, options_ARG) && ((r = _get_report_options(cmd, report_type, &options, &fields_to_compact) != ECMD_PROCESSED))) return r; /* -O overrides default sort settings */ keys = arg_str_value(cmd, sort_ARG, keys); separator = arg_str_value(cmd, separator_ARG, separator); if (arg_count(cmd, separator_ARG)) aligned = 0; if (arg_count(cmd, aligned_ARG)) aligned = 1; if (arg_count(cmd, unbuffered_ARG) && !arg_count(cmd, sort_ARG)) buffered = 0; if (arg_count(cmd, noheadings_ARG)) headings = 0; if (arg_count(cmd, nameprefixes_ARG)) { aligned = 0; field_prefixes = 1; } if (arg_count(cmd, unquoted_ARG)) quoted = 0; if (arg_count(cmd, rows_ARG)) columns_as_rows = 1; if (arg_count(cmd, select_ARG)) selection = arg_str_value(cmd, select_ARG, NULL); if (!(report_handle = report_init(cmd, options, keys, &report_type, separator, aligned, buffered, headings, field_prefixes, quoted, columns_as_rows, selection))) return_ECMD_FAILED; if (!_get_final_report_type(args_are_pvs, report_type, &lv_info_needed, &lv_segment_status_needed, &report_type)) { dm_report_free(report_handle); return ECMD_FAILED; } /* * We lock VG_GLOBAL to enable use of metadata cache. * This can pause alongide pvscan or vgscan process for a while. */ if (args_are_pvs && (report_type == PVS || report_type == PVSEGS) && !lvmetad_active()) { lock_global = 1; if (!lock_vol(cmd, VG_GLOBAL, LCK_VG_READ, NULL)) { log_error("Unable to obtain global lock."); dm_report_free(report_handle); return ECMD_FAILED; } } handle.internal_report_for_select = 0; handle.custom_handle = report_handle; switch (report_type) { case DEVTYPES: r = _process_each_devtype(cmd, argc, &handle); break; case LVSINFO: /* fall through */ case LVSSTATUS: /* fall through */ case LVSINFOSTATUS: /* fall through */ case LVS: r = process_each_lv(cmd, argc, argv, 0, &handle, lv_info_needed && !lv_segment_status_needed ? &_lvs_with_info_single : !lv_info_needed && lv_segment_status_needed ? &_lvs_with_status_single : lv_info_needed && lv_segment_status_needed ? &_lvs_with_info_and_status_single : &_lvs_single); break; case VGS: r = process_each_vg(cmd, argc, argv, NULL, 0, &handle, &_vgs_single); break; case LABEL: r = process_each_label(cmd, argc, argv, &handle, &_label_single); break; case PVS: if (args_are_pvs) r = process_each_pv(cmd, argc, argv, NULL, 0, &handle, &_pvs_single); else r = process_each_vg(cmd, argc, argv, NULL, 0, &handle, &_pvs_in_vg); break; case SEGSSTATUS: /* fall through */ case SEGS: r = process_each_lv(cmd, argc, argv, 0, &handle, lv_info_needed && !lv_segment_status_needed ? &_lvsegs_with_info_single : !lv_info_needed && lv_segment_status_needed ? &_lvsegs_with_status_single : lv_info_needed && lv_segment_status_needed ? &_lvsegs_with_info_and_status_single : &_lvsegs_single); break; case PVSEGS: if (args_are_pvs) r = process_each_pv(cmd, argc, argv, NULL, 0, &handle, lv_info_needed && !lv_segment_status_needed ? &_pvsegs_with_lv_info_single : !lv_info_needed && lv_segment_status_needed ? &_pvsegs_with_lv_status_single : lv_info_needed && lv_segment_status_needed ? &_pvsegs_with_lv_info_and_status_single : &_pvsegs_single); else r = process_each_vg(cmd, argc, argv, NULL, 0, &handle, &_pvsegs_in_vg); break; } if (find_config_tree_bool(cmd, report_compact_output_CFG, NULL)) { if (!dm_report_compact_fields(report_handle)) log_error("Failed to compact report output."); } else if (fields_to_compact || (fields_to_compact = find_config_tree_str_allow_empty(cmd, report_compact_output_cols_CFG, NULL))) { if (!dm_report_compact_given_fields(report_handle, fields_to_compact)) log_error("Failed to compact given columns in report output."); } dm_report_output(report_handle); dm_report_free(report_handle); if (lock_global) unlock_vg(cmd, VG_GLOBAL); return r; } int lvs(struct cmd_context *cmd, int argc, char **argv) { report_type_t type; if (arg_count(cmd, segments_ARG)) type = SEGS; else type = LVS; return _report(cmd, argc, argv, type); } int vgs(struct cmd_context *cmd, int argc, char **argv) { return _report(cmd, argc, argv, VGS); } int pvs(struct cmd_context *cmd, int argc, char **argv) { report_type_t type; if (arg_count(cmd, segments_ARG)) type = PVSEGS; else type = LABEL; return _report(cmd, argc, argv, type); } int devtypes(struct cmd_context *cmd, int argc, char **argv) { return _report(cmd, argc, argv, DEVTYPES); }