mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-22 17:35:59 +03:00
1573 lines
34 KiB
C
1573 lines
34 KiB
C
/*
|
|
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
|
|
* Copyright (C) 2004 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 General Public License v.2.
|
|
*
|
|
* You should have received a copy of the GNU 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 "lib.h"
|
|
#include "metadata.h"
|
|
#include "report.h"
|
|
#include "toolcontext.h"
|
|
#include "lvm-string.h"
|
|
#include "display.h"
|
|
#include "activate.h"
|
|
#include "segtype.h"
|
|
#include "str_list.h"
|
|
|
|
/*
|
|
* For macro use
|
|
*/
|
|
static union {
|
|
struct physical_volume _pv;
|
|
struct logical_volume _lv;
|
|
struct volume_group _vg;
|
|
struct lv_segment _seg;
|
|
struct pv_segment _pvseg;
|
|
} _dummy;
|
|
|
|
/*
|
|
* Report handle flags
|
|
*/
|
|
#define RH_SORT_REQUIRED 0x00000001
|
|
#define RH_HEADINGS_PRINTED 0x00000002
|
|
#define RH_BUFFERED 0x00000004
|
|
#define RH_ALIGNED 0x00000008
|
|
#define RH_HEADINGS 0x00000010
|
|
|
|
struct report_handle {
|
|
struct cmd_context *cmd;
|
|
struct dm_pool *mem;
|
|
|
|
report_type_t type;
|
|
const char *field_prefix;
|
|
uint32_t flags;
|
|
const char *separator;
|
|
|
|
uint32_t keys_count;
|
|
|
|
/* Ordered list of fields needed for this report */
|
|
struct list field_props;
|
|
|
|
/* Rows of report data */
|
|
struct list rows;
|
|
};
|
|
|
|
/*
|
|
* Per-field flags
|
|
*/
|
|
#define FLD_ALIGN_LEFT 0x00000001
|
|
#define FLD_ALIGN_RIGHT 0x00000002
|
|
#define FLD_STRING 0x00000004
|
|
#define FLD_NUMBER 0x00000008
|
|
#define FLD_HIDDEN 0x00000010
|
|
#define FLD_SORT_KEY 0x00000020
|
|
#define FLD_ASCENDING 0x00000040
|
|
#define FLD_DESCENDING 0x00000080
|
|
|
|
struct field_properties {
|
|
struct list list;
|
|
uint32_t field_num;
|
|
uint32_t sort_posn;
|
|
int width;
|
|
uint32_t flags;
|
|
};
|
|
|
|
/*
|
|
* Report data
|
|
*/
|
|
struct field {
|
|
struct list list;
|
|
struct field_properties *props;
|
|
|
|
const char *report_string; /* Formatted ready for display */
|
|
const void *sort_value; /* Raw value for sorting */
|
|
};
|
|
|
|
struct row {
|
|
struct list list;
|
|
struct report_handle *rh;
|
|
struct list fields; /* Fields in display order */
|
|
struct field *(*sort_fields)[]; /* Fields in sort order */
|
|
};
|
|
|
|
static char _alloc_policy_char(alloc_policy_t alloc)
|
|
{
|
|
switch (alloc) {
|
|
case ALLOC_CONTIGUOUS:
|
|
return 'c';
|
|
case ALLOC_CLING:
|
|
return 'C';
|
|
case ALLOC_NORMAL:
|
|
return 'n';
|
|
case ALLOC_ANYWHERE:
|
|
return 'a';
|
|
default:
|
|
return 'i';
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Data-munging functions to prepare each data type for display and sorting
|
|
*/
|
|
static int _string_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
if (!
|
|
(field->report_string =
|
|
dm_pool_strdup(rh->mem, *(const char **) data))) {
|
|
log_error("dm_pool_strdup failed");
|
|
return 0;
|
|
}
|
|
|
|
field->sort_value = (const void *) field->report_string;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _dev_name_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const char *name = dev_name(*(const struct device **) data);
|
|
|
|
return _string_disp(rh, field, &name);
|
|
}
|
|
|
|
static int _devices_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
unsigned int s;
|
|
const char *name = NULL;
|
|
uint32_t extent = 0;
|
|
char extent_str[32];
|
|
|
|
if (!dm_pool_begin_object(rh->mem, 256)) {
|
|
log_error("dm_pool_begin_object failed");
|
|
return 0;
|
|
}
|
|
|
|
for (s = 0; s < seg->area_count; s++) {
|
|
switch (seg_type(seg, s)) {
|
|
case AREA_LV:
|
|
name = seg_lv(seg, s)->name;
|
|
extent = seg_le(seg, s);
|
|
break;
|
|
case AREA_PV:
|
|
name = dev_name(seg_dev(seg, s));
|
|
extent = seg_pe(seg, s);
|
|
break;
|
|
case AREA_UNASSIGNED:
|
|
name = "unassigned";
|
|
extent = 0;
|
|
}
|
|
|
|
if (!dm_pool_grow_object(rh->mem, name, strlen(name))) {
|
|
log_error("dm_pool_grow_object failed");
|
|
return 0;
|
|
}
|
|
|
|
if (dm_snprintf(extent_str, sizeof(extent_str), "(%" PRIu32
|
|
")", extent) < 0) {
|
|
log_error("Extent number dm_snprintf failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!dm_pool_grow_object(rh->mem, extent_str, strlen(extent_str))) {
|
|
log_error("dm_pool_grow_object failed");
|
|
return 0;
|
|
}
|
|
|
|
if ((s != seg->area_count - 1) &&
|
|
!dm_pool_grow_object(rh->mem, ",", 1)) {
|
|
log_error("dm_pool_grow_object failed");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
|
|
log_error("dm_pool_grow_object failed");
|
|
return 0;
|
|
}
|
|
|
|
field->report_string = dm_pool_end_object(rh->mem);
|
|
field->sort_value = (const void *) field->report_string;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _tags_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct list *tags = (const struct list *) data;
|
|
struct str_list *sl;
|
|
|
|
if (!dm_pool_begin_object(rh->mem, 256)) {
|
|
log_error("dm_pool_begin_object failed");
|
|
return 0;
|
|
}
|
|
|
|
list_iterate_items(sl, tags) {
|
|
if (!dm_pool_grow_object(rh->mem, sl->str, strlen(sl->str)) ||
|
|
(sl->list.n != tags && !dm_pool_grow_object(rh->mem, ",", 1))) {
|
|
log_error("dm_pool_grow_object failed");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
|
|
log_error("dm_pool_grow_object failed");
|
|
return 0;
|
|
}
|
|
|
|
field->report_string = dm_pool_end_object(rh->mem);
|
|
field->sort_value = (const void *) field->report_string;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _modules_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
struct list *modules;
|
|
|
|
if (!(modules = str_list_create(rh->mem))) {
|
|
log_error("modules str_list allocation failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!list_lv_modules(rh->mem, lv, modules))
|
|
return_0;
|
|
|
|
return _tags_disp(rh, field, modules);
|
|
}
|
|
|
|
static int _vgfmt_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct volume_group *vg = (const struct volume_group *) data;
|
|
|
|
if (!vg->fid) {
|
|
field->report_string = "";
|
|
field->sort_value = (const void *) field->report_string;
|
|
return 1;
|
|
}
|
|
|
|
return _string_disp(rh, field, &vg->fid->fmt->name);
|
|
}
|
|
|
|
static int _pvfmt_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct physical_volume *pv =
|
|
(const struct physical_volume *) data;
|
|
|
|
if (!pv->fmt) {
|
|
field->report_string = "";
|
|
field->sort_value = (const void *) field->report_string;
|
|
return 1;
|
|
}
|
|
|
|
return _string_disp(rh, field, &pv->fmt->name);
|
|
}
|
|
|
|
static int _int_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const int value = *(const int *) data;
|
|
uint64_t *sortval;
|
|
char *repstr;
|
|
|
|
if (!(repstr = dm_pool_zalloc(rh->mem, 13))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!(sortval = dm_pool_alloc(rh->mem, sizeof(int64_t)))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (dm_snprintf(repstr, 12, "%d", value) < 0) {
|
|
log_error("int too big: %d", value);
|
|
return 0;
|
|
}
|
|
|
|
*sortval = (const uint64_t) value;
|
|
field->sort_value = sortval;
|
|
field->report_string = repstr;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _lvkmaj_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
struct lvinfo info;
|
|
uint64_t minusone = UINT64_C(-1);
|
|
|
|
if (lv_info(lv->vg->cmd, lv, &info, 0) && info.exists)
|
|
return _int_disp(rh, field, &info.major);
|
|
else
|
|
return _int_disp(rh, field, &minusone);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _lvkmin_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
struct lvinfo info;
|
|
uint64_t minusone = UINT64_C(-1);
|
|
|
|
if (lv_info(lv->vg->cmd, lv, &info, 0) && info.exists)
|
|
return _int_disp(rh, field, &info.minor);
|
|
else
|
|
return _int_disp(rh, field, &minusone);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _lvstatus_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
struct lvinfo info;
|
|
char *repstr;
|
|
float snap_percent;
|
|
|
|
if (!(repstr = dm_pool_zalloc(rh->mem, 7))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (lv->status & PVMOVE)
|
|
repstr[0] = 'p';
|
|
else if (lv->status & MIRRORED) {
|
|
if (lv->status & MIRROR_NOTSYNCED)
|
|
repstr[0] = 'M';
|
|
else
|
|
repstr[0] = 'm';
|
|
}else if (lv->status & MIRROR_IMAGE)
|
|
repstr[0] = 'i';
|
|
else if (lv->status & MIRROR_LOG)
|
|
repstr[0] = 'l';
|
|
else if (lv->status & VIRTUAL)
|
|
repstr[0] = 'v';
|
|
else if (lv_is_origin(lv))
|
|
repstr[0] = 'o';
|
|
else if (lv_is_cow(lv))
|
|
repstr[0] = 's';
|
|
else
|
|
repstr[0] = '-';
|
|
|
|
if (lv->status & PVMOVE)
|
|
repstr[1] = '-';
|
|
else if (lv->status & LVM_WRITE)
|
|
repstr[1] = 'w';
|
|
else
|
|
repstr[1] = 'r';
|
|
|
|
repstr[2] = _alloc_policy_char(lv->alloc);
|
|
|
|
if (lv->status & LOCKED)
|
|
repstr[2] = toupper(repstr[2]);
|
|
|
|
if (lv->status & FIXED_MINOR)
|
|
repstr[3] = 'm'; /* Fixed Minor */
|
|
else
|
|
repstr[3] = '-';
|
|
|
|
if (lv_info(lv->vg->cmd, lv, &info, 1) && info.exists) {
|
|
if (info.suspended)
|
|
repstr[4] = 's'; /* Suspended */
|
|
else if (info.live_table)
|
|
repstr[4] = 'a'; /* Active */
|
|
else if (info.inactive_table)
|
|
repstr[4] = 'i'; /* Inactive with table */
|
|
else
|
|
repstr[4] = 'd'; /* Inactive without table */
|
|
|
|
/* Snapshot dropped? */
|
|
if (info.live_table && lv_is_cow(lv) &&
|
|
(!lv_snapshot_percent(lv, &snap_percent) ||
|
|
snap_percent < 0 || snap_percent >= 100)) {
|
|
repstr[0] = toupper(repstr[0]);
|
|
if (info.suspended)
|
|
repstr[4] = 'S'; /* Susp Inv snapshot */
|
|
else
|
|
repstr[4] = 'I'; /* Invalid snapshot */
|
|
}
|
|
|
|
if (info.open_count)
|
|
repstr[5] = 'o'; /* Open */
|
|
else
|
|
repstr[5] = '-';
|
|
} else {
|
|
repstr[4] = '-';
|
|
repstr[5] = '-';
|
|
}
|
|
|
|
field->report_string = repstr;
|
|
field->sort_value = (const void *) field->report_string;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _pvstatus_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const uint32_t status = *(const uint32_t *) data;
|
|
char *repstr;
|
|
|
|
if (!(repstr = dm_pool_zalloc(rh->mem, 4))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (status & ALLOCATABLE_PV)
|
|
repstr[0] = 'a';
|
|
else
|
|
repstr[0] = '-';
|
|
|
|
if (status & EXPORTED_VG)
|
|
repstr[1] = 'x';
|
|
else
|
|
repstr[1] = '-';
|
|
|
|
field->report_string = repstr;
|
|
field->sort_value = (const void *) field->report_string;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _vgstatus_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct volume_group *vg = (const struct volume_group *) data;
|
|
char *repstr;
|
|
|
|
if (!(repstr = dm_pool_zalloc(rh->mem, 7))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (vg->status & LVM_WRITE)
|
|
repstr[0] = 'w';
|
|
else
|
|
repstr[0] = 'r';
|
|
|
|
if (vg->status & RESIZEABLE_VG)
|
|
repstr[1] = 'z';
|
|
else
|
|
repstr[1] = '-';
|
|
|
|
if (vg->status & EXPORTED_VG)
|
|
repstr[2] = 'x';
|
|
else
|
|
repstr[2] = '-';
|
|
|
|
if (vg->status & PARTIAL_VG)
|
|
repstr[3] = 'p';
|
|
else
|
|
repstr[3] = '-';
|
|
|
|
repstr[4] = _alloc_policy_char(vg->alloc);
|
|
|
|
if (vg->status & CLUSTERED)
|
|
repstr[5] = 'c';
|
|
else
|
|
repstr[5] = '-';
|
|
|
|
field->report_string = repstr;
|
|
field->sort_value = (const void *) field->report_string;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _segtype_disp(struct report_handle *rh __attribute((unused)),
|
|
struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
|
|
if (seg->area_count == 1)
|
|
field->report_string = "linear";
|
|
else
|
|
field->report_string = seg->segtype->ops->name(seg);
|
|
field->sort_value = (const void *) field->report_string;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _origin_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
|
|
if (lv_is_cow(lv))
|
|
return _string_disp(rh, field, &origin_from_cow(lv)->name);
|
|
|
|
field->report_string = "";
|
|
field->sort_value = (const void *) field->report_string;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _loglv_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
struct lv_segment *seg;
|
|
|
|
list_iterate_items(seg, &lv->segments) {
|
|
if (!seg_is_mirrored(seg) || !seg->log_lv)
|
|
continue;
|
|
return _string_disp(rh, field, &seg->log_lv->name);
|
|
}
|
|
|
|
field->report_string = "";
|
|
field->sort_value = (const void *) field->report_string;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _lvname_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
char *repstr;
|
|
size_t len;
|
|
|
|
if (lv_is_visible(lv)) {
|
|
repstr = lv->name;
|
|
return _string_disp(rh, field, &repstr);
|
|
}
|
|
|
|
len = strlen(lv->name) + 3;
|
|
if (!(repstr = dm_pool_zalloc(rh->mem, len))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (dm_snprintf(repstr, len, "[%s]", lv->name) < 0) {
|
|
log_error("lvname snprintf failed");
|
|
return 0;
|
|
}
|
|
|
|
field->report_string = repstr;
|
|
|
|
if (!(field->sort_value = dm_pool_strdup(rh->mem, lv->name))) {
|
|
log_error("dm_pool_strdup failed");
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _movepv_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
const char *name;
|
|
struct lv_segment *seg;
|
|
|
|
list_iterate_items(seg, &lv->segments) {
|
|
if (!(seg->status & PVMOVE))
|
|
continue;
|
|
name = dev_name(seg_dev(seg, 0));
|
|
return _string_disp(rh, field, &name);
|
|
}
|
|
|
|
field->report_string = "";
|
|
field->sort_value = (const void *) field->report_string;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _size32_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const uint32_t size = *(const uint32_t *) data;
|
|
const char *disp;
|
|
uint64_t *sortval;
|
|
|
|
if (!*(disp = display_size_units(rh->cmd, (uint64_t) size))) {
|
|
stack;
|
|
return 0;
|
|
}
|
|
|
|
if (!(field->report_string = dm_pool_strdup(rh->mem, disp))) {
|
|
log_error("dm_pool_strdup failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!(sortval = dm_pool_alloc(rh->mem, sizeof(uint64_t)))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
*sortval = (const uint64_t) size;
|
|
field->sort_value = (const void *) sortval;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _size64_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const uint64_t size = *(const uint64_t *) data;
|
|
const char *disp;
|
|
uint64_t *sortval;
|
|
|
|
if (!*(disp = display_size_units(rh->cmd, size))) {
|
|
stack;
|
|
return 0;
|
|
}
|
|
|
|
if (!(field->report_string = dm_pool_strdup(rh->mem, disp))) {
|
|
log_error("dm_pool_strdup failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!(sortval = dm_pool_alloc(rh->mem, sizeof(uint64_t)))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
*sortval = size;
|
|
field->sort_value = sortval;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _vgsize_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct volume_group *vg = (const struct volume_group *) data;
|
|
uint64_t size;
|
|
|
|
size = (uint64_t) vg->extent_count * vg->extent_size;
|
|
|
|
return _size64_disp(rh, field, &size);
|
|
}
|
|
|
|
static int _segstart_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
uint64_t start;
|
|
|
|
start = (uint64_t) seg->le * seg->lv->vg->extent_size;
|
|
|
|
return _size64_disp(rh, field, &start);
|
|
}
|
|
|
|
static int _segsize_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
uint64_t size;
|
|
|
|
size = (uint64_t) seg->len * seg->lv->vg->extent_size;
|
|
|
|
return _size64_disp(rh, field, &size);
|
|
}
|
|
|
|
static int _chunksize_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct lv_segment *seg = (const struct lv_segment *) data;
|
|
uint64_t size;
|
|
|
|
if (lv_is_cow(seg->lv))
|
|
size = (uint64_t) find_cow(seg->lv)->chunk_size;
|
|
else
|
|
size = 0;
|
|
|
|
return _size64_disp(rh, field, &size);
|
|
}
|
|
|
|
static int _pvused_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct physical_volume *pv =
|
|
(const struct physical_volume *) data;
|
|
uint64_t used;
|
|
|
|
if (!pv->pe_count)
|
|
used = 0LL;
|
|
else
|
|
used = (uint64_t) pv->pe_alloc_count * pv->pe_size;
|
|
|
|
return _size64_disp(rh, field, &used);
|
|
}
|
|
|
|
static int _pvfree_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct physical_volume *pv =
|
|
(const struct physical_volume *) data;
|
|
uint64_t freespace;
|
|
|
|
if (!pv->pe_count)
|
|
freespace = pv->size;
|
|
else
|
|
freespace = (uint64_t) (pv->pe_count - pv->pe_alloc_count) * pv->pe_size;
|
|
|
|
return _size64_disp(rh, field, &freespace);
|
|
}
|
|
|
|
static int _pvsize_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct physical_volume *pv =
|
|
(const struct physical_volume *) data;
|
|
uint64_t size;
|
|
|
|
if (!pv->pe_count)
|
|
size = pv->size;
|
|
else
|
|
size = (uint64_t) pv->pe_count * pv->pe_size;
|
|
|
|
return _size64_disp(rh, field, &size);
|
|
}
|
|
|
|
static int _devsize_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct device *dev = *(const struct device **) data;
|
|
uint64_t size;
|
|
|
|
if (!dev_get_size(dev, &size))
|
|
size = 0;
|
|
|
|
return _size64_disp(rh, field, &size);
|
|
}
|
|
|
|
static int _vgfree_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct volume_group *vg = (const struct volume_group *) data;
|
|
uint64_t freespace;
|
|
|
|
freespace = (uint64_t) vg->free_count * vg->extent_size;
|
|
|
|
return _size64_disp(rh, field, &freespace);
|
|
}
|
|
|
|
static int _uuid_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
char *repstr = NULL;
|
|
|
|
if (!(repstr = dm_pool_alloc(rh->mem, 40))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!id_write_format((const struct id *) data, repstr, 40)) {
|
|
stack;
|
|
return 0;
|
|
}
|
|
|
|
field->report_string = repstr;
|
|
field->sort_value = (const void *) field->report_string;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _uint32_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const uint32_t value = *(const uint32_t *) data;
|
|
uint64_t *sortval;
|
|
char *repstr;
|
|
|
|
if (!(repstr = dm_pool_zalloc(rh->mem, 12))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!(sortval = dm_pool_alloc(rh->mem, sizeof(uint64_t)))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (dm_snprintf(repstr, 11, "%u", value) < 0) {
|
|
log_error("uint32 too big: %u", value);
|
|
return 0;
|
|
}
|
|
|
|
*sortval = (const uint64_t) value;
|
|
field->sort_value = sortval;
|
|
field->report_string = repstr;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _int32_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const int32_t value = *(const int32_t *) data;
|
|
uint64_t *sortval;
|
|
char *repstr;
|
|
|
|
if (!(repstr = dm_pool_zalloc(rh->mem, 13))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!(sortval = dm_pool_alloc(rh->mem, sizeof(int64_t)))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (dm_snprintf(repstr, 12, "%d", value) < 0) {
|
|
log_error("int32 too big: %d", value);
|
|
return 0;
|
|
}
|
|
|
|
*sortval = (const uint64_t) value;
|
|
field->sort_value = sortval;
|
|
field->report_string = repstr;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _lvsegcount_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
uint32_t count;
|
|
|
|
count = list_size(&lv->segments);
|
|
|
|
return _uint32_disp(rh, field, &count);
|
|
}
|
|
|
|
static int _snpercent_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
const struct logical_volume *lv = (const struct logical_volume *) data;
|
|
struct lvinfo info;
|
|
float snap_percent;
|
|
uint64_t *sortval;
|
|
char *repstr;
|
|
|
|
if (!(sortval = dm_pool_alloc(rh->mem, sizeof(uint64_t)))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (!lv_is_cow(lv) ||
|
|
(lv_info(lv->vg->cmd, lv, &info, 0) && !info.exists)) {
|
|
field->report_string = "";
|
|
*sortval = UINT64_C(0);
|
|
field->sort_value = sortval;
|
|
return 1;
|
|
}
|
|
|
|
if (!lv_snapshot_percent(lv, &snap_percent) || snap_percent < 0) {
|
|
field->report_string = "100.00";
|
|
*sortval = UINT64_C(100);
|
|
field->sort_value = sortval;
|
|
return 1;
|
|
}
|
|
|
|
if (!(repstr = dm_pool_zalloc(rh->mem, 8))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (dm_snprintf(repstr, 7, "%.2f", snap_percent) < 0) {
|
|
log_error("snapshot percentage too large");
|
|
return 0;
|
|
}
|
|
|
|
*sortval = snap_percent * UINT64_C(1000);
|
|
field->sort_value = sortval;
|
|
field->report_string = repstr;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _copypercent_disp(struct report_handle *rh, struct field *field,
|
|
const void *data)
|
|
{
|
|
struct logical_volume *lv = (struct logical_volume *) data;
|
|
float percent;
|
|
uint64_t *sortval;
|
|
char *repstr;
|
|
|
|
if (!(sortval = dm_pool_alloc(rh->mem, sizeof(uint64_t)))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if ((!(lv->status & PVMOVE) && !(lv->status & MIRRORED)) ||
|
|
!lv_mirror_percent(lv->vg->cmd, lv, 0, &percent, NULL)) {
|
|
field->report_string = "";
|
|
*sortval = UINT64_C(0);
|
|
field->sort_value = sortval;
|
|
return 1;
|
|
}
|
|
|
|
percent = copy_percent(lv);
|
|
|
|
if (!(repstr = dm_pool_zalloc(rh->mem, 8))) {
|
|
log_error("dm_pool_alloc failed");
|
|
return 0;
|
|
}
|
|
|
|
if (dm_snprintf(repstr, 7, "%.2f", percent) < 0) {
|
|
log_error("copy percentage too large");
|
|
return 0;
|
|
}
|
|
|
|
*sortval = percent * UINT64_C(1000);
|
|
field->sort_value = sortval;
|
|
field->report_string = repstr;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Import column definitions
|
|
*/
|
|
|
|
#define STR (FLD_STRING | FLD_ALIGN_LEFT)
|
|
#define NUM (FLD_NUMBER | FLD_ALIGN_RIGHT)
|
|
#define FIELD(type, strct, sorttype, head, field, width, func, id) {type, id, (off_t)((void *)&_dummy._ ## strct.field - (void *)&_dummy._ ## strct), head, width, sorttype, &_ ## func ## _disp},
|
|
|
|
static struct {
|
|
report_type_t type;
|
|
const char id[32];
|
|
off_t offset;
|
|
const char heading[32];
|
|
int width;
|
|
uint32_t flags;
|
|
field_report_fn report_fn;
|
|
} _fields[] = {
|
|
#include "columns.h"
|
|
};
|
|
|
|
#undef STR
|
|
#undef NUM
|
|
#undef FIELD
|
|
|
|
const unsigned int _num_fields = sizeof(_fields) / sizeof(_fields[0]);
|
|
|
|
static void _display_fields(void)
|
|
{
|
|
uint32_t f;
|
|
const char *type, *last_type = "";
|
|
|
|
for (f = 0; f < _num_fields; f++) {
|
|
switch (_fields[f].type) {
|
|
case PVS:
|
|
type = "Physical Volume";
|
|
break;
|
|
case LVS:
|
|
type = "Logical Volume";
|
|
break;
|
|
case VGS:
|
|
type = "Volume Group";
|
|
break;
|
|
case SEGS:
|
|
type = "Logical Volume Segment";
|
|
break;
|
|
case PVSEGS:
|
|
type = "Physical Volume Segment";
|
|
break;
|
|
default:
|
|
type = " ";
|
|
}
|
|
|
|
if (type != last_type) {
|
|
if (*last_type)
|
|
log_print(" ");
|
|
log_print("%s Fields", type);
|
|
}
|
|
|
|
log_print("- %s", _fields[f].id);
|
|
|
|
last_type = type;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialise report handle
|
|
*/
|
|
static int _field_match(struct report_handle *rh, const char *field, size_t len)
|
|
{
|
|
uint32_t f, l;
|
|
struct field_properties *fp;
|
|
|
|
if (!len)
|
|
return 0;
|
|
|
|
for (f = 0; f < _num_fields; f++) {
|
|
if ((!strncasecmp(_fields[f].id, field, len) &&
|
|
strlen(_fields[f].id) == len) ||
|
|
(l = strlen(rh->field_prefix),
|
|
!strncasecmp(rh->field_prefix, _fields[f].id, l) &&
|
|
!strncasecmp(_fields[f].id + l, field, len) &&
|
|
strlen(_fields[f].id) == l + len)) {
|
|
rh->type |= _fields[f].type;
|
|
if (!(fp = dm_pool_zalloc(rh->mem, sizeof(*fp)))) {
|
|
log_error("struct field_properties allocation "
|
|
"failed");
|
|
return 0;
|
|
}
|
|
fp->field_num = f;
|
|
fp->width = _fields[f].width;
|
|
fp->flags = _fields[f].flags;
|
|
|
|
/* Suppress snapshot percentage if not using driver */
|
|
if (!activation()
|
|
&& !strncmp(field, "snap_percent", len))
|
|
fp->flags |= FLD_HIDDEN;
|
|
|
|
list_add(&rh->field_props, &fp->list);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _add_sort_key(struct report_handle *rh, uint32_t field_num,
|
|
uint32_t flags)
|
|
{
|
|
struct field_properties *fp, *found = NULL;
|
|
|
|
list_iterate_items(fp, &rh->field_props) {
|
|
if (fp->field_num == field_num) {
|
|
found = fp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
/* Add as a non-display field */
|
|
if (!(found = dm_pool_zalloc(rh->mem, sizeof(*found)))) {
|
|
log_error("struct field_properties allocation failed");
|
|
return 0;
|
|
}
|
|
|
|
rh->type |= _fields[field_num].type;
|
|
found->field_num = field_num;
|
|
found->width = _fields[field_num].width;
|
|
found->flags = _fields[field_num].flags | FLD_HIDDEN;
|
|
|
|
list_add(&rh->field_props, &found->list);
|
|
}
|
|
|
|
if (found->flags & FLD_SORT_KEY) {
|
|
log_error("Ignoring duplicate sort field: %s",
|
|
_fields[field_num].id);
|
|
return 1;
|
|
}
|
|
|
|
found->flags |= FLD_SORT_KEY;
|
|
found->sort_posn = rh->keys_count++;
|
|
found->flags |= flags;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _key_match(struct report_handle *rh, const char *key, size_t len)
|
|
{
|
|
uint32_t f, l;
|
|
uint32_t flags = 0;
|
|
|
|
if (!len)
|
|
return 0;
|
|
|
|
if (*key == '+') {
|
|
key++;
|
|
len--;
|
|
flags = FLD_ASCENDING;
|
|
} else if (*key == '-') {
|
|
key++;
|
|
len--;
|
|
flags = FLD_DESCENDING;
|
|
} else
|
|
flags = FLD_ASCENDING;
|
|
|
|
if (!len) {
|
|
log_error("Missing sort field name");
|
|
return 0;
|
|
}
|
|
|
|
for (f = 0; f < _num_fields; f++) {
|
|
if ((!strncasecmp(_fields[f].id, key, len) &&
|
|
strlen(_fields[f].id) == len) ||
|
|
(l = strlen(rh->field_prefix),
|
|
!strncasecmp(rh->field_prefix, _fields[f].id, l) &&
|
|
!strncasecmp(_fields[f].id + l, key, len) &&
|
|
strlen(_fields[f].id) == l + len)) {
|
|
return _add_sort_key(rh, f, flags);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _parse_options(struct report_handle *rh, const char *format)
|
|
{
|
|
const char *ws; /* Word start */
|
|
const char *we = format; /* Word end */
|
|
|
|
while (*we) {
|
|
/* Allow consecutive commas */
|
|
while (*we && *we == ',')
|
|
we++;
|
|
ws = we;
|
|
while (*we && *we != ',')
|
|
we++;
|
|
if (!_field_match(rh, ws, (size_t) (we - ws))) {
|
|
_display_fields();
|
|
log_print(" ");
|
|
log_error("Unrecognised field: %.*s", (int) (we - ws),
|
|
ws);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _parse_keys(struct report_handle *rh, const char *keys)
|
|
{
|
|
const char *ws; /* Word start */
|
|
const char *we = keys; /* Word end */
|
|
|
|
while (*we) {
|
|
/* Allow consecutive commas */
|
|
while (*we && *we == ',')
|
|
we++;
|
|
ws = we;
|
|
while (*we && *we != ',')
|
|
we++;
|
|
if (!_key_match(rh, ws, (size_t) (we - ws))) {
|
|
log_error("Unrecognised field: %.*s", (int) (we - ws),
|
|
ws);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void *report_init(struct cmd_context *cmd, const char *format, const char *keys,
|
|
report_type_t *report_type, const char *separator,
|
|
int aligned, int buffered, int headings)
|
|
{
|
|
struct report_handle *rh;
|
|
|
|
if (!(rh = dm_pool_zalloc(cmd->mem, sizeof(*rh)))) {
|
|
log_error("report_handle dm_pool_zalloc failed");
|
|
return 0;
|
|
}
|
|
|
|
rh->cmd = cmd;
|
|
rh->type = *report_type;
|
|
rh->separator = separator;
|
|
|
|
if (aligned)
|
|
rh->flags |= RH_ALIGNED;
|
|
|
|
if (buffered)
|
|
rh->flags |= RH_BUFFERED | RH_SORT_REQUIRED;
|
|
|
|
if (headings)
|
|
rh->flags |= RH_HEADINGS;
|
|
|
|
list_init(&rh->field_props);
|
|
list_init(&rh->rows);
|
|
|
|
switch (rh->type) {
|
|
case PVS:
|
|
rh->field_prefix = "pv_";
|
|
break;
|
|
case LVS:
|
|
rh->field_prefix = "lv_";
|
|
break;
|
|
case VGS:
|
|
rh->field_prefix = "vg_";
|
|
break;
|
|
case SEGS:
|
|
rh->field_prefix = "seg_";
|
|
break;
|
|
case PVSEGS:
|
|
rh->field_prefix = "pvseg_";
|
|
break;
|
|
default:
|
|
rh->field_prefix = "";
|
|
}
|
|
|
|
if (!(rh->mem = dm_pool_create("report", 10 * 1024))) {
|
|
log_error("Allocation of memory pool for report failed");
|
|
return NULL;
|
|
}
|
|
|
|
/* Generate list of fields for output based on format string & flags */
|
|
if (!_parse_options(rh, format))
|
|
return NULL;
|
|
|
|
if (!_parse_keys(rh, keys))
|
|
return NULL;
|
|
|
|
/* Ensure options selected are compatible */
|
|
if (rh->type & SEGS)
|
|
rh->type |= LVS;
|
|
if (rh->type & PVSEGS)
|
|
rh->type |= PVS;
|
|
if ((rh->type & LVS) && (rh->type & PVS)) {
|
|
log_error("Can't report LV and PV fields at the same time");
|
|
return NULL;
|
|
}
|
|
|
|
/* Change report type if fields specified makes this necessary */
|
|
if (rh->type & SEGS)
|
|
*report_type = SEGS;
|
|
else if (rh->type & LVS)
|
|
*report_type = LVS;
|
|
else if (rh->type & PVSEGS)
|
|
*report_type = PVSEGS;
|
|
else if (rh->type & PVS)
|
|
*report_type = PVS;
|
|
|
|
return rh;
|
|
}
|
|
|
|
void report_free(void *handle)
|
|
{
|
|
struct report_handle *rh = handle;
|
|
|
|
dm_pool_destroy(rh->mem);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Create a row of data for an object
|
|
*/
|
|
int report_object(void *handle, struct volume_group *vg,
|
|
struct logical_volume *lv, struct physical_volume *pv,
|
|
struct lv_segment *seg, struct pv_segment *pvseg)
|
|
{
|
|
struct report_handle *rh = handle;
|
|
struct field_properties *fp;
|
|
struct row *row;
|
|
struct field *field;
|
|
void *data = NULL;
|
|
int skip;
|
|
|
|
if (lv && pv) {
|
|
log_error("report_object: One of *lv and *pv must be NULL!");
|
|
return 0;
|
|
}
|
|
|
|
if (!(row = dm_pool_zalloc(rh->mem, sizeof(*row)))) {
|
|
log_error("struct row allocation failed");
|
|
return 0;
|
|
}
|
|
|
|
row->rh = rh;
|
|
|
|
if ((rh->flags & RH_SORT_REQUIRED) &&
|
|
!(row->sort_fields = dm_pool_zalloc(rh->mem, sizeof(struct field *) *
|
|
rh->keys_count))) {
|
|
log_error("row sort value structure allocation failed");
|
|
return 0;
|
|
}
|
|
|
|
list_init(&row->fields);
|
|
list_add(&rh->rows, &row->list);
|
|
|
|
/* For each field to be displayed, call its report_fn */
|
|
list_iterate_items(fp, &rh->field_props) {
|
|
skip = 0;
|
|
|
|
if (!(field = dm_pool_zalloc(rh->mem, sizeof(*field)))) {
|
|
log_error("struct field allocation failed");
|
|
return 0;
|
|
}
|
|
field->props = fp;
|
|
|
|
switch (_fields[fp->field_num].type) {
|
|
case LVS:
|
|
data = (void *) lv + _fields[fp->field_num].offset;
|
|
break;
|
|
case VGS:
|
|
if (!vg) {
|
|
skip = 1;
|
|
break;
|
|
}
|
|
data = (void *) vg + _fields[fp->field_num].offset;
|
|
break;
|
|
case PVS:
|
|
data = (void *) pv + _fields[fp->field_num].offset;
|
|
break;
|
|
case SEGS:
|
|
data = (void *) seg + _fields[fp->field_num].offset;
|
|
break;
|
|
case PVSEGS:
|
|
data = (void *) pvseg + _fields[fp->field_num].offset;
|
|
}
|
|
|
|
if (skip) {
|
|
field->report_string = "";
|
|
field->sort_value = (const void *) field->report_string;
|
|
} else if (!_fields[fp->field_num].report_fn(rh, field, data)) {
|
|
log_error("report function failed for field %s",
|
|
_fields[fp->field_num].id);
|
|
return 0;
|
|
}
|
|
|
|
if ((strlen(field->report_string) > field->props->width))
|
|
field->props->width = strlen(field->report_string);
|
|
|
|
if ((rh->flags & RH_SORT_REQUIRED) &&
|
|
(field->props->flags & FLD_SORT_KEY)) {
|
|
(*row->sort_fields)[field->props->sort_posn] = field;
|
|
}
|
|
list_add(&row->fields, &field->list);
|
|
}
|
|
|
|
if (!(rh->flags & RH_BUFFERED))
|
|
report_output(handle);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Print row of headings
|
|
*/
|
|
static int _report_headings(void *handle)
|
|
{
|
|
struct report_handle *rh = handle;
|
|
struct field_properties *fp;
|
|
const char *heading;
|
|
char buf[1024];
|
|
|
|
if (rh->flags & RH_HEADINGS_PRINTED)
|
|
return 1;
|
|
|
|
rh->flags |= RH_HEADINGS_PRINTED;
|
|
|
|
if (!(rh->flags & RH_HEADINGS))
|
|
return 1;
|
|
|
|
if (!dm_pool_begin_object(rh->mem, 128)) {
|
|
log_error("dm_pool_begin_object failed for headings");
|
|
return 0;
|
|
}
|
|
|
|
/* First heading line */
|
|
list_iterate_items(fp, &rh->field_props) {
|
|
if (fp->flags & FLD_HIDDEN)
|
|
continue;
|
|
|
|
heading = _fields[fp->field_num].heading;
|
|
if (rh->flags & RH_ALIGNED) {
|
|
if (dm_snprintf(buf, sizeof(buf), "%-*.*s",
|
|
fp->width, fp->width, heading) < 0) {
|
|
log_error("snprintf heading failed");
|
|
dm_pool_end_object(rh->mem);
|
|
return 0;
|
|
}
|
|
if (!dm_pool_grow_object(rh->mem, buf, fp->width))
|
|
goto bad;
|
|
} else if (!dm_pool_grow_object(rh->mem, heading, strlen(heading)))
|
|
goto bad;
|
|
|
|
if (!list_end(&rh->field_props, &fp->list))
|
|
if (!dm_pool_grow_object(rh->mem, rh->separator,
|
|
strlen(rh->separator)))
|
|
goto bad;
|
|
}
|
|
if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
|
|
log_error("dm_pool_grow_object failed");
|
|
goto bad;
|
|
}
|
|
log_print("%s", (char *) dm_pool_end_object(rh->mem));
|
|
|
|
return 1;
|
|
|
|
bad:
|
|
log_error("Failed to generate report headings for printing");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Sort rows of data
|
|
*/
|
|
static int _row_compare(const void *a, const void *b)
|
|
{
|
|
const struct row *rowa = *(const struct row **) a;
|
|
const struct row *rowb = *(const struct row **) b;
|
|
const struct field *sfa, *sfb;
|
|
int32_t cnt = -1;
|
|
|
|
for (cnt = 0; cnt < rowa->rh->keys_count; cnt++) {
|
|
sfa = (*rowa->sort_fields)[cnt];
|
|
sfb = (*rowb->sort_fields)[cnt];
|
|
if (sfa->props->flags & FLD_NUMBER) {
|
|
const uint64_t numa =
|
|
*(const uint64_t *) sfa->sort_value;
|
|
const uint64_t numb =
|
|
*(const uint64_t *) sfb->sort_value;
|
|
|
|
if (numa == numb)
|
|
continue;
|
|
|
|
if (sfa->props->flags & FLD_ASCENDING) {
|
|
return (numa > numb) ? 1 : -1;
|
|
} else { /* FLD_DESCENDING */
|
|
return (numa < numb) ? 1 : -1;
|
|
}
|
|
} else { /* FLD_STRING */
|
|
const char *stra = (const char *) sfa->sort_value;
|
|
const char *strb = (const char *) sfb->sort_value;
|
|
int cmp = strcmp(stra, strb);
|
|
|
|
if (!cmp)
|
|
continue;
|
|
|
|
if (sfa->props->flags & FLD_ASCENDING) {
|
|
return (cmp > 0) ? 1 : -1;
|
|
} else { /* FLD_DESCENDING */
|
|
return (cmp < 0) ? 1 : -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0; /* Identical */
|
|
}
|
|
|
|
static int _sort_rows(struct report_handle *rh)
|
|
{
|
|
struct row *(*rows)[];
|
|
uint32_t count = 0;
|
|
struct row *row;
|
|
|
|
if (!(rows = dm_pool_alloc(rh->mem, sizeof(**rows) *
|
|
list_size(&rh->rows)))) {
|
|
log_error("sort array allocation failed");
|
|
return 0;
|
|
}
|
|
|
|
list_iterate_items(row, &rh->rows)
|
|
(*rows)[count++] = row;
|
|
|
|
qsort(rows, count, sizeof(**rows), _row_compare);
|
|
|
|
list_init(&rh->rows);
|
|
while (count--)
|
|
list_add_h(&rh->rows, &(*rows)[count]->list);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Produce report output
|
|
*/
|
|
int report_output(void *handle)
|
|
{
|
|
struct report_handle *rh = handle;
|
|
struct list *fh, *rowh, *ftmp, *rtmp;
|
|
struct row *row = NULL;
|
|
struct field *field;
|
|
const char *repstr;
|
|
char buf[4096];
|
|
int width;
|
|
|
|
if (list_empty(&rh->rows))
|
|
return 1;
|
|
|
|
/* Sort rows */
|
|
if ((rh->flags & RH_SORT_REQUIRED))
|
|
_sort_rows(rh);
|
|
|
|
/* If headings not printed yet, calculate field widths and print them */
|
|
if (!(rh->flags & RH_HEADINGS_PRINTED))
|
|
_report_headings(rh);
|
|
|
|
/* Print and clear buffer */
|
|
list_iterate_safe(rowh, rtmp, &rh->rows) {
|
|
if (!dm_pool_begin_object(rh->mem, 512)) {
|
|
log_error("dm_pool_begin_object failed for row");
|
|
return 0;
|
|
}
|
|
row = list_item(rowh, struct row);
|
|
list_iterate_safe(fh, ftmp, &row->fields) {
|
|
field = list_item(fh, struct field);
|
|
if (field->props->flags & FLD_HIDDEN)
|
|
continue;
|
|
|
|
repstr = field->report_string;
|
|
width = field->props->width;
|
|
if (!(rh->flags & RH_ALIGNED)) {
|
|
if (!dm_pool_grow_object(rh->mem, repstr,
|
|
strlen(repstr)))
|
|
goto bad;
|
|
} else if (field->props->flags & FLD_ALIGN_LEFT) {
|
|
if (dm_snprintf(buf, sizeof(buf), "%-*.*s",
|
|
width, width, repstr) < 0) {
|
|
log_error("snprintf repstr failed");
|
|
dm_pool_end_object(rh->mem);
|
|
return 0;
|
|
}
|
|
if (!dm_pool_grow_object(rh->mem, buf, width))
|
|
goto bad;
|
|
} else if (field->props->flags & FLD_ALIGN_RIGHT) {
|
|
if (dm_snprintf(buf, sizeof(buf), "%*.*s",
|
|
width, width, repstr) < 0) {
|
|
log_error("snprintf repstr failed");
|
|
dm_pool_end_object(rh->mem);
|
|
return 0;
|
|
}
|
|
if (!dm_pool_grow_object(rh->mem, buf, width))
|
|
goto bad;
|
|
}
|
|
|
|
if (!list_end(&row->fields, fh))
|
|
if (!dm_pool_grow_object(rh->mem, rh->separator,
|
|
strlen(rh->separator)))
|
|
goto bad;
|
|
list_del(&field->list);
|
|
}
|
|
if (!dm_pool_grow_object(rh->mem, "\0", 1)) {
|
|
log_error("dm_pool_grow_object failed for row");
|
|
return 0;
|
|
}
|
|
log_print("%s", (char *) dm_pool_end_object(rh->mem));
|
|
list_del(&row->list);
|
|
}
|
|
|
|
if (row)
|
|
dm_pool_free(rh->mem, row);
|
|
|
|
return 1;
|
|
|
|
bad:
|
|
log_error("Failed to generate row for printing");
|
|
return 0;
|
|
}
|