From: Karel Zak Date: Fri, 7 Dec 2018 11:29:50 +0000 (+0100) Subject: lsblk: add --merge X-Git-Tag: v2.34-rc1~195 X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Futil-linux.git;a=commitdiff_plain;h=0bd05f5ee4876ffd13c98acd56c2bff9971f28f1 lsblk: add --merge Signed-off-by: Karel Zak --- diff --git a/Documentation/TODO b/Documentation/TODO index 118aa9a896..03e0a75cb6 100644 --- a/Documentation/TODO +++ b/Documentation/TODO @@ -43,22 +43,6 @@ lsblk and it may be confusing for end-users when FS to DEV mapping is not 1:1 (RAIDs, btrfs, ...) - - add --merge option to merge the same trees - sdb 8:16 0 204M 0 disk - └─md127 9:127 0 611.5M 0 raid0 - sdc 8:32 0 204M 0 disk - └─md127 9:127 0 611.5M 0 raid0 - sdd 8:48 0 204M 0 disk - └─md127 9:127 0 611.5M 0 raid0 - to - sdb| 8:16 0 204M 0 disk - sdc| 8:32 0 204M 0 disk - sdd| 8:48 0 204M 0 disk - └─md127 9:127 0 611.5M 0 raid0 - - This feature will require change to libsmartcols to support "multi-parent" - output formatting (more than one parent). - nsenter(1) ---------- - read the default UID and GID from the target process. diff --git a/misc-utils/lsblk-devtree.c b/misc-utils/lsblk-devtree.c index c38f3eb1eb..82db4f8cf3 100644 --- a/misc-utils/lsblk-devtree.c +++ b/misc-utils/lsblk-devtree.c @@ -157,7 +157,7 @@ int lsblk_device_new_dependence(struct lsblk_device *parent, struct lsblk_device list_add_tail(&dp->ls_childs, &parent->childs); dp->parent = parent; - list_add_tail(&dp->ls_parents, &parent->parents); + list_add_tail(&dp->ls_parents, &child->parents); DBG(DEV, ul_debugobj(parent, "add dependence 0x%p [%s->%s]", dp, parent->name, child->name)); @@ -198,6 +198,38 @@ int lsblk_device_next_child(struct lsblk_device *dev, return rc; } +int lsblk_device_is_last_parent(struct lsblk_device *dev, struct lsblk_device *parent) +{ + struct lsblk_devdep *dp = list_last_entry( + &dev->parents, + struct lsblk_devdep, ls_parents); + + return dp->parent == parent; +} + +int lsblk_device_next_parent( + struct lsblk_device *dev, + struct lsblk_iter *itr, + struct lsblk_device **parent) +{ + int rc = 1; + + if (!dev || !itr || !parent) + return -EINVAL; + *parent = NULL; + + if (!itr->head) + LSBLK_ITER_INIT(itr, &dev->parents); + if (itr->p != itr->head) { + struct lsblk_devdep *dp = NULL; + LSBLK_ITER_ITERATE(itr, dp, struct lsblk_devdep, ls_parents); + if (dp) + *parent = dp->parent; + rc = 0; + } + + return rc; +} struct lsblk_devtree *lsblk_new_devtree() { diff --git a/misc-utils/lsblk.8 b/misc-utils/lsblk.8 index d2a58ed085..2787569fed 100644 --- a/misc-utils/lsblk.8 +++ b/misc-utils/lsblk.8 @@ -51,13 +51,18 @@ Print the SIZE column in bytes rather than in a human-readable format. .BR \-D , " \-\-discard" Print information about the discarding capabilities (TRIM, UNMAP) for each device. .TP -.BR \-z , " \-\-zoned" -Print the zone model for each device. -.TP .BR \-d , " \-\-nodeps" Do not print holder devices or slaves. For example, \fBlsblk --nodeps /dev/sda\fR prints information about the sda device only. .TP +.BR \-E , " \-\-dedup " \fIcolumn\fP +Use \fIcolumn\fP as a de-duplication key to de-duplicate output tree. If the +key is not available for the device, or the device is a partition and parental +whole-disk device provides the same key than the device is always printed. + +The usual use case is to de-duplicate output on system multi-path devices, for +example by \fB\-E WWN\fR. +.TP .BR \-e , " \-\-exclude " \fIlist\fP Exclude the devices specified by the comma-separated \fIlist\fR of major device numbers. Note that RAM disks (major=1) are excluded by default if \fB\-\-all\fR is no specified. @@ -86,15 +91,13 @@ Use ASCII characters for tree formatting. Use JSON output format. .TP .BR \-l , " \-\-list" -Produce output in the form of a list. +Produce output in the form of a list. The output does not provide information +about relationships between devices and since version 2.34 every device is +printed only once. .TP -.BR \-M , " \-\-dedup " \fIcolumn\fP -Use \fIcolumn\fP as a de-duplication key to de-duplicate output tree. If the -key is not available for the device, or the device is a partition and parental -whole-disk device provides the same key than the device is always printed. - -The usual use case is to de-duplicate output on system multi-path devices, for -example by \fB\-M WWN\fR. +.BR \-M , " \-\-merge" +Group parents of sub-trees to provide more readable output for RAIDs and +Multi-path devices. The tree-like output is required. .TP .BR \-m , " \-\-perms" Output info about device owner, group and mode. This option is equivalent to @@ -145,6 +148,9 @@ Sort output lines by \fIcolumn\fP. This option enables \fB\-\-list\fR output for It is possible to use the option \fI\-\-tree\fP to force tree-like output and than the tree branches are sorted by the \fIcolumn\fP. .TP +.BR \-z , " \-\-zoned" +Print the zone model for each device. +.TP .BR " \-\-sysroot " \fIdirectory\fP Gather data for a Linux instance other than the instance from which the lsblk command is issued. The specified directory is the system root of the Linux diff --git a/misc-utils/lsblk.c b/misc-utils/lsblk.c index 95c3cdc4d4..7ee6dff90e 100644 --- a/misc-utils/lsblk.c +++ b/misc-utils/lsblk.c @@ -141,7 +141,6 @@ struct colinfo { double whint; /* width hint (N < 1 is in percent of termwidth) */ int flags; /* SCOLS_FL_* */ const char *help; - int type; /* COLTYPE_* */ }; @@ -217,7 +216,6 @@ struct lsblk *lsblk; /* global handler */ static int columns[ARRAY_SIZE(infos) * 2]; static size_t ncolumns; - static inline void add_column(int id) { if (ncolumns >= ARRAY_SIZE(columns)) @@ -405,7 +403,7 @@ static char *get_type(struct lsblk_device *dev) { char *res = NULL, *p; - if (cxt->partition) + if (device_is_partition(dev)) return xstrdup("part"); if (is_dm(dev->name)) { @@ -701,7 +699,6 @@ static int is_removable_device(struct lsblk_device *dev, struct lsblk_device *pa /* parent is something else, use sysfs parent */ ul_path_scanf(pc, "removable", "%d", &dev->removable); } - done: if (dev->removable == -1) dev->removable = 0; @@ -718,7 +715,6 @@ static uint64_t device_get_discard_granularity(struct lsblk_device *dev) return dev->discard_granularity; } - /* * Generates data (string) for column specified by column ID for specified device. If sortdata * is not NULL then returns number usable to sort the column if the data are available for the @@ -1032,13 +1028,42 @@ static void device_to_scols( struct libscols_line *ln; struct lsblk_iter itr; struct lsblk_device *child = NULL; + int link_group = 0; ON_DBG(DEV, if (ul_path_isopen_dirfd(dev->sysfs)) ul_debugobj(dev, "%s ---> is open!", dev->name)); - ln = scols_table_new_line(tab, parent_line); + /* Do not print device more than one in --list mode */ + if (!(lsblk->flags & LSBLK_TREE) && dev->is_printed) + return; + + if (lsblk->merge && list_count_entries(&dev->parents) > 1) { + if (!lsblk_device_is_last_parent(dev, parent)) + return; + link_group = 1; + } + + ln = scols_table_new_line(tab, link_group ? NULL : parent_line); if (!ln) err(EXIT_FAILURE, _("failed to allocate output line")); + dev->is_printed = 1; + + if (link_group) { + struct lsblk_device *p; + struct libscols_line *gr = parent_line; + + /* Merge all my parents to the one group */ + lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD); + while (lsblk_device_next_parent(dev, &itr, &p) == 0) { + if (!p->scols_line) + continue; + scols_table_group_lines(tab, gr, p->scols_line, 0); + } + + /* Link the group -- this makes group->child connection */ + scols_line_link_group(ln, gr, 0); + } + /* read column specific data and set it to smartcols table line */ for (i = 0; i < ncolumns; i++) { char *data; @@ -1057,13 +1082,15 @@ static void device_to_scols( err(EXIT_FAILURE, _("failed to add output data")); } + dev->scols_line = ln; + if (dev->npartitions == 0) /* For partitions we often read from parental whole-disk sysfs, * otherwise we can close */ ul_path_close_dirfd(dev->sysfs); - lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD); + lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD); while (lsblk_device_next_child(dev, &itr, &child) == 0) device_to_scols(child, dev, tab, ln); @@ -1547,6 +1574,7 @@ static int process_all_devices(struct lsblk_devtree *tr) DBG(DEV, ul_debug(" %s: ignore (in-middle)", d->d_name)); goto next; } + lsblk_devtree_add_root(tr, dev); process_dependencies(tr, dev, 1); next: @@ -1690,29 +1718,30 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_("List information about block devices.\n"), out); fputs(USAGE_OPTIONS, out); + fputs(_(" -D, --discard print discard capabilities\n"), out); + fputs(_(" -E, --dedup de-duplicate output by \n"), out); + fputs(_(" -I, --include show only devices with specified major numbers\n"), out); + fputs(_(" -J, --json use JSON output format\n"), out); + fputs(_(" -O, --output-all output all columns\n"), out); + fputs(_(" -P, --pairs use key=\"value\" output format\n"), out); + fputs(_(" -S, --scsi output info about SCSI devices\n"), out); + fputs(_(" -T, --tree use tree format output\n"), out); fputs(_(" -a, --all print all devices\n"), out); fputs(_(" -b, --bytes print SIZE in bytes rather than in human readable format\n"), out); fputs(_(" -d, --nodeps don't print slaves or holders\n"), out); - fputs(_(" -D, --discard print discard capabilities\n"), out); - fputs(_(" -z, --zoned print zone model\n"), out); fputs(_(" -e, --exclude exclude devices by major number (default: RAM disks)\n"), out); fputs(_(" -f, --fs output info about filesystems\n"), out); fputs(_(" -i, --ascii use ascii characters only\n"), out); - fputs(_(" -I, --include show only devices with specified major numbers\n"), out); - fputs(_(" -J, --json use JSON output format\n"), out); fputs(_(" -l, --list use list format output\n"), out); - fputs(_(" -T, --tree use tree format output\n"), out); - fputs(_(" -M, --dedup de-duplicate output by \n"), out); + fputs(_(" -M, --merge group parents of sub-trees (usable for RAIDs, Multi-path)\n"), out); fputs(_(" -m, --perms output info about permissions\n"), out); fputs(_(" -n, --noheadings don't print headings\n"), out); fputs(_(" -o, --output output columns\n"), out); - fputs(_(" -O, --output-all output all columns\n"), out); fputs(_(" -p, --paths print complete device path\n"), out); - fputs(_(" -P, --pairs use key=\"value\" output format\n"), out); fputs(_(" -r, --raw use raw output format\n"), out); fputs(_(" -s, --inverse inverse dependencies\n"), out); - fputs(_(" -S, --scsi output info about SCSI devices\n"), out); fputs(_(" -t, --topology output info about topology\n"), out); + fputs(_(" -z, --zoned print zone model\n"), out); fputs(_(" -x, --sort sort output by \n"), out); fputs(_(" --sysroot use specified directory as system root\n"), out); fputs(USAGE_SEPARATOR, out); @@ -1757,12 +1786,13 @@ int main(int argc, char *argv[]) { "bytes", no_argument, NULL, 'b' }, { "nodeps", no_argument, NULL, 'd' }, { "discard", no_argument, NULL, 'D' }, - { "dedup", required_argument, NULL, 'M' }, + { "dedup", required_argument, NULL, 'E' }, { "zoned", no_argument, NULL, 'z' }, { "help", no_argument, NULL, 'h' }, { "json", no_argument, NULL, 'J' }, { "output", required_argument, NULL, 'o' }, { "output-all", no_argument, NULL, 'O' }, + { "merge", no_argument, NULL, 'M' }, { "perms", no_argument, NULL, 'm' }, { "noheadings", no_argument, NULL, 'n' }, { "list", no_argument, NULL, 'l' }, @@ -1806,7 +1836,7 @@ int main(int argc, char *argv[]) lsblk_init_debug(); while((c = getopt_long(argc, argv, - "abdDze:fhJlnmM:o:OpPiI:rstVSTx:", longopts, NULL)) != -1) { + "abdDzE:e:fhJlnMmo:OpPiI:rstVSTx:", longopts, NULL)) != -1) { err_exclusive_options(c, longopts, excl, excl_st); @@ -1843,6 +1873,9 @@ int main(int argc, char *argv[]) case 'l': lsblk->flags &= ~LSBLK_TREE; /* disable the default */ break; + case 'M': + lsblk->merge = 1; + break; case 'n': lsblk->flags |= LSBLK_NOHEADINGS; break; @@ -1923,7 +1956,7 @@ int main(int argc, char *argv[]) case 'V': printf(UTIL_LINUX_VERSION); return EXIT_SUCCESS; - case 'M': + case 'E': lsblk->dedup_id = column_name_to_id(optarg, strlen(optarg)); if (lsblk->dedup_id >= 0) break; diff --git a/misc-utils/lsblk.h b/misc-utils/lsblk.h index 8f8a89dfd4..a043e7fd76 100644 --- a/misc-utils/lsblk.h +++ b/misc-utils/lsblk.h @@ -45,6 +45,7 @@ struct lsblk { unsigned int all_devices:1; /* print all devices, including empty */ unsigned int bytes:1; /* print SIZE in bytes */ unsigned int inverse:1; /* print inverse dependencies */ + unsigned int merge:1; /* merge sub-trees */ unsigned int nodeps:1; /* don't print slaves/holders */ unsigned int scsi:1; /* print only device with HCTL (SCSI) */ unsigned int paths:1; /* print devnames with "/dev" prefix */ @@ -93,6 +94,8 @@ struct lsblk_device { struct lsblk_device *wholedisk; /* for partitions */ + struct libscols_line *scols_line; + struct lsblk_devprop *properties; struct stat st; @@ -120,6 +123,7 @@ struct lsblk_device { unsigned int is_mounted : 1, is_swap : 1, + is_printed : 1, udev_requested : 1, blkid_requested : 1; }; @@ -196,6 +200,12 @@ int lsblk_device_next_child(struct lsblk_device *dev, struct lsblk_iter *itr, struct lsblk_device **child); +int lsblk_device_is_last_parent(struct lsblk_device *dev, struct lsblk_device *parent); +int lsblk_device_next_parent( + struct lsblk_device *dev, + struct lsblk_iter *itr, + struct lsblk_device **parent); + struct lsblk_devtree *lsblk_new_devtree(void); void lsblk_ref_devtree(struct lsblk_devtree *tr); void lsblk_unref_devtree(struct lsblk_devtree *tr);