X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=misc-utils%2Flsblk.c;h=e95af7af0256ec85ad063ba3e3f9669d4e92e81d;hb=HEAD;hp=ab4c42179b599e2226ed92f090f88fce7e0c52ff;hpb=41c499cb33ea572d5c3ba499634335a400ff4dde;p=thirdparty%2Futil-linux.git diff --git a/misc-utils/lsblk.c b/misc-utils/lsblk.c index ab4c42179b..2df7752670 100644 --- a/misc-utils/lsblk.c +++ b/misc-utils/lsblk.c @@ -2,7 +2,7 @@ * lsblk(8) - list block devices * * Copyright (C) 2010-2018 Red Hat, Inc. All rights reserved. - * Written by Milan Broz + * Written by Milan Broz * Karel Zak * * This program is free software; you can redistribute it and/or modify @@ -52,6 +52,8 @@ #include "fileutils.h" #include "loopdev.h" #include "buffer.h" +#include "colors.h" +#include "column-list-table.h" #include "lsblk.h" @@ -66,9 +68,12 @@ static int column_id_to_number(int id); /* column IDs */ enum { COL_ALIOFF = 0, + COL_IDLINK, + COL_ID, COL_DALIGN, COL_DAX, COL_DGRAN, + COL_DISKSEQ, COL_DMAX, COL_DZERO, COL_FSAVAIL, @@ -85,14 +90,18 @@ enum { COL_LABEL, COL_LOGSEC, COL_MAJMIN, + COL_MAJ, + COL_MIN, COL_MINIO, COL_MODE, COL_MODEL, + COL_MQ, COL_NAME, COL_OPTIO, COL_OWNER, COL_PARTFLAGS, COL_PARTLABEL, + COL_PARTN, COL_PARTTYPE, COL_PARTTYPENAME, COL_PARTUUID, @@ -153,7 +162,7 @@ enum { /* column names */ struct colinfo { - const char *name; /* header */ + const char * const name; /* header */ double whint; /* width hint (N < 1 is in percent of termwidth) */ int flags; /* SCOLS_FL_* */ const char *help; @@ -161,18 +170,21 @@ struct colinfo { }; /* columns descriptions */ -static struct colinfo infos[] = { +static const struct colinfo infos[] = { [COL_ALIOFF] = { "ALIGNMENT", 6, SCOLS_FL_RIGHT, N_("alignment offset"), COLTYPE_NUM }, + [COL_ID] = { "ID", 0.1, SCOLS_FL_NOEXTREMES, N_("udev ID (based on ID-LINK)") }, + [COL_IDLINK] = { "ID-LINK", 0.1, SCOLS_FL_NOEXTREMES, N_("the shortest udev /dev/disk/by-id link name") }, [COL_DALIGN] = { "DISC-ALN", 6, SCOLS_FL_RIGHT, N_("discard alignment offset"), COLTYPE_NUM }, [COL_DAX] = { "DAX", 1, SCOLS_FL_RIGHT, N_("dax-capable device"), COLTYPE_BOOL }, - [COL_DGRAN] = { "DISC-GRAN", 6, SCOLS_FL_RIGHT, N_("discard granularity"), COLTYPE_SIZE }, - [COL_DMAX] = { "DISC-MAX", 6, SCOLS_FL_RIGHT, N_("discard max bytes"), COLTYPE_SIZE }, + [COL_DGRAN] = { "DISC-GRAN", 6, SCOLS_FL_RIGHT, N_("discard granularity, use if --bytes is given"), COLTYPE_SIZE }, + [COL_DISKSEQ] = { "DISK-SEQ", 1, SCOLS_FL_RIGHT, N_("disk sequence number"), COLTYPE_NUM }, + [COL_DMAX] = { "DISC-MAX", 6, SCOLS_FL_RIGHT, N_("discard max bytes, use if --bytes is given"), COLTYPE_SIZE }, [COL_DZERO] = { "DISC-ZERO", 1, SCOLS_FL_RIGHT, N_("discard zeroes data"), COLTYPE_BOOL }, - [COL_FSAVAIL] = { "FSAVAIL", 5, SCOLS_FL_RIGHT, N_("filesystem size available"), COLTYPE_SIZE }, + [COL_FSAVAIL] = { "FSAVAIL", 5, SCOLS_FL_RIGHT, N_("filesystem size available for unprivileged users, use if --bytes is given"), COLTYPE_SIZE }, [COL_FSROOTS] = { "FSROOTS", 0.1, SCOLS_FL_WRAP, N_("mounted filesystem roots") }, - [COL_FSSIZE] = { "FSSIZE", 5, SCOLS_FL_RIGHT, N_("filesystem size"), COLTYPE_SIZE }, + [COL_FSSIZE] = { "FSSIZE", 5, SCOLS_FL_RIGHT, N_("filesystem size, use if --bytes is given"), COLTYPE_SIZE }, [COL_FSTYPE] = { "FSTYPE", 0.1, SCOLS_FL_TRUNC, N_("filesystem type") }, - [COL_FSUSED] = { "FSUSED", 5, SCOLS_FL_RIGHT, N_("filesystem size used"), COLTYPE_SIZE }, + [COL_FSUSED] = { "FSUSED", 5, SCOLS_FL_RIGHT, N_("filesystem size used, use if --bytes is given"), COLTYPE_SIZE }, [COL_FSUSEPERC] = { "FSUSE%", 3, SCOLS_FL_RIGHT, N_("filesystem use percentage") }, [COL_FSVERSION] = { "FSVER", 0.1, SCOLS_FL_TRUNC, N_("filesystem version") }, [COL_GROUP] = { "GROUP", 0.1, SCOLS_FL_TRUNC, N_("group name") }, @@ -182,14 +194,18 @@ static struct colinfo infos[] = { [COL_LABEL] = { "LABEL", 0.1, 0, N_("filesystem LABEL") }, [COL_LOGSEC] = { "LOG-SEC", 7, SCOLS_FL_RIGHT, N_("logical sector size"), COLTYPE_NUM }, [COL_MAJMIN] = { "MAJ:MIN", 6, 0, N_("major:minor device number"), COLTYPE_SORTNUM }, + [COL_MAJ] = { "MAJ", 3, 0, N_("major device number"), COLTYPE_SORTNUM }, + [COL_MIN] = { "MIN", 3, 0, N_("minor device number"), COLTYPE_SORTNUM }, [COL_MINIO] = { "MIN-IO", 6, SCOLS_FL_RIGHT, N_("minimum I/O size"), COLTYPE_NUM }, [COL_MODEL] = { "MODEL", 0.1, SCOLS_FL_TRUNC, N_("device identifier") }, [COL_MODE] = { "MODE", 10, 0, N_("device node permissions") }, + [COL_MQ] = { "MQ", 3, SCOLS_FL_RIGHT, N_("device queues") }, [COL_NAME] = { "NAME", 0.25, SCOLS_FL_NOEXTREMES, N_("device name") }, [COL_OPTIO] = { "OPT-IO", 6, SCOLS_FL_RIGHT, N_("optimal I/O size"), COLTYPE_NUM }, [COL_OWNER] = { "OWNER", 0.1, SCOLS_FL_TRUNC, N_("user name"), }, [COL_PARTFLAGS] = { "PARTFLAGS", 36, 0, N_("partition flags") }, [COL_PARTLABEL] = { "PARTLABEL", 0.1, 0, N_("partition LABEL") }, + [COL_PARTN] = { "PARTN", 2, SCOLS_FL_RIGHT, N_("partition number as read from the partition table"), COLTYPE_NUM }, [COL_PARTTYPENAME] = { "PARTTYPENAME", 0.1, 0, N_("partition type name") }, [COL_PARTTYPE] = { "PARTTYPE", 36, 0, N_("partition type code or UUID") }, [COL_PARTUUID] = { "PARTUUID", 36, 0, N_("partition UUID") }, @@ -207,27 +223,33 @@ static struct colinfo infos[] = { [COL_RQ_SIZE]= { "RQ-SIZE", 5, SCOLS_FL_RIGHT, N_("request queue size"), COLTYPE_NUM }, [COL_SCHED] = { "SCHED", 0.1, 0, N_("I/O scheduler name") }, [COL_SERIAL] = { "SERIAL", 0.1, SCOLS_FL_TRUNC, N_("disk serial number") }, - [COL_SIZE] = { "SIZE", 5, SCOLS_FL_RIGHT, N_("size of the device"), COLTYPE_SIZE }, - [COL_START] = { "START", 5, SCOLS_FL_RIGHT, N_("partition start offset"), COLTYPE_NUM }, + [COL_SIZE] = { "SIZE", 5, SCOLS_FL_RIGHT, N_("size of the device, use if --bytes is given"), COLTYPE_SIZE }, + [COL_START] = { "START", 5, SCOLS_FL_RIGHT, N_("partition start offset (in 512-byte sectors)"), COLTYPE_NUM }, [COL_STATE] = { "STATE", 7, SCOLS_FL_TRUNC, N_("state of the device") }, [COL_SUBSYS] = { "SUBSYSTEMS", 0.1, SCOLS_FL_NOEXTREMES, N_("de-duplicated chain of subsystems") }, - [COL_TARGETS] = { "MOUNTPOINTS", 0.10, SCOLS_FL_WRAP, N_("all locations where device is mounted") }, - [COL_TARGET] = { "MOUNTPOINT", 0.10, SCOLS_FL_TRUNC, N_("where the device is mounted") }, + [COL_TARGETS] = { "MOUNTPOINTS", 0.10, SCOLS_FL_WRAP | SCOLS_FL_NOEXTREMES, N_("all locations where device is mounted") }, + [COL_TARGET] = { "MOUNTPOINT", 0.10, SCOLS_FL_TRUNC | SCOLS_FL_NOEXTREMES, N_("where the device is mounted") }, [COL_TRANSPORT] = { "TRAN", 6, 0, N_("device transport type") }, [COL_TYPE] = { "TYPE", 4, 0, N_("device type") }, [COL_UUID] = { "UUID", 36, 0, N_("filesystem UUID") }, [COL_VENDOR] = { "VENDOR", 0.1, SCOLS_FL_TRUNC, N_("device vendor") }, - [COL_WSAME] = { "WSAME", 6, SCOLS_FL_RIGHT, N_("write same max bytes"), COLTYPE_SIZE }, + [COL_WSAME] = { "WSAME", 6, SCOLS_FL_RIGHT, N_("write same max bytes, use if --bytes is given"), COLTYPE_SIZE }, [COL_WWN] = { "WWN", 18, 0, N_("unique storage identifier") }, [COL_ZONED] = { "ZONED", 0.3, 0, N_("zone model") }, - [COL_ZONE_SZ] = { "ZONE-SZ", 9, SCOLS_FL_RIGHT, N_("zone size"), COLTYPE_SIZE }, - [COL_ZONE_WGRAN] = { "ZONE-WGRAN", 10, SCOLS_FL_RIGHT, N_("zone write granularity"), COLTYPE_SIZE }, - [COL_ZONE_APP] = { "ZONE-APP", 11, SCOLS_FL_RIGHT, N_("zone append max bytes"), COLTYPE_SIZE }, + [COL_ZONE_SZ] = { "ZONE-SZ", 9, SCOLS_FL_RIGHT, N_("zone size, use if --bytes is given"), COLTYPE_SIZE }, + [COL_ZONE_WGRAN] = { "ZONE-WGRAN", 10, SCOLS_FL_RIGHT, N_("zone write granularity, use if --bytes is given"), COLTYPE_SIZE }, + [COL_ZONE_APP] = { "ZONE-APP", 11, SCOLS_FL_RIGHT, N_("zone append max bytes, use if --bytes is given"), COLTYPE_SIZE }, [COL_ZONE_NR] = { "ZONE-NR", 8, SCOLS_FL_RIGHT, N_("number of zones"), COLTYPE_NUM }, [COL_ZONE_OMAX] = { "ZONE-OMAX", 10, SCOLS_FL_RIGHT, N_("maximum number of open zones"), COLTYPE_NUM }, [COL_ZONE_AMAX] = { "ZONE-AMAX", 10, SCOLS_FL_RIGHT, N_("maximum number of active zones"), COLTYPE_NUM }, }; +/* "userdata" used by callback for libsmartcols filter */ +struct filler_data { + struct lsblk_device *dev; + struct lsblk_device *parent; +}; + struct lsblk *lsblk; /* global handler */ /* @@ -314,7 +336,7 @@ static int get_column_id(int num) } /* Returns column description for the column sequential number */ -static struct colinfo *get_column_info(int num) +static const struct colinfo *get_column_info(int num) { return &infos[ get_column_id(num) ]; } @@ -324,12 +346,30 @@ static int column_name_to_id(const char *name, size_t namesz) { size_t i; + /* name as diplayed for users */ for (i = 0; i < ARRAY_SIZE(infos); i++) { const char *cn = infos[i].name; if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) return i; } + + /* name as used in expressions, JSON output etc. */ + if (strnchr(name, namesz, '_')) { + char *buf = NULL; + size_t bufsz = 0; + + for (i = 0; i < ARRAY_SIZE(infos); i++) { + if (scols_shellvar_name(infos[i].name, &buf, &bufsz) != 0) + continue; + if (!strncasecmp(name, buf, namesz) && !*(buf + namesz)) { + free(buf); + return i; + } + } + free(buf); + } + warnx(_("unknown column: %s"), name); return -1; } @@ -461,7 +501,7 @@ static char *get_type(struct lsblk_device *dev) } /* Thanks to lsscsi code for idea of detection logic used here */ -static char *get_transport(struct lsblk_device *dev) +static const char *get_transport(struct lsblk_device *dev) { struct path_cxt *sysfs = dev->sysfs; char *attr = NULL; @@ -510,10 +550,14 @@ static char *get_transport(struct lsblk_device *dev) trans = "ata"; free(attr); - } else if (strncmp(dev->name, "nvme", 4) == 0) + } else if (strncmp(dev->name, "nvme", 4) == 0) { trans = "nvme"; + } else if (strncmp(dev->name, "vd", 2) == 0) + trans = "virtio"; + else if (strncmp(dev->name, "mmcblk", 6) == 0) + trans = "mmc"; - return trans ? xstrdup(trans) : NULL; + return trans; } static char *get_subsystems(struct lsblk_device *dev) @@ -583,7 +627,7 @@ static char *mk_dm_name(const char *name) /* stores data to scols cell userdata (invisible and independent on output) * to make the original values accessible for sort functions */ -static void set_sortdata_u64(struct libscols_line *ln, int col, uint64_t x) +static void set_rawdata_u64(struct libscols_line *ln, int col, uint64_t x) { struct libscols_cell *ce = scols_line_get_cell(ln, col); uint64_t *data; @@ -611,22 +655,37 @@ static void str2u64(const char *str, uint64_t *data) *data = num; } -static void unref_sortdata(struct libscols_table *tb) +static void unref_line_rawdata(struct libscols_line *ln, struct libscols_table *tb) +{ + size_t i; + + for (i = 0; i < ncolumns; i++) { + struct libscols_column *cl = scols_table_get_column(tb, i); + struct libscols_cell *ce; + void *data; + + if (cl != lsblk->sort_col && !scols_column_has_data_func(cl)) + continue; + + ce = scols_line_get_column_cell(ln, cl); + data = scols_cell_get_userdata(ce); + free(data); + } +} + +static void unref_table_rawdata(struct libscols_table *tb) { struct libscols_iter *itr; struct libscols_line *ln; - if (!tb || !lsblk->sort_col) + if (!tb || !lsblk->rawdata) return; + itr = scols_new_iter(SCOLS_ITER_FORWARD); if (!itr) return; - while (scols_table_next_line(tb, itr, &ln) == 0) { - struct libscols_cell *ce = scols_line_get_column_cell(ln, - lsblk->sort_col); - void *data = scols_cell_get_userdata(ce); - free(data); - } + while (scols_table_next_line(tb, itr, &ln) == 0) + unref_line_rawdata(ln, tb); scols_free_iter(itr); } @@ -689,21 +748,20 @@ static int is_removable_device(struct lsblk_device *dev, struct lsblk_device *pa if (dev->removable != -1) goto done; - if (ul_path_scanf(dev->sysfs, "removable", "%d", &dev->removable) == 1) - goto done; - if (parent) { + dev->removable = sysfs_blkdev_is_removable(dev->sysfs); + + if (!dev->removable && parent) { pc = sysfs_blkdev_get_parent(dev->sysfs); if (!pc) goto done; - /* dev is partition and parent is whole-disk */ if (pc == parent->sysfs) + /* dev is partition and parent is whole-disk */ dev->removable = is_removable_device(parent, NULL); - - /* parent is something else, use sysfs parent */ - else if (ul_path_scanf(pc, "removable", "%d", &dev->removable) != 1) - dev->removable = 0; + else + /* parent is something else, use sysfs parent */ + dev->removable = sysfs_blkdev_is_removable(pc); } done: if (dev->removable == -1) @@ -722,26 +780,43 @@ static uint64_t device_get_discard_granularity(struct lsblk_device *dev) } static void device_read_bytes(struct lsblk_device *dev, char *path, char **str, - uint64_t *sortdata) + uint64_t *rawdata) { uint64_t x; if (lsblk->bytes) { ul_path_read_string(dev->sysfs, str, path); - if (sortdata) - str2u64(*str, sortdata); + if (rawdata) + str2u64(*str, rawdata); return; } if (ul_path_read_u64(dev->sysfs, &x, path) == 0) { *str = size_to_human_string(SIZE_SUFFIX_1LETTER, x); - if (sortdata) - *sortdata = x; + if (rawdata) + *rawdata = x; } } +static void process_mq(struct lsblk_device *dev, char **str) +{ + unsigned int queues = 0; + + DBG(DEV, ul_debugobj(dev, "%s: process mq", dev->name)); + + queues = ul_path_count_dirents(dev->sysfs, "mq"); + if (!queues) { + *str = xstrdup("1"); + DBG(DEV, ul_debugobj(dev, "%s: no mq supported, use a single queue", dev->name)); + return; + } + + DBG(DEV, ul_debugobj(dev, "%s: has %d queues", dev->name, queues)); + xasprintf(str, "%3u", queues); +} + /* - * Generates data (string) for column specified by column ID for specified device. If sortdata + * Generates data (string) for column specified by column ID for specified device. If rawdata * is not NULL then returns number usable to sort the column if the data are available for the * column. */ @@ -749,7 +824,8 @@ static char *device_get_data( struct lsblk_device *dev, /* device */ struct lsblk_device *parent, /* device parent as defined in the tree */ int id, /* column ID (COL_*) */ - uint64_t *sortdata) /* returns sort data as number */ + uint64_t *rawdata, /* returns sort data as number */ + size_t *datasiz) { struct lsblk_devprop *prop = NULL; char *str = NULL; @@ -811,8 +887,18 @@ static char *device_get_data( xasprintf(&str, "%u:%u", dev->maj, dev->min); else xasprintf(&str, "%3u:%-3u", dev->maj, dev->min); - if (sortdata) - *sortdata = makedev(dev->maj, dev->min); + if (rawdata) + *rawdata = makedev(dev->maj, dev->min); + break; + case COL_MAJ: + xasprintf(&str, "%u", dev->maj); + if (rawdata) + *rawdata = dev->maj; + break; + case COL_MIN: + xasprintf(&str, "%u", dev->min); + if (rawdata) + *rawdata = dev->min; break; case COL_FSTYPE: prop = lsblk_device_get_properties(dev); @@ -849,10 +935,9 @@ static char *device_get_data( ul_buffer_append_string(&buf, "[SWAP]"); else ul_buffer_append_string(&buf, mnt_fs_get_target(fs)); - if (i + 1 < n) - ul_buffer_append_data(&buf, "\n", 1); + ul_buffer_append_data(&buf, "\0", 1); } - str = ul_buffer_get_data(&buf, NULL, NULL); + str = ul_buffer_get_data(&buf, datasiz, NULL); break; } case COL_FSROOTS: @@ -867,10 +952,9 @@ static char *device_get_data( if (mnt_fs_is_swaparea(fs)) continue; ul_buffer_append_string(&buf, root ? root : "/"); - if (i + 1 < n) - ul_buffer_append_data(&buf, "\n", 1); + ul_buffer_append_data(&buf, "\0", 1); } - str = ul_buffer_get_data(&buf, NULL, NULL); + str = ul_buffer_get_data(&buf, datasiz, NULL); break; } case COL_LABEL: @@ -922,15 +1006,33 @@ static char *device_get_data( if (prop && prop->partflags) str = xstrdup(prop->partflags); break; + case COL_PARTN: + prop = lsblk_device_get_properties(dev); + if (prop && prop->partn) + str = xstrdup(prop->partn); + break; case COL_WWN: prop = lsblk_device_get_properties(dev); if (prop && prop->wwn) str = xstrdup(prop->wwn); break; + case COL_IDLINK: + prop = lsblk_device_get_properties(dev); + if (prop && prop->idlink) + str = xstrdup(prop->idlink); + break; + case COL_ID: + prop = lsblk_device_get_properties(dev); + if (prop && prop->idlink) { + /* skip bus/subsystem prefix */ + const char *p = strchr(prop->idlink, '-'); + str = p && *(p + 1) ? xstrdup(p+1) : xstrdup(prop->idlink); + } + break; case COL_RA: ul_path_read_string(dev->sysfs, &str, "queue/read_ahead_kb"); - if (sortdata) - str2u64(str, sortdata); + if (rawdata) + str2u64(str, rawdata); break; case COL_RO: str = xstrdup(is_readonly_device(dev) ? "1" : "0"); @@ -966,8 +1068,13 @@ static char *device_get_data( } break; case COL_REV: - if (!device_is_partition(dev) && dev->nslaves == 0) - ul_path_read_string(dev->sysfs, &str, "device/rev"); + if (!device_is_partition(dev) && dev->nslaves == 0) { + prop = lsblk_device_get_properties(dev); + if (prop && prop->revision) + str = xstrdup(prop->revision); + else + ul_path_read_string(dev->sysfs, &str, "device/rev"); + } break; case COL_VENDOR: if (!device_is_partition(dev) && dev->nslaves == 0) @@ -978,13 +1085,13 @@ static char *device_get_data( xasprintf(&str, "%ju", dev->size); else str = size_to_human_string(SIZE_SUFFIX_1LETTER, dev->size); - if (sortdata) - *sortdata = dev->size; + if (rawdata) + *rawdata = dev->size; break; case COL_START: ul_path_read_string(dev->sysfs, &str, "start"); - if (sortdata) - str2u64(str, sortdata); + if (rawdata) + str2u64(str, rawdata); break; case COL_STATE: if (!device_is_partition(dev) && !dev->dm_name) @@ -997,36 +1104,36 @@ static char *device_get_data( break; case COL_ALIOFF: ul_path_read_string(dev->sysfs, &str, "alignment_offset"); - if (sortdata) - str2u64(str, sortdata); + if (rawdata) + str2u64(str, rawdata); break; case COL_MINIO: ul_path_read_string(dev->sysfs, &str, "queue/minimum_io_size"); - if (sortdata) - str2u64(str, sortdata); + if (rawdata) + str2u64(str, rawdata); break; case COL_OPTIO: ul_path_read_string(dev->sysfs, &str, "queue/optimal_io_size"); - if (sortdata) - str2u64(str, sortdata); + if (rawdata) + str2u64(str, rawdata); break; case COL_PHYSEC: ul_path_read_string(dev->sysfs, &str, "queue/physical_block_size"); - if (sortdata) - str2u64(str, sortdata); + if (rawdata) + str2u64(str, rawdata); break; case COL_LOGSEC: ul_path_read_string(dev->sysfs, &str, "queue/logical_block_size"); - if (sortdata) - str2u64(str, sortdata); + if (rawdata) + str2u64(str, rawdata); break; case COL_SCHED: str = get_scheduler(dev); break; case COL_RQ_SIZE: ul_path_read_string(dev->sysfs, &str, "queue/nr_requests"); - if (sortdata) - str2u64(str, sortdata); + if (rawdata) + str2u64(str, rawdata); break; case COL_TYPE: str = get_type(dev); @@ -1039,8 +1146,12 @@ static char *device_get_data( break; } case COL_TRANSPORT: - str = get_transport(dev); + { + const char *trans = get_transport(dev); + if (trans) + str = xstrdup(trans); break; + } case COL_SUBSYS: str = get_subsystems(dev); break; @@ -1049,23 +1160,23 @@ static char *device_get_data( ul_path_read_string(dev->sysfs, &str, "discard_alignment"); if (!str) str = xstrdup("0"); - if (sortdata) - str2u64(str, sortdata); + if (rawdata) + str2u64(str, rawdata); break; case COL_DGRAN: if (lsblk->bytes) { ul_path_read_string(dev->sysfs, &str, "queue/discard_granularity"); - if (sortdata) - str2u64(str, sortdata); + if (rawdata) + str2u64(str, rawdata); } else { uint64_t x = device_get_discard_granularity(dev); str = size_to_human_string(SIZE_SUFFIX_1LETTER, x); - if (sortdata) - *sortdata = x; + if (rawdata) + *rawdata = x; } break; case COL_DMAX: - device_read_bytes(dev, "queue/discard_max_bytes", &str, sortdata); + device_read_bytes(dev, "queue/discard_max_bytes", &str, rawdata); break; case COL_DZERO: if (device_get_discard_granularity(dev) > 0) @@ -1074,7 +1185,7 @@ static char *device_get_data( str = xstrdup("0"); break; case COL_WSAME: - device_read_bytes(dev, "queue/write_same_max_bytes", &str, sortdata); + device_read_bytes(dev, "queue/write_same_max_bytes", &str, rawdata); if (!str) str = xstrdup("0"); break; @@ -1091,44 +1202,96 @@ static char *device_get_data( xasprintf(&str, "%ju", x); else str = size_to_human_string(SIZE_SUFFIX_1LETTER, x); - if (sortdata) - *sortdata = x; + if (rawdata) + *rawdata = x; } break; } case COL_ZONE_WGRAN: - device_read_bytes(dev, "queue/zone_write_granularity", &str, sortdata); + device_read_bytes(dev, "queue/zone_write_granularity", &str, rawdata); break; case COL_ZONE_APP: - device_read_bytes(dev, "queue/zone_append_max_bytes", &str, sortdata); + device_read_bytes(dev, "queue/zone_append_max_bytes", &str, rawdata); break; case COL_ZONE_NR: ul_path_read_string(dev->sysfs, &str, "queue/nr_zones"); - if (sortdata) - str2u64(str, sortdata); + if (rawdata) + str2u64(str, rawdata); break; case COL_ZONE_OMAX: ul_path_read_string(dev->sysfs, &str, "queue/max_open_zones"); if (!str) str = xstrdup("0"); - if (sortdata) - str2u64(str, sortdata); + if (rawdata) + str2u64(str, rawdata); break; case COL_ZONE_AMAX: ul_path_read_string(dev->sysfs, &str, "queue/max_active_zones"); if (!str) str = xstrdup("0"); - if (sortdata) - str2u64(str, sortdata); + if (rawdata) + str2u64(str, rawdata); break; case COL_DAX: ul_path_read_string(dev->sysfs, &str, "queue/dax"); break; + case COL_MQ: + process_mq(dev, &str); + break; + case COL_DISKSEQ: + ul_path_read_string(dev->sysfs, &str, "diskseq"); + if (rawdata) + str2u64(str, rawdata); + break; }; return str; } +static void device_fill_scols_cell(struct lsblk_device *dev, + struct lsblk_device *parent, + struct libscols_line *ln, + size_t colnum) +{ + struct libscols_cell *ce; + struct libscols_column *cl = scols_table_get_column(lsblk->table, colnum); + char *data; + size_t datasiz = 0; + int rc, id = get_column_id(colnum); + + if (lsblk->sort_id == id || scols_column_has_data_func(cl)) { + uint64_t rawdata = (uint64_t) -1; + + data = device_get_data(dev, parent, id, &rawdata, &datasiz); + if (data && rawdata != (uint64_t) -1) + set_rawdata_u64(ln, colnum, rawdata); + } else + data = device_get_data(dev, parent, id, NULL, &datasiz); + + if (!data) + return; + DBG(DEV, ul_debugobj(dev, " refer data[%zu]=\"%s\"", colnum, data)); + ce = scols_line_get_cell(ln, colnum); + if (!ce) + return; + rc = datasiz ? scols_cell_refer_memory(ce, data, datasiz) + : scols_cell_refer_data(ce, data); + if (rc) + err(EXIT_FAILURE, _("failed to add output data")); +} + +static int filter_filler_cb( + struct libscols_filter *fltr __attribute__((__unused__)), + struct libscols_line *ln, + size_t colnum, + void *userdata) +{ + struct filler_data *fid = (struct filler_data *) userdata; + + device_fill_scols_cell(fid->dev, fid->parent, ln, colnum); + return 0; +} + /* * Adds data for all wanted columns about the device to the smartcols table */ @@ -1142,7 +1305,7 @@ static void device_to_scols( struct libscols_line *ln; struct lsblk_iter itr; struct lsblk_device *child = NULL; - int link_group = 0; + int link_group = 0, nocount = 0; DBG(DEV, ul_debugobj(dev, "add '%s' to scols", dev->name)); @@ -1167,7 +1330,38 @@ static void device_to_scols( dev->is_printed = 1; - if (link_group) { + /* filter lines, smartcols filter can ask for data */ + if (lsblk->filter) { + int status = 0; + struct filler_data fid = { + .dev = dev, + .parent = parent + }; + + scols_filter_set_filler_cb(lsblk->filter, + filter_filler_cb, (void *) &fid); + + if (scols_line_apply_filter(ln, lsblk->filter, &status)) + err(EXIT_FAILURE, _("failed to apply filter")); + if (status == 0) { + struct libscols_line *x = scols_line_get_parent(ln); + + if (x) + scols_line_remove_child(x, ln); + unref_line_rawdata(ln, tab); + scols_table_remove_line(tab, ln); + ln = NULL; + } + } + + /* read column specific data and set it to smartcols table line */ + for (i = 0; ln && i < ncolumns; i++) { + if (scols_line_is_filled(ln, i)) + continue; + device_fill_scols_cell(dev, parent, ln, i); + } + + if (ln && link_group) { struct lsblk_device *p; struct libscols_line *gr = parent_line; @@ -1188,24 +1382,9 @@ static void device_to_scols( 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; - int id = get_column_id(i); - - if (lsblk->sort_id != id) - data = device_get_data(dev, parent, id, NULL); - else { - uint64_t sortdata = (uint64_t) -1; - - data = device_get_data(dev, parent, id, &sortdata); - if (data && sortdata != (uint64_t) -1) - set_sortdata_u64(ln, i, sortdata); - } - DBG(DEV, ul_debugobj(dev, " refer data[%zu]=\"%s\"", i, data)); - if (data && scols_line_refer_data(ln, i, data)) - err(EXIT_FAILURE, _("failed to add output data")); - } + /* The same device could be printed more than once, don't use it in counter */ + if (dev->scols_line) + nocount = 1; dev->scols_line = ln; @@ -1221,6 +1400,21 @@ static void device_to_scols( DBG(DEV, ul_debugobj(dev, "%s <- child done", dev->name)); } + /* apply highligther */ + if (ln && lsblk->hlighter) { + int status = 0; + + if (scols_line_apply_filter(ln, lsblk->hlighter, &status) == 0 + && status) + scols_line_set_color(ln, lsblk->hlighter_seq); + } + + /* apply counters */ + if (!nocount) { + for (i = 0; ln && i < lsblk->ncts; i++) + scols_line_apply_filter(ln, lsblk->ct_filters[i], NULL); + } + /* Let's be careful with number of open files */ ul_path_close_dirfd(dev->sysfs); } @@ -1332,6 +1526,26 @@ static int initialize_device(struct lsblk_device *dev, return -1; } + /* ignore non-NVMe devices */ + if (lsblk->nvme) { + const char *transport = get_transport(dev); + + if (!transport || strcmp(transport, "nvme")) { + DBG(DEV, ul_debugobj(dev, "non-nvme device -- ignore")); + return -1; + } + } + + /* ignore non-virtio devices */ + if (lsblk->virtio) { + const char *transport = get_transport(dev); + + if (!transport || strcmp(transport, "virtio")) { + DBG(DEV, ul_debugobj(dev, "non-virtio device -- ignore")); + return -1; + } + } + DBG(DEV, ul_debugobj(dev, "%s: context successfully initialized", dev->name)); return 0; } @@ -1832,7 +2046,7 @@ static void parse_includes(const char *str0) } /* - * see set_sortdata_u64() and columns initialization in main() + * see set_rawdata_u64() and columns initialization in main() */ static int cmp_u64_cells(struct libscols_cell *a, struct libscols_cell *b, @@ -1850,6 +2064,13 @@ static int cmp_u64_cells(struct libscols_cell *a, return *adata == *bdata ? 0 : *adata >= *bdata ? 1 : -1; } +static void *get_u64_cell(const struct libscols_column *cl __attribute__((__unused__)), + struct libscols_cell *ce, + void *data __attribute__((__unused__))) +{ + return scols_cell_get_userdata(ce); +} + static void device_set_dedupkey( struct lsblk_device *dev, struct lsblk_device *parent, @@ -1858,7 +2079,7 @@ static void device_set_dedupkey( struct lsblk_iter itr; struct lsblk_device *child = NULL; - dev->dedupkey = device_get_data(dev, parent, id, NULL); + dev->dedupkey = device_get_data(dev, parent, id, NULL, NULL); if (dev->dedupkey) DBG(DEV, ul_debugobj(dev, "%s: de-duplication key: %s", dev->name, dev->dedupkey)); @@ -1887,10 +2108,193 @@ static void devtree_set_dedupkeys(struct lsblk_devtree *tr, int id) device_set_dedupkey(dev, NULL, id); } +static struct libscols_filter *new_filter(const char *query) +{ + struct libscols_filter *f; + + f = scols_new_filter(NULL); + if (!f) + err(EXIT_FAILURE, _("failed to allocate filter")); + if (query && scols_filter_parse_string(f, query) != 0) + errx(EXIT_FAILURE, _("failed to parse \"%s\": %s"), query, + scols_filter_get_errmsg(f)); + return f; +} + +static struct libscols_filter *new_counter_filter(const char *query) +{ + lsblk->ct_filters = xreallocarray(lsblk->ct_filters, lsblk->ncts + 1, + sizeof(struct libscols_filter *)); + + lsblk->ct_filters[lsblk->ncts] = new_filter(query); + lsblk->ncts++; + + return lsblk->ct_filters[lsblk->ncts - 1]; +} + +static void set_counter_properties(const char *str0) +{ + struct libscols_filter *fltr = NULL; + struct libscols_counter *ct; + char *p, *str = xstrdup(str0); + char *name = NULL, *param = NULL, *func = NULL; + + for (p = strtok(str, ":"); p != NULL; p = strtok((char *)0, ":")) { + if (!name) + name = p; + else if (!param) + param = p; + else if (!func) + func = p; + else + errx(EXIT_FAILURE, _("unexpected counter specification: %s"), str0); + } + + if (!name) + errx(EXIT_FAILURE, _("counter not properly specified")); + + /* use the latest counter filter (--ct-filter) or create empty */ + if (lsblk->ncts) + fltr = lsblk->ct_filters[lsblk->ncts - 1]; + else + fltr = new_counter_filter(NULL); + + ct = scols_filter_new_counter(fltr); + if (!ct) + err(EXIT_FAILURE, _("failed to allocate counter")); + + scols_counter_set_name(ct, name); + if (param) + scols_counter_set_param(ct, param); + if (func) { + int x; + + if (strcmp(func, "max") == 0) + x = SCOLS_COUNTER_MAX; + else if (strcmp(func, "min") == 0) + x = SCOLS_COUNTER_MIN; + else if (strcmp(func, "sum") == 0) + x = SCOLS_COUNTER_SUM; + else if (strcmp(func, "count") == 0) + x = SCOLS_COUNTER_COUNT; + else + errx(EXIT_FAILURE, _("unsupported counter type: %s"), func); + + scols_counter_set_func(ct, x); + } + free(str); +} + +static void print_counters(void) +{ + struct libscols_iter *itr; + size_t i; + + fputc('\n', stdout); + fputs(_("Summary:\n"), stdout); + + itr = scols_new_iter(SCOLS_ITER_FORWARD); + if (!itr) + err(EXIT_FAILURE, _("failed to allocate iterator")); + + for (i = 0; i < lsblk->ncts; i++) { + struct libscols_filter *fltr = lsblk->ct_filters[i]; + struct libscols_counter *ct = NULL; + + scols_reset_iter(itr, SCOLS_ITER_FORWARD); + while (scols_filter_next_counter(fltr, itr, &ct) == 0) { + printf("%16llu %s\n", + scols_counter_get_result(ct), + scols_counter_get_name(ct)); + } + } + + scols_free_iter(itr); +} + +static void set_column_type(const struct colinfo *ci, struct libscols_column *cl, int fl) +{ + switch (ci->type) { + case COLTYPE_SIZE: + /* See init_scols_filter(), it may overwrite the type */ + if (!lsblk->bytes) + break; + /* fallthrough */ + case COLTYPE_NUM: + scols_column_set_json_type(cl, SCOLS_JSON_NUMBER); + scols_column_set_data_type(cl, SCOLS_DATA_U64); + return; + case COLTYPE_BOOL: + scols_column_set_json_type(cl, SCOLS_JSON_BOOLEAN); + scols_column_set_data_type(cl, SCOLS_DATA_BOOLEAN); + return; + default: + break; + } + + /* default */ + if (fl & SCOLS_FL_WRAP) + scols_column_set_json_type(cl, SCOLS_JSON_ARRAY_STRING); + else + scols_column_set_json_type(cl, SCOLS_JSON_STRING); + + scols_column_set_data_type(cl, SCOLS_DATA_STRING); +} + +static void init_scols_filter(struct libscols_table *tb, struct libscols_filter *f) +{ + struct libscols_iter *itr; + const char *name = NULL; + int nerrs = 0; + + itr = scols_new_iter(SCOLS_ITER_FORWARD); + if (!itr) + err(EXIT_FAILURE, _("failed to allocate iterator")); + + while (scols_filter_next_holder(f, itr, &name, 0) == 0) { + struct libscols_column *col = scols_table_get_column_by_name(tb, name); + int id = column_name_to_id(name, strlen(name)); + const struct colinfo *ci = id >= 0 ? &infos[id] : NULL; + + if (!ci) { + nerrs++; + continue; /* report all unknown columns */ + } + if (!col) { + add_column(id); + col = scols_table_new_column(tb, ci->name, + ci->whint, SCOLS_FL_HIDDEN); + if (!col) + err(EXIT_FAILURE,_("failed to allocate output column")); + + set_column_type(ci, col, ci->flags); + } + + /* For sizes use rawdata (u64) rather than strings from table */ + if (ci->type == COLTYPE_SIZE + && !lsblk->bytes + && !scols_column_has_data_func(col)) { + + scols_column_set_data_type(col, SCOLS_DATA_U64); + scols_column_set_data_func(col, get_u64_cell, NULL); + lsblk->rawdata = 1; + } + + scols_filter_assign_column(f, itr, name, col); + } + + scols_free_iter(itr); + + if (!nerrs) + return; + + errx(EXIT_FAILURE, _("failed to initialize filter")); +} + + static void __attribute__((__noreturn__)) usage(void) { FILE *out = stdout; - size_t i; fputs(USAGE_HEADER, out); fprintf(out, _(" %s [options] [ ...]\n"), program_invocation_short_name); @@ -1907,7 +2311,13 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" -M, --merge group parents of sub-trees (usable for RAIDs, Multi-path)\n"), out); fputs(_(" -O, --output-all output all columns\n"), out); fputs(_(" -P, --pairs use key=\"value\" output format\n"), out); + fputs(_(" -Q, --filter print only lines maching the expression\n"), out); + fputs(_(" --highlight colorize lines maching the expression\n"), out); + fputs(_(" --ct-filter restrict the next counter\n"), out); + fputs(_(" --ct [:[:]] define a custom counter\n"), out); fputs(_(" -S, --scsi output info about SCSI devices\n"), out); + fputs(_(" -N, --nvme output info about NVMe devices\n"), out); + fputs(_(" -v, --virtio output info about virtio 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); @@ -1918,7 +2328,7 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" -l, --list use list format output\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 output columns (see --list-columns)\n"), out); fputs(_(" -p, --paths print complete device path\n"), out); fputs(_(" -r, --raw use raw output format\n"), out); fputs(_(" -s, --inverse inverse dependencies\n"), out); @@ -1928,15 +2338,40 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" -y, --shell use column names to be usable as shell variable identifiers\n"), out); fputs(_(" -z, --zoned print zone related information\n"), out); fputs(_(" --sysroot use specified directory as system root\n"), out); + fputs(USAGE_SEPARATOR, out); - printf(USAGE_HELP_OPTIONS(22)); + fputs(_(" -H, --list-columns list the available columns\n"), out); + fprintf(out, USAGE_HELP_OPTIONS(22)); - fprintf(out, USAGE_COLUMNS); + fprintf(out, USAGE_MAN_TAIL("lsblk(8)")); - for (i = 0; i < ARRAY_SIZE(infos); i++) - fprintf(out, " %12s %s\n", infos[i].name, _(infos[i].help)); + exit(EXIT_SUCCESS); +} - printf(USAGE_MAN_TAIL("lsblk(8)")); + +static void __attribute__((__noreturn__)) list_colunms(void) +{ + size_t i; + struct libscols_table *tb = xcolumn_list_table_new("lsblk-columns", stdout, + lsblk->flags & LSBLK_RAW, + lsblk->flags & LSBLK_JSON); + + for (i = 0; i < ARRAY_SIZE(infos); i++) { + const struct colinfo *ci = &infos[i]; + + xcolumn_list_table_append_line(tb, ci->name, + ci->type == COLTYPE_SIZE ? -1 : + ci->type == COLTYPE_NUM ? SCOLS_JSON_NUMBER : + ci->type == COLTYPE_BOOL ? SCOLS_JSON_BOOLEAN : + ci->flags & SCOLS_FL_WRAP ? SCOLS_JSON_ARRAY_STRING : + SCOLS_JSON_STRING, + ci->type == COLTYPE_SIZE ? "" : NULL, + _(ci->help)); + } + + + scols_print_table(tb); + scols_unref_table(tb); exit(EXIT_SUCCESS); } @@ -1957,14 +2392,17 @@ int main(int argc, char *argv[]) .tree_id = COL_NAME }; struct lsblk_devtree *tr = NULL; - int c, status = EXIT_FAILURE; + int c, status = EXIT_FAILURE, collist = 0; char *outarg = NULL; size_t i; unsigned int width = 0; int force_tree = 0, has_tree_col = 0; enum { - OPT_SYSROOT = CHAR_MAX + 1 + OPT_SYSROOT = CHAR_MAX + 1, + OPT_COUNTER_FILTER, + OPT_COUNTER, + OPT_HIGHLIGHT, }; static const struct option longopts[] = { @@ -1979,6 +2417,8 @@ int main(int argc, char *argv[]) { "json", no_argument, NULL, 'J' }, { "output", required_argument, NULL, 'o' }, { "output-all", no_argument, NULL, 'O' }, + { "filter", required_argument, NULL, 'Q' }, + { "highlight", required_argument, NULL, OPT_HIGHLIGHT }, { "merge", no_argument, NULL, 'M' }, { "perms", no_argument, NULL, 'm' }, { "noheadings", no_argument, NULL, 'n' }, @@ -1993,12 +2433,17 @@ int main(int argc, char *argv[]) { "paths", no_argument, NULL, 'p' }, { "pairs", no_argument, NULL, 'P' }, { "scsi", no_argument, NULL, 'S' }, + { "nvme", no_argument, NULL, 'N' }, + { "virtio", no_argument, NULL, 'v' }, { "sort", required_argument, NULL, 'x' }, { "sysroot", required_argument, NULL, OPT_SYSROOT }, { "shell", no_argument, NULL, 'y' }, { "tree", optional_argument, NULL, 'T' }, { "version", no_argument, NULL, 'V' }, { "width", required_argument, NULL, 'w' }, + { "ct-filter", required_argument, NULL, OPT_COUNTER_FILTER }, + { "ct", required_argument, NULL, OPT_COUNTER }, + { "list-columns", no_argument, NULL, 'H' }, { NULL, 0, NULL, 0 }, }; @@ -2024,9 +2469,10 @@ int main(int argc, char *argv[]) lsblk = &_ls; lsblk_init_debug(); + scols_init_debug(0); while((c = getopt_long(argc, argv, - "AabdDzE:e:fhJlnMmo:OpPiI:rstVST::w:x:y", + "AabdDzE:e:fHhJlNnMmo:OpPQ:iI:rstVvST::w:x:y", longopts, NULL)) != -1) { err_exclusive_options(c, longopts, excl, excl_st); @@ -2090,6 +2536,10 @@ int main(int argc, char *argv[]) lsblk->flags |= LSBLK_EXPORT; lsblk->flags &= ~LSBLK_TREE; /* disable the default */ break; + case 'Q': + lsblk->filter = new_filter(optarg); + lsblk->flags &= ~LSBLK_TREE; /* disable the default */ + break; case 'y': lsblk->flags |= LSBLK_SHELLVAR; break; @@ -2148,6 +2598,28 @@ int main(int argc, char *argv[]) add_uniq_column(COL_SERIAL); add_uniq_column(COL_TRANSPORT); break; + case 'N': + lsblk->nodeps = 1; + lsblk->nvme = 1; + add_uniq_column(COL_NAME); + add_uniq_column(COL_TYPE); + add_uniq_column(COL_MODEL); + add_uniq_column(COL_SERIAL); + add_uniq_column(COL_REV); + add_uniq_column(COL_TRANSPORT); + add_uniq_column(COL_RQ_SIZE); + add_uniq_column(COL_MQ); + break; + case 'v': + lsblk->nodeps = 1; + lsblk->virtio = 1; + add_uniq_column(COL_NAME); + add_uniq_column(COL_TYPE); + add_uniq_column(COL_TRANSPORT); + add_uniq_column(COL_SIZE); + add_uniq_column(COL_RQ_SIZE); + add_uniq_column(COL_MQ); + break; case 'T': force_tree = 1; if (optarg) { @@ -2176,6 +2648,19 @@ int main(int argc, char *argv[]) errtryhelp(EXIT_FAILURE); break; + case OPT_COUNTER_FILTER: + new_counter_filter(optarg); + break; + case OPT_COUNTER: + set_counter_properties(optarg); + break; + case OPT_HIGHLIGHT: + lsblk->hlighter = new_filter(optarg); + break; + + case 'H': + collist = 1; + break; case 'h': usage(); case 'V': @@ -2185,6 +2670,9 @@ int main(int argc, char *argv[]) } } + if (collist) + list_colunms(); /* print end exit */ + if (force_tree) lsblk->flags |= LSBLK_TREE; @@ -2230,7 +2718,6 @@ int main(int argc, char *argv[]) } lsblk_mnt_init(); - scols_init_debug(0); ul_path_init_debug(); /* @@ -2253,7 +2740,7 @@ int main(int argc, char *argv[]) } for (i = 0; i < ncolumns; i++) { - struct colinfo *ci = get_column_info(i); + const struct colinfo *ci = get_column_info(i); struct libscols_column *cl; int id = get_column_id(i), fl = ci->flags; @@ -2286,6 +2773,7 @@ int main(int argc, char *argv[]) } if (!lsblk->sort_col && lsblk->sort_id == id) { lsblk->sort_col = cl; + lsblk->rawdata = 1; scols_column_set_cmpfunc(cl, ci->type == COLTYPE_NUM ? cmp_u64_cells : ci->type == COLTYPE_SIZE ? cmp_u64_cells : @@ -2293,35 +2781,22 @@ int main(int argc, char *argv[]) NULL); } /* multi-line cells (now used for MOUNTPOINTS) */ - if (fl & SCOLS_FL_WRAP) { - scols_column_set_wrapfunc(cl, - scols_wrapnl_chunksize, - scols_wrapnl_nextchunk, - NULL); - scols_column_set_safechars(cl, "\n"); - } + if (fl & SCOLS_FL_WRAP) + scols_column_set_wrapfunc(cl, NULL, scols_wrapzero_nextchunk, NULL); - if (lsblk->flags & LSBLK_JSON) { - switch (ci->type) { - case COLTYPE_SIZE: - if (!lsblk->bytes) - break; - /* fallthrough */ - case COLTYPE_NUM: - scols_column_set_json_type(cl, SCOLS_JSON_NUMBER); - break; - case COLTYPE_BOOL: - scols_column_set_json_type(cl, SCOLS_JSON_BOOLEAN); - break; - default: - if (fl & SCOLS_FL_WRAP) - scols_column_set_json_type(cl, SCOLS_JSON_ARRAY_STRING); - else - scols_column_set_json_type(cl, SCOLS_JSON_STRING); - break; - } - } + set_column_type(ci, cl, fl); + } + + if (lsblk->filter) + init_scols_filter(lsblk->table, lsblk->filter); + + if (lsblk->hlighter && colors_init(UL_COLORMODE_AUTO, "lsblk") > 0) { + lsblk->hlighter_seq = color_scheme_get_sequence("highlight-line", UL_COLOR_RED); + scols_table_enable_colors(lsblk->table, 1); + init_scols_filter(lsblk->table, lsblk->hlighter); } + for (i = 0; i < lsblk->ncts; i++) + init_scols_filter(lsblk->table, lsblk->ct_filters[i]); tr = lsblk_new_devtree(); if (!tr) @@ -2361,11 +2836,19 @@ int main(int argc, char *argv[]) scols_print_table(lsblk->table); + if (lsblk->ncts) + print_counters(); leave: - if (lsblk->sort_col) - unref_sortdata(lsblk->table); + if (lsblk->rawdata) + unref_table_rawdata(lsblk->table); scols_unref_table(lsblk->table); + scols_unref_filter(lsblk->filter); + scols_unref_filter(lsblk->hlighter); + + for (i = 0; i < lsblk->ncts; i++) + scols_unref_filter(lsblk->ct_filters[i]); + free(lsblk->ct_filters); lsblk_mnt_deinit(); lsblk_properties_deinit();