]> git.ipfire.org Git - thirdparty/util-linux.git/blob - misc-utils/lsblk.c
tests: logger with socat device
[thirdparty/util-linux.git] / misc-utils / lsblk.c
1 /*
2 * lsblk(8) - list block devices
3 *
4 * Copyright (C) 2010,2011,2012 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 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.
21 */
22
23 #include <stdio.h>
24 #include <errno.h>
25 #include <getopt.h>
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>
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 #include <ctype.h>
41
42 #include <blkid.h>
43 #include <libmount.h>
44 #include <libsmartcols.h>
45
46 #ifdef HAVE_LIBUDEV
47 #include <libudev.h>
48 #endif
49
50 #include <assert.h>
51
52 #include "c.h"
53 #include "pathnames.h"
54 #include "blkdev.h"
55 #include "canonicalize.h"
56 #include "nls.h"
57 #include "xalloc.h"
58 #include "strutils.h"
59 #include "at.h"
60 #include "sysfs.h"
61 #include "closestream.h"
62 #include "mangle.h"
63 #include "optutils.h"
64
65 #include "debug.h"
66
67 UL_DEBUG_DEFINE_MASK(lsblk);
68 UL_DEBUG_DEFINE_MASKNAMES(lsblk) = UL_DEBUG_EMPTY_MASKNAMES;
69
70 #define LSBLK_DEBUG_INIT (1 << 1)
71 #define LSBLK_DEBUG_FILTER (1 << 2)
72 #define LSBLK_DEBUG_DEV (1 << 3)
73 #define LSBLK_DEBUG_CXT (1 << 4)
74 #define LSBLK_DEBUG_ALL 0xFFFF
75
76 #define DBG(m, x) __UL_DBG(lsblk, LSBLK_DEBUG_, m, x)
77 #define ON_DBG(m, x) __UL_DBG_CALL(lsblk, LSBLK_DEBUG_, m, x)
78
79
80 #define LSBLK_EXIT_SOMEOK 64
81 #define LSBLK_EXIT_ALLFAILED 32
82
83 /* column IDs */
84 enum {
85 COL_NAME = 0,
86 COL_KNAME,
87 COL_MAJMIN,
88 COL_FSTYPE,
89 COL_TARGET,
90 COL_LABEL,
91 COL_UUID,
92 COL_PARTTYPE,
93 COL_PARTLABEL,
94 COL_PARTUUID,
95 COL_PARTFLAGS,
96 COL_RA,
97 COL_RO,
98 COL_RM,
99 COL_HOTPLUG,
100 COL_MODEL,
101 COL_SERIAL,
102 COL_SIZE,
103 COL_STATE,
104 COL_OWNER,
105 COL_GROUP,
106 COL_MODE,
107 COL_ALIOFF,
108 COL_MINIO,
109 COL_OPTIO,
110 COL_PHYSEC,
111 COL_LOGSEC,
112 COL_ROTA,
113 COL_SCHED,
114 COL_RQ_SIZE,
115 COL_TYPE,
116 COL_DALIGN,
117 COL_DGRAN,
118 COL_DMAX,
119 COL_DZERO,
120 COL_WSAME,
121 COL_WWN,
122 COL_RAND,
123 COL_PKNAME,
124 COL_HCTL,
125 COL_TRANSPORT,
126 COL_SUBSYS,
127 COL_REV,
128 COL_VENDOR
129 };
130
131 /* basic table settings */
132 enum {
133 LSBLK_ASCII = (1 << 0),
134 LSBLK_RAW = (1 << 1),
135 LSBLK_NOHEADINGS = (1 << 2),
136 LSBLK_EXPORT = (1 << 3),
137 LSBLK_TREE = (1 << 4),
138 };
139
140 enum {
141 SORT_STRING = 0, /* default is to use scols_cell_get_data() */
142 SORT_U64 = 1 /* use private pointer from scols_cell_get_userdata() */
143 };
144
145 /* column names */
146 struct colinfo {
147 const char *name; /* header */
148 double whint; /* width hint (N < 1 is in percent of termwidth) */
149 int flags; /* SCOLS_FL_* */
150 const char *help;
151
152 int sort_type; /* SORT_* */
153 };
154
155 /* columns descriptions */
156 static struct colinfo infos[] = {
157 [COL_NAME] = { "NAME", 0.25, SCOLS_FL_TREE | SCOLS_FL_NOEXTREMES, N_("device name") },
158 [COL_KNAME] = { "KNAME", 0.3, 0, N_("internal kernel device name") },
159 [COL_PKNAME] = { "PKNAME", 0.3, 0, N_("internal parent kernel device name") },
160 [COL_MAJMIN] = { "MAJ:MIN", 6, 0, N_("major:minor device number"), SORT_U64 },
161 [COL_FSTYPE] = { "FSTYPE", 0.1, SCOLS_FL_TRUNC, N_("filesystem type") },
162 [COL_TARGET] = { "MOUNTPOINT", 0.10, SCOLS_FL_TRUNC, N_("where the device is mounted") },
163 [COL_LABEL] = { "LABEL", 0.1, 0, N_("filesystem LABEL") },
164 [COL_UUID] = { "UUID", 36, 0, N_("filesystem UUID") },
165
166 [COL_PARTTYPE] = { "PARTTYPE", 36, 0, N_("partition type UUID") },
167 [COL_PARTLABEL] = { "PARTLABEL", 0.1, 0, N_("partition LABEL") },
168 [COL_PARTUUID] = { "PARTUUID", 36, 0, N_("partition UUID") },
169 [COL_PARTFLAGS] = { "PARTFLAGS", 36, 0, N_("partition flags") },
170
171 [COL_RA] = { "RA", 3, SCOLS_FL_RIGHT, N_("read-ahead of the device"), SORT_U64 },
172 [COL_RO] = { "RO", 1, SCOLS_FL_RIGHT, N_("read-only device") },
173 [COL_RM] = { "RM", 1, SCOLS_FL_RIGHT, N_("removable device") },
174 [COL_HOTPLUG]= { "HOTPLUG", 1, SCOLS_FL_RIGHT, N_("removable or hotplug device (usb, pcmcia, ...)") },
175 [COL_ROTA] = { "ROTA", 1, SCOLS_FL_RIGHT, N_("rotational device") },
176 [COL_RAND] = { "RAND", 1, SCOLS_FL_RIGHT, N_("adds randomness") },
177 [COL_MODEL] = { "MODEL", 0.1, SCOLS_FL_TRUNC, N_("device identifier") },
178 [COL_SERIAL] = { "SERIAL", 0.1, SCOLS_FL_TRUNC, N_("disk serial number") },
179 [COL_SIZE] = { "SIZE", 5, SCOLS_FL_RIGHT, N_("size of the device"), SORT_U64 },
180 [COL_STATE] = { "STATE", 7, SCOLS_FL_TRUNC, N_("state of the device") },
181 [COL_OWNER] = { "OWNER", 0.1, SCOLS_FL_TRUNC, N_("user name"), },
182 [COL_GROUP] = { "GROUP", 0.1, SCOLS_FL_TRUNC, N_("group name") },
183 [COL_MODE] = { "MODE", 10, 0, N_("device node permissions") },
184 [COL_ALIOFF] = { "ALIGNMENT", 6, SCOLS_FL_RIGHT, N_("alignment offset"), SORT_U64 },
185 [COL_MINIO] = { "MIN-IO", 6, SCOLS_FL_RIGHT, N_("minimum I/O size"), SORT_U64 },
186 [COL_OPTIO] = { "OPT-IO", 6, SCOLS_FL_RIGHT, N_("optimal I/O size"), SORT_U64 },
187 [COL_PHYSEC] = { "PHY-SEC", 7, SCOLS_FL_RIGHT, N_("physical sector size"), SORT_U64 },
188 [COL_LOGSEC] = { "LOG-SEC", 7, SCOLS_FL_RIGHT, N_("logical sector size"), SORT_U64 },
189 [COL_SCHED] = { "SCHED", 0.1, 0, N_("I/O scheduler name") },
190 [COL_RQ_SIZE]= { "RQ-SIZE", 5, SCOLS_FL_RIGHT, N_("request queue size"), SORT_U64 },
191 [COL_TYPE] = { "TYPE", 4, 0, N_("device type") },
192 [COL_DALIGN] = { "DISC-ALN", 6, SCOLS_FL_RIGHT, N_("discard alignment offset"), SORT_U64 },
193 [COL_DGRAN] = { "DISC-GRAN", 6, SCOLS_FL_RIGHT, N_("discard granularity"), SORT_U64 },
194 [COL_DMAX] = { "DISC-MAX", 6, SCOLS_FL_RIGHT, N_("discard max bytes"), SORT_U64 },
195 [COL_DZERO] = { "DISC-ZERO", 1, SCOLS_FL_RIGHT, N_("discard zeroes data") },
196 [COL_WSAME] = { "WSAME", 6, SCOLS_FL_RIGHT, N_("write same max bytes"), SORT_U64 },
197 [COL_WWN] = { "WWN", 18, 0, N_("unique storage identifier") },
198 [COL_HCTL] = { "HCTL", 10, 0, N_("Host:Channel:Target:Lun for SCSI") },
199 [COL_TRANSPORT] = { "TRAN", 6, 0, N_("device transport type") },
200 [COL_SUBSYS] = { "SUBSYSTEMS", 0.1, SCOLS_FL_NOEXTREMES, N_("de-duplicated chain of subsystems") },
201 [COL_REV] = { "REV", 4, SCOLS_FL_RIGHT, N_("device revision") },
202 [COL_VENDOR] = { "VENDOR", 0.1, SCOLS_FL_TRUNC, N_("device vendor") },
203 };
204
205 struct lsblk {
206 struct libscols_table *table; /* output table */
207 struct libscols_column *sort_col;/* sort output by this colum */
208 int sort_id;
209
210 unsigned int all_devices:1; /* print all devices, including empty */
211 unsigned int bytes:1; /* print SIZE in bytes */
212 unsigned int inverse:1; /* print inverse dependencies */
213 unsigned int nodeps:1; /* don't print slaves/holders */
214 unsigned int scsi:1; /* print only device with HCTL (SCSI) */
215 unsigned int paths:1; /* print devnames with "/dev" prefix */
216 };
217
218 struct lsblk *lsblk; /* global handler */
219
220 /* columns[] array specifies all currently wanted output column. The columns
221 * are defined by infos[] array and you can specify (on command line) each
222 * column twice. That's enough, dynamically allocated array of the columns is
223 * unnecessary overkill and over-engineering in this case */
224 static int columns[ARRAY_SIZE(infos) * 2];
225 static int ncolumns;
226
227 static inline size_t err_columns_index(size_t arysz, size_t idx)
228 {
229 if (idx >= arysz)
230 errx(EXIT_FAILURE, _("too many columns specified, "
231 "the limit is %zu columns"),
232 arysz - 1);
233 return idx;
234 }
235
236 #define add_column(ary, n, id) \
237 ((ary)[ err_columns_index(ARRAY_SIZE(ary), (n)) ] = (id))
238
239 static int excludes[256];
240 static size_t nexcludes;
241
242 static int includes[256];
243 static size_t nincludes;
244
245 static struct libmnt_table *mtab, *swaps;
246 static struct libmnt_cache *mntcache;
247
248 #ifdef HAVE_LIBUDEV
249 struct udev *udev;
250 #endif
251
252 struct blkdev_cxt {
253 struct blkdev_cxt *parent;
254
255 struct libscols_line *scols_line;
256 struct stat st;
257
258 char *name; /* kernel name in /sys/block */
259 char *dm_name; /* DM name (dm/block) */
260
261 char *filename; /* path to device node */
262
263 struct sysfs_cxt sysfs;
264
265 int partition; /* is partition? TRUE/FALSE */
266
267 int probed; /* already probed */
268 char *fstype; /* detected fs, NULL or "?" if cannot detect */
269 char *uuid; /* filesystem UUID (or stack uuid) */
270 char *label; /* filesystem label */
271 char *parttype; /* partiton type UUID */
272 char *partuuid; /* partition UUID */
273 char *partlabel; /* partiton label */
274 char *partflags; /* partition flags */
275 char *wwn; /* storage WWN */
276 char *serial; /* disk serial number */
277
278 int npartitions; /* # of partitions this device has */
279 int nholders; /* # of devices mapped directly to this device
280 * /sys/block/.../holders */
281 int nslaves; /* # of devices this device maps to */
282 int maj, min; /* devno */
283 int discard; /* supports discard */
284
285 uint64_t size; /* device size */
286 };
287
288 static void lsblk_init_debug(void)
289 {
290 __UL_INIT_DEBUG(lsblk, LSBLK_DEBUG_, 0, LSBLK_DEBUG);
291 }
292
293 static int is_maj_excluded(int maj)
294 {
295 size_t i;
296
297 assert(ARRAY_SIZE(excludes) > nexcludes);
298
299 if (!nexcludes)
300 return 0; /* filter not enabled, device not exluded */
301
302 for (i = 0; i < nexcludes; i++) {
303 if (excludes[i] == maj) {
304 DBG(FILTER, ul_debug("exclude: maj=%d", maj));
305 return 1;
306 }
307 }
308 return 0;
309 }
310
311 static int is_maj_included(int maj)
312 {
313 size_t i;
314
315 assert(ARRAY_SIZE(includes) > nincludes);
316
317 if (!nincludes)
318 return 1; /* filter not enabled, device is included */
319
320 for (i = 0; i < nincludes; i++) {
321 if (includes[i] == maj) {
322 DBG(FILTER, ul_debug("include: maj=%d", maj));
323 return 1;
324 }
325 }
326 return 0;
327 }
328
329 /* array with IDs of enabled columns */
330 static int get_column_id(int num)
331 {
332 assert(num < ncolumns);
333 assert(columns[num] < (int) ARRAY_SIZE(infos));
334 return columns[num];
335 }
336
337 static struct colinfo *get_column_info(int num)
338 {
339 return &infos[ get_column_id(num) ];
340 }
341
342 static int column_name_to_id(const char *name, size_t namesz)
343 {
344 size_t i;
345
346 for (i = 0; i < ARRAY_SIZE(infos); i++) {
347 const char *cn = infos[i].name;
348
349 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
350 return i;
351 }
352 warnx(_("unknown column: %s"), name);
353 return -1;
354 }
355
356 static int column_id_to_number(int id)
357 {
358 size_t i;
359
360 for (i = 0; i < (size_t) ncolumns; i++)
361 if (columns[i] == id)
362 return i;
363 return -1;
364 }
365
366 static void reset_blkdev_cxt(struct blkdev_cxt *cxt)
367 {
368 if (!cxt)
369 return;
370
371 DBG(CXT, ul_debugobj(cxt, "reset"));
372
373 free(cxt->name);
374 free(cxt->dm_name);
375 free(cxt->filename);
376 free(cxt->fstype);
377 free(cxt->uuid);
378 free(cxt->label);
379 free(cxt->parttype);
380 free(cxt->partuuid);
381 free(cxt->partlabel);
382 free(cxt->wwn);
383 free(cxt->serial);
384
385 sysfs_deinit(&cxt->sysfs);
386
387 memset(cxt, 0, sizeof(*cxt));
388 }
389
390 static int is_dm(const char *name)
391 {
392 return strncmp(name, "dm-", 3) ? 0 : 1;
393 }
394
395 static struct dirent *xreaddir(DIR *dp)
396 {
397 struct dirent *d;
398
399 assert(dp);
400
401 while ((d = readdir(dp))) {
402 if (!strcmp(d->d_name, ".") ||
403 !strcmp(d->d_name, ".."))
404 continue;
405
406 /* blacklist here? */
407 break;
408 }
409 return d;
410 }
411
412 static char *get_device_path(struct blkdev_cxt *cxt)
413 {
414 char path[PATH_MAX];
415
416 assert(cxt);
417 assert(cxt->name);
418
419 if (is_dm(cxt->name))
420 return canonicalize_dm_name(cxt->name);
421
422 snprintf(path, sizeof(path), "/dev/%s", cxt->name);
423 return xstrdup(path);
424 }
425
426 static int is_active_swap(const char *filename)
427 {
428 if (!swaps) {
429 swaps = mnt_new_table();
430 if (!swaps)
431 return 0;
432 if (!mntcache)
433 mntcache = mnt_new_cache();
434
435 mnt_table_set_cache(swaps, mntcache);
436 mnt_table_parse_swaps(swaps, NULL);
437 }
438
439 return mnt_table_find_srcpath(swaps, filename, MNT_ITER_BACKWARD) != 0;
440 }
441
442 static char *get_device_mountpoint(struct blkdev_cxt *cxt)
443 {
444 struct libmnt_fs *fs;
445 const char *fsroot;
446
447 assert(cxt);
448 assert(cxt->filename);
449
450 if (!mtab) {
451 mtab = mnt_new_table();
452 if (!mtab)
453 return NULL;
454 if (!mntcache)
455 mntcache = mnt_new_cache();
456
457 mnt_table_set_cache(mtab, mntcache);
458 mnt_table_parse_mtab(mtab, NULL);
459 }
460
461 /* Note that maj:min in /proc/self/mouninfo does not have to match with
462 * devno as returned by stat(), so we have to try devname too
463 */
464 fs = mnt_table_find_devno(mtab, makedev(cxt->maj, cxt->min), MNT_ITER_BACKWARD);
465 if (!fs)
466 fs = mnt_table_find_srcpath(mtab, cxt->filename, MNT_ITER_BACKWARD);
467 if (!fs)
468 return is_active_swap(cxt->filename) ? xstrdup("[SWAP]") : NULL;
469
470 /* found */
471 fsroot = mnt_fs_get_root(fs);
472 if (fsroot && strcmp(fsroot, "/") != 0) {
473 /* hmm.. we found bind mount or btrfs subvolume, let's try to
474 * get real FS root mountpoint */
475 struct libmnt_fs *rfs;
476 struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_BACKWARD);
477
478 mnt_table_set_iter(mtab, itr, fs);
479 while (mnt_table_next_fs(mtab, itr, &rfs) == 0) {
480 fsroot = mnt_fs_get_root(rfs);
481 if ((!fsroot || strcmp(fsroot, "/") == 0)
482 && mnt_fs_match_source(rfs, cxt->filename, mntcache)) {
483 fs = rfs;
484 break;
485 }
486 }
487 mnt_free_iter(itr);
488 }
489
490 DBG(DEV, ul_debugobj(cxt, "mountpoint: %s", mnt_fs_get_target(fs)));
491 return xstrdup(mnt_fs_get_target(fs));
492 }
493
494 #ifndef HAVE_LIBUDEV
495 static int get_udev_properties(struct blkdev_cxt *cxt
496 __attribute__((__unused__)))
497 {
498 return -1;
499 }
500 #else
501 static int get_udev_properties(struct blkdev_cxt *cxt)
502 {
503 struct udev_device *dev;
504
505 if (cxt->probed)
506 return 0; /* already done */
507
508 if (!udev)
509 udev = udev_new();
510 if (!udev)
511 return -1;
512
513 dev = udev_device_new_from_subsystem_sysname(udev, "block", cxt->name);
514 if (dev) {
515 const char *data;
516
517 if ((data = udev_device_get_property_value(dev, "ID_FS_LABEL_ENC"))) {
518 cxt->label = xstrdup(data);
519 unhexmangle_string(cxt->label);
520 }
521 if ((data = udev_device_get_property_value(dev, "ID_FS_UUID_ENC"))) {
522 cxt->uuid = xstrdup(data);
523 unhexmangle_string(cxt->uuid);
524 }
525 if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_NAME"))) {
526 cxt->partlabel = xstrdup(data);
527 unhexmangle_string(cxt->partlabel);
528 }
529 if ((data = udev_device_get_property_value(dev, "ID_FS_TYPE")))
530 cxt->fstype = xstrdup(data);
531 if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_TYPE")))
532 cxt->parttype = xstrdup(data);
533 if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_UUID")))
534 cxt->partuuid = xstrdup(data);
535 if ((data = udev_device_get_property_value(dev, "ID_PART_ENTRY_FLAGS")))
536 cxt->partflags = xstrdup(data);
537 if ((data = udev_device_get_property_value(dev, "ID_WWN")))
538 cxt->wwn = xstrdup(data);
539 if ((data = udev_device_get_property_value(dev, "ID_SERIAL_SHORT")))
540 cxt->serial = xstrdup(data);
541 udev_device_unref(dev);
542 cxt->probed = 1;
543 DBG(DEV, ul_debugobj(cxt, "%s: found udev properties", cxt->name));
544 }
545
546 return cxt->probed == 1 ? 0 : -1;
547
548 }
549 #endif /* HAVE_LIBUDEV */
550
551 static void probe_device(struct blkdev_cxt *cxt)
552 {
553 blkid_probe pr = NULL;
554
555 if (cxt->probed)
556 return;
557
558 if (!cxt->size)
559 return;
560
561 /* try udev DB */
562 if (get_udev_properties(cxt) == 0)
563 return; /* success */
564
565 cxt->probed = 1;
566
567 /* try libblkid (fallback) */
568 if (getuid() != 0)
569 return; /* no permissions to read from the device */
570
571 pr = blkid_new_probe_from_filename(cxt->filename);
572 if (!pr)
573 return;
574
575 blkid_probe_enable_superblocks(pr, 1);
576 blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_LABEL |
577 BLKID_SUBLKS_UUID |
578 BLKID_SUBLKS_TYPE);
579 blkid_probe_enable_partitions(pr, 1);
580 blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
581
582 if (!blkid_do_safeprobe(pr)) {
583 const char *data = NULL;
584
585 if (!blkid_probe_lookup_value(pr, "TYPE", &data, NULL))
586 cxt->fstype = xstrdup(data);
587 if (!blkid_probe_lookup_value(pr, "UUID", &data, NULL))
588 cxt->uuid = xstrdup(data);
589 if (!blkid_probe_lookup_value(pr, "LABEL", &data, NULL))
590 cxt->label = xstrdup(data);
591 if (!blkid_probe_lookup_value(pr, "PART_ENTRY_TYPE", &data, NULL))
592 cxt->parttype = xstrdup(data);
593 if (!blkid_probe_lookup_value(pr, "PART_ENTRY_UUID", &data, NULL))
594 cxt->partuuid = xstrdup(data);
595 if (!blkid_probe_lookup_value(pr, "PART_ENTRY_NAME", &data, NULL))
596 cxt->partlabel = xstrdup(data);
597 if (!blkid_probe_lookup_value(pr, "PART_ENTRY_FLAGS", &data, NULL))
598 cxt->partflags = xstrdup(data);
599 DBG(DEV, ul_debugobj(cxt, "%s: found blkid properties", cxt->name));
600 }
601
602 blkid_free_probe(pr);
603 return;
604 }
605
606 static int is_readonly_device(struct blkdev_cxt *cxt)
607 {
608 int fd, ro = 0;
609
610 if (sysfs_scanf(&cxt->sysfs, "ro", "%d", &ro) == 1)
611 return ro;
612
613 /* fallback if "ro" attribute does not exist */
614 fd = open(cxt->filename, O_RDONLY);
615 if (fd != -1) {
616 if (ioctl(fd, BLKROGET, &ro) != 0)
617 ro = 0;
618 close(fd);
619 }
620 return ro;
621 }
622
623 static char *get_scheduler(struct blkdev_cxt *cxt)
624 {
625 char *str = sysfs_strdup(&cxt->sysfs, "queue/scheduler");
626 char *p, *res = NULL;
627
628 if (!str)
629 return NULL;
630 p = strchr(str, '[');
631 if (p) {
632 res = p + 1;
633 p = strchr(res, ']');
634 if (p) {
635 *p = '\0';
636 res = xstrdup(res);
637 } else
638 res = NULL;
639 }
640 free(str);
641 return res;
642 }
643
644 static char *get_type(struct blkdev_cxt *cxt)
645 {
646 char *res = NULL, *p;
647
648 if (is_dm(cxt->name)) {
649 char *dm_uuid = sysfs_strdup(&cxt->sysfs, "dm/uuid");
650
651 /* The DM_UUID prefix should be set to subsystem owning
652 * the device - LVM, CRYPT, DMRAID, MPATH, PART */
653 if (dm_uuid) {
654 char *tmp = dm_uuid;
655 char *dm_uuid_prefix = strsep(&tmp, "-");
656
657 if (dm_uuid_prefix) {
658 /* kpartx hack to remove partition number */
659 if (strncasecmp(dm_uuid_prefix, "part", 4) == 0)
660 dm_uuid_prefix[4] = '\0';
661
662 res = xstrdup(dm_uuid_prefix);
663 }
664 }
665
666 free(dm_uuid);
667 if (!res)
668 /* No UUID or no prefix - just mark it as DM device */
669 res = xstrdup("dm");
670
671 } else if (!strncmp(cxt->name, "loop", 4)) {
672 res = xstrdup("loop");
673
674 } else if (!strncmp(cxt->name, "md", 2)) {
675 char *md_level = sysfs_strdup(&cxt->sysfs, "md/level");
676 res = md_level ? md_level : xstrdup("md");
677
678 } else {
679 const char *type = NULL;
680 int x = 0;
681
682 if (!sysfs_read_int(&cxt->sysfs, "device/type", &x))
683 type = blkdev_scsi_type_to_name(x);
684
685 if (!type)
686 type = cxt->partition ? "part" : "disk";
687 res = xstrdup(type);
688 }
689
690 for (p = res; p && *p; p++)
691 *p = tolower((unsigned char) *p);
692 return res;
693 }
694
695 /* Thanks to lsscsi code for idea of detection logic used here */
696 static char *get_transport(struct blkdev_cxt *cxt)
697 {
698 struct sysfs_cxt *sysfs = &cxt->sysfs;
699 char *attr = NULL;
700 const char *trans = NULL;
701
702 /* SCSI - Serial Peripheral Interface */
703 if (sysfs_scsi_host_is(sysfs, "spi"))
704 trans = "spi";
705
706 /* FC/FCoE - Fibre Channel / Fibre Channel over Ethernet */
707 else if (sysfs_scsi_host_is(sysfs, "fc")) {
708 attr = sysfs_scsi_host_strdup_attribute(sysfs, "fc", "symbolic_name");
709 if (!attr)
710 return NULL;
711 trans = strstr(attr, " over ") ? "fcoe" : "fc";
712 free(attr);
713 }
714
715 /* SAS - Serial Attached SCSI */
716 else if (sysfs_scsi_host_is(sysfs, "sas") ||
717 sysfs_scsi_has_attribute(sysfs, "sas_device"))
718 trans = "sas";
719
720
721 /* SBP - Serial Bus Protocol (FireWire) */
722 else if (sysfs_scsi_has_attribute(sysfs, "ieee1394_id"))
723 trans = "sbp";
724
725 /* iSCSI */
726 else if (sysfs_scsi_host_is(sysfs, "iscsi"))
727 trans ="iscsi";
728
729 /* USB - Universal Serial Bus */
730 else if (sysfs_scsi_path_contains(sysfs, "usb"))
731 trans = "usb";
732
733 /* ATA, SATA */
734 else if (sysfs_scsi_host_is(sysfs, "scsi")) {
735 attr = sysfs_scsi_host_strdup_attribute(sysfs, "scsi", "proc_name");
736 if (!attr)
737 return NULL;
738 if (!strncmp(attr, "ahci", 4) || !strncmp(attr, "sata", 4))
739 trans = "sata";
740 else if (strstr(attr, "ata"))
741 trans = "ata";
742 free(attr);
743 }
744
745 return trans ? xstrdup(trans) : NULL;
746 }
747
748 static char *get_subsystems(struct blkdev_cxt *cxt)
749 {
750 char path[PATH_MAX];
751 char *sub, *chain, *res = NULL;
752 size_t len = 0, last = 0;
753
754 chain = sysfs_get_devchain(&cxt->sysfs, path, sizeof(path));
755 if (!chain)
756 return NULL;
757
758 while (sysfs_next_subsystem(&cxt->sysfs, chain, &sub) == 0) {
759 size_t sz;
760
761 /* don't create "block:scsi:scsi", but "block:scsi" */
762 if (len && strcmp(res + last, sub) == 0)
763 continue;
764
765 sz = strlen(sub);
766 res = xrealloc(res, len + sz + 2);
767 if (len)
768 res[len++] = ':';
769
770 memcpy(res + len, sub, sz + 1);
771 last = len;
772 len += sz;
773 free(sub);
774 }
775
776 return res;
777 }
778
779
780 #define is_parsable(_l) (scols_table_is_raw((_l)->table) || \
781 scols_table_is_export((_l)->table))
782
783 static char *mk_name(const char *name)
784 {
785 char *p;
786 if (!name)
787 return NULL;
788 if (lsblk->paths)
789 xasprintf(&p, "/dev/%s", name);
790 else
791 p = xstrdup(name);
792 return p;
793 }
794
795 static char *mk_dm_name(const char *name)
796 {
797 char *p;
798 if (!name)
799 return NULL;
800 if (lsblk->paths)
801 xasprintf(&p, "/dev/mapper/%s", name);
802 else
803 p = xstrdup(name);
804 return p;
805 }
806
807 /* stores data to scols cell userdata (invisible and independent on output)
808 * to make the original values accessible for sort functions
809 */
810 static void set_sortdata_u64(struct libscols_line *ln, int col, uint64_t x)
811 {
812 struct libscols_cell *ce = scols_line_get_cell(ln, col);
813 uint64_t *data;
814
815 if (!ce)
816 return;
817 data = xmalloc(sizeof(uint64_t));
818 *data = x;
819 scols_cell_set_userdata(ce, data);
820 }
821
822 static void set_sortdata_u64_from_string(struct libscols_line *ln, int col, const char *str)
823 {
824 uint64_t x;
825
826 if (!str || sscanf(str, "%"SCNu64, &x) != 1)
827 return;
828
829 set_sortdata_u64(ln, col, x);
830 }
831
832 static void unref_sortdata(struct libscols_table *tb)
833 {
834 struct libscols_iter *itr;
835 struct libscols_line *ln;
836
837 if (!tb || !lsblk->sort_col)
838 return;
839 itr = scols_new_iter(SCOLS_ITER_FORWARD);
840 if (!itr)
841 return;
842 while (scols_table_next_line(tb, itr, &ln) == 0) {
843 struct libscols_cell *ce = scols_line_get_column_cell(ln,
844 lsblk->sort_col);
845 void *data = scols_cell_get_userdata(ce);
846 free(data);
847 }
848
849 scols_free_iter(itr);
850 }
851
852 static void set_scols_data(struct blkdev_cxt *cxt, int col, int id, struct libscols_line *ln)
853 {
854 int sort = 0, st_rc = 0;
855 char *str = NULL;
856
857 if (!cxt->st.st_rdev && (id == COL_OWNER || id == COL_GROUP ||
858 id == COL_MODE))
859 st_rc = stat(cxt->filename, &cxt->st);
860
861 if (lsblk->sort_id == id)
862 sort = 1;
863
864 switch(id) {
865 case COL_NAME:
866 str = cxt->dm_name ? mk_dm_name(cxt->dm_name) : mk_name(cxt->name);
867 break;
868 case COL_KNAME:
869 str = mk_name(cxt->name);
870 break;
871 case COL_PKNAME:
872 if (cxt->parent)
873 str = mk_name(cxt->parent->name);
874 break;
875 case COL_OWNER:
876 {
877 struct passwd *pw = st_rc ? NULL : getpwuid(cxt->st.st_uid);
878 if (pw)
879 str = xstrdup(pw->pw_name);
880 break;
881 }
882 case COL_GROUP:
883 {
884 struct group *gr = st_rc ? NULL : getgrgid(cxt->st.st_gid);
885 if (gr)
886 str = xstrdup(gr->gr_name);
887 break;
888 }
889 case COL_MODE:
890 {
891 char md[11];
892
893 if (!st_rc) {
894 strmode(cxt->st.st_mode, md);
895 str = xstrdup(md);
896 }
897 break;
898 }
899 case COL_MAJMIN:
900 if (is_parsable(lsblk))
901 xasprintf(&str, "%u:%u", cxt->maj, cxt->min);
902 else
903 xasprintf(&str, "%3u:%-3u", cxt->maj, cxt->min);
904 if (sort)
905 set_sortdata_u64(ln, col, makedev(cxt->maj, cxt->min));
906 break;
907 case COL_FSTYPE:
908 probe_device(cxt);
909 if (cxt->fstype)
910 str = xstrdup(cxt->fstype);
911 break;
912 case COL_TARGET:
913 if (!(cxt->nholders + cxt->npartitions))
914 str = get_device_mountpoint(cxt);
915 break;
916 case COL_LABEL:
917 probe_device(cxt);
918 if (cxt->label)
919 str = xstrdup(cxt->label);
920 break;
921 case COL_UUID:
922 probe_device(cxt);
923 if (cxt->uuid)
924 str = xstrdup(cxt->uuid);
925 break;
926 case COL_PARTTYPE:
927 probe_device(cxt);
928 if (cxt->parttype)
929 str = xstrdup(cxt->parttype);
930 break;
931 case COL_PARTLABEL:
932 probe_device(cxt);
933 if (cxt->partlabel)
934 str = xstrdup(cxt->partlabel);
935 break;
936 case COL_PARTUUID:
937 probe_device(cxt);
938 if (cxt->partuuid)
939 str = xstrdup(cxt->partuuid);
940 break;
941 case COL_PARTFLAGS:
942 probe_device(cxt);
943 if (cxt->partflags)
944 str = xstrdup(cxt->partflags);
945 break;
946 case COL_WWN:
947 get_udev_properties(cxt);
948 if (cxt->wwn)
949 str = xstrdup(cxt->wwn);
950 break;
951 case COL_RA:
952 str = sysfs_strdup(&cxt->sysfs, "queue/read_ahead_kb");
953 if (sort)
954 set_sortdata_u64_from_string(ln, col, str);
955 break;
956 case COL_RO:
957 str = xstrdup(is_readonly_device(cxt) ? "1" : "0");
958 break;
959 case COL_RM:
960 str = sysfs_strdup(&cxt->sysfs, "removable");
961 if (!str && cxt->sysfs.parent)
962 str = sysfs_strdup(cxt->sysfs.parent, "removable");
963 break;
964 case COL_HOTPLUG:
965 str = sysfs_is_hotpluggable(&cxt->sysfs) ? xstrdup("1") : xstrdup("0");
966 break;
967 case COL_ROTA:
968 str = sysfs_strdup(&cxt->sysfs, "queue/rotational");
969 break;
970 case COL_RAND:
971 str = sysfs_strdup(&cxt->sysfs, "queue/add_random");
972 break;
973 case COL_MODEL:
974 if (!cxt->partition && cxt->nslaves == 0)
975 str = sysfs_strdup(&cxt->sysfs, "device/model");
976 break;
977 case COL_SERIAL:
978 if (!cxt->partition && cxt->nslaves == 0) {
979 get_udev_properties(cxt);
980 if (cxt->serial)
981 str = xstrdup(cxt->serial);
982 }
983 break;
984 case COL_REV:
985 if (!cxt->partition && cxt->nslaves == 0)
986 str = sysfs_strdup(&cxt->sysfs, "device/rev");
987 break;
988 case COL_VENDOR:
989 if (!cxt->partition && cxt->nslaves == 0)
990 str = sysfs_strdup(&cxt->sysfs, "device/vendor");
991 break;
992 case COL_SIZE:
993 if (!cxt->size)
994 break;
995 if (lsblk->bytes)
996 xasprintf(&str, "%jd", cxt->size);
997 else
998 str = size_to_human_string(SIZE_SUFFIX_1LETTER, cxt->size);
999 if (sort)
1000 set_sortdata_u64(ln, col, cxt->size);
1001 break;
1002 case COL_STATE:
1003 if (!cxt->partition && !cxt->dm_name)
1004 str = sysfs_strdup(&cxt->sysfs, "device/state");
1005 else if (cxt->dm_name) {
1006 int x = 0;
1007 if (sysfs_read_int(&cxt->sysfs, "dm/suspended", &x) == 0)
1008 str = xstrdup(x ? "suspended" : "running");
1009 }
1010 break;
1011 case COL_ALIOFF:
1012 str = sysfs_strdup(&cxt->sysfs, "alignment_offset");
1013 if (sort)
1014 set_sortdata_u64_from_string(ln, col, str);
1015 break;
1016 case COL_MINIO:
1017 str = sysfs_strdup(&cxt->sysfs, "queue/minimum_io_size");
1018 if (sort)
1019 set_sortdata_u64_from_string(ln, col, str);
1020 break;
1021 case COL_OPTIO:
1022 str = sysfs_strdup(&cxt->sysfs, "queue/optimal_io_size");
1023 if (sort)
1024 set_sortdata_u64_from_string(ln, col, str);
1025 break;
1026 case COL_PHYSEC:
1027 str = sysfs_strdup(&cxt->sysfs, "queue/physical_block_size");
1028 if (sort)
1029 set_sortdata_u64_from_string(ln, col, str);
1030 break;
1031 case COL_LOGSEC:
1032 str = sysfs_strdup(&cxt->sysfs, "queue/logical_block_size");
1033 if (sort)
1034 set_sortdata_u64_from_string(ln, col, str);
1035 break;
1036 case COL_SCHED:
1037 str = get_scheduler(cxt);
1038 break;
1039 case COL_RQ_SIZE:
1040 str = sysfs_strdup(&cxt->sysfs, "queue/nr_requests");
1041 if (sort)
1042 set_sortdata_u64_from_string(ln, col, str);
1043 break;
1044 case COL_TYPE:
1045 str = get_type(cxt);
1046 break;
1047 case COL_HCTL:
1048 {
1049 int h, c, t, l;
1050 if (sysfs_scsi_get_hctl(&cxt->sysfs, &h, &c, &t, &l) == 0)
1051 xasprintf(&str, "%d:%d:%d:%d", h, c, t, l);
1052 break;
1053 }
1054 case COL_TRANSPORT:
1055 str = get_transport(cxt);
1056 break;
1057 case COL_SUBSYS:
1058 str = get_subsystems(cxt);
1059 break;
1060 case COL_DALIGN:
1061 if (cxt->discard)
1062 str = sysfs_strdup(&cxt->sysfs, "discard_alignment");
1063 if (!str)
1064 str = xstrdup("0");
1065 if (sort)
1066 set_sortdata_u64_from_string(ln, col, str);
1067 break;
1068 case COL_DGRAN:
1069 if (lsblk->bytes) {
1070 str = sysfs_strdup(&cxt->sysfs, "queue/discard_granularity");
1071 if (sort)
1072 set_sortdata_u64_from_string(ln, col, str);
1073 } else {
1074 uint64_t x;
1075 if (sysfs_read_u64(&cxt->sysfs,
1076 "queue/discard_granularity", &x) == 0) {
1077 str = size_to_human_string(SIZE_SUFFIX_1LETTER, x);
1078 if (sort)
1079 set_sortdata_u64(ln, col, x);
1080 }
1081 }
1082 break;
1083 case COL_DMAX:
1084 if (lsblk->bytes) {
1085 str = sysfs_strdup(&cxt->sysfs, "queue/discard_max_bytes");
1086 if (sort)
1087 set_sortdata_u64_from_string(ln, col, str);
1088 } else {
1089 uint64_t x;
1090 if (sysfs_read_u64(&cxt->sysfs,
1091 "queue/discard_max_bytes", &x) == 0) {
1092 str = size_to_human_string(SIZE_SUFFIX_1LETTER, x);
1093 if (sort)
1094 set_sortdata_u64(ln, col, x);
1095 }
1096 }
1097 break;
1098 case COL_DZERO:
1099 if (cxt->discard)
1100 str = sysfs_strdup(&cxt->sysfs, "queue/discard_zeroes_data");
1101 if (!str)
1102 str = xstrdup("0");
1103 break;
1104 case COL_WSAME:
1105 if (lsblk->bytes) {
1106 str = sysfs_strdup(&cxt->sysfs, "queue/write_same_max_bytes");
1107 if (sort)
1108 set_sortdata_u64_from_string(ln, col, str);
1109 } else {
1110 uint64_t x;
1111
1112 if (sysfs_read_u64(&cxt->sysfs,
1113 "queue/write_same_max_bytes", &x) == 0) {
1114 str = size_to_human_string(SIZE_SUFFIX_1LETTER, x);
1115 if (sort)
1116 set_sortdata_u64(ln, col, x);
1117 }
1118 }
1119 if (!str)
1120 str = xstrdup("0");
1121 break;
1122 };
1123
1124 if (str)
1125 scols_line_refer_data(ln, col, str);
1126 }
1127
1128 static void fill_table_line(struct blkdev_cxt *cxt, struct libscols_line *scols_parent)
1129 {
1130 int i;
1131
1132 cxt->scols_line = scols_table_new_line(lsblk->table, scols_parent);
1133 if (!cxt->scols_line)
1134 return;
1135
1136 for (i = 0; i < ncolumns; i++)
1137 set_scols_data(cxt, i, get_column_id(i), cxt->scols_line);
1138 }
1139
1140 static int set_cxt(struct blkdev_cxt *cxt,
1141 struct blkdev_cxt *parent,
1142 struct blkdev_cxt *wholedisk,
1143 const char *name)
1144 {
1145 dev_t devno;
1146
1147 DBG(CXT, ul_debugobj(cxt, "setting context for %s [parent=%p, wholedisk=%p]",
1148 name, parent, wholedisk));
1149
1150 cxt->parent = parent;
1151 cxt->name = xstrdup(name);
1152 cxt->partition = wholedisk != NULL;
1153
1154 cxt->filename = get_device_path(cxt);
1155 if (!cxt->filename) {
1156 warnx(_("%s: failed to get device path"), name);
1157 return -1;
1158 }
1159 DBG(CXT, ul_debugobj(cxt, "%s: filename=%s", cxt->name, cxt->filename));
1160
1161 devno = sysfs_devname_to_devno(name, wholedisk ? wholedisk->name : NULL);
1162
1163 if (!devno) {
1164 warnx(_("%s: unknown device name"), name);
1165 return -1;
1166 }
1167
1168 if (lsblk->inverse) {
1169 if (sysfs_init(&cxt->sysfs, devno, wholedisk ? &wholedisk->sysfs : NULL)) {
1170 warnx(_("%s: failed to initialize sysfs handler"), name);
1171 return -1;
1172 }
1173 if (parent)
1174 parent->sysfs.parent = &cxt->sysfs;
1175 } else {
1176 if (sysfs_init(&cxt->sysfs, devno, parent ? &parent->sysfs : NULL)) {
1177 warnx(_("%s: failed to initialize sysfs handler"), name);
1178 return -1;
1179 }
1180 }
1181
1182 cxt->maj = major(devno);
1183 cxt->min = minor(devno);
1184 cxt->size = 0;
1185
1186 if (sysfs_read_u64(&cxt->sysfs, "size", &cxt->size) == 0) /* in sectors */
1187 cxt->size <<= 9; /* in bytes */
1188
1189 if (sysfs_read_int(&cxt->sysfs,
1190 "queue/discard_granularity", &cxt->discard) != 0)
1191 cxt->discard = 0;
1192
1193 /* Ignore devices of zero size */
1194 if (!lsblk->all_devices && cxt->size == 0) {
1195 DBG(CXT, ul_debugobj(cxt, "zero size device -- ignore"));
1196 return -1;
1197 }
1198 if (is_dm(name)) {
1199 cxt->dm_name = sysfs_strdup(&cxt->sysfs, "dm/name");
1200 if (!cxt->dm_name) {
1201 warnx(_("%s: failed to get dm name"), name);
1202 return -1;
1203 }
1204 }
1205
1206 cxt->npartitions = sysfs_count_partitions(&cxt->sysfs, name);
1207 cxt->nholders = sysfs_count_dirents(&cxt->sysfs, "holders");
1208 cxt->nslaves = sysfs_count_dirents(&cxt->sysfs, "slaves");
1209
1210 DBG(CXT, ul_debugobj(cxt, "%s: npartitions=%d, nholders=%d, nslaves=%d",
1211 cxt->name, cxt->npartitions, cxt->nholders, cxt->nslaves));
1212
1213 /* ignore non-SCSI devices */
1214 if (lsblk->scsi && sysfs_scsi_get_hctl(&cxt->sysfs, NULL, NULL, NULL, NULL)) {
1215 DBG(CXT, ul_debugobj(cxt, "non-scsi device -- ignore"));
1216 return -1;
1217 }
1218
1219 DBG(CXT, ul_debugobj(cxt, "%s: context successfully initialized", cxt->name));
1220 return 0;
1221 }
1222
1223 static int process_blkdev(struct blkdev_cxt *cxt, struct blkdev_cxt *parent,
1224 int do_partitions, const char *part_name);
1225
1226 /*
1227 * List device partitions if any.
1228 */
1229 static int list_partitions(struct blkdev_cxt *wholedisk_cxt, struct blkdev_cxt *parent_cxt,
1230 const char *part_name)
1231 {
1232 DIR *dir;
1233 struct dirent *d;
1234 struct blkdev_cxt part_cxt = { 0 };
1235 int r = -1;
1236
1237 assert(wholedisk_cxt);
1238
1239 /*
1240 * Do not process further if there are no partitions for
1241 * this device or the device itself is a partition.
1242 */
1243 if (!wholedisk_cxt->npartitions || wholedisk_cxt->partition)
1244 return -1;
1245
1246 DBG(CXT, ul_debugobj(wholedisk_cxt, "probe whole-disk for partitions"));
1247
1248 dir = sysfs_opendir(&wholedisk_cxt->sysfs, NULL);
1249 if (!dir)
1250 err(EXIT_FAILURE, _("failed to open device directory in sysfs"));
1251
1252 while ((d = xreaddir(dir))) {
1253 /* Process particular partition only? */
1254 if (part_name && strcmp(part_name, d->d_name))
1255 continue;
1256
1257 if (!(sysfs_is_partition_dirent(dir, d, wholedisk_cxt->name)))
1258 continue;
1259
1260 DBG(CXT, ul_debugobj(wholedisk_cxt, " checking %s", d->d_name));
1261
1262 if (lsblk->inverse) {
1263 /*
1264 * <parent_cxt>
1265 * `-<part_cxt>
1266 * `-<wholedisk_cxt>
1267 * `-...
1268 */
1269 if (set_cxt(&part_cxt, parent_cxt, wholedisk_cxt, d->d_name))
1270 goto next;
1271
1272 if (!parent_cxt && part_cxt.nholders)
1273 goto next;
1274
1275 wholedisk_cxt->parent = &part_cxt;
1276 fill_table_line(&part_cxt, parent_cxt ? parent_cxt->scols_line : NULL);
1277 if (!lsblk->nodeps)
1278 process_blkdev(wholedisk_cxt, &part_cxt, 0, NULL);
1279 } else {
1280 /*
1281 * <parent_cxt>
1282 * `-<wholedisk_cxt>
1283 * `-<part_cxt>
1284 * `-...
1285 */
1286 int ps = set_cxt(&part_cxt, wholedisk_cxt, wholedisk_cxt, d->d_name);
1287
1288 /* Print whole disk only once */
1289 if (r)
1290 fill_table_line(wholedisk_cxt, parent_cxt ? parent_cxt->scols_line : NULL);
1291 if (ps == 0 && !lsblk->nodeps)
1292 process_blkdev(&part_cxt, wholedisk_cxt, 0, NULL);
1293 }
1294 next:
1295 reset_blkdev_cxt(&part_cxt);
1296 r = 0;
1297 }
1298
1299 DBG(CXT, ul_debugobj(wholedisk_cxt, "probe whole-disk for partitions -- done"));
1300 closedir(dir);
1301 return r;
1302 }
1303
1304 static int get_wholedisk_from_partition_dirent(DIR *dir, const char *dirname,
1305 struct dirent *d, struct blkdev_cxt *cxt)
1306 {
1307 char path[PATH_MAX];
1308 char *p;
1309 int len;
1310
1311 if ((len = readlink_at(dirfd(dir), dirname,
1312 d->d_name, path, sizeof(path) - 1)) < 0)
1313 return 0;
1314
1315 path[len] = '\0';
1316
1317 /* The path ends with ".../<device>/<partition>" */
1318 p = strrchr(path, '/');
1319 if (!p)
1320 return 0;
1321 *p = '\0';
1322
1323 p = strrchr(path, '/');
1324 if (!p)
1325 return 0;
1326 p++;
1327
1328 return set_cxt(cxt, NULL, NULL, p);
1329 }
1330
1331 /*
1332 * List device dependencies: partitions, holders (inverse = 0) or slaves (inverse = 1).
1333 */
1334 static int list_deps(struct blkdev_cxt *cxt)
1335 {
1336 DIR *dir;
1337 struct dirent *d;
1338 struct blkdev_cxt dep = { 0 };
1339 char dirname[PATH_MAX];
1340 const char *depname;
1341
1342 assert(cxt);
1343
1344 if (lsblk->nodeps)
1345 return 0;
1346
1347 DBG(CXT, ul_debugobj(cxt, "%s: list dependencies", cxt->name));
1348
1349 if (!(lsblk->inverse ? cxt->nslaves : cxt->nholders))
1350 return 0;
1351
1352 depname = lsblk->inverse ? "slaves" : "holders";
1353 dir = sysfs_opendir(&cxt->sysfs, depname);
1354 if (!dir)
1355 return 0;
1356
1357 DBG(CXT, ul_debugobj(cxt, "%s: checking for '%s' dependence", cxt->name, depname));
1358
1359 snprintf(dirname, sizeof(dirname), "%s/%s", cxt->sysfs.dir_path, depname);
1360
1361 while ((d = xreaddir(dir))) {
1362 /* Is the dependency a partition? */
1363 if (sysfs_is_partition_dirent(dir, d, NULL)) {
1364 if (!get_wholedisk_from_partition_dirent(dir, dirname, d, &dep)) {
1365 DBG(CXT, ul_debugobj(cxt, "%s: %s: dependence is partition",
1366 cxt->name, d->d_name));
1367 process_blkdev(&dep, cxt, 1, d->d_name);
1368 }
1369 }
1370 /* The dependency is a whole device. */
1371 else if (!set_cxt(&dep, cxt, NULL, d->d_name)) {
1372 DBG(CXT, ul_debugobj(cxt, "%s: %s: dependence is whole-disk",
1373 cxt->name, d->d_name));
1374 /* For inverse tree we don't want to show partitions
1375 * if the dependence is pn whle-disk */
1376 process_blkdev(&dep, cxt, lsblk->inverse ? 0 : 1, NULL);
1377 }
1378 reset_blkdev_cxt(&dep);
1379 }
1380 closedir(dir);
1381
1382 DBG(CXT, ul_debugobj(cxt, "%s: checking for '%s' -- done", cxt->name, depname));
1383 return 0;
1384 }
1385
1386 static int process_blkdev(struct blkdev_cxt *cxt, struct blkdev_cxt *parent,
1387 int do_partitions, const char *part_name)
1388 {
1389 if (do_partitions && cxt->npartitions)
1390 list_partitions(cxt, parent, part_name); /* partitoins + whole-disk */
1391 else
1392 fill_table_line(cxt, parent ? parent->scols_line : NULL); /* whole-disk only */
1393
1394 return list_deps(cxt);
1395 }
1396
1397 /* Iterate devices in sysfs */
1398 static int iterate_block_devices(void)
1399 {
1400 DIR *dir;
1401 struct dirent *d;
1402 struct blkdev_cxt cxt = { 0 };
1403
1404 if (!(dir = opendir(_PATH_SYS_BLOCK)))
1405 return -errno;
1406
1407 DBG(DEV, ul_debug("iterate on " _PATH_SYS_BLOCK));
1408
1409 while ((d = xreaddir(dir))) {
1410
1411 DBG(DEV, ul_debug(" %s dentry", d->d_name));
1412
1413 if (set_cxt(&cxt, NULL, NULL, d->d_name))
1414 goto next;
1415
1416 if (is_maj_excluded(cxt.maj) || !is_maj_included(cxt.maj))
1417 goto next;
1418
1419 /* Skip devices in the middle of dependency tree. */
1420 if ((lsblk->inverse ? cxt.nholders : cxt.nslaves) > 0)
1421 goto next;
1422
1423 process_blkdev(&cxt, NULL, 1, NULL);
1424 next:
1425 reset_blkdev_cxt(&cxt);
1426 }
1427
1428 closedir(dir);
1429
1430 DBG(DEV, ul_debug("iterate on " _PATH_SYS_BLOCK " -- done"));
1431 return 0;
1432 }
1433
1434 static char *devno_to_sysfs_name(dev_t devno, char *devname, char *buf, size_t buf_size)
1435 {
1436 char path[PATH_MAX];
1437 ssize_t len;
1438
1439 if (!sysfs_devno_path(devno, path, sizeof(path))) {
1440 warn(_("%s: failed to compose sysfs path"), devname);
1441 return NULL;
1442 }
1443
1444 len = readlink(path, buf, buf_size - 1);
1445 if (len < 0) {
1446 warn(_("%s: failed to read link"), path);
1447 return NULL;
1448 }
1449 buf[len] = '\0';
1450
1451 return xstrdup(strrchr(buf, '/') + 1);
1452 }
1453
1454 static int process_one_device(char *devname)
1455 {
1456 struct blkdev_cxt parent = { 0 }, cxt = { 0 };
1457 struct stat st;
1458 char buf[PATH_MAX + 1], *name = NULL, *diskname = NULL;
1459 dev_t disk = 0;
1460 int real_part = 0, rc = -EINVAL;
1461
1462 if (stat(devname, &st) || !S_ISBLK(st.st_mode)) {
1463 warnx(_("%s: not a block device"), devname);
1464 goto leave;
1465 }
1466
1467 if (!(name = devno_to_sysfs_name(st.st_rdev, devname, buf, PATH_MAX))) {
1468 warn(_("%s: failed to get sysfs name"), devname);
1469 goto leave;
1470 }
1471
1472 if (!strncmp(name, "dm-", 3)) {
1473 /* dm mapping is never a real partition! */
1474 real_part = 0;
1475 } else {
1476 if (blkid_devno_to_wholedisk(st.st_rdev, buf, sizeof(buf), &disk)) {
1477 warn(_("%s: failed to get whole-disk device number"), devname);
1478 goto leave;
1479 }
1480 diskname = buf;
1481 real_part = st.st_rdev != disk;
1482 }
1483
1484 if (!real_part) {
1485 /*
1486 * Device is not a partition.
1487 */
1488 if (set_cxt(&cxt, NULL, NULL, name))
1489 goto leave;
1490 process_blkdev(&cxt, NULL, !lsblk->inverse, NULL);
1491 } else {
1492 /*
1493 * Partition, read sysfs name of the device.
1494 */
1495 if (set_cxt(&parent, NULL, NULL, diskname))
1496 goto leave;
1497 if (set_cxt(&cxt, &parent, &parent, name))
1498 goto leave;
1499
1500 if (lsblk->inverse)
1501 process_blkdev(&parent, &cxt, 1, cxt.name);
1502 else
1503 process_blkdev(&cxt, &parent, 1, NULL);
1504 }
1505
1506 rc = 0;
1507 leave:
1508 free(name);
1509 reset_blkdev_cxt(&cxt);
1510
1511 if (real_part)
1512 reset_blkdev_cxt(&parent);
1513
1514 return rc;
1515 }
1516
1517 static void parse_excludes(const char *str0)
1518 {
1519 const char *str = str0;
1520
1521 while (str && *str) {
1522 char *end = NULL;
1523 unsigned long n;
1524
1525 errno = 0;
1526 n = strtoul(str, &end, 10);
1527
1528 if (end == str || (end && *end && *end != ','))
1529 errx(EXIT_FAILURE, _("failed to parse list '%s'"), str0);
1530 if (errno != 0 && (n == ULONG_MAX || n == 0))
1531 err(EXIT_FAILURE, _("failed to parse list '%s'"), str0);
1532 excludes[nexcludes++] = n;
1533
1534 if (nexcludes == ARRAY_SIZE(excludes))
1535 /* TRANSLATORS: The standard value for %d is 256. */
1536 errx(EXIT_FAILURE, _("the list of excluded devices is "
1537 "too large (limit is %d devices)"),
1538 (int)ARRAY_SIZE(excludes));
1539
1540 str = end && *end ? end + 1 : NULL;
1541 }
1542 }
1543
1544 static void parse_includes(const char *str0)
1545 {
1546 const char *str = str0;
1547
1548 while (str && *str) {
1549 char *end = NULL;
1550 unsigned long n;
1551
1552 errno = 0;
1553 n = strtoul(str, &end, 10);
1554
1555 if (end == str || (end && *end && *end != ','))
1556 errx(EXIT_FAILURE, _("failed to parse list '%s'"), str0);
1557 if (errno != 0 && (n == ULONG_MAX || n == 0))
1558 err(EXIT_FAILURE, _("failed to parse list '%s'"), str0);
1559 includes[nincludes++] = n;
1560
1561 if (nincludes == ARRAY_SIZE(includes))
1562 /* TRANSLATORS: The standard value for %d is 256. */
1563 errx(EXIT_FAILURE, _("the list of included devices is "
1564 "too large (limit is %d devices)"),
1565 (int)ARRAY_SIZE(includes));
1566 str = end && *end ? end + 1 : NULL;
1567 }
1568 }
1569
1570 /*
1571 * see set_sortdata_u64() and columns initialization in main()
1572 */
1573 static int cmp_u64_cells(struct libscols_cell *a,
1574 struct libscols_cell *b,
1575 __attribute__((__unused__)) void *data)
1576 {
1577 uint64_t *adata = (uint64_t *) scols_cell_get_userdata(a),
1578 *bdata = (uint64_t *) scols_cell_get_userdata(b);
1579
1580 if (adata == NULL && bdata == NULL)
1581 return 0;
1582 if (adata == NULL)
1583 return -1;
1584 if (bdata == NULL)
1585 return 1;
1586 return *adata == *bdata ? 0 : *adata >= *bdata ? 1 : -1;
1587 }
1588
1589 static void __attribute__((__noreturn__)) help(FILE *out)
1590 {
1591 size_t i;
1592
1593 fputs(USAGE_HEADER, out);
1594 fprintf(out, _(" %s [options] [<device> ...]\n"), program_invocation_short_name);
1595
1596 fputs(USAGE_SEPARATOR, out);
1597 fputs(_("List information about block devices.\n"), out);
1598
1599 fputs(USAGE_OPTIONS, out);
1600 fputs(_(" -a, --all print all devices\n"), out);
1601 fputs(_(" -b, --bytes print SIZE in bytes rather than in human readable format\n"), out);
1602 fputs(_(" -d, --nodeps don't print slaves or holders\n"), out);
1603 fputs(_(" -D, --discard print discard capabilities\n"), out);
1604 fputs(_(" -e, --exclude <list> exclude devices by major number (default: RAM disks)\n"), out);
1605 fputs(_(" -f, --fs output info about filesystems\n"), out);
1606 fputs(_(" -i, --ascii use ascii characters only\n"), out);
1607 fputs(_(" -I, --include <list> show only devices with specified major numbers\n"), out);
1608 fputs(_(" -l, --list use list format output\n"), out);
1609 fputs(_(" -m, --perms output info about permissions\n"), out);
1610 fputs(_(" -n, --noheadings don't print headings\n"), out);
1611 fputs(_(" -o, --output <list> output columns\n"), out);
1612 fputs(_(" -O, --output-all output all columns\n"), out);
1613 fputs(_(" -p, --paths print complete device path\n"), out);
1614 fputs(_(" -P, --pairs use key=\"value\" output format\n"), out);
1615 fputs(_(" -r, --raw use raw output format\n"), out);
1616 fputs(_(" -s, --inverse inverse dependencies\n"), out);
1617 fputs(_(" -S, --scsi output info about SCSI devices\n"), out);
1618 fputs(_(" -t, --topology output info about topology\n"), out);
1619 fputs(_(" -x, --sort <column> sort output by <column>\n"), out);
1620 fputs(USAGE_SEPARATOR, out);
1621 fputs(USAGE_HELP, out);
1622 fputs(USAGE_VERSION, out);
1623
1624 fprintf(out, _("\nAvailable columns (for --output):\n"));
1625
1626 for (i = 0; i < ARRAY_SIZE(infos); i++)
1627 fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help));
1628
1629 fprintf(out, USAGE_MAN_TAIL("lsblk(8)"));
1630
1631 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
1632 }
1633
1634 static void check_sysdevblock(void)
1635 {
1636 if (access(_PATH_SYS_DEVBLOCK, R_OK) != 0)
1637 err(EXIT_FAILURE, _("failed to access sysfs directory: %s"),
1638 _PATH_SYS_DEVBLOCK);
1639 }
1640
1641 int main(int argc, char *argv[])
1642 {
1643 struct lsblk _ls = { .sort_id = -1 };
1644 int scols_flags = LSBLK_TREE;
1645 int i, c, status = EXIT_FAILURE;
1646 char *outarg = NULL;
1647
1648 static const struct option longopts[] = {
1649 { "all", 0, 0, 'a' },
1650 { "bytes", 0, 0, 'b' },
1651 { "nodeps", 0, 0, 'd' },
1652 { "discard", 0, 0, 'D' },
1653 { "help", 0, 0, 'h' },
1654 { "output", 1, 0, 'o' },
1655 { "output-all", 0, 0, 'O' },
1656 { "perms", 0, 0, 'm' },
1657 { "noheadings", 0, 0, 'n' },
1658 { "list", 0, 0, 'l' },
1659 { "ascii", 0, 0, 'i' },
1660 { "raw", 0, 0, 'r' },
1661 { "inverse", 0, 0, 's' },
1662 { "fs", 0, 0, 'f' },
1663 { "exclude", 1, 0, 'e' },
1664 { "include", 1, 0, 'I' },
1665 { "topology", 0, 0, 't' },
1666 { "paths", 0, 0, 'p' },
1667 { "pairs", 0, 0, 'P' },
1668 { "scsi", 0, 0, 'S' },
1669 { "sort", 1, 0, 'x' },
1670 { "version", 0, 0, 'V' },
1671 { NULL, 0, 0, 0 },
1672 };
1673
1674 static const ul_excl_t excl[] = { /* rows and cols in in ASCII order */
1675 { 'D','O' },
1676 { 'I','e' },
1677 { 'O','S' },
1678 { 'O','f' },
1679 { 'O','m' },
1680 { 'O','t' },
1681 { 'P','l','r' },
1682 { 0 }
1683 };
1684 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
1685
1686 setlocale(LC_ALL, "");
1687 bindtextdomain(PACKAGE, LOCALEDIR);
1688 textdomain(PACKAGE);
1689 atexit(close_stdout);
1690
1691 lsblk = &_ls;
1692
1693 lsblk_init_debug();
1694
1695 while((c = getopt_long(argc, argv,
1696 "abdDe:fhlnmo:OpPiI:rstVSx:", longopts, NULL)) != -1) {
1697
1698 err_exclusive_options(c, longopts, excl, excl_st);
1699
1700 switch(c) {
1701 case 'a':
1702 lsblk->all_devices = 1;
1703 break;
1704 case 'b':
1705 lsblk->bytes = 1;
1706 break;
1707 case 'd':
1708 lsblk->nodeps = 1;
1709 break;
1710 case 'D':
1711 add_column(columns, ncolumns++, COL_NAME);
1712 add_column(columns, ncolumns++, COL_DALIGN);
1713 add_column(columns, ncolumns++, COL_DGRAN);
1714 add_column(columns, ncolumns++, COL_DMAX);
1715 add_column(columns, ncolumns++, COL_DZERO);
1716 break;
1717 case 'e':
1718 parse_excludes(optarg);
1719 break;
1720 case 'h':
1721 help(stdout);
1722 break;
1723 case 'l':
1724 scols_flags &= ~LSBLK_TREE; /* disable the default */
1725 break;
1726 case 'n':
1727 scols_flags |= LSBLK_NOHEADINGS;
1728 break;
1729 case 'o':
1730 outarg = optarg;
1731 break;
1732 case 'O':
1733 for (ncolumns = 0 ; ncolumns < (int) ARRAY_SIZE(infos); ncolumns++)
1734 columns[ncolumns] = ncolumns;
1735 break;
1736 case 'p':
1737 lsblk->paths = 1;
1738 break;
1739 case 'P':
1740 scols_flags |= LSBLK_EXPORT;
1741 scols_flags &= ~LSBLK_TREE; /* disable the default */
1742 break;
1743 case 'i':
1744 scols_flags |= LSBLK_ASCII;
1745 break;
1746 case 'I':
1747 parse_includes(optarg);
1748 break;
1749 case 'r':
1750 scols_flags &= ~LSBLK_TREE; /* disable the default */
1751 scols_flags |= LSBLK_RAW; /* enable raw */
1752 break;
1753 case 's':
1754 lsblk->inverse = 1;
1755 break;
1756 case 'f':
1757 add_column(columns, ncolumns++, COL_NAME);
1758 add_column(columns, ncolumns++, COL_FSTYPE);
1759 add_column(columns, ncolumns++, COL_LABEL);
1760 add_column(columns, ncolumns++, COL_UUID);
1761 add_column(columns, ncolumns++, COL_TARGET);
1762 break;
1763 case 'm':
1764 add_column(columns, ncolumns++, COL_NAME);
1765 add_column(columns, ncolumns++, COL_SIZE);
1766 add_column(columns, ncolumns++, COL_OWNER);
1767 add_column(columns, ncolumns++, COL_GROUP);
1768 add_column(columns, ncolumns++, COL_MODE);
1769 break;
1770 case 't':
1771 add_column(columns, ncolumns++, COL_NAME);
1772 add_column(columns, ncolumns++, COL_ALIOFF);
1773 add_column(columns, ncolumns++, COL_MINIO);
1774 add_column(columns, ncolumns++, COL_OPTIO);
1775 add_column(columns, ncolumns++, COL_PHYSEC);
1776 add_column(columns, ncolumns++, COL_LOGSEC);
1777 add_column(columns, ncolumns++, COL_ROTA);
1778 add_column(columns, ncolumns++, COL_SCHED);
1779 add_column(columns, ncolumns++, COL_RQ_SIZE);
1780 add_column(columns, ncolumns++, COL_RA);
1781 add_column(columns, ncolumns++, COL_WSAME);
1782 break;
1783 case 'S':
1784 lsblk->nodeps = 1;
1785 lsblk->scsi = 1;
1786 add_column(columns, ncolumns++, COL_NAME);
1787 add_column(columns, ncolumns++, COL_HCTL);
1788 add_column(columns, ncolumns++, COL_TYPE);
1789 add_column(columns, ncolumns++, COL_VENDOR);
1790 add_column(columns, ncolumns++, COL_MODEL);
1791 add_column(columns, ncolumns++, COL_REV);
1792 add_column(columns, ncolumns++, COL_TRANSPORT);
1793 break;
1794 case 'V':
1795 printf(UTIL_LINUX_VERSION);
1796 return EXIT_SUCCESS;
1797 case 'x':
1798 scols_flags &= ~LSBLK_TREE; /* disable the default */
1799 lsblk->sort_id = column_name_to_id(optarg, strlen(optarg));
1800 if (lsblk->sort_id >= 0)
1801 break;
1802 /* fallthrough */
1803 default:
1804 help(stderr);
1805 }
1806 }
1807
1808 check_sysdevblock();
1809
1810 if (!ncolumns) {
1811 add_column(columns, ncolumns++, COL_NAME);
1812 add_column(columns, ncolumns++, COL_MAJMIN);
1813 add_column(columns, ncolumns++, COL_RM);
1814 add_column(columns, ncolumns++, COL_SIZE);
1815 add_column(columns, ncolumns++, COL_RO);
1816 add_column(columns, ncolumns++, COL_TYPE);
1817 add_column(columns, ncolumns++, COL_TARGET);
1818 }
1819
1820 if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
1821 &ncolumns, column_name_to_id) < 0)
1822 return EXIT_FAILURE;
1823
1824 if (nexcludes == 0 && nincludes == 0)
1825 excludes[nexcludes++] = 1; /* default: ignore RAM disks */
1826
1827 if (lsblk->sort_id >= 0 && column_id_to_number(lsblk->sort_id) < 0)
1828 errx(EXIT_FAILURE, _("the sort column has to be among the output columns"));
1829
1830 mnt_init_debug(0);
1831 scols_init_debug(0);
1832
1833 /*
1834 * initialize output columns
1835 */
1836 if (!(lsblk->table = scols_new_table()))
1837 errx(EXIT_FAILURE, _("failed to initialize output table"));
1838 scols_table_enable_raw(lsblk->table, !!(scols_flags & LSBLK_RAW));
1839 scols_table_enable_export(lsblk->table, !!(scols_flags & LSBLK_EXPORT));
1840 scols_table_enable_ascii(lsblk->table, !!(scols_flags & LSBLK_ASCII));
1841 scols_table_enable_noheadings(lsblk->table, !!(scols_flags & LSBLK_NOHEADINGS));
1842
1843 for (i = 0; i < ncolumns; i++) {
1844 struct colinfo *ci = get_column_info(i);
1845 struct libscols_column *cl;
1846 int id = get_column_id(i), fl = ci->flags;
1847
1848 if (!(scols_flags & LSBLK_TREE) && id == COL_NAME)
1849 fl &= ~SCOLS_FL_TREE;
1850
1851 cl = scols_table_new_column(lsblk->table, ci->name, ci->whint, fl);
1852 if (!cl) {
1853 warn(_("failed to initialize output column"));
1854 goto leave;
1855 }
1856 if (!lsblk->sort_col && lsblk->sort_id == id) {
1857 lsblk->sort_col = cl;
1858 scols_column_set_cmpfunc(cl,
1859 ci->sort_type == SORT_STRING ?
1860 scols_cmpstr_cells : cmp_u64_cells, NULL);
1861 }
1862 }
1863
1864 if (optind == argc)
1865 status = iterate_block_devices() == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1866 else {
1867 int cnt = 0, cnt_err = 0;
1868
1869 while (optind < argc) {
1870 if (process_one_device(argv[optind++]) != 0)
1871 cnt_err++;
1872 cnt++;
1873 }
1874 status = cnt == 0 ? EXIT_FAILURE : /* nothing */
1875 cnt == cnt_err ? LSBLK_EXIT_ALLFAILED :/* all failed */
1876 cnt_err ? LSBLK_EXIT_SOMEOK : /* some ok */
1877 EXIT_SUCCESS; /* all success */
1878 }
1879
1880 if (lsblk->sort_col)
1881 scols_sort_table(lsblk->table, lsblk->sort_col);
1882
1883 scols_print_table(lsblk->table);
1884
1885 leave:
1886 if (lsblk->sort_col)
1887 unref_sortdata(lsblk->table);
1888
1889 scols_unref_table(lsblk->table);
1890
1891 mnt_unref_table(mtab);
1892 mnt_unref_table(swaps);
1893 mnt_unref_cache(mntcache);
1894 #ifdef HAVE_LIBUDEV
1895 udev_unref(udev);
1896 #endif
1897 return status;
1898 }