]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lsblk: add --merge
authorKarel Zak <kzak@redhat.com>
Fri, 7 Dec 2018 11:29:50 +0000 (12:29 +0100)
committerKarel Zak <kzak@redhat.com>
Fri, 7 Dec 2018 11:57:49 +0000 (12:57 +0100)
Signed-off-by: Karel Zak <kzak@redhat.com>
Documentation/TODO
misc-utils/lsblk-devtree.c
misc-utils/lsblk.8
misc-utils/lsblk.c
misc-utils/lsblk.h

index 118aa9a896fc2880676b39a4b1e26f4662e67db1..03e0a75cb66274e12668b7f9ab6473d2b0a9e9f4 100644 (file)
@@ -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.
index c38f3eb1eb6cb5883dd63383f145764ad10733d2..82db4f8cf3334846f6d0c6a5f0f2ed8ed56f9c57 100644 (file)
@@ -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()
 {
index d2a58ed085a680df2a321edb96ae33058d66b97e..2787569fed5108b207dd47862e712fd8b6784829 100644 (file)
@@ -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
index 95c3cdc4d4d6df0e1c445d42aa513f53357adb87..7ee6dff90ea167f07ae9dedbb6ecc2382cf7e22d 100644 (file)
@@ -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 <column> de-duplicate output by <column>\n"), out);
+       fputs(_(" -I, --include <list> 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 <list> 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 <list> 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 <column> de-duplicate output by <column>\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 <list>  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 <column>  sort output by <column>\n"), out);
        fputs(_("     --sysroot <dir>  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;
index 8f8a89dfd41b646e3594cd4b98a0a66b4018d48a..a043e7fd76ecce2dae8914c2f3dc56cfb78aa8c1 100644 (file)
@@ -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);