commit - fa7b2359dac517846025752716dd71ecb8caa78e
commit + 9c5d8ecc060ea66ceddf6a38fb534a5d20b124c7
blob - 05d9c0c4cd4088d749a7af9815166c15385efaa7
blob + d85e3f0b3106dae1358bc50204994bee2e43b695
--- Makefile
+++ Makefile
# See LICENSE file for copyright and license details.
.POSIX:
-VERSION = 1.0
+VERSION = 1.1
PREFIX = /usr/local
MANPREFIX = ${PREFIX}/man
MY_CFLAGS = -Wall -Wextra -Werror -pedantic -std=c99 -DVERSION=\"${VERSION}\" ${CFLAGS}
blob - cd9b7911c785a05bc3b250486b501508782734d5
blob + db64be1359770b9fbbefe55530c01f16881c728d
--- README.md
+++ README.md
## TODO
- [ ] Fancy unicodes: `├─` & `└─`
+- [ ] Where should "LABEL" go? After "NAME" or before "MOUNT"?
blob - 5f5517620940cc53d22dab6a586393a78b4a344a
blob + 49ef289a0665d75471d727bc2a2a1b7cc9da2edd
--- lsblk.8
+++ lsblk.8
.SH NAME
lsblk \- list block devices
.SH SYNOPSIS
-.B lsblk [-Vn]
+.B lsblk [-Vlnu]
.SH DESCRIPTION
The
.B lsblk
.SH OPTIONS
.TP
.B \-V
-prints version information to stdout, then exits.
+prints version information to stdout, then exit.
.TP
+.B \-l
+also print the label (packname) of each disk.
+.TP
.B \-n
don't print the header.
+.TP
+.B \-u
+also print the filesystem usage of each partition
+and the disklabel usage of each disk.
+
+Note: The disklabel usage is calculated by summing the size of all (except "c") partitions.
+
+Note 2: There is some error in the filesystem usage, please consult df(1) for more correct numbers.
.SH SEE ALSO
-.BR disklabel (8)
+.BR disklabel (8) ,
+.BR df (1)
blob - 5cdd6db7b9dda8c564aafc9dd261b220f9e4049c
blob + 7dca37ea4d6456dff08809004520e0dd4d1ae999
--- lsblk.c
+++ lsblk.c
#define _BSD_SOURCE 1
#define DKTYPENAMES
#include <stddef.h>
+#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/disklabel.h>
#include <sys/sysctl.h>
#include <fcntl.h>
#include <errno.h>
-static void die (const char *fmt, ...)
+static __dead void die (const char *fmt, ...)
{
va_list ap;
return buffer;
}
-static void printsize (uint64_t sz)
+static char *stripdisk (char *n)
{
+ const char sufx[] = " disk";
+ const size_t ln = strnlen (n, 16);
+ const size_t ls = sizeof sufx - 1;
+
+ if (!memcmp (n + ln - ls, sufx, ls)) {
+ n[ln - ls] = '\0';
+ }
+
+ return n;
+}
+
+static void print_size (uint64_t sz)
+{
struct unit {
char sym;
uint64_t factor;
}
putchar (sym);
+ putchar (' ');
}
-static int usage (void)
+enum {
+ FIELD_NAME = 0x01,
+ FIELD_LABEL = 0x02,
+ FIELD_SIZE = 0x04,
+ FIELD_USED = 0x08,
+ FIELD_FREE = 0x10,
+ FIELD_TYPE = 0x20,
+ FIELD_MOUNT = 0x40,
+
+ FIELD_DEFAULT = FIELD_NAME | FIELD_SIZE | FIELD_TYPE | FIELD_MOUNT,
+};
+
+struct my_partinfo {
+ char name[5];
+ uint64_t size;
+ uint64_t fssize;
+ uint64_t free;
+ const char *fstype;
+ const char *mount;
+};
+
+struct my_diskinfo {
+ char type[16];
+ char label[16];
+ char name[4];
+ uint64_t size;
+ uint64_t used;
+ const char *mount;
+ uint8_t num_parts;
+ struct my_partinfo parts[MAXPARTITIONS];
+};
+
+static void print_header (int fields)
{
- fputs ("Usage: lsblk [-Vn]\n", stderr);
- return 1;
+ if (fields & FIELD_NAME)
+ printf ("%-6s ", "NAME");
+
+ if (fields & FIELD_LABEL)
+ printf ("%-16s ", "LABEL");
+
+ if (fields & FIELD_SIZE)
+ printf ("%-4s ", "SIZE");
+
+ if (fields & FIELD_USED)
+ printf ("%-4s ", "USED");
+
+ if (fields & FIELD_FREE)
+ printf ("%-4s ", "FREE");
+
+ if (fields & FIELD_TYPE)
+ printf ("%-8s ", "TYPE");
+
+ if (fields & FIELD_MOUNT)
+ printf ("MOUNT ");
+
+ putchar ('\n');
}
-static char *stripdisk (char *n)
+static void print_part (const struct my_partinfo *part, int fields)
{
- const char sufx[] = " disk";
- const size_t ln = strnlen (n, 16);
- const size_t ls = sizeof sufx - 1;
+ if (fields & FIELD_NAME)
+ printf (" %s ", part->name);
- if (!memcmp (n + ln - ls, sufx, ls)) {
- n[ln - ls] = '\0';
+ if (fields & FIELD_LABEL)
+ printf ("%16s ", "");
+
+ if (fields & FIELD_SIZE)
+ print_size (part->size);
+
+ if (fields & FIELD_USED) {
+ if (part->fssize) {
+ print_size (part->fssize - part->free);
+ } else {
+ printf (" N/A ");
+ }
}
- return n;
+ if (fields & FIELD_FREE) {
+ if (part->fssize) {
+ print_size (part->free);
+ } else {
+ printf (" N/A ");
+ }
+ }
+
+ if (fields & FIELD_TYPE)
+ printf ("%-8.16s ", part->fstype);
+
+ if (fields & FIELD_MOUNT && part->mount)
+ printf ("%s ", part->mount);
+
+ putchar ('\n');
+}
+
+static void print_disk (const struct my_diskinfo *disk, int fields)
+{
+ if (fields & FIELD_NAME)
+ printf ("%s ", disk->name);
+
+ if (fields & FIELD_LABEL)
+ printf ("%-16.16s ", disk->label);
+
+ if (fields & FIELD_SIZE)
+ print_size (disk->size);
+
+ if (fields & FIELD_USED)
+ print_size (disk->used);
+
+ if (fields & FIELD_FREE)
+ print_size (disk->size - disk->used);
+
+ // Pad only upto 8 characters because most disk types are <=8 bytes long.
+ if (fields & FIELD_TYPE)
+ printf ("%-8.16s ", disk->type);
+
+ if (fields & FIELD_MOUNT && disk->mount)
+ printf ("%s ", disk->mount);
+
+ putchar ('\n');
+
+ for (uint8_t i = 0; i < disk->num_parts; ++i)
+ print_part (&disk->parts[i], fields);
+}
+
+static const struct statfs *find_mount (const char *dev)
+{
+ static struct statfs *mounts = NULL;
+ static int n_mounts;
+
+ if (!mounts) {
+ n_mounts = getmntinfo (&mounts, MNT_NOWAIT);
+ if (n_mounts == 0)
+ die ("getmntinfo()");
+ }
+
+ for (int i = 0; i < n_mounts; ++i) {
+ if (!strcmp (dev, mounts[i].f_mntfromname))
+ return &mounts[i];
+ }
+
+ return NULL;
}
+static struct my_diskinfo read_disk (const char *name)
+{
+ struct my_diskinfo disk;
+ struct disklabel label;
+
+ bzero (&disk, sizeof disk);
+
+ { // Read disklabel.
+ const struct statfs *mnt;
+ char path[5 + 4 + 1];
+ int fd;
+
+ snprintf (path, sizeof path, "/dev/%.3sc", name);
+ fd = open (path, O_RDONLY);
+ if (fd < 0)
+ die ("open(%s)", path);
+ if (ioctl (fd, DIOCGDINFO, &label) < 0)
+ die ("ioctl(%s, DIOCGDINFO)", path);
+ close (fd);
+
+ mnt = find_mount (path);
+ if (mnt)
+ disk.mount = mnt->f_mntonname;
+ }
+
+ memcpy (disk.name, name, 3);
+ disk.name[3] = '\0';
+ disk.size = DL_GETDSIZE (&label) * label.d_secsize;
+ memcpy (disk.type, label.d_typename, 16);
+ stripdisk (disk.type);
+ memcpy (disk.label, label.d_packname, 16);
+ disk.num_parts = 0;
+
+ for (uint16_t i = 0; i < label.d_npartitions; ++i) {
+ const struct partition *p = &label.d_partitions[i];
+ const struct statfs *mnt;
+ struct my_partinfo part;
+ char path[5 + 4 + 1];
+
+ bzero (&part, sizeof part);
+
+ part.size = DL_GETPSIZE (p) * label.d_secsize;
+
+ if (!part.size)
+ continue;
+
+ memcpy (part.name, disk.name, 3);
+ part.name[3] = 'a' + i;
+ part.name[4] = '\0';
+
+ snprintf (path, sizeof path, "/dev/%s", part.name);
+ mnt = find_mount (path);
+
+ if (mnt) {
+ const uint64_t bs = mnt->f_bsize;
+ part.mount = mnt->f_mntonname;
+ part.fssize = mnt->f_blocks * bs;
+ part.free = mnt->f_bfree * bs;
+ }
+
+ part.fstype = fstypenames[p->p_fstype];
+ if (i != 2)
+ disk.used += part.size;
+ disk.parts[disk.num_parts++] = part;
+ }
+
+ return disk;
+}
+
+static int usage (void)
+{
+ fputs ("Usage: lsblk [-Vlnu]\n", stderr);
+ return 1;
+}
+
int main (int argc, char *argv[])
{
int option;
bool header = true;
+ int fields = FIELD_DEFAULT;
if (unveil ("/dev", "r") == -1)
die ("unveil(/dev)");
if (unveil (NULL, NULL) == -1)
die ("unveil()");
- while ((option = getopt (argc, argv, ":Vn")) != -1) {
+ while ((option = getopt (argc, argv, ":Vlnu")) != -1) {
switch (option) {
case 'V':
puts ("lsblk-" VERSION);
case 'n':
header = false;
break;
+ case 'l':
+ fields |= FIELD_LABEL;
+ break;
+ case 'u':
+ fields |= FIELD_USED | FIELD_FREE;
+ break;
default:
return usage ();
}
if (n_mounts == 0)
die ("getmntinfo()");
- if (header)
- puts ("NAME SIZE TYPE MOUNTPOINT");
+ size_t cap_disks = 10;
+ size_t num_disks = 0;
+ struct my_diskinfo *disks = calloc (cap_disks, sizeof (struct my_diskinfo));
- for (char *disk; (disk = strsep (&names, ",")) != NULL;) {
- struct disklabel label;
- char *colon, *name = NULL;
- char dtype[16];
- int fd;
-
- colon = strchr (disk, ':');
- if (colon)
- *colon = '\0';
-
- asprintf (&name, "/dev/%sc", disk);
-
- fd = open (name, O_RDONLY);
- if (fd < 0)
- die ("open(%s)", name);
-
- if (ioctl (fd, DIOCGDINFO, &label) < 0)
- die ("ioctl(%s, IOCGDINFO)", name);
-
- close (fd);
-
- const uint32_t ss = label.d_secsize;
-
- char *letter = name + strlen (name) - 1;
-
- printf ("%s ", disk);
- printsize (DL_GETDSIZE (&label) * ss);
- memcpy (dtype, label.d_typename, sizeof dtype);
- printf (" %-8.16s\n", stripdisk (dtype));
- for (uint16_t i = 0; i < label.d_npartitions; ++i) {
- const struct partition *p = &label.d_partitions[i];
- const uint64_t size = DL_GETPSIZE (p) * ss;
- if (size == 0)
- continue;
-
- *letter = i + 'a';
- const char *mp = "";
- for (int i = 0; i < n_mounts; ++i) {
- const struct statfs *m = &mounts[i];
- if (!strcmp (name, m->f_mntfromname)) {
- mp = m->f_mntonname;
- break;
- }
- }
-
- printf (" %s%c ", disk, *letter);
- printsize (size);
- printf (" %-8s %s\n", fstypenames[p->p_fstype], mp);
+ for (char *disk; (disk = strsep (&names, ",")) != NULL; ) {
+ if (num_disks == cap_disks) {
+ cap_disks = cap_disks * 13 / 8;
+ disks = reallocarray (disks, cap_disks, sizeof (struct my_diskinfo));
}
+ disks[num_disks++] = read_disk (disk);
}
free (names);
+
+ if (header)
+ print_header (fields);
+
+ for (size_t i = 0; i < num_disks; ++i) {
+ print_disk (&disks[i], fields);
+ }
return 0;
}