]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/lsblk.c
partx: remove __NCOLUMNS
[thirdparty/util-linux.git] / misc-utils / lsblk.c
CommitLineData
2a4c734b 1/*
f77fa578 2 * lsblk(8) - list block devices
2a4c734b 3 *
270e66ec 4 * Copyright (C) 2010,2011 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 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include <stdio.h>
24#include <errno.h>
25#include <getopt.h>
2a4c734b
MB
26#include <stdlib.h>
27#include <unistd.h>
28#include <stdint.h>
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <dirent.h>
32#include <fcntl.h>
33#include <string.h>
2a4c734b
MB
34#include <sys/ioctl.h>
35#include <inttypes.h>
36#include <stdarg.h>
37#include <locale.h>
38#include <pwd.h>
39#include <grp.h>
18eac5ba 40#include <ctype.h>
2a4c734b
MB
41
42#include <blkid.h>
43
f17f5f48
IM
44#ifdef HAVE_LIBUDEV
45#include <libudev.h>
46#endif
47
2a4c734b
MB
48#include <assert.h>
49
50#include "pathnames.h"
51#include "blkdev.h"
52#include "canonicalize.h"
53#include "ismounted.h"
54#include "nls.h"
55#include "tt.h"
56#include "xalloc.h"
57#include "strutils.h"
eb76ca98 58#include "c.h"
766ab9b0 59#include "sysfs.h"
2a4c734b
MB
60
61/* column IDs */
62enum {
63 COL_NAME = 0,
64 COL_KNAME,
65 COL_MAJMIN,
66 COL_FSTYPE,
67 COL_TARGET,
68 COL_LABEL,
69 COL_UUID,
70 COL_RO,
627970a7 71 COL_RM,
2a4c734b
MB
72 COL_MODEL,
73 COL_SIZE,
01e487c0 74 COL_STATE,
2a4c734b
MB
75 COL_OWNER,
76 COL_GROUP,
77 COL_MODE,
78 COL_ALIOFF,
79 COL_MINIO,
80 COL_OPTIO,
81 COL_PHYSEC,
82 COL_LOGSEC,
83 COL_ROTA,
84 COL_SCHED,
64c9e22a 85 COL_RQ_SIZE,
18eac5ba 86 COL_TYPE,
2d232246
MP
87 COL_DALIGN,
88 COL_DGRAN,
89 COL_DMAX,
90 COL_DZERO,
2a4c734b
MB
91
92 __NCOLUMNS
93};
94
95/* column names */
96struct colinfo {
97 const char *name; /* header */
98 double whint; /* width hint (N < 1 is in percent of termwidth) */
99 int flags; /* TT_FL_* */
100 const char *help;
101};
102
103/* columns descriptions */
104static struct colinfo infos[__NCOLUMNS] = {
105 [COL_NAME] = { "NAME", 0.25, TT_FL_TREE, N_("device name") },
e22d8b95 106 [COL_KNAME] = { "KNAME", 0.3, 0, N_("internal kernel device name") },
2a4c734b
MB
107 [COL_MAJMIN] = { "MAJ:MIN", 6, 0, N_("major:minor device number") },
108 [COL_FSTYPE] = { "FSTYPE", 0.1, TT_FL_TRUNC, N_("filesystem type") },
109 [COL_TARGET] = { "MOUNTPOINT", 0.10, TT_FL_TRUNC, N_("where the device is mounted") },
110 [COL_LABEL] = { "LABEL", 0.1, 0, N_("filesystem LABEL") },
111 [COL_UUID] = { "UUID", 36, 0, N_("filesystem UUID") },
112 [COL_RO] = { "RO", 1, TT_FL_RIGHT, N_("read-only device") },
627970a7 113 [COL_RM] = { "RM", 1, TT_FL_RIGHT, N_("removable device") },
2a4c734b
MB
114 [COL_ROTA] = { "ROTA", 1, TT_FL_RIGHT, N_("rotational device") },
115 [COL_MODEL] = { "MODEL", 0.1, TT_FL_TRUNC, N_("device identifier") },
116 [COL_SIZE] = { "SIZE", 6, TT_FL_RIGHT, N_("size of the device") },
01e487c0 117 [COL_STATE] = { "STATE", 7, TT_FL_TRUNC, N_("state of the device") },
2a4c734b
MB
118 [COL_OWNER] = { "OWNER", 0.1, TT_FL_TRUNC, N_("user name"), },
119 [COL_GROUP] = { "GROUP", 0.1, TT_FL_TRUNC, N_("group name") },
120 [COL_MODE] = { "MODE", 10, 0, N_("device node permissions") },
121 [COL_ALIOFF] = { "ALIGNMENT", 6, TT_FL_RIGHT, N_("alignment offset") },
122 [COL_MINIO] = { "MIN-IO", 6, TT_FL_RIGHT, N_("minimum I/O size") },
123 [COL_OPTIO] = { "OPT-IO", 6, TT_FL_RIGHT, N_("optimal I/O size") },
124 [COL_PHYSEC] = { "PHY-SEC", 7, TT_FL_RIGHT, N_("physical sector size") },
125 [COL_LOGSEC] = { "LOG-SEC", 7, TT_FL_RIGHT, N_("logical sector size") },
18eac5ba 126 [COL_SCHED] = { "SCHED", 0.1, 0, N_("I/O scheduler name") },
64c9e22a 127 [COL_RQ_SIZE]= { "RQ-SIZE", 5, TT_FL_RIGHT, N_("request queue size") },
2d232246
MP
128 [COL_TYPE] = { "TYPE", 4, 0, N_("device type") },
129 [COL_DALIGN] = { "DISC-ALN", 6, TT_FL_RIGHT, N_("discard alignment offset") },
130 [COL_DGRAN] = { "DISC-GRAN", 6, TT_FL_RIGHT, N_("discard granularity") },
131 [COL_DMAX] = { "DISC-MAX", 6, TT_FL_RIGHT, N_("discard max bytes") },
132 [COL_DZERO] = { "DISC-ZERO", 1, TT_FL_RIGHT, N_("discard zeroes data") },
2a4c734b
MB
133};
134
135struct lsblk {
9feec79c
KZ
136 struct tt *tt; /* output table */
137 unsigned int all_devices:1; /* print all devices */
138 unsigned int bytes:1; /* print SIZE in bytes */
139 unsigned int nodeps:1; /* don't print slaves/holders */
2a4c734b
MB
140};
141
142struct lsblk *lsblk; /* global handler */
143int columns[__NCOLUMNS];/* enabled columns */
144int ncolumns; /* number of enabled columns */
145
6aace32f
KZ
146int excludes[256];
147size_t nexcludes;
2a4c734b
MB
148
149struct blkdev_cxt {
150 struct blkdev_cxt *parent;
151
152 struct tt_line *tt_line;
153 struct stat st;
154
155 char *name; /* kernel name in /sys/block */
156 char *dm_name; /* DM name (dm/block) */
157
158 char *filename; /* path to device node */
766ab9b0
KZ
159
160 struct sysfs_cxt sysfs;
2a4c734b
MB
161
162 int partition; /* is partition? TRUE/FALSE */
163
164 int probed; /* already probed */
165 char *fstype; /* detected fs, NULL or "?" if cannot detect */
166 char *uuid; /* UUID of device / filesystem */
167 char *label; /* FS label */
168
169 int nholders; /* # of devices mapped directly to this device
170 * /sys/block/.../holders + number of partition */
171 int nslaves; /* # of devices this device maps to */
172 int maj, min; /* devno */
98055888 173 int discard; /* supports discard */
2a4c734b
MB
174
175 uint64_t size; /* device size */
176};
177
178static int is_maj_excluded(int maj)
179{
6aace32f 180 size_t i;
2a4c734b
MB
181
182 assert(ARRAY_SIZE(excludes) > nexcludes);
183
184 for (i = 0; i < nexcludes; i++)
185 if (excludes[i] == maj)
186 return 1;
187 return 0;
188}
189
2a4c734b
MB
190/* array with IDs of enabled columns */
191static int get_column_id(int num)
192{
193 assert(ARRAY_SIZE(columns) == __NCOLUMNS);
194 assert(num < ncolumns);
195 assert(columns[num] < __NCOLUMNS);
196 return columns[num];
197}
198
199static struct colinfo *get_column_info(int num)
200{
201 return &infos[ get_column_id(num) ];
202}
203
2a4c734b
MB
204static int column_name_to_id(const char *name, size_t namesz)
205{
206 int i;
207
208 for (i = 0; i < __NCOLUMNS; i++) {
209 const char *cn = infos[i].name;
210
211 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
212 return i;
213 }
214 warnx(_("unknown column: %s"), name);
215 return -1;
216}
217
218static void reset_blkdev_cxt(struct blkdev_cxt *cxt)
219{
220 if (!cxt)
221 return;
222 free(cxt->name);
223 free(cxt->dm_name);
224 free(cxt->filename);
225 free(cxt->fstype);
226 free(cxt->uuid);
227 free(cxt->label);
228
766ab9b0 229 sysfs_deinit(&cxt->sysfs);
2a4c734b
MB
230
231 memset(cxt, 0, sizeof(*cxt));
232}
233
234static int is_dm(const char *name)
235{
236 return strncmp(name, "dm-", 3) ? 0 : 1;
237}
238
239static struct dirent *xreaddir(DIR *dp)
240{
241 struct dirent *d;
242
243 assert(dp);
244
245 while ((d = readdir(dp))) {
246 if (!strcmp(d->d_name, ".") ||
247 !strcmp(d->d_name, ".."))
248 continue;
249
250 /* blacklist here? */
251 break;
252 }
253 return d;
254}
255
2a4c734b
MB
256static char *get_device_path(struct blkdev_cxt *cxt)
257{
258 char path[PATH_MAX];
259
260 assert(cxt);
261 assert(cxt->name);
262
263 if (is_dm(cxt->name))
264 return canonicalize_dm_name(cxt->name);
265
266 snprintf(path, sizeof(path), "/dev/%s", cxt->name);
267 return xstrdup(path);
268}
269
2a4c734b
MB
270static char *get_device_mountpoint(struct blkdev_cxt *cxt)
271{
272 int fl = 0;
273 char mnt[PATH_MAX];
274
270e66ec
MB
275 assert(cxt);
276 assert(cxt->filename);
277
2a4c734b
MB
278 *mnt = '\0';
279
280 /*
281 * TODO: use libmount and parse /proc/mountinfo only once
282 */
283 if (check_mount_point(cxt->filename, &fl, mnt, sizeof(mnt)) == 0 &&
284 (fl & MF_MOUNTED)) {
285 if (fl & MF_SWAP)
286 strcpy(mnt, "[SWAP]");
287 }
288 return strlen(mnt) ? xstrdup(mnt) : NULL;
289}
290
f17f5f48
IM
291#ifndef HAVE_LIBUDEV
292static int probe_device_by_udev(struct blkdev_cxt *cxt
293 __attribute__((__unused__)))
294{
295 return -1;
296}
297#else
298static int probe_device_by_udev(struct blkdev_cxt *cxt)
299{
300 struct udev *udev;
301 struct udev_device *dev;
302
303 udev = udev_new();
304 if (!udev)
305 return -1;
306
307 dev = udev_device_new_from_subsystem_sysname(udev, "block", cxt->name);
308 if (dev) {
309 const char *data;
310
311 if ((data = udev_device_get_property_value(dev, "ID_FS_LABEL")))
312 cxt->label = xstrdup(data);
313 if ((data = udev_device_get_property_value(dev, "ID_FS_TYPE")))
314 cxt->fstype = xstrdup(data);
315 if ((data = udev_device_get_property_value(dev, "ID_FS_UUID")))
316 cxt->uuid = xstrdup(data);
317
318 udev_device_unref(dev);
319 }
320
321 udev_unref(udev);
322 return 0;
323}
324#endif /* HAVE_LIBUDEV */
325
2a4c734b
MB
326static void probe_device(struct blkdev_cxt *cxt)
327{
2a4c734b
MB
328 blkid_probe pr = NULL;
329
330 if (cxt->probed)
331 return;
f17f5f48 332
2a4c734b
MB
333 cxt->probed = 1;
334
335 if (!cxt->size)
336 return;
337
f17f5f48
IM
338 /* try udev DB */
339 if (probe_device_by_udev(cxt) == 0)
340 return; /* success */
341
342 /* try libblkid */
2a4c734b
MB
343 pr = blkid_new_probe_from_filename(cxt->filename);
344 if (!pr)
345 return;
346
347 /* TODO: we have to enable partitions probing to avoid conflicts
348 * between raids and PT -- see blkid(8) code for more details
349 */
350 blkid_probe_enable_superblocks(pr, 1);
351 blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_LABEL |
352 BLKID_SUBLKS_UUID |
353 BLKID_SUBLKS_TYPE);
354 if (!blkid_do_safeprobe(pr)) {
355 const char *data = NULL;
356
357 if (!blkid_probe_lookup_value(pr, "TYPE", &data, NULL))
358 cxt->fstype = xstrdup(data);
359 if (!blkid_probe_lookup_value(pr, "UUID", &data, NULL))
360 cxt->uuid = xstrdup(data);
361 if (!blkid_probe_lookup_value(pr, "LABEL", &data, NULL))
362 cxt->label = xstrdup(data);
363 }
364
2a4c734b
MB
365 blkid_free_probe(pr);
366 return;
367}
368
369static int is_readonly_device(struct blkdev_cxt *cxt)
370{
371 int fd, ro = 0;
372
766ab9b0 373 if (sysfs_scanf(&cxt->sysfs, "ro", "%d", &ro) == 0)
2a4c734b
MB
374 return ro;
375
376 /* fallback if "ro" attribute does not exist */
377 fd = open(cxt->filename, O_RDONLY);
378 if (fd != -1) {
379 ioctl(fd, BLKROGET, &ro);
380 close(fd);
381 }
382 return ro;
383}
384
385static char *get_scheduler(struct blkdev_cxt *cxt)
386{
766ab9b0 387 char *str = sysfs_strdup(&cxt->sysfs, "queue/scheduler");
2a4c734b
MB
388 char *p, *res = NULL;
389
390 if (!str)
391 return NULL;
392 p = strchr(str, '[');
393 if (p) {
394 res = p + 1;
395 p = strchr(res, ']');
396 if (p) {
397 *p = '\0';
398 res = xstrdup(res);
399 } else
400 res = NULL;
401 }
402 free(str);
403 return res;
404}
405
18eac5ba
MB
406static char *get_type(struct blkdev_cxt *cxt)
407{
408 char *res = NULL, *p;
409
410 if (is_dm(cxt->name)) {
766ab9b0 411 char *dm_uuid = sysfs_strdup(&cxt->sysfs, "dm/uuid");
18eac5ba
MB
412
413 /* The DM_UUID prefix should be set to subsystem owning
414 * the device - LVM, CRYPT, DMRAID, MPATH, PART */
415 if (dm_uuid) {
416 char *tmp = dm_uuid;
417 char *dm_uuid_prefix = strsep(&tmp, "-");
418
419 if (dm_uuid_prefix) {
420 /* kpartx hack to remove partition number */
421 if (strncasecmp(dm_uuid_prefix, "part", 4) == 0)
422 dm_uuid_prefix[4] = '\0';
423
424 res = xstrdup(dm_uuid_prefix);
425 }
426 }
427
428 free(dm_uuid);
429 if (!res)
430 /* No UUID or no prefix - just mark it as DM device */
431 res = xstrdup("dm");
432
433 } else if (!strncmp(cxt->name, "loop", 4)) {
434 res = xstrdup("loop");
435
436 } else if (!strncmp(cxt->name, "md", 2)) {
766ab9b0 437 char *md_level = sysfs_strdup(&cxt->sysfs, "md/level");
18eac5ba
MB
438 res = md_level ? md_level : xstrdup("md");
439
440 } else {
441 const char *type = cxt->partition ? "part" : "disk";
90e9fcda 442 int x = 0;
18eac5ba 443
90e9fcda
KZ
444 sysfs_read_int(&cxt->sysfs, "device/type", &x);
445
446 switch (x) {
18eac5ba
MB
447 case 0x0c: /* TYPE_RAID */
448 type = "raid"; break;
449 case 0x01: /* TYPE_TAPE */
450 type = "raid"; break;
451 case 0x04: /* TYPE_WORM */
452 case 0x05: /* TYPE_ROM */
453 type = "rom"; break;
454 case 0x07: /* TYPE_MOD */
455 type = "mo-disk"; break;
456 case 0x0e: /* TYPE_RBC */
457 type = "rbc"; break;
458 }
459
90e9fcda 460 res = xstrdup(type);
18eac5ba
MB
461 }
462
463 for (p = res; p && *p; p++)
464 *p = tolower((unsigned char) *p);
465 return res;
466}
467
2a4c734b
MB
468static void set_tt_data(struct blkdev_cxt *cxt, int col, int id, struct tt_line *ln)
469{
470 char buf[1024];
9cfe1b9c 471 char *p = NULL;
2a4c734b
MB
472
473 if (!cxt->st.st_rdev && (id == COL_OWNER || id == COL_GROUP ||
474 id == COL_MODE))
475 stat(cxt->filename, &cxt->st);
476
477 switch(id) {
478 case COL_NAME:
479 if (cxt->dm_name) {
480 snprintf(buf, sizeof(buf), "%s (%s)",
481 cxt->dm_name, cxt->name);
482 tt_line_set_data(ln, col, xstrdup(buf));
483 break;
484 }
485 case COL_KNAME:
486 tt_line_set_data(ln, col, xstrdup(cxt->name));
487 break;
488 case COL_OWNER:
489 {
490 struct passwd *pw = getpwuid(cxt->st.st_uid);
491 if (pw)
492 tt_line_set_data(ln, col, xstrdup(pw->pw_name));
493 break;
494 }
495 case COL_GROUP:
496 {
497 struct group *gr = getgrgid(cxt->st.st_gid);
498 if (gr)
499 tt_line_set_data(ln, col, xstrdup(gr->gr_name));
500 break;
501 }
502 case COL_MODE:
503 {
504 char md[11];
505 strmode(cxt->st.st_mode, md);
506 tt_line_set_data(ln, col, xstrdup(md));
507 break;
508 }
509 case COL_MAJMIN:
6ea1bdd3
KZ
510 if ((lsblk->tt->flags & TT_FL_RAW) ||
511 (lsblk->tt->flags & TT_FL_EXPORT))
2a4c734b
MB
512 snprintf(buf, sizeof(buf), "%u:%u", cxt->maj, cxt->min);
513 else
514 snprintf(buf, sizeof(buf), "%3u:%-3u", cxt->maj, cxt->min);
515 tt_line_set_data(ln, col, xstrdup(buf));
516 break;
517 case COL_FSTYPE:
518 probe_device(cxt);
519 if (cxt->fstype)
520 tt_line_set_data(ln, col, xstrdup(cxt->fstype));
521 break;
522 case COL_TARGET:
523 if (!cxt->nholders) {
524 p = get_device_mountpoint(cxt);
525 if (p)
526 tt_line_set_data(ln, col, p);
527 }
528 break;
529 case COL_LABEL:
530 probe_device(cxt);
531 if (cxt->label)
532 tt_line_set_data(ln, col, xstrdup(cxt->label));
533 break;
534 case COL_UUID:
535 probe_device(cxt);
536 if (cxt->uuid)
537 tt_line_set_data(ln, col, xstrdup(cxt->uuid));
538 break;
539 case COL_RO:
540 tt_line_set_data(ln, col, is_readonly_device(cxt) ?
541 xstrdup("1") : xstrdup("0"));
542 break;
627970a7 543 case COL_RM:
766ab9b0 544 p = sysfs_strdup(&cxt->sysfs, "removable");
2a4c734b 545 if (!p && cxt->parent)
766ab9b0 546 p = sysfs_strdup(&cxt->parent->sysfs, "removable");
2a4c734b
MB
547 if (p)
548 tt_line_set_data(ln, col, p);
549 break;
550 case COL_ROTA:
766ab9b0 551 p = sysfs_strdup(&cxt->sysfs, "queue/rotational");
2a4c734b
MB
552 if (p)
553 tt_line_set_data(ln, col, p);
554 break;
555 case COL_MODEL:
556 if (!cxt->partition && cxt->nslaves == 0) {
766ab9b0 557 p = sysfs_strdup(&cxt->sysfs, "device/model");
2a4c734b
MB
558 if (p)
559 tt_line_set_data(ln, col, p);
560 }
561 break;
562 case COL_SIZE:
563 if (cxt->size) {
564 if (lsblk->bytes) {
565 if (asprintf(&p, "%jd", cxt->size) < 0)
566 p = NULL;
567 } else
5d2a9849 568 p = size_to_human_string(SIZE_SUFFIX_1LETTER, cxt->size);
2a4c734b
MB
569 if (p)
570 tt_line_set_data(ln, col, p);
571 }
572 break;
01e487c0
MB
573 case COL_STATE:
574 if (!cxt->partition && !cxt->dm_name) {
575 p = sysfs_strdup(&cxt->sysfs, "device/state");
576 } else if (cxt->dm_name) {
577 int x = 0;
578 if (sysfs_read_int(&cxt->sysfs, "dm/suspended", &x) == 0)
579 p = x ? xstrdup("suspended") : xstrdup("running");
580 }
581 if (p)
582 tt_line_set_data(ln, col, p);
583 break;
2a4c734b 584 case COL_ALIOFF:
766ab9b0 585 p = sysfs_strdup(&cxt->sysfs, "alignment_offset");
2a4c734b
MB
586 if (p)
587 tt_line_set_data(ln, col, p);
588 break;
589 case COL_MINIO:
766ab9b0 590 p = sysfs_strdup(&cxt->sysfs, "queue/minimum_io_size");
2a4c734b
MB
591 if (p)
592 tt_line_set_data(ln, col, p);
593 break;
594 case COL_OPTIO:
766ab9b0 595 p = sysfs_strdup(&cxt->sysfs, "queue/optimal_io_size");
2a4c734b
MB
596 if (p)
597 tt_line_set_data(ln, col, p);
598 break;
599 case COL_PHYSEC:
766ab9b0 600 p = sysfs_strdup(&cxt->sysfs, "queue/physical_block_size");
2a4c734b
MB
601 if (p)
602 tt_line_set_data(ln, col, p);
603 break;
604 case COL_LOGSEC:
766ab9b0 605 p = sysfs_strdup(&cxt->sysfs, "queue/logical_block_size");
2a4c734b
MB
606 if (p)
607 tt_line_set_data(ln, col, p);
608 break;
609 case COL_SCHED:
610 p = get_scheduler(cxt);
611 if (p)
612 tt_line_set_data(ln, col, p);
613 break;
64c9e22a
MB
614 case COL_RQ_SIZE:
615 p = sysfs_strdup(&cxt->sysfs, "queue/nr_requests");
616 if (p)
617 tt_line_set_data(ln, col, p);
618 break;
18eac5ba
MB
619 case COL_TYPE:
620 p = get_type(cxt);
621 if (p)
622 tt_line_set_data(ln, col, p);
623 break;
2d232246 624 case COL_DALIGN:
766ab9b0 625 p = sysfs_strdup(&cxt->sysfs, "discard_alignment");
98055888 626 if (cxt->discard && p)
2d232246 627 tt_line_set_data(ln, col, p);
98055888
MP
628 else
629 tt_line_set_data(ln, col, "0");
2d232246
MP
630 break;
631 case COL_DGRAN:
9cfe1b9c
KZ
632 if (lsblk->bytes)
633 p = sysfs_strdup(&cxt->sysfs, "queue/discard_granularity");
634 else {
635 uint64_t x;
636
637 if (sysfs_read_u64(&cxt->sysfs,
638 "queue/discard_granularity", &x) == 0)
5d2a9849 639 p = size_to_human_string(SIZE_SUFFIX_1LETTER, x);
9cfe1b9c 640 }
2d232246
MP
641 if (p)
642 tt_line_set_data(ln, col, p);
643 break;
644 case COL_DMAX:
9cfe1b9c
KZ
645 if (lsblk->bytes)
646 p = sysfs_strdup(&cxt->sysfs, "queue/discard_max_bytes");
647 else {
648 uint64_t x;
649
650 if (sysfs_read_u64(&cxt->sysfs,
651 "queue/discard_max_bytes", &x) == 0)
5d2a9849 652 p = size_to_human_string(SIZE_SUFFIX_1LETTER, x);
9cfe1b9c 653 }
2d232246
MP
654 if (p)
655 tt_line_set_data(ln, col, p);
656 break;
657 case COL_DZERO:
766ab9b0 658 p = sysfs_strdup(&cxt->sysfs, "queue/discard_zeroes_data");
98055888 659 if (cxt->discard && p)
2d232246 660 tt_line_set_data(ln, col, p);
98055888
MP
661 else
662 tt_line_set_data(ln, col, "0");
2d232246 663 break;
2a4c734b
MB
664 };
665}
666
667static void print_device(struct blkdev_cxt *cxt, struct tt_line *tt_parent)
668{
669 int i;
670
671 cxt->tt_line = tt_add_line(lsblk->tt, tt_parent);
672
673 for (i = 0; i < ncolumns; i++)
674 set_tt_data(cxt, i, get_column_id(i), cxt->tt_line);
675}
676
677static int set_cxt(struct blkdev_cxt *cxt,
678 struct blkdev_cxt *parent,
679 const char *name,
680 int partition)
681{
766ab9b0 682 dev_t devno;
2a4c734b
MB
683
684 cxt->parent = parent;
685 cxt->name = xstrdup(name);
686 cxt->partition = partition;
687
688 cxt->filename = get_device_path(cxt);
270e66ec
MB
689 if (!cxt->filename) {
690 warnx(_("%s: failed to get device path"), name);
691 return -1;
692 }
2a4c734b 693
766ab9b0
KZ
694 devno = sysfs_devname_to_devno(name,
695 partition && parent ? parent->name : NULL);
270e66ec
MB
696 if (!devno) {
697 warnx(_("%s: unknown device name"), name);
698 return -1;
699 }
2a4c734b 700
270e66ec
MB
701 if (sysfs_init(&cxt->sysfs, devno, parent ? &parent->sysfs : NULL)) {
702 warnx(_("%s: failed to initialize sysfs handler"), name);
703 return -1;
704 }
766ab9b0
KZ
705
706 cxt->maj = major(devno);
707 cxt->min = minor(devno);
2a4c734b 708
90e9fcda
KZ
709 sysfs_read_u64(&cxt->sysfs, "size", &cxt->size); /* in sectors */
710 cxt->size <<= 9; /* in bytes */
711
712 sysfs_read_int(&cxt->sysfs, "queue/discard_granularity", &cxt->discard);
2a4c734b
MB
713
714 /* Ignore devices of zero size */
715 if (!lsblk->all_devices && cxt->size == 0)
716 return -1;
717
766ab9b0
KZ
718 if (is_dm(name)) {
719 cxt->dm_name = sysfs_strdup(&cxt->sysfs, "dm/name");
270e66ec
MB
720 if (!cxt->dm_name) {
721 warnx(_("%s: failed to get dm name"), name);
722 return -1;
723 }
766ab9b0
KZ
724 }
725 cxt->nholders = sysfs_count_dirents(&cxt->sysfs, "holders") +
726 sysfs_count_partitions(&cxt->sysfs, name);
2a4c734b 727
766ab9b0 728 cxt->nslaves = sysfs_count_dirents(&cxt->sysfs, "slaves");
2a4c734b
MB
729
730 return 0;
731}
732
733/*
734 * List devices (holders) mapped to device
735 */
736static int list_holders(struct blkdev_cxt *cxt)
737{
738 DIR *dir;
739 struct dirent *d;
740 struct blkdev_cxt holder = {};
741
742 assert(cxt);
2a4c734b 743
f31505fe
KZ
744 if (lsblk->nodeps)
745 return 0;
746
2a4c734b
MB
747 if (!cxt->nholders)
748 return 0;
749
750 /* Partitions */
766ab9b0 751 dir = sysfs_opendir(&cxt->sysfs, NULL);
2a4c734b
MB
752 if (!dir)
753 err(EXIT_FAILURE, _("failed to open device directory in sysfs"));
754
755 while ((d = xreaddir(dir))) {
766ab9b0 756 if (!sysfs_is_partition_dirent(dir, d, cxt->name))
2a4c734b
MB
757 continue;
758
270e66ec
MB
759 if (set_cxt(&holder, cxt, d->d_name, 1)) {
760 reset_blkdev_cxt(&holder);
761 continue;
762 }
2a4c734b
MB
763 print_device(&holder, cxt->tt_line);
764 list_holders(&holder);
765 reset_blkdev_cxt(&holder);
766 }
767 closedir(dir);
768
769 /* Holders */
766ab9b0 770 dir = sysfs_opendir(&cxt->sysfs, "holders");
2a4c734b
MB
771 if (!dir)
772 return 0;
773
774 while ((d = xreaddir(dir))) {
270e66ec
MB
775 if (set_cxt(&holder, cxt, d->d_name, 0)) {
776 reset_blkdev_cxt(&holder);
777 continue;
778 }
2a4c734b
MB
779 print_device(&holder, cxt->tt_line);
780 list_holders(&holder);
781 reset_blkdev_cxt(&holder);
782 }
783 closedir(dir);
784
785 return 0;
786}
787
788/* Iterate top-level devices in sysfs */
789static int iterate_block_devices(void)
790{
791 DIR *dir;
792 struct dirent *d;
793 struct blkdev_cxt cxt = {};
794
795 if (!(dir = opendir(_PATH_SYS_BLOCK)))
796 return EXIT_FAILURE;
797
798 while ((d = xreaddir(dir))) {
2a4c734b
MB
799 if (set_cxt(&cxt, NULL, d->d_name, 0))
800 goto next;
801
802 /* Skip devices in the middle of dependence tree */
803 if (cxt.nslaves > 0)
804 goto next;
805
806 if (!lsblk->all_devices && is_maj_excluded(cxt.maj))
807 goto next;
808
809 print_device(&cxt, NULL);
810 list_holders(&cxt);
811 next:
812 reset_blkdev_cxt(&cxt);
813 }
814
815 closedir(dir);
816
817 return EXIT_SUCCESS;
818}
819
820static int process_one_device(char *devname)
821{
822 struct blkdev_cxt parent = {}, cxt = {};
823 struct stat st;
270e66ec 824 char buf[PATH_MAX + 1], *diskname = NULL;
2a4c734b 825 dev_t disk = 0;
270e66ec 826 int status = EXIT_FAILURE;
2a4c734b
MB
827
828 if (stat(devname, &st) || !S_ISBLK(st.st_mode)) {
829 warnx(_("%s: not a block device"), devname);
830 return EXIT_FAILURE;
831 }
832 if (blkid_devno_to_wholedisk(st.st_rdev, buf, sizeof(buf), &disk)) {
1117f18f 833 warn(_("%s: failed to get whole-disk device number"), devname);
2a4c734b
MB
834 return EXIT_FAILURE;
835 }
270e66ec 836 if (st.st_rdev == disk) {
2a4c734b
MB
837 /*
838 * unpartitioned device
839 */
270e66ec
MB
840 if (set_cxt(&cxt, NULL, buf, 0))
841 goto leave;
842 } else {
2a4c734b
MB
843 /*
844 * Parititioned, read sysfs name of the device
845 */
eb9a65bb 846 ssize_t len;
270e66ec 847 char path[PATH_MAX], *name;
2a4c734b 848
270e66ec
MB
849 if (!sysfs_devno_path(st.st_rdev, path, sizeof(path))) {
850 warn(_("failed to compose sysfs path for %s"), devname);
851 goto leave;
852 }
2a4c734b 853
766ab9b0 854 diskname = xstrdup(buf);
cc6b1d11 855 len = readlink(path, buf, PATH_MAX);
2a4c734b
MB
856 if (len < 0) {
857 warn(_("%s: failed to read link"), path);
270e66ec 858 goto leave;
2a4c734b
MB
859 }
860 buf[len] = '\0';
861
862 /* sysfs device name */
863 name = strrchr(buf, '/') + 1;
864
270e66ec
MB
865 if (set_cxt(&parent, NULL, diskname, 0))
866 goto leave;
867 if (set_cxt(&cxt, &parent, name, 1))
868 goto leave;
2a4c734b
MB
869 }
870
871 print_device(&cxt, NULL);
872 list_holders(&cxt);
270e66ec
MB
873 status = EXIT_SUCCESS;
874leave:
875 free(diskname);
2a4c734b
MB
876 reset_blkdev_cxt(&cxt);
877
878 if (st.st_rdev != disk)
879 reset_blkdev_cxt(&parent);
880
270e66ec 881 return status;
2a4c734b
MB
882}
883
884static void parse_excludes(const char *str)
885{
886 nexcludes = 0;
887
888 while (str && *str) {
889 char *end = NULL;
ed34643c 890 unsigned long n;
2a4c734b
MB
891
892 errno = 0;
893 n = strtoul(str, &end, 10);
894
895 if (end == str || (errno != 0 && (n == ULONG_MAX || n == 0)))
896 err(EXIT_FAILURE, _("failed to parse list '%s'"), str);
897 excludes[nexcludes++] = n;
898
899 if (nexcludes == ARRAY_SIZE(excludes))
f14f02f5 900 /* TRANSLATORS: The standard value for %d is 256. */
2a4c734b
MB
901 errx(EXIT_FAILURE, _("the list of excluded devices is "
902 "too large (limit is %d devices)"),
903 (int)ARRAY_SIZE(excludes));
904 str = end && *end ? end + 1 : NULL;
905 }
906}
907
abafd686 908static void __attribute__((__noreturn__)) help(FILE *out)
2a4c734b
MB
909{
910 int i;
911
912 fprintf(out, _(
913 "\nUsage:\n"
914 " %s [options] [<device> ...]\n"), program_invocation_short_name);
915
916 fprintf(out, _(
917 "\nOptions:\n"
918 " -a, --all print all devices\n"
919 " -b, --bytes print SIZE in bytes rather than in human readable format\n"
f31505fe 920 " -d, --nodeps don't print slaves or holders\n"
2d232246 921 " -D, --discard print discard capabilities\n"
2a4c734b
MB
922 " -e, --exclude <list> exclude devices by major number (default: RAM disks)\n"
923 " -f, --fs output info about filesystems\n"
924 " -h, --help usage information (this)\n"
925 " -i, --ascii use ascii characters only\n"
926 " -m, --perms output info about permissions\n"
927 " -l, --list use list format ouput\n"
928 " -n, --noheadings don't print headings\n"
929 " -o, --output <list> output columns\n"
6ea1bdd3
KZ
930 " -P, --pairs use key=\"value\" output format\n"
931 " -r, --raw use raw output format\n"
2a4c734b
MB
932 " -t, --topology output info about topology\n"));
933
934 fprintf(out, _("\nAvailable columns:\n"));
935
936 for (i = 0; i < __NCOLUMNS; i++)
d015794e 937 fprintf(out, " %10s %s\n", infos[i].name, _(infos[i].help));
2a4c734b 938
f77fa578 939 fprintf(out, _("\nFor more information see lsblk(8).\n"));
2a4c734b
MB
940
941 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
942}
943
abafd686 944static void __attribute__((__noreturn__))
2a4c734b
MB
945errx_mutually_exclusive(const char *opts)
946{
947 errx(EXIT_FAILURE, "%s %s", opts, _("options are mutually exclusive"));
948}
949
f65b3bb6
SK
950static void check_sysdevblock(void)
951{
952 if (access(_PATH_SYS_DEVBLOCK, R_OK) != 0)
953 err(EXIT_FAILURE, _("failed to access sysfs directory: %s"),
954 _PATH_SYS_DEVBLOCK);
955}
956
2a4c734b
MB
957int main(int argc, char *argv[])
958{
959 struct lsblk _ls;
960 int tt_flags = TT_FL_TREE;
961 int i, c, status = EXIT_FAILURE;
962
6c7d5ae9 963 static const struct option longopts[] = {
2a4c734b 964 { "all", 0, 0, 'a' },
f31505fe
KZ
965 { "bytes", 0, 0, 'b' },
966 { "nodeps", 0, 0, 'd' },
2d232246 967 { "discard", 0, 0, 'D' },
2a4c734b
MB
968 { "help", 0, 0, 'h' },
969 { "output", 1, 0, 'o' },
970 { "perms", 0, 0, 'm' },
971 { "noheadings", 0, 0, 'n' },
972 { "list", 0, 0, 'l' },
973 { "ascii", 0, 0, 'i' },
974 { "raw", 0, 0, 'r' },
975 { "fs", 0, 0, 'f' },
976 { "exclude", 1, 0, 'e' },
977 { "topology", 0, 0, 't' },
6ea1bdd3 978 { "pairs", 0, 0, 'P' },
2a4c734b
MB
979 { NULL, 0, 0, 0 },
980 };
981
982 setlocale(LC_ALL, "");
983 bindtextdomain(PACKAGE, LOCALEDIR);
984 textdomain(PACKAGE);
985
986 lsblk = &_ls;
987 memset(lsblk, 0, sizeof(*lsblk));
988
6ea1bdd3 989 while((c = getopt_long(argc, argv, "abdDe:fhlnmo:Pirt", longopts, NULL)) != -1) {
2a4c734b
MB
990 switch(c) {
991 case 'a':
992 lsblk->all_devices = 1;
993 break;
994 case 'b':
995 lsblk->bytes = 1;
996 break;
f31505fe
KZ
997 case 'd':
998 lsblk->nodeps = 1;
999 break;
2d232246
MP
1000 case 'D':
1001 columns[ncolumns++] = COL_NAME;
1002 columns[ncolumns++] = COL_DALIGN;
1003 columns[ncolumns++] = COL_DGRAN;
1004 columns[ncolumns++] = COL_DMAX;
1005 columns[ncolumns++] = COL_DZERO;
1006 break;
2a4c734b
MB
1007 case 'e':
1008 parse_excludes(optarg);
1009 break;
1010 case 'h':
1011 help(stdout);
1012 break;
1013 case 'l':
6ea1bdd3
KZ
1014 if ((tt_flags & TT_FL_RAW)|| (tt_flags & TT_FL_EXPORT))
1015 errx_mutually_exclusive("--{raw,list,export}");
2a4c734b
MB
1016
1017 tt_flags &= ~TT_FL_TREE; /* disable the default */
1018 break;
1019 case 'n':
1020 tt_flags |= TT_FL_NOHEADINGS;
1021 break;
1022 case 'o':
c87638ad 1023 ncolumns = string_to_idarray(optarg,
bdc3ed66
KZ
1024 columns, ARRAY_SIZE(columns),
1025 column_name_to_id);
1026 if (ncolumns < 0)
2a4c734b
MB
1027 return EXIT_FAILURE;
1028 break;
6ea1bdd3
KZ
1029 case 'P':
1030 tt_flags |= TT_FL_EXPORT;
1031 tt_flags &= ~TT_FL_TREE; /* disable the default */
1032 break;
2a4c734b 1033 case 'i':
449f9336 1034 tt_flags |= TT_FL_ASCII;
2a4c734b
MB
1035 break;
1036 case 'r':
1037 tt_flags &= ~TT_FL_TREE; /* disable the default */
1038 tt_flags |= TT_FL_RAW; /* enable raw */
1039 break;
1040 case 'f':
1041 columns[ncolumns++] = COL_NAME;
1042 columns[ncolumns++] = COL_FSTYPE;
1043 columns[ncolumns++] = COL_LABEL;
1044 columns[ncolumns++] = COL_TARGET;
1045 break;
1046 case 'm':
1047 columns[ncolumns++] = COL_NAME;
1048 columns[ncolumns++] = COL_SIZE;
1049 columns[ncolumns++] = COL_OWNER;
1050 columns[ncolumns++] = COL_GROUP;
1051 columns[ncolumns++] = COL_MODE;
1052 break;
1053 case 't':
1054 columns[ncolumns++] = COL_NAME;
1055 columns[ncolumns++] = COL_ALIOFF;
1056 columns[ncolumns++] = COL_MINIO;
1057 columns[ncolumns++] = COL_OPTIO;
1058 columns[ncolumns++] = COL_PHYSEC;
1059 columns[ncolumns++] = COL_LOGSEC;
1060 columns[ncolumns++] = COL_ROTA;
1061 columns[ncolumns++] = COL_SCHED;
64c9e22a 1062 columns[ncolumns++] = COL_RQ_SIZE;
2a4c734b
MB
1063 break;
1064 default:
1065 help(stderr);
1066 }
1067 }
1068
f65b3bb6
SK
1069 check_sysdevblock();
1070
2a4c734b
MB
1071 if (!ncolumns) {
1072 columns[ncolumns++] = COL_NAME;
1073 columns[ncolumns++] = COL_MAJMIN;
627970a7 1074 columns[ncolumns++] = COL_RM;
2a4c734b
MB
1075 columns[ncolumns++] = COL_SIZE;
1076 columns[ncolumns++] = COL_RO;
18eac5ba 1077 columns[ncolumns++] = COL_TYPE;
2a4c734b
MB
1078 columns[ncolumns++] = COL_TARGET;
1079 }
1080
1081 if (nexcludes && lsblk->all_devices)
1082 errx_mutually_exclusive("--{all,exclude}");
1083 else if (!nexcludes)
1084 excludes[nexcludes++] = 1; /* default: ignore RAM disks */
1085 /*
1086 * initialize output columns
1087 */
1088 if (!(lsblk->tt = tt_new_table(tt_flags)))
1089 errx(EXIT_FAILURE, _("failed to initialize output table"));
1090
1091 for (i = 0; i < ncolumns; i++) {
1092 struct colinfo *ci = get_column_info(i);
1093 int fl = ci->flags;
1094
1095 if (!(tt_flags & TT_FL_TREE) && get_column_id(i) == COL_NAME)
1096 fl &= ~TT_FL_TREE;
1097
1098 if (!tt_define_column(lsblk->tt, ci->name, ci->whint, fl)) {
1099 warn(_("failed to initialize output column"));
1100 goto leave;
1101 }
1102 }
1103
1104 if (optind == argc)
1105 status = iterate_block_devices();
1106 else while (optind < argc)
1107 status = process_one_device(argv[optind++]);
1108
1109 tt_print_table(lsblk->tt);
1110
1111leave:
1112 tt_free_table(lsblk->tt);
1113 return status;
1114}