From 4c64ed4ced7dcd3a98a63da1cd3a6f2f1f2d1701 Mon Sep 17 00:00:00 2001 From: Alasdair Kergon Date: Thu, 12 Dec 2002 20:55:49 +0000 Subject: [PATCH] New column-based reporting tools: lvs, pvs & vgs. --- VERSION | 2 +- include/.symlinks | 1 + lib/Makefile.in | 1 + lib/commands/toolcontext.c | 15 + lib/commands/toolcontext.h | 3 + lib/config/config.c | 2 +- lib/config/config.h | 4 +- lib/config/defaults.h | 22 + lib/datastruct/list.h | 25 +- lib/display/display.c | 214 +++++--- lib/display/display.h | 9 +- lib/format1/format1.c | 27 +- lib/log/log.h | 7 +- lib/report/columns.h | 52 ++ lib/report/report.c | 1067 ++++++++++++++++++++++++++++++++++++ lib/report/report.h | 43 ++ libdm/datastruct/list.h | 25 +- man/Makefile.in | 6 +- man/lvs.8 | 64 +++ man/pvs.8 | 54 ++ man/vgs.8 | 58 ++ tools/Makefile.in | 5 +- tools/args.h | 9 + tools/commands.h | 174 +++++- tools/lvcreate.c | 12 +- tools/lvdisplay.c | 15 +- tools/lvm.c | 14 +- tools/lvmdiskscan.c | 21 +- tools/lvresize.c | 23 +- tools/lvscan.c | 9 +- tools/pvcreate.c | 4 +- tools/pvdisplay.c | 66 +-- tools/pvscan.c | 41 +- tools/report.c | 235 ++++++++ tools/toollib.c | 65 ++- tools/toollib.h | 13 +- tools/tools.h | 14 +- tools/vgck.c | 1 - tools/vgconvert.c | 2 +- tools/vgdisplay.c | 17 + 40 files changed, 2192 insertions(+), 249 deletions(-) create mode 100644 lib/report/columns.h create mode 100644 lib/report/report.c create mode 100644 lib/report/report.h create mode 100644 man/lvs.8 create mode 100644 man/pvs.8 create mode 100644 man/vgs.8 create mode 100644 tools/report.c diff --git a/VERSION b/VERSION index 146b0f6a3..91055e9a0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.95.11-cvs (2002-11-18) +1.95.12-cvs (2002-12-12) diff --git a/include/.symlinks b/include/.symlinks index 30c49c871..b8232bb99 100644 --- a/include/.symlinks +++ b/include/.symlinks @@ -32,4 +32,5 @@ ../lib/misc/lvm-string.h ../lib/misc/sharedlib.h ../lib/regex/matcher.h +../lib/report/report.h ../lib/uuid/uuid.h diff --git a/lib/Makefile.in b/lib/Makefile.in index d208a2e7d..323996064 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -56,6 +56,7 @@ SOURCES=\ regex/matcher.c \ regex/parse_rx.c \ regex/ttree.c \ + report/report.c \ uuid/uuid.c ifeq ("@LVM1@", "internal") diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c index c3fcbda29..2019720aa 100644 --- a/lib/commands/toolcontext.c +++ b/lib/commands/toolcontext.c @@ -20,6 +20,7 @@ #include "lvm-file.h" #include "format-text.h" #include "sharedlib.h" +#include "display.h" #ifdef LVM1_INTERNAL #include "format1.h" @@ -153,6 +154,20 @@ static int _process_config(struct cmd_context *cmd) DEFAULT_ACTIVATION); set_activation(cmd->default_settings.activation); + cmd->default_settings.suffix = find_config_int(cmd->cf->root, + "global/suffix", + '/', DEFAULT_SUFFIX); + + if (!(cmd->default_settings.unit_factor = + units_to_bytes(find_config_str(cmd->cf->root, + "global/units", + '/', + DEFAULT_UNITS), + &cmd->default_settings.unit_type))) { + log_error("Invalid units specification"); + return 0; + } + return 1; } diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h index 396cea7cb..f3ecddf35 100644 --- a/lib/commands/toolcontext.h +++ b/lib/commands/toolcontext.h @@ -25,6 +25,9 @@ struct config_info { int test; int syslog; int activation; + int suffix; + uint64_t unit_factor; + char unit_type; const char *msg_prefix; int cmd_name; /* Show command name? */ diff --git a/lib/config/config.c b/lib/config/config.c index d2105afae..3bd9765ed 100644 --- a/lib/config/config.c +++ b/lib/config/config.c @@ -715,7 +715,7 @@ struct config_node *find_config_node(struct config_node *cn, } const char *find_config_str(struct config_node *cn, - const char *path, char sep, const char *fail) + const char *path, const char sep, const char *fail) { struct config_node *n = find_config_node(cn, path, sep); diff --git a/lib/config/config.h b/lib/config/config.h index f1ceda0ad..d2c05fe43 100644 --- a/lib/config/config.h +++ b/lib/config/config.h @@ -51,10 +51,10 @@ int write_config_file(struct config_tree *cf, const char *file); int reload_config_file(struct config_tree **cf); struct config_node *find_config_node(struct config_node *cn, - const char *path, char seperator); + const char *path, char separator); const char *find_config_str(struct config_node *cn, - const char *path, char sep, const char *fail); + const char *path, const char sep, const char *fail); int find_config_int(struct config_node *cn, const char *path, char sep, int fail); diff --git a/lib/config/defaults.h b/lib/config/defaults.h index eeaf647a5..7a35aacc2 100644 --- a/lib/config/defaults.h +++ b/lib/config/defaults.h @@ -47,6 +47,8 @@ #define DEFAULT_VERBOSE 0 #define DEFAULT_LOGLEVEL 0 #define DEFAULT_INDENT 1 +#define DEFAULT_UNITS "h" +#define DEFAULT_SUFFIX 1 #define DEFAULT_ACTIVATION 1 @@ -54,4 +56,24 @@ #define DEFAULT_MAX_HISTORY 100 #endif +#define DEFAULT_REP_ALIGNED 1 +#define DEFAULT_REP_BUFFERED 1 +#define DEFAULT_REP_HEADINGS 1 +#define DEFAULT_REP_SEPARATOR " " + +#define DEFAULT_LVS_COLS "lv_name,vg_name,lv_attr,lv_size,origin,snap_percent" +#define DEFAULT_VGS_COLS "vg_name,pv_count,lv_count,snap_count,vg_attr,vg_size,vg_free" +#define DEFAULT_PVS_COLS "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free" +#define DEFAULT_SEGS_COLS "lv_name,vg_name,lv_attr,stripes,segtype,seg_size" + +#define DEFAULT_LVS_COLS_VERB "lv_name,vg_name,seg_count,lv_attr,lv_size,lv_minor,origin,snap_percent,lv_uuid" +#define DEFAULT_VGS_COLS_VERB "vg_name,vg_attr,vg_extent_size,pv_count,lv_count,snap_count,vg_size,vg_free,vg_uuid" +#define DEFAULT_PVS_COLS_VERB "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pv_uuid" +#define DEFAULT_SEGS_COLS_VERB "lv_name,vg_name,lv_attr,seg_start,seg_size,stripes,segtype,stripesize,chunksize" + +#define DEFAULT_LVS_SORT "vg_name,lv_name" +#define DEFAULT_VGS_SORT "vg_name" +#define DEFAULT_PVS_SORT "pv_name" +#define DEFAULT_SEGS_SORT "vg_name,lv_name,seg_start" + #endif /* _LVM_DEFAULTS_H */ diff --git a/lib/datastruct/list.h b/lib/datastruct/list.h index 5f2e14c11..4aeb7b40e 100644 --- a/lib/datastruct/list.h +++ b/lib/datastruct/list.h @@ -13,11 +13,13 @@ struct list { struct list *n, *p; }; -static inline void list_init(struct list *head) { +static inline void list_init(struct list *head) +{ head->n = head->p = head; } -static inline void list_add(struct list *head, struct list *elem) { +static inline void list_add(struct list *head, struct list *elem) +{ assert(head->n); elem->n = head; @@ -27,7 +29,8 @@ static inline void list_add(struct list *head, struct list *elem) { head->p = elem; } -static inline void list_add_h(struct list *head, struct list *elem) { +static inline void list_add_h(struct list *head, struct list *elem) +{ assert(head->n); elem->n = head->n; @@ -37,27 +40,35 @@ static inline void list_add_h(struct list *head, struct list *elem) { head->n = elem; } -static inline void list_del(struct list *elem) { +static inline void list_del(struct list *elem) +{ elem->n->p = elem->p; elem->p->n = elem->n; } -static inline int list_empty(struct list *head) { +static inline int list_empty(struct list *head) +{ return head->n == head; } +static inline int list_end(struct list *head, struct list *elem) +{ + return elem->n == head; +} + #define list_iterate(v, head) \ for (v = (head)->n; v != head; v = v->n) #define list_iterate_safe(v, t, head) \ for (v = (head)->n, t = v->n; v != head; v = t, t = v->n) -static inline int list_size(struct list *head) { +static inline int list_size(struct list *head) +{ int s = 0; struct list *v; list_iterate(v, head) - s++; + s++; return s; } diff --git a/lib/display/display.c b/lib/display/display.c index f96d15006..8574fb3d0 100644 --- a/lib/display/display.c +++ b/lib/display/display.c @@ -49,6 +49,72 @@ static struct { static int _num_policies = sizeof(_policies) / sizeof(*_policies); static int _num_segtypes = sizeof(_segtypes) / sizeof(*_segtypes); +uint64_t units_to_bytes(const char *units, char *unit_type) +{ + + char *ptr; + uint64_t v; + + ptr = (char *) units; + + if (isdigit(*units)) { + v = (uint64_t) strtod(units, &ptr); + if (ptr == units) + return 0; + } else + v = 1; + + if (v == 1) + *unit_type = *ptr; + else + *unit_type = 'U'; + + switch (*ptr) { + case 'h': + case 'H': + v = 1ULL; + *unit_type = *ptr; + break; + case 's': + v *= SECTOR_SIZE; + case 'b': + case 'B': + v *= 1ULL; + break; + case 'k': + v *= 1024ULL; + break; + case 'm': + v *= 1024ULL * 1024ULL; + break; + case 'g': + v *= 1024ULL * 1024ULL * 1024ULL; + break; + case 't': + v *= 1024ULL * 1024ULL * 1024ULL * 1024ULL; + break; + case 'K': + v *= 1000ULL; + break; + case 'M': + v *= 1000ULL * 1000ULL; + break; + case 'G': + v *= 1000ULL * 1000ULL * 1000ULL; + break; + case 'T': + v *= 1000ULL * 1000ULL * 1000ULL * 1000ULL; + break; + default: + return 0; + } + + if (*(ptr + 1)) + return 0; + + return v; +} + const char *get_alloc_string(alloc_policy_t alloc) { int i; @@ -68,7 +134,7 @@ const char *get_segtype_string(segment_type_t segtype) if (_segtypes[i].segtype == segtype) return _segtypes[i].str; - return NULL; + return "unknown"; } alloc_policy_t get_alloc_from_string(const char *str) @@ -95,36 +161,60 @@ segment_type_t get_segtype_from_string(const char *str) return SEG_STRIPED; } -char *display_size(uint64_t size, size_len_t sl) +const char *display_size(struct cmd_context *cmd, uint64_t size, size_len_t sl) { int s; - ulong byte = 1024 * 1024 * 1024; + int suffix = 1; + uint64_t byte; + uint64_t units = 1024ULL; char *size_buf = NULL; - char *size_str[][2] = { - {" Terabyte", "TB"}, - {" Gigabyte", "GB"}, - {" Megabyte", "MB"}, - {" Kilobyte", "KB"}, - {"", ""}, - {" ", " "} + char *size_str[][3] = { + {" Terabyte", " TB", "T"}, + {" Gigabyte", " GB", "G"}, + {" Megabyte", " MB", "M"}, + {" Kilobyte", " KB", "K"}, + {"", "", ""}, + {" Byte ", " B ", "B"}, + {" Units ", " Un", "U"}, + {" Sectors ", " Se", "S"}, + {" ", " ", " "}, }; - if (!(size_buf = dbg_malloc(SIZE_BUF))) { + if (!(size_buf = pool_alloc(cmd->mem, SIZE_BUF))) { log_error("no memory for size display buffer"); - return NULL; + return ""; } - if (size == 0LL) - sprintf(size_buf, "0%s", size_str[5][sl]); - else { + suffix = cmd->current_settings.suffix; + + for (s = 0; s < 8; s++) + if (toupper((int) cmd->current_settings.unit_type) == + *size_str[s][2]) + break; + + if (size == 0ULL) { + sprintf(size_buf, "0%s", suffix ? size_str[s][sl] : ""); + return size_buf; + } + + if (s < 8) { + byte = cmd->current_settings.unit_factor; + size *= 1024ULL; + } else { + suffix = 1; + if (cmd->current_settings.unit_type == 'H') + units = 1000ULL; + else + units = 1024ULL; + byte = units * units * units; s = 0; while (size_str[s] && size < byte) - s++, byte /= 1024; - snprintf(size_buf, SIZE_BUF - 1, - "%.2f%s", (float) size / byte, size_str[s][sl]); + s++, byte /= units; } - /* Caller to deallocate */ + snprintf(size_buf, SIZE_BUF - 1, "%.2f%s", (float) size / byte, + suffix ? size_str[s][sl] : ""); + return size_buf; } @@ -155,10 +245,11 @@ void pvdisplay_colons(struct physical_volume *pv) } /* FIXME Include label fields */ -void pvdisplay_full(struct physical_volume *pv, void *handle) +void pvdisplay_full(struct cmd_context *cmd, struct physical_volume *pv, + void *handle) { char uuid[64]; - char *size, *size1; /*, *size2; */ + const char *size; uint64_t pe_free; @@ -175,23 +266,21 @@ void pvdisplay_full(struct physical_volume *pv, void *handle) log_print("VG Name %s%s", pv->vg_name, pv->status & EXPORTED_VG ? " (exported)" : ""); - size = display_size((uint64_t) pv->size / 2, SIZE_SHORT); + size = display_size(cmd, (uint64_t) pv->size / 2, SIZE_SHORT); if (pv->pe_size && pv->pe_count) { - size1 = display_size((pv->size - pv->pe_count * pv->pe_size) - / 2, SIZE_SHORT); /******** FIXME display LVM on-disk data size size2 = display_size(pv->size / 2, SIZE_SHORT); ********/ log_print("PV Size %s" " / not usable %s", /* [LVM: %s]", */ - size, size1); /* , size2); */ + size, display_size(cmd, + (pv->size - + pv->pe_count * pv->pe_size) / 2, + SIZE_SHORT)); - dbg_free(size1); - /* dbg_free(size2); */ } else log_print("PV Size %s", size); - dbg_free(size); /* PV number not part of LVM2 design log_print("PV# %u", pv->pv_number); @@ -265,7 +354,6 @@ void lvdisplay_colons(struct logical_volume *lv) int lvdisplay_full(struct cmd_context *cmd, struct logical_volume *lv, void *handle) { - char *size; struct dm_info info; int inkernel, snap_active; char uuid[64]; @@ -326,10 +414,10 @@ int lvdisplay_full(struct cmd_context *cmd, struct logical_volume *lv, if (inkernel) log_print("# open %u", info.open_count); - size = display_size(snap ? snap->origin->size / 2 : lv->size / 2, - SIZE_SHORT); - log_print("LV Size %s", size); - dbg_free(size); + log_print("LV Size %s", + display_size(cmd, + snap ? snap->origin->size / 2 : lv->size / 2, + SIZE_SHORT)); log_print("Current LE %u", snap ? snap->origin->le_count : lv->le_count); @@ -348,9 +436,8 @@ int lvdisplay_full(struct cmd_context *cmd, struct logical_volume *lv, if (snap_percent == -1) snap_percent = 100; - size = display_size(snap->chunk_size / 2, SIZE_SHORT); - log_print("Snapshot chunk size %s", size); - dbg_free(size); + log_print("Snapshot chunk size %s", + display_size(cmd, snap->chunk_size / 2, SIZE_SHORT)); /* size = display_size(lv->size / 2, SIZE_SHORT); @@ -451,7 +538,6 @@ void vgdisplay_full(struct volume_group *vg) { uint32_t access; uint32_t active_pvs; - char *s1; char uuid[64]; if (vg->status & PARTIAL_VG) @@ -497,28 +583,33 @@ void vgdisplay_full(struct volume_group *vg) log_print("Cur PV %u", vg->pv_count); log_print("Act PV %u", active_pvs); - s1 = display_size((uint64_t) vg->extent_count * (vg->extent_size / 2), - SIZE_SHORT); - log_print("VG Size %s", s1); - dbg_free(s1); + log_print("VG Size %s", + display_size(vg->cmd, + (uint64_t) vg->extent_count * (vg->extent_size / + 2), SIZE_SHORT)); - s1 = display_size(vg->extent_size / 2, SIZE_SHORT); - log_print("PE Size %s", s1); - dbg_free(s1); + log_print("PE Size %s", + display_size(vg->cmd, vg->extent_size / 2, SIZE_SHORT)); log_print("Total PE %u", vg->extent_count); - s1 = display_size(((uint64_t) - vg->extent_count - vg->free_count) * - (vg->extent_size / 2), SIZE_SHORT); log_print("Alloc PE / Size %u / %s", - vg->extent_count - vg->free_count, s1); - dbg_free(s1); + vg->extent_count - vg->free_count, display_size(vg->cmd, + ((uint64_t) + vg-> + extent_count + - + vg-> + free_count) * + (vg-> + extent_size / + 2), + SIZE_SHORT)); - s1 = display_size((uint64_t) vg->free_count * (vg->extent_size / 2), - SIZE_SHORT); - log_print("Free PE / Size %u / %s", vg->free_count, s1); - dbg_free(s1); + log_print("Free PE / Size %u / %s", vg->free_count, + display_size(vg->cmd, + (uint64_t) vg->free_count * (vg->extent_size / + 2), SIZE_SHORT)); if (!id_write_format(&vg->id, uuid, sizeof(uuid))) { stack; @@ -538,16 +629,15 @@ void vgdisplay_colons(struct volume_group *vg) void vgdisplay_short(struct volume_group *vg) { - char *s1, *s2, *s3; - s1 = display_size(vg->extent_count * vg->extent_size / 2, SIZE_SHORT); - s2 = display_size((vg->extent_count - - vg->free_count) * vg->extent_size / 2, SIZE_SHORT); - s3 = display_size(vg->free_count * vg->extent_size / 2, SIZE_SHORT); log_print("\"%s\" %-9s [%-9s used / %s free]", vg->name, /********* FIXME if "open" print "/used" else print "/idle"??? ******/ - s1, s2, s3); - dbg_free(s1); - dbg_free(s2); - dbg_free(s3); + display_size(vg->cmd, vg->extent_count * vg->extent_size / 2, + SIZE_SHORT), display_size(vg->cmd, + (vg->extent_count - + vg->free_count) * + vg->extent_size / 2, + SIZE_SHORT), + display_size(vg->cmd, vg->free_count * vg->extent_size / 2, + SIZE_SHORT)); return; } diff --git a/lib/display/display.h b/lib/display/display.h index 3b59a5ee6..578843fb2 100644 --- a/lib/display/display.h +++ b/lib/display/display.h @@ -25,14 +25,17 @@ #include -typedef enum { SIZE_LONG = 0, SIZE_SHORT = 1 } size_len_t; +typedef enum { SIZE_LONG = 0, SIZE_SHORT = 1, SIZE_UNIT = 2 } size_len_t; + +uint64_t units_to_bytes(const char *units, char *unit_type); /* Specify size in KB */ -char *display_size(uint64_t size, size_len_t sl); +const char *display_size(struct cmd_context *cmd, uint64_t size, size_len_t sl); char *display_uuid(char *uuidstr); void pvdisplay_colons(struct physical_volume *pv); -void pvdisplay_full(struct physical_volume *pv, void *handle); +void pvdisplay_full(struct cmd_context *cmd, struct physical_volume *pv, + void *handle); int pvdisplay_short(struct cmd_context *cmd, struct volume_group *vg, struct physical_volume *pv, void *handle); diff --git a/lib/format1/format1.c b/lib/format1/format1.c index 7e7205397..e2953361f 100644 --- a/lib/format1/format1.c +++ b/lib/format1/format1.c @@ -297,14 +297,11 @@ static int _pv_setup(struct format_type *fmt, uint64_t pvmetadatasize, struct list *mdas, struct physical_volume *pv, struct volume_group *vg) { - char *sz; - if (pv->size > MAX_PV_SIZE) pv->size--; if (pv->size > MAX_PV_SIZE) { log_error("Physical volumes cannot be bigger than %s", - sz = display_size(MAX_PV_SIZE / 2, SIZE_SHORT)); - dbg_free(sz); + display_size(fmt->cmd, MAX_PV_SIZE / 2, SIZE_SHORT)); return 0; } @@ -365,9 +362,9 @@ static int _lv_setup(struct format_instance *fid, struct logical_volume *lv) return 0; } if (lv->size > max_size) { - char *dummy = display_size(max_size / 2, SIZE_SHORT); - log_error("logical volumes cannot be larger than %s", dummy); - dbg_free(dummy); + log_error("logical volumes cannot be larger than %s", + display_size(fid->fmt->cmd, max_size / 2, + SIZE_SHORT)); return 0; } @@ -447,22 +444,20 @@ int _vg_setup(struct format_instance *fid, struct volume_group *vg) vg->max_pv = MAX_PV - 1; if (vg->extent_size > MAX_PE_SIZE || vg->extent_size < MIN_PE_SIZE) { - char *dummy, *dummy2; - log_error("Extent size must be between %s and %s", - (dummy = display_size(MIN_PE_SIZE / 2, SIZE_SHORT)), - (dummy2 = display_size(MAX_PE_SIZE / 2, SIZE_SHORT))); + display_size(fid->fmt->cmd, MIN_PE_SIZE / 2, + SIZE_SHORT), display_size(fid->fmt->cmd, + MAX_PE_SIZE / + 2, + SIZE_SHORT)); - dbg_free(dummy); - dbg_free(dummy2); return 0; } if (vg->extent_size % MIN_PE_SIZE) { - char *dummy; log_error("Extent size must be multiple of %s", - (dummy = display_size(MIN_PE_SIZE / 2, SIZE_SHORT))); - dbg_free(dummy); + display_size(fid->fmt->cmd, MIN_PE_SIZE / 2, + SIZE_SHORT)); return 0; } diff --git a/lib/log/log.h b/lib/log/log.h index 6ed54016a..d46a2a861 100644 --- a/lib/log/log.h +++ b/lib/log/log.h @@ -28,8 +28,8 @@ * */ -#include /* FILE */ -#include /* strerror() */ +#include /* FILE */ +#include /* strerror() */ #include #define _LOG_DEBUG 7 @@ -65,7 +65,7 @@ int ignorelockingfailure(void); void log_suppress(int suppress); void print_log(int level, const char *file, int line, const char *format, ...) - __attribute__ (( format (printf, 4, 5) )); + __attribute__ ((format(printf, 4, 5))); #define plog(l, x...) print_log(l, __FILE__, __LINE__ , ## x) @@ -90,4 +90,3 @@ void print_log(int level, const char *file, int line, const char *format, ...) log_info("%s: %s failed: %s", y, x, strerror(errno)) #endif - diff --git a/lib/report/columns.h b/lib/report/columns.h new file mode 100644 index 000000000..b2eb3cad2 --- /dev/null +++ b/lib/report/columns.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2002 Sistina Software (UK) Limited. + * + * This file is released under the LGPL. + */ + +/* Report type, Containing struct, Field type, Report heading, + * Data field with struct to pass to display function, Minimum display width, + * Display Fn, Unique format identifier */ +FIELD(LVS, lv, STR, "LV UUID", lvid.id[1], 38, uuid, "lv_uuid") +FIELD(LVS, lv, STR, "LV", name, 4, string, "lv_name") +FIELD(LVS, lv, STR, "Attr", lvid, 4, lvstatus, "lv_attr") +FIELD(LVS, lv, NUM, "Min", minor, 3, int32, "lv_minor") +FIELD(LVS, lv, NUM, "LSize", size, 5, size64, "lv_size") +FIELD(LVS, lv, NUM, "#Seg", lvid, 4, lvsegcount, "seg_count") +FIELD(LVS, lv, STR, "Origin", lvid, 6, origin, "origin") +FIELD(LVS, lv, NUM, "Snap%", lvid, 6, snpercent, "snap_percent") + +FIELD(PVS, pv, STR, "Fmt", id, 3, pvfmt, "pv_fmt") +FIELD(PVS, pv, STR, "PV UUID", id, 38, uuid, "pv_uuid") +FIELD(PVS, pv, NUM, "PSize", id, 5, pvsize, "pv_size") +FIELD(PVS, pv, NUM, "PFree", id, 5, pvfree, "pv_free") +FIELD(PVS, pv, NUM, "Used", id, 4, pvused, "pv_used") +FIELD(PVS, pv, STR, "PV", dev, 10, dev_name, "pv_name") +FIELD(PVS, pv, STR, "Attr", status, 4, pvstatus, "pv_attr") +FIELD(PVS, pv, NUM, "PE", pe_count, 3, uint32, "pv_pe_count") +FIELD(PVS, pv, NUM, "Alloc", pe_alloc_count, 5, uint32, "pv_pe_alloc_count") + +FIELD(VGS, vg, STR, "Fmt", cmd, 3, vgfmt, "vg_fmt") +FIELD(VGS, vg, STR, "VG UUID", id, 38, uuid, "vg_uuid") +FIELD(VGS, vg, STR, "VG", name, 4, string, "vg_name") +FIELD(VGS, vg, STR, "Attr", status, 4, vgstatus, "vg_attr") +FIELD(VGS, vg, NUM, "VSize", cmd, 5, vgsize, "vg_size") +FIELD(VGS, vg, NUM, "VFree", cmd, 5, vgfree, "vg_free") +FIELD(VGS, vg, STR, "SYS ID", system_id, 6, string, "vg_sysid") +FIELD(VGS, vg, NUM, "Ext", extent_size, 3, size32, "vg_extent_size") +FIELD(VGS, vg, NUM, "#Ext", extent_count, 4, uint32, "vg_extent_count") +FIELD(VGS, vg, NUM, "Free", free_count, 4, uint32, "vg_free_count") +FIELD(VGS, vg, NUM, "MaxLV", max_lv, 5, uint32, "max_lv") +FIELD(VGS, vg, NUM, "MaxPV", max_pv, 5, uint32, "max_pv") +FIELD(VGS, vg, NUM, "#PV", pv_count, 3, uint32, "pv_count") +FIELD(VGS, vg, NUM, "#LV", lv_count, 3, uint32, "lv_count") +FIELD(VGS, vg, NUM, "#SN", snapshot_count, 3, uint32, "snap_count") +FIELD(VGS, vg, NUM, "Seq", seqno, 3, uint32, "vg_seqno") + +FIELD(SEGS, seg, STR, "Type", list, 4, segtype, "segtype") +FIELD(SEGS, seg, NUM, "#Str", stripes, 4, uint32, "stripes") +FIELD(SEGS, seg, NUM, "Stripe", stripe_size, 6, size32, "stripesize") +FIELD(SEGS, seg, NUM, "Chunk", chunk_size, 5, size32, "chunksize") +FIELD(SEGS, seg, NUM, "Start", list, 5, segstart, "seg_start") +FIELD(SEGS, seg, NUM, "SSize", list, 5, segsize, "seg_size") + diff --git a/lib/report/report.c b/lib/report/report.c new file mode 100644 index 000000000..f789380a2 --- /dev/null +++ b/lib/report/report.c @@ -0,0 +1,1067 @@ +/* + * Copyright (C) 2002 Sistina Software + * + * This LVM library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This LVM library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this LVM library; 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 "pool.h" +#include "lvm-string.h" +#include "display.h" +#include "activate.h" + +#include + +/* + * For macro use + */ +static union { + struct physical_volume _pv; + struct logical_volume _lv; + struct volume_group _vg; + struct lv_segment _seg; +} _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 pool *mem; + + report_type_t type; + 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; + + char *report_string; /* Formatted ready for display */ + 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 */ +}; + +/* + * 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 = pool_strdup(rh->mem, *(char **) data))) { + log_error("pool_strdup failed"); + return 0; + } + + field->sort_value = (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(*(struct device **) data); + + return _string_disp(rh, field, &name); +} + +static int _vgfmt_disp(struct report_handle *rh, struct field *field, + const void *data) +{ + struct volume_group *vg = (struct volume_group *) data; + + if (!vg->fid) { + field->report_string = ""; + field->sort_value = (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) +{ + struct physical_volume *pv = (struct physical_volume *) data; + + if (!pv->fmt) { + field->report_string = ""; + field->sort_value = (void *) field->report_string; + return 1; + } + + return _string_disp(rh, field, &pv->fmt->name); +} + +static int _lvstatus_disp(struct report_handle *rh, struct field *field, + const void *data) +{ + struct logical_volume *lv = (struct logical_volume *) data; + struct dm_info info; + + if (!(field->report_string = pool_zalloc(rh->mem, 7))) { + log_error("pool_alloc failed"); + return 0; + } + + if (lv_is_origin(lv)) + field->report_string[0] = 'o'; + else if (find_cow(lv)) + field->report_string[0] = 's'; + else + field->report_string[0] = '-'; + + if (lv->status & LVM_WRITE) + field->report_string[1] = 'w'; + else + field->report_string[1] = 'r'; + + if (lv->alloc == ALLOC_CONTIGUOUS) + field->report_string[2] = 'c'; + else + field->report_string[2] = 'n'; + + if (lv->status & FIXED_MINOR) + field->report_string[3] = 'm'; /* Fixed Minor */ + else + field->report_string[3] = '-'; + + if (lv_info(lv, &info) && info.exists) { + if (info.suspended) + field->report_string[4] = 's'; /* Suspended */ + else + field->report_string[4] = 'a'; /* Active */ + if (info.open_count) + field->report_string[5] = 'o'; /* Open */ + else + field->report_string[5] = '-'; + } else { + field->report_string[4] = '-'; + field->report_string[5] = '-'; + } + + field->sort_value = (void *) field->report_string; + + return 1; +} + +static int _pvstatus_disp(struct report_handle *rh, struct field *field, + const void *data) +{ + uint32_t status = *(uint32_t *) data; + + if (!(field->report_string = pool_zalloc(rh->mem, 4))) { + log_error("pool_alloc failed"); + return 0; + } + + if (status & ALLOCATABLE_PV) + field->report_string[0] = 'a'; + else + field->report_string[0] = '-'; + + if (status & EXPORTED_VG) + field->report_string[1] = 'x'; + else + field->report_string[1] = '-'; + + field->sort_value = (void *) field->report_string; + + return 1; +} + +static int _vgstatus_disp(struct report_handle *rh, struct field *field, + const void *data) +{ + uint32_t status = *(uint32_t *) data; + + if (!(field->report_string = pool_zalloc(rh->mem, 5))) { + log_error("pool_alloc failed"); + return 0; + } + + if (status & LVM_WRITE) + field->report_string[0] = 'w'; + else + field->report_string[0] = 'r'; + + if (status & RESIZEABLE_VG) + field->report_string[1] = 'z'; + else + field->report_string[1] = '-'; + + if (status & EXPORTED_VG) + field->report_string[2] = 'x'; + else + field->report_string[2] = '-'; + + if (status & PARTIAL_VG) + field->report_string[3] = 'p'; + else + field->report_string[3] = '-'; + + field->sort_value = (void *) field->report_string; + + return 1; +} + +static int _segtype_disp(struct report_handle *rh, struct field *field, + const void *data) +{ + struct lv_segment *seg = (struct lv_segment *) data; + + if (seg->stripes == 1) + field->report_string = "linear"; + else + field->report_string = (char *) get_segtype_string(seg->type); + field->sort_value = (void *) field->report_string; + + return 1; +} + +static int _origin_disp(struct report_handle *rh, struct field *field, + const void *data) +{ + struct logical_volume *lv = (struct logical_volume *) data; + struct snapshot *snap; + + if ((snap = find_cow(lv))) + return _string_disp(rh, field, &snap->origin->name); + + field->report_string = ""; + field->sort_value = (void *) field->report_string; + + return 1; +} + +static int _size32_disp(struct report_handle *rh, struct field *field, + const void *data) +{ + uint32_t size = *(uint32_t *) data; + const char *disp; + + if (!*(disp = display_size(rh->cmd, (uint64_t) size / 2, SIZE_UNIT))) { + stack; + return 0; + } + + if (!(field->report_string = pool_strdup(rh->mem, disp))) { + log_error("pool_strdup failed"); + return 0; + } + + if (!(field->sort_value = pool_alloc(rh->mem, sizeof(uint64_t)))) { + log_error("pool_alloc failed"); + return 0; + } + + *(uint64_t *) field->sort_value = (uint64_t) size; + + return 1; +} + +static int _size64_disp(struct report_handle *rh, struct field *field, + const void *data) +{ + uint64_t size = *(uint64_t *) data; + const char *disp; + + if (!*(disp = display_size(rh->cmd, size / 2, SIZE_UNIT))) { + stack; + return 0; + } + + if (!(field->report_string = pool_strdup(rh->mem, disp))) { + log_error("pool_strdup failed"); + return 0; + } + + if (!(field->sort_value = pool_alloc(rh->mem, sizeof(uint64_t)))) { + log_error("pool_alloc failed"); + return 0; + } + + *(uint64_t *) field->sort_value = size; + + return 1; +} + +static int _vgsize_disp(struct report_handle *rh, struct field *field, + const void *data) +{ + struct volume_group *vg = (struct volume_group *) data; + uint64_t size; + + size = 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) +{ + struct lv_segment *seg = (struct lv_segment *) data; + uint64_t start; + + start = 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) +{ + struct lv_segment *seg = (struct lv_segment *) data; + uint64_t size; + + size = seg->len * seg->lv->vg->extent_size; + + return _size64_disp(rh, field, &size); +} + +static int _pvused_disp(struct report_handle *rh, struct field *field, + const void *data) +{ + struct physical_volume *pv = (struct physical_volume *) data; + uint64_t used; + + if (!pv->pe_count) + used = 0LL; + else + used = 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) +{ + struct physical_volume *pv = (struct physical_volume *) data; + uint64_t free; + + if (!pv->pe_count) + free = pv->size; + else + free = (pv->pe_count - pv->pe_alloc_count) * pv->pe_size; + + return _size64_disp(rh, field, &free); +} + +static int _pvsize_disp(struct report_handle *rh, struct field *field, + const void *data) +{ + struct physical_volume *pv = (struct physical_volume *) data; + uint64_t size; + + if (!pv->pe_count) + size = pv->size; + else + size = pv->pe_count * pv->pe_size; + + return _size64_disp(rh, field, &size); +} + +static int _vgfree_disp(struct report_handle *rh, struct field *field, + const void *data) +{ + struct volume_group *vg = (struct volume_group *) data; + uint64_t free; + + free = vg->free_count * vg->extent_size; + + return _size64_disp(rh, field, &free); +} + +static int _uuid_disp(struct report_handle *rh, struct field *field, + const void *data) +{ + if (!(field->report_string = pool_alloc(rh->mem, 40))) { + log_error("pool_alloc failed"); + return 0; + } + + if (!id_write_format((struct id *) data, field->report_string, 40)) { + stack; + return 0; + } + + field->sort_value = (void *) field->report_string; + + return 1; +} + +static int _uint32_disp(struct report_handle *rh, struct field *field, + const void *data) +{ + uint32_t value = *(uint32_t *) data; + + if (!(field->report_string = pool_zalloc(rh->mem, 12))) { + log_error("pool_alloc failed"); + return 0; + } + + if (!(field->sort_value = pool_alloc(rh->mem, sizeof(uint64_t)))) { + log_error("pool_alloc failed"); + return 0; + } + + *(uint64_t *) field->sort_value = value; + + if (lvm_snprintf(field->report_string, 11, "%u", value) < 0) { + log_error("uint32 too big: %u", value); + return 0; + } + + return 1; +} + +static int _int32_disp(struct report_handle *rh, struct field *field, + const void *data) +{ + int32_t value = *(int32_t *) data; + + if (!(field->report_string = pool_zalloc(rh->mem, 13))) { + log_error("pool_alloc failed"); + return 0; + } + + if (!(field->sort_value = pool_alloc(rh->mem, sizeof(int64_t)))) { + log_error("pool_alloc failed"); + return 0; + } + + *(int64_t *) field->sort_value = value; + + if (lvm_snprintf(field->report_string, 12, "%d", value) < 0) { + log_error("int32 too big: %d", value); + return 0; + } + + return 1; +} + +static int _lvsegcount_disp(struct report_handle *rh, struct field *field, + const void *data) +{ + struct logical_volume *lv = (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) +{ + struct logical_volume *lv = (struct logical_volume *) data; + struct snapshot *snap; + float snap_percent; + + if (!(field->sort_value = pool_alloc(rh->mem, sizeof(uint64_t)))) { + log_error("pool_alloc failed"); + return 0; + } + + if (!(snap = find_cow(lv)) + || !lv_snapshot_percent(snap->cow, &snap_percent)) { + field->report_string = ""; + *(uint64_t *) field->sort_value = 0LL; + return 1; + } + + if (!(field->report_string = pool_zalloc(rh->mem, 8))) { + log_error("pool_alloc failed"); + return 0; + } + + if (snap_percent == -1) + snap_percent = 100; + + *(uint64_t *) field->sort_value = snap_percent * 1000; + + if (lvm_snprintf(field->report_string, 7, "%.2f", snap_percent) < 0) { + log_error("snapshot percentage too large"); + return 0; + } + + 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[30]; + off_t offset; + const char heading[30]; + int width; + uint32_t flags; + field_report_fn report_fn; +} _fields[] = { +#include "columns.h" +}; + +#undef STR +#undef NUM +#undef FIELD + +const int _num_fields = sizeof(_fields) / sizeof(_fields[0]); + +/* + * Initialise report handle + */ +static int _field_match(struct report_handle *rh, const char *field, int 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 = 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 list *fh; + struct field_properties *fp, *found = NULL; + + list_iterate(fh, &rh->field_props) { + fp = list_item(fh, struct field_properties); + + if (fp->field_num == field_num) { + found = fp; + break; + } + } + + if (!found) { + /* Add as a non-display field */ + if (!(fp = pool_zalloc(rh->mem, sizeof(*fp)))) { + log_error("struct field_properties allocation failed"); + return 0; + } + + rh->type |= _fields[field_num].type; + fp->field_num = field_num; + fp->width = _fields[field_num].width; + fp->flags = _fields[field_num].flags | FLD_HIDDEN; + + list_add(&rh->field_props, &fp->list); + } + + if (fp->flags & FLD_SORT_KEY) { + log_error("Ignoring duplicate sort field: %s", + _fields[field_num].id); + return 1; + } + + fp->flags |= FLD_SORT_KEY; + fp->sort_posn = rh->keys_count++; + fp->flags |= flags; + + return 1; +} + +static int _key_match(struct report_handle *rh, const char *key, int 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, we - ws)) { + log_error("Unrecognised field: %.*s", 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, we - ws)) { + log_error("Unrecognised field: %.*s", 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 = pool_zalloc(cmd->mem, sizeof(*rh)))) { + log_error("report_handle 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; + default: + rh->field_prefix = ""; + } + + if (!(rh->mem = pool_create(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 & 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 & PVS) + *report_type = PVS; + + return rh; +} + +void report_free(void *handle) +{ + struct report_handle *rh = handle; + + 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 report_handle *rh = handle; + struct list *fh; + struct field_properties *fp; + struct row *row; + struct field *field; + void *data; + + if (lv && pv) { + log_error("report_object: One of *lv and *pv must be NULL!"); + return 0; + } + + if (!(row = 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 = 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(fh, &rh->field_props) { + fp = list_item(fh, struct field_properties); + + if (!(field = 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: + 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; + } + + 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 + */ +int report_headings(void *handle) +{ + struct report_handle *rh = handle; + struct list *fh; + struct field_properties *fp; + + if (rh->flags & RH_HEADINGS_PRINTED) + return 1; + + if (!(rh->flags & RH_HEADINGS)) + goto out; + + /* First heading line */ + list_iterate(fh, &rh->field_props) { + fp = list_item(fh, struct field_properties); + if (fp->flags & FLD_HIDDEN) + continue; + if (rh->flags & RH_ALIGNED) + printf("%-*.*s", fp->width, fp->width, + _fields[fp->field_num].heading); + else + printf("%s", _fields[fp->field_num].heading); + if (!list_end(&rh->field_props, fh)) + printf("%s", rh->separator); + } + printf("\n"); + + out: + rh->flags |= RH_HEADINGS_PRINTED; + + return 1; +} + +/* + * Sort rows of data + */ +static int _row_compare(const void *a, const void *b) +{ + struct row *rowa = *(struct row **) a; + struct row *rowb = *(struct row **) b; + 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) { + uint64_t numa = *(uint64_t *) sfa->sort_value; + uint64_t numb = *(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 */ + char *stra = (char *) sfa->sort_value; + char *strb = (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)[]; + struct list *rowh; + uint32_t count = 0; + struct row *row; + + if (!(rows = pool_alloc(rh->mem, sizeof(**rows) * + list_size(&rh->rows)))) { + log_error("sort array allocation failed"); + return 0; + } + + list_iterate(rowh, &rh->rows) { + row = list_item(rowh, struct row); + (*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; + + 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) { + 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; + if (!(rh->flags & RH_ALIGNED)) + printf("%s", field->report_string); + else if (field->props->flags & FLD_ALIGN_LEFT) + printf("%-*.*s", field->props->width, + field->props->width, + field->report_string); + else if (field->props->flags & FLD_ALIGN_RIGHT) + printf("%*.*s", field->props->width, + field->props->width, + field->report_string); + if (!list_end(&row->fields, fh)) + printf("%s", rh->separator); + list_del(&field->list); + } + printf("\n"); + list_del(&row->list); + } + + if (row) + pool_free(rh->mem, row); + + return 1; +} diff --git a/lib/report/report.h b/lib/report/report.h new file mode 100644 index 000000000..a9259601c --- /dev/null +++ b/lib/report/report.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2002 Sistina Software + * + * This LVM library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This LVM library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this LVM library; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA + * + */ + +#ifndef _LVM_REPORT_H +#define _LVM_REPORT_H + +#include "metadata.h" + +typedef enum { LVS = 1, PVS = 2, VGS = 4, SEGS = 8 } report_type_t; + +struct field; +struct report_handle; + +typedef int (*field_report_fn) (struct report_handle * dh, struct field * field, + const void *data); + +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); +void report_free(void *handle); +int report_object(void *handle, struct volume_group *vg, + struct logical_volume *lv, struct physical_volume *pv, + struct lv_segment *seg); +int report_output(void *handle); + +#endif diff --git a/libdm/datastruct/list.h b/libdm/datastruct/list.h index 5f2e14c11..4aeb7b40e 100644 --- a/libdm/datastruct/list.h +++ b/libdm/datastruct/list.h @@ -13,11 +13,13 @@ struct list { struct list *n, *p; }; -static inline void list_init(struct list *head) { +static inline void list_init(struct list *head) +{ head->n = head->p = head; } -static inline void list_add(struct list *head, struct list *elem) { +static inline void list_add(struct list *head, struct list *elem) +{ assert(head->n); elem->n = head; @@ -27,7 +29,8 @@ static inline void list_add(struct list *head, struct list *elem) { head->p = elem; } -static inline void list_add_h(struct list *head, struct list *elem) { +static inline void list_add_h(struct list *head, struct list *elem) +{ assert(head->n); elem->n = head->n; @@ -37,27 +40,35 @@ static inline void list_add_h(struct list *head, struct list *elem) { head->n = elem; } -static inline void list_del(struct list *elem) { +static inline void list_del(struct list *elem) +{ elem->n->p = elem->p; elem->p->n = elem->n; } -static inline int list_empty(struct list *head) { +static inline int list_empty(struct list *head) +{ return head->n == head; } +static inline int list_end(struct list *head, struct list *elem) +{ + return elem->n == head; +} + #define list_iterate(v, head) \ for (v = (head)->n; v != head; v = v->n) #define list_iterate_safe(v, t, head) \ for (v = (head)->n, t = v->n; v != head; v = t, t = v->n) -static inline int list_size(struct list *head) { +static inline int list_size(struct list *head) +{ int s = 0; struct list *v; list_iterate(v, head) - s++; + s++; return s; } diff --git a/man/Makefile.in b/man/Makefile.in index 0b688cb48..30c70b2e6 100644 --- a/man/Makefile.in +++ b/man/Makefile.in @@ -22,11 +22,11 @@ VPATH = @srcdir@ MAN5=lvm.conf.5 MAN8=lvchange.8 lvcreate.8 lvdisplay.8 lvextend.8 lvm.8 lvmchange.8 \ - lvreduce.8 lvremove.8 lvrename.8 lvscan.8 pvchange.8 \ - pvcreate.8 pvdisplay.8 pvremove.8 pvscan.8 vgcfgbackup.8 \ + lvreduce.8 lvremove.8 lvrename.8 lvs.8 lvscan.8 pvchange.8 \ + pvcreate.8 pvdisplay.8 pvremove.8 pvs.8 pvscan.8 vgcfgbackup.8 \ vgcfgrestore.8 vgchange.8 vgck.8 vgcreate.8 \ vgconvert.8 vgdisplay.8 vgextend.8 vgmerge.8 vgreduce.8 vgremove.8 \ - vgrename.8 vgscan.8 + vgrename.8 vgs.8 vgscan.8 MAN5DIR=${mandir}/man5 MAN8DIR=${mandir}/man8 diff --git a/man/lvs.8 b/man/lvs.8 new file mode 100644 index 000000000..08d5eaccc --- /dev/null +++ b/man/lvs.8 @@ -0,0 +1,64 @@ +.TH LVS 8 "LVM TOOLS" "Sistina Software UK" \" -*- nroff -*- +.SH NAME +lvs \- report information about logical volumes +.SH SYNOPSIS +.B lvs +[\-\-aligned] [\-d/\-\-debug] [\-h/\-?/\-\-help] +[\-\-ignorelockingfailure] [\-\-noheadings] [\-\-nosuffix] +[\-o/\-\-options [+]Field[,Field]] +[\-O/\-\-sort [+/-]Key1[,[+/-]Key2[,...]]] +[\-P/\-\-partial] [\-\-segments] +[\-\-separator Separator] [\-\-unbuffered] +[\-\-units hsbkmgtHKMGT] +[\-v/\-\-verbose] +[\-\-version] [VolumeGroupName [VolumeGroupName...]] +.SH DESCRIPTION +vgs produces formatted output about volume groups +.SH OPTIONS +See \fBlvm\fP for common options. +.TP +.I \-\-aligned +Use with \-\-separator to align the output columns. +.TP +.I \-\-noheadings +Suppress the headings line that is normally the first line of output. +Useful if grepping the output. +.TP +.I \-\-nosuffix +Suppress the suffix on output sizes. Use with \-\-units (except h and H) +if processing the output. +.TP +.I \-o, \-\-options +Comma-separated ordered list of columns. Precede the list with '+' to append +to the default selection of columns. Column names are: +lv_uuid, lv_name, lv_attr, lv_minor, lv_size, seg_count, origin, +snap_percent (suppressed if no kernel driver), segtype, stripes, +stripesize, chunksize, seg_start, seg_size. +With \-\-segments, any "seg_" prefixes are optional; otherwise any "lv_" +prefixes are optional. Columns mentioned in \fBvgs (8)\fP +can also be chosen +The lv_attr bits are: (o)rigin, (s)napshot, (w)riteable, (r)eadonly, +(c)ontiguous allocation, (n)ext free allocation, fixed (m)inor, (s)uspended, +(a)ctive, device (o)pen. +.TP +.I \-\-segments +Use default columns that emphasize segment information. +.TP +.I \-O, \-\-sort +Comma-separated ordered list of columns to sort by. Replaces the default +selection. Precede any column with - for a reverse sort on that column. +.TP +.I \-\-separator Separator +String to use to separate each column. Useful if grepping the output. +.TP +.I \-\-unbuffered +Produce output immediately without sorting or aligning the columns properly. +.TP +.I \-\-units hsbkmgtHKMGT +All sizes are output in these units: (h)uman-readable, (s)ectors, (b)ytes, +(k)ilobytes, (m)egabytes, (g)igabytes, (t)erabytes. Capitalise to use multiples +of 1000 (S.I.) instead of 1024. Can also specify custom (u)nits e.g. +\-\-units 3M +.SH SEE ALSO +.BR pvs (8), +.BR vgs (8) diff --git a/man/pvs.8 b/man/pvs.8 new file mode 100644 index 000000000..d3d83d978 --- /dev/null +++ b/man/pvs.8 @@ -0,0 +1,54 @@ +.TH PVS 8 "LVM TOOLS" "Sistina Software UK" \" -*- nroff -*- +.SH NAME +pvs \- report information about physical volumes +.SH SYNOPSIS +.B pvs +[\-\-aligned] [\-d/\-\-debug] [\-h/\-?/\-\-help] +[\-\-ignorelockingfailure] [\-\-noheadings] [\-\-nosuffix] +[\-o/\-\-options [+]Field[,Field]] +[\-O/\-\-sort [+/-]Key1[,[+/-]Key2[,...]]] +[\-\-separator Separator] [\-\-unbuffered] +[\-\-units hsbkmgtHKMGT] +[\-v/\-\-verbose] +[\-\-version] [PhysicalVolume [PhysicalVolume...]] +.SH DESCRIPTION +pvs produces formatted output about physical volumes +.SH OPTIONS +See \fBlvm\fP for common options. +.TP +.I \-\-aligned +Use with \-\-separator to align the output columns. +.TP +.I \-\-noheadings +Suppress the headings line that is normally the first line of output. +Useful if grepping the output. +.TP +.I \-\-nosuffix +Suppress the suffix on output sizes. Use with \-\-units (except h and H) +if processing the output. +.TP +.I \-o, \-\-options +Comma-separated ordered list of columns. Precede the list with '+' to append +to the default selection of columns. Column names are: pv_fmt, pv_uuid, +pv_size, pv_free, pv_used, pv_name, pv_attr, pv_pe_count, pv_pe_alloc_count. +The "pv_" prefix is optional. Columns mentioned in \fBvgs (8)\fP can also +be chosen. The pv_attr bits are: (a)llocatable and e(x)ported. +.TP +.I \-O, \-\-sort +Comma-separated ordered list of columns to sort by. Replaces the default +selection. Precede any column with - for a reverse sort on that column. +.TP +.I \-\-separator Separator +String to use to separate each column. Useful if grepping the output. +.TP +.I \-\-unbuffered +Produce output immediately without sorting or aligning the columns properly. +.TP +.I \-\-units hsbkmgtHKMGT +All sizes are output in these units: (h)uman-readable, (s)ectors, (b)ytes, +(k)ilobytes, (m)egabytes, (g)igabytes, (t)erabytes. Capitalise to use multiples +of 1000 (S.I.) instead of 1024. Can also specify custom (u)nits e.g. +\-\-units 3M +.SH SEE ALSO +.BR lvs (8), +.BR vgs (8) diff --git a/man/vgs.8 b/man/vgs.8 new file mode 100644 index 000000000..0f47235ce --- /dev/null +++ b/man/vgs.8 @@ -0,0 +1,58 @@ +.TH VGS 8 "LVM TOOLS" "Sistina Software UK" \" -*- nroff -*- +.SH NAME +vgs \- report information about volume groups +.SH SYNOPSIS +.B vgs +[\-\-aligned] [\-d/\-\-debug] [\-h/\-?/\-\-help] +[\-\-ignorelockingfailure] [\-\-noheadings] [\-\-nosuffix] +[\-o/\-\-options [+]Field[,Field]] +[\-O/\-\-sort [+/-]Key1[,[+/-]Key2[,...]]] +[\-P/\-\-partial] +[\-\-separator Separator] [\-\-unbuffered] +[\-\-units hsbkmgtHKMGT] +[\-v/\-\-verbose] +[\-\-version] [VolumeGroupName [VolumeGroupName...]] +.SH DESCRIPTION +vgs produces formatted output about volume groups +.SH OPTIONS +See \fBlvm\fP for common options. +.TP +.I \-\-aligned +Use with \-\-separator to align the output columns. +.TP +.I \-\-noheadings +Suppress the headings line that is normally the first line of output. +Useful if grepping the output. +.TP +.I \-\-nosuffix +Suppress the suffix on output sizes. Use with \-\-units (except h and H) +if processing the output. +.TP +.I \-o, \-\-options +Comma-separated ordered list of columns. Precede the list with '+' to append +to the default selection of columns. Column names are: vg_fmt, vg_uuid, +vg_name, vg_attr, vg_size, vg_free, vg_sysid, vg_extent_size, vg_extent_count, +vg_free_count, max_lv, max_pv, pv_count, lv_count, snap_count, vg_seqno. +Any "vg_" prefixes are optional. Columns mentioned in either \fBpvs (8)\fP +or \fBlvs (8)\fP can also be chosen, but columns cannot be taken from both +at the same time. The vg_attr bits are: (w)riteable, (r)eadonly, +resi(z)eable, e(x)ported, (p)artial. +.TP +.I \-O, \-\-sort +Comma-separated ordered list of columns to sort by. Replaces the default +selection. Precede any column with - for a reverse sort on that column. +.TP +.I \-\-separator Separator +String to use to separate each column. Useful if grepping the output. +.TP +.I \-\-unbuffered +Produce output immediately without sorting or aligning the columns properly. +.TP +.I \-\-units hsbkmgtHKMGT +All sizes are output in these units: (h)uman-readable, (s)ectors, (b)ytes, +(k)ilobytes, (m)egabytes, (g)igabytes, (t)erabytes. Capitalise to use multiples +of 1000 (S.I.) instead of 1024. Can also specify custom (u)nits e.g. +\-\-units 3M +.SH SEE ALSO +.BR pvs (8), +.BR lvs (8) diff --git a/tools/Makefile.in b/tools/Makefile.in index 42ae8aef7..604e942bb 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -39,6 +39,7 @@ SOURCES=\ pvdisplay.c \ pvremove.c \ pvscan.c \ + report.c \ toollib.c \ vgcfgbackup.c \ vgcfgrestore.c \ @@ -67,8 +68,8 @@ lvm: $(OBJECTS) $(top_srcdir)/lib/liblvm.a $(CC) -o lvm $(OBJECTS) $(LD_FLAGS) -L$(top_srcdir)/lib \ -L$(DESTDIR)/lib -llvm -ldevmapper $(LIBS) -ldl -rdynamic -.commands: commands.h cmdnames.h - $(CC) -E -P cmdnames.h | grep -v help > .commands +.commands: commands.h cmdnames.h Makefile + $(CC) -E -P cmdnames.h | egrep -v '(help|version)' > .commands install: $(TARGETS) $(INSTALL) -D -o $(OWNER) -g $(GROUP) -m 555 $(STRIP) lvm \ diff --git a/tools/args.h b/tools/args.h index d859f16a2..06d5a0a2f 100644 --- a/tools/args.h +++ b/tools/args.h @@ -17,6 +17,12 @@ arg(metadatasize_ARG, '\0', "metadatasize", size_mb_arg) arg(restorefile_ARG, '\0', "restorefile", string_arg) arg(labelsector_ARG, '\0', "labelsector", int_arg) arg(driverloaded_ARG, '\0', "driverloaded", yes_no_arg) +arg(aligned_ARG, '\0', "aligned", NULL) +arg(unbuffered_ARG, '\0', "unbuffered", NULL) +arg(noheadings_ARG, '\0', "noheadings", NULL) +arg(segments_ARG, '\0', "segments", NULL) +arg(units_ARG, '\0', "units", string_arg) +arg(nosuffix_ARG, '\0', "nosuffix", NULL) /* Allow some variations */ arg(resizable_ARG, '\0', "resizable", yes_no_arg) @@ -32,6 +38,7 @@ arg(activevolumegroups_ARG, 'A', "activevolumegroups", NULL) arg(blockdevice_ARG, 'b', "blockdevice", NULL) arg(chunksize_ARG, 'c', "chunksize", size_kb_arg) arg(colon_ARG, 'c', "colon", NULL) +arg(columns_ARG, 'C', "columns", NULL) arg(contiguous_ARG, 'C', "contiguous", yes_no_arg) arg(debug_ARG, 'd', "debug", NULL) arg(disk_ARG, 'D', "disk", NULL) @@ -61,6 +68,7 @@ arg(oldpath_ARG, 'n', "oldpath", NULL) arg(nofsck_ARG, 'n', "nofsck", NULL) arg(novolumegroup_ARG, 'n', "novolumegroup", NULL) arg(options_ARG, 'o', "options", string_arg) +arg(sort_ARG, 'O', "sort", string_arg) arg(permission_ARG, 'p', "permission", permission_arg) arg(maxphysicalvolumes_ARG, 'p', "maxphysicalvolumes", int_arg) arg(partial_ARG, 'P', "partial", NULL) @@ -68,6 +76,7 @@ arg(physicalvolume_ARG, 'P', "physicalvolume", NULL) arg(readahead_ARG, 'r', "readahead", int_arg) arg(reset_ARG, 'R', "reset", NULL) arg(physicalextentsize_ARG, 's', "physicalextentsize", size_mb_arg) +arg(separator_ARG, 's', "separator", string_arg) arg(stdin_ARG, 's', "stdin", NULL) arg(snapshot_ARG, 's', "snapshot", NULL) arg(short_ARG, 's', "short", NULL) diff --git a/tools/commands.h b/tools/commands.h index 5e984b422..2923f91d9 100644 --- a/tools/commands.h +++ b/tools/commands.h @@ -62,6 +62,7 @@ xx(lvchange, "\t[-r|--readahead ReadAheadSectors]\n" "\t[-t|--test]\n" "\t[-v|--verbose]\n" + "\t[--version]" "\n" "\tLogicalVolume[Path] [LogicalVolume[Path]...]\n", autobackup_ARG, available_ARG, contiguous_ARG, @@ -118,11 +119,34 @@ xx(lvdisplay, "\t[-h|--help]\n" "\t[--ignorelockingfailure]\n" "\t[-m|--maps]\n" + "\t[--nosuffix]\n" "\t[-P|--partial] " "\n" + "\t[--units hsbkmgtHKMGT]\n" "\t[-v|--verbose]\n" - "\tLogicalVolume[Path] [LogicalVolume[Path]...]\n", + "\t[--version]" "\n" + "\t[LogicalVolume[Path] [LogicalVolume[Path]...]]\n" + "\n" + "lvdisplay --columns|-C\n" + "\t[--aligned]\n" + "\t[-d|--debug]\n" + "\t[-h|--help]\n" + "\t[--ignorelockingfailure]\n" + "\t[--noheadings]\n" + "\t[--nosuffix]\n" + "\t[-o|--options [+]Field[,Field]]\n" + "\t[-O|--sort [+|-]key1[,[+|-]key2[,...]]]\n" + "\t[-P|--partial] " "\n" + "\t[--segments]\n" + "\t[--separator Separator]\n" + "\t[--unbuffered]\n" + "\t[--units hsbkmgtHKMGT]\n" + "\t[-v|--verbose]\n" + "\t[--version]" "\n" + "\t[LogicalVolume[Path] [LogicalVolume[Path]...]]\n", - colon_ARG, disk_ARG, ignorelockingfailure_ARG, maps_ARG, partial_ARG) + aligned_ARG, colon_ARG, columns_ARG, disk_ARG, ignorelockingfailure_ARG, + maps_ARG, noheadings_ARG, nosuffix_ARG, options_ARG, sort_ARG, + partial_ARG, segments_ARG, separator_ARG, unbuffered_ARG, units_ARG) xx(lvextend, "Add space to a logical volume", @@ -135,6 +159,7 @@ xx(lvextend, "\t -L|--size [+]LogicalVolumeSize[kKmMgGtT]}\n" "\t[-t|--test]\n" "\t[-v|--verbose]\n" + "\t[--version]" "\n" "\tLogicalVolume[Path] [ PhysicalVolumePath... ]\n", autobackup_ARG, extents_ARG, size_ARG, stripes_ARG, @@ -146,7 +171,8 @@ xx(lvmchange, "\t[-d|--debug]\n" "\t[-h|--help]\n" "\t[-R|--reset]\n" - "\t[-v|--verbose]\n", + "\t[-v|--verbose]\n" + "\t[--version]" "\n", reset_ARG) @@ -155,7 +181,8 @@ xx(lvmdiskscan, "lvmdiskscan\n" "\t[-d|--debug]\n" "\t[-h|--help]\n" - "\t[-l|--lvmpartition]\n", + "\t[-l|--lvmpartition]\n" + "\t[--version]" "\n", lvmpartition_ARG) @@ -165,6 +192,7 @@ xx(lvmsadc, "\t[-d|--debug]\n" "\t[-h|--help]\n" "\t[-v|--verbose]\n" + "\t[--version]" "\n" "\t[LogFilePath]\n" ) xx(lvmsar, @@ -175,6 +203,7 @@ xx(lvmsar, "\t[-h|--help]\n" "\t[-s|--stdin]\n" "\t[-v|--verbose]\n" + "\t[--version]" "\n" "\tLogFilePath\n", full_ARG, stdin_ARG) @@ -190,6 +219,7 @@ xx(lvreduce, "\t -L|--size [-]LogicalVolumeSize[kKmMgGtT]}\n" "\t[-t|--test]\n" "\t[-v|--verbose]\n" + "\t[--version]" "\n" "\tLogicalVolume[Path]\n", autobackup_ARG, force_ARG, extents_ARG, @@ -204,6 +234,7 @@ xx(lvremove, "\t[-h|--help]\n" "\t[-t|--test]\n" "\t[-v|--verbose]\n" + "\t[--version]" "\n" "\tLogicalVolume[Path] [LogicalVolume[Path]...]\n", autobackup_ARG, force_ARG, test_ARG) @@ -233,11 +264,36 @@ xx(lvresize, "\t -L|--size [+|-]LogicalVolumeSize[kKmMgGtT]}\n" "\t[-t|--test]\n" "\t[-v|--verbose]\n" + "\t[--version]" "\n" "\tLogicalVolume[Path] [ PhysicalVolumePath... ]\n", autobackup_ARG, extents_ARG, size_ARG, stripes_ARG, stripesize_ARG, test_ARG) +xx(lvs, + "Display information about logical volumes", + "lvs" "\n" + "\t[--aligned]\n" + "\t[-d|--debug]\n" + "\t[-h|--help]\n" + "\t[--ignorelockingfailure]\n" + "\t[--noheadings]\n" + "\t[--nosuffix]\n" + "\t[-o|--options [+]Field[,Field]]\n" + "\t[-O|--sort [+|-]key1[,[+|-]key2[,...]]]\n" + "\t[-P|--partial] " "\n" + "\t[--segments]\n" + "\t[--separator Separator]\n" + "\t[--unbuffered]\n" + "\t[--units hsbkmgtHKMGT]\n" + "\t[-v|--verbose]\n" + "\t[--version]" "\n" + "\t[LogicalVolume[Path] [LogicalVolume[Path]...]]\n", + + aligned_ARG, ignorelockingfailure_ARG, noheadings_ARG, nosuffix_ARG, + options_ARG, partial_ARG, segments_ARG, separator_ARG, sort_ARG, + unbuffered_ARG, units_ARG) + xx(lvscan, "List all logical volumes in all volume groups", "lvscan " "\n" @@ -258,6 +314,7 @@ xx(pvchange, "\t[-d|--debug]\n" "\t[-h|--help]\n" "\t[-v|--verbose]\n" + "\t[--version]" "\n" "\t[-a|--all]\n" "\t[-t|--test]\n" "\t[-x|--allocatable y|n]\n" @@ -307,18 +364,39 @@ xx(pvdata, physicalvolume_ARG, uuidlist_ARG, volumegroup_ARG) xx(pvdisplay, - "Display various attributes of logical volume(s)", + "Display various attributes of physical volume(s)", "pvdisplay\n" "\t[-c|--colon]\n" "\t[-d|--debug]\n" "\t[-h|--help]\n" "\t[--ignorelockingfailure]\n" "\t[-m|--maps]\n" + "\t[--nosuffix]\n" "\t[-s|--short]\n" + "\t[--units hsbkmgtHKMGT]\n" "\t[-v|--verbose]\n" - "\tPhysicalVolumePath [PhysicalVolumePath...]\n", + "\t[--version]" "\n" + "\t[PhysicalVolumePath [PhysicalVolumePath...]]\n" + "\n" + "pvdisplay --columns|-C\n" + "\t[--aligned]\n" + "\t[-d|--debug]\n" + "\t[-h|--help]\n" + "\t[--ignorelockingfailure]\n" + "\t[--noheadings]\n" + "\t[--nosuffix]\n" + "\t[-o|--options [+]Field[,Field]]\n" + "\t[-O|--sort [+|-]key1[,[+|-]key2[,...]]]\n" + "\t[--separator Separator]\n" + "\t[--unbuffered]\n" + "\t[--units hsbkmgtHKMGT]\n" + "\t[-v|--verbose]\n" + "\t[--version]" "\n" + "\t[PhysicalVolumePath [PhysicalVolumePath...]]\n", - colon_ARG, ignorelockingfailure_ARG, maps_ARG, short_ARG) + aligned_ARG, colon_ARG, columns_ARG, ignorelockingfailure_ARG, maps_ARG, + noheadings_ARG, nosuffix_ARG, options_ARG, separator_ARG, short_ARG, + sort_ARG, unbuffered_ARG, units_ARG) xx(pvmove, "Move extents from one physical volume to another", @@ -366,6 +444,27 @@ xx(pvresize, ***/ autobackup_ARG, physicalvolumesize_ARG) +xx(pvs, + "Display information about physical volumes", + "pvs" "\n" + "\t[--aligned]\n" + "\t[-d|--debug]" "\n" + "\t[-h|-?|--help] " "\n" + "\t[--noheadings]\n" + "\t[--ignorelockingfailure]\n" + "\t[--nosuffix]\n" + "\t[-o|--options [+]Field[,Field]]\n" + "\t[-O|--sort [+|-]key1[,[+|-]key2[,...]]]\n" + "\t[--separator Separator]\n" + "\t[--unbuffered]\n" + "\t[--units hsbkmgtHKMGT]\n" + "\t[-v|--verbose]\n" + "\t[--version]\n" + "\t[PhysicalVolume [PhysicalVolume...]]\n", + + aligned_ARG, ignorelockingfailure_ARG, noheadings_ARG, nosuffix_ARG, + options_ARG, separator_ARG, sort_ARG, unbuffered_ARG, units_ARG) + xx(pvscan, "List all physical volumes", "pvscan " "\n" @@ -391,7 +490,7 @@ xx(vgcfgbackup, "\t[--ignorelockingfailure]\n" "\t[-P|--partial] " "\n" "\t[-v|--verbose]" "\n" - "\t[-V|--version] " "\n" + "\t[--version] " "\n" "\t[VolumeGroupName...]\n", file_ARG, ignorelockingfailure_ARG, partial_ARG) @@ -437,6 +536,7 @@ xx(vgck, "\t[-d|--debug]\n" "\t[-h|--help]\n" "\t[-v|--verbose]\n" + "\t[--version]" "\n", "\t[VolumeGroupName...]\n" ) xx(vgconvert, @@ -481,13 +581,33 @@ xx(vgdisplay, "\t[-d|--debug] " "\n" "\t[-h|--help] " "\n" "\t[--ignorelockingfailure]" "\n" + "\t[--nosuffix]\n" "\t[-P|--partial] " "\n" + "\t[--units hsbkmgtHKMGT]\n" "\t[-A|--activevolumegroups | [-D|--disk]" "\n" "\t[--version]" "\n" - "\t[VolumeGroupName...] ]\n", + "\t[VolumeGroupName [VolumeGroupName...]]\n" + "\n" + "vgdisplay --columns|-C\n" + "\t[--aligned]\n" + "\t[-d|--debug] " "\n" + "\t[-h|--help] " "\n" + "\t[--ignorelockingfailure]" "\n" + "\t[--noheadings]\n" + "\t[--nosuffix]\n" + "\t[-o|--options [+]Field[,Field]]\n" + "\t[-O|--sort [+|-]key1[,[+|-]key2[,...]]]\n" + "\t[-P|--partial] " "\n" + "\t[--separator Separator]\n" + "\t[--unbuffered]\n" + "\t[--units hsbkmgtHKMGT]\n" + "\t[--verbose]" "\n" + "\t[--version]" "\n" + "\t[VolumeGroupName [VolumeGroupName...]]\n", - activevolumegroups_ARG, colon_ARG, disk_ARG, ignorelockingfailure_ARG, - short_ARG, partial_ARG) + activevolumegroups_ARG, aligned_ARG, colon_ARG, columns_ARG, disk_ARG, + ignorelockingfailure_ARG, noheadings_ARG, nosuffix_ARG, options_ARG, + partial_ARG, separator_ARG, short_ARG, sort_ARG, unbuffered_ARG, units_ARG) xx(vgexport, "Unregister volume group(s) from the system", @@ -509,6 +629,7 @@ xx(vgextend, "\t[-h|--help]\n" "\t[-t|--test]\n" "\t[-v|--verbose]\n" + "\t[--version]" "\n" "\tVolumeGroupName PhysicalDevicePath [PhysicalDevicePath...]\n", autobackup_ARG, test_ARG) @@ -522,6 +643,7 @@ xx(vgimport, "\t[-h|--help] " "\n" "\t[-t|--test] " "\n" "\t[-v|--verbose]" "\n" + "\t[--version]" "\n" "\tVolumeGroupName..." "\n", all_ARG, force_ARG, test_ARG) @@ -535,6 +657,7 @@ xx(vgmerge, "\t[-l|--list]\n" "\t[-t|--test]\n" "\t[-v|--verbose]\n" + "\t[--version]" "\n" "\tDestinationVolumeGroupName SourceVolumeGroupName\n", autobackup_ARG, list_ARG, test_ARG) @@ -545,6 +668,7 @@ xx(vgmknodes, "\t[-d|--debug]\n" "\t[-h|--help]\n" "\t[-v|--verbose]\n" + "\t[--version]" "\n" "\t[VolumeGroupName...]\n" ) xx(vgreduce, @@ -556,6 +680,7 @@ xx(vgreduce, "\t[-h|--help]\n" "\t[-t|--test]\n" "\t[-v|--verbose]\n" + "\t[--version]" "\n" "\tVolumeGroupName\n" "\t[PhysicalVolumePath...]\n", @@ -568,6 +693,7 @@ xx(vgremove, "\t[-h|--help]\n" "\t[-t|--test]\n" "\t[-v|--verbose]\n" + "\t[--version]" "\n" "\tVolumeGroupName [VolumeGroupName...]\n", test_ARG) @@ -580,11 +706,34 @@ xx(vgrename, "\t[-h|--help]\n" "\t[-t|--test]\n" "\t[-v|--verbose]\n" + "\t[--version]" "\n" "\tOldVolumeGroupPath NewVolumeGroupPath |\n" "\tOldVolumeGroupName NewVolumeGroupName\n", autobackup_ARG, force_ARG, test_ARG) +xx(vgs, + "Display information about volume groups", + "vgs" "\n" + "\t[--aligned]\n" + "\t[-d|--debug]\n" + "\t[-h|--help]\n" + "\t[--ignorelockingfailure]\n" + "\t[--noheadings]\n" + "\t[--nosuffix]\n" + "\t[-o|--options [+]Field[,Field]]\n" + "\t[-O|--sort [+|-]key1[,[+|-]key2[,...]]]\n" + "\t[-P|--partial] " "\n" + "\t[--separator Separator]\n" + "\t[--unbuffered]\n" + "\t[--units hsbkmgtHKMGT]\n" + "\t[-v|--verbose]\n" + "\t[--version]\n" + "\t[VolumeGroupName [VolumeGroupName...]]\n", + + aligned_ARG, ignorelockingfailure_ARG, noheadings_ARG, nosuffix_ARG, + options_ARG, partial_ARG, separator_ARG, sort_ARG, unbuffered_ARG, units_ARG) + xx(vgscan, "Search for all volume groups", "vgscan " @@ -592,7 +741,8 @@ xx(vgscan, "\t[-h|--help]\n" "\t[--ignorelockingfailure]\n" "\t[-P|--partial] " "\n" - "\t[-v|--verbose]\n" , + "\t[-v|--verbose]\n" + "\t[--version]" "\n", ignorelockingfailure_ARG, partial_ARG) diff --git a/tools/lvcreate.c b/tools/lvcreate.c index 176c7f4e4..b7ca5315c 100644 --- a/tools/lvcreate.c +++ b/tools/lvcreate.c @@ -144,7 +144,7 @@ static int _read_size_params(struct lvcreate_params *lp, /* Size returned in kilobyte units; held in sectors */ if (arg_count(cmd, size_ARG)) - lp->size = arg_int64_value(cmd, size_ARG, 0) * 2ull; + lp->size = arg_uint64_value(cmd, size_ARG, 0) * 2ull; return 1; } @@ -378,15 +378,11 @@ static int _lvcreate(struct cmd_context *cmd, struct lvcreate_params *lp) lp->extents = lp->size; if (lp->extents % vg->extent_size) { - char *s1; - lp->extents += vg->extent_size - lp->extents % vg->extent_size; - log_print("Rounding up size to full physical " - "extent %s", - (s1 = display_size(lp->extents / 2, - SIZE_SHORT))); - dbg_free(s1); + log_print("Rounding up size to full physical extent %s", + display_size(cmd, lp->extents / 2, + SIZE_SHORT)); } lp->extents /= vg->extent_size; diff --git a/tools/lvdisplay.c b/tools/lvdisplay.c index 69971b1be..47d546da8 100644 --- a/tools/lvdisplay.c +++ b/tools/lvdisplay.c @@ -36,7 +36,20 @@ int lvdisplay_single(struct cmd_context *cmd, struct logical_volume *lv, int lvdisplay(struct cmd_context *cmd, int argc, char **argv) { - /* FIXME Allow VG args via process_each */ + if (arg_count(cmd, columns_ARG)) { + if (arg_count(cmd, colon_ARG) || arg_count(cmd, maps_ARG)) { + log_error("Incompatible options selected"); + return EINVALID_CMD_LINE; + } + return lvs(cmd, argc, argv); + } else if (arg_count(cmd, aligned_ARG) || + arg_count(cmd, noheadings_ARG) || + arg_count(cmd, options_ARG) || + arg_count(cmd, separator_ARG) || + arg_count(cmd, sort_ARG) || arg_count(cmd, unbuffered_ARG)) { + log_error("Incompatible options selected"); + return EINVALID_CMD_LINE; + } if (arg_count(cmd, colon_ARG) && arg_count(cmd, verbose_ARG)) { log_error("Options -v and -c are incompatible"); diff --git a/tools/lvm.c b/tools/lvm.c index dbc295ff9..665503190 100644 --- a/tools/lvm.c +++ b/tools/lvm.c @@ -157,7 +157,8 @@ static int _size_arg(struct cmd_context *cmd, struct arg *a, int factor) v *= factor; a->i_value = (uint32_t) v; - a->i64_value = (uint64_t) v; + a->i64_value = (int64_t) v; + a->ui64_value = (uint64_t) v; return 1; } @@ -547,6 +548,17 @@ static int _get_settings(struct cmd_context *cmd) else init_ignorelockingfailure(0); + if (arg_count(cmd, nosuffix_ARG)) + cmd->current_settings.suffix = 0; + + if (arg_count(cmd, units_ARG)) + if (!(cmd->current_settings.unit_factor = + units_to_bytes(arg_str_value(cmd, units_ARG, ""), + &cmd->current_settings.unit_type))) { + log_error("Invalid units specification"); + return EINVALID_CMD_LINE; + } + /* Handle synonyms */ if (!_merge_synonym(cmd, resizable_ARG, resizeable_ARG) || !_merge_synonym(cmd, allocation_ARG, allocatable_ARG) || diff --git a/tools/lvmdiskscan.c b/tools/lvmdiskscan.c index a6edfa6bf..a33c128e8 100644 --- a/tools/lvmdiskscan.c +++ b/tools/lvmdiskscan.c @@ -52,19 +52,14 @@ static void _count(struct device *dev, int *disks, int *parts) (*parts)++; } -static void _print(struct device *dev, uint64_t size, char *what) +static void _print(struct cmd_context *cmd, struct device *dev, uint64_t size, + char *what) { - char *dummy = display_size(size / 2, SIZE_SHORT); - const char *name = dev_name(dev); - - if (!what) - what = ""; - - log_print("%-*s [%15s] %s", max_len, name, dummy, what); - dbg_free(dummy); + log_print("%-*s [%15s] %s", max_len, dev_name(dev), + display_size(cmd, size / 2, SIZE_SHORT), what ? : ""); } -static int _check_device(struct device *dev) +static int _check_device(struct cmd_context *cmd, struct device *dev) { char buffer; uint64_t size; @@ -79,7 +74,7 @@ static int _check_device(struct device *dev) if (!dev_get_size(dev, &size)) { log_error("Couldn't get size of \"%s\"", dev_name(dev)); } - _print(dev, size, NULL); + _print(cmd, dev, size, NULL); _count(dev, &disks_found, &parts_found); if (!dev_close(dev)) { log_error("dev_close on \"%s\" failed", dev_name(dev)); @@ -114,7 +109,7 @@ int lvmdiskscan(struct cmd_context *cmd, int argc, char **argv) dev_name(dev)); continue; } - _print(dev, size, "LVM physical volume"); + _print(cmd, dev, size, "LVM physical volume"); _count(dev, &pv_disks_found, &pv_parts_found); continue; } @@ -123,7 +118,7 @@ int lvmdiskscan(struct cmd_context *cmd, int argc, char **argv) continue; /* What other device is it? */ - if (!_check_device(dev)) + if (!_check_device(cmd, dev)) continue; } dev_iter_destroy(iter); diff --git a/tools/lvresize.c b/tools/lvresize.c index b058d8f51..163450b02 100644 --- a/tools/lvresize.c +++ b/tools/lvresize.c @@ -34,7 +34,6 @@ int lvresize(struct cmd_context *cmd, int argc, char **argv) sign_t sign = SIGN_NONE; char *lv_name, *vg_name; char *st; - char *dummy; const char *cmd_name; struct list *pvh, *segh; struct lv_list *lvl; @@ -145,8 +144,6 @@ int lvresize(struct cmd_context *cmd, int argc, char **argv) extents = size * 2; if (extents % vg->extent_size) { - char *s1; - if (sign == SIGN_MINUS) extents -= extents % vg->extent_size; else @@ -154,8 +151,7 @@ int lvresize(struct cmd_context *cmd, int argc, char **argv) (extents % vg->extent_size); log_print("Rounding up size to full physical extent %s", - (s1 = display_size(extents / 2, SIZE_SHORT))); - dbg_free(s1); + display_size(cmd, extents / 2, SIZE_SHORT)); } extents /= vg->extent_size; @@ -310,16 +306,14 @@ int lvresize(struct cmd_context *cmd, int argc, char **argv) } if (info.exists) { - dummy = display_size((uint64_t) - extents * (vg->extent_size / 2), - SIZE_SHORT); log_print("WARNING: Reducing active%s logical volume " "to %s", info.open_count ? " and open" : "", - dummy); + display_size(cmd, (uint64_t) + extents * (vg->extent_size / 2), + SIZE_SHORT)); log_print("THIS MAY DESTROY YOUR DATA " "(filesystem etc.)"); - dbg_free(dummy); } if (!arg_count(cmd, force_ARG)) { @@ -352,11 +346,10 @@ int lvresize(struct cmd_context *cmd, int argc, char **argv) /* Use full list from VG */ pvh = &vg->pvs; } - dummy = display_size((uint64_t) - extents * (vg->extent_size / 2), - SIZE_SHORT); - log_print("Extending logical volume %s to %s", lv_name, dummy); - dbg_free(dummy); + log_print("Extending logical volume %s to %s", lv_name, + display_size(cmd, (uint64_t) + extents * (vg->extent_size / 2), + SIZE_SHORT)); if (!lv_extend(vg->fid, lv, stripes, stripesize, extents - lv->le_count, pvh)) diff --git a/tools/lvscan.c b/tools/lvscan.c index d0449288a..fd93d58e9 100644 --- a/tools/lvscan.c +++ b/tools/lvscan.c @@ -27,7 +27,6 @@ static int lvscan_single(struct cmd_context *cmd, struct logical_volume *lv, int lv_total = 0; ulong lv_capacity_total = 0; - char *dummy; const char *active_str, *snapshot_str; if (lv_info(lv, &info) && info.exists) @@ -42,14 +41,11 @@ static int lvscan_single(struct cmd_context *cmd, struct logical_volume *lv, else snapshot_str = " "; - dummy = display_size(lv->size / 2, SIZE_SHORT); - log_print("%s%s '%s%s/%s' [%s] %s", active_str, snapshot_str, - cmd->dev_dir, lv->vg->name, lv->name, dummy, + cmd->dev_dir, lv->vg->name, lv->name, + display_size(cmd, lv->size / 2, SIZE_SHORT), get_alloc_string(lv->alloc)); - dbg_free(dummy); - lv_total++; lv_capacity_total += lv->size; @@ -66,4 +62,5 @@ int lvscan(struct cmd_context *cmd, int argc, char **argv) return process_each_lv(cmd, argc, argv, LCK_VG_READ, NULL, &lvscan_single); + } diff --git a/tools/pvcreate.c b/tools/pvcreate.c index 7ca11ede1..5534437db 100644 --- a/tools/pvcreate.c +++ b/tools/pvcreate.c @@ -130,9 +130,9 @@ static void pvcreate_single(struct cmd_context *cmd, const char *pv_name, if (!pvcreate_check(cmd, pv_name)) goto error; - size = arg_int64_value(cmd, physicalvolumesize_ARG, 0) * 2; + size = arg_uint64_value(cmd, physicalvolumesize_ARG, 0) * 2; - pvmetadatasize = arg_int64_value(cmd, metadatasize_ARG, 0) * 2; + pvmetadatasize = arg_uint64_value(cmd, metadatasize_ARG, 0) * 2; if (!pvmetadatasize) pvmetadatasize = find_config_int(cmd->cf->root, "metadata/pvmetadatasize", diff --git a/tools/pvdisplay.c b/tools/pvdisplay.c index ff4d121a1..2bb74ea4a 100644 --- a/tools/pvdisplay.c +++ b/tools/pvdisplay.c @@ -20,10 +20,9 @@ #include "tools.h" -void pvdisplay_single(struct cmd_context *cmd, struct physical_volume *pv, - void *handle) +int pvdisplay_single(struct cmd_context *cmd, struct volume_group *vg, + struct physical_volume *pv, void *handle) { - char *sz; uint64_t size; const char *pv_name = dev_name(pv->dev); @@ -34,67 +33,56 @@ void pvdisplay_single(struct cmd_context *cmd, struct physical_volume *pv, size = (pv->pe_count - pv->pe_alloc_count) * pv->pe_size; if (arg_count(cmd, short_ARG)) { - sz = display_size(size / 2, SIZE_SHORT); - log_print("Device \"%s\" has a capacity of %s", pv_name, sz); - dbg_free(sz); - return; + log_print("Device \"%s\" has a capacity of %s", pv_name, + display_size(cmd, size / 2, SIZE_SHORT)); + return 0; } if (pv->status & EXPORTED_VG) log_print("Physical volume \"%s\" of volume group \"%s\" " "is exported", pv_name, pv->vg_name); - if (!pv->vg_name) { + if (!pv->vg_name) log_print("\"%s\" is a new physical volume of \"%s\"", - pv_name, (sz = display_size(size / 2, SIZE_SHORT))); - dbg_free(sz); - } + pv_name, display_size(cmd, size / 2, SIZE_SHORT)); if (arg_count(cmd, colon_ARG)) { pvdisplay_colons(pv); - return; + return 0; } - pvdisplay_full(pv, handle); + pvdisplay_full(cmd, pv, handle); if (!arg_count(cmd, maps_ARG)) - return; + return 0; - return; + return 0; } int pvdisplay(struct cmd_context *cmd, int argc, char **argv) { - int opt = 0; - - struct list *pvh, *pvs; - struct physical_volume *pv; + if (arg_count(cmd, columns_ARG)) { + if (arg_count(cmd, colon_ARG) || arg_count(cmd, maps_ARG) || + arg_count(cmd, short_ARG)) { + log_error("Incompatible options selected"); + return EINVALID_CMD_LINE; + } + return pvs(cmd, argc, argv); + } else if (arg_count(cmd, aligned_ARG) || + arg_count(cmd, noheadings_ARG) || + arg_count(cmd, options_ARG) || + arg_count(cmd, separator_ARG) || + arg_count(cmd, sort_ARG) || arg_count(cmd, unbuffered_ARG)) { + log_error("Incompatible options selected"); + return EINVALID_CMD_LINE; + } if (arg_count(cmd, colon_ARG) && arg_count(cmd, maps_ARG)) { log_error("Option -v not allowed with option -c"); return EINVALID_CMD_LINE; } - if (argc) { - log_very_verbose("Using physical volume(s) on command line"); - - for (; opt < argc; opt++) { - if (!(pv = pv_read(cmd, argv[opt], NULL, NULL))) { - log_error("Failed to read physical " - "volume \"%s\"", argv[opt]); - continue; - } - pvdisplay_single(cmd, pv, NULL); - } - } else { - log_verbose("Scanning for physical volume names"); - if (!(pvs = get_pvs(cmd))) - return ECMD_FAILED; - - list_iterate(pvh, pvs) - pvdisplay_single(cmd, list_item(pvh, struct pv_list)->pv, - NULL); - } + process_each_pv(cmd, argc, argv, NULL, NULL, pvdisplay_single); return 0; } diff --git a/tools/pvscan.c b/tools/pvscan.c index 3f3b2f1ba..395a245ac 100644 --- a/tools/pvscan.c +++ b/tools/pvscan.c @@ -29,8 +29,6 @@ void pvscan_display_single(struct cmd_context *cmd, struct physical_volume *pv, char uuid[64]; int vg_name_len = 0; - char *s1, *s2; - char pv_tmp_name[NAME_LEN] = { 0, }; char vg_tmp_name[NAME_LEN] = { 0, }; char vg_name_this[NAME_LEN] = { 0, }; @@ -73,8 +71,7 @@ void pvscan_display_single(struct cmd_context *cmd, struct physical_volume *pv, pv_max_name_len, pv_tmp_name, vg_max_name_len, " ", pv->fmt ? pv->fmt->name : " ", - (s1 = display_size(pv->size / 2, SIZE_SHORT))); - dbg_free(s1); + display_size(cmd, pv->size / 2, SIZE_SHORT)); return; } @@ -83,14 +80,12 @@ void pvscan_display_single(struct cmd_context *cmd, struct physical_volume *pv, log_print("PV %-*s is in exported VG %s " "[%s / %s free]", pv_max_name_len, pv_tmp_name, - vg_name_this, (s1 = - display_size(pv->pe_count * - pv->pe_size / 2, - SIZE_SHORT)), - (s2 = display_size((pv->pe_count - pv->pe_alloc_count) - * pv->pe_size / 2, SIZE_SHORT))); - dbg_free(s1); - dbg_free(s2); + vg_name_this, + display_size(cmd, pv->pe_count * + pv->pe_size / 2, + SIZE_SHORT), + display_size(cmd, (pv->pe_count - pv->pe_alloc_count) + * pv->pe_size / 2, SIZE_SHORT)); return; } @@ -99,13 +94,10 @@ void pvscan_display_single(struct cmd_context *cmd, struct physical_volume *pv, ("PV %-*s VG %-*s %s [%s / %s free]", pv_max_name_len, pv_tmp_name, vg_max_name_len, vg_tmp_name, pv->fmt ? pv->fmt->name : " ", - (s1 = display_size(pv->pe_count * pv->pe_size / 2, SIZE_SHORT)), - (s2 = - display_size((pv->pe_count - pv->pe_alloc_count) * pv->pe_size / - 2, SIZE_SHORT))); - dbg_free(s1); - dbg_free(s2); - + display_size(cmd, pv->pe_count * pv->pe_size / 2, SIZE_SHORT), + display_size(cmd, + (pv->pe_count - pv->pe_alloc_count) * pv->pe_size / 2, + SIZE_SHORT)); return; } @@ -113,7 +105,6 @@ int pvscan(struct cmd_context *cmd, int argc, char **argv) { int new_pvs_found = 0; int pvs_found = 0; - char *s1, *s2, *s3; struct list *pvs; struct list *pvh; @@ -205,14 +196,10 @@ int pvscan(struct cmd_context *cmd, int argc, char **argv) log_print("Total: %d [%s] / in use: %d [%s] / in no VG: %d [%s]", pvs_found, - (s1 = display_size(size_total / 2, SIZE_SHORT)), + display_size(cmd, size_total / 2, SIZE_SHORT), pvs_found - new_pvs_found, - (s2 = - display_size((size_total - size_new) / 2, SIZE_SHORT)), - new_pvs_found, (s3 = display_size(size_new / 2, SIZE_SHORT))); - dbg_free(s1); - dbg_free(s2); - dbg_free(s3); + display_size(cmd, (size_total - size_new) / 2, SIZE_SHORT), + new_pvs_found, display_size(cmd, size_new / 2, SIZE_SHORT)); return 0; } diff --git a/tools/report.c b/tools/report.c new file mode 100644 index 000000000..21b4afc17 --- /dev/null +++ b/tools/report.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2001 Sistina Software + * + * LVM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * LVM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with LVM; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "tools.h" +#include "report.h" +#include "defaults.h" + +static int _vgs_single(struct cmd_context *cmd, const char *vg_name, + struct volume_group *vg, int consistent, void *handle) +{ + if (!vg) { + log_error("Volume group %s not found", vg_name); + return ECMD_FAILED; + } + + if (!report_object(handle, vg, NULL, NULL, NULL)) + return ECMD_FAILED; + + return 0; +} + +static int _lvs_single(struct cmd_context *cmd, struct logical_volume *lv, + void *handle) +{ + if (!report_object(handle, lv->vg, lv, NULL, NULL)) + return ECMD_FAILED; + + return 0; +} + +static int _segs_single(struct cmd_context *cmd, struct lv_segment *seg, + void *handle) +{ + if (!report_object(handle, seg->lv->vg, seg->lv, NULL, seg)) + return ECMD_FAILED; + + return 0; +} + +static int _lvsegs_single(struct cmd_context *cmd, struct logical_volume *lv, + void *handle) +{ + return process_each_segment_in_lv(cmd, lv, handle, _segs_single); +} + +static int _pvs_single(struct cmd_context *cmd, struct volume_group *vg, + struct physical_volume *pv, void *handle) +{ + int consistent = 0; + + if (!lock_vol(cmd, pv->vg_name, LCK_VG_READ)) { + log_error("Can't lock %s: skipping", pv->vg_name); + return 0; + } + vg = vg_read(cmd, pv->vg_name, &consistent); + + if (!report_object(handle, vg, NULL, pv, NULL)) + return ECMD_FAILED; + + unlock_vg(cmd, pv->vg_name); + + return 0; +} + +static int _report(struct cmd_context *cmd, int argc, char **argv, + report_type_t report_type) +{ + void *report_handle; + char *opts, *str; + const char *keys, *options, *separator; + + int aligned, buffered, headings; + + aligned = find_config_int(cmd->cf->root, "report/aligned", '/', + DEFAULT_REP_ALIGNED); + buffered = find_config_int(cmd->cf->root, "report/buffered", '/', + DEFAULT_REP_BUFFERED); + headings = find_config_int(cmd->cf->root, "report/headings", '/', + DEFAULT_REP_HEADINGS); + separator = find_config_str(cmd->cf->root, "report/separator", '/', + DEFAULT_REP_SEPARATOR); + + switch (report_type) { + case LVS: + keys = find_config_str(cmd->cf->root, "report/lvs_sort", '/', + DEFAULT_LVS_SORT); + if (!arg_count(cmd, verbose_ARG)) + options = find_config_str(cmd->cf->root, + "report/lvs_cols", '/', + DEFAULT_LVS_COLS); + else + options = find_config_str(cmd->cf->root, + "report/lvs_cols_verbose", + '/', DEFAULT_LVS_COLS_VERB); + break; + case VGS: + keys = find_config_str(cmd->cf->root, "report/vgs_sort", '/', + DEFAULT_VGS_SORT); + if (!arg_count(cmd, verbose_ARG)) + options = find_config_str(cmd->cf->root, + "report/vgs_cols", '/', + DEFAULT_VGS_COLS); + else + options = find_config_str(cmd->cf->root, + "report/vgs_cols_verbose", + '/', DEFAULT_VGS_COLS_VERB); + break; + case PVS: + keys = find_config_str(cmd->cf->root, "report/pvs_sort", '/', + DEFAULT_PVS_SORT); + if (!arg_count(cmd, verbose_ARG)) + options = find_config_str(cmd->cf->root, + "report/pvs_cols", '/', + DEFAULT_PVS_COLS); + else + options = find_config_str(cmd->cf->root, + "report/pvs_cols_verbose", + '/', DEFAULT_PVS_COLS_VERB); + break; + case SEGS: + keys = find_config_str(cmd->cf->root, "report/segs_sort", '/', + DEFAULT_SEGS_SORT); + if (!arg_count(cmd, verbose_ARG)) + options = find_config_str(cmd->cf->root, + "report/segs_cols", '/', + DEFAULT_SEGS_COLS); + else + options = find_config_str(cmd->cf->root, + "report/segs_cols_verbose", + '/', DEFAULT_SEGS_COLS_VERB); + break; + } + + /* If -o supplied use it, else use default for report_type */ + if (arg_count(cmd, options_ARG)) { + opts = arg_str_value(cmd, options_ARG, ""); + if (!opts || !*opts) { + log_error("Invalid options string: %s", opts); + return 0; + } + if (*opts == '+') { + *opts = ','; + str = + pool_alloc(cmd->mem, + strlen(options) + strlen(opts) + 1); + strcpy(str, options); + strcat(str, opts); + options = str; + } else + options = opts; + } + + /* -O overrides default sort settings */ + if (arg_count(cmd, sort_ARG)) + keys = arg_str_value(cmd, sort_ARG, ""); + + if (arg_count(cmd, separator_ARG)) + separator = arg_str_value(cmd, separator_ARG, " "); + 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 (!(report_handle = report_init(cmd, options, keys, &report_type, + separator, aligned, buffered, + headings))) + return 0; + + switch (report_type) { + case LVS: + process_each_lv(cmd, argc, argv, LCK_VG_READ, report_handle, + &_lvs_single); + break; + case VGS: + process_each_vg(cmd, argc, argv, LCK_VG_READ, 0, report_handle, + &_vgs_single); + break; + case PVS: + process_each_pv(cmd, argc, argv, NULL, report_handle, + &_pvs_single); + break; + case SEGS: + process_each_lv(cmd, argc, argv, LCK_VG_READ, report_handle, + &_lvsegs_single); + break; + } + + report_output(report_handle); + + report_free(report_handle); + return ECMD_PROCESSED; +} + +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) +{ + return _report(cmd, argc, argv, PVS); +} diff --git a/tools/toollib.c b/tools/toollib.c index 917b4ff05..80d0fbee3 100644 --- a/tools/toollib.c +++ b/tools/toollib.c @@ -202,6 +202,28 @@ int process_each_lv(struct cmd_context *cmd, int argc, char **argv, return ret_max; } +int process_each_segment_in_lv(struct cmd_context *cmd, + struct logical_volume *lv, + void *handle, + int (*process_single) (struct cmd_context * cmd, + struct lv_segment * seg, + void *handle)) +{ + struct list *segh; + struct lv_segment *seg; + int ret_max = 0; + int ret; + + list_iterate(segh, &lv->segments) { + seg = list_item(segh, struct lv_segment); + ret = process_single(cmd, seg, handle); + if (ret > ret_max) + ret_max = ret; + } + + return ret_max; +} + int process_each_vg(struct cmd_context *cmd, int argc, char **argv, int lock_type, int consistent, void *handle, int (*process_single) (struct cmd_context * cmd, @@ -303,23 +325,50 @@ int process_each_pv(struct cmd_context *cmd, int argc, char **argv, int ret = 0; struct pv_list *pvl; + struct physical_volume *pv; + struct list *pvs, *pvh; if (argc) { log_verbose("Using physical volume(s) on command line"); for (; opt < argc; opt++) { - if (!(pvl = find_pv_in_vg(vg, argv[opt]))) { - log_error("Physical Volume \"%s\" not found in " - "Volume Group \"%s\"", argv[opt], - vg->name); - continue; + if (vg) { + if (!(pvl = find_pv_in_vg(vg, argv[opt]))) { + log_error("Physical Volume \"%s\" not " + "found in Volume Group " + "\"%s\"", argv[opt], + vg->name); + continue; + } + pv = pvl->pv; + } else { + if (!(pv = pv_read(cmd, argv[opt], NULL, NULL))) { + log_error("Failed to read physical " + "volume \"%s\"", argv[opt]); + continue; + } } - ret = process_single(cmd, vg, pvl->pv, handle); + + ret = process_single(cmd, vg, pv, handle); if (ret > ret_max) ret_max = ret; } } else { - log_verbose("Using all physical volume(s) in volume group"); - process_each_pv_in_vg(cmd, vg, handle, process_single); + if (vg) { + log_verbose("Using all physical volume(s) in " + "volume group"); + process_each_pv_in_vg(cmd, vg, handle, process_single); + } else { + log_verbose("Scanning for physical volume names"); + if (!(pvs = get_pvs(cmd))) + return ECMD_FAILED; + + list_iterate(pvh, pvs) { + pv = list_item(pvh, struct pv_list)->pv; + ret = process_single(cmd, NULL, pv, handle); + if (ret > ret_max) + ret_max = ret; + } + } } return ret_max; diff --git a/tools/toollib.h b/tools/toollib.h index 8b5ef7ed7..9390ff3e0 100644 --- a/tools/toollib.h +++ b/tools/toollib.h @@ -35,10 +35,9 @@ struct volume_group *recover_vg(struct cmd_context *cmd, const char *vgname, int process_each_vg(struct cmd_context *cmd, int argc, char **argv, int lock_type, int consistent, void *handle, int (*process_single) (struct cmd_context * cmd, - const char *vg_name, - struct volume_group *vg, - int consistent, - void *handle)); + const char *vg_name, + struct volume_group * vg, + int consistent, void *handle)); int process_each_pv(struct cmd_context *cmd, int argc, char **argv, struct volume_group *vg, void *handle, @@ -53,6 +52,12 @@ int process_each_lv(struct cmd_context *cmd, int argc, char **argv, struct logical_volume * lv, void *handle)); +int process_each_segment_in_lv(struct cmd_context *cmd, + struct logical_volume *lv, void *handle, + int (*process_single) (struct cmd_context * cmd, + struct lv_segment * seg, + void *handle)); + int process_each_pv_in_vg(struct cmd_context *cmd, struct volume_group *vg, void *handle, int (*process_single) (struct cmd_context * cmd, diff --git a/tools/tools.h b/tools/tools.h index 9a16b6c47..7e5fc9b73 100644 --- a/tools/tools.h +++ b/tools/tools.h @@ -74,7 +74,8 @@ struct arg { int count; char *value; uint32_t i_value; - uint64_t i64_value; + int64_t i64_value; + uint64_t ui64_value; sign_t sign; void *ptr; }; @@ -102,6 +103,7 @@ int minor_arg(struct cmd_context *cmd, struct arg *a); int string_arg(struct cmd_context *cmd, struct arg *a); int permission_arg(struct cmd_context *cmd, struct arg *a); int metadatatype_arg(struct cmd_context *cmd, struct arg *a); +int units_arg(struct cmd_context *cmd, struct arg *a); char yes_no_prompt(const char *prompt, ...); @@ -127,12 +129,18 @@ static inline uint32_t arg_int_value(struct cmd_context *cmd, int a, return arg_count(cmd, a) ? cmd->args[a].i_value : def; } -static inline uint64_t arg_int64_value(struct cmd_context *cmd, int a, - uint64_t def) +static inline int64_t arg_int64_value(struct cmd_context *cmd, int a, + uint64_t def) { return arg_count(cmd, a) ? cmd->args[a].i64_value : def; } +static inline uint64_t arg_uint64_value(struct cmd_context *cmd, int a, + uint64_t def) +{ + return arg_count(cmd, a) ? cmd->args[a].ui64_value : def; +} + static inline void *arg_ptr_value(struct cmd_context *cmd, int a, void *def) { return arg_count(cmd, a) ? cmd->args[a].ptr : def; diff --git a/tools/vgck.c b/tools/vgck.c index 6a6eb1ec7..5cd9a8585 100644 --- a/tools/vgck.c +++ b/tools/vgck.c @@ -38,7 +38,6 @@ static int vgck_single(struct cmd_context *cmd, const char *vg_name, return ECMD_FAILED; } - /* FIXME: free */ return 0; } diff --git a/tools/vgconvert.c b/tools/vgconvert.c index 59043af73..5116b3829 100644 --- a/tools/vgconvert.c +++ b/tools/vgconvert.c @@ -63,7 +63,7 @@ static int vgconvert_single(struct cmd_context *cmd, const char *vg_name, } if (cmd->fmt->features & FMT_MDAS) { - pvmetadatasize = arg_int64_value(cmd, metadatasize_ARG, 0) * 2; + pvmetadatasize = arg_uint64_value(cmd, metadatasize_ARG, 0) * 2; if (!pvmetadatasize) pvmetadatasize = find_config_int(cmd->cf->root, diff --git a/tools/vgdisplay.c b/tools/vgdisplay.c index 72bc3b388..7a4e4beda 100644 --- a/tools/vgdisplay.c +++ b/tools/vgdisplay.c @@ -62,6 +62,23 @@ static int vgdisplay_single(struct cmd_context *cmd, const char *vg_name, int vgdisplay(struct cmd_context *cmd, int argc, char **argv) { + if (arg_count(cmd, columns_ARG)) { + if (arg_count(cmd, colon_ARG) || + arg_count(cmd, activevolumegroups_ARG) || + arg_count(cmd, short_ARG)) { + log_error("Incompatible options selected"); + return EINVALID_CMD_LINE; + } + return vgs(cmd, argc, argv); + } else if (arg_count(cmd, aligned_ARG) || + arg_count(cmd, noheadings_ARG) || + arg_count(cmd, options_ARG) || + arg_count(cmd, separator_ARG) || + arg_count(cmd, sort_ARG) || arg_count(cmd, unbuffered_ARG)) { + log_error("Incompatible options selected"); + return EINVALID_CMD_LINE; + } + if (arg_count(cmd, colon_ARG) && arg_count(cmd, short_ARG)) { log_error("Option -c is not allowed with option -s"); return EINVALID_CMD_LINE;