summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--misc-utils/lsblk.83
-rw-r--r--misc-utils/lsblk.c211
2 files changed, 182 insertions, 32 deletions
diff --git a/misc-utils/lsblk.8 b/misc-utils/lsblk.8
index e87689b46..664db0e91 100644
--- a/misc-utils/lsblk.8
+++ b/misc-utils/lsblk.8
@@ -110,6 +110,9 @@ This option is equivalent to
.TP
.BR \-V , " \-\-version"
Display version information and exit.
+.TP
+.BR \-x , " \-\-sort " \fIcolumn\fP
+Sort output lines by output \fIcolumn\fP.
.SH NOTES
For partitions, some information (e.g. queue attributes) is inherited from the
parent device.
diff --git a/misc-utils/lsblk.c b/misc-utils/lsblk.c
index 24853dcf8..e87b5f968 100644
--- a/misc-utils/lsblk.c
+++ b/misc-utils/lsblk.c
@@ -117,12 +117,19 @@ enum {
LSBLK_TREE = (1 << 4),
};
+enum {
+ SORT_STRING = 0, /* default is to use scols_cell_get_data() */
+ SORT_U64 = 1 /* use private pointer from scols_cell_get_userdata() */
+};
+
/* column names */
struct colinfo {
const char *name; /* header */
double whint; /* width hint (N < 1 is in percent of termwidth) */
int flags; /* SCOLS_FL_* */
const char *help;
+
+ int sort_type; /* SORT_* */
};
/* columns descriptions */
@@ -130,7 +137,7 @@ static struct colinfo infos[] = {
[COL_NAME] = { "NAME", 0.25, SCOLS_FL_TREE | SCOLS_FL_NOEXTREMES, N_("device name") },
[COL_KNAME] = { "KNAME", 0.3, 0, N_("internal kernel device name") },
[COL_PKNAME] = { "PKNAME", 0.3, 0, N_("internal parent kernel device name") },
- [COL_MAJMIN] = { "MAJ:MIN", 6, 0, N_("major:minor device number") },
+ [COL_MAJMIN] = { "MAJ:MIN", 6, 0, N_("major:minor device number"), SORT_U64 },
[COL_FSTYPE] = { "FSTYPE", 0.1, SCOLS_FL_TRUNC, N_("filesystem type") },
[COL_TARGET] = { "MOUNTPOINT", 0.10, SCOLS_FL_TRUNC, N_("where the device is mounted") },
[COL_LABEL] = { "LABEL", 0.1, 0, N_("filesystem LABEL") },
@@ -141,31 +148,31 @@ static struct colinfo infos[] = {
[COL_PARTUUID] = { "PARTUUID", 36, 0, N_("partition UUID") },
[COL_PARTFLAGS] = { "PARTFLAGS", 36, 0, N_("partition flags") },
- [COL_RA] = { "RA", 3, SCOLS_FL_RIGHT, N_("read-ahead of the device") },
+ [COL_RA] = { "RA", 3, SCOLS_FL_RIGHT, N_("read-ahead of the device"), SORT_U64 },
[COL_RO] = { "RO", 1, SCOLS_FL_RIGHT, N_("read-only device") },
[COL_RM] = { "RM", 1, SCOLS_FL_RIGHT, N_("removable device") },
[COL_ROTA] = { "ROTA", 1, SCOLS_FL_RIGHT, N_("rotational device") },
[COL_RAND] = { "RAND", 1, SCOLS_FL_RIGHT, N_("adds randomness") },
[COL_MODEL] = { "MODEL", 0.1, SCOLS_FL_TRUNC, N_("device identifier") },
[COL_SERIAL] = { "SERIAL", 0.1, SCOLS_FL_TRUNC, N_("disk serial number") },
- [COL_SIZE] = { "SIZE", 5, SCOLS_FL_RIGHT, N_("size of the device") },
+ [COL_SIZE] = { "SIZE", 5, SCOLS_FL_RIGHT, N_("size of the device"), SORT_U64 },
[COL_STATE] = { "STATE", 7, SCOLS_FL_TRUNC, N_("state of the device") },
[COL_OWNER] = { "OWNER", 0.1, SCOLS_FL_TRUNC, N_("user name"), },
[COL_GROUP] = { "GROUP", 0.1, SCOLS_FL_TRUNC, N_("group name") },
[COL_MODE] = { "MODE", 10, 0, N_("device node permissions") },
- [COL_ALIOFF] = { "ALIGNMENT", 6, SCOLS_FL_RIGHT, N_("alignment offset") },
- [COL_MINIO] = { "MIN-IO", 6, SCOLS_FL_RIGHT, N_("minimum I/O size") },
- [COL_OPTIO] = { "OPT-IO", 6, SCOLS_FL_RIGHT, N_("optimal I/O size") },
- [COL_PHYSEC] = { "PHY-SEC", 7, SCOLS_FL_RIGHT, N_("physical sector size") },
- [COL_LOGSEC] = { "LOG-SEC", 7, SCOLS_FL_RIGHT, N_("logical sector size") },
+ [COL_ALIOFF] = { "ALIGNMENT", 6, SCOLS_FL_RIGHT, N_("alignment offset"), SORT_U64 },
+ [COL_MINIO] = { "MIN-IO", 6, SCOLS_FL_RIGHT, N_("minimum I/O size"), SORT_U64 },
+ [COL_OPTIO] = { "OPT-IO", 6, SCOLS_FL_RIGHT, N_("optimal I/O size"), SORT_U64 },
+ [COL_PHYSEC] = { "PHY-SEC", 7, SCOLS_FL_RIGHT, N_("physical sector size"), SORT_U64 },
+ [COL_LOGSEC] = { "LOG-SEC", 7, SCOLS_FL_RIGHT, N_("logical sector size"), SORT_U64 },
[COL_SCHED] = { "SCHED", 0.1, 0, N_("I/O scheduler name") },
- [COL_RQ_SIZE]= { "RQ-SIZE", 5, SCOLS_FL_RIGHT, N_("request queue size") },
+ [COL_RQ_SIZE]= { "RQ-SIZE", 5, SCOLS_FL_RIGHT, N_("request queue size"), SORT_U64 },
[COL_TYPE] = { "TYPE", 4, 0, N_("device type") },
- [COL_DALIGN] = { "DISC-ALN", 6, SCOLS_FL_RIGHT, N_("discard alignment offset") },
- [COL_DGRAN] = { "DISC-GRAN", 6, SCOLS_FL_RIGHT, N_("discard granularity") },
- [COL_DMAX] = { "DISC-MAX", 6, SCOLS_FL_RIGHT, N_("discard max bytes") },
+ [COL_DALIGN] = { "DISC-ALN", 6, SCOLS_FL_RIGHT, N_("discard alignment offset"), SORT_U64 },
+ [COL_DGRAN] = { "DISC-GRAN", 6, SCOLS_FL_RIGHT, N_("discard granularity"), SORT_U64 },
+ [COL_DMAX] = { "DISC-MAX", 6, SCOLS_FL_RIGHT, N_("discard max bytes"), SORT_U64 },
[COL_DZERO] = { "DISC-ZERO", 1, SCOLS_FL_RIGHT, N_("discard zeroes data") },
- [COL_WSAME] = { "WSAME", 6, SCOLS_FL_RIGHT, N_("write same max bytes") },
+ [COL_WSAME] = { "WSAME", 6, SCOLS_FL_RIGHT, N_("write same max bytes"), SORT_U64 },
[COL_WWN] = { "WWN", 18, 0, N_("unique storage identifier") },
[COL_HCTL] = { "HCTL", 10, 0, N_("Host:Channel:Target:Lun for SCSI") },
[COL_TRANSPORT] = { "TRAN", 6, 0, N_("device transport type") },
@@ -175,6 +182,9 @@ static struct colinfo infos[] = {
struct lsblk {
struct libscols_table *table; /* output table */
+ struct libscols_column *sort_col;/* sort output by this colum */
+ int sort_id;
+
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 */
@@ -296,6 +306,16 @@ static int column_name_to_id(const char *name, size_t namesz)
return -1;
}
+static int column_id_to_number(int id)
+{
+ size_t i;
+
+ for (i = 0; i < (size_t) ncolumns; i++)
+ if (columns[i] == id)
+ return i;
+ return -1;
+}
+
static void reset_blkdev_cxt(struct blkdev_cxt *cxt)
{
if (!cxt)
@@ -699,15 +719,63 @@ static char *mk_dm_name(const char *name)
return p;
}
+/* 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)
+{
+ struct libscols_cell *ce = scols_line_get_cell(ln, col);
+ uint64_t *data;
+
+ if (!ce)
+ return;
+ data = xmalloc(sizeof(uint64_t));
+ *data = x;
+ scols_cell_set_userdata(ce, data);
+}
+
+static void set_sortdata_u64_from_string(struct libscols_line *ln, int col, const char *str)
+{
+ uint64_t x;
+
+ if (!str || sscanf(str, "%"SCNu64, &x) != 1)
+ return;
+
+ set_sortdata_u64(ln, col, x);
+}
+
+static void unref_sortdata(struct libscols_table *tb)
+{
+ struct libscols_iter *itr;
+ struct libscols_line *ln;
+
+ if (!tb || !lsblk->sort_col)
+ 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);
+ }
+
+ scols_free_iter(itr);
+}
+
static void set_scols_data(struct blkdev_cxt *cxt, int col, int id, struct libscols_line *ln)
{
- int st_rc = 0;
+ int sort = 0, st_rc = 0;
char *str = NULL;
if (!cxt->st.st_rdev && (id == COL_OWNER || id == COL_GROUP ||
id == COL_MODE))
st_rc = stat(cxt->filename, &cxt->st);
+ if (lsblk->sort_id == id)
+ sort = 1;
+
switch(id) {
case COL_NAME:
str = cxt->dm_name ? mk_dm_name(cxt->dm_name) : mk_name(cxt->name);
@@ -748,6 +816,8 @@ static void set_scols_data(struct blkdev_cxt *cxt, int col, int id, struct libsc
xasprintf(&str, "%u:%u", cxt->maj, cxt->min);
else
xasprintf(&str, "%3u:%-3u", cxt->maj, cxt->min);
+ if (sort)
+ set_sortdata_u64(ln, col, makedev(cxt->maj, cxt->min));
break;
case COL_FSTYPE:
probe_device(cxt);
@@ -795,6 +865,8 @@ static void set_scols_data(struct blkdev_cxt *cxt, int col, int id, struct libsc
break;
case COL_RA:
str = sysfs_strdup(&cxt->sysfs, "queue/read_ahead_kb");
+ if (sort)
+ set_sortdata_u64_from_string(ln, col, str);
break;
case COL_RO:
str = xstrdup(is_readonly_device(cxt) ? "1" : "0");
@@ -836,6 +908,8 @@ static void set_scols_data(struct blkdev_cxt *cxt, int col, int id, struct libsc
xasprintf(&str, "%jd", cxt->size);
else
str = size_to_human_string(SIZE_SUFFIX_1LETTER, cxt->size);
+ if (sort)
+ set_sortdata_u64(ln, col, cxt->size);
break;
case COL_STATE:
if (!cxt->partition && !cxt->dm_name)
@@ -848,24 +922,36 @@ static void set_scols_data(struct blkdev_cxt *cxt, int col, int id, struct libsc
break;
case COL_ALIOFF:
str = sysfs_strdup(&cxt->sysfs, "alignment_offset");
+ if (sort)
+ set_sortdata_u64_from_string(ln, col, str);
break;
case COL_MINIO:
str = sysfs_strdup(&cxt->sysfs, "queue/minimum_io_size");
+ if (sort)
+ set_sortdata_u64_from_string(ln, col, str);
break;
case COL_OPTIO:
str = sysfs_strdup(&cxt->sysfs, "queue/optimal_io_size");
+ if (sort)
+ set_sortdata_u64_from_string(ln, col, str);
break;
case COL_PHYSEC:
str = sysfs_strdup(&cxt->sysfs, "queue/physical_block_size");
+ if (sort)
+ set_sortdata_u64_from_string(ln, col, str);
break;
case COL_LOGSEC:
str = sysfs_strdup(&cxt->sysfs, "queue/logical_block_size");
+ if (sort)
+ set_sortdata_u64_from_string(ln, col, str);
break;
case COL_SCHED:
str = get_scheduler(cxt);
break;
case COL_RQ_SIZE:
str = sysfs_strdup(&cxt->sysfs, "queue/nr_requests");
+ if (sort)
+ set_sortdata_u64_from_string(ln, col, str);
break;
case COL_TYPE:
str = get_type(cxt);
@@ -885,25 +971,37 @@ static void set_scols_data(struct blkdev_cxt *cxt, int col, int id, struct libsc
str = sysfs_strdup(&cxt->sysfs, "discard_alignment");
if (!str)
str = xstrdup("0");
+ if (sort)
+ set_sortdata_u64_from_string(ln, col, str);
break;
case COL_DGRAN:
- if (lsblk->bytes)
+ if (lsblk->bytes) {
str = sysfs_strdup(&cxt->sysfs, "queue/discard_granularity");
- else {
+ if (sort)
+ set_sortdata_u64_from_string(ln, col, str);
+ } else {
uint64_t x;
if (sysfs_read_u64(&cxt->sysfs,
- "queue/discard_granularity", &x) == 0)
+ "queue/discard_granularity", &x) == 0) {
str = size_to_human_string(SIZE_SUFFIX_1LETTER, x);
+ if (sort)
+ set_sortdata_u64(ln, col, x);
+ }
}
break;
case COL_DMAX:
- if (lsblk->bytes)
+ if (lsblk->bytes) {
str = sysfs_strdup(&cxt->sysfs, "queue/discard_max_bytes");
- else {
+ if (sort)
+ set_sortdata_u64_from_string(ln, col, str);
+ } else {
uint64_t x;
if (sysfs_read_u64(&cxt->sysfs,
- "queue/discard_max_bytes", &x) == 0)
+ "queue/discard_max_bytes", &x) == 0) {
str = size_to_human_string(SIZE_SUFFIX_1LETTER, x);
+ if (sort)
+ set_sortdata_u64(ln, col, x);
+ }
}
break;
case COL_DZERO:
@@ -913,14 +1011,19 @@ static void set_scols_data(struct blkdev_cxt *cxt, int col, int id, struct libsc
str = xstrdup("0");
break;
case COL_WSAME:
- if (lsblk->bytes)
+ if (lsblk->bytes) {
str = sysfs_strdup(&cxt->sysfs, "queue/write_same_max_bytes");
- else {
+ if (sort)
+ set_sortdata_u64_from_string(ln, col, str);
+ } else {
uint64_t x;
if (sysfs_read_u64(&cxt->sysfs,
- "queue/write_same_max_bytes", &x) == 0)
+ "queue/write_same_max_bytes", &x) == 0) {
str = size_to_human_string(SIZE_SUFFIX_1LETTER, x);
+ if (sort)
+ set_sortdata_u64(ln, col, x);
+ }
}
if (!str)
str = xstrdup("0");
@@ -931,7 +1034,7 @@ static void set_scols_data(struct blkdev_cxt *cxt, int col, int id, struct libsc
scols_line_refer_data(ln, col, str);
}
-static void print_device(struct blkdev_cxt *cxt, struct libscols_line *scols_parent)
+static void fill_table_line(struct blkdev_cxt *cxt, struct libscols_line *scols_parent)
{
int i;
@@ -1064,7 +1167,7 @@ static int list_partitions(struct blkdev_cxt *wholedisk_cxt, struct blkdev_cxt *
goto next;
wholedisk_cxt->parent = &part_cxt;
- print_device(&part_cxt, parent_cxt ? parent_cxt->scols_line : NULL);
+ fill_table_line(&part_cxt, parent_cxt ? parent_cxt->scols_line : NULL);
if (!lsblk->nodeps)
process_blkdev(wholedisk_cxt, &part_cxt, 0, NULL);
} else {
@@ -1078,7 +1181,7 @@ static int list_partitions(struct blkdev_cxt *wholedisk_cxt, struct blkdev_cxt *
/* Print whole disk only once */
if (r)
- print_device(wholedisk_cxt, parent_cxt ? parent_cxt->scols_line : NULL);
+ fill_table_line(wholedisk_cxt, parent_cxt ? parent_cxt->scols_line : NULL);
if (ps == 0 && !lsblk->nodeps)
process_blkdev(&part_cxt, wholedisk_cxt, 0, NULL);
}
@@ -1167,7 +1270,7 @@ static int process_blkdev(struct blkdev_cxt *cxt, struct blkdev_cxt *parent,
if (do_partitions && cxt->npartitions)
return list_partitions(cxt, parent, part_name);
- print_device(cxt, parent ? parent->scols_line : NULL);
+ fill_table_line(cxt, parent ? parent->scols_line : NULL);
return list_deps(cxt);
}
@@ -1339,6 +1442,25 @@ static void parse_includes(const char *str0)
}
}
+/*
+ * see set_sortdata_u64() and columns initialization in main()
+ */
+static int cmp_u64_cells(struct libscols_cell *a,
+ struct libscols_cell *b,
+ __attribute__((__unused__)) void *data)
+{
+ uint64_t *adata = (uint64_t *) scols_cell_get_userdata(a),
+ *bdata = (uint64_t *) scols_cell_get_userdata(b);
+
+ if (adata == NULL && bdata == NULL)
+ return 0;
+ if (adata == NULL)
+ return -1;
+ if (bdata == NULL)
+ return 1;
+ return *adata == *bdata ? 0 : *adata >= *bdata ? 1 : -1;
+}
+
static void __attribute__((__noreturn__)) help(FILE *out)
{
size_t i;
@@ -1365,6 +1487,7 @@ static void __attribute__((__noreturn__)) help(FILE *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(_(" -x, --sort <column> sort output by <colum>\n"), out);
fputs(USAGE_SEPARATOR, out);
fputs(USAGE_HELP, out);
fputs(USAGE_VERSION, out);
@@ -1388,7 +1511,7 @@ static void check_sysdevblock(void)
int main(int argc, char *argv[])
{
- struct lsblk _ls;
+ struct lsblk _ls = { .sort_id = -1 };
int scols_flags = LSBLK_TREE;
int i, c, status = EXIT_FAILURE;
char *outarg = NULL;
@@ -1414,6 +1537,7 @@ int main(int argc, char *argv[])
{ "paths", 0, 0, 'p' },
{ "pairs", 0, 0, 'P' },
{ "scsi", 0, 0, 'S' },
+ { "sort", 1, 0, 'x' },
{ "version", 0, 0, 'V' },
{ NULL, 0, 0, 0 },
};
@@ -1439,7 +1563,7 @@ int main(int argc, char *argv[])
memset(lsblk, 0, sizeof(*lsblk));
while((c = getopt_long(argc, argv,
- "abdDe:fhlnmo:OpPiI:rstVS", longopts, NULL)) != -1) {
+ "abdDe:fhlnmo:OpPiI:rstVSx:", longopts, NULL)) != -1) {
err_exclusive_options(c, longopts, excl, excl_st);
@@ -1540,6 +1664,12 @@ int main(int argc, char *argv[])
case 'V':
printf(UTIL_LINUX_VERSION);
return EXIT_SUCCESS;
+ case 'x':
+ scols_flags &= ~LSBLK_TREE; /* disable the default */
+ lsblk->sort_id = column_name_to_id(optarg, strlen(optarg));
+ if (lsblk->sort_id >= 0)
+ break;
+ /* fallthrough */
default:
help(stderr);
}
@@ -1564,6 +1694,9 @@ int main(int argc, char *argv[])
if (nexcludes == 0 && nincludes == 0)
excludes[nexcludes++] = 1; /* default: ignore RAM disks */
+ if (lsblk->sort_id >= 0 && column_id_to_number(lsblk->sort_id) < 0)
+ errx(EXIT_FAILURE, _("the sort column has to be between output columns."));
+
mnt_init_debug(0);
/*
@@ -1578,15 +1711,23 @@ int main(int argc, char *argv[])
for (i = 0; i < ncolumns; i++) {
struct colinfo *ci = get_column_info(i);
- int fl = ci->flags;
+ struct libscols_column *cl;
+ int id = get_column_id(i), fl = ci->flags;
- if (!(scols_flags & LSBLK_TREE) && get_column_id(i) == COL_NAME)
+ if (!(scols_flags & LSBLK_TREE) && id == COL_NAME)
fl &= ~SCOLS_FL_TREE;
- if (!scols_table_new_column(lsblk->table, ci->name, ci->whint, fl)) {
+ cl = scols_table_new_column(lsblk->table, ci->name, ci->whint, fl);
+ if (!cl) {
warn(_("failed to initialize output column"));
goto leave;
}
+ if (!lsblk->sort_col && lsblk->sort_id == id) {
+ lsblk->sort_col = cl;
+ scols_column_set_cmpfunc(cl,
+ ci->sort_type == SORT_STRING ?
+ scols_cmpstr_cells : cmp_u64_cells, NULL);
+ }
}
if (optind == argc)
@@ -1594,9 +1735,15 @@ int main(int argc, char *argv[])
else while (optind < argc)
status = process_one_device(argv[optind++]);
+ if (lsblk->sort_col)
+ scols_sort_table(lsblk->table, lsblk->sort_col);
+
scols_print_table(lsblk->table);
leave:
+ if (lsblk->sort_col)
+ unref_sortdata(lsblk->table);
+
scols_unref_table(lsblk->table);
mnt_unref_table(mtab);