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