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