90
99
/* columns descriptions */
91
100
static struct colinfo infos[__NCOLUMNS] = {
92
101
[COL_NAME] = { "NAME", 0.25, TT_FL_TREE, N_("device name") },
93
[COL_KNAME] = { "KNAME", 0.3, 0, N_("internel kernel device name") },
102
[COL_KNAME] = { "KNAME", 0.3, 0, N_("internal kernel device name") },
94
103
[COL_MAJMIN] = { "MAJ:MIN", 6, 0, N_("major:minor device number") },
95
104
[COL_FSTYPE] = { "FSTYPE", 0.1, TT_FL_TRUNC, N_("filesystem type") },
96
105
[COL_TARGET] = { "MOUNTPOINT", 0.10, TT_FL_TRUNC, N_("where the device is mounted") },
101
110
[COL_ROTA] = { "ROTA", 1, TT_FL_RIGHT, N_("rotational device") },
102
111
[COL_MODEL] = { "MODEL", 0.1, TT_FL_TRUNC, N_("device identifier") },
103
112
[COL_SIZE] = { "SIZE", 6, TT_FL_RIGHT, N_("size of the device") },
113
[COL_STATE] = { "STATE", 7, TT_FL_TRUNC, N_("state of the device") },
104
114
[COL_OWNER] = { "OWNER", 0.1, TT_FL_TRUNC, N_("user name"), },
105
115
[COL_GROUP] = { "GROUP", 0.1, TT_FL_TRUNC, N_("group name") },
106
116
[COL_MODE] = { "MODE", 10, 0, N_("device node permissions") },
109
119
[COL_OPTIO] = { "OPT-IO", 6, TT_FL_RIGHT, N_("optimal I/O size") },
110
120
[COL_PHYSEC] = { "PHY-SEC", 7, TT_FL_RIGHT, N_("physical sector size") },
111
121
[COL_LOGSEC] = { "LOG-SEC", 7, TT_FL_RIGHT, N_("logical sector size") },
112
[COL_SCHED] = { "SCHED", 0.1, 0, N_("I/O scheduler name") }
122
[COL_SCHED] = { "SCHED", 0.1, 0, N_("I/O scheduler name") },
123
[COL_RQ_SIZE]= { "RQ-SIZE", 5, TT_FL_RIGHT, N_("request queue size") },
124
[COL_TYPE] = { "TYPE", 4, 0, N_("device type") },
125
[COL_DALIGN] = { "DISC-ALN", 6, TT_FL_RIGHT, N_("discard alignment offset") },
126
[COL_DGRAN] = { "DISC-GRAN", 6, TT_FL_RIGHT, N_("discard granularity") },
127
[COL_DMAX] = { "DISC-MAX", 6, TT_FL_RIGHT, N_("discard max bytes") },
128
[COL_DZERO] = { "DISC-ZERO", 1, TT_FL_RIGHT, N_("discard zeroes data") },
117
struct tt *tt; /* output table */
118
int all_devices:1; /* print all devices */
119
int bytes:1; /* print SIZE in bytes */
120
int nodeps:1; /* don't print slaves/holders */
132
struct tt *tt; /* output table */
133
unsigned int all_devices:1; /* print all devices */
134
unsigned int bytes:1; /* print SIZE in bytes */
135
unsigned int nodeps:1; /* don't print slaves/holders */
123
138
struct lsblk *lsblk; /* global handler */
124
139
int columns[__NCOLUMNS];/* enabled columns */
125
140
int ncolumns; /* number of enabled columns */
127
unsigned int excludes[256];
130
145
struct blkdev_cxt {
131
146
struct blkdev_cxt *parent;
150
166
* /sys/block/.../holders + number of partition */
151
167
int nslaves; /* # of devices this device maps to */
152
168
int maj, min; /* devno */
169
int discard; /* supports discard */
154
171
uint64_t size; /* device size */
157
174
static int is_maj_excluded(int maj)
161
178
assert(ARRAY_SIZE(excludes) > nexcludes);
239
static int is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_name)
246
#ifdef _DIRENT_HAVE_D_TYPE
247
if (d->d_type != DT_DIR)
250
if (strncmp(parent_name, d->d_name, strlen(parent_name)))
253
/* Cannot use /partition file, not supported on old sysfs */
254
snprintf(path, sizeof(path), "%s/start", d->d_name);
256
return faccessat(dirfd(dir), path, R_OK, 0) == 0;
259
252
static char *get_device_path(struct blkdev_cxt *cxt)
261
254
char path[PATH_MAX];
270
263
return xstrdup(path);
273
static char *get_sysfs_path(struct blkdev_cxt *cxt)
280
if (cxt->partition && cxt->parent)
281
snprintf(path, sizeof(path), _PATH_SYS_BLOCK "/%s/%s",
282
cxt->parent->name, cxt->name);
284
snprintf(path, sizeof(path), _PATH_SYS_BLOCK "/%s", cxt->name);
286
return xstrdup(path);
289
static int sysfs_open(struct blkdev_cxt *cxt, const char *attr)
294
assert(cxt->sysfs_fd >= 0);
296
fd = openat(cxt->sysfs_fd, attr, O_RDONLY);
297
if (fd == -1 && errno == ENOENT && !strncmp(attr, "queue/", 6) && cxt->parent) {
298
fd = openat(cxt->parent->sysfs_fd, attr, O_RDONLY);
303
static FILE *sysfs_fopen(struct blkdev_cxt *cxt, const char *attr)
305
int fd = sysfs_open(cxt, attr);
307
return fd < 0 ? NULL : fdopen(fd, "r");
310
static DIR *sysfs_opendir(struct blkdev_cxt *cxt, const char *attr)
316
fd = sysfs_open(cxt, attr);
318
/* request to open root of device in sysfs (/sys/block/<dev>)
319
* -- we cannot use cxt->sysfs_fd directly, because closedir()
320
* will close this our persistent file descriptor.
323
assert(cxt->sysfs_fd >= 0);
325
fd = dup(cxt->sysfs_fd);
340
static __attribute__ ((format (scanf, 3, 4)))
341
int sysfs_scanf(struct blkdev_cxt *cxt, const char *attr, const char *fmt, ...)
343
FILE *f = sysfs_fopen(cxt, attr);
350
rc = vfscanf(f, fmt, ap);
357
static uint64_t sysfs_read_u64(struct blkdev_cxt *cxt, const char *attr)
360
return sysfs_scanf(cxt, attr, "%"SCNu64, &x) == 1 ? x : 0;
363
static char *sysfs_strdup(struct blkdev_cxt *cxt, const char *attr)
366
return sysfs_scanf(cxt, attr, "%1024[^\n]", buf) == 1 ?
370
static int sysfs_count_dirents(struct blkdev_cxt *cxt, const char *attr)
375
if (!(dir = sysfs_opendir(cxt, attr)))
378
while (xreaddir(dir)) r++;
384
static int sysfs_count_partitions(struct blkdev_cxt *cxt)
390
if (!(dir = sysfs_opendir(cxt, NULL)))
393
while ((d = xreaddir(dir))) {
394
if (is_partition_dirent(dir, d, cxt->name))
402
266
static char *get_device_mountpoint(struct blkdev_cxt *cxt)
405
269
char mnt[PATH_MAX];
272
assert(cxt->filename);
365
static char *get_type(struct blkdev_cxt *cxt)
367
char *res = NULL, *p;
369
if (is_dm(cxt->name)) {
370
char *dm_uuid = sysfs_strdup(&cxt->sysfs, "dm/uuid");
372
/* The DM_UUID prefix should be set to subsystem owning
373
* the device - LVM, CRYPT, DMRAID, MPATH, PART */
376
char *dm_uuid_prefix = strsep(&tmp, "-");
378
if (dm_uuid_prefix) {
379
/* kpartx hack to remove partition number */
380
if (strncasecmp(dm_uuid_prefix, "part", 4) == 0)
381
dm_uuid_prefix[4] = '\0';
383
res = xstrdup(dm_uuid_prefix);
389
/* No UUID or no prefix - just mark it as DM device */
392
} else if (!strncmp(cxt->name, "loop", 4)) {
393
res = xstrdup("loop");
395
} else if (!strncmp(cxt->name, "md", 2)) {
396
char *md_level = sysfs_strdup(&cxt->sysfs, "md/level");
397
res = md_level ? md_level : xstrdup("md");
400
const char *type = cxt->partition ? "part" : "disk";
403
sysfs_read_int(&cxt->sysfs, "device/type", &x);
406
case 0x0c: /* TYPE_RAID */
407
type = "raid"; break;
408
case 0x01: /* TYPE_TAPE */
409
type = "raid"; break;
410
case 0x04: /* TYPE_WORM */
411
case 0x05: /* TYPE_ROM */
413
case 0x07: /* TYPE_MOD */
414
type = "mo-disk"; break;
415
case 0x0e: /* TYPE_RBC */
422
for (p = res; p && *p; p++)
423
*p = tolower((unsigned char) *p);
498
427
static void set_tt_data(struct blkdev_cxt *cxt, int col, int id, struct tt_line *ln)
503
432
if (!cxt->st.st_rdev && (id == COL_OWNER || id == COL_GROUP ||
540
if (lsblk->tt->flags & TT_FL_RAW)
469
if ((lsblk->tt->flags & TT_FL_RAW) ||
470
(lsblk->tt->flags & TT_FL_EXPORT))
541
471
snprintf(buf, sizeof(buf), "%u:%u", cxt->maj, cxt->min);
543
473
snprintf(buf, sizeof(buf), "%3u:%-3u", cxt->maj, cxt->min);
570
500
xstrdup("1") : xstrdup("0"));
573
p = sysfs_strdup(cxt, "removable");
503
p = sysfs_strdup(&cxt->sysfs, "removable");
574
504
if (!p && cxt->parent)
575
p = sysfs_strdup(cxt->parent, "removable");
505
p = sysfs_strdup(&cxt->parent->sysfs, "removable");
577
507
tt_line_set_data(ln, col, p);
580
p = sysfs_strdup(cxt, "queue/rotational");
510
p = sysfs_strdup(&cxt->sysfs, "queue/rotational");
582
512
tt_line_set_data(ln, col, p);
585
515
if (!cxt->partition && cxt->nslaves == 0) {
586
p = sysfs_strdup(cxt, "device/model");
516
p = sysfs_strdup(&cxt->sysfs, "device/model");
588
518
tt_line_set_data(ln, col, p);
594
524
if (asprintf(&p, "%jd", cxt->size) < 0)
597
p = size_to_human_string(cxt->size);
527
p = size_to_human_string(SIZE_SUFFIX_1LETTER, cxt->size);
599
529
tt_line_set_data(ln, col, p);
533
if (!cxt->partition && !cxt->dm_name) {
534
p = sysfs_strdup(&cxt->sysfs, "device/state");
535
} else if (cxt->dm_name) {
537
if (sysfs_read_int(&cxt->sysfs, "dm/suspended", &x) == 0)
538
p = x ? xstrdup("suspended") : xstrdup("running");
541
tt_line_set_data(ln, col, p);
603
p = sysfs_strdup(cxt, "alignment_offset");
544
p = sysfs_strdup(&cxt->sysfs, "alignment_offset");
605
546
tt_line_set_data(ln, col, p);
608
p = sysfs_strdup(cxt, "queue/minimum_io_size");
549
p = sysfs_strdup(&cxt->sysfs, "queue/minimum_io_size");
610
551
tt_line_set_data(ln, col, p);
613
p = sysfs_strdup(cxt, "queue/optimal_io_size");
554
p = sysfs_strdup(&cxt->sysfs, "queue/optimal_io_size");
615
556
tt_line_set_data(ln, col, p);
618
p = sysfs_strdup(cxt, "queue/physical_block_size");
559
p = sysfs_strdup(&cxt->sysfs, "queue/physical_block_size");
620
561
tt_line_set_data(ln, col, p);
623
p = sysfs_strdup(cxt, "queue/logical_block_size");
564
p = sysfs_strdup(&cxt->sysfs, "queue/logical_block_size");
625
566
tt_line_set_data(ln, col, p);
630
571
tt_line_set_data(ln, col, p);
574
p = sysfs_strdup(&cxt->sysfs, "queue/nr_requests");
576
tt_line_set_data(ln, col, p);
581
tt_line_set_data(ln, col, p);
584
p = sysfs_strdup(&cxt->sysfs, "discard_alignment");
585
if (cxt->discard && p)
586
tt_line_set_data(ln, col, p);
588
tt_line_set_data(ln, col, "0");
592
p = sysfs_strdup(&cxt->sysfs, "queue/discard_granularity");
596
if (sysfs_read_u64(&cxt->sysfs,
597
"queue/discard_granularity", &x) == 0)
598
p = size_to_human_string(SIZE_SUFFIX_1LETTER, x);
601
tt_line_set_data(ln, col, p);
605
p = sysfs_strdup(&cxt->sysfs, "queue/discard_max_bytes");
609
if (sysfs_read_u64(&cxt->sysfs,
610
"queue/discard_max_bytes", &x) == 0)
611
p = size_to_human_string(SIZE_SUFFIX_1LETTER, x);
614
tt_line_set_data(ln, col, p);
617
p = sysfs_strdup(&cxt->sysfs, "queue/discard_zeroes_data");
618
if (cxt->discard && p)
619
tt_line_set_data(ln, col, p);
621
tt_line_set_data(ln, col, "0");
647
638
const char *name,
652
643
cxt->parent = parent;
653
644
cxt->name = xstrdup(name);
654
645
cxt->partition = partition;
656
647
cxt->filename = get_device_path(cxt);
658
/* open /sys/block/<name> */
659
p = get_sysfs_path(cxt);
660
cxt->sysfs_fd = open(p, O_RDONLY);
661
if (cxt->sysfs_fd < 0)
662
err(EXIT_FAILURE, _("%s: open failed"), p);
665
if (sysfs_scanf(cxt, "dev", "%u:%u", &cxt->maj, &cxt->min) != 2)
668
cxt->size = sysfs_read_u64(cxt, "size") << 9;
648
if (!cxt->filename) {
649
warnx(_("%s: failed to get device path"), name);
653
devno = sysfs_devname_to_devno(name,
654
partition && parent ? parent->name : NULL);
656
warnx(_("%s: unknown device name"), name);
660
if (sysfs_init(&cxt->sysfs, devno, parent ? &parent->sysfs : NULL)) {
661
warnx(_("%s: failed to initialize sysfs handler"), name);
665
cxt->maj = major(devno);
666
cxt->min = minor(devno);
668
sysfs_read_u64(&cxt->sysfs, "size", &cxt->size); /* in sectors */
669
cxt->size <<= 9; /* in bytes */
671
sysfs_read_int(&cxt->sysfs, "queue/discard_granularity", &cxt->discard);
670
673
/* Ignore devices of zero size */
671
674
if (!lsblk->all_devices && cxt->size == 0)
675
cxt->dm_name = sysfs_strdup(cxt, "dm/name");
678
cxt->dm_name = sysfs_strdup(&cxt->sysfs, "dm/name");
680
warnx(_("%s: failed to get dm name"), name);
684
cxt->nholders = sysfs_count_dirents(&cxt->sysfs, "holders") +
685
sysfs_count_partitions(&cxt->sysfs, name);
677
cxt->nholders = sysfs_count_dirents(cxt, "holders") +
678
sysfs_count_partitions(cxt);
679
cxt->nslaves = sysfs_count_dirents(cxt, "slaves");
687
cxt->nslaves = sysfs_count_dirents(&cxt->sysfs, "slaves");
703
dir = sysfs_opendir(cxt, NULL);
710
dir = sysfs_opendir(&cxt->sysfs, NULL);
705
712
err(EXIT_FAILURE, _("failed to open device directory in sysfs"));
707
714
while ((d = xreaddir(dir))) {
708
if (!is_partition_dirent(dir, d, cxt->name))
715
if (!sysfs_is_partition_dirent(dir, d, cxt->name))
711
set_cxt(&holder, cxt, d->d_name, 1);
718
if (set_cxt(&holder, cxt, d->d_name, 1)) {
719
reset_blkdev_cxt(&holder);
712
722
print_device(&holder, cxt->tt_line);
713
723
list_holders(&holder);
714
724
reset_blkdev_cxt(&holder);
719
dir = sysfs_opendir(cxt, "holders");
729
dir = sysfs_opendir(&cxt->sysfs, "holders");
723
733
while ((d = xreaddir(dir))) {
724
set_cxt(&holder, cxt, d->d_name, 0);
734
if (set_cxt(&holder, cxt, d->d_name, 0)) {
735
reset_blkdev_cxt(&holder);
725
738
print_device(&holder, cxt->tt_line);
726
739
list_holders(&holder);
727
740
reset_blkdev_cxt(&holder);
769
781
struct blkdev_cxt parent = {}, cxt = {};
771
char buf[PATH_MAX + 1];
783
char buf[PATH_MAX + 1], *diskname = NULL;
785
int status = EXIT_FAILURE;
774
787
if (stat(devname, &st) || !S_ISBLK(st.st_mode)) {
775
788
warnx(_("%s: not a block device"), devname);
776
789
return EXIT_FAILURE;
778
791
if (blkid_devno_to_wholedisk(st.st_rdev, buf, sizeof(buf), &disk)) {
779
warn(_("%s: failed to get whole-list devno"), devname);
792
warn(_("%s: failed to get whole-disk device number"), devname);
780
793
return EXIT_FAILURE;
782
if (st.st_rdev == disk)
795
if (st.st_rdev == disk) {
784
797
* unpartitioned device
786
set_cxt(&cxt, NULL, buf, 0);
799
if (set_cxt(&cxt, NULL, buf, 0))
789
803
* Parititioned, read sysfs name of the device
792
char path[PATH_MAX], *diskname, *name;
794
snprintf(path, sizeof(path), "/sys/dev/block/%d:%d",
795
major(st.st_rdev), minor(st.st_rdev));
806
char path[PATH_MAX], *name;
808
if (!sysfs_devno_path(st.st_rdev, path, sizeof(path))) {
809
warn(_("failed to compose sysfs path for %s"), devname);
796
813
diskname = xstrdup(buf);
798
814
len = readlink(path, buf, PATH_MAX);
800
816
warn(_("%s: failed to read link"), path);
805
821
/* sysfs device name */
806
822
name = strrchr(buf, '/') + 1;
808
set_cxt(&parent, NULL, diskname, 0);
809
set_cxt(&cxt, &parent, name, 1);
824
if (set_cxt(&parent, NULL, diskname, 0))
826
if (set_cxt(&cxt, &parent, name, 1))
814
830
print_device(&cxt, NULL);
815
831
list_holders(&cxt);
832
status = EXIT_SUCCESS;
816
835
reset_blkdev_cxt(&cxt);
818
837
if (st.st_rdev != disk)
819
838
reset_blkdev_cxt(&parent);
824
843
static void parse_excludes(const char *str)
857
877
" -a, --all print all devices\n"
858
878
" -b, --bytes print SIZE in bytes rather than in human readable format\n"
859
879
" -d, --nodeps don't print slaves or holders\n"
880
" -D, --discard print discard capabilities\n"
860
881
" -e, --exclude <list> exclude devices by major number (default: RAM disks)\n"
861
882
" -f, --fs output info about filesystems\n"
862
883
" -h, --help usage information (this)\n"
865
886
" -l, --list use list format ouput\n"
866
887
" -n, --noheadings don't print headings\n"
867
888
" -o, --output <list> output columns\n"
868
" -r, --raw use raw format output\n"
889
" -P, --pairs use key=\"value\" output format\n"
890
" -r, --raw use raw output format\n"
869
891
" -t, --topology output info about topology\n"));
871
893
fprintf(out, _("\nAvailable columns:\n"));
915
939
memset(lsblk, 0, sizeof(*lsblk));
917
while((c = getopt_long(argc, argv, "abde:fhlnmo:irt", longopts, NULL)) != -1) {
941
while((c = getopt_long(argc, argv, "abdDe:fhlnmo:Pirt", longopts, NULL)) != -1) {
920
944
lsblk->all_devices = 1;
941
972
tt_flags |= TT_FL_NOHEADINGS;
944
if (tt_parse_columns_list(optarg, columns, &ncolumns,
975
ncolumns = string_to_idarray(optarg,
976
columns, ARRAY_SIZE(columns),
946
979
return EXIT_FAILURE;
982
tt_flags |= TT_FL_EXPORT;
983
tt_flags &= ~TT_FL_TREE; /* disable the default */
949
986
tt_flags |= TT_FL_ASCII;