summaryrefslogtreecommitdiffstats
path: root/misc-utils
diff options
context:
space:
mode:
Diffstat (limited to 'misc-utils')
-rw-r--r--misc-utils/Makemodule.am7
-rw-r--r--misc-utils/blkid.88
-rw-r--r--misc-utils/blkid.c19
-rw-r--r--misc-utils/cal.c65
-rw-r--r--misc-utils/fincore.12
-rw-r--r--misc-utils/logger.135
-rw-r--r--misc-utils/logger.c25
-rw-r--r--misc-utils/lsblk-devtree.c470
-rw-r--r--misc-utils/lsblk-mnt.c127
-rw-r--r--misc-utils/lsblk-properties.c188
-rw-r--r--misc-utils/lsblk.834
-rw-r--r--misc-utils/lsblk.c1622
-rw-r--r--misc-utils/lsblk.h225
-rw-r--r--misc-utils/namei.c2
-rw-r--r--misc-utils/rename.c4
-rw-r--r--misc-utils/uuidd.c8
-rw-r--r--misc-utils/uuidd.service.in12
-rw-r--r--misc-utils/uuidparse.123
-rw-r--r--misc-utils/whereis.c4
-rw-r--r--misc-utils/wipefs.83
-rw-r--r--misc-utils/wipefs.c61
21 files changed, 2131 insertions, 813 deletions
diff --git a/misc-utils/Makemodule.am b/misc-utils/Makemodule.am
index 30b7c2f0f..f56a819ac 100644
--- a/misc-utils/Makemodule.am
+++ b/misc-utils/Makemodule.am
@@ -77,7 +77,12 @@ endif
if BUILD_LSBLK
bin_PROGRAMS += lsblk
dist_man_MANS += misc-utils/lsblk.8
-lsblk_SOURCES = misc-utils/lsblk.c
+lsblk_SOURCES = \
+ misc-utils/lsblk.c \
+ misc-utils/lsblk-mnt.c \
+ misc-utils/lsblk-properties.c \
+ misc-utils/lsblk-devtree.c \
+ misc-utils/lsblk.h
lsblk_LDADD = $(LDADD) libblkid.la libmount.la libcommon.la libsmartcols.la
lsblk_CFLAGS = $(AM_CFLAGS) -I$(ul_libblkid_incdir) -I$(ul_libmount_incdir) -I$(ul_libsmartcols_incdir)
if HAVE_UDEV
diff --git a/misc-utils/blkid.8 b/misc-utils/blkid.8
index 13b5edb4d..ac36bee48 100644
--- a/misc-utils/blkid.8
+++ b/misc-utils/blkid.8
@@ -35,6 +35,7 @@ blkid \- locate/print block device attributes
.IR list ]
.RB [ \-\-usages
.IR list ]
+.RB [ \-\-no\-part\-details ]
.IR device " ..."
.IP \fBblkid\fR
@@ -77,7 +78,7 @@ is specified, tokens from only this device are displayed.
It is possible to specify multiple
.I device
arguments on the command line.
-If none is given, all devices which appear in
+If none is given, all partitions or unpartitioned devices which appear in
.I /proc/partitions
are shown, if they are recognized.
.PP
@@ -114,6 +115,9 @@ Don't encode non-printing characters. The non-printing characters are encoded
by ^ and M- notation by default. Note that the \fB\-\-output udev\fR output format uses
a different encoding which cannot be disabled.
.TP
+\fB\-D\fR, \fB\-\-no\-part\-details\fR
+Don't print information (PART_ENTRY_* tags) from partition table in low-level probing mode.
+.TP
\fB\-g\fR, \fB\-\-garbage\-collect\fR
Perform a garbage collection pass on the blkid cache to remove
devices which no longer exist.
@@ -224,7 +228,7 @@ Note that low-level probing also returns information about partition table type
(PTTYPE tag) and partitions (PART_ENTRY_* tags). The tag names produced by
low-level probing are based on names used internally by libblkid and it may be
different than when executed without \fB\-\-probe\fR (for example PART_ENTRY_UUID= vs
-PARTUUID=).
+PARTUUID=). See also \fB\-\-no\-part\-details\fR.
.TP
\fB\-s\fR, \fB\-\-match\-tag\fR \fItag\fR
For each (specified) device, show only the tags that match
diff --git a/misc-utils/blkid.c b/misc-utils/blkid.c
index 8179a6ac4..e94edfcf2 100644
--- a/misc-utils/blkid.c
+++ b/misc-utils/blkid.c
@@ -58,6 +58,7 @@ struct blkid_control {
lowprobe:1,
lowprobe_superblocks:1,
lowprobe_topology:1,
+ no_part_details:1,
raw_chars:1;
};
@@ -101,6 +102,7 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_( " -O, --offset <offset> probe at the given offset\n"), out);
fputs(_( " -u, --usages <list> filter by \"usage\" (e.g. -u filesystem,raid)\n"), out);
fputs(_( " -n, --match-types <list> filter by filesystem type (e.g. -n vfat,ext3)\n"), out);
+ fputs(_( " -D, --no-part-details don't print info from partition table\n"), out);
fputs(USAGE_SEPARATOR, out);
printf(USAGE_HELP_OPTIONS(28));
@@ -444,7 +446,7 @@ done:
return rc;
}
-static int lowprobe_superblocks(blkid_probe pr)
+static int lowprobe_superblocks(blkid_probe pr, struct blkid_control *ctl)
{
struct stat st;
int rc, fd = blkid_probe_get_fd(pr);
@@ -470,7 +472,8 @@ static int lowprobe_superblocks(blkid_probe pr)
return 0; /* partition table detected */
}
- blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
+ if (!ctl->no_part_details)
+ blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
blkid_probe_enable_superblocks(pr, 1);
return blkid_do_safeprobe(pr);
@@ -509,7 +512,7 @@ static int lowprobe_device(blkid_probe pr, const char *devname,
if (ctl->lowprobe_topology)
rc = lowprobe_topology(pr);
if (rc >= 0 && ctl->lowprobe_superblocks)
- rc = lowprobe_superblocks(pr);
+ rc = lowprobe_superblocks(pr, ctl);
if (rc < 0)
goto done;
@@ -530,8 +533,8 @@ static int lowprobe_device(blkid_probe pr, const char *devname,
continue;
if (ctl->show[0] && !has_item(ctl, name))
continue;
- len = strnlen((char *) data, len);
- print_value(ctl, num++, devname, (char *) data, name, len);
+ len = strnlen(data, len);
+ print_value(ctl, num++, devname, data, name, len);
}
if (first)
@@ -661,6 +664,7 @@ int main(int argc, char **argv)
static const struct option longopts[] = {
{ "cache-file", required_argument, NULL, 'c' },
{ "no-encoding", no_argument, NULL, 'd' },
+ { "no-part-details", no_argument, NULL, 'D' },
{ "garbage-collect", no_argument, NULL, 'g' },
{ "output", required_argument, NULL, 'o' },
{ "list-filesystems", no_argument, NULL, 'k' },
@@ -694,7 +698,7 @@ int main(int argc, char **argv)
strutils_set_exitcode(BLKID_EXIT_OTHER);
while ((c = getopt_long (argc, argv,
- "c:dghilL:n:ko:O:ps:S:t:u:U:w:Vv", longopts, NULL)) != -1) {
+ "c:DdghilL:n:ko:O:ps:S:t:u:U:w:Vv", longopts, NULL)) != -1) {
err_exclusive_options(c, NULL, excl, excl_st);
@@ -705,6 +709,9 @@ int main(int argc, char **argv)
case 'd':
ctl.raw_chars = 1;
break;
+ case 'D':
+ ctl.no_part_details = 1;
+ break;
case 'L':
ctl.eval = 1;
search_value = xstrdup(optarg);
diff --git a/misc-utils/cal.c b/misc-utils/cal.c
index 266f77bdc..bd643f3b4 100644
--- a/misc-utils/cal.c
+++ b/misc-utils/cal.c
@@ -550,9 +550,21 @@ int main(int argc, char **argv)
headers_init(&ctl);
- if (!colors_init(ctl.colormode, "cal")) {
- ctl.req.day = 0;
- ctl.weektype &= ~WEEK_NUM_MASK;
+ if (colors_init(ctl.colormode, "cal") == 0) {
+ /*
+ * If standout mode available (Senter and Sexit are set) and
+ * user or terminal-colors.d do not disable colors than
+ * ignore colors_init().
+ */
+ if (*Senter && *Sexit && colors_mode() != UL_COLORMODE_NEVER) {
+ /* let use standout mode */
+ ;
+ } else {
+ /* disable */
+ Senter = Sexit = '\0';
+ ctl.req.day = 0;
+ ctl.weektype &= ~WEEK_NUM_MASK;
+ }
}
if (yflag || Yflag) {
@@ -739,19 +751,19 @@ static void cal_output_header(struct cal_month *month, const struct cal_control
if (ctl->header_hint || ctl->header_year) {
for (i = month; i; i = i->next) {
- sprintf(out, _("%s"), ctl->full_month[i->month - 1]);
+ snprintf(out, sizeof(out), "%s", ctl->full_month[i->month - 1]);
center(out, ctl->week_width - 1, i->next == NULL ? 0 : ctl->gutter_width);
}
if (!ctl->header_year) {
my_putstring("\n");
for (i = month; i; i = i->next) {
- sprintf(out, _("%04d"), i->year);
+ snprintf(out, sizeof(out), "%04d", i->year);
center(out, ctl->week_width - 1, i->next == NULL ? 0 : ctl->gutter_width);
}
}
} else {
for (i = month; i; i = i->next) {
- sprintf(out, _("%s %04d"), ctl->full_month[i->month - 1], i->year);
+ snprintf(out, sizeof(out), "%s %04d", ctl->full_month[i->month - 1], i->year);
center(out, ctl->week_width - 1, i->next == NULL ? 0 : ctl->gutter_width);
}
}
@@ -759,14 +771,14 @@ static void cal_output_header(struct cal_month *month, const struct cal_control
for (i = month; i; i = i->next) {
if (ctl->weektype) {
if (ctl->julian)
- sprintf(out, "%*s%s", (int)ctl->day_width - 1, "", day_headings);
+ snprintf(out, sizeof(out), "%*s%s", (int)ctl->day_width - 1, "", day_headings);
else
- sprintf(out, "%*s%s", (int)ctl->day_width, "", day_headings);
+ snprintf(out, sizeof(out), "%*s%s", (int)ctl->day_width, "", day_headings);
my_putstring(out);
} else
my_putstring(day_headings);
if (i->next != NULL) {
- sprintf(out, "%*s", ctl->gutter_width, "");
+ snprintf(out, sizeof(out), "%*s", ctl->gutter_width, "");
my_putstring(out);
}
}
@@ -797,12 +809,13 @@ static void cal_output_months(struct cal_month *month, const struct cal_control
if (0 < i->weeks[week_line]) {
if ((ctl->weektype & WEEK_NUM_MASK) ==
i->weeks[week_line])
- sprintf(out, "%s%2d%s", Senter, i->weeks[week_line],
- Sexit);
+ snprintf(out, sizeof(out), "%s%2d%s",
+ Senter, i->weeks[week_line],
+ Sexit);
else
- sprintf(out, "%2d", i->weeks[week_line]);
+ snprintf(out, sizeof(out), "%2d", i->weeks[week_line]);
} else
- sprintf(out, "%2s", "");
+ snprintf(out, sizeof(out), "%2s", "");
my_putstring(out);
skip = ctl->day_width;
} else
@@ -814,19 +827,20 @@ static void cal_output_months(struct cal_month *month, const struct cal_control
d < DAYS_IN_WEEK * week_line + DAYS_IN_WEEK; d++) {
if (0 < i->days[d]) {
if (reqday == i->days[d])
- sprintf(out, "%*s%s%*d%s", skip - (ctl->julian ? 3 : 2),
+ snprintf(out, sizeof(out), "%*s%s%*d%s",
+ skip - (ctl->julian ? 3 : 2),
"", Senter, (ctl->julian ? 3 : 2),
i->days[d], Sexit);
else
- sprintf(out, "%*d", skip, i->days[d]);
+ snprintf(out, sizeof(out), "%*d", skip, i->days[d]);
} else
- sprintf(out, "%*s", skip, "");
+ snprintf(out, sizeof(out), "%*s", skip, "");
my_putstring(out);
if (skip < (int)ctl->day_width)
skip++;
}
if (i->next != NULL) {
- sprintf(out, "%*s", ctl->gutter_width, "");
+ snprintf(out, sizeof(out), "%*s", ctl->gutter_width, "");
my_putstring(out);
}
}
@@ -838,17 +852,20 @@ static void cal_output_months(struct cal_month *month, const struct cal_control
static void monthly(const struct cal_control *ctl)
{
struct cal_month m1,m2,m3, *m;
- int i, rows, new_month, month = ctl->req.start_month ? ctl->req.start_month : ctl->req.month;
+ int i, rows, month = ctl->req.start_month ? ctl->req.start_month : ctl->req.month;
int32_t year = ctl->req.year;
/* cal -3, cal -Y --span, etc. */
if (ctl->span_months) {
- new_month = month - ctl->num_months / 2;
+ int new_month = month - ctl->num_months / 2;
if (new_month < 1) {
- month = new_month + MONTHS_IN_YEAR;
- year--;
- }
- else
+ new_month *= -1;
+ year -= (new_month / MONTHS_IN_YEAR) + 1;
+
+ if (new_month > MONTHS_IN_YEAR)
+ new_month %= MONTHS_IN_YEAR;
+ month = MONTHS_IN_YEAR - new_month;
+ } else
month = new_month;
}
@@ -892,7 +909,7 @@ static void yearly(const struct cal_control *ctl)
year_width--;
if (ctl->header_year) {
- sprintf(out, "%04d", ctl->req.year);
+ snprintf(out, sizeof(out), "%04d", ctl->req.year);
center(out, year_width, 0);
my_putstring("\n\n");
}
diff --git a/misc-utils/fincore.1 b/misc-utils/fincore.1
index 30e292cc8..97c134bbb 100644
--- a/misc-utils/fincore.1
+++ b/misc-utils/fincore.1
@@ -10,7 +10,7 @@ fincore \- count pages of file contents in core
.I file ...
.SH DESCRIPTION
.B fincore
-counts pages of file contents being resident in memory(in core), and reports
+counts pages of file contents being resident in memory (in core), and reports
the numbers. If an error occurs during counting, then an error message is
printed to the stderr and
.B fincore
diff --git a/misc-utils/logger.1 b/misc-utils/logger.1
index 9e83a0b53..3530cd9e8 100644
--- a/misc-utils/logger.1
+++ b/misc-utils/logger.1
@@ -50,6 +50,8 @@ given either, then standard input is logged.
.BR \-d , " \-\-udp"
Use datagrams (UDP) only. By default the connection is tried to the
syslog port defined in /etc/services, which is often 514 .
+.sp
+See also \fB\-\-server\fR and \fB\-\-socket\fR to specify where to connect.
.TP
.BR \-e , " \-\-skip-empty"
Ignore empty lines when processing files. An empty line
@@ -108,6 +110,10 @@ execution of
will display MESSAGE field. Use
.B journalctl \-\-output json-pretty
to see rest of the fields.
+.sp
+To include newlines in MESSAGE, specify MESSAGE several times. This is
+handled as a special case, other fields will be stored as an array in
+the journal if they appear multiple times.
.TP
.BR \-\-msgid " \fImsgid
Sets the RFC5424 MSGID field. Note that the space character is not permitted
@@ -224,7 +230,7 @@ produces:
.fi
.IP
.TP
-.BR \-\-size " \fIsize
+.BR \-S , " -\-size " \fIsize
Sets the maximum permitted message size to \fIsize\fR. The default
is 1KiB characters, which is the limit traditionally used and specified
in RFC 3164. With RFC 5424, this limit has become flexible. A good assumption
@@ -265,6 +271,8 @@ Use stream (TCP) only. By default the connection is tried to the
.I syslog-conn
port defined in /etc/services, which is often
.IR 601 .
+.sp
+See also \fB\-\-server\fR and \fB\-\-socket\fR to specify where to connect.
.TP
.BR \-t , " \-\-tag " \fItag
Mark every line to be logged with the specified
@@ -293,15 +301,18 @@ utility exits 0 on success, and >0 if an error occurs.
.SH FACILITIES AND LEVELS
Valid facility names are:
.IP
+.nr WI \n(.lu-\n(.iu-\w'\fBauthpriv\fR'u-3n
.TS
tab(:);
-left l l.
+l lw(\n(WIu).
\fBauth
\fBauthpriv\fR:for security information of a sensitive nature
\fBcron
\fBdaemon
\fBftp
-\fBkern\fR:cannot be generated from userspace process, automatically converted to \fBuser
+\fBkern\fR:T{
+cannot be generated from userspace process, automatically converted to \fBuser
+T}
\fBlpr
\fBmail
\fBnews
@@ -318,7 +329,7 @@ Valid level names are:
.IP
.TS
tab(:);
-left l l.
+l l.
\fBemerg
\fBalert
\fBcrit
@@ -348,6 +359,22 @@ For the priority order and intended purposes of these facilities and levels, see
The
.B logger
command is expected to be IEEE Std 1003.2 ("POSIX.2") compatible.
+.SH AUTHORS
+The
+.B logger
+command
+was originally written by University of California in 1983-1993 and later
+rewritten by
+.MT kzak@redhat.com
+Karel Zak
+.ME ,
+.MT rgerhards@adiscon.com
+Rainer Gerhards
+.ME
+and
+.MT kerolasa@iki.fi
+Sami Kerola
+.ME .
.SH AVAILABILITY
The logger command is part of the util-linux package and is available from
.UR https://\:www.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
diff --git a/misc-utils/logger.c b/misc-utils/logger.c
index 846c85273..10b307ef9 100644
--- a/misc-utils/logger.c
+++ b/misc-utils/logger.c
@@ -68,6 +68,7 @@
#include <syslog.h>
#ifdef HAVE_LIBSYSTEMD
+# define SD_JOURNAL_SUPPRESS_LOCATION
# include <systemd/sd-daemon.h>
# include <systemd/sd-journal.h>
#endif
@@ -335,11 +336,11 @@ static int journald_entry(struct logger_ctl *ctl, FILE *fp)
struct iovec *iovec;
char *buf = NULL;
ssize_t sz;
- int n, lines, vectors = 8, ret = 0;
+ int n, lines = 0, vectors = 8, ret = 0, msgline = -1;
size_t dummy = 0;
iovec = xmalloc(vectors * sizeof(struct iovec));
- for (lines = 0; /* nothing */ ; lines++) {
+ while (1) {
buf = NULL;
sz = getline(&buf, &dummy, fp);
if (sz == -1 ||
@@ -347,6 +348,25 @@ static int journald_entry(struct logger_ctl *ctl, FILE *fp)
free(buf);
break;
}
+
+ if (strncmp(buf, "MESSAGE=", 8) == 0) {
+ if (msgline == -1)
+ msgline = lines; /* remember the first message */
+ else {
+ char *p = xrealloc(iovec[msgline].iov_base,
+ iovec[msgline].iov_len + sz - 8 + 2);
+
+ iovec[msgline].iov_base = p;
+ p += iovec[msgline].iov_len;
+ *p++ = '\n';
+ memcpy(p, buf + 8, sz - 8);
+ free(buf);
+
+ iovec[msgline].iov_len += sz - 8 + 1;
+ continue;
+ }
+ }
+
if (lines == vectors) {
vectors *= 2;
if (IOV_MAX < vectors)
@@ -355,6 +375,7 @@ static int journald_entry(struct logger_ctl *ctl, FILE *fp)
}
iovec[lines].iov_base = buf;
iovec[lines].iov_len = sz;
+ ++lines;
}
if (!ctl->noact)
diff --git a/misc-utils/lsblk-devtree.c b/misc-utils/lsblk-devtree.c
new file mode 100644
index 000000000..a2aa26aed
--- /dev/null
+++ b/misc-utils/lsblk-devtree.c
@@ -0,0 +1,470 @@
+/*
+ * These functions implement tree of block devices. The devtree struct contains
+ * two basic lists:
+ *
+ * 1) devtree->devices -- This is simple list without any hierarchy. We use
+ * reference counting here.
+ *
+ * 2) devtree->roots -- The root nodes of the trees. The code does not use
+ * reference counting here due to complexity and it's unnecessary.
+ *
+ * Note that the same device maybe have more parents and more children. The
+ * device is allocated only once and shared within the tree. The dependence
+ * (devdep struct) contains reference to child as well as to parent and the
+ * dependence is reference by ls_childs from parent device and by ls_parents
+ * from child. (Yes, "childs" is used for children ;-)
+ *
+ * Copyright (C) 2018 Karel Zak <kzak@redhat.com>
+ */
+#include "lsblk.h"
+#include "sysfs.h"
+
+
+void lsblk_reset_iter(struct lsblk_iter *itr, int direction)
+{
+ if (direction == -1)
+ direction = itr->direction;
+
+ memset(itr, 0, sizeof(*itr));
+ itr->direction = direction;
+}
+
+struct lsblk_device *lsblk_new_device()
+{
+ struct lsblk_device *dev;
+
+ dev = calloc(1, sizeof(*dev));
+ if (!dev)
+ return NULL;
+
+ dev->refcount = 1;
+ dev->removable = -1;
+ dev->discard_granularity = (uint64_t) -1;
+
+ INIT_LIST_HEAD(&dev->childs);
+ INIT_LIST_HEAD(&dev->parents);
+ INIT_LIST_HEAD(&dev->ls_roots);
+ INIT_LIST_HEAD(&dev->ls_devices);
+
+ DBG(DEV, ul_debugobj(dev, "alloc"));
+ return dev;
+}
+
+void lsblk_ref_device(struct lsblk_device *dev)
+{
+ if (dev)
+ dev->refcount++;
+}
+
+/* removes dependence from child as well as from parent */
+static int remove_dependence(struct lsblk_devdep *dep)
+{
+ if (!dep)
+ return -EINVAL;
+
+ DBG(DEP, ul_debugobj(dep, " dealloc"));
+
+ list_del_init(&dep->ls_childs);
+ list_del_init(&dep->ls_parents);
+
+ free(dep);
+ return 0;
+}
+
+static int device_remove_dependences(struct lsblk_device *dev)
+{
+ if (!dev)
+ return -EINVAL;
+
+ if (!list_empty(&dev->childs))
+ DBG(DEV, ul_debugobj(dev, " %s: remove all children deps", dev->name));
+ while (!list_empty(&dev->childs)) {
+ struct lsblk_devdep *dp = list_entry(dev->childs.next,
+ struct lsblk_devdep, ls_childs);
+ remove_dependence(dp);
+ }
+
+ if (!list_empty(&dev->parents))
+ DBG(DEV, ul_debugobj(dev, " %s: remove all parents deps", dev->name));
+ while (!list_empty(&dev->parents)) {
+ struct lsblk_devdep *dp = list_entry(dev->parents.next,
+ struct lsblk_devdep, ls_parents);
+ remove_dependence(dp);
+ }
+
+ return 0;
+}
+
+void lsblk_unref_device(struct lsblk_device *dev)
+{
+ if (!dev)
+ return;
+
+ if (--dev->refcount <= 0) {
+ DBG(DEV, ul_debugobj(dev, " freeing [%s] <<", dev->name));
+
+ device_remove_dependences(dev);
+ lsblk_device_free_properties(dev->properties);
+
+ lsblk_unref_device(dev->wholedisk);
+
+ free(dev->dm_name);
+ free(dev->filename);
+ free(dev->mountpoint);
+ free(dev->dedupkey);
+
+ ul_unref_path(dev->sysfs);
+
+ DBG(DEV, ul_debugobj(dev, " >> dealloc [%s]", dev->name));
+ free(dev->name);
+ free(dev);
+ }
+}
+
+int lsblk_device_has_child(struct lsblk_device *dev, struct lsblk_device *child)
+{
+ struct lsblk_device *x = NULL;
+ struct lsblk_iter itr;
+
+ lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD);
+
+ while (lsblk_device_next_child(dev, &itr, &x) == 0) {
+ if (x == child)
+ return 1;
+ }
+
+ return 0;
+}
+
+int lsblk_device_new_dependence(struct lsblk_device *parent, struct lsblk_device *child)
+{
+ struct lsblk_devdep *dp;
+
+ if (!parent || !child)
+ return -EINVAL;
+
+ if (lsblk_device_has_child(parent, child))
+ return 1;
+
+ dp = calloc(1, sizeof(*dp));
+ if (!dp)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&dp->ls_childs);
+ INIT_LIST_HEAD(&dp->ls_parents);
+
+ dp->child = child;
+ list_add_tail(&dp->ls_childs, &parent->childs);
+
+ dp->parent = parent;
+ list_add_tail(&dp->ls_parents, &child->parents);
+
+ DBG(DEV, ul_debugobj(parent, "add dependence 0x%p [%s->%s]", dp, parent->name, child->name));
+
+ return 0;
+}
+
+static int device_next_child(struct lsblk_device *dev,
+ struct lsblk_iter *itr,
+ struct lsblk_devdep **dp)
+{
+ int rc = 1;
+
+ if (!dev || !itr || !dp)
+ return -EINVAL;
+ *dp = NULL;
+
+ if (!itr->head)
+ LSBLK_ITER_INIT(itr, &dev->childs);
+ if (itr->p != itr->head) {
+ LSBLK_ITER_ITERATE(itr, *dp, struct lsblk_devdep, ls_childs);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+int lsblk_device_next_child(struct lsblk_device *dev,
+ struct lsblk_iter *itr,
+ struct lsblk_device **child)
+{
+ struct lsblk_devdep *dp = NULL;
+ int rc = device_next_child(dev, itr, &dp);
+
+ if (!child)
+ return -EINVAL;
+
+ *child = rc == 0 ? dp->child : NULL;
+ 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);
+ if (!dp)
+ return 0;
+ 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()
+{
+ struct lsblk_devtree *tr;
+
+ tr = calloc(1, sizeof(*tr));
+ if (!tr)
+ return NULL;
+
+ tr->refcount = 1;
+
+ INIT_LIST_HEAD(&tr->roots);
+ INIT_LIST_HEAD(&tr->devices);
+
+ DBG(TREE, ul_debugobj(tr, "alloc"));
+ return tr;
+}
+
+void lsblk_ref_devtree(struct lsblk_devtree *tr)
+{
+ if (tr)
+ tr->refcount++;
+}
+
+void lsblk_unref_devtree(struct lsblk_devtree *tr)
+{
+ if (!tr)
+ return;
+
+ if (--tr->refcount <= 0) {
+ DBG(TREE, ul_debugobj(tr, "dealloc"));
+
+ while (!list_empty(&tr->devices)) {
+ struct lsblk_device *dev = list_entry(tr->devices.next,
+ struct lsblk_device, ls_devices);
+ lsblk_devtree_remove_device(tr, dev);
+ }
+ free(tr);
+ }
+}
+
+int lsblk_devtree_add_root(struct lsblk_devtree *tr, struct lsblk_device *dev)
+{
+ if (!lsblk_devtree_has_device(tr, dev))
+ lsblk_devtree_add_device(tr, dev);
+
+ /* We don't increment reference counter for tr->roots list. The primary
+ * reference is tr->devices */
+
+ DBG(TREE, ul_debugobj(tr, "add root device 0x%p [%s]", dev, dev->name));
+ list_add_tail(&dev->ls_roots, &tr->roots);
+ return 0;
+}
+
+int lsblk_devtree_next_root(struct lsblk_devtree *tr,
+ struct lsblk_iter *itr,
+ struct lsblk_device **dev)
+{
+ int rc = 1;
+
+ if (!tr || !itr || !dev)
+ return -EINVAL;
+ *dev = NULL;
+ if (!itr->head)
+ LSBLK_ITER_INIT(itr, &tr->roots);
+ if (itr->p != itr->head) {
+ LSBLK_ITER_ITERATE(itr, *dev, struct lsblk_device, ls_roots);
+ rc = 0;
+ }
+ return rc;
+}
+
+int lsblk_devtree_add_device(struct lsblk_devtree *tr, struct lsblk_device *dev)
+{
+ lsblk_ref_device(dev);
+
+ DBG(TREE, ul_debugobj(tr, "add device 0x%p [%s]", dev, dev->name));
+ list_add_tail(&dev->ls_devices, &tr->devices);
+ return 0;
+}
+
+int lsblk_devtree_next_device(struct lsblk_devtree *tr,
+ struct lsblk_iter *itr,
+ struct lsblk_device **dev)
+{
+ int rc = 1;
+
+ if (!tr || !itr || !dev)
+ return -EINVAL;
+ *dev = NULL;
+ if (!itr->head)
+ LSBLK_ITER_INIT(itr, &tr->devices);
+ if (itr->p != itr->head) {
+ LSBLK_ITER_ITERATE(itr, *dev, struct lsblk_device, ls_devices);
+ rc = 0;
+ }
+ return rc;
+}
+
+int lsblk_devtree_has_device(struct lsblk_devtree *tr, struct lsblk_device *dev)
+{
+ struct lsblk_device *x = NULL;
+ struct lsblk_iter itr;
+
+ lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD);
+
+ while (lsblk_devtree_next_device(tr, &itr, &x) == 0) {
+ if (x == dev)
+ return 1;
+ }
+
+ return 0;
+}
+
+struct lsblk_device *lsblk_devtree_get_device(struct lsblk_devtree *tr, const char *name)
+{
+ struct lsblk_device *dev = NULL;
+ struct lsblk_iter itr;
+
+ lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD);
+
+ while (lsblk_devtree_next_device(tr, &itr, &dev) == 0) {
+ if (strcmp(name, dev->name) == 0)
+ return dev;
+ }
+
+ return NULL;
+}
+
+int lsblk_devtree_remove_device(struct lsblk_devtree *tr, struct lsblk_device *dev)
+{
+ DBG(TREE, ul_debugobj(tr, "remove device 0x%p [%s]", dev, dev->name));
+
+ if (!lsblk_devtree_has_device(tr, dev))
+ return 1;
+
+ list_del_init(&dev->ls_roots);
+ list_del_init(&dev->ls_devices);
+ lsblk_unref_device(dev);
+
+ return 0;
+}
+
+static int device_dedupkey_is_equal(
+ struct lsblk_device *dev,
+ struct lsblk_device *pattern)
+{
+ assert(pattern->dedupkey);
+
+ if (!dev->dedupkey || dev == pattern)
+ return 0;
+ if (strcmp(dev->dedupkey, pattern->dedupkey) == 0) {
+ if (!device_is_partition(dev) ||
+ strcmp(dev->dedupkey, dev->wholedisk->dedupkey) != 0) {
+ DBG(DEV, ul_debugobj(dev, "%s: match deduplication pattern", dev->name));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void device_dedup_dependencies(
+ struct lsblk_device *dev,
+ struct lsblk_device *pattern)
+{
+ struct lsblk_iter itr;
+ struct lsblk_devdep *dp;
+
+ lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD);
+
+ while (device_next_child(dev, &itr, &dp) == 0) {
+ struct lsblk_device *child = dp->child;
+
+ if (device_dedupkey_is_equal(child, pattern)) {
+ DBG(DEV, ul_debugobj(dev, "remove duplicate dependence: 0x%p [%s]",
+ dp->child, dp->child->name));
+ remove_dependence(dp);
+ } else
+ device_dedup_dependencies(child, pattern);
+ }
+}
+
+static void devtree_dedup(struct lsblk_devtree *tr, struct lsblk_device *pattern)
+{
+ struct lsblk_iter itr;
+ struct lsblk_device *dev = NULL;
+
+ lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD);
+
+ DBG(TREE, ul_debugobj(tr, "de-duplicate by key: %s", pattern->dedupkey));
+
+ while (lsblk_devtree_next_root(tr, &itr, &dev) == 0) {
+ if (device_dedupkey_is_equal(dev, pattern)) {
+ DBG(TREE, ul_debugobj(tr, "remove duplicate device: 0x%p [%s]",
+ dev, dev->name));
+ /* Note that root list does not use ref-counting; the
+ * primary reference is ls_devices */
+ list_del_init(&dev->ls_roots);
+ } else
+ device_dedup_dependencies(dev, pattern);
+ }
+}
+
+static int cmp_devices_devno(struct list_head *a, struct list_head *b,
+ __attribute__((__unused__)) void *data)
+{
+ struct lsblk_device *ax = list_entry(a, struct lsblk_device, ls_devices),
+ *bx = list_entry(b, struct lsblk_device, ls_devices);
+
+ return cmp_numbers(makedev(ax->maj, ax->min),
+ makedev(bx->maj, bx->min));
+}
+
+/* Note that dev->dedupkey has to be already set */
+int lsblk_devtree_deduplicate_devices(struct lsblk_devtree *tr)
+{
+ struct lsblk_device *pattern = NULL;
+ struct lsblk_iter itr;
+ char *last = NULL;
+
+ list_sort(&tr->devices, cmp_devices_devno, NULL);
+ lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD);
+
+ while (lsblk_devtree_next_device(tr, &itr, &pattern) == 0) {
+ if (!pattern->dedupkey)
+ continue;
+ if (device_is_partition(pattern) &&
+ strcmp(pattern->dedupkey, pattern->wholedisk->dedupkey) == 0)
+ continue;
+ if (last && strcmp(pattern->dedupkey, last) == 0)
+ continue;
+
+ devtree_dedup(tr, pattern);
+ last = pattern->dedupkey;
+ }
+ return 0;
+}
diff --git a/misc-utils/lsblk-mnt.c b/misc-utils/lsblk-mnt.c
new file mode 100644
index 000000000..c034e34bb
--- /dev/null
+++ b/misc-utils/lsblk-mnt.c
@@ -0,0 +1,127 @@
+#include "c.h"
+#include "pathnames.h"
+#include "xalloc.h"
+#include "nls.h"
+
+#include <libmount.h>
+
+#include "lsblk.h"
+
+static struct libmnt_table *mtab, *swaps;
+static struct libmnt_cache *mntcache;
+
+static int table_parser_errcb(struct libmnt_table *tb __attribute__((__unused__)),
+ const char *filename, int line)
+{
+ if (filename)
+ warnx(_("%s: parse error at line %d -- ignored"), filename, line);
+ return 1;
+}
+
+static int is_active_swap(const char *filename)
+{
+ if (!swaps) {
+ swaps = mnt_new_table();
+ if (!swaps)
+ return 0;
+ if (!mntcache)
+ mntcache = mnt_new_cache();
+
+ mnt_table_set_parser_errcb(swaps, table_parser_errcb);
+ mnt_table_set_cache(swaps, mntcache);
+
+ if (!lsblk->sysroot)
+ mnt_table_parse_swaps(swaps, NULL);
+ else {
+ char buf[PATH_MAX];
+ snprintf(buf, sizeof(buf), "%s" _PATH_PROC_SWAPS, lsblk->sysroot);
+ mnt_table_parse_swaps(swaps, buf);
+ }
+ }
+
+ return mnt_table_find_srcpath(swaps, filename, MNT_ITER_BACKWARD) != NULL;
+}
+
+char *lsblk_device_get_mountpoint(struct lsblk_device *dev)
+{
+ struct libmnt_fs *fs;
+ const char *fsroot;
+
+ assert(dev);
+ assert(dev->filename);
+
+ if (dev->is_mounted || dev->is_swap)
+ return dev->mountpoint;
+
+ if (!mtab) {
+ mtab = mnt_new_table();
+ if (!mtab)
+ return NULL;
+ if (!mntcache)
+ mntcache = mnt_new_cache();
+
+ mnt_table_set_parser_errcb(mtab, table_parser_errcb);
+ mnt_table_set_cache(mtab, mntcache);
+
+ if (!lsblk->sysroot)
+ mnt_table_parse_mtab(mtab, NULL);
+ else {
+ char buf[PATH_MAX];
+ snprintf(buf, sizeof(buf), "%s" _PATH_PROC_MOUNTINFO, lsblk->sysroot);
+ mnt_table_parse_mtab(mtab, buf);
+ }
+ }
+
+ /* Note that maj:min in /proc/self/mountinfo does not have to match with
+ * devno as returned by stat(), so we have to try devname too
+ */
+ fs = mnt_table_find_devno(mtab, makedev(dev->maj, dev->min), MNT_ITER_BACKWARD);
+ if (!fs)
+ fs = mnt_table_find_srcpath(mtab, dev->filename, MNT_ITER_BACKWARD);
+ if (!fs) {
+ if (is_active_swap(dev->filename)) {
+ dev->mountpoint = xstrdup("[SWAP]");
+ dev->is_swap = 1;
+ } else
+ dev->mountpoint = NULL;
+
+ return dev->mountpoint;
+ }
+
+ /* found */
+ fsroot = mnt_fs_get_root(fs);
+ if (fsroot && strcmp(fsroot, "/") != 0) {
+ /* hmm.. we found bind mount or btrfs subvolume, let's try to
+ * get real FS root mountpoint */
+ struct libmnt_fs *rfs;
+ struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_BACKWARD);
+
+ mnt_table_set_iter(mtab, itr, fs);
+ while (mnt_table_next_fs(mtab, itr, &rfs) == 0) {
+ fsroot = mnt_fs_get_root(rfs);
+ if ((!fsroot || strcmp(fsroot, "/") == 0)
+ && mnt_fs_match_source(rfs, dev->filename, mntcache)) {
+ fs = rfs;
+ break;
+ }
+ }
+ mnt_free_iter(itr);
+ }
+
+ DBG(DEV, ul_debugobj(dev, "mountpoint: %s", mnt_fs_get_target(fs)));
+ dev->mountpoint = xstrdup(mnt_fs_get_target(fs));
+ dev->is_mounted = 1;
+ return dev->mountpoint;
+}
+
+void lsblk_mnt_init(void)
+{
+ mnt_init_debug(0);
+}
+
+void lsblk_mnt_deinit(void)
+{
+ mnt_unref_table(mtab);
+ mnt_unref_table(swaps);
+ mnt_unref_cache(mntcache);
+}
diff --git a/misc-utils/lsblk-properties.c b/misc-utils/lsblk-properties.c
new file mode 100644
index 000000000..30168dd48
--- /dev/null
+++ b/misc-utils/lsblk-properties.c
@@ -0,0 +1,188 @@
+
+#include <blkid.h>
+
+#ifdef HAVE_LIBUDEV
+# include <libudev.h>
+#endif
+
+#include "c.h"
+#include "xalloc.h"
+#include "mangle.h"
+
+#include "lsblk.h"
+
+#ifdef HAVE_LIBUDEV
+static struct udev *udev;
+#endif
+
+void lsblk_device_free_properties(struct lsblk_devprop *p)
+{
+ if (!p)
+ return;
+
+ free(p->fstype);
+ free(p->uuid);
+ free(p->ptuuid);
+ free(p->pttype);
+ free(p->label);
+ free(p->parttype);
+ free(p->partuuid);
+ free(p->partlabel);
+ free(p->wwn);
+ free(p->serial);
+ free(p->model);
+
+ free(p);
+}
+
+#ifndef HAVE_LIBUDEV
+static struct lsblk_devprop *get_properties_by_udev(struct lsblk_device *dev
+ __attribute__((__unused__)))
+{
+ return NULL;
+}
+#else
+static struct lsblk_devprop *get_properties_by_udev(struct lsblk_device *ld)
+{
+ struct udev_device *dev;
+
+ if (ld->udev_requested)
+ return ld->properties;
+
+ if (lsblk->sysroot)
+ goto done;
+ if (!udev)
+ udev = udev_new(); /* global handler */
+ if (!udev)
+ goto done;
+
+ dev = udev_device_new_from_subsystem_sysname(udev, "block", ld->name);
+ if (dev) {
+ const char *data;
+ struct lsblk_devprop *prop;
+
+ if (ld->properties)
+ lsblk_device_free_properties(ld->properties);
+ prop = ld->properties = xcalloc(1, sizeof(*ld->properties));
+
+ if ((data = udev_device_get_property_value(dev, "ID_FS_LABEL_ENC"))) {
+ prop->label = xstrdup(data);
+ unhexmangle_string(prop->label);
+ }
+ if ((data = udev_device_get_property_value(dev, "ID_FS_UUID_ENC"))) {
+ prop->uuid = xstrdup(data);
+ unhexmangle_string(prop->uuid);
+ }
+ if ((data = udev_device_get_property_value(dev, "ID_PART_TABLE_UUID")))
+ prop->ptuuid = xstrdup(data);
+ if ((data = udev_device_get_property_value(dev, "ID_PART_TABLE_TYPE")))
+ prop->pttype = xstrdup(data);
+ if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_NAME"))) {
+ prop->partlabel = xstrdup(data);
+ unhexmangle_string(prop->partlabel);
+ }
+ if ((data = udev_device_get_property_value(dev, "ID_FS_TYPE")))
+ prop->fstype = xstrdup(data);
+ if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_TYPE")))
+ prop->parttype = xstrdup(data);
+ if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_UUID")))
+ prop->partuuid = xstrdup(data);
+ if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_FLAGS")))
+ prop->partflags = xstrdup(data);
+
+ data = udev_device_get_property_value(dev, "ID_WWN_WITH_EXTENSION");
+ if (!data)
+ data = udev_device_get_property_value(dev, "ID_WWN");
+ if (data)
+ prop->wwn = xstrdup(data);
+
+ if ((data = udev_device_get_property_value(dev, "ID_SERIAL_SHORT")))
+ prop->serial = xstrdup(data);
+ if ((data = udev_device_get_property_value(dev, "ID_MODEL")))
+ prop->model = xstrdup(data);
+
+ udev_device_unref(dev);
+ DBG(DEV, ul_debugobj(ld, "%s: found udev properties", ld->name));
+ }
+
+done:
+ ld->udev_requested = 1;
+ return ld->properties;
+}
+#endif /* HAVE_LIBUDEV */
+
+static struct lsblk_devprop *get_properties_by_blkid(struct lsblk_device *dev)
+{
+ blkid_probe pr = NULL;
+
+ if (dev->blkid_requested)
+ return dev->properties;
+
+ if (!dev->size)
+ goto done;
+ if (getuid() != 0)
+ goto done;; /* no permissions to read from the device */
+
+ pr = blkid_new_probe_from_filename(dev->filename);
+ if (!pr)
+ goto done;
+
+ blkid_probe_enable_superblocks(pr, 1);
+ blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_LABEL |
+ BLKID_SUBLKS_UUID |
+ BLKID_SUBLKS_TYPE);
+ blkid_probe_enable_partitions(pr, 1);
+ blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
+
+ if (!blkid_do_safeprobe(pr)) {
+ const char *data = NULL;
+ struct lsblk_devprop *prop;
+
+ if (dev->properties)
+ lsblk_device_free_properties(dev->properties);
+ prop = dev->properties = xcalloc(1, sizeof(*dev->properties));
+
+ if (!blkid_probe_lookup_value(pr, "TYPE", &data, NULL))
+ prop->fstype = xstrdup(data);
+ if (!blkid_probe_lookup_value(pr, "UUID", &data, NULL))
+ prop->uuid = xstrdup(data);
+ if (!blkid_probe_lookup_value(pr, "PTUUID", &data, NULL))
+ prop->ptuuid = xstrdup(data);
+ if (!blkid_probe_lookup_value(pr, "PTTYPE", &data, NULL))
+ prop->pttype = xstrdup(data);
+ if (!blkid_probe_lookup_value(pr, "LABEL", &data, NULL))
+ prop->label = xstrdup(data);
+ if (!blkid_probe_lookup_value(pr, "PART_ENTRY_TYPE", &data, NULL))
+ prop->parttype = xstrdup(data);
+ if (!blkid_probe_lookup_value(pr, "PART_ENTRY_UUID", &data, NULL))
+ prop->partuuid = xstrdup(data);
+ if (!blkid_probe_lookup_value(pr, "PART_ENTRY_NAME", &data, NULL))
+ prop->partlabel = xstrdup(data);
+ if (!blkid_probe_lookup_value(pr, "PART_ENTRY_FLAGS", &data, NULL))
+ prop->partflags = xstrdup(data);
+
+ DBG(DEV, ul_debugobj(dev, "%s: found blkid properties", dev->name));
+ }
+
+done:
+ blkid_free_probe(pr);
+
+ dev->blkid_requested = 1;
+ return dev->properties;
+}
+
+struct lsblk_devprop *lsblk_device_get_properties(struct lsblk_device *dev)
+{
+ struct lsblk_devprop *p = get_properties_by_udev(dev);
+
+ if (!p)
+ p = get_properties_by_blkid(dev);
+ return p;
+}
+
+void lsblk_properties_deinit(void)
+{
+#ifdef HAVE_LIBUDEV
+ udev_unref(udev);
+#endif
+}
diff --git a/misc-utils/lsblk.8 b/misc-utils/lsblk.8
index 708381448..2787569fe 100644
--- a/misc-utils/lsblk.8
+++ b/misc-utils/lsblk.8
@@ -51,17 +51,23 @@ 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.
-The filter is applied to the top-level devices only.
+The filter is applied to the top-level devices only. This maybe be confusing for
+\fB\-\-list\fR output format where hierarchy of the devices is not obvious.
.TP
.BR \-f , " \-\-fs"
Output info about filesystems. This option is equivalent to
@@ -75,7 +81,8 @@ Display help text and exit.
.TP
.BR \-I , " \-\-include " \fIlist\fP
Include devices specified by the comma-separated \fIlist\fR of major device numbers.
-The filter is applied to the top-level devices only.
+The filter is applied to the top-level devices only. This maybe be confusing for
+\fB\-\-list\fR output format where hierarchy of the devices is not obvious.
.TP
.BR \-i , " \-\-ascii"
Use ASCII characters for tree formatting.
@@ -84,7 +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 , " \-\-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
@@ -134,6 +147,15 @@ Display version information and exit.
Sort output lines by \fIcolumn\fP. This option enables \fB\-\-list\fR output format by default.
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
+instance to be inspected. This option is designed for the testing purpose.
+
.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 fadef65c4..ffa1f82bf 100644
--- a/misc-utils/lsblk.c
+++ b/misc-utils/lsblk.c
@@ -1,7 +1,7 @@
/*
* lsblk(8) - list block devices
*
- * Copyright (C) 2010,2011,2012 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2010-2018 Red Hat, Inc. All rights reserved.
* Written by Milan Broz <mbroz@redhat.com>
* Karel Zak <kzak@redhat.com>
*
@@ -19,35 +19,25 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-
#include <stdio.h>
#include <errno.h>
#include <getopt.h>
#include <stdlib.h>
#include <unistd.h>
-#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
-#include <inttypes.h>
#include <stdarg.h>
#include <locale.h>
#include <pwd.h>
#include <grp.h>
#include <ctype.h>
+#include <assert.h>
#include <blkid.h>
-#include <libmount.h>
-#include <libsmartcols.h>
-
-#ifdef HAVE_LIBUDEV
-#include <libudev.h>
-#endif
-
-#include <assert.h>
#include "c.h"
#include "pathnames.h"
@@ -58,26 +48,13 @@
#include "strutils.h"
#include "sysfs.h"
#include "closestream.h"
-#include "mangle.h"
#include "optutils.h"
-#include "debug.h"
+#include "lsblk.h"
-static UL_DEBUG_DEFINE_MASK(lsblk);
+UL_DEBUG_DEFINE_MASK(lsblk);
UL_DEBUG_DEFINE_MASKNAMES(lsblk) = UL_DEBUG_EMPTY_MASKNAMES;
-#define LSBLK_DEBUG_INIT (1 << 1)
-#define LSBLK_DEBUG_FILTER (1 << 2)
-#define LSBLK_DEBUG_DEV (1 << 3)
-#define LSBLK_DEBUG_CXT (1 << 4)
-#define LSBLK_DEBUG_ALL 0xFFFF
-
-#define DBG(m, x) __UL_DBG(lsblk, LSBLK_DEBUG_, m, x)
-#define ON_DBG(m, x) __UL_DBG_CALL(lsblk, LSBLK_DEBUG_, m, x)
-
-#define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(lsblk)
-#include "debugobj.h"
-
#define LSBLK_EXIT_SOMEOK 64
#define LSBLK_EXIT_ALLFAILED 32
@@ -87,11 +64,18 @@ static int column_id_to_number(int id);
enum {
COL_NAME = 0,
COL_KNAME,
+ COL_PATH,
COL_MAJMIN,
+ COL_FSAVAIL,
+ COL_FSSIZE,
COL_FSTYPE,
+ COL_FSUSED,
+ COL_FSUSEPERC,
COL_TARGET,
COL_LABEL,
COL_UUID,
+ COL_PTUUID,
+ COL_PTTYPE,
COL_PARTTYPE,
COL_PARTLABEL,
COL_PARTUUID,
@@ -157,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_* */
};
@@ -165,13 +148,23 @@ struct colinfo {
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_PKNAME] = { "PKNAME", 0.3, 0, N_("internal parent kernel device name") },
+ [COL_PATH] = { "PATH", 0.3, 0, N_("path to the device node") },
[COL_MAJMIN] = { "MAJ:MIN", 6, 0, N_("major:minor device number"), COLTYPE_SORTNUM },
- [COL_FSTYPE] = { "FSTYPE", 0.1, SCOLS_FL_TRUNC, N_("filesystem type") },
+
+ [COL_FSAVAIL] = { "FSAVAIL", 5, SCOLS_FL_RIGHT, N_("filesystem size available") },
+ [COL_FSSIZE] = { "FSSIZE", 5, SCOLS_FL_RIGHT, N_("filesystem size") },
+ [COL_FSTYPE] = { "FSTYPE", 0.1, SCOLS_FL_TRUNC, N_("filesystem type") },
+ [COL_FSUSED] = { "FSUSED", 5, SCOLS_FL_RIGHT, N_("filesystem size used") },
+ [COL_FSUSEPERC] = { "FSUSE%", 3, SCOLS_FL_RIGHT, N_("filesystem use percentage") },
+
[COL_TARGET] = { "MOUNTPOINT", 0.10, SCOLS_FL_TRUNC, N_("where the device is mounted") },
[COL_LABEL] = { "LABEL", 0.1, 0, N_("filesystem LABEL") },
[COL_UUID] = { "UUID", 36, 0, N_("filesystem UUID") },
+ [COL_PTUUID] = { "PTUUID", 36, 0, N_("partition table identifier (usually UUID)") },
+ [COL_PTTYPE] = { "PTTYPE", 0.1, 0, N_("partition table type") },
+
[COL_PARTTYPE] = { "PARTTYPE", 36, 0, N_("partition type UUID") },
[COL_PARTLABEL] = { "PARTLABEL", 0.1, 0, N_("partition LABEL") },
[COL_PARTUUID] = { "PARTUUID", 36, 0, N_("partition UUID") },
@@ -212,33 +205,17 @@ static struct colinfo infos[] = {
[COL_ZONED] = { "ZONED", 0.3, 0, N_("zone model") },
};
-struct lsblk {
- struct libscols_table *table; /* output table */
- struct libscols_column *sort_col;/* sort output by this column */
- int sort_id;
-
- int flags; /* 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 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 */
- unsigned int sort_hidden:1; /* sort column not between output columns */
- unsigned int force_tree_order:1;/* sort lines by parent->tree relation */
-};
+struct lsblk *lsblk; /* global handler */
-static struct lsblk *lsblk; /* global handler */
-
-/* columns[] array specifies all currently wanted output column. The columns
+/*
+ * columns[] array specifies all currently wanted output column. The columns
* are defined by infos[] array and you can specify (on command line) each
* column twice. That's enough, dynamically allocated array of the columns is
- * unnecessary overkill and over-engineering in this case */
+ * unnecessary overkill and over-engineering in this case
+ */
static int columns[ARRAY_SIZE(infos) * 2];
static size_t ncolumns;
-
static inline void add_column(int id)
{
if (ncolumns >= ARRAY_SIZE(columns))
@@ -254,60 +231,20 @@ static inline void add_uniq_column(int id)
add_column(id);
}
+static void lsblk_init_debug(void)
+{
+ __UL_INIT_DEBUG_FROM_ENV(lsblk, LSBLK_DEBUG_, 0, LSBLK_DEBUG);
+}
+
+/*
+ * exclude/include devices filter based on major device numbers
+ */
static int excludes[256];
static size_t nexcludes;
static int includes[256];
static size_t nincludes;
-static struct libmnt_table *mtab, *swaps;
-static struct libmnt_cache *mntcache;
-
-#ifdef HAVE_LIBUDEV
-static struct udev *udev;
-#endif
-
-struct blkdev_cxt {
- struct blkdev_cxt *parent;
-
- struct libscols_line *scols_line;
- struct stat st;
-
- char *name; /* kernel name in /sys/block */
- char *dm_name; /* DM name (dm/block) */
-
- char *filename; /* path to device node */
-
- struct sysfs_cxt sysfs;
-
- int partition; /* is partition? TRUE/FALSE */
-
- int probed; /* already probed */
- char *fstype; /* detected fs, NULL or "?" if cannot detect */
- char *uuid; /* filesystem UUID (or stack uuid) */
- char *label; /* filesystem label */
- char *parttype; /* partition type UUID */
- char *partuuid; /* partition UUID */
- char *partlabel; /* partition label */
- char *partflags; /* partition flags */
- char *wwn; /* storage WWN */
- char *serial; /* disk serial number */
-
- int npartitions; /* # of partitions this device has */
- int nholders; /* # of devices mapped directly to this device
- * /sys/block/.../holders */
- int nslaves; /* # of devices this device maps to */
- int maj, min; /* devno */
- int discard; /* supports discard */
-
- uint64_t size; /* device size */
-};
-
-static void lsblk_init_debug(void)
-{
- __UL_INIT_DEBUG_FROM_ENV(lsblk, LSBLK_DEBUG_, 0, LSBLK_DEBUG);
-}
-
static int is_maj_excluded(int maj)
{
size_t i;
@@ -344,7 +281,7 @@ static int is_maj_included(int maj)
return 0;
}
-/* array with IDs of enabled columns */
+/* Converts column sequential number to column ID (COL_*) */
static int get_column_id(int num)
{
assert(num >= 0);
@@ -353,11 +290,13 @@ static int get_column_id(int num)
return columns[num];
}
+/* Returns column description for the column sequential number */
static struct colinfo *get_column_info(int num)
{
return &infos[ get_column_id(num) ];
}
+/* Converts column name (as defined in the infos[] to the column ID */
static int column_name_to_id(const char *name, size_t namesz)
{
size_t i;
@@ -372,6 +311,7 @@ static int column_name_to_id(const char *name, size_t namesz)
return -1;
}
+/* Converts column ID (COL_*) to column sequential number */
static int column_id_to_number(int id)
{
size_t i;
@@ -382,35 +322,13 @@ static int column_id_to_number(int id)
return -1;
}
-static void reset_blkdev_cxt(struct blkdev_cxt *cxt)
-{
- if (!cxt)
- return;
-
- DBG(CXT, ul_debugobj(cxt, "reset"));
-
- free(cxt->name);
- free(cxt->dm_name);
- free(cxt->filename);
- free(cxt->fstype);
- free(cxt->uuid);
- free(cxt->label);
- free(cxt->parttype);
- free(cxt->partuuid);
- free(cxt->partlabel);
- free(cxt->wwn);
- free(cxt->serial);
-
- sysfs_deinit(&cxt->sysfs);
-
- memset(cxt, 0, sizeof(*cxt));
-}
-
+/* Checks for DM prefix in the device name */
static int is_dm(const char *name)
{
return strncmp(name, "dm-", 3) ? 0 : 1;
}
+/* This is readdir()-like function, but skips "." and ".." directory entries */
static struct dirent *xreaddir(DIR *dp)
{
struct dirent *d;
@@ -428,225 +346,31 @@ static struct dirent *xreaddir(DIR *dp)
return d;
}
-static char *get_device_path(struct blkdev_cxt *cxt)
+/* Returns full pat to the device node (TODO: what about sysfs_blkdev_get_path()) */
+static char *get_device_path(struct lsblk_device *dev)
{
char path[PATH_MAX];
- assert(cxt);
- assert(cxt->name);
+ assert(dev);
+ assert(dev->name);
- if (is_dm(cxt->name))
- return canonicalize_dm_name(cxt->name);
+ if (is_dm(dev->name))
+ return __canonicalize_dm_name(lsblk->sysroot, dev->name);
- snprintf(path, sizeof(path), "/dev/%s", cxt->name);
+ snprintf(path, sizeof(path), "/dev/%s", dev->name);
sysfs_devname_sys_to_dev(path);
return xstrdup(path);
}
-static int table_parser_errcb(struct libmnt_table *tb __attribute__((__unused__)),
- const char *filename, int line)
-{
- if (filename)
- warnx(_("%s: parse error at line %d -- ignored"), filename, line);
- return 1;
-}
-
-static int is_active_swap(const char *filename)
-{
- if (!swaps) {
- swaps = mnt_new_table();
- if (!swaps)
- return 0;
- if (!mntcache)
- mntcache = mnt_new_cache();
-
- mnt_table_set_parser_errcb(swaps, table_parser_errcb);
- mnt_table_set_cache(swaps, mntcache);
- mnt_table_parse_swaps(swaps, NULL);
- }
-
- return mnt_table_find_srcpath(swaps, filename, MNT_ITER_BACKWARD) != NULL;
-}
-
-static char *get_device_mountpoint(struct blkdev_cxt *cxt)
-{
- struct libmnt_fs *fs;
- const char *fsroot;
-
- assert(cxt);
- assert(cxt->filename);
-
- if (!mtab) {
- mtab = mnt_new_table();
- if (!mtab)
- return NULL;
- if (!mntcache)
- mntcache = mnt_new_cache();
-
- mnt_table_set_parser_errcb(mtab, table_parser_errcb);
- mnt_table_set_cache(mtab, mntcache);
- mnt_table_parse_mtab(mtab, NULL);
- }
-
- /* Note that maj:min in /proc/self/mountinfo does not have to match with
- * devno as returned by stat(), so we have to try devname too
- */
- fs = mnt_table_find_devno(mtab, makedev(cxt->maj, cxt->min), MNT_ITER_BACKWARD);
- if (!fs)
- fs = mnt_table_find_srcpath(mtab, cxt->filename, MNT_ITER_BACKWARD);
- if (!fs)
- return is_active_swap(cxt->filename) ? xstrdup("[SWAP]") : NULL;
-
- /* found */
- fsroot = mnt_fs_get_root(fs);
- if (fsroot && strcmp(fsroot, "/") != 0) {
- /* hmm.. we found bind mount or btrfs subvolume, let's try to
- * get real FS root mountpoint */
- struct libmnt_fs *rfs;
- struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_BACKWARD);
-
- mnt_table_set_iter(mtab, itr, fs);
- while (mnt_table_next_fs(mtab, itr, &rfs) == 0) {
- fsroot = mnt_fs_get_root(rfs);
- if ((!fsroot || strcmp(fsroot, "/") == 0)
- && mnt_fs_match_source(rfs, cxt->filename, mntcache)) {
- fs = rfs;
- break;
- }
- }
- mnt_free_iter(itr);
- }
-
- DBG(DEV, ul_debugobj(cxt, "mountpoint: %s", mnt_fs_get_target(fs)));
- return xstrdup(mnt_fs_get_target(fs));
-}
-
-#ifndef HAVE_LIBUDEV
-static int get_udev_properties(struct blkdev_cxt *cxt
- __attribute__((__unused__)))
-{
- return -1;
-}
-#else
-static int get_udev_properties(struct blkdev_cxt *cxt)
-{
- struct udev_device *dev;
-
- if (cxt->probed)
- return 0; /* already done */
-
- if (!udev)
- udev = udev_new();
- if (!udev)
- return -1;
-
- dev = udev_device_new_from_subsystem_sysname(udev, "block", cxt->name);
- if (dev) {
- const char *data;
-
- if ((data = udev_device_get_property_value(dev, "ID_FS_LABEL_ENC"))) {
- cxt->label = xstrdup(data);
- unhexmangle_string(cxt->label);
- }
- if ((data = udev_device_get_property_value(dev, "ID_FS_UUID_ENC"))) {
- cxt->uuid = xstrdup(data);
- unhexmangle_string(cxt->uuid);
- }
- if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_NAME"))) {
- cxt->partlabel = xstrdup(data);
- unhexmangle_string(cxt->partlabel);
- }
- if ((data = udev_device_get_property_value(dev, "ID_FS_TYPE")))
- cxt->fstype = xstrdup(data);
- if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_TYPE")))
- cxt->parttype = xstrdup(data);
- if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_UUID")))
- cxt->partuuid = xstrdup(data);
- if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_FLAGS")))
- cxt->partflags = xstrdup(data);
-
- data = udev_device_get_property_value(dev, "ID_WWN_WITH_EXTENSION");
- if (!data)
- data = udev_device_get_property_value(dev, "ID_WWN");
- if (data)
- cxt->wwn = xstrdup(data);
-
- if ((data = udev_device_get_property_value(dev, "ID_SERIAL_SHORT")))
- cxt->serial = xstrdup(data);
- udev_device_unref(dev);
- cxt->probed = 1;
- DBG(DEV, ul_debugobj(cxt, "%s: found udev properties", cxt->name));
- }
-
- return cxt->probed == 1 ? 0 : -1;
-
-}
-#endif /* HAVE_LIBUDEV */
-
-static void probe_device(struct blkdev_cxt *cxt)
-{
- blkid_probe pr = NULL;
-
- if (cxt->probed)
- return;
-
- if (!cxt->size)
- return;
-
- /* try udev DB */
- if (get_udev_properties(cxt) == 0)
- return; /* success */
-
- cxt->probed = 1;
-
- /* try libblkid (fallback) */
- if (getuid() != 0)
- return; /* no permissions to read from the device */
-
- pr = blkid_new_probe_from_filename(cxt->filename);
- if (!pr)
- return;
-
- blkid_probe_enable_superblocks(pr, 1);
- blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_LABEL |
- BLKID_SUBLKS_UUID |
- BLKID_SUBLKS_TYPE);
- blkid_probe_enable_partitions(pr, 1);
- blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
-
- if (!blkid_do_safeprobe(pr)) {
- const char *data = NULL;
-
- if (!blkid_probe_lookup_value(pr, "TYPE", &data, NULL))
- cxt->fstype = xstrdup(data);
- if (!blkid_probe_lookup_value(pr, "UUID", &data, NULL))
- cxt->uuid = xstrdup(data);
- if (!blkid_probe_lookup_value(pr, "LABEL", &data, NULL))
- cxt->label = xstrdup(data);
- if (!blkid_probe_lookup_value(pr, "PART_ENTRY_TYPE", &data, NULL))
- cxt->parttype = xstrdup(data);
- if (!blkid_probe_lookup_value(pr, "PART_ENTRY_UUID", &data, NULL))
- cxt->partuuid = xstrdup(data);
- if (!blkid_probe_lookup_value(pr, "PART_ENTRY_NAME", &data, NULL))
- cxt->partlabel = xstrdup(data);
- if (!blkid_probe_lookup_value(pr, "PART_ENTRY_FLAGS", &data, NULL))
- cxt->partflags = xstrdup(data);
- DBG(DEV, ul_debugobj(cxt, "%s: found blkid properties", cxt->name));
- }
-
- blkid_free_probe(pr);
- return;
-}
-
-static int is_readonly_device(struct blkdev_cxt *cxt)
+static int is_readonly_device(struct lsblk_device *dev)
{
int fd, ro = 0;
- if (sysfs_scanf(&cxt->sysfs, "ro", "%d", &ro) == 1)
+ if (ul_path_scanf(dev->sysfs, "ro", "%d", &ro) == 1)
return ro;
/* fallback if "ro" attribute does not exist */
- fd = open(cxt->filename, O_RDONLY);
+ fd = open(dev->filename, O_RDONLY);
if (fd != -1) {
if (ioctl(fd, BLKROGET, &ro) != 0)
ro = 0;
@@ -655,14 +379,14 @@ static int is_readonly_device(struct blkdev_cxt *cxt)
return ro;
}
-static char *get_scheduler(struct blkdev_cxt *cxt)
+static char *get_scheduler(struct lsblk_device *dev)
{
- char *str = sysfs_strdup(&cxt->sysfs, "queue/scheduler");
+ char buf[128];
char *p, *res = NULL;
- if (!str)
+ if (ul_path_read_buffer(dev->sysfs, buf, sizeof(buf), "queue/scheduler") == 0)
return NULL;
- p = strchr(str, '[');
+ p = strchr(buf, '[');
if (p) {
res = p + 1;
p = strchr(res, ']');
@@ -672,20 +396,23 @@ static char *get_scheduler(struct blkdev_cxt *cxt)
} else
res = NULL;
}
- free(str);
return res;
}
-static char *get_type(struct blkdev_cxt *cxt)
+static char *get_type(struct lsblk_device *dev)
{
char *res = NULL, *p;
- if (is_dm(cxt->name)) {
- char *dm_uuid = sysfs_strdup(&cxt->sysfs, "dm/uuid");
+ if (device_is_partition(dev))
+ return xstrdup("part");
+
+ if (is_dm(dev->name)) {
+ char *dm_uuid = NULL;
/* The DM_UUID prefix should be set to subsystem owning
* the device - LVM, CRYPT, DMRAID, MPATH, PART */
- if (dm_uuid) {
+ if (ul_path_read_string(dev->sysfs, &dm_uuid, "dm/uuid") > 0
+ && dm_uuid) {
char *tmp = dm_uuid;
char *dm_uuid_prefix = strsep(&tmp, "-");
@@ -703,22 +430,23 @@ static char *get_type(struct blkdev_cxt *cxt)
/* No UUID or no prefix - just mark it as DM device */
res = xstrdup("dm");
- } else if (!strncmp(cxt->name, "loop", 4)) {
+ } else if (!strncmp(dev->name, "loop", 4)) {
res = xstrdup("loop");
- } else if (!strncmp(cxt->name, "md", 2)) {
- char *md_level = sysfs_strdup(&cxt->sysfs, "md/level");
+ } else if (!strncmp(dev->name, "md", 2)) {
+ char *md_level = NULL;
+
+ ul_path_read_string(dev->sysfs, &md_level, "md/level");
res = md_level ? md_level : xstrdup("md");
} else {
const char *type = NULL;
int x = 0;
- if (!sysfs_read_int(&cxt->sysfs, "device/type", &x))
+ if (ul_path_read_s32(dev->sysfs, &x, "device/type") == 0)
type = blkdev_scsi_type_to_name(x);
-
if (!type)
- type = cxt->partition ? "part" : "disk";
+ type = "disk";
res = xstrdup(type);
}
@@ -728,19 +456,20 @@ static char *get_type(struct blkdev_cxt *cxt)
}
/* Thanks to lsscsi code for idea of detection logic used here */
-static char *get_transport(struct blkdev_cxt *cxt)
+static char *get_transport(struct lsblk_device *dev)
{
- struct sysfs_cxt *sysfs = &cxt->sysfs;
+ struct path_cxt *sysfs = dev->sysfs;
char *attr = NULL;
const char *trans = NULL;
+
/* SCSI - Serial Peripheral Interface */
- if (sysfs_scsi_host_is(sysfs, "spi"))
+ if (sysfs_blkdev_scsi_host_is(sysfs, "spi"))
trans = "spi";
/* FC/FCoE - Fibre Channel / Fibre Channel over Ethernet */
- else if (sysfs_scsi_host_is(sysfs, "fc")) {
- attr = sysfs_scsi_host_strdup_attribute(sysfs, "fc", "symbolic_name");
+ else if (sysfs_blkdev_scsi_host_is(sysfs, "fc")) {
+ attr = sysfs_blkdev_scsi_host_strdup_attribute(sysfs, "fc", "symbolic_name");
if (!attr)
return NULL;
trans = strstr(attr, " over ") ? "fcoe" : "fc";
@@ -748,26 +477,26 @@ static char *get_transport(struct blkdev_cxt *cxt)
}
/* SAS - Serial Attached SCSI */
- else if (sysfs_scsi_host_is(sysfs, "sas") ||
- sysfs_scsi_has_attribute(sysfs, "sas_device"))
+ else if (sysfs_blkdev_scsi_host_is(sysfs, "sas") ||
+ sysfs_blkdev_scsi_has_attribute(sysfs, "sas_device"))
trans = "sas";
/* SBP - Serial Bus Protocol (FireWire) */
- else if (sysfs_scsi_has_attribute(sysfs, "ieee1394_id"))
+ else if (sysfs_blkdev_scsi_has_attribute(sysfs, "ieee1394_id"))
trans = "sbp";
/* iSCSI */
- else if (sysfs_scsi_host_is(sysfs, "iscsi"))
+ else if (sysfs_blkdev_scsi_host_is(sysfs, "iscsi"))
trans ="iscsi";
/* USB - Universal Serial Bus */
- else if (sysfs_scsi_path_contains(sysfs, "usb"))
+ else if (sysfs_blkdev_scsi_path_contains(sysfs, "usb"))
trans = "usb";
/* ATA, SATA */
- else if (sysfs_scsi_host_is(sysfs, "scsi")) {
- attr = sysfs_scsi_host_strdup_attribute(sysfs, "scsi", "proc_name");
+ else if (sysfs_blkdev_scsi_host_is(sysfs, "scsi")) {
+ attr = sysfs_blkdev_scsi_host_strdup_attribute(sysfs, "scsi", "proc_name");
if (!attr)
return NULL;
if (!strncmp(attr, "ahci", 4) || !strncmp(attr, "sata", 4))
@@ -776,23 +505,23 @@ static char *get_transport(struct blkdev_cxt *cxt)
trans = "ata";
free(attr);
- } else if (strncmp(cxt->name, "nvme", 4) == 0)
+ } else if (strncmp(dev->name, "nvme", 4) == 0)
trans = "nvme";
return trans ? xstrdup(trans) : NULL;
}
-static char *get_subsystems(struct blkdev_cxt *cxt)
+static char *get_subsystems(struct lsblk_device *dev)
{
char path[PATH_MAX];
char *sub, *chain, *res = NULL;
size_t len = 0, last = 0;
- chain = sysfs_get_devchain(&cxt->sysfs, path, sizeof(path));
+ chain = sysfs_blkdev_get_devchain(dev->sysfs, path, sizeof(path));
if (!chain)
return NULL;
- while (sysfs_next_subsystem(&cxt->sysfs, chain, &sub) == 0) {
+ while (sysfs_blkdev_next_subsystem(dev->sysfs, chain, &sub) == 0) {
size_t sz;
/* don't create "block:scsi:scsi", but "block:scsi" */
@@ -861,14 +590,20 @@ static void set_sortdata_u64(struct libscols_line *ln, int col, uint64_t x)
scols_cell_set_userdata(ce, data);
}
-static void set_sortdata_u64_from_string(struct libscols_line *ln, int col, const char *str)
+/* do not modify *data on any error */
+static void str2u64(const char *str, uint64_t *data)
{
- uint64_t x;
+ uintmax_t num;
+ char *end = NULL;
- if (!str || sscanf(str, "%"SCNu64, &x) != 1)
+ errno = 0;
+ if (str == NULL || *str == '\0')
return;
+ num = strtoumax(str, &end, 10);
- set_sortdata_u64(ln, col, x);
+ if (errno || str == end || (end && *end))
+ return;
+ *data = num;
}
static void unref_sortdata(struct libscols_table *tb)
@@ -891,671 +626,973 @@ static void unref_sortdata(struct libscols_table *tb)
scols_free_iter(itr);
}
-static void set_scols_data(struct blkdev_cxt *cxt, int col, int id, struct libscols_line *ln)
+static char *get_vfs_attribute(struct lsblk_device *dev, int id)
{
- int sort = 0, st_rc = 0;
- char *str = NULL;
+ char *sizestr;
+ uint64_t vfs_attr = 0;
+ char *mnt;
+
+ if (!dev->fsstat.f_blocks) {
+ mnt = lsblk_device_get_mountpoint(dev);
+ if (!mnt || dev->is_swap)
+ return NULL;
+ if (statvfs(mnt, &dev->fsstat) != 0)
+ return NULL;
+ }
+
+ switch(id) {
+ case COL_FSSIZE:
+ vfs_attr = dev->fsstat.f_frsize * dev->fsstat.f_blocks;
+ break;
+ case COL_FSAVAIL:
+ vfs_attr = dev->fsstat.f_frsize * dev->fsstat.f_bavail;
+ break;
+ case COL_FSUSED:
+ vfs_attr = dev->fsstat.f_frsize * (dev->fsstat.f_blocks - dev->fsstat.f_bfree);
+ break;
+ case COL_FSUSEPERC:
+ if (dev->fsstat.f_blocks == 0)
+ return xstrdup("-");
+
+ xasprintf(&sizestr, "%.0f%%",
+ (double)(dev->fsstat.f_blocks - dev->fsstat.f_bfree) /
+ dev->fsstat.f_blocks * 100);
+ return sizestr;
+ }
+
+ if (!vfs_attr)
+ sizestr = xstrdup("0");
+ else if (lsblk->bytes)
+ xasprintf(&sizestr, "%ju", vfs_attr);
+ else
+ sizestr = size_to_human_string(SIZE_SUFFIX_1LETTER, vfs_attr);
+
+ return sizestr;
+}
+
+static struct stat *device_get_stat(struct lsblk_device *dev)
+{
+ if (!dev->st.st_rdev)
+ stat(dev->filename, &dev->st);
+
+ return &dev->st;
+}
- if (!cxt->st.st_rdev && (id == COL_OWNER || id == COL_GROUP ||
- id == COL_MODE))
- st_rc = stat(cxt->filename, &cxt->st);
+static int is_removable_device(struct lsblk_device *dev, struct lsblk_device *parent)
+{
+ struct path_cxt *pc;
- if (lsblk->sort_id == id)
- sort = 1;
+ if (dev->removable != -1)
+ goto done;
+ if (ul_path_scanf(dev->sysfs, "removable", "%d", &dev->removable) == 1)
+ goto done;
+
+ if (parent) {
+ pc = sysfs_blkdev_get_parent(dev->sysfs);
+ if (!pc)
+ goto done;
+
+ if (pc == parent->sysfs)
+ /* dev is partition and parent is whole-disk */
+ dev->removable = is_removable_device(parent, NULL);
+ else
+ /* parent is something else, use sysfs parent */
+ ul_path_scanf(pc, "removable", "%d", &dev->removable);
+ }
+done:
+ if (dev->removable == -1)
+ dev->removable = 0;
+ return dev->removable;
+}
+
+static uint64_t device_get_discard_granularity(struct lsblk_device *dev)
+{
+ if (dev->discard_granularity == (uint64_t) -1
+ && ul_path_read_u64(dev->sysfs, &dev->discard_granularity,
+ "queue/discard_granularity") != 0)
+ dev->discard_granularity = 0;
+
+ 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
+ * column.
+ */
+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 */
+{
+ struct lsblk_devprop *prop;
+ char *str = NULL;
switch(id) {
case COL_NAME:
- str = cxt->dm_name ? mk_dm_name(cxt->dm_name) : mk_name(cxt->name);
+ str = dev->dm_name ? mk_dm_name(dev->dm_name) : mk_name(dev->name);
break;
case COL_KNAME:
- str = mk_name(cxt->name);
+ str = mk_name(dev->name);
break;
case COL_PKNAME:
- if (cxt->parent)
- str = mk_name(cxt->parent->name);
+ if (parent)
+ str = mk_name(parent->name);
+ break;
+ case COL_PATH:
+ if (dev->filename)
+ str = xstrdup(dev->filename);
break;
case COL_OWNER:
{
- struct passwd *pw = st_rc ? NULL : getpwuid(cxt->st.st_uid);
+ struct stat *st = device_get_stat(dev);
+ struct passwd *pw = st ? getpwuid(st->st_uid) : NULL;
if (pw)
str = xstrdup(pw->pw_name);
break;
}
case COL_GROUP:
{
- struct group *gr = st_rc ? NULL : getgrgid(cxt->st.st_gid);
+ struct stat *st = device_get_stat(dev);
+ struct group *gr = st ? getgrgid(st->st_gid) : NULL;
if (gr)
str = xstrdup(gr->gr_name);
break;
}
case COL_MODE:
{
- char md[11];
+ struct stat *st = device_get_stat(dev);
+ char md[11] = { '\0' };
- if (!st_rc) {
- xstrmode(cxt->st.st_mode, md);
- str = xstrdup(md);
- }
+ if (st)
+ str = xstrdup(xstrmode(st->st_mode, md));
break;
}
case COL_MAJMIN:
if (is_parsable(lsblk))
- xasprintf(&str, "%u:%u", cxt->maj, cxt->min);
+ xasprintf(&str, "%u:%u", dev->maj, dev->min);
else
- xasprintf(&str, "%3u:%-3u", cxt->maj, cxt->min);
- if (sort)
- set_sortdata_u64(ln, col, makedev(cxt->maj, cxt->min));
+ xasprintf(&str, "%3u:%-3u", dev->maj, dev->min);
+ if (sortdata)
+ *sortdata = makedev(dev->maj, dev->min);
break;
case COL_FSTYPE:
- probe_device(cxt);
- if (cxt->fstype)
- str = xstrdup(cxt->fstype);
+ prop = lsblk_device_get_properties(dev);
+ if (prop && prop->fstype)
+ str = xstrdup(prop->fstype);
+ break;
+ case COL_FSSIZE:
+ case COL_FSAVAIL:
+ case COL_FSUSED:
+ case COL_FSUSEPERC:
+ str = get_vfs_attribute(dev, id);
break;
case COL_TARGET:
- str = get_device_mountpoint(cxt);
+ str = xstrdup(lsblk_device_get_mountpoint(dev));
break;
case COL_LABEL:
- probe_device(cxt);
- if (cxt->label)
- str = xstrdup(cxt->label);
+ prop = lsblk_device_get_properties(dev);
+ if (prop && prop->label)
+ str = xstrdup(prop->label);
break;
case COL_UUID:
- probe_device(cxt);
- if (cxt->uuid)
- str = xstrdup(cxt->uuid);
+ prop = lsblk_device_get_properties(dev);
+ if (prop && prop->uuid)
+ str = xstrdup(prop->uuid);
+ break;
+ case COL_PTUUID:
+ prop = lsblk_device_get_properties(dev);
+ if (prop && prop->ptuuid)
+ str = xstrdup(prop->ptuuid);
+ break;
+ case COL_PTTYPE:
+ prop = lsblk_device_get_properties(dev);
+ if (prop && prop->pttype)
+ str = xstrdup(prop->pttype);
break;
case COL_PARTTYPE:
- probe_device(cxt);
- if (cxt->parttype)
- str = xstrdup(cxt->parttype);
+ prop = lsblk_device_get_properties(dev);
+ if (prop && prop->parttype)
+ str = xstrdup(prop->parttype);
break;
case COL_PARTLABEL:
- probe_device(cxt);
- if (cxt->partlabel)
- str = xstrdup(cxt->partlabel);
+ prop = lsblk_device_get_properties(dev);
+ if (prop && prop->partlabel)
+ str = xstrdup(prop->partlabel);
break;
case COL_PARTUUID:
- probe_device(cxt);
- if (cxt->partuuid)
- str = xstrdup(cxt->partuuid);
+ prop = lsblk_device_get_properties(dev);
+ if (prop && prop->partuuid)
+ str = xstrdup(prop->partuuid);
break;
case COL_PARTFLAGS:
- probe_device(cxt);
- if (cxt->partflags)
- str = xstrdup(cxt->partflags);
+ prop = lsblk_device_get_properties(dev);
+ if (prop && prop->partflags)
+ str = xstrdup(prop->partflags);
break;
case COL_WWN:
- get_udev_properties(cxt);
- if (cxt->wwn)
- str = xstrdup(cxt->wwn);
+ prop = lsblk_device_get_properties(dev);
+ if (prop && prop->wwn)
+ str = xstrdup(prop->wwn);
break;
case COL_RA:
- str = sysfs_strdup(&cxt->sysfs, "queue/read_ahead_kb");
- if (sort)
- set_sortdata_u64_from_string(ln, col, str);
+ ul_path_read_string(dev->sysfs, &str, "queue/read_ahead_kb");
+ if (sortdata)
+ str2u64(str, sortdata);
break;
case COL_RO:
- str = xstrdup(is_readonly_device(cxt) ? "1" : "0");
+ str = xstrdup(is_readonly_device(dev) ? "1" : "0");
break;
case COL_RM:
- str = sysfs_strdup(&cxt->sysfs, "removable");
- if (!str && cxt->sysfs.parent)
- str = sysfs_strdup(cxt->sysfs.parent, "removable");
+ str = xstrdup(is_removable_device(dev, parent) ? "1" : "0");
break;
case COL_HOTPLUG:
- str = sysfs_is_hotpluggable(&cxt->sysfs) ? xstrdup("1") : xstrdup("0");
+ str = sysfs_blkdev_is_hotpluggable(dev->sysfs) ? xstrdup("1") : xstrdup("0");
break;
case COL_ROTA:
- str = sysfs_strdup(&cxt->sysfs, "queue/rotational");
+ ul_path_read_string(dev->sysfs, &str, "queue/rotational");
break;
case COL_RAND:
- str = sysfs_strdup(&cxt->sysfs, "queue/add_random");
+ ul_path_read_string(dev->sysfs, &str, "queue/add_random");
break;
case COL_MODEL:
- if (!cxt->partition && cxt->nslaves == 0)
- str = sysfs_strdup(&cxt->sysfs, "device/model");
+ if (!device_is_partition(dev) && dev->nslaves == 0) {
+ prop = lsblk_device_get_properties(dev);
+ if (prop && prop->model)
+ str = xstrdup(prop->model);
+ else
+ ul_path_read_string(dev->sysfs, &str, "device/model");
+ }
break;
case COL_SERIAL:
- if (!cxt->partition && cxt->nslaves == 0) {
- get_udev_properties(cxt);
- if (cxt->serial)
- str = xstrdup(cxt->serial);
+ if (!device_is_partition(dev) && dev->nslaves == 0) {
+ prop = lsblk_device_get_properties(dev);
+ if (prop && prop->serial)
+ str = xstrdup(prop->serial);
else
- str = sysfs_strdup(&cxt->sysfs, "device/serial");
+ ul_path_read_string(dev->sysfs, &str, "device/serial");
}
break;
case COL_REV:
- if (!cxt->partition && cxt->nslaves == 0)
- str = sysfs_strdup(&cxt->sysfs, "device/rev");
+ if (!device_is_partition(dev) && dev->nslaves == 0)
+ ul_path_read_string(dev->sysfs, &str, "device/rev");
break;
case COL_VENDOR:
- if (!cxt->partition && cxt->nslaves == 0)
- str = sysfs_strdup(&cxt->sysfs, "device/vendor");
+ if (!device_is_partition(dev) && dev->nslaves == 0)
+ ul_path_read_string(dev->sysfs, &str, "device/vendor");
break;
case COL_SIZE:
- if (!cxt->size)
+ if (!dev->size)
break;
if (lsblk->bytes)
- xasprintf(&str, "%ju", cxt->size);
+ xasprintf(&str, "%ju", dev->size);
else
- str = size_to_human_string(SIZE_SUFFIX_1LETTER, cxt->size);
- if (sort)
- set_sortdata_u64(ln, col, cxt->size);
+ str = size_to_human_string(SIZE_SUFFIX_1LETTER, dev->size);
+ if (sortdata)
+ *sortdata = dev->size;
break;
case COL_STATE:
- if (!cxt->partition && !cxt->dm_name)
- str = sysfs_strdup(&cxt->sysfs, "device/state");
- else if (cxt->dm_name) {
+ if (!device_is_partition(dev) && !dev->dm_name)
+ ul_path_read_string(dev->sysfs, &str, "device/state");
+ else if (dev->dm_name) {
int x = 0;
- if (sysfs_read_int(&cxt->sysfs, "dm/suspended", &x) == 0)
+ if (ul_path_read_s32(dev->sysfs, &x, "dm/suspended") == 0)
str = xstrdup(x ? "suspended" : "running");
}
break;
case COL_ALIOFF:
- str = sysfs_strdup(&cxt->sysfs, "alignment_offset");
- if (sort)
- set_sortdata_u64_from_string(ln, col, str);
+ ul_path_read_string(dev->sysfs, &str, "alignment_offset");
+ if (sortdata)
+ str2u64(str, sortdata);
break;
case COL_MINIO:
- str = sysfs_strdup(&cxt->sysfs, "queue/minimum_io_size");
- if (sort)
- set_sortdata_u64_from_string(ln, col, str);
+ ul_path_read_string(dev->sysfs, &str, "queue/minimum_io_size");
+ if (sortdata)
+ str2u64(str, sortdata);
break;
case COL_OPTIO:
- str = sysfs_strdup(&cxt->sysfs, "queue/optimal_io_size");
- if (sort)
- set_sortdata_u64_from_string(ln, col, str);
+ ul_path_read_string(dev->sysfs, &str, "queue/optimal_io_size");
+ if (sortdata)
+ str2u64(str, sortdata);
break;
case COL_PHYSEC:
- str = sysfs_strdup(&cxt->sysfs, "queue/physical_block_size");
- if (sort)
- set_sortdata_u64_from_string(ln, col, str);
+ ul_path_read_string(dev->sysfs, &str, "queue/physical_block_size");
+ if (sortdata)
+ str2u64(str, sortdata);
break;
case COL_LOGSEC:
- str = sysfs_strdup(&cxt->sysfs, "queue/logical_block_size");
- if (sort)
- set_sortdata_u64_from_string(ln, col, str);
+ ul_path_read_string(dev->sysfs, &str, "queue/logical_block_size");
+ if (sortdata)
+ str2u64(str, sortdata);
break;
case COL_SCHED:
- str = get_scheduler(cxt);
+ str = get_scheduler(dev);
break;
case COL_RQ_SIZE:
- str = sysfs_strdup(&cxt->sysfs, "queue/nr_requests");
- if (sort)
- set_sortdata_u64_from_string(ln, col, str);
+ ul_path_read_string(dev->sysfs, &str, "queue/nr_requests");
+ if (sortdata)
+ str2u64(str, sortdata);
break;
case COL_TYPE:
- str = get_type(cxt);
+ str = get_type(dev);
break;
case COL_HCTL:
{
int h, c, t, l;
- if (sysfs_scsi_get_hctl(&cxt->sysfs, &h, &c, &t, &l) == 0)
+ if (sysfs_blkdev_scsi_get_hctl(dev->sysfs, &h, &c, &t, &l) == 0)
xasprintf(&str, "%d:%d:%d:%d", h, c, t, l);
break;
}
case COL_TRANSPORT:
- str = get_transport(cxt);
+ str = get_transport(dev);
break;
case COL_SUBSYS:
- str = get_subsystems(cxt);
+ str = get_subsystems(dev);
break;
case COL_DALIGN:
- if (cxt->discard)
- str = sysfs_strdup(&cxt->sysfs, "discard_alignment");
+ if (device_get_discard_granularity(dev) > 0)
+ ul_path_read_string(dev->sysfs, &str, "discard_alignment");
if (!str)
str = xstrdup("0");
- if (sort)
- set_sortdata_u64_from_string(ln, col, str);
+ if (sortdata)
+ str2u64(str, sortdata);
break;
case COL_DGRAN:
if (lsblk->bytes) {
- str = sysfs_strdup(&cxt->sysfs, "queue/discard_granularity");
- if (sort)
- set_sortdata_u64_from_string(ln, col, str);
+ ul_path_read_string(dev->sysfs, &str, "queue/discard_granularity");
+ if (sortdata)
+ str2u64(str, sortdata);
} else {
- uint64_t x;
- if (sysfs_read_u64(&cxt->sysfs,
- "queue/discard_granularity", &x) == 0) {
- str = size_to_human_string(SIZE_SUFFIX_1LETTER, x);
- if (sort)
- set_sortdata_u64(ln, col, x);
- }
+ uint64_t x = device_get_discard_granularity(dev);
+ str = size_to_human_string(SIZE_SUFFIX_1LETTER, x);
+ if (sortdata)
+ *sortdata = x;
}
break;
case COL_DMAX:
if (lsblk->bytes) {
- str = sysfs_strdup(&cxt->sysfs, "queue/discard_max_bytes");
- if (sort)
- set_sortdata_u64_from_string(ln, col, str);
+ ul_path_read_string(dev->sysfs, &str, "queue/discard_max_bytes");
+ if (sortdata)
+ str2u64(str, sortdata);
} else {
uint64_t x;
- if (sysfs_read_u64(&cxt->sysfs,
- "queue/discard_max_bytes", &x) == 0) {
+ if (ul_path_read_u64(dev->sysfs, &x, "queue/discard_max_bytes") == 0) {
str = size_to_human_string(SIZE_SUFFIX_1LETTER, x);
- if (sort)
- set_sortdata_u64(ln, col, x);
+ if (sortdata)
+ *sortdata = x;
}
}
break;
case COL_DZERO:
- if (cxt->discard)
- str = sysfs_strdup(&cxt->sysfs, "queue/discard_zeroes_data");
+ if (device_get_discard_granularity(dev) > 0)
+ ul_path_read_string(dev->sysfs, &str, "queue/discard_zeroes_data");
if (!str)
str = xstrdup("0");
break;
case COL_WSAME:
if (lsblk->bytes) {
- str = sysfs_strdup(&cxt->sysfs, "queue/write_same_max_bytes");
- if (sort)
- set_sortdata_u64_from_string(ln, col, str);
+ ul_path_read_string(dev->sysfs, &str, "queue/write_same_max_bytes");
+ if (sortdata)
+ str2u64(str, sortdata);
} else {
uint64_t x;
- if (sysfs_read_u64(&cxt->sysfs,
- "queue/write_same_max_bytes", &x) == 0) {
+ if (ul_path_read_u64(dev->sysfs, &x, "queue/write_same_max_bytes") == 0) {
str = size_to_human_string(SIZE_SUFFIX_1LETTER, x);
- if (sort)
- set_sortdata_u64(ln, col, x);
+ if (sortdata)
+ *sortdata = x;
}
}
if (!str)
str = xstrdup("0");
break;
case COL_ZONED:
- str = sysfs_strdup(&cxt->sysfs, "queue/zoned");
+ ul_path_read_string(dev->sysfs, &str, "queue/zoned");
break;
};
- if (str && scols_line_refer_data(ln, col, str))
- err(EXIT_FAILURE, _("failed to add output data"));
+ return str;
}
-static void fill_table_line(struct blkdev_cxt *cxt, struct libscols_line *scols_parent)
+/*
+ * Adds data for all wanted columns about the device to the smartcols table
+ */
+static void device_to_scols(
+ struct lsblk_device *dev,
+ struct lsblk_device *parent,
+ struct libscols_table *tab,
+ struct libscols_line *parent_line)
{
size_t i;
+ struct libscols_line *ln;
+ struct lsblk_iter itr;
+ struct lsblk_device *child = NULL;
+ int link_group = 0;
- cxt->scols_line = scols_table_new_line(lsblk->table, scols_parent);
- if (!cxt->scols_line)
+ ON_DBG(DEV, if (ul_path_isopen_dirfd(dev->sysfs)) ul_debugobj(dev, "%s ---> is open!", dev->name));
+
+ /* 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"));
- for (i = 0; i < ncolumns; i++)
- set_scols_data(cxt, i, get_column_id(i), cxt->scols_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;
+ 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);
+ }
+ if (data && scols_line_refer_data(ln, i, data))
+ 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);
+ while (lsblk_device_next_child(dev, &itr, &child) == 0)
+ device_to_scols(child, dev, tab, ln);
+
+ /* Let's be careful with number of open files */
+ ul_path_close_dirfd(dev->sysfs);
}
-static int set_cxt(struct blkdev_cxt *cxt,
- struct blkdev_cxt *parent,
- struct blkdev_cxt *wholedisk,
+/*
+ * Walks on tree and adds one line for each device to the smartcols table
+ */
+static void devtree_to_scols(struct lsblk_devtree *tr, struct libscols_table *tab)
+{
+ struct lsblk_iter itr;
+ struct lsblk_device *dev = NULL;
+
+ lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD);
+
+ while (lsblk_devtree_next_root(tr, &itr, &dev) == 0)
+ device_to_scols(dev, NULL, tab, NULL);
+}
+
+/*
+ * Reads very basic information about the device from sysfs into the device struct
+ */
+static int initialize_device(struct lsblk_device *dev,
+ struct lsblk_device *wholedisk,
const char *name)
{
dev_t devno;
- DBG(CXT, ul_debugobj(cxt, "setting context for %s [parent=%p, wholedisk=%p]",
- name, parent, wholedisk));
+ DBG(DEV, ul_debugobj(dev, "initialize %s [wholedisk=%p %s]",
+ name, wholedisk, wholedisk ? wholedisk->name : ""));
- cxt->parent = parent;
- cxt->name = xstrdup(name);
- cxt->partition = wholedisk != NULL;
+ dev->name = xstrdup(name);
- cxt->filename = get_device_path(cxt);
- if (!cxt->filename) {
- DBG(CXT, ul_debugobj(cxt, "%s: failed to get device path", cxt->name));
- return -1;
+ if (wholedisk) {
+ dev->wholedisk = wholedisk;
+ lsblk_ref_device(wholedisk);
}
- DBG(CXT, ul_debugobj(cxt, "%s: filename=%s", cxt->name, cxt->filename));
- devno = sysfs_devname_to_devno(cxt->name, wholedisk ? wholedisk->name : NULL);
+ dev->filename = get_device_path(dev);
+ if (!dev->filename) {
+ DBG(DEV, ul_debugobj(dev, "%s: failed to get device path", dev->name));
+ return -1;
+ }
+ DBG(DEV, ul_debugobj(dev, "%s: filename=%s", dev->name, dev->filename));
+ devno = __sysfs_devname_to_devno(lsblk->sysroot, dev->name, wholedisk ? wholedisk->name : NULL);
if (!devno) {
- DBG(CXT, ul_debugobj(cxt, "%s: unknown device name", cxt->name));
+ DBG(DEV, ul_debugobj(dev, "%s: unknown device name", dev->name));
return -1;
}
- if (lsblk->inverse) {
- if (sysfs_init(&cxt->sysfs, devno, wholedisk ? &wholedisk->sysfs : NULL)) {
- DBG(CXT, ul_debugobj(cxt, "%s: failed to initialize sysfs handler", cxt->name));
- return -1;
- }
- if (parent)
- parent->sysfs.parent = &cxt->sysfs;
- } else {
- if (sysfs_init(&cxt->sysfs, devno, parent ? &parent->sysfs : NULL)) {
- DBG(CXT, ul_debugobj(cxt, "%s: failed to initialize sysfs handler", cxt->name));
- return -1;
- }
+ dev->sysfs = ul_new_sysfs_path(devno, wholedisk ? wholedisk->sysfs : NULL, lsblk->sysroot);
+ if (!dev->sysfs) {
+ DBG(DEV, ul_debugobj(dev, "%s: failed to initialize sysfs handler", dev->name));
+ return -1;
}
- cxt->maj = major(devno);
- cxt->min = minor(devno);
- cxt->size = 0;
-
- if (sysfs_read_u64(&cxt->sysfs, "size", &cxt->size) == 0) /* in sectors */
- cxt->size <<= 9; /* in bytes */
+ dev->maj = major(devno);
+ dev->min = minor(devno);
+ dev->size = 0;
- if (sysfs_read_int(&cxt->sysfs,
- "queue/discard_granularity", &cxt->discard) != 0)
- cxt->discard = 0;
+ if (ul_path_read_u64(dev->sysfs, &dev->size, "size") == 0) /* in sectors */
+ dev->size <<= 9; /* in bytes */
/* Ignore devices of zero size */
- if (!lsblk->all_devices && cxt->size == 0) {
- DBG(CXT, ul_debugobj(cxt, "zero size device -- ignore"));
+ if (!lsblk->all_devices && dev->size == 0) {
+ DBG(DEV, ul_debugobj(dev, "zero size device -- ignore"));
return -1;
}
- if (is_dm(cxt->name)) {
- cxt->dm_name = sysfs_strdup(&cxt->sysfs, "dm/name");
- if (!cxt->dm_name) {
- DBG(CXT, ul_debugobj(cxt, "%s: failed to get dm name", cxt->name));
+ if (is_dm(dev->name)) {
+ ul_path_read_string(dev->sysfs, &dev->dm_name, "dm/name");
+ if (!dev->dm_name) {
+ DBG(DEV, ul_debugobj(dev, "%s: failed to get dm name", dev->name));
return -1;
}
}
- cxt->npartitions = sysfs_count_partitions(&cxt->sysfs, cxt->name);
- cxt->nholders = sysfs_count_dirents(&cxt->sysfs, "holders");
- cxt->nslaves = sysfs_count_dirents(&cxt->sysfs, "slaves");
+ dev->npartitions = sysfs_blkdev_count_partitions(dev->sysfs, dev->name);
+ dev->nholders = ul_path_count_dirents(dev->sysfs, "holders");
+ dev->nslaves = ul_path_count_dirents(dev->sysfs, "slaves");
- DBG(CXT, ul_debugobj(cxt, "%s: npartitions=%d, nholders=%d, nslaves=%d",
- cxt->name, cxt->npartitions, cxt->nholders, cxt->nslaves));
+ DBG(DEV, ul_debugobj(dev, "%s: npartitions=%d, nholders=%d, nslaves=%d",
+ dev->name, dev->npartitions, dev->nholders, dev->nslaves));
/* ignore non-SCSI devices */
- if (lsblk->scsi && sysfs_scsi_get_hctl(&cxt->sysfs, NULL, NULL, NULL, NULL)) {
- DBG(CXT, ul_debugobj(cxt, "non-scsi device -- ignore"));
+ if (lsblk->scsi && sysfs_blkdev_scsi_get_hctl(dev->sysfs, NULL, NULL, NULL, NULL)) {
+ DBG(DEV, ul_debugobj(dev, "non-scsi device -- ignore"));
return -1;
}
- DBG(CXT, ul_debugobj(cxt, "%s: context successfully initialized", cxt->name));
+ DBG(DEV, ul_debugobj(dev, "%s: context successfully initialized", dev->name));
return 0;
}
-static int process_blkdev(struct blkdev_cxt *cxt, struct blkdev_cxt *parent,
- int do_partitions, const char *part_name);
+static struct lsblk_device *devtree_get_device_or_new(struct lsblk_devtree *tr,
+ struct lsblk_device *disk,
+ const char *name)
+{
+ struct lsblk_device *dev = lsblk_devtree_get_device(tr, name);
+
+ if (!dev) {
+ dev = lsblk_new_device();
+ if (!dev)
+ err(EXIT_FAILURE, _("failed to allocate device"));
+
+ if (initialize_device(dev, disk, name) != 0) {
+ lsblk_unref_device(dev);
+ return NULL;
+ }
+ lsblk_devtree_add_device(tr, dev);
+ lsblk_unref_device(dev); /* keep it referenced by devtree only */
+ } else
+ DBG(DEV, ul_debugobj(dev, "%s: already processed", name));
+
+ return dev;
+}
+
+static int process_dependencies(
+ struct lsblk_devtree *tr,
+ struct lsblk_device *dev,
+ int do_partitions);
/*
- * List device partitions if any.
+ * Read devices from whole-disk device into tree
*/
-static int list_partitions(struct blkdev_cxt *wholedisk_cxt, struct blkdev_cxt *parent_cxt,
- const char *part_name)
+static int process_partitions(struct lsblk_devtree *tr, struct lsblk_device *disk)
{
DIR *dir;
struct dirent *d;
- struct blkdev_cxt part_cxt = { NULL };
- int r = -1;
- assert(wholedisk_cxt);
+ assert(disk);
/*
* Do not process further if there are no partitions for
* this device or the device itself is a partition.
*/
- if (!wholedisk_cxt->npartitions || wholedisk_cxt->partition)
- return -1;
+ if (!disk->npartitions || device_is_partition(disk))
+ return -EINVAL;
- DBG(CXT, ul_debugobj(wholedisk_cxt, "probe whole-disk for partitions"));
+ DBG(DEV, ul_debugobj(disk, "%s: probe whole-disk for partitions", disk->name));
- dir = sysfs_opendir(&wholedisk_cxt->sysfs, NULL);
+ dir = ul_path_opendir(disk->sysfs, NULL);
if (!dir)
err(EXIT_FAILURE, _("failed to open device directory in sysfs"));
while ((d = xreaddir(dir))) {
- /* Process particular partition only? */
- if (part_name && strcmp(part_name, d->d_name))
- continue;
+ struct lsblk_device *part;
- if (!(sysfs_is_partition_dirent(dir, d, wholedisk_cxt->name)))
+ if (!(sysfs_blkdev_is_partition_dirent(dir, d, disk->name)))
continue;
- DBG(CXT, ul_debugobj(wholedisk_cxt, " checking %s", d->d_name));
+ DBG(DEV, ul_debugobj(disk, " checking %s", d->d_name));
- if (lsblk->inverse) {
- /*
- * <parent_cxt>
- * `-<part_cxt>
- * `-<wholedisk_cxt>
- * `-...
- */
- if (set_cxt(&part_cxt, parent_cxt, wholedisk_cxt, d->d_name))
- goto next;
+ part = devtree_get_device_or_new(tr, disk, d->d_name);
+ if (!part)
+ continue;
- if (!parent_cxt && part_cxt.nholders)
- goto next;
+ if (lsblk_device_new_dependence(disk, part) == 0)
+ process_dependencies(tr, part, 0);
- wholedisk_cxt->parent = &part_cxt;
- fill_table_line(&part_cxt, parent_cxt ? parent_cxt->scols_line : NULL);
- if (!lsblk->nodeps)
- process_blkdev(wholedisk_cxt, &part_cxt, 0, NULL);
- } else {
- /*
- * <parent_cxt>
- * `-<wholedisk_cxt>
- * `-<part_cxt>
- * `-...
- */
- int ps = set_cxt(&part_cxt, wholedisk_cxt, wholedisk_cxt, d->d_name);
-
- /* Print whole disk only once */
- if (r)
- 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);
- }
- next:
- reset_blkdev_cxt(&part_cxt);
- r = 0;
+ ul_path_close_dirfd(part->sysfs);
}
- DBG(CXT, ul_debugobj(wholedisk_cxt, "probe whole-disk for partitions -- done"));
+ /* For partitions we need parental (whole-disk) sysfs directory pretty
+ * often, so close it now when all is done */
+ ul_path_close_dirfd(disk->sysfs);
+
+ DBG(DEV, ul_debugobj(disk, "probe whole-disk for partitions -- done"));
closedir(dir);
- return r;
+ return 0;
}
-static int get_wholedisk_from_partition_dirent(DIR *dir,
- struct dirent *d, struct blkdev_cxt *cxt)
+static char *get_wholedisk_from_partition_dirent(DIR *dir, struct dirent *d, char *buf, size_t bufsz)
{
- char path[PATH_MAX];
char *p;
int len;
- if ((len = readlinkat(dirfd(dir), d->d_name, path, sizeof(path) - 1)) < 0)
+ if ((len = readlinkat(dirfd(dir), d->d_name, buf, bufsz - 1)) < 0)
return 0;
- path[len] = '\0';
+ buf[len] = '\0';
/* The path ends with ".../<device>/<partition>" */
- p = strrchr(path, '/');
+ p = strrchr(buf, '/');
if (!p)
- return 0;
+ return NULL;
*p = '\0';
- p = strrchr(path, '/');
+ p = strrchr(buf, '/');
if (!p)
- return 0;
+ return NULL;
p++;
- return set_cxt(cxt, NULL, NULL, p);
+ return p;
}
/*
- * List device dependencies: partitions, holders (inverse = 0) or slaves (inverse = 1).
+ * Reads slaves/holders and partitions for specified device into device tree
*/
-static int list_deps(struct blkdev_cxt *cxt)
+static int process_dependencies(
+ struct lsblk_devtree *tr,
+ struct lsblk_device *dev,
+ int do_partitions)
{
DIR *dir;
struct dirent *d;
- struct blkdev_cxt dep = { NULL };
const char *depname;
- assert(cxt);
+ assert(dev);
if (lsblk->nodeps)
return 0;
- DBG(CXT, ul_debugobj(cxt, "%s: list dependencies", cxt->name));
+ /* read all or specified partition */
+ if (do_partitions && dev->npartitions)
+ process_partitions(tr, dev);
+
+ DBG(DEV, ul_debugobj(dev, "%s: reading dependencies", dev->name));
- if (!(lsblk->inverse ? cxt->nslaves : cxt->nholders))
+ if (!(lsblk->inverse ? dev->nslaves : dev->nholders)) {
+ DBG(DEV, ul_debugobj(dev, " ignore (no slaves/holders)"));
return 0;
+ }
depname = lsblk->inverse ? "slaves" : "holders";
- dir = sysfs_opendir(&cxt->sysfs, depname);
- if (!dir)
+ dir = ul_path_opendir(dev->sysfs, depname);
+ if (!dir) {
+ DBG(DEV, ul_debugobj(dev, " ignore (no slaves/holders directory)"));
return 0;
-
- DBG(CXT, ul_debugobj(cxt, "%s: checking for '%s' dependence", cxt->name, depname));
-
- while ((d = xreaddir(dir))) {
- /* Is the dependency a partition? */
- if (sysfs_is_partition_dirent(dir, d, NULL)) {
- if (!get_wholedisk_from_partition_dirent(dir, d, &dep)) {
- DBG(CXT, ul_debugobj(cxt, "%s: %s: dependence is partition",
- cxt->name, d->d_name));
- process_blkdev(&dep, cxt, 1, d->d_name);
- }
- }
- /* The dependency is a whole device. */
- else if (!set_cxt(&dep, cxt, NULL, d->d_name)) {
- DBG(CXT, ul_debugobj(cxt, "%s: %s: dependence is whole-disk",
- cxt->name, d->d_name));
- /* For inverse tree we don't want to show partitions
- * if the dependence is on whole-disk */
- process_blkdev(&dep, cxt, lsblk->inverse ? 0 : 1, NULL);
- }
- reset_blkdev_cxt(&dep);
}
- closedir(dir);
+ ul_path_close_dirfd(dev->sysfs);
- DBG(CXT, ul_debugobj(cxt, "%s: checking for '%s' -- done", cxt->name, depname));
- return 0;
-}
+ DBG(DEV, ul_debugobj(dev, " %s: checking for '%s' dependence", dev->name, depname));
-static int process_blkdev(struct blkdev_cxt *cxt, struct blkdev_cxt *parent,
- int do_partitions, const char *part_name)
-{
- if (do_partitions && cxt->npartitions)
- list_partitions(cxt, parent, part_name); /* partitions + whole-disk */
- else
- fill_table_line(cxt, parent ? parent->scols_line : NULL); /* whole-disk only */
-
- return list_deps(cxt);
-}
+ while ((d = xreaddir(dir))) {
+ struct lsblk_device *dep = NULL;
+ struct lsblk_device *disk = NULL;
-/* Iterate devices in sysfs */
-static int iterate_block_devices(void)
-{
- DIR *dir;
- struct dirent *d;
- struct blkdev_cxt cxt = { NULL };
+ /* Is the dependency a partition? */
+ if (sysfs_blkdev_is_partition_dirent(dir, d, NULL)) {
- if (!(dir = opendir(_PATH_SYS_BLOCK)))
- return -errno;
+ char buf[PATH_MAX];
+ char *diskname;
- DBG(DEV, ul_debug("iterate on " _PATH_SYS_BLOCK));
+ DBG(DEV, ul_debugobj(dev, " %s: dependence is partition", d->d_name));
- while ((d = xreaddir(dir))) {
+ diskname = get_wholedisk_from_partition_dirent(dir, d, buf, sizeof(buf));
+ if (diskname)
+ disk = devtree_get_device_or_new(tr, NULL, diskname);
+ if (!disk) {
+ DBG(DEV, ul_debugobj(dev, " ignore no wholedisk ???"));
+ goto next;
+ }
- DBG(DEV, ul_debug(" %s dentry", d->d_name));
+ dep = devtree_get_device_or_new(tr, disk, d->d_name);
+ if (!dep)
+ goto next;
- if (set_cxt(&cxt, NULL, NULL, d->d_name))
- goto next;
+ if (lsblk_device_new_dependence(dev, dep) == 0)
+ process_dependencies(tr, dep, 1);
- if (is_maj_excluded(cxt.maj) || !is_maj_included(cxt.maj))
- goto next;
+ if (lsblk->inverse
+ && lsblk_device_new_dependence(dep, disk) == 0)
+ process_dependencies(tr, disk, 0);
+ }
+ /* The dependency is a whole device. */
+ else {
+ DBG(DEV, ul_debugobj(dev, " %s: %s: dependence is whole-disk",
+ dev->name, d->d_name));
- /* Skip devices in the middle of dependency tree. */
- if ((lsblk->inverse ? cxt.nholders : cxt.nslaves) > 0)
- goto next;
+ dep = devtree_get_device_or_new(tr, NULL, d->d_name);
+ if (!dep)
+ goto next;
- process_blkdev(&cxt, NULL, 1, NULL);
- next:
- reset_blkdev_cxt(&cxt);
+ if (lsblk_device_new_dependence(dev, dep) == 0)
+ /* For inverse tree we don't want to show partitions
+ * if the dependence is on whole-disk */
+ process_dependencies(tr, dep, lsblk->inverse ? 0 : 1);
+ }
+next:
+ if (dep && dep->sysfs)
+ ul_path_close_dirfd(dep->sysfs);
+ if (disk && disk->sysfs)
+ ul_path_close_dirfd(disk->sysfs);
}
-
closedir(dir);
- DBG(DEV, ul_debug("iterate on " _PATH_SYS_BLOCK " -- done"));
+ DBG(DEV, ul_debugobj(dev, "%s: checking for '%s' -- done", dev->name, depname));
return 0;
}
-static char *devno_to_sysfs_name(dev_t devno, char *devname, char *buf, size_t buf_size)
-{
- char path[PATH_MAX];
- ssize_t len;
-
- if (!sysfs_devno_path(devno, path, sizeof(path))) {
- warn(_("%s: failed to compose sysfs path"), devname);
- return NULL;
- }
-
- len = readlink(path, buf, buf_size - 1);
- if (len < 0) {
- warn(_("%s: failed to read link"), path);
- return NULL;
- }
- buf[len] = '\0';
-
- return xstrdup(strrchr(buf, '/') + 1);
-}
-
-static int process_one_device(char *devname)
+/*
+ * Defines the device as root node in the device tree and walks on all dependencies of the device.
+ */
+static int __process_one_device(struct lsblk_devtree *tr, char *devname, dev_t devno)
{
- struct blkdev_cxt parent = { NULL }, cxt = { NULL };
- struct stat st;
+ struct lsblk_device *dev = NULL;
+ struct lsblk_device *disk = NULL;
char buf[PATH_MAX + 1], *name = NULL, *diskname = NULL;
- dev_t disk = 0;
int real_part = 0, rc = -EINVAL;
- if (stat(devname, &st) || !S_ISBLK(st.st_mode)) {
- warnx(_("%s: not a block device"), devname);
- goto leave;
- }
+ if (devno == 0) {
+ struct stat st;
- if (!(name = devno_to_sysfs_name(st.st_rdev, devname, buf, PATH_MAX))) {
- warn(_("%s: failed to get sysfs name"), devname);
+ DBG(DEV, ul_debug("%s: reading alone device", devname));
+
+ if (stat(devname, &st) || !S_ISBLK(st.st_mode)) {
+ warnx(_("%s: not a block device"), devname);
+ goto leave;
+ }
+ devno = st.st_rdev;
+ } else
+ DBG(DEV, ul_debug("%d:%d: reading alone device", major(devno), minor(devno)));
+
+ /* TODO: sysfs_devno_to_devname() internally initializes path_cxt, it
+ * would be better to use ul_new_sysfs_path() + sysfs_blkdev_get_name()
+ * and reuse path_cxt for initialize_device()
+ */
+ name = sysfs_devno_to_devname(devno, buf, sizeof(buf));
+ if (!name) {
+ if (devname)
+ warn(_("%s: failed to get sysfs name"), devname);
goto leave;
}
+ name = xstrdup(name);
if (!strncmp(name, "dm-", 3)) {
/* dm mapping is never a real partition! */
real_part = 0;
} else {
- if (blkid_devno_to_wholedisk(st.st_rdev, buf, sizeof(buf), &disk)) {
- warn(_("%s: failed to get whole-disk device number"), devname);
+ dev_t diskno = 0;
+
+ if (blkid_devno_to_wholedisk(devno, buf, sizeof(buf), &diskno)) {
+ warn(_("%s: failed to get whole-disk device number"), name);
goto leave;
}
diskname = buf;
- real_part = st.st_rdev != disk;
+ real_part = devno != diskno;
}
if (!real_part) {
/*
* Device is not a partition.
*/
- if (set_cxt(&cxt, NULL, NULL, name))
+ DBG(DEV, ul_debug(" non-partition"));
+
+ dev = devtree_get_device_or_new(tr, NULL, name);
+ if (!dev)
goto leave;
- process_blkdev(&cxt, NULL, !lsblk->inverse, NULL);
+
+ lsblk_devtree_add_root(tr, dev);
+ process_dependencies(tr, dev, !lsblk->inverse);
} else {
/*
- * Partition, read sysfs name of the device.
+ * Partition, read sysfs name of the disk device
*/
- if (set_cxt(&parent, NULL, NULL, diskname))
+ DBG(DEV, ul_debug(" partition"));
+
+ disk = devtree_get_device_or_new(tr, NULL, diskname);
+ if (!disk)
goto leave;
- if (set_cxt(&cxt, &parent, &parent, name))
+
+ dev = devtree_get_device_or_new(tr, disk, name);
+ if (!dev)
goto leave;
- if (lsblk->inverse)
- process_blkdev(&parent, &cxt, 1, cxt.name);
+ lsblk_devtree_add_root(tr, dev);
+ process_dependencies(tr, dev, 1);
+
+ if (lsblk->inverse
+ && lsblk_device_new_dependence(dev, disk) == 0)
+ process_dependencies(tr, disk, 0);
else
- process_blkdev(&cxt, &parent, 1, NULL);
+ ul_path_close_dirfd(disk->sysfs);
}
rc = 0;
leave:
+ if (dev && dev->sysfs)
+ ul_path_close_dirfd(dev->sysfs);
+ if (disk && disk->sysfs)
+ ul_path_close_dirfd(disk->sysfs);
free(name);
- reset_blkdev_cxt(&cxt);
+ return rc;
+}
- if (real_part)
- reset_blkdev_cxt(&parent);
+static int process_one_device(struct lsblk_devtree *tr, char *devname)
+{
+ return __process_one_device(tr, devname, 0);
+}
- return rc;
+/*
+ * The /sys/block contains only root devices, and no partitions. It seems more
+ * simple to scan /sys/dev/block where are all devices without exceptions to get
+ * top-level devices for the reverse tree.
+ */
+static int process_all_devices_inverse(struct lsblk_devtree *tr)
+{
+ DIR *dir;
+ struct dirent *d;
+ struct path_cxt *pc = ul_new_path(_PATH_SYS_DEVBLOCK);
+
+ assert(lsblk->inverse);
+
+ if (!pc)
+ err(EXIT_FAILURE, _("failed to allocate /sys handler"));
+
+ ul_path_set_prefix(pc, lsblk->sysroot);
+ dir = ul_path_opendir(pc, NULL);
+ if (!dir)
+ goto done;
+
+ DBG(DEV, ul_debug("iterate on " _PATH_SYS_DEVBLOCK));
+
+ while ((d = xreaddir(dir))) {
+ dev_t devno;
+ int maj, min;
+
+ DBG(DEV, ul_debug(" %s dentry", d->d_name));
+
+ if (sscanf(d->d_name, "%d:%d", &maj, &min) != 2)
+ continue;
+ devno = makedev(maj, min);
+
+ if (is_maj_excluded(maj) || !is_maj_included(maj))
+ continue;
+ if (ul_path_countf_dirents(pc, "%s/holders", d->d_name) != 0)
+ continue;
+ if (sysfs_devno_count_partitions(devno) != 0)
+ continue;
+ __process_one_device(tr, NULL, devno);
+ }
+
+ closedir(dir);
+done:
+ ul_unref_path(pc);
+ DBG(DEV, ul_debug("iterate on " _PATH_SYS_DEVBLOCK " -- done"));
+ return 0;
+}
+
+/*
+ * Reads root nodes (devices) from /sys/block into devices tree
+ */
+static int process_all_devices(struct lsblk_devtree *tr)
+{
+ DIR *dir;
+ struct dirent *d;
+ struct path_cxt *pc;
+
+ assert(lsblk->inverse == 0);
+
+ pc = ul_new_path(_PATH_SYS_BLOCK);
+ if (!pc)
+ err(EXIT_FAILURE, _("failed to allocate /sys handler"));
+
+ ul_path_set_prefix(pc, lsblk->sysroot);
+ dir = ul_path_opendir(pc, NULL);
+ if (!dir)
+ goto done;
+
+ DBG(DEV, ul_debug("iterate on " _PATH_SYS_BLOCK));
+
+ while ((d = xreaddir(dir))) {
+ struct lsblk_device *dev = NULL;
+
+ DBG(DEV, ul_debug(" %s dentry", d->d_name));
+ dev = devtree_get_device_or_new(tr, NULL, d->d_name);
+ if (!dev)
+ goto next;
+
+ /* remove unwanted devices */
+ if (is_maj_excluded(dev->maj) || !is_maj_included(dev->maj)) {
+ DBG(DEV, ul_debug(" %s: ignore (by filter)", d->d_name));
+ lsblk_devtree_remove_device(tr, dev);
+ goto next;
+ }
+
+ if (dev->nslaves) {
+ 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:
+ /* Let's be careful with number of open files */
+ if (dev && dev->sysfs)
+ ul_path_close_dirfd(dev->sysfs);
+ }
+
+ closedir(dir);
+done:
+ ul_unref_path(pc);
+ DBG(DEV, ul_debug("iterate on " _PATH_SYS_BLOCK " -- done"));
+ return 0;
}
+/*
+ * Parses major numbers as specified on lsblk command line
+ */
static void parse_excludes(const char *str0)
{
const char *str = str0;
@@ -1583,6 +1620,10 @@ static void parse_excludes(const char *str0)
}
}
+/*
+ * Parses major numbers as specified on lsblk command line
+ * (TODO: what about refactor and merge parse_excludes() and parse_includes().)
+ */
static void parse_includes(const char *str0)
{
const char *str = str0;
@@ -1628,6 +1669,43 @@ static int cmp_u64_cells(struct libscols_cell *a,
return *adata == *bdata ? 0 : *adata >= *bdata ? 1 : -1;
}
+static void device_set_dedupkey(
+ struct lsblk_device *dev,
+ struct lsblk_device *parent,
+ int id)
+{
+ struct lsblk_iter itr;
+ struct lsblk_device *child = NULL;
+
+ dev->dedupkey = device_get_data(dev, parent, id, NULL);
+ if (dev->dedupkey)
+ DBG(DEV, ul_debugobj(dev, "%s: de-duplication key: %s", dev->name, dev->dedupkey));
+
+ 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);
+
+ while (lsblk_device_next_child(dev, &itr, &child) == 0)
+ device_set_dedupkey(child, dev, id);
+
+ /* Let's be careful with number of open files */
+ ul_path_close_dirfd(dev->sysfs);
+}
+
+static void devtree_set_dedupkeys(struct lsblk_devtree *tr, int id)
+{
+ struct lsblk_iter itr;
+ struct lsblk_device *dev = NULL;
+
+ lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD);
+
+ while (lsblk_devtree_next_root(tr, &itr, &dev) == 0)
+ device_set_dedupkey(dev, NULL, id);
+}
+
static void __attribute__((__noreturn__)) usage(void)
{
FILE *out = stdout;
@@ -1640,29 +1718,32 @@ 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, --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);
printf(USAGE_HELP_OPTIONS(22));
@@ -1685,22 +1766,33 @@ static void check_sysdevblock(void)
int main(int argc, char *argv[])
{
- struct lsblk _ls = { .sort_id = -1, .flags = LSBLK_TREE };
+ struct lsblk _ls = {
+ .sort_id = -1,
+ .dedup_id = -1,
+ .flags = LSBLK_TREE
+ };
+ struct lsblk_devtree *tr = NULL;
int c, status = EXIT_FAILURE;
char *outarg = NULL;
size_t i;
int force_tree = 0;
+ enum {
+ OPT_SYSROOT = CHAR_MAX + 1
+ };
+
static const struct option longopts[] = {
{ "all", no_argument, NULL, 'a' },
{ "bytes", no_argument, NULL, 'b' },
{ "nodeps", no_argument, NULL, 'd' },
{ "discard", no_argument, NULL, 'D' },
+ { "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' },
@@ -1715,6 +1807,7 @@ int main(int argc, char *argv[])
{ "pairs", no_argument, NULL, 'P' },
{ "scsi", no_argument, NULL, 'S' },
{ "sort", required_argument, NULL, 'x' },
+ { "sysroot", required_argument, NULL, OPT_SYSROOT },
{ "tree", no_argument, NULL, 'T' },
{ "version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 },
@@ -1743,7 +1836,7 @@ int main(int argc, char *argv[])
lsblk_init_debug();
while((c = getopt_long(argc, argv,
- "abdDze:fhJlnmo:OpPiI:rstVSTx:", longopts, NULL)) != -1) {
+ "abdDzE:e:fhJlnMmo:OpPiI:rstVSTx:", longopts, NULL)) != -1) {
err_exclusive_options(c, longopts, excl, excl_st);
@@ -1780,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;
@@ -1815,6 +1911,8 @@ int main(int argc, char *argv[])
add_uniq_column(COL_FSTYPE);
add_uniq_column(COL_LABEL);
add_uniq_column(COL_UUID);
+ add_uniq_column(COL_FSAVAIL);
+ add_uniq_column(COL_FSUSEPERC);
add_uniq_column(COL_TARGET);
break;
case 'm':
@@ -1851,9 +1949,19 @@ int main(int argc, char *argv[])
case 'T':
force_tree = 1;
break;
+
+ case OPT_SYSROOT:
+ lsblk->sysroot = optarg;
+ break;
case 'V':
printf(UTIL_LINUX_VERSION);
return EXIT_SUCCESS;
+ case 'E':
+ lsblk->dedup_id = column_name_to_id(optarg, strlen(optarg));
+ if (lsblk->dedup_id >= 0)
+ break;
+ errtryhelp(EXIT_FAILURE);
+ break;
case 'x':
lsblk->flags &= ~LSBLK_TREE; /* disable the default */
lsblk->sort_id = column_name_to_id(optarg, strlen(optarg));
@@ -1902,8 +2010,15 @@ int main(int argc, char *argv[])
lsblk->sort_hidden = 1;
}
- mnt_init_debug(0);
+ if (lsblk->dedup_id >= 0 && column_id_to_number(lsblk->dedup_id) < 0) {
+ /* the deduplication column is not between output columns -- add as hidden */
+ add_column(lsblk->dedup_id);
+ lsblk->dedup_hidden = 1;
+ }
+
+ lsblk_mnt_init();
scols_init_debug(0);
+ ul_path_init_debug();
/*
* initialize output columns
@@ -1928,6 +2043,8 @@ int main(int argc, char *argv[])
fl &= ~SCOLS_FL_TREE;
if (lsblk->sort_hidden && lsblk->sort_id == id)
fl |= SCOLS_FL_HIDDEN;
+ if (lsblk->dedup_hidden && lsblk->dedup_id == id)
+ fl |= SCOLS_FL_HIDDEN;
cl = scols_table_new_column(lsblk->table, ci->name, ci->whint, fl);
if (!cl) {
@@ -1961,13 +2078,21 @@ int main(int argc, char *argv[])
}
}
- if (optind == argc)
- status = iterate_block_devices() == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
- else {
+ tr = lsblk_new_devtree();
+ if (!tr)
+ err(EXIT_FAILURE, _("failed to allocate device tree"));
+
+ if (optind == argc) {
+ int rc = lsblk->inverse ?
+ process_all_devices_inverse(tr) :
+ process_all_devices(tr);
+
+ status = rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+ } else {
int cnt = 0, cnt_err = 0;
while (optind < argc) {
- if (process_one_device(argv[optind++]) != 0)
+ if (process_one_device(tr, argv[optind++]) != 0)
cnt_err++;
cnt++;
}
@@ -1977,6 +2102,13 @@ int main(int argc, char *argv[])
EXIT_SUCCESS; /* all success */
}
+ if (lsblk->dedup_id > -1) {
+ devtree_set_dedupkeys(tr, lsblk->dedup_id);
+ lsblk_devtree_deduplicate_devices(tr);
+ }
+
+ devtree_to_scols(tr, lsblk->table);
+
if (lsblk->sort_col)
scols_sort_table(lsblk->table, lsblk->sort_col);
if (lsblk->force_tree_order)
@@ -1990,11 +2122,9 @@ leave:
scols_unref_table(lsblk->table);
- mnt_unref_table(mtab);
- mnt_unref_table(swaps);
- mnt_unref_cache(mntcache);
-#ifdef HAVE_LIBUDEV
- udev_unref(udev);
-#endif
+ lsblk_mnt_deinit();
+ lsblk_properties_deinit();
+ lsblk_unref_devtree(tr);
+
return status;
}
diff --git a/misc-utils/lsblk.h b/misc-utils/lsblk.h
new file mode 100644
index 000000000..a043e7fd7
--- /dev/null
+++ b/misc-utils/lsblk.h
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2010-2018 Red Hat, Inc. All rights reserved.
+ * Written by Milan Broz <mbroz@redhat.com>
+ * Karel Zak <kzak@redhat.com>
+ */
+#ifndef UTIL_LINUX_LSBLK_H
+#define UTIL_LINUX_LSBLK_H
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+
+#include <libsmartcols.h>
+
+#include "c.h"
+#include "list.h"
+#include "debug.h"
+
+#define LSBLK_DEBUG_INIT (1 << 1)
+#define LSBLK_DEBUG_FILTER (1 << 2)
+#define LSBLK_DEBUG_DEV (1 << 3)
+#define LSBLK_DEBUG_TREE (1 << 4)
+#define LSBLK_DEBUG_DEP (1 << 5)
+#define LSBLK_DEBUG_ALL 0xFFFF
+
+UL_DEBUG_DECLARE_MASK(lsblk);
+#define DBG(m, x) __UL_DBG(lsblk, LSBLK_DEBUG_, m, x)
+#define ON_DBG(m, x) __UL_DBG_CALL(lsblk, LSBLK_DEBUG_, m, x)
+
+#define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(lsblk)
+#include "debugobj.h"
+
+struct lsblk {
+ struct libscols_table *table; /* output table */
+
+ struct libscols_column *sort_col;/* sort output by this column */
+ int sort_id;
+
+ int dedup_id;
+
+ const char *sysroot;
+ int flags; /* 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 */
+ unsigned int sort_hidden:1; /* sort column not between output columns */
+ unsigned int dedup_hidden :1; /* deduplication column not between output columns */
+ unsigned int force_tree_order:1;/* sort lines by parent->tree relation */
+};
+
+extern struct lsblk *lsblk; /* global handler */
+
+struct lsblk_devprop {
+ char *fstype; /* detected fs, NULL or "?" if cannot detect */
+ char *uuid; /* filesystem UUID (or stack uuid) */
+ char *ptuuid; /* partition table UUID */
+ char *pttype; /* partition table type */
+ char *label; /* filesystem label */
+ char *parttype; /* partition type UUID */
+ char *partuuid; /* partition UUID */
+ char *partlabel; /* partition label */
+ char *partflags; /* partition flags */
+ char *wwn; /* storage WWN */
+ char *serial; /* disk serial number */
+ char *model; /* disk model */
+};
+
+/* Device dependence
+ *
+ * Note that the same device may be slave/holder for more another devices. It
+ * means we need to allocate list member rather than use @child directly.
+ */
+struct lsblk_devdep {
+ struct list_head ls_childs; /* item in parent->childs */
+ struct list_head ls_parents; /* item in child->parents */
+
+ struct lsblk_device *child;
+ struct lsblk_device *parent;
+};
+
+struct lsblk_device {
+ int refcount;
+
+ struct list_head childs; /* list with lsblk_devdep */
+ struct list_head parents;
+ struct list_head ls_roots; /* item in devtree->roots list */
+ struct list_head ls_devices; /* item in devtree->devices list */
+
+ struct lsblk_device *wholedisk; /* for partitions */
+
+ struct libscols_line *scols_line;
+
+ struct lsblk_devprop *properties;
+ struct stat st;
+
+ char *name; /* kernel name in /sys/block */
+ char *dm_name; /* DM name (dm/block) */
+
+ char *filename; /* path to device node */
+ char *dedupkey; /* de-duplication key */
+
+ struct path_cxt *sysfs;
+
+ char *mountpoint; /* device mountpoint */
+ struct statvfs fsstat; /* statvfs() result */
+
+ int npartitions; /* # of partitions this device has */
+ int nholders; /* # of devices mapped directly to this device
+ * /sys/block/.../holders */
+ int nslaves; /* # of devices this device maps to */
+ int maj, min; /* devno */
+
+ uint64_t discard_granularity; /* sunknown:-1, yes:1, not:0 */
+
+ uint64_t size; /* device size */
+ int removable; /* unknown:-1, yes:1, not:0 */
+
+ unsigned int is_mounted : 1,
+ is_swap : 1,
+ is_printed : 1,
+ udev_requested : 1,
+ blkid_requested : 1;
+};
+
+#define device_is_partition(_x) ((_x)->wholedisk != NULL)
+
+/*
+ * Note that lsblk tree uses botton devices (devices without slaves) as root
+ * of the tree, and partitions are interpreted as a dependence too; it means:
+ * sda -> sda1 -> md0
+ *
+ * The flag 'is_inverted' turns the tree over (root is device without holders):
+ * md0 -> sda1 -> sda
+ */
+struct lsblk_devtree {
+ int refcount;
+
+ struct list_head roots; /* tree root devices */
+ struct list_head devices; /* all devices */
+
+ unsigned int is_inverse : 1; /* inverse tree */
+};
+
+
+/*
+ * Generic iterator
+ */
+struct lsblk_iter {
+ struct list_head *p; /* current position */
+ struct list_head *head; /* start position */
+ int direction; /* LSBLK_ITER_{FOR,BACK}WARD */
+};
+
+#define LSBLK_ITER_FORWARD 0
+#define LSBLK_ITER_BACKWARD 1
+
+#define IS_ITER_FORWARD(_i) ((_i)->direction == LSBLK_ITER_FORWARD)
+#define IS_ITER_BACKWARD(_i) ((_i)->direction == LSBLK_ITER_BACKWARD)
+
+#define LSBLK_ITER_INIT(itr, list) \
+ do { \
+ (itr)->p = IS_ITER_FORWARD(itr) ? \
+ (list)->next : (list)->prev; \
+ (itr)->head = (list); \
+ } while(0)
+
+#define LSBLK_ITER_ITERATE(itr, res, restype, member) \
+ do { \
+ res = list_entry((itr)->p, restype, member); \
+ (itr)->p = IS_ITER_FORWARD(itr) ? \
+ (itr)->p->next : (itr)->p->prev; \
+ } while(0)
+
+
+/* lsblk-mnt.c */
+extern void lsblk_mnt_init(void);
+extern void lsblk_mnt_deinit(void);
+
+extern char *lsblk_device_get_mountpoint(struct lsblk_device *dev);
+
+/* lsblk-properties.c */
+extern void lsblk_device_free_properties(struct lsblk_devprop *p);
+extern struct lsblk_devprop *lsblk_device_get_properties(struct lsblk_device *dev);
+extern void lsblk_properties_deinit(void);
+
+/* lsblk-devtree.c */
+void lsblk_reset_iter(struct lsblk_iter *itr, int direction);
+struct lsblk_device *lsblk_new_device(void);
+void lsblk_ref_device(struct lsblk_device *dev);
+void lsblk_unref_device(struct lsblk_device *dev);
+int lsblk_device_new_dependence(struct lsblk_device *parent, struct lsblk_device *child);
+int lsblk_device_has_child(struct lsblk_device *dev, struct lsblk_device *child);
+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);
+int lsblk_devtree_add_root(struct lsblk_devtree *tr, struct lsblk_device *dev);
+int lsblk_devtree_next_root(struct lsblk_devtree *tr,
+ struct lsblk_iter *itr,
+ struct lsblk_device **dev);
+int lsblk_devtree_add_device(struct lsblk_devtree *tr, struct lsblk_device *dev);
+int lsblk_devtree_next_device(struct lsblk_devtree *tr,
+ struct lsblk_iter *itr,
+ struct lsblk_device **dev);
+int lsblk_devtree_has_device(struct lsblk_devtree *tr, struct lsblk_device *dev);
+struct lsblk_device *lsblk_devtree_get_device(struct lsblk_devtree *tr, const char *name);
+int lsblk_devtree_remove_device(struct lsblk_devtree *tr, struct lsblk_device *dev);
+int lsblk_devtree_deduplicate_devices(struct lsblk_devtree *tr);
+
+#endif /* UTIL_LINUX_LSBLK_H */
diff --git a/misc-utils/namei.c b/misc-utils/namei.c
index 35cdf1964..60171f600 100644
--- a/misc-utils/namei.c
+++ b/misc-utils/namei.c
@@ -281,7 +281,7 @@ print_namei(struct namei *nm, char *path)
blanks += 1;
blanks += nm->level * 2;
printf("%*s ", blanks, "");
- printf(_("%s - %s\n"), nm->name, strerror(nm->noent));
+ printf("%s - %s\n", nm->name, strerror(nm->noent));
return -1;
}
diff --git a/misc-utils/rename.c b/misc-utils/rename.c
index 0e3e1b9e1..1d9315793 100644
--- a/misc-utils/rename.c
+++ b/misc-utils/rename.c
@@ -17,9 +17,11 @@ for i in $@; do N=`echo "$i" | sed "s/$FROM/$TO/g"`; mv "$i" "$N"; done
#ifdef HAVE_STDIO_EXT_H
# include <stdio_ext.h>
#endif
-#ifdef HAVE_FPURGE
+#ifndef HAVE___FPURGE
+# ifdef HAVE_FPURGE
# define HAVE___FPURGE 1
# define __fpurge fpurge
+# endif
#endif
#include <string.h>
#include <stdlib.h>
diff --git a/misc-utils/uuidd.c b/misc-utils/uuidd.c
index 8b83d91c0..e0be809dd 100644
--- a/misc-utils/uuidd.c
+++ b/misc-utils/uuidd.c
@@ -298,7 +298,9 @@ static void timeout_handler(int sig __attribute__((__unused__)),
siginfo_t * info,
void *context __attribute__((__unused__)))
{
+#ifdef HAVE_TIMER_CREATE
if (info->si_code == SI_TIMER)
+#endif
errx(EXIT_FAILURE, _("timed out"));
}
@@ -327,18 +329,18 @@ static void server_loop(const char *socket_path, const char *pidfile_path,
if (!uuidd_cxt->no_sock) /* no_sock implies no_fork and no_pid */
#endif
{
- static timer_t t_id;
+ struct ul_timer timer;
struct itimerval timeout;
memset(&timeout, 0, sizeof timeout);
timeout.it_value.tv_sec = 30;
- if (setup_timer(&t_id, &timeout, &timeout_handler))
+ if (setup_timer(&timer, &timeout, &timeout_handler))
err(EXIT_FAILURE, _("cannot set up timer"));
if (pidfile_path)
fd_pidfile = create_pidfile(uuidd_cxt, pidfile_path);
ret = call_daemon(socket_path, UUIDD_OP_GETPID, reply_buf,
sizeof(reply_buf), 0, NULL);
- cancel_timer(&t_id);
+ cancel_timer(&timer);
if (ret > 0) {
if (!uuidd_cxt->quiet)
warnx(_("uuidd daemon is already running at pid %s"),
diff --git a/misc-utils/uuidd.service.in b/misc-utils/uuidd.service.in
index 531672765..b4c9c4635 100644
--- a/misc-utils/uuidd.service.in
+++ b/misc-utils/uuidd.service.in
@@ -1,5 +1,6 @@
[Unit]
Description=Daemon for generating UUIDs
+Documentation=man:uuidd(8)
Requires=uuidd.socket
[Service]
@@ -7,6 +8,17 @@ ExecStart=@usrsbin_execdir@/uuidd --socket-activation
Restart=no
User=uuidd
Group=uuidd
+ProtectSystem=strict
+ProtectHome=yes
+PrivateDevices=yes
+PrivateNetwork=yes
+PrivateUsers=yes
+ProtectKernelTunables=yes
+ProtectKernelModules=yes
+ProtectControlGroups=yes
+RestrictAddressFamilies=AF_UNIX
+MemoryDenyWriteExecute=yes
+SystemCallFilter=@default @file-system @basic-io @system-service @signal @io-event @network-io
[Install]
Also=uuidd.socket
diff --git a/misc-utils/uuidparse.1 b/misc-utils/uuidparse.1
index 9afa6b071..516f45836 100644
--- a/misc-utils/uuidparse.1
+++ b/misc-utils/uuidparse.1
@@ -2,7 +2,7 @@
.\" The 3-Clause BSD License
.TH UUIDPARSE "1" "2017-06-18" "util-linux" "User Commands"
.SH NAME
-uuidparse \- an utility to parse unique identifiers
+uuidparse \- a utility to parse unique identifiers
.SH SYNOPSIS
.B uuidparse
[options]
@@ -12,18 +12,27 @@ This command will parse unique identifier inputs from either command line
arguments or standard input. The inputs are white-space separated.
.SH OUTPUT
.SS Variants
+.nr WI \n(.lu-\n(.iu-\w'Microsoft'u-3n
.TS
tab(:);
-left l l.
-NCS:Network Computing System identifier. These were the original UUIDs.
-DCE:The Open Software Foundation's (OSF) Distributed Computing Environment UUIDs.
-Microsoft:Microsoft Windows platform globally unique identifier (GUID).
-other:Unknown variant. Usually invalid input data.
+l lw(\n(WIu).
+NCS:T{
+Network Computing System identifier. These were the original UUIDs.
+T}
+DCE:T{
+The Open Software Foundation's (OSF) Distributed Computing Environment UUIDs.
+T}
+Microsoft:T{
+Microsoft Windows platform globally unique identifier (GUID).
+T}
+other:T{
+Unknown variant. Usually invalid input data.
+T}
.TE
.SS Types
.TS
tab(:);
-left l l.
+l l.
nil:Special type for zero in type file.
time-based:The DCE time based.
DCE:The DCE time and MAC Address.
diff --git a/misc-utils/whereis.c b/misc-utils/whereis.c
index 828106ffe..c598d494e 100644
--- a/misc-utils/whereis.c
+++ b/misc-utils/whereis.c
@@ -99,6 +99,8 @@ struct wh_dirlist {
static const char *bindirs[] = {
"/usr/bin",
"/usr/sbin",
+ "/bin",
+ "/sbin",
#if defined(MULTIARCHTRIPLET)
"/lib/" MULTIARCHTRIPLET,
"/usr/lib/" MULTIARCHTRIPLET,
@@ -106,8 +108,6 @@ static const char *bindirs[] = {
#endif
"/usr/lib",
"/usr/lib64",
- "/bin",
- "/sbin",
"/etc",
"/usr/etc",
"/lib",
diff --git a/misc-utils/wipefs.8 b/misc-utils/wipefs.8
index c26b7890f..3874e1a7f 100644
--- a/misc-utils/wipefs.8
+++ b/misc-utils/wipefs.8
@@ -38,7 +38,8 @@ in environments where a stable output is required.
.B wipefs
calls the BLKRRPART ioctl when it has erased a partition-table signature
-to inform the kernel about the change.
+to inform the kernel about the change. The ioctl is called as the last step
+and when all specified signatures from all specified devices are already erased.
Note that some filesystems and some partition tables store more magic strings on
the device (e.g. FAT, ZFS, GPT). The
diff --git a/misc-utils/wipefs.c b/misc-utils/wipefs.c
index 76149d9fc..13a720e85 100644
--- a/misc-utils/wipefs.c
+++ b/misc-utils/wipefs.c
@@ -62,12 +62,17 @@ struct wipe_desc {
};
struct wipe_control {
- const char *devname;
+ char *devname;
const char *type_pattern; /* -t <pattern> */
struct libscols_table *outtab;
struct wipe_desc *offsets; /* -o <offset> -o <offset> ... */
+ size_t ndevs; /* number of devices to probe */
+
+ char **reread; /* devices to BLKRRPART */
+ size_t nrereads; /* size of reread */
+
unsigned int noact : 1,
all : 1,
quiet : 1,
@@ -504,12 +509,25 @@ err:
static void rereadpt(int fd, const char *devname)
{
struct stat st;
+ int try = 0;
if (fstat(fd, &st) || !S_ISBLK(st.st_mode))
return;
- errno = 0;
- ioctl(fd, BLKRRPART);
+ do {
+ /*
+ * Unfortunately, it's pretty common that the first re-read
+ * without delay is uncuccesful. The reason is probably kernel
+ * and/or udevd. Let's wait a moment and try more attempts.
+ */
+ xusleep(25000);
+
+ errno = 0;
+ ioctl(fd, BLKRRPART);
+ if (errno != EBUSY)
+ break;
+ } while (try++ < 4);
+
printf(_("%s: calling ioctl to re-read partition table: %m\n"), devname);
}
#endif
@@ -590,8 +608,21 @@ static int do_wipe(struct wipe_control *ctl)
fsync(blkid_probe_get_fd(pr));
#ifdef BLKRRPART
- if (reread && (mode & O_EXCL))
- rereadpt(blkid_probe_get_fd(pr), ctl->devname);
+ if (reread && (mode & O_EXCL)) {
+ if (ctl->ndevs > 1) {
+ /*
+ * We're going to probe more device, let's postpone
+ * re-read PT ioctl until all is erased to avoid
+ * situation we erase PT on /dev/sda before /dev/sdaN
+ * devices are processed.
+ */
+ if (!ctl->reread)
+ ctl->reread = xcalloc(ctl->ndevs, sizeof(char *));
+
+ ctl->reread[ctl->nrereads++] = ctl->devname;
+ } else
+ rereadpt(blkid_probe_get_fd(pr), ctl->devname);
+ }
#endif
close(blkid_probe_get_fd(pr));
@@ -771,11 +802,29 @@ main(int argc, char **argv)
/*
* Erase
*/
+ ctl.ndevs = argc - optind;
+
while (optind < argc) {
ctl.devname = argv[optind++];
do_wipe(&ctl);
+ ctl.ndevs--;
}
- }
+#ifdef BLKRRPART
+ /* Re-read partition tables on whole-disk devices. This is
+ * postponed until all is done to avoid conflicts.
+ */
+ for (size_t i = 0; i < ctl.nrereads; i++) {
+ char *devname = ctl.reread[i];
+ int fd = open(devname, O_RDONLY);
+
+ if (fd >= 0) {
+ rereadpt(fd, devname);
+ close(fd);
+ }
+ }
+ free(ctl.reread);
+#endif
+ }
return EXIT_SUCCESS;
}