]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/lsblk.c
wipefs: fix man page --no-headings short option
[thirdparty/util-linux.git] / misc-utils / lsblk.c
CommitLineData
2a4c734b 1/*
f77fa578 2 * lsblk(8) - list block devices
2a4c734b 3 *
b93585e8 4 * Copyright (C) 2010-2018 Red Hat, Inc. All rights reserved.
2a4c734b
MB
5 * Written by Milan Broz <mbroz@redhat.com>
6 * Karel Zak <kzak@redhat.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it would be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
7cebf0bb
SK
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
2a4c734b 21 */
2a4c734b
MB
22#include <stdio.h>
23#include <errno.h>
24#include <getopt.h>
2a4c734b
MB
25#include <stdlib.h>
26#include <unistd.h>
2a4c734b
MB
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <dirent.h>
30#include <fcntl.h>
31#include <string.h>
2a4c734b 32#include <sys/ioctl.h>
2a4c734b
MB
33#include <stdarg.h>
34#include <locale.h>
35#include <pwd.h>
36#include <grp.h>
18eac5ba 37#include <ctype.h>
ccafadb7 38#include <assert.h>
2a4c734b
MB
39
40#include <blkid.h>
2a4c734b 41
540d506f 42#include "c.h"
2a4c734b
MB
43#include "pathnames.h"
44#include "blkdev.h"
45#include "canonicalize.h"
2a4c734b 46#include "nls.h"
2a4c734b
MB
47#include "xalloc.h"
48#include "strutils.h"
766ab9b0 49#include "sysfs.h"
c05a80ca 50#include "closestream.h"
05187653 51#include "optutils.h"
7761bd3b 52#include "fileutils.h"
2a4c734b 53
14560b7f 54#include "lsblk.h"
7e786eca 55
14560b7f 56UL_DEBUG_DEFINE_MASK(lsblk);
7e786eca
KZ
57UL_DEBUG_DEFINE_MASKNAMES(lsblk) = UL_DEBUG_EMPTY_MASKNAMES;
58
1539750f
KZ
59#define LSBLK_EXIT_SOMEOK 64
60#define LSBLK_EXIT_ALLFAILED 32
61
be685b98
KZ
62static int column_id_to_number(int id);
63
2a4c734b
MB
64/* column IDs */
65enum {
66 COL_NAME = 0,
67 COL_KNAME,
66638b0b 68 COL_PATH,
2a4c734b 69 COL_MAJMIN,
bc405d64
KZ
70 COL_FSAVAIL,
71 COL_FSSIZE,
2a4c734b 72 COL_FSTYPE,
bc405d64
KZ
73 COL_FSUSED,
74 COL_FSUSEPERC,
9cca1ef2 75 COL_FSVERSION,
2a4c734b
MB
76 COL_TARGET,
77 COL_LABEL,
78 COL_UUID,
1b06b33d
MB
79 COL_PTUUID,
80 COL_PTTYPE,
4686ba17 81 COL_PARTTYPE,
107e9559 82 COL_PARTTYPENAME,
aa0903e0
KZ
83 COL_PARTLABEL,
84 COL_PARTUUID,
5a2fd932 85 COL_PARTFLAGS,
150db7a9 86 COL_RA,
2a4c734b 87 COL_RO,
627970a7 88 COL_RM,
483987c2 89 COL_HOTPLUG,
2a4c734b 90 COL_MODEL,
460c7afb 91 COL_SERIAL,
2a4c734b 92 COL_SIZE,
01e487c0 93 COL_STATE,
2a4c734b
MB
94 COL_OWNER,
95 COL_GROUP,
96 COL_MODE,
97 COL_ALIOFF,
98 COL_MINIO,
99 COL_OPTIO,
100 COL_PHYSEC,
101 COL_LOGSEC,
102 COL_ROTA,
103 COL_SCHED,
64c9e22a 104 COL_RQ_SIZE,
18eac5ba 105 COL_TYPE,
2d232246
MP
106 COL_DALIGN,
107 COL_DGRAN,
108 COL_DMAX,
109 COL_DZERO,
2adb1a44 110 COL_WSAME,
88ca32b3 111 COL_WWN,
12b06c3d 112 COL_RAND,
310c0603 113 COL_PKNAME,
699e5c4f 114 COL_HCTL,
a5fb4d23 115 COL_TRANSPORT,
7f14ee1b 116 COL_SUBSYS,
d6681cee 117 COL_REV,
f2df4365
DLM
118 COL_VENDOR,
119 COL_ZONED,
2a4c734b
MB
120};
121
9bd4e5c0
OO
122/* basic table settings */
123enum {
124 LSBLK_ASCII = (1 << 0),
125 LSBLK_RAW = (1 << 1),
126 LSBLK_NOHEADINGS = (1 << 2),
bb6e822a
KZ
127 LSBLK_EXPORT = (1 << 3),
128 LSBLK_TREE = (1 << 4),
4a102a48 129 LSBLK_JSON = (1 << 5),
9bd4e5c0
OO
130};
131
407e0ea6 132/* Types used for qsort() and JSON */
642048e4 133enum {
407e0ea6
KZ
134 COLTYPE_STR = 0, /* default */
135 COLTYPE_NUM = 1, /* always u64 number */
136 COLTYPE_SORTNUM = 2, /* string on output, u64 for qsort() */
137 COLTYPE_SIZE = 3, /* srring by default, number when --bytes */
138 COLTYPE_BOOL = 4 /* 0 or 1 */
642048e4
KZ
139};
140
2a4c734b
MB
141/* column names */
142struct colinfo {
143 const char *name; /* header */
144 double whint; /* width hint (N < 1 is in percent of termwidth) */
9bd4e5c0 145 int flags; /* SCOLS_FL_* */
2a4c734b 146 const char *help;
407e0ea6 147 int type; /* COLTYPE_* */
2a4c734b
MB
148};
149
150/* columns descriptions */
507d39c2 151static struct colinfo infos[] = {
b9c088f2 152 [COL_NAME] = { "NAME", 0.25, SCOLS_FL_NOEXTREMES, N_("device name") },
e22d8b95 153 [COL_KNAME] = { "KNAME", 0.3, 0, N_("internal kernel device name") },
bc405d64 154 [COL_PKNAME] = { "PKNAME", 0.3, 0, N_("internal parent kernel device name") },
66638b0b 155 [COL_PATH] = { "PATH", 0.3, 0, N_("path to the device node") },
407e0ea6 156 [COL_MAJMIN] = { "MAJ:MIN", 6, 0, N_("major:minor device number"), COLTYPE_SORTNUM },
bc405d64
KZ
157
158 [COL_FSAVAIL] = { "FSAVAIL", 5, SCOLS_FL_RIGHT, N_("filesystem size available") },
159 [COL_FSSIZE] = { "FSSIZE", 5, SCOLS_FL_RIGHT, N_("filesystem size") },
160 [COL_FSTYPE] = { "FSTYPE", 0.1, SCOLS_FL_TRUNC, N_("filesystem type") },
161 [COL_FSUSED] = { "FSUSED", 5, SCOLS_FL_RIGHT, N_("filesystem size used") },
162 [COL_FSUSEPERC] = { "FSUSE%", 3, SCOLS_FL_RIGHT, N_("filesystem use percentage") },
9cca1ef2 163 [COL_FSVERSION] = { "FSVER", 0.1, SCOLS_FL_TRUNC, N_("filesystem version") },
bc405d64 164
9bd4e5c0 165 [COL_TARGET] = { "MOUNTPOINT", 0.10, SCOLS_FL_TRUNC, N_("where the device is mounted") },
2a4c734b
MB
166 [COL_LABEL] = { "LABEL", 0.1, 0, N_("filesystem LABEL") },
167 [COL_UUID] = { "UUID", 36, 0, N_("filesystem UUID") },
aa0903e0 168
1b06b33d
MB
169 [COL_PTUUID] = { "PTUUID", 36, 0, N_("partition table identifier (usually UUID)") },
170 [COL_PTTYPE] = { "PTTYPE", 0.1, 0, N_("partition table type") },
171
107e9559
KZ
172 [COL_PARTTYPE] = { "PARTTYPE", 36, 0, N_("partition type code or UUID") },
173 [COL_PARTTYPENAME] = { "PARTTYPENAME", 0.1, 0, N_("partition type name") },
aa0903e0
KZ
174 [COL_PARTLABEL] = { "PARTLABEL", 0.1, 0, N_("partition LABEL") },
175 [COL_PARTUUID] = { "PARTUUID", 36, 0, N_("partition UUID") },
5a2fd932 176 [COL_PARTFLAGS] = { "PARTFLAGS", 36, 0, N_("partition flags") },
aa0903e0 177
407e0ea6
KZ
178 [COL_RA] = { "RA", 3, SCOLS_FL_RIGHT, N_("read-ahead of the device"), COLTYPE_NUM },
179 [COL_RO] = { "RO", 1, SCOLS_FL_RIGHT, N_("read-only device"), COLTYPE_BOOL },
180 [COL_RM] = { "RM", 1, SCOLS_FL_RIGHT, N_("removable device"), COLTYPE_BOOL },
181 [COL_HOTPLUG]= { "HOTPLUG", 1, SCOLS_FL_RIGHT, N_("removable or hotplug device (usb, pcmcia, ...)"), COLTYPE_BOOL },
182 [COL_ROTA] = { "ROTA", 1, SCOLS_FL_RIGHT, N_("rotational device"), COLTYPE_BOOL },
183 [COL_RAND] = { "RAND", 1, SCOLS_FL_RIGHT, N_("adds randomness"), COLTYPE_BOOL },
9bd4e5c0
OO
184 [COL_MODEL] = { "MODEL", 0.1, SCOLS_FL_TRUNC, N_("device identifier") },
185 [COL_SERIAL] = { "SERIAL", 0.1, SCOLS_FL_TRUNC, N_("disk serial number") },
407e0ea6 186 [COL_SIZE] = { "SIZE", 5, SCOLS_FL_RIGHT, N_("size of the device"), COLTYPE_SIZE },
9bd4e5c0
OO
187 [COL_STATE] = { "STATE", 7, SCOLS_FL_TRUNC, N_("state of the device") },
188 [COL_OWNER] = { "OWNER", 0.1, SCOLS_FL_TRUNC, N_("user name"), },
189 [COL_GROUP] = { "GROUP", 0.1, SCOLS_FL_TRUNC, N_("group name") },
2a4c734b 190 [COL_MODE] = { "MODE", 10, 0, N_("device node permissions") },
407e0ea6
KZ
191 [COL_ALIOFF] = { "ALIGNMENT", 6, SCOLS_FL_RIGHT, N_("alignment offset"), COLTYPE_NUM },
192 [COL_MINIO] = { "MIN-IO", 6, SCOLS_FL_RIGHT, N_("minimum I/O size"), COLTYPE_NUM },
193 [COL_OPTIO] = { "OPT-IO", 6, SCOLS_FL_RIGHT, N_("optimal I/O size"), COLTYPE_NUM },
194 [COL_PHYSEC] = { "PHY-SEC", 7, SCOLS_FL_RIGHT, N_("physical sector size"), COLTYPE_NUM },
195 [COL_LOGSEC] = { "LOG-SEC", 7, SCOLS_FL_RIGHT, N_("logical sector size"), COLTYPE_NUM },
18eac5ba 196 [COL_SCHED] = { "SCHED", 0.1, 0, N_("I/O scheduler name") },
407e0ea6 197 [COL_RQ_SIZE]= { "RQ-SIZE", 5, SCOLS_FL_RIGHT, N_("request queue size"), COLTYPE_NUM },
2d232246 198 [COL_TYPE] = { "TYPE", 4, 0, N_("device type") },
407e0ea6
KZ
199 [COL_DALIGN] = { "DISC-ALN", 6, SCOLS_FL_RIGHT, N_("discard alignment offset"), COLTYPE_NUM },
200 [COL_DGRAN] = { "DISC-GRAN", 6, SCOLS_FL_RIGHT, N_("discard granularity"), COLTYPE_SIZE },
201 [COL_DMAX] = { "DISC-MAX", 6, SCOLS_FL_RIGHT, N_("discard max bytes"), COLTYPE_SIZE },
202 [COL_DZERO] = { "DISC-ZERO", 1, SCOLS_FL_RIGHT, N_("discard zeroes data"), COLTYPE_BOOL },
203 [COL_WSAME] = { "WSAME", 6, SCOLS_FL_RIGHT, N_("write same max bytes"), COLTYPE_SIZE },
88ca32b3 204 [COL_WWN] = { "WWN", 18, 0, N_("unique storage identifier") },
699e5c4f 205 [COL_HCTL] = { "HCTL", 10, 0, N_("Host:Channel:Target:Lun for SCSI") },
a5fb4d23 206 [COL_TRANSPORT] = { "TRAN", 6, 0, N_("device transport type") },
7f14ee1b 207 [COL_SUBSYS] = { "SUBSYSTEMS", 0.1, SCOLS_FL_NOEXTREMES, N_("de-duplicated chain of subsystems") },
9bd4e5c0
OO
208 [COL_REV] = { "REV", 4, SCOLS_FL_RIGHT, N_("device revision") },
209 [COL_VENDOR] = { "VENDOR", 0.1, SCOLS_FL_TRUNC, N_("device vendor") },
f2df4365 210 [COL_ZONED] = { "ZONED", 0.3, 0, N_("zone model") },
2a4c734b
MB
211};
212
14560b7f 213struct lsblk *lsblk; /* global handler */
507d39c2 214
b93585e8
KZ
215/*
216 * columns[] array specifies all currently wanted output column. The columns
b6327c6f
KZ
217 * are defined by infos[] array and you can specify (on command line) each
218 * column twice. That's enough, dynamically allocated array of the columns is
b93585e8
KZ
219 * unnecessary overkill and over-engineering in this case
220 */
b6327c6f 221static int columns[ARRAY_SIZE(infos) * 2];
40b17508 222static size_t ncolumns;
b6327c6f 223
be685b98 224static inline void add_column(int id)
b6327c6f 225{
be685b98 226 if (ncolumns >= ARRAY_SIZE(columns))
b6327c6f 227 errx(EXIT_FAILURE, _("too many columns specified, "
1d231190 228 "the limit is %zu columns"),
be685b98
KZ
229 ARRAY_SIZE(columns) - 1);
230 columns[ ncolumns++ ] = id;
b6327c6f
KZ
231}
232
be685b98
KZ
233static inline void add_uniq_column(int id)
234{
235 if (column_id_to_number(id) < 0)
236 add_column(id);
237}
2a4c734b 238
b93585e8
KZ
239static void lsblk_init_debug(void)
240{
241 __UL_INIT_DEBUG_FROM_ENV(lsblk, LSBLK_DEBUG_, 0, LSBLK_DEBUG);
242}
243
244/*
245 * exclude/include devices filter based on major device numbers
246 */
f6da50d4
BV
247static int excludes[256];
248static size_t nexcludes;
2a4c734b 249
2ef4e8ba
KZ
250static int includes[256];
251static size_t nincludes;
252
2a4c734b
MB
253static int is_maj_excluded(int maj)
254{
6aace32f 255 size_t i;
2a4c734b
MB
256
257 assert(ARRAY_SIZE(excludes) > nexcludes);
258
2ef4e8ba 259 if (!nexcludes)
9e930041 260 return 0; /* filter not enabled, device not excluded */
2ef4e8ba 261
7e786eca
KZ
262 for (i = 0; i < nexcludes; i++) {
263 if (excludes[i] == maj) {
264 DBG(FILTER, ul_debug("exclude: maj=%d", maj));
2a4c734b 265 return 1;
7e786eca
KZ
266 }
267 }
2a4c734b
MB
268 return 0;
269}
270
2ef4e8ba
KZ
271static int is_maj_included(int maj)
272{
273 size_t i;
274
275 assert(ARRAY_SIZE(includes) > nincludes);
276
277 if (!nincludes)
278 return 1; /* filter not enabled, device is included */
279
7e786eca
KZ
280 for (i = 0; i < nincludes; i++) {
281 if (includes[i] == maj) {
282 DBG(FILTER, ul_debug("include: maj=%d", maj));
2ef4e8ba 283 return 1;
7e786eca
KZ
284 }
285 }
2ef4e8ba
KZ
286 return 0;
287}
288
b93585e8 289/* Converts column sequential number to column ID (COL_*) */
2a4c734b
MB
290static int get_column_id(int num)
291{
40b17508
KZ
292 assert(num >= 0);
293 assert((size_t) num < ncolumns);
b6327c6f 294 assert(columns[num] < (int) ARRAY_SIZE(infos));
2a4c734b
MB
295 return columns[num];
296}
297
b93585e8 298/* Returns column description for the column sequential number */
2a4c734b
MB
299static struct colinfo *get_column_info(int num)
300{
301 return &infos[ get_column_id(num) ];
302}
303
b93585e8 304/* Converts column name (as defined in the infos[] to the column ID */
2a4c734b
MB
305static int column_name_to_id(const char *name, size_t namesz)
306{
507d39c2 307 size_t i;
2a4c734b 308
03df0d11 309 for (i = 0; i < ARRAY_SIZE(infos); i++) {
2a4c734b
MB
310 const char *cn = infos[i].name;
311
312 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
313 return i;
314 }
315 warnx(_("unknown column: %s"), name);
316 return -1;
317}
318
b93585e8 319/* Converts column ID (COL_*) to column sequential number */
642048e4
KZ
320static int column_id_to_number(int id)
321{
322 size_t i;
323
40b17508 324 for (i = 0; i < ncolumns; i++)
642048e4
KZ
325 if (columns[i] == id)
326 return i;
327 return -1;
328}
329
b93585e8 330/* Checks for DM prefix in the device name */
2a4c734b
MB
331static int is_dm(const char *name)
332{
333 return strncmp(name, "dm-", 3) ? 0 : 1;
334}
335
b158bd29 336/* Returns full pat to the device node (TODO: what about sysfs_blkdev_get_path()) */
fed34a1e 337static char *get_device_path(struct lsblk_device *dev)
2a4c734b
MB
338{
339 char path[PATH_MAX];
340
fed34a1e
KZ
341 assert(dev);
342 assert(dev->name);
2a4c734b 343
fed34a1e
KZ
344 if (is_dm(dev->name))
345 return __canonicalize_dm_name(lsblk->sysroot, dev->name);
2a4c734b 346
fed34a1e 347 snprintf(path, sizeof(path), "/dev/%s", dev->name);
92441d5c 348 sysfs_devname_sys_to_dev(path);
2a4c734b
MB
349 return xstrdup(path);
350}
351
fed34a1e 352static int is_readonly_device(struct lsblk_device *dev)
2a4c734b
MB
353{
354 int fd, ro = 0;
355
fed34a1e 356 if (ul_path_scanf(dev->sysfs, "ro", "%d", &ro) == 1)
2a4c734b
MB
357 return ro;
358
359 /* fallback if "ro" attribute does not exist */
fed34a1e 360 fd = open(dev->filename, O_RDONLY);
2a4c734b 361 if (fd != -1) {
9c53a49c
KZ
362 if (ioctl(fd, BLKROGET, &ro) != 0)
363 ro = 0;
2a4c734b
MB
364 close(fd);
365 }
366 return ro;
367}
368
fed34a1e 369static char *get_scheduler(struct lsblk_device *dev)
2a4c734b 370{
f153614c 371 char buf[128];
2a4c734b
MB
372 char *p, *res = NULL;
373
fed34a1e 374 if (ul_path_read_buffer(dev->sysfs, buf, sizeof(buf), "queue/scheduler") == 0)
2a4c734b 375 return NULL;
f153614c 376 p = strchr(buf, '[');
2a4c734b
MB
377 if (p) {
378 res = p + 1;
379 p = strchr(res, ']');
380 if (p) {
381 *p = '\0';
382 res = xstrdup(res);
383 } else
384 res = NULL;
385 }
2a4c734b
MB
386 return res;
387}
388
fed34a1e 389static char *get_type(struct lsblk_device *dev)
18eac5ba
MB
390{
391 char *res = NULL, *p;
392
0bd05f5e 393 if (device_is_partition(dev))
ef2ce68b
KZ
394 return xstrdup("part");
395
fed34a1e 396 if (is_dm(dev->name)) {
f153614c 397 char *dm_uuid = NULL;
18eac5ba
MB
398
399 /* The DM_UUID prefix should be set to subsystem owning
400 * the device - LVM, CRYPT, DMRAID, MPATH, PART */
fed34a1e 401 if (ul_path_read_string(dev->sysfs, &dm_uuid, "dm/uuid") > 0
f153614c 402 && dm_uuid) {
18eac5ba
MB
403 char *tmp = dm_uuid;
404 char *dm_uuid_prefix = strsep(&tmp, "-");
405
406 if (dm_uuid_prefix) {
407 /* kpartx hack to remove partition number */
408 if (strncasecmp(dm_uuid_prefix, "part", 4) == 0)
409 dm_uuid_prefix[4] = '\0';
410
411 res = xstrdup(dm_uuid_prefix);
412 }
413 }
414
415 free(dm_uuid);
416 if (!res)
417 /* No UUID or no prefix - just mark it as DM device */
418 res = xstrdup("dm");
419
fed34a1e 420 } else if (!strncmp(dev->name, "loop", 4)) {
18eac5ba
MB
421 res = xstrdup("loop");
422
fed34a1e 423 } else if (!strncmp(dev->name, "md", 2)) {
f153614c
KZ
424 char *md_level = NULL;
425
fed34a1e 426 ul_path_read_string(dev->sysfs, &md_level, "md/level");
18eac5ba
MB
427 res = md_level ? md_level : xstrdup("md");
428
429 } else {
8d687180 430 const char *type = NULL;
90e9fcda 431 int x = 0;
9d738ff8 432
fed34a1e 433 if (ul_path_read_s32(dev->sysfs, &x, "device/type") == 0)
8d687180 434 type = blkdev_scsi_type_to_name(x);
9d738ff8 435 if (!type)
ef2ce68b 436 type = "disk";
9d738ff8 437 res = xstrdup(type);
18eac5ba
MB
438 }
439
440 for (p = res; p && *p; p++)
441 *p = tolower((unsigned char) *p);
442 return res;
443}
444
a5fb4d23 445/* Thanks to lsscsi code for idea of detection logic used here */
fed34a1e 446static char *get_transport(struct lsblk_device *dev)
a5fb4d23 447{
fed34a1e 448 struct path_cxt *sysfs = dev->sysfs;
2f2f7e80
KZ
449 char *attr = NULL;
450 const char *trans = NULL;
a5fb4d23 451
f153614c 452
28ffc2b7 453 /* SCSI - Serial Peripheral Interface */
f153614c 454 if (sysfs_blkdev_scsi_host_is(sysfs, "spi"))
2f2f7e80 455 trans = "spi";
a5fb4d23
MB
456
457 /* FC/FCoE - Fibre Channel / Fibre Channel over Ethernet */
f153614c
KZ
458 else if (sysfs_blkdev_scsi_host_is(sysfs, "fc")) {
459 attr = sysfs_blkdev_scsi_host_strdup_attribute(sysfs, "fc", "symbolic_name");
23a11c74 460 if (!attr)
a5fb4d23 461 return NULL;
2f2f7e80
KZ
462 trans = strstr(attr, " over ") ? "fcoe" : "fc";
463 free(attr);
a5fb4d23
MB
464 }
465
466 /* SAS - Serial Attached SCSI */
f153614c
KZ
467 else if (sysfs_blkdev_scsi_host_is(sysfs, "sas") ||
468 sysfs_blkdev_scsi_has_attribute(sysfs, "sas_device"))
2f2f7e80 469 trans = "sas";
a5fb4d23 470
a5fb4d23
MB
471
472 /* SBP - Serial Bus Protocol (FireWire) */
f153614c 473 else if (sysfs_blkdev_scsi_has_attribute(sysfs, "ieee1394_id"))
2f2f7e80 474 trans = "sbp";
a5fb4d23
MB
475
476 /* iSCSI */
f153614c 477 else if (sysfs_blkdev_scsi_host_is(sysfs, "iscsi"))
2f2f7e80 478 trans ="iscsi";
a5fb4d23
MB
479
480 /* USB - Universal Serial Bus */
f153614c 481 else if (sysfs_blkdev_scsi_path_contains(sysfs, "usb"))
2f2f7e80 482 trans = "usb";
a5fb4d23
MB
483
484 /* ATA, SATA */
f153614c
KZ
485 else if (sysfs_blkdev_scsi_host_is(sysfs, "scsi")) {
486 attr = sysfs_blkdev_scsi_host_strdup_attribute(sysfs, "scsi", "proc_name");
23a11c74 487 if (!attr)
a5fb4d23 488 return NULL;
2f2f7e80
KZ
489 if (!strncmp(attr, "ahci", 4) || !strncmp(attr, "sata", 4))
490 trans = "sata";
491 else if (strstr(attr, "ata"))
492 trans = "ata";
493 free(attr);
3d305170 494
fed34a1e 495 } else if (strncmp(dev->name, "nvme", 4) == 0)
3d305170 496 trans = "nvme";
a5fb4d23 497
2f2f7e80 498 return trans ? xstrdup(trans) : NULL;
a5fb4d23
MB
499}
500
fed34a1e 501static char *get_subsystems(struct lsblk_device *dev)
7f14ee1b
KZ
502{
503 char path[PATH_MAX];
504 char *sub, *chain, *res = NULL;
505 size_t len = 0, last = 0;
506
fed34a1e 507 chain = sysfs_blkdev_get_devchain(dev->sysfs, path, sizeof(path));
7f14ee1b
KZ
508 if (!chain)
509 return NULL;
510
fed34a1e 511 while (sysfs_blkdev_next_subsystem(dev->sysfs, chain, &sub) == 0) {
7f14ee1b
KZ
512 size_t sz;
513
514 /* don't create "block:scsi:scsi", but "block:scsi" */
56e78cb5
AH
515 if (len && strcmp(res + last, sub) == 0) {
516 free(sub);
7f14ee1b 517 continue;
56e78cb5 518 }
7f14ee1b
KZ
519
520 sz = strlen(sub);
521 res = xrealloc(res, len + sz + 2);
522 if (len)
523 res[len++] = ':';
524
525 memcpy(res + len, sub, sz + 1);
526 last = len;
527 len += sz;
528 free(sub);
529 }
530
531 return res;
532}
533
534
9bd4e5c0 535#define is_parsable(_l) (scols_table_is_raw((_l)->table) || \
4a102a48
KZ
536 scols_table_is_export((_l)->table) || \
537 scols_table_is_json((_l)->table))
7c9c872c 538
c7e76cd1
KZ
539static char *mk_name(const char *name)
540{
541 char *p;
542 if (!name)
543 return NULL;
544 if (lsblk->paths)
545 xasprintf(&p, "/dev/%s", name);
546 else
547 p = xstrdup(name);
92441d5c
KZ
548 if (p)
549 sysfs_devname_sys_to_dev(p);
c7e76cd1
KZ
550 return p;
551}
552
553static char *mk_dm_name(const char *name)
554{
555 char *p;
556 if (!name)
557 return NULL;
558 if (lsblk->paths)
559 xasprintf(&p, "/dev/mapper/%s", name);
560 else
561 p = xstrdup(name);
562 return p;
563}
564
642048e4
KZ
565/* stores data to scols cell userdata (invisible and independent on output)
566 * to make the original values accessible for sort functions
567 */
568static void set_sortdata_u64(struct libscols_line *ln, int col, uint64_t x)
569{
570 struct libscols_cell *ce = scols_line_get_cell(ln, col);
571 uint64_t *data;
572
573 if (!ce)
574 return;
575 data = xmalloc(sizeof(uint64_t));
576 *data = x;
577 scols_cell_set_userdata(ce, data);
578}
579
d81e32bd
KZ
580/* do not modify *data on any error */
581static void str2u64(const char *str, uint64_t *data)
642048e4 582{
d81e32bd
KZ
583 uintmax_t num;
584 char *end = NULL;
642048e4 585
d81e32bd
KZ
586 errno = 0;
587 if (str == NULL || *str == '\0')
642048e4 588 return;
d81e32bd 589 num = strtoumax(str, &end, 10);
642048e4 590
d81e32bd
KZ
591 if (errno || str == end || (end && *end))
592 return;
593 *data = num;
642048e4
KZ
594}
595
596static void unref_sortdata(struct libscols_table *tb)
597{
598 struct libscols_iter *itr;
599 struct libscols_line *ln;
600
601 if (!tb || !lsblk->sort_col)
602 return;
603 itr = scols_new_iter(SCOLS_ITER_FORWARD);
604 if (!itr)
605 return;
606 while (scols_table_next_line(tb, itr, &ln) == 0) {
607 struct libscols_cell *ce = scols_line_get_column_cell(ln,
608 lsblk->sort_col);
609 void *data = scols_cell_get_userdata(ce);
610 free(data);
611 }
612
613 scols_free_iter(itr);
614}
615
fed34a1e 616static char *get_vfs_attribute(struct lsblk_device *dev, int id)
bc405d64
KZ
617{
618 char *sizestr;
619 uint64_t vfs_attr = 0;
620 char *mnt;
621
fed34a1e
KZ
622 if (!dev->fsstat.f_blocks) {
623 mnt = lsblk_device_get_mountpoint(dev);
624 if (!mnt || dev->is_swap)
bc405d64 625 return NULL;
fed34a1e 626 if (statvfs(mnt, &dev->fsstat) != 0)
bc405d64
KZ
627 return NULL;
628 }
629
630 switch(id) {
631 case COL_FSSIZE:
fed34a1e 632 vfs_attr = dev->fsstat.f_frsize * dev->fsstat.f_blocks;
bc405d64
KZ
633 break;
634 case COL_FSAVAIL:
fed34a1e 635 vfs_attr = dev->fsstat.f_frsize * dev->fsstat.f_bavail;
bc405d64
KZ
636 break;
637 case COL_FSUSED:
fed34a1e 638 vfs_attr = dev->fsstat.f_frsize * (dev->fsstat.f_blocks - dev->fsstat.f_bfree);
bc405d64
KZ
639 break;
640 case COL_FSUSEPERC:
fed34a1e 641 if (dev->fsstat.f_blocks == 0)
bc405d64
KZ
642 return xstrdup("-");
643
644 xasprintf(&sizestr, "%.0f%%",
fed34a1e
KZ
645 (double)(dev->fsstat.f_blocks - dev->fsstat.f_bfree) /
646 dev->fsstat.f_blocks * 100);
bc405d64
KZ
647 return sizestr;
648 }
649
650 if (!vfs_attr)
651 sizestr = xstrdup("0");
652 else if (lsblk->bytes)
653 xasprintf(&sizestr, "%ju", vfs_attr);
654 else
655 sizestr = size_to_human_string(SIZE_SUFFIX_1LETTER, vfs_attr);
656
657 return sizestr;
658}
659
fed34a1e 660static struct stat *device_get_stat(struct lsblk_device *dev)
652cb1cd 661{
404eef65
KZ
662 if (!dev->st.st_rdev
663 && stat(dev->filename, &dev->st) != 0)
664 return NULL;
652cb1cd 665
fed34a1e 666 return &dev->st;
652cb1cd 667}
bc405d64 668
81a8936c
KZ
669static int is_removable_device(struct lsblk_device *dev, struct lsblk_device *parent)
670{
671 struct path_cxt *pc;
672
673 if (dev->removable != -1)
674 goto done;
675 if (ul_path_scanf(dev->sysfs, "removable", "%d", &dev->removable) == 1)
676 goto done;
677
678 if (parent) {
679 pc = sysfs_blkdev_get_parent(dev->sysfs);
680 if (!pc)
681 goto done;
682
74152bf9 683 /* dev is partition and parent is whole-disk */
81a8936c 684 if (pc == parent->sysfs)
81a8936c 685 dev->removable = is_removable_device(parent, NULL);
74152bf9
KZ
686
687 /* parent is something else, use sysfs parent */
688 else if (ul_path_scanf(pc, "removable", "%d", &dev->removable) != 1)
689 dev->removable = 0;
81a8936c 690 }
81a8936c
KZ
691done:
692 if (dev->removable == -1)
693 dev->removable = 0;
694 return dev->removable;
695}
696
8a2f175c
KZ
697static uint64_t device_get_discard_granularity(struct lsblk_device *dev)
698{
699 if (dev->discard_granularity == (uint64_t) -1
700 && ul_path_read_u64(dev->sysfs, &dev->discard_granularity,
701 "queue/discard_granularity") != 0)
702 dev->discard_granularity = 0;
703
704 return dev->discard_granularity;
705}
706
b93585e8 707/*
d81e32bd
KZ
708 * Generates data (string) for column specified by column ID for specified device. If sortdata
709 * is not NULL then returns number usable to sort the column if the data are available for the
710 * column.
b93585e8 711 */
d81e32bd
KZ
712static char *device_get_data(
713 struct lsblk_device *dev, /* device */
714 struct lsblk_device *parent, /* device parent as defined in the tree */
715 int id, /* column ID (COL_*) */
716 uint64_t *sortdata) /* returns sort data as number */
2a4c734b 717{
6f74ede5 718 struct lsblk_devprop *prop = NULL;
bb6e822a 719 char *str = NULL;
2a4c734b 720
2a4c734b
MB
721 switch(id) {
722 case COL_NAME:
fed34a1e 723 str = dev->dm_name ? mk_dm_name(dev->dm_name) : mk_name(dev->name);
c7e76cd1 724 break;
2a4c734b 725 case COL_KNAME:
fed34a1e 726 str = mk_name(dev->name);
2a4c734b 727 break;
310c0603 728 case COL_PKNAME:
ae4c2c37
KZ
729 if (parent)
730 str = mk_name(parent->name);
310c0603 731 break;
66638b0b 732 case COL_PATH:
fed34a1e
KZ
733 if (dev->filename)
734 str = xstrdup(dev->filename);
66638b0b 735 break;
2a4c734b 736 case COL_OWNER:
6f74ede5
KZ
737 if (lsblk->sysroot)
738 prop = lsblk_device_get_properties(dev);
739 if (prop && prop->owner) {
740 str = xstrdup(prop->owner);
741 } else {
742 struct stat *st = device_get_stat(dev);
743 struct passwd *pw = st ? getpwuid(st->st_uid) : NULL;
744 if (pw)
745 str = xstrdup(pw->pw_name);
746 }
2a4c734b 747 break;
2a4c734b 748 case COL_GROUP:
6f74ede5
KZ
749 if (lsblk->sysroot)
750 prop = lsblk_device_get_properties(dev);
751 if (prop && prop->group) {
752 str = xstrdup(prop->group);
753 } else {
754 struct stat *st = device_get_stat(dev);
755 struct group *gr = st ? getgrgid(st->st_gid) : NULL;
756 if (gr)
757 str = xstrdup(gr->gr_name);
758 }
2a4c734b 759 break;
2a4c734b 760 case COL_MODE:
6f74ede5
KZ
761 if (lsblk->sysroot)
762 prop = lsblk_device_get_properties(dev);
763 if (prop && prop->mode) {
764 str = xstrdup(prop->mode);
765 } else {
766 struct stat *st = device_get_stat(dev);
767 char md[11] = { '\0' };
768
769 if (st)
770 str = xstrdup(xstrmode(st->st_mode, md));
771 }
2a4c734b 772 break;
2a4c734b 773 case COL_MAJMIN:
7c9c872c 774 if (is_parsable(lsblk))
fed34a1e 775 xasprintf(&str, "%u:%u", dev->maj, dev->min);
2a4c734b 776 else
fed34a1e 777 xasprintf(&str, "%3u:%-3u", dev->maj, dev->min);
d81e32bd
KZ
778 if (sortdata)
779 *sortdata = makedev(dev->maj, dev->min);
2a4c734b
MB
780 break;
781 case COL_FSTYPE:
fed34a1e 782 prop = lsblk_device_get_properties(dev);
baad6dcc
KZ
783 if (prop && prop->fstype)
784 str = xstrdup(prop->fstype);
2a4c734b 785 break;
bc405d64
KZ
786 case COL_FSSIZE:
787 case COL_FSAVAIL:
788 case COL_FSUSED:
789 case COL_FSUSEPERC:
fed34a1e 790 str = get_vfs_attribute(dev, id);
bc405d64 791 break;
9cca1ef2
KZ
792 case COL_FSVERSION:
793 prop = lsblk_device_get_properties(dev);
794 if (prop && prop->fsversion)
795 str = xstrdup(prop->fsversion);
796 break;
2a4c734b 797 case COL_TARGET:
f3aded3f
SK
798 {
799 char *s = lsblk_device_get_mountpoint(dev);
800 if (s)
801 str = xstrdup(s);
802 else
803 str = NULL;
2a4c734b 804 break;
f3aded3f 805 }
2a4c734b 806 case COL_LABEL:
fed34a1e 807 prop = lsblk_device_get_properties(dev);
baad6dcc
KZ
808 if (prop && prop->label)
809 str = xstrdup(prop->label);
2a4c734b
MB
810 break;
811 case COL_UUID:
fed34a1e 812 prop = lsblk_device_get_properties(dev);
baad6dcc
KZ
813 if (prop && prop->uuid)
814 str = xstrdup(prop->uuid);
2a4c734b 815 break;
1b06b33d 816 case COL_PTUUID:
fed34a1e 817 prop = lsblk_device_get_properties(dev);
baad6dcc
KZ
818 if (prop && prop->ptuuid)
819 str = xstrdup(prop->ptuuid);
1b06b33d
MB
820 break;
821 case COL_PTTYPE:
fed34a1e 822 prop = lsblk_device_get_properties(dev);
baad6dcc
KZ
823 if (prop && prop->pttype)
824 str = xstrdup(prop->pttype);
1b06b33d 825 break;
4686ba17 826 case COL_PARTTYPE:
fed34a1e 827 prop = lsblk_device_get_properties(dev);
baad6dcc
KZ
828 if (prop && prop->parttype)
829 str = xstrdup(prop->parttype);
4686ba17 830 break;
107e9559
KZ
831 case COL_PARTTYPENAME:
832 prop = lsblk_device_get_properties(dev);
833 if (prop && prop->parttype && prop->pttype) {
834 const char *x = lsblk_parttype_code_to_string(
835 prop->parttype, prop->pttype);
836 if (x)
837 str = xstrdup(x);
838 }
839 break;
aa0903e0 840 case COL_PARTLABEL:
fed34a1e 841 prop = lsblk_device_get_properties(dev);
baad6dcc
KZ
842 if (prop && prop->partlabel)
843 str = xstrdup(prop->partlabel);
aa0903e0
KZ
844 break;
845 case COL_PARTUUID:
fed34a1e 846 prop = lsblk_device_get_properties(dev);
baad6dcc
KZ
847 if (prop && prop->partuuid)
848 str = xstrdup(prop->partuuid);
aa0903e0 849 break;
5a2fd932 850 case COL_PARTFLAGS:
fed34a1e 851 prop = lsblk_device_get_properties(dev);
baad6dcc
KZ
852 if (prop && prop->partflags)
853 str = xstrdup(prop->partflags);
5a2fd932 854 break;
88ca32b3 855 case COL_WWN:
fed34a1e 856 prop = lsblk_device_get_properties(dev);
baad6dcc
KZ
857 if (prop && prop->wwn)
858 str = xstrdup(prop->wwn);
88ca32b3 859 break;
150db7a9 860 case COL_RA:
fed34a1e 861 ul_path_read_string(dev->sysfs, &str, "queue/read_ahead_kb");
d81e32bd
KZ
862 if (sortdata)
863 str2u64(str, sortdata);
150db7a9 864 break;
2a4c734b 865 case COL_RO:
fed34a1e 866 str = xstrdup(is_readonly_device(dev) ? "1" : "0");
2a4c734b 867 break;
627970a7 868 case COL_RM:
81a8936c 869 str = xstrdup(is_removable_device(dev, parent) ? "1" : "0");
2a4c734b 870 break;
483987c2 871 case COL_HOTPLUG:
fed34a1e 872 str = sysfs_blkdev_is_hotpluggable(dev->sysfs) ? xstrdup("1") : xstrdup("0");
483987c2 873 break;
2a4c734b 874 case COL_ROTA:
fed34a1e 875 ul_path_read_string(dev->sysfs, &str, "queue/rotational");
2a4c734b 876 break;
12b06c3d 877 case COL_RAND:
fed34a1e 878 ul_path_read_string(dev->sysfs, &str, "queue/add_random");
12b06c3d 879 break;
2a4c734b 880 case COL_MODEL:
10501add 881 if (!device_is_partition(dev) && dev->nslaves == 0) {
fed34a1e 882 prop = lsblk_device_get_properties(dev);
baad6dcc
KZ
883 if (prop && prop->model)
884 str = xstrdup(prop->model);
13cbc6f2 885 else
fed34a1e 886 ul_path_read_string(dev->sysfs, &str, "device/model");
13cbc6f2 887 }
d6681cee 888 break;
460c7afb 889 case COL_SERIAL:
10501add 890 if (!device_is_partition(dev) && dev->nslaves == 0) {
fed34a1e 891 prop = lsblk_device_get_properties(dev);
baad6dcc
KZ
892 if (prop && prop->serial)
893 str = xstrdup(prop->serial);
3d305170 894 else
fed34a1e 895 ul_path_read_string(dev->sysfs, &str, "device/serial");
460c7afb
KZ
896 }
897 break;
d6681cee 898 case COL_REV:
10501add 899 if (!device_is_partition(dev) && dev->nslaves == 0)
fed34a1e 900 ul_path_read_string(dev->sysfs, &str, "device/rev");
99f43b72
MB
901 break;
902 case COL_VENDOR:
10501add 903 if (!device_is_partition(dev) && dev->nslaves == 0)
fed34a1e 904 ul_path_read_string(dev->sysfs, &str, "device/vendor");
2a4c734b
MB
905 break;
906 case COL_SIZE:
fed34a1e 907 if (!dev->size)
bb6e822a
KZ
908 break;
909 if (lsblk->bytes)
fed34a1e 910 xasprintf(&str, "%ju", dev->size);
bb6e822a 911 else
fed34a1e 912 str = size_to_human_string(SIZE_SUFFIX_1LETTER, dev->size);
d81e32bd
KZ
913 if (sortdata)
914 *sortdata = dev->size;
2a4c734b 915 break;
01e487c0 916 case COL_STATE:
10501add 917 if (!device_is_partition(dev) && !dev->dm_name)
fed34a1e
KZ
918 ul_path_read_string(dev->sysfs, &str, "device/state");
919 else if (dev->dm_name) {
01e487c0 920 int x = 0;
fed34a1e 921 if (ul_path_read_s32(dev->sysfs, &x, "dm/suspended") == 0)
bb6e822a 922 str = xstrdup(x ? "suspended" : "running");
01e487c0 923 }
01e487c0 924 break;
2a4c734b 925 case COL_ALIOFF:
fed34a1e 926 ul_path_read_string(dev->sysfs, &str, "alignment_offset");
d81e32bd
KZ
927 if (sortdata)
928 str2u64(str, sortdata);
2a4c734b
MB
929 break;
930 case COL_MINIO:
fed34a1e 931 ul_path_read_string(dev->sysfs, &str, "queue/minimum_io_size");
d81e32bd
KZ
932 if (sortdata)
933 str2u64(str, sortdata);
2a4c734b
MB
934 break;
935 case COL_OPTIO:
fed34a1e 936 ul_path_read_string(dev->sysfs, &str, "queue/optimal_io_size");
d81e32bd
KZ
937 if (sortdata)
938 str2u64(str, sortdata);
2a4c734b
MB
939 break;
940 case COL_PHYSEC:
fed34a1e 941 ul_path_read_string(dev->sysfs, &str, "queue/physical_block_size");
d81e32bd
KZ
942 if (sortdata)
943 str2u64(str, sortdata);
2a4c734b
MB
944 break;
945 case COL_LOGSEC:
fed34a1e 946 ul_path_read_string(dev->sysfs, &str, "queue/logical_block_size");
d81e32bd
KZ
947 if (sortdata)
948 str2u64(str, sortdata);
2a4c734b
MB
949 break;
950 case COL_SCHED:
fed34a1e 951 str = get_scheduler(dev);
2a4c734b 952 break;
64c9e22a 953 case COL_RQ_SIZE:
fed34a1e 954 ul_path_read_string(dev->sysfs, &str, "queue/nr_requests");
d81e32bd
KZ
955 if (sortdata)
956 str2u64(str, sortdata);
64c9e22a 957 break;
18eac5ba 958 case COL_TYPE:
fed34a1e 959 str = get_type(dev);
18eac5ba 960 break;
699e5c4f
MB
961 case COL_HCTL:
962 {
963 int h, c, t, l;
fed34a1e 964 if (sysfs_blkdev_scsi_get_hctl(dev->sysfs, &h, &c, &t, &l) == 0)
bb6e822a 965 xasprintf(&str, "%d:%d:%d:%d", h, c, t, l);
699e5c4f
MB
966 break;
967 }
a5fb4d23 968 case COL_TRANSPORT:
fed34a1e 969 str = get_transport(dev);
a5fb4d23 970 break;
7f14ee1b 971 case COL_SUBSYS:
fed34a1e 972 str = get_subsystems(dev);
7f14ee1b 973 break;
2d232246 974 case COL_DALIGN:
8a2f175c 975 if (device_get_discard_granularity(dev) > 0)
fed34a1e 976 ul_path_read_string(dev->sysfs, &str, "discard_alignment");
bb6e822a
KZ
977 if (!str)
978 str = xstrdup("0");
d81e32bd
KZ
979 if (sortdata)
980 str2u64(str, sortdata);
2d232246
MP
981 break;
982 case COL_DGRAN:
642048e4 983 if (lsblk->bytes) {
fed34a1e 984 ul_path_read_string(dev->sysfs, &str, "queue/discard_granularity");
d81e32bd
KZ
985 if (sortdata)
986 str2u64(str, sortdata);
642048e4 987 } else {
8a2f175c
KZ
988 uint64_t x = device_get_discard_granularity(dev);
989 str = size_to_human_string(SIZE_SUFFIX_1LETTER, x);
d81e32bd
KZ
990 if (sortdata)
991 *sortdata = x;
9cfe1b9c 992 }
2d232246
MP
993 break;
994 case COL_DMAX:
642048e4 995 if (lsblk->bytes) {
fed34a1e 996 ul_path_read_string(dev->sysfs, &str, "queue/discard_max_bytes");
d81e32bd
KZ
997 if (sortdata)
998 str2u64(str, sortdata);
642048e4 999 } else {
9cfe1b9c 1000 uint64_t x;
fed34a1e 1001 if (ul_path_read_u64(dev->sysfs, &x, "queue/discard_max_bytes") == 0) {
bb6e822a 1002 str = size_to_human_string(SIZE_SUFFIX_1LETTER, x);
d81e32bd
KZ
1003 if (sortdata)
1004 *sortdata = x;
642048e4 1005 }
9cfe1b9c 1006 }
2d232246
MP
1007 break;
1008 case COL_DZERO:
8a2f175c 1009 if (device_get_discard_granularity(dev) > 0)
fed34a1e 1010 ul_path_read_string(dev->sysfs, &str, "queue/discard_zeroes_data");
bb6e822a
KZ
1011 if (!str)
1012 str = xstrdup("0");
2d232246 1013 break;
2adb1a44 1014 case COL_WSAME:
642048e4 1015 if (lsblk->bytes) {
fed34a1e 1016 ul_path_read_string(dev->sysfs, &str, "queue/write_same_max_bytes");
d81e32bd
KZ
1017 if (sortdata)
1018 str2u64(str, sortdata);
642048e4 1019 } else {
2adb1a44
MB
1020 uint64_t x;
1021
fed34a1e 1022 if (ul_path_read_u64(dev->sysfs, &x, "queue/write_same_max_bytes") == 0) {
bb6e822a 1023 str = size_to_human_string(SIZE_SUFFIX_1LETTER, x);
d81e32bd
KZ
1024 if (sortdata)
1025 *sortdata = x;
642048e4 1026 }
2adb1a44 1027 }
bb6e822a
KZ
1028 if (!str)
1029 str = xstrdup("0");
2adb1a44 1030 break;
f2df4365 1031 case COL_ZONED:
fed34a1e 1032 ul_path_read_string(dev->sysfs, &str, "queue/zoned");
f2df4365 1033 break;
2a4c734b 1034 };
bb6e822a 1035
d81e32bd 1036 return str;
2a4c734b
MB
1037}
1038
b93585e8
KZ
1039/*
1040 * Adds data for all wanted columns about the device to the smartcols table
1041 */
d81e32bd
KZ
1042static void device_to_scols(
1043 struct lsblk_device *dev,
1044 struct lsblk_device *parent,
1045 struct libscols_table *tab,
1046 struct libscols_line *parent_line)
2a4c734b 1047{
40b17508 1048 size_t i;
d81e32bd 1049 struct libscols_line *ln;
f43b8297
KZ
1050 struct lsblk_iter itr;
1051 struct lsblk_device *child = NULL;
0bd05f5e 1052 int link_group = 0;
2a4c734b 1053
fe22543d
KZ
1054
1055 DBG(DEV, ul_debugobj(dev, "add '%s' to scols", dev->name));
1056 ON_DBG(DEV, if (ul_path_isopen_dirfd(dev->sysfs)) ul_debugobj(dev, " %s ---> is open!", dev->name));
ff7992d6 1057
e3bb9bfb
KZ
1058 if (!parent && dev->wholedisk)
1059 parent = dev->wholedisk;
1060
bbe1b314
KZ
1061 /* Do not print device more than once on --list if tree order is not requested */
1062 if (!(lsblk->flags & LSBLK_TREE) && !lsblk->force_tree_order && dev->is_printed)
0bd05f5e
KZ
1063 return;
1064
1065 if (lsblk->merge && list_count_entries(&dev->parents) > 1) {
1066 if (!lsblk_device_is_last_parent(dev, parent))
1067 return;
1068 link_group = 1;
1069 }
1070
1071 ln = scols_table_new_line(tab, link_group ? NULL : parent_line);
d81e32bd 1072 if (!ln)
780ce22c 1073 err(EXIT_FAILURE, _("failed to allocate output line"));
2a4c734b 1074
0bd05f5e
KZ
1075 dev->is_printed = 1;
1076
1077 if (link_group) {
1078 struct lsblk_device *p;
1079 struct libscols_line *gr = parent_line;
1080
1081 /* Merge all my parents to the one group */
fe22543d 1082 DBG(DEV, ul_debugobj(dev, " grouping parents [--merge]"));
0bd05f5e
KZ
1083 lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD);
1084 while (lsblk_device_next_parent(dev, &itr, &p) == 0) {
fe22543d
KZ
1085 if (!p->scols_line) {
1086 DBG(DEV, ul_debugobj(dev, " *** ignore '%s' no scols line yet", p->name));
0bd05f5e 1087 continue;
fe22543d
KZ
1088 }
1089 DBG(DEV, ul_debugobj(dev, " group '%s'", p->name));
1090 scols_table_group_lines(tab, p->scols_line, gr, 0);
0bd05f5e
KZ
1091 }
1092
1093 /* Link the group -- this makes group->child connection */
fe22543d 1094 DBG(DEV, ul_debugobj(dev, " linking the group [--merge]"));
0bd05f5e
KZ
1095 scols_line_link_group(ln, gr, 0);
1096 }
1097
d81e32bd
KZ
1098 /* read column specific data and set it to smartcols table line */
1099 for (i = 0; i < ncolumns; i++) {
1100 char *data;
1101 int id = get_column_id(i);
1102
1103 if (lsblk->sort_id != id)
1104 data = device_get_data(dev, parent, id, NULL);
1105 else {
1106 uint64_t sortdata = (uint64_t) -1;
1107
1108 data = device_get_data(dev, parent, id, &sortdata);
1109 if (data && sortdata != (uint64_t) -1)
1110 set_sortdata_u64(ln, i, sortdata);
1111 }
fe22543d 1112 DBG(DEV, ul_debugobj(dev, " refer data[%zu]=\"%s\"", i, data));
d81e32bd
KZ
1113 if (data && scols_line_refer_data(ln, i, data))
1114 err(EXIT_FAILURE, _("failed to add output data"));
1115 }
f43b8297 1116
0bd05f5e
KZ
1117 dev->scols_line = ln;
1118
ff7992d6
KZ
1119 if (dev->npartitions == 0)
1120 /* For partitions we often read from parental whole-disk sysfs,
1121 * otherwise we can close */
1122 ul_path_close_dirfd(dev->sysfs);
1123
0bd05f5e 1124 lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD);
fe22543d
KZ
1125 while (lsblk_device_next_child(dev, &itr, &child) == 0) {
1126 DBG(DEV, ul_debugobj(dev, "%s -> continue to child", dev->name));
d81e32bd 1127 device_to_scols(child, dev, tab, ln);
fe22543d
KZ
1128 DBG(DEV, ul_debugobj(dev, "%s <- child done", dev->name));
1129 }
ff7992d6
KZ
1130
1131 /* Let's be careful with number of open files */
1132 ul_path_close_dirfd(dev->sysfs);
f43b8297
KZ
1133}
1134
b93585e8
KZ
1135/*
1136 * Walks on tree and adds one line for each device to the smartcols table
1137 */
f43b8297
KZ
1138static void devtree_to_scols(struct lsblk_devtree *tr, struct libscols_table *tab)
1139{
1140 struct lsblk_iter itr;
1141 struct lsblk_device *dev = NULL;
1142
1143 lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD);
1144
1145 while (lsblk_devtree_next_root(tr, &itr, &dev) == 0)
d81e32bd 1146 device_to_scols(dev, NULL, tab, NULL);
2a4c734b
MB
1147}
1148
b93585e8
KZ
1149/*
1150 * Reads very basic information about the device from sysfs into the device struct
1151 */
b158bd29 1152static int initialize_device(struct lsblk_device *dev,
fed34a1e 1153 struct lsblk_device *wholedisk,
09a71aa1 1154 const char *name)
2a4c734b 1155{
766ab9b0 1156 dev_t devno;
2a4c734b 1157
b158bd29
KZ
1158 DBG(DEV, ul_debugobj(dev, "initialize %s [wholedisk=%p %s]",
1159 name, wholedisk, wholedisk ? wholedisk->name : ""));
7e786eca 1160
fed34a1e 1161 dev->name = xstrdup(name);
10501add
KZ
1162
1163 if (wholedisk) {
1164 dev->wholedisk = wholedisk;
1165 lsblk_ref_device(wholedisk);
1166 }
2a4c734b 1167
fed34a1e
KZ
1168 dev->filename = get_device_path(dev);
1169 if (!dev->filename) {
3bf2a36c 1170 DBG(DEV, ul_debugobj(dev, "%s: failed to get device path", dev->name));
270e66ec
MB
1171 return -1;
1172 }
3bf2a36c 1173 DBG(DEV, ul_debugobj(dev, "%s: filename=%s", dev->name, dev->filename));
2a4c734b 1174
fed34a1e 1175 devno = __sysfs_devname_to_devno(lsblk->sysroot, dev->name, wholedisk ? wholedisk->name : NULL);
270e66ec 1176 if (!devno) {
3bf2a36c 1177 DBG(DEV, ul_debugobj(dev, "%s: unknown device name", dev->name));
270e66ec
MB
1178 return -1;
1179 }
2a4c734b 1180
0e086f82
KZ
1181 dev->sysfs = ul_new_sysfs_path(devno, wholedisk ? wholedisk->sysfs : NULL, lsblk->sysroot);
1182 if (!dev->sysfs) {
1183 DBG(DEV, ul_debugobj(dev, "%s: failed to initialize sysfs handler", dev->name));
1184 return -1;
270e66ec 1185 }
766ab9b0 1186
fed34a1e
KZ
1187 dev->maj = major(devno);
1188 dev->min = minor(devno);
1189 dev->size = 0;
2a4c734b 1190
fed34a1e
KZ
1191 if (ul_path_read_u64(dev->sysfs, &dev->size, "size") == 0) /* in sectors */
1192 dev->size <<= 9; /* in bytes */
90e9fcda 1193
2a4c734b 1194 /* Ignore devices of zero size */
fed34a1e 1195 if (!lsblk->all_devices && dev->size == 0) {
3bf2a36c 1196 DBG(DEV, ul_debugobj(dev, "zero size device -- ignore"));
2a4c734b 1197 return -1;
7e786eca 1198 }
fed34a1e
KZ
1199 if (is_dm(dev->name)) {
1200 ul_path_read_string(dev->sysfs, &dev->dm_name, "dm/name");
1201 if (!dev->dm_name) {
3bf2a36c 1202 DBG(DEV, ul_debugobj(dev, "%s: failed to get dm name", dev->name));
270e66ec
MB
1203 return -1;
1204 }
766ab9b0 1205 }
2a4c734b 1206
fed34a1e
KZ
1207 dev->npartitions = sysfs_blkdev_count_partitions(dev->sysfs, dev->name);
1208 dev->nholders = ul_path_count_dirents(dev->sysfs, "holders");
1209 dev->nslaves = ul_path_count_dirents(dev->sysfs, "slaves");
2a4c734b 1210
3bf2a36c 1211 DBG(DEV, ul_debugobj(dev, "%s: npartitions=%d, nholders=%d, nslaves=%d",
fed34a1e 1212 dev->name, dev->npartitions, dev->nholders, dev->nslaves));
7e786eca 1213
28ffc2b7 1214 /* ignore non-SCSI devices */
fed34a1e 1215 if (lsblk->scsi && sysfs_blkdev_scsi_get_hctl(dev->sysfs, NULL, NULL, NULL, NULL)) {
3bf2a36c 1216 DBG(DEV, ul_debugobj(dev, "non-scsi device -- ignore"));
28ffc2b7 1217 return -1;
7e786eca 1218 }
28ffc2b7 1219
3bf2a36c 1220 DBG(DEV, ul_debugobj(dev, "%s: context successfully initialized", dev->name));
2a4c734b
MB
1221 return 0;
1222}
1223
4ca3c472 1224static struct lsblk_device *devtree_get_device_or_new(struct lsblk_devtree *tr,
583b6489
KZ
1225 struct lsblk_device *disk,
1226 const char *name)
1227{
1228 struct lsblk_device *dev = lsblk_devtree_get_device(tr, name);
1229
1230 if (!dev) {
10501add 1231 dev = lsblk_new_device();
583b6489
KZ
1232 if (!dev)
1233 err(EXIT_FAILURE, _("failed to allocate device"));
1234
b158bd29 1235 if (initialize_device(dev, disk, name) != 0) {
583b6489
KZ
1236 lsblk_unref_device(dev);
1237 return NULL;
1238 }
1239 lsblk_devtree_add_device(tr, dev);
1240 lsblk_unref_device(dev); /* keep it referenced by devtree only */
1241 } else
1242 DBG(DEV, ul_debugobj(dev, "%s: already processed", name));
1243
1244 return dev;
1245}
a0a76f42 1246
4ca3c472
KZ
1247static int process_dependencies(
1248 struct lsblk_devtree *tr,
1249 struct lsblk_device *dev,
1250 int do_partitions);
09a71aa1 1251
2a4c734b 1252/*
4ca3c472 1253 * Read devices from whole-disk device into tree
2a4c734b 1254 */
cf3c1bc7 1255static int process_partitions(struct lsblk_devtree *tr, struct lsblk_device *disk)
2a4c734b
MB
1256{
1257 DIR *dir;
1258 struct dirent *d;
2a4c734b 1259
cf3c1bc7 1260 assert(disk);
f31505fe 1261
09a71aa1
PR
1262 /*
1263 * Do not process further if there are no partitions for
1264 * this device or the device itself is a partition.
1265 */
10501add 1266 if (!disk->npartitions || device_is_partition(disk))
cf3c1bc7 1267 return -EINVAL;
2a4c734b 1268
cf3c1bc7 1269 DBG(DEV, ul_debugobj(disk, "%s: probe whole-disk for partitions", disk->name));
7e786eca 1270
cf3c1bc7 1271 dir = ul_path_opendir(disk->sysfs, NULL);
2a4c734b
MB
1272 if (!dir)
1273 err(EXIT_FAILURE, _("failed to open device directory in sysfs"));
1274
1275 while ((d = xreaddir(dir))) {
4ca3c472 1276 struct lsblk_device *part;
2a4c734b 1277
cf3c1bc7 1278 if (!(sysfs_blkdev_is_partition_dirent(dir, d, disk->name)))
270e66ec 1279 continue;
09a71aa1 1280
cf3c1bc7 1281 DBG(DEV, ul_debugobj(disk, " checking %s", d->d_name));
7e786eca 1282
cf3c1bc7 1283 part = devtree_get_device_or_new(tr, disk, d->d_name);
4ca3c472
KZ
1284 if (!part)
1285 continue;
1286
cf3c1bc7 1287 if (lsblk_device_new_dependence(disk, part) == 0)
4ca3c472 1288 process_dependencies(tr, part, 0);
ff7992d6
KZ
1289
1290 ul_path_close_dirfd(part->sysfs);
2a4c734b 1291 }
09a71aa1 1292
ff7992d6
KZ
1293 /* For partitions we need parental (whole-disk) sysfs directory pretty
1294 * often, so close it now when all is done */
1295 ul_path_close_dirfd(disk->sysfs);
1296
cf3c1bc7 1297 DBG(DEV, ul_debugobj(disk, "probe whole-disk for partitions -- done"));
2a4c734b 1298 closedir(dir);
4ca3c472 1299 return 0;
09a71aa1
PR
1300}
1301
4ca3c472 1302static char *get_wholedisk_from_partition_dirent(DIR *dir, struct dirent *d, char *buf, size_t bufsz)
09a71aa1 1303{
09a71aa1
PR
1304 char *p;
1305 int len;
1306
4ca3c472 1307 if ((len = readlinkat(dirfd(dir), d->d_name, buf, bufsz - 1)) < 0)
09a71aa1 1308 return 0;
540d506f 1309
4ca3c472 1310 buf[len] = '\0';
09a71aa1
PR
1311
1312 /* The path ends with ".../<device>/<partition>" */
4ca3c472 1313 p = strrchr(buf, '/');
540d506f 1314 if (!p)
4ca3c472 1315 return NULL;
540d506f
KZ
1316 *p = '\0';
1317
4ca3c472 1318 p = strrchr(buf, '/');
540d506f 1319 if (!p)
4ca3c472 1320 return NULL;
540d506f 1321 p++;
2a4c734b 1322
4ca3c472 1323 return p;
09a71aa1
PR
1324}
1325
1326/*
b93585e8 1327 * Reads slaves/holders and partitions for specified device into device tree
09a71aa1 1328 */
4ca3c472
KZ
1329static int process_dependencies(
1330 struct lsblk_devtree *tr,
1331 struct lsblk_device *dev,
1332 int do_partitions)
09a71aa1
PR
1333{
1334 DIR *dir;
1335 struct dirent *d;
540d506f 1336 const char *depname;
09a71aa1 1337
fed34a1e 1338 assert(dev);
09a71aa1 1339
57a06133
KZ
1340 if (lsblk->nodeps)
1341 return 0;
1342
4ca3c472
KZ
1343 /* read all or specified partition */
1344 if (do_partitions && dev->npartitions)
1345 process_partitions(tr, dev);
1346
4ca3c472 1347 DBG(DEV, ul_debugobj(dev, "%s: reading dependencies", dev->name));
7e786eca 1348
4ca3c472
KZ
1349 if (!(lsblk->inverse ? dev->nslaves : dev->nholders)) {
1350 DBG(DEV, ul_debugobj(dev, " ignore (no slaves/holders)"));
09a71aa1 1351 return 0;
4ca3c472 1352 }
09a71aa1 1353
540d506f 1354 depname = lsblk->inverse ? "slaves" : "holders";
fed34a1e 1355 dir = ul_path_opendir(dev->sysfs, depname);
4ca3c472
KZ
1356 if (!dir) {
1357 DBG(DEV, ul_debugobj(dev, " ignore (no slaves/holders directory)"));
2a4c734b 1358 return 0;
4ca3c472 1359 }
ff7992d6 1360 ul_path_close_dirfd(dev->sysfs);
2a4c734b 1361
4ca3c472 1362 DBG(DEV, ul_debugobj(dev, " %s: checking for '%s' dependence", dev->name, depname));
7e786eca 1363
2a4c734b 1364 while ((d = xreaddir(dir))) {
ff7992d6
KZ
1365 struct lsblk_device *dep = NULL;
1366 struct lsblk_device *disk = NULL;
4ca3c472 1367
09a71aa1 1368 /* Is the dependency a partition? */
f153614c 1369 if (sysfs_blkdev_is_partition_dirent(dir, d, NULL)) {
4ca3c472
KZ
1370
1371 char buf[PATH_MAX];
1372 char *diskname;
4ca3c472
KZ
1373
1374 DBG(DEV, ul_debugobj(dev, " %s: dependence is partition", d->d_name));
1375
1376 diskname = get_wholedisk_from_partition_dirent(dir, d, buf, sizeof(buf));
1377 if (diskname)
0e086f82 1378 disk = devtree_get_device_or_new(tr, NULL, diskname);
4ca3c472
KZ
1379 if (!disk) {
1380 DBG(DEV, ul_debugobj(dev, " ignore no wholedisk ???"));
ff7992d6 1381 goto next;
7e786eca 1382 }
4ca3c472 1383
0e086f82 1384 dep = devtree_get_device_or_new(tr, disk, d->d_name);
4ca3c472 1385 if (!dep)
ff7992d6 1386 goto next;
4ca3c472
KZ
1387
1388 if (lsblk_device_new_dependence(dev, dep) == 0)
1389 process_dependencies(tr, dep, 1);
1390
1391 if (lsblk->inverse
1392 && lsblk_device_new_dependence(dep, disk) == 0)
1393 process_dependencies(tr, disk, 0);
270e66ec 1394 }
09a71aa1 1395 /* The dependency is a whole device. */
4ca3c472
KZ
1396 else {
1397 DBG(DEV, ul_debugobj(dev, " %s: %s: dependence is whole-disk",
fed34a1e 1398 dev->name, d->d_name));
4ca3c472 1399
0e086f82 1400 dep = devtree_get_device_or_new(tr, NULL, d->d_name);
4ca3c472 1401 if (!dep)
ff7992d6 1402 goto next;
4ca3c472
KZ
1403
1404 if (lsblk_device_new_dependence(dev, dep) == 0)
1405 /* For inverse tree we don't want to show partitions
1406 * if the dependence is on whole-disk */
1407 process_dependencies(tr, dep, lsblk->inverse ? 0 : 1);
7e786eca 1408 }
ff7992d6
KZ
1409next:
1410 if (dep && dep->sysfs)
1411 ul_path_close_dirfd(dep->sysfs);
1412 if (disk && disk->sysfs)
1413 ul_path_close_dirfd(disk->sysfs);
2a4c734b
MB
1414 }
1415 closedir(dir);
1416
3bf2a36c 1417 DBG(DEV, ul_debugobj(dev, "%s: checking for '%s' -- done", dev->name, depname));
2a4c734b
MB
1418 return 0;
1419}
1420
b93585e8
KZ
1421/*
1422 * Defines the device as root node in the device tree and walks on all dependencies of the device.
1423 */
e7b5353e 1424static int __process_one_device(struct lsblk_devtree *tr, char *devname, dev_t devno)
2a4c734b 1425{
291606fa 1426 struct lsblk_device *dev = NULL;
ff7992d6 1427 struct lsblk_device *disk = NULL;
1539750f 1428 char buf[PATH_MAX + 1], *name = NULL, *diskname = NULL;
1539750f 1429 int real_part = 0, rc = -EINVAL;
2a4c734b 1430
106e6960 1431 if (devno == 0 && devname) {
e7b5353e
KZ
1432 struct stat st;
1433
1434 DBG(DEV, ul_debug("%s: reading alone device", devname));
1435
1436 if (stat(devname, &st) || !S_ISBLK(st.st_mode)) {
1437 warnx(_("%s: not a block device"), devname);
1438 goto leave;
1439 }
1440 devno = st.st_rdev;
106e6960 1441 } else if (devno) {
e7b5353e 1442 DBG(DEV, ul_debug("%d:%d: reading alone device", major(devno), minor(devno)));
106e6960
KZ
1443 } else {
1444 assert(devno || devname);
1445 return -EINVAL;
1446 }
da30cb2a 1447
f153614c
KZ
1448 /* TODO: sysfs_devno_to_devname() internally initializes path_cxt, it
1449 * would be better to use ul_new_sysfs_path() + sysfs_blkdev_get_name()
b158bd29 1450 * and reuse path_cxt for initialize_device()
f153614c 1451 */
e7b5353e 1452 name = sysfs_devno_to_devname(devno, buf, sizeof(buf));
f153614c 1453 if (!name) {
e7b5353e
KZ
1454 if (devname)
1455 warn(_("%s: failed to get sysfs name"), devname);
1539750f 1456 goto leave;
2a4c734b 1457 }
3273ce07 1458 name = xstrdup(name);
da30cb2a
PR
1459
1460 if (!strncmp(name, "dm-", 3)) {
1461 /* dm mapping is never a real partition! */
1462 real_part = 0;
1463 } else {
ff7992d6
KZ
1464 dev_t diskno = 0;
1465
1466 if (blkid_devno_to_wholedisk(devno, buf, sizeof(buf), &diskno)) {
e7b5353e 1467 warn(_("%s: failed to get whole-disk device number"), name);
1539750f 1468 goto leave;
da30cb2a
PR
1469 }
1470 diskname = buf;
ff7992d6 1471 real_part = devno != diskno;
da30cb2a
PR
1472 }
1473
1474 if (!real_part) {
2a4c734b 1475 /*
09a71aa1 1476 * Device is not a partition.
2a4c734b 1477 */
4ca3c472
KZ
1478 DBG(DEV, ul_debug(" non-partition"));
1479
0e086f82 1480 dev = devtree_get_device_or_new(tr, NULL, name);
291606fa 1481 if (!dev)
270e66ec 1482 goto leave;
291606fa 1483
4ca3c472
KZ
1484 lsblk_devtree_add_root(tr, dev);
1485 process_dependencies(tr, dev, !lsblk->inverse);
270e66ec 1486 } else {
2a4c734b 1487 /*
291606fa 1488 * Partition, read sysfs name of the disk device
2a4c734b 1489 */
4ca3c472
KZ
1490 DBG(DEV, ul_debug(" partition"));
1491
0e086f82
KZ
1492 disk = devtree_get_device_or_new(tr, NULL, diskname);
1493 if (!disk)
270e66ec 1494 goto leave;
291606fa 1495
0e086f82 1496 dev = devtree_get_device_or_new(tr, disk, name);
291606fa 1497 if (!dev)
270e66ec 1498 goto leave;
09a71aa1 1499
4ca3c472
KZ
1500 lsblk_devtree_add_root(tr, dev);
1501 process_dependencies(tr, dev, 1);
291606fa 1502
4ca3c472 1503 if (lsblk->inverse
0e086f82
KZ
1504 && lsblk_device_new_dependence(dev, disk) == 0)
1505 process_dependencies(tr, disk, 0);
ff7992d6
KZ
1506 else
1507 ul_path_close_dirfd(disk->sysfs);
2a4c734b
MB
1508 }
1509
1539750f 1510 rc = 0;
270e66ec 1511leave:
ff7992d6
KZ
1512 if (dev && dev->sysfs)
1513 ul_path_close_dirfd(dev->sysfs);
1514 if (disk && disk->sysfs)
1515 ul_path_close_dirfd(disk->sysfs);
da30cb2a 1516 free(name);
1539750f 1517 return rc;
2a4c734b
MB
1518}
1519
e7b5353e
KZ
1520static int process_one_device(struct lsblk_devtree *tr, char *devname)
1521{
106e6960 1522 assert(devname);
e7b5353e
KZ
1523 return __process_one_device(tr, devname, 0);
1524}
1525
1526/*
1527 * The /sys/block contains only root devices, and no partitions. It seems more
1528 * simple to scan /sys/dev/block where are all devices without exceptions to get
1529 * top-level devices for the reverse tree.
1530 */
1531static int process_all_devices_inverse(struct lsblk_devtree *tr)
e15c9e3d
KZ
1532{
1533 DIR *dir;
1534 struct dirent *d;
e7b5353e
KZ
1535 struct path_cxt *pc = ul_new_path(_PATH_SYS_DEVBLOCK);
1536
1537 assert(lsblk->inverse);
e15c9e3d
KZ
1538
1539 if (!pc)
1540 err(EXIT_FAILURE, _("failed to allocate /sys handler"));
1541
1542 ul_path_set_prefix(pc, lsblk->sysroot);
e7b5353e
KZ
1543 dir = ul_path_opendir(pc, NULL);
1544 if (!dir)
1545 goto done;
1546
1547 DBG(DEV, ul_debug("iterate on " _PATH_SYS_DEVBLOCK));
1548
1549 while ((d = xreaddir(dir))) {
1550 dev_t devno;
1551 int maj, min;
e15c9e3d 1552
e7b5353e
KZ
1553 DBG(DEV, ul_debug(" %s dentry", d->d_name));
1554
1555 if (sscanf(d->d_name, "%d:%d", &maj, &min) != 2)
1556 continue;
1557 devno = makedev(maj, min);
1558
1559 if (is_maj_excluded(maj) || !is_maj_included(maj))
1560 continue;
1561 if (ul_path_countf_dirents(pc, "%s/holders", d->d_name) != 0)
1562 continue;
1563 if (sysfs_devno_count_partitions(devno) != 0)
1564 continue;
1565 __process_one_device(tr, NULL, devno);
1566 }
1567
1568 closedir(dir);
1569done:
1570 ul_unref_path(pc);
1571 DBG(DEV, ul_debug("iterate on " _PATH_SYS_DEVBLOCK " -- done"));
1572 return 0;
1573}
1574
b93585e8
KZ
1575/*
1576 * Reads root nodes (devices) from /sys/block into devices tree
1577 */
e7b5353e
KZ
1578static int process_all_devices(struct lsblk_devtree *tr)
1579{
1580 DIR *dir;
1581 struct dirent *d;
1582 struct path_cxt *pc;
1583
1584 assert(lsblk->inverse == 0);
1585
1586 pc = ul_new_path(_PATH_SYS_BLOCK);
1587 if (!pc)
1588 err(EXIT_FAILURE, _("failed to allocate /sys handler"));
1589
1590 ul_path_set_prefix(pc, lsblk->sysroot);
e15c9e3d
KZ
1591 dir = ul_path_opendir(pc, NULL);
1592 if (!dir)
1593 goto done;
1594
e7b5353e 1595 DBG(DEV, ul_debug("iterate on " _PATH_SYS_BLOCK));
e15c9e3d
KZ
1596
1597 while ((d = xreaddir(dir))) {
ff7992d6 1598 struct lsblk_device *dev = NULL;
e15c9e3d
KZ
1599
1600 DBG(DEV, ul_debug(" %s dentry", d->d_name));
e15c9e3d
KZ
1601 dev = devtree_get_device_or_new(tr, NULL, d->d_name);
1602 if (!dev)
ff7992d6 1603 goto next;
e15c9e3d
KZ
1604
1605 /* remove unwanted devices */
1606 if (is_maj_excluded(dev->maj) || !is_maj_included(dev->maj)) {
1607 DBG(DEV, ul_debug(" %s: ignore (by filter)", d->d_name));
1608 lsblk_devtree_remove_device(tr, dev);
f6f8a671 1609 dev = NULL;
ff7992d6 1610 goto next;
e15c9e3d
KZ
1611 }
1612
e7b5353e
KZ
1613 if (dev->nslaves) {
1614 DBG(DEV, ul_debug(" %s: ignore (in-middle)", d->d_name));
ff7992d6 1615 goto next;
e15c9e3d 1616 }
0bd05f5e 1617
e7b5353e
KZ
1618 lsblk_devtree_add_root(tr, dev);
1619 process_dependencies(tr, dev, 1);
ff7992d6
KZ
1620next:
1621 /* Let's be careful with number of open files */
1622 if (dev && dev->sysfs)
1623 ul_path_close_dirfd(dev->sysfs);
e15c9e3d
KZ
1624 }
1625
1626 closedir(dir);
1627done:
1628 ul_unref_path(pc);
1629 DBG(DEV, ul_debug("iterate on " _PATH_SYS_BLOCK " -- done"));
1630 return 0;
1631}
1632
b93585e8
KZ
1633/*
1634 * Parses major numbers as specified on lsblk command line
1635 */
2dc03af7 1636static void parse_excludes(const char *str0)
2a4c734b 1637{
2dc03af7
KZ
1638 const char *str = str0;
1639
2a4c734b
MB
1640 while (str && *str) {
1641 char *end = NULL;
ed34643c 1642 unsigned long n;
2a4c734b
MB
1643
1644 errno = 0;
1645 n = strtoul(str, &end, 10);
1646
2dc03af7
KZ
1647 if (end == str || (end && *end && *end != ','))
1648 errx(EXIT_FAILURE, _("failed to parse list '%s'"), str0);
1649 if (errno != 0 && (n == ULONG_MAX || n == 0))
1650 err(EXIT_FAILURE, _("failed to parse list '%s'"), str0);
2a4c734b
MB
1651 excludes[nexcludes++] = n;
1652
1653 if (nexcludes == ARRAY_SIZE(excludes))
f14f02f5 1654 /* TRANSLATORS: The standard value for %d is 256. */
2a4c734b
MB
1655 errx(EXIT_FAILURE, _("the list of excluded devices is "
1656 "too large (limit is %d devices)"),
1657 (int)ARRAY_SIZE(excludes));
2dc03af7 1658
2a4c734b
MB
1659 str = end && *end ? end + 1 : NULL;
1660 }
1661}
1662
b93585e8
KZ
1663/*
1664 * Parses major numbers as specified on lsblk command line
1665 * (TODO: what about refactor and merge parse_excludes() and parse_includes().)
1666 */
2dc03af7 1667static void parse_includes(const char *str0)
2ef4e8ba 1668{
2dc03af7
KZ
1669 const char *str = str0;
1670
2ef4e8ba
KZ
1671 while (str && *str) {
1672 char *end = NULL;
1673 unsigned long n;
1674
1675 errno = 0;
1676 n = strtoul(str, &end, 10);
1677
2dc03af7
KZ
1678 if (end == str || (end && *end && *end != ','))
1679 errx(EXIT_FAILURE, _("failed to parse list '%s'"), str0);
1680 if (errno != 0 && (n == ULONG_MAX || n == 0))
1681 err(EXIT_FAILURE, _("failed to parse list '%s'"), str0);
2ef4e8ba
KZ
1682 includes[nincludes++] = n;
1683
1684 if (nincludes == ARRAY_SIZE(includes))
1685 /* TRANSLATORS: The standard value for %d is 256. */
1686 errx(EXIT_FAILURE, _("the list of included devices is "
1687 "too large (limit is %d devices)"),
1688 (int)ARRAY_SIZE(includes));
1689 str = end && *end ? end + 1 : NULL;
1690 }
1691}
1692
642048e4
KZ
1693/*
1694 * see set_sortdata_u64() and columns initialization in main()
1695 */
1696static int cmp_u64_cells(struct libscols_cell *a,
1697 struct libscols_cell *b,
1698 __attribute__((__unused__)) void *data)
1699{
1700 uint64_t *adata = (uint64_t *) scols_cell_get_userdata(a),
1701 *bdata = (uint64_t *) scols_cell_get_userdata(b);
1702
1703 if (adata == NULL && bdata == NULL)
1704 return 0;
1705 if (adata == NULL)
1706 return -1;
1707 if (bdata == NULL)
1708 return 1;
1709 return *adata == *bdata ? 0 : *adata >= *bdata ? 1 : -1;
1710}
1711
dc4662f0
KZ
1712static void device_set_dedupkey(
1713 struct lsblk_device *dev,
1714 struct lsblk_device *parent,
1715 int id)
1716{
1717 struct lsblk_iter itr;
1718 struct lsblk_device *child = NULL;
1719
1720 dev->dedupkey = device_get_data(dev, parent, id, NULL);
1721 if (dev->dedupkey)
1722 DBG(DEV, ul_debugobj(dev, "%s: de-duplication key: %s", dev->name, dev->dedupkey));
1723
1724 if (dev->npartitions == 0)
1725 /* For partitions we often read from parental whole-disk sysfs,
1726 * otherwise we can close */
1727 ul_path_close_dirfd(dev->sysfs);
1728
1729 lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD);
1730
1731 while (lsblk_device_next_child(dev, &itr, &child) == 0)
1732 device_set_dedupkey(child, dev, id);
1733
1734 /* Let's be careful with number of open files */
1735 ul_path_close_dirfd(dev->sysfs);
1736}
1737
1738static void devtree_set_dedupkeys(struct lsblk_devtree *tr, int id)
1739{
1740 struct lsblk_iter itr;
1741 struct lsblk_device *dev = NULL;
1742
1743 lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD);
1744
1745 while (lsblk_devtree_next_root(tr, &itr, &dev) == 0)
1746 device_set_dedupkey(dev, NULL, id);
1747}
1748
86be6a32 1749static void __attribute__((__noreturn__)) usage(void)
2a4c734b 1750{
86be6a32 1751 FILE *out = stdout;
507d39c2 1752 size_t i;
2a4c734b 1753
39b232c1
SK
1754 fputs(USAGE_HEADER, out);
1755 fprintf(out, _(" %s [options] [<device> ...]\n"), program_invocation_short_name);
451dbcfa
BS
1756
1757 fputs(USAGE_SEPARATOR, out);
1758 fputs(_("List information about block devices.\n"), out);
1759
39b232c1 1760 fputs(USAGE_OPTIONS, out);
0bd05f5e
KZ
1761 fputs(_(" -D, --discard print discard capabilities\n"), out);
1762 fputs(_(" -E, --dedup <column> de-duplicate output by <column>\n"), out);
1763 fputs(_(" -I, --include <list> show only devices with specified major numbers\n"), out);
1764 fputs(_(" -J, --json use JSON output format\n"), out);
1765 fputs(_(" -O, --output-all output all columns\n"), out);
1766 fputs(_(" -P, --pairs use key=\"value\" output format\n"), out);
1767 fputs(_(" -S, --scsi output info about SCSI devices\n"), out);
b9c088f2 1768 fputs(_(" -T, --tree[=<column>] use tree format output\n"), out);
39b232c1
SK
1769 fputs(_(" -a, --all print all devices\n"), out);
1770 fputs(_(" -b, --bytes print SIZE in bytes rather than in human readable format\n"), out);
1771 fputs(_(" -d, --nodeps don't print slaves or holders\n"), out);
39b232c1 1772 fputs(_(" -e, --exclude <list> exclude devices by major number (default: RAM disks)\n"), out);
39b232c1 1773 fputs(_(" -f, --fs output info about filesystems\n"), out);
39b232c1 1774 fputs(_(" -i, --ascii use ascii characters only\n"), out);
39b232c1 1775 fputs(_(" -l, --list use list format output\n"), out);
0bd05f5e 1776 fputs(_(" -M, --merge group parents of sub-trees (usable for RAIDs, Multi-path)\n"), out);
3e59c481 1777 fputs(_(" -m, --perms output info about permissions\n"), out);
39b232c1
SK
1778 fputs(_(" -n, --noheadings don't print headings\n"), out);
1779 fputs(_(" -o, --output <list> output columns\n"), out);
ef75bc88 1780 fputs(_(" -p, --paths print complete device path\n"), out);
39b232c1
SK
1781 fputs(_(" -r, --raw use raw output format\n"), out);
1782 fputs(_(" -s, --inverse inverse dependencies\n"), out);
3e59c481 1783 fputs(_(" -t, --topology output info about topology\n"), out);
0bd05f5e 1784 fputs(_(" -z, --zoned print zone model\n"), out);
b5af3ee8 1785 fputs(_(" -x, --sort <column> sort output by <column>\n"), out);
498d1486 1786 fputs(_(" --sysroot <dir> use specified directory as system root\n"), out);
39b232c1 1787 fputs(USAGE_SEPARATOR, out);
f45f3ec3 1788 printf(USAGE_HELP_OPTIONS(22));
2a4c734b 1789
c3a4cfc5 1790 fprintf(out, USAGE_COLUMNS);
2a4c734b 1791
03df0d11 1792 for (i = 0; i < ARRAY_SIZE(infos); i++)
57912387 1793 fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help));
2a4c734b 1794
f45f3ec3 1795 printf(USAGE_MAN_TAIL("lsblk(8)"));
2a4c734b 1796
86be6a32 1797 exit(EXIT_SUCCESS);
2a4c734b
MB
1798}
1799
f65b3bb6
SK
1800static void check_sysdevblock(void)
1801{
1802 if (access(_PATH_SYS_DEVBLOCK, R_OK) != 0)
1803 err(EXIT_FAILURE, _("failed to access sysfs directory: %s"),
1804 _PATH_SYS_DEVBLOCK);
1805}
1806
2a4c734b
MB
1807int main(int argc, char *argv[])
1808{
dc4662f0
KZ
1809 struct lsblk _ls = {
1810 .sort_id = -1,
1811 .dedup_id = -1,
b9c088f2
KZ
1812 .flags = LSBLK_TREE,
1813 .tree_id = COL_NAME
dc4662f0 1814 };
57502dd3 1815 struct lsblk_devtree *tr = NULL;
40b17508 1816 int c, status = EXIT_FAILURE;
43bca827 1817 char *outarg = NULL;
40b17508 1818 size_t i;
b9c088f2 1819 int force_tree = 0, has_tree_col = 0;
2a4c734b 1820
ffc27623
KZ
1821 enum {
1822 OPT_SYSROOT = CHAR_MAX + 1
1823 };
1824
6c7d5ae9 1825 static const struct option longopts[] = {
87918040
SK
1826 { "all", no_argument, NULL, 'a' },
1827 { "bytes", no_argument, NULL, 'b' },
1828 { "nodeps", no_argument, NULL, 'd' },
1829 { "discard", no_argument, NULL, 'D' },
0bd05f5e 1830 { "dedup", required_argument, NULL, 'E' },
f2df4365 1831 { "zoned", no_argument, NULL, 'z' },
87918040
SK
1832 { "help", no_argument, NULL, 'h' },
1833 { "json", no_argument, NULL, 'J' },
1834 { "output", required_argument, NULL, 'o' },
1835 { "output-all", no_argument, NULL, 'O' },
0bd05f5e 1836 { "merge", no_argument, NULL, 'M' },
87918040
SK
1837 { "perms", no_argument, NULL, 'm' },
1838 { "noheadings", no_argument, NULL, 'n' },
1839 { "list", no_argument, NULL, 'l' },
1840 { "ascii", no_argument, NULL, 'i' },
1841 { "raw", no_argument, NULL, 'r' },
1842 { "inverse", no_argument, NULL, 's' },
1843 { "fs", no_argument, NULL, 'f' },
1844 { "exclude", required_argument, NULL, 'e' },
1845 { "include", required_argument, NULL, 'I' },
1846 { "topology", no_argument, NULL, 't' },
1847 { "paths", no_argument, NULL, 'p' },
1848 { "pairs", no_argument, NULL, 'P' },
1849 { "scsi", no_argument, NULL, 'S' },
1850 { "sort", required_argument, NULL, 'x' },
ffc27623 1851 { "sysroot", required_argument, NULL, OPT_SYSROOT },
b9c088f2 1852 { "tree", optional_argument, NULL, 'T' },
87918040
SK
1853 { "version", no_argument, NULL, 'V' },
1854 { NULL, 0, NULL, 0 },
2a4c734b
MB
1855 };
1856
a7349ee3 1857 static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
1b4d2a4a 1858 { 'D','O' },
98f2dc7a 1859 { 'I','e' },
4a102a48 1860 { 'J', 'P', 'r' },
1b4d2a4a
MY
1861 { 'O','S' },
1862 { 'O','f' },
1863 { 'O','m' },
a44cd891 1864 { 'O','o' },
1b4d2a4a 1865 { 'O','t' },
1d9e35cc 1866 { 'P','T', 'l','r' },
98f2dc7a
KZ
1867 { 0 }
1868 };
1869 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
1870
2a4c734b
MB
1871 setlocale(LC_ALL, "");
1872 bindtextdomain(PACKAGE, LOCALEDIR);
1873 textdomain(PACKAGE);
2c308875 1874 close_stdout_atexit();
2a4c734b
MB
1875
1876 lsblk = &_ls;
2a4c734b 1877
7e786eca
KZ
1878 lsblk_init_debug();
1879
98f2dc7a 1880 while((c = getopt_long(argc, argv,
b9c088f2 1881 "abdDzE:e:fhJlnMmo:OpPiI:rstVST:x:", longopts, NULL)) != -1) {
98f2dc7a
KZ
1882
1883 err_exclusive_options(c, longopts, excl, excl_st);
1884
2a4c734b
MB
1885 switch(c) {
1886 case 'a':
1887 lsblk->all_devices = 1;
1888 break;
1889 case 'b':
1890 lsblk->bytes = 1;
1891 break;
f31505fe
KZ
1892 case 'd':
1893 lsblk->nodeps = 1;
1894 break;
2d232246 1895 case 'D':
be685b98
KZ
1896 add_uniq_column(COL_NAME);
1897 add_uniq_column(COL_DALIGN);
1898 add_uniq_column(COL_DGRAN);
1899 add_uniq_column(COL_DMAX);
1900 add_uniq_column(COL_DZERO);
2d232246 1901 break;
f2df4365 1902 case 'z':
be685b98
KZ
1903 add_uniq_column(COL_NAME);
1904 add_uniq_column(COL_ZONED);
f2df4365 1905 break;
2a4c734b
MB
1906 case 'e':
1907 parse_excludes(optarg);
1908 break;
4a102a48 1909 case 'J':
341c4ae2 1910 lsblk->flags |= LSBLK_JSON;
4a102a48 1911 break;
2a4c734b 1912 case 'l':
341c4ae2 1913 lsblk->flags &= ~LSBLK_TREE; /* disable the default */
2a4c734b 1914 break;
0bd05f5e
KZ
1915 case 'M':
1916 lsblk->merge = 1;
1917 break;
2a4c734b 1918 case 'n':
341c4ae2 1919 lsblk->flags |= LSBLK_NOHEADINGS;
2a4c734b
MB
1920 break;
1921 case 'o':
43bca827 1922 outarg = optarg;
2a4c734b 1923 break;
1b4d2a4a 1924 case 'O':
40b17508 1925 for (ncolumns = 0 ; ncolumns < ARRAY_SIZE(infos); ncolumns++)
1b4d2a4a
MY
1926 columns[ncolumns] = ncolumns;
1927 break;
c7e76cd1
KZ
1928 case 'p':
1929 lsblk->paths = 1;
1930 break;
6ea1bdd3 1931 case 'P':
341c4ae2
KZ
1932 lsblk->flags |= LSBLK_EXPORT;
1933 lsblk->flags &= ~LSBLK_TREE; /* disable the default */
6ea1bdd3 1934 break;
2a4c734b 1935 case 'i':
341c4ae2 1936 lsblk->flags |= LSBLK_ASCII;
2a4c734b 1937 break;
2ef4e8ba 1938 case 'I':
2ef4e8ba
KZ
1939 parse_includes(optarg);
1940 break;
2a4c734b 1941 case 'r':
341c4ae2
KZ
1942 lsblk->flags &= ~LSBLK_TREE; /* disable the default */
1943 lsblk->flags |= LSBLK_RAW; /* enable raw */
2a4c734b 1944 break;
09a71aa1
PR
1945 case 's':
1946 lsblk->inverse = 1;
1947 break;
2a4c734b 1948 case 'f':
be685b98
KZ
1949 add_uniq_column(COL_NAME);
1950 add_uniq_column(COL_FSTYPE);
4631edae 1951 add_uniq_column(COL_FSVERSION);
be685b98
KZ
1952 add_uniq_column(COL_LABEL);
1953 add_uniq_column(COL_UUID);
bc405d64
KZ
1954 add_uniq_column(COL_FSAVAIL);
1955 add_uniq_column(COL_FSUSEPERC);
be685b98 1956 add_uniq_column(COL_TARGET);
2a4c734b
MB
1957 break;
1958 case 'm':
be685b98
KZ
1959 add_uniq_column(COL_NAME);
1960 add_uniq_column(COL_SIZE);
1961 add_uniq_column(COL_OWNER);
1962 add_uniq_column(COL_GROUP);
1963 add_uniq_column(COL_MODE);
2a4c734b
MB
1964 break;
1965 case 't':
be685b98
KZ
1966 add_uniq_column(COL_NAME);
1967 add_uniq_column(COL_ALIOFF);
1968 add_uniq_column(COL_MINIO);
1969 add_uniq_column(COL_OPTIO);
1970 add_uniq_column(COL_PHYSEC);
1971 add_uniq_column(COL_LOGSEC);
1972 add_uniq_column(COL_ROTA);
1973 add_uniq_column(COL_SCHED);
1974 add_uniq_column(COL_RQ_SIZE);
1975 add_uniq_column(COL_RA);
1976 add_uniq_column(COL_WSAME);
2a4c734b 1977 break;
28ffc2b7
MB
1978 case 'S':
1979 lsblk->nodeps = 1;
1980 lsblk->scsi = 1;
be685b98
KZ
1981 add_uniq_column(COL_NAME);
1982 add_uniq_column(COL_HCTL);
1983 add_uniq_column(COL_TYPE);
1984 add_uniq_column(COL_VENDOR);
1985 add_uniq_column(COL_MODEL);
1986 add_uniq_column(COL_REV);
1987 add_uniq_column(COL_TRANSPORT);
28ffc2b7 1988 break;
1d9e35cc
KZ
1989 case 'T':
1990 force_tree = 1;
b9c088f2
KZ
1991 if (optarg)
1992 lsblk->tree_id = column_name_to_id(optarg, strlen(optarg));
1d9e35cc 1993 break;
ffc27623
KZ
1994 case OPT_SYSROOT:
1995 lsblk->sysroot = optarg;
1996 break;
0bd05f5e 1997 case 'E':
dc4662f0
KZ
1998 lsblk->dedup_id = column_name_to_id(optarg, strlen(optarg));
1999 if (lsblk->dedup_id >= 0)
2000 break;
2001 errtryhelp(EXIT_FAILURE);
2002 break;
642048e4 2003 case 'x':
341c4ae2 2004 lsblk->flags &= ~LSBLK_TREE; /* disable the default */
642048e4
KZ
2005 lsblk->sort_id = column_name_to_id(optarg, strlen(optarg));
2006 if (lsblk->sort_id >= 0)
2007 break;
2c308875
KZ
2008 errtryhelp(EXIT_FAILURE);
2009 break;
2010
2011 case 'h':
2012 usage();
2013 case 'V':
2014 print_version(EXIT_SUCCESS);
2a4c734b 2015 default:
193b3239 2016 errtryhelp(EXIT_FAILURE);
2a4c734b
MB
2017 }
2018 }
2019
1d9e35cc
KZ
2020 if (force_tree)
2021 lsblk->flags |= LSBLK_TREE;
2022
f65b3bb6
SK
2023 check_sysdevblock();
2024
2a4c734b 2025 if (!ncolumns) {
be685b98
KZ
2026 add_column(COL_NAME);
2027 add_column(COL_MAJMIN);
2028 add_column(COL_RM);
2029 add_column(COL_SIZE);
2030 add_column(COL_RO);
2031 add_column(COL_TYPE);
2032 add_column(COL_TARGET);
2a4c734b
MB
2033 }
2034
43bca827
MB
2035 if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
2036 &ncolumns, column_name_to_id) < 0)
2037 return EXIT_FAILURE;
2038
b9dd5721 2039 if (lsblk->all_devices == 0 && nexcludes == 0 && nincludes == 0)
2a4c734b 2040 excludes[nexcludes++] = 1; /* default: ignore RAM disks */
9554f7ab 2041
5e2305ca
KZ
2042 if (lsblk->sort_id < 0)
2043 /* Since Linux 4.8 we have sort devices by default, because
2044 * /sys is no more sorted */
2045 lsblk->sort_id = COL_MAJMIN;
2046
bbe1b314
KZ
2047 /* For --{inverse,raw,pairs} --list we still follow parent->child relation */
2048 if (!(lsblk->flags & LSBLK_TREE)
2049 && (lsblk->inverse || lsblk->flags & LSBLK_EXPORT || lsblk->flags & LSBLK_RAW))
091683a8
KZ
2050 lsblk->force_tree_order = 1;
2051
a6dc8dcd
KZ
2052 if (lsblk->sort_id >= 0 && column_id_to_number(lsblk->sort_id) < 0) {
2053 /* the sort column is not between output columns -- add as hidden */
be685b98 2054 add_column(lsblk->sort_id);
a6dc8dcd
KZ
2055 lsblk->sort_hidden = 1;
2056 }
642048e4 2057
dc4662f0
KZ
2058 if (lsblk->dedup_id >= 0 && column_id_to_number(lsblk->dedup_id) < 0) {
2059 /* the deduplication column is not between output columns -- add as hidden */
2060 add_column(lsblk->dedup_id);
2061 lsblk->dedup_hidden = 1;
2062 }
2063
e0c016f1 2064 lsblk_mnt_init();
710ed55d 2065 scols_init_debug(0);
5a89aa99 2066 ul_path_init_debug();
9554f7ab 2067
2a4c734b
MB
2068 /*
2069 * initialize output columns
2070 */
0925a9dd 2071 if (!(lsblk->table = scols_new_table()))
780ce22c 2072 errx(EXIT_FAILURE, _("failed to allocate output table"));
341c4ae2
KZ
2073 scols_table_enable_raw(lsblk->table, !!(lsblk->flags & LSBLK_RAW));
2074 scols_table_enable_export(lsblk->table, !!(lsblk->flags & LSBLK_EXPORT));
2075 scols_table_enable_ascii(lsblk->table, !!(lsblk->flags & LSBLK_ASCII));
2076 scols_table_enable_json(lsblk->table, !!(lsblk->flags & LSBLK_JSON));
2077 scols_table_enable_noheadings(lsblk->table, !!(lsblk->flags & LSBLK_NOHEADINGS));
2a4c734b 2078
341c4ae2 2079 if (lsblk->flags & LSBLK_JSON)
4a102a48
KZ
2080 scols_table_set_name(lsblk->table, "blockdevices");
2081
2a4c734b
MB
2082 for (i = 0; i < ncolumns; i++) {
2083 struct colinfo *ci = get_column_info(i);
642048e4
KZ
2084 struct libscols_column *cl;
2085 int id = get_column_id(i), fl = ci->flags;
2a4c734b 2086
b9c088f2
KZ
2087 if ((lsblk->flags & LSBLK_TREE)
2088 && has_tree_col == 0
2089 && id == lsblk->tree_id) {
2090 fl |= SCOLS_FL_TREE;
2091 fl &= ~SCOLS_FL_RIGHT;
2092 has_tree_col = 1;
2093 }
2094
a6dc8dcd
KZ
2095 if (lsblk->sort_hidden && lsblk->sort_id == id)
2096 fl |= SCOLS_FL_HIDDEN;
dc4662f0
KZ
2097 if (lsblk->dedup_hidden && lsblk->dedup_id == id)
2098 fl |= SCOLS_FL_HIDDEN;
2a4c734b 2099
fb376180
KZ
2100 if (force_tree
2101 && lsblk->flags & LSBLK_JSON
2102 && has_tree_col == 0
2103 && i + 1 == ncolumns)
2104 /* The "--tree --json" specified, but no column with
2105 * SCOLS_FL_TREE yet; force it for the last column
2106 */
2107 fl |= SCOLS_FL_TREE;
2108
642048e4
KZ
2109 cl = scols_table_new_column(lsblk->table, ci->name, ci->whint, fl);
2110 if (!cl) {
780ce22c 2111 warn(_("failed to allocate output column"));
2a4c734b
MB
2112 goto leave;
2113 }
642048e4
KZ
2114 if (!lsblk->sort_col && lsblk->sort_id == id) {
2115 lsblk->sort_col = cl;
2116 scols_column_set_cmpfunc(cl,
407e0ea6
KZ
2117 ci->type == COLTYPE_NUM ? cmp_u64_cells :
2118 ci->type == COLTYPE_SIZE ? cmp_u64_cells :
2119 ci->type == COLTYPE_SORTNUM ? cmp_u64_cells : scols_cmpstr_cells,
2120 NULL);
2121 }
2122 if (lsblk->flags & LSBLK_JSON) {
2123 switch (ci->type) {
2124 case COLTYPE_SIZE:
2125 if (!lsblk->bytes)
2126 break;
2127 /* fallthrough */
2128 case COLTYPE_NUM:
2129 scols_column_set_json_type(cl, SCOLS_JSON_NUMBER);
2130 break;
2131 case COLTYPE_BOOL:
2132 scols_column_set_json_type(cl, SCOLS_JSON_BOOLEAN);
2133 break;
2134 default:
2135 scols_column_set_json_type(cl, SCOLS_JSON_STRING);
2136 break;
2137 }
642048e4 2138 }
2a4c734b
MB
2139 }
2140
f43b8297
KZ
2141 tr = lsblk_new_devtree();
2142 if (!tr)
2143 err(EXIT_FAILURE, _("failed to allocate device tree"));
2144
e7b5353e
KZ
2145 if (optind == argc) {
2146 int rc = lsblk->inverse ?
2147 process_all_devices_inverse(tr) :
2148 process_all_devices(tr);
2149
2150 status = rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
2151 } else {
1539750f
KZ
2152 int cnt = 0, cnt_err = 0;
2153
2154 while (optind < argc) {
291606fa 2155 if (process_one_device(tr, argv[optind++]) != 0)
1539750f
KZ
2156 cnt_err++;
2157 cnt++;
2158 }
2159 status = cnt == 0 ? EXIT_FAILURE : /* nothing */
2160 cnt == cnt_err ? LSBLK_EXIT_ALLFAILED :/* all failed */
2161 cnt_err ? LSBLK_EXIT_SOMEOK : /* some ok */
2162 EXIT_SUCCESS; /* all success */
2163 }
2a4c734b 2164
dc4662f0
KZ
2165 if (lsblk->dedup_id > -1) {
2166 devtree_set_dedupkeys(tr, lsblk->dedup_id);
2167 lsblk_devtree_deduplicate_devices(tr);
2168 }
2169
f43b8297
KZ
2170 devtree_to_scols(tr, lsblk->table);
2171
642048e4
KZ
2172 if (lsblk->sort_col)
2173 scols_sort_table(lsblk->table, lsblk->sort_col);
091683a8
KZ
2174 if (lsblk->force_tree_order)
2175 scols_sort_table_by_tree(lsblk->table);
642048e4 2176
9bd4e5c0 2177 scols_print_table(lsblk->table);
2a4c734b
MB
2178
2179leave:
642048e4
KZ
2180 if (lsblk->sort_col)
2181 unref_sortdata(lsblk->table);
2182
9bd4e5c0 2183 scols_unref_table(lsblk->table);
50fccba1 2184
e0c016f1 2185 lsblk_mnt_deinit();
ccafadb7 2186 lsblk_properties_deinit();
57502dd3 2187 lsblk_unref_devtree(tr);
ccafadb7 2188
2a4c734b
MB
2189 return status;
2190}