commit 9c5d8ecc060ea66ceddf6a38fb534a5d20b124c7 from: Benjamin Stürz date: Tue May 02 16:25:37 2023 UTC Redesign + Added LABEL, USED, FREE fields commit - fa7b2359dac517846025752716dd71ecb8caa78e commit + 9c5d8ecc060ea66ceddf6a38fb534a5d20b124c7 blob - 05d9c0c4cd4088d749a7af9815166c15385efaa7 blob + d85e3f0b3106dae1358bc50204994bee2e43b695 --- Makefile +++ Makefile @@ -2,7 +2,7 @@ # 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 @@ -7,3 +7,4 @@ This project aims to implement a clone of lsblk for Op ## TODO - [ ] Fancy unicodes: `├─` & `└─` +- [ ] Where should "LABEL" go? After "NAME" or before "MOUNT"? blob - 5f5517620940cc53d22dab6a586393a78b4a344a blob + 49ef289a0665d75471d727bc2a2a1b7cc9da2edd --- lsblk.8 +++ lsblk.8 @@ -15,7 +15,7 @@ .SH NAME lsblk \- list block devices .SH SYNOPSIS -.B lsblk [-Vn] +.B lsblk [-Vlnu] .SH DESCRIPTION The .B lsblk @@ -23,9 +23,21 @@ utility can be used to examine block devices attached .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 @@ -17,6 +17,7 @@ #define _BSD_SOURCE 1 #define DKTYPENAMES #include +#include #include #include #include @@ -34,7 +35,7 @@ #include #include -static void die (const char *fmt, ...) +static __dead void die (const char *fmt, ...) { va_list ap; @@ -76,8 +77,21 @@ static char *disknames (void) 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; @@ -114,31 +128,232 @@ static void printsize (uint64_t sz) } 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)"); @@ -146,7 +361,7 @@ int main (int argc, char *argv[]) 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); @@ -154,6 +369,12 @@ int main (int argc, char *argv[]) case 'n': header = false; break; + case 'l': + fields |= FIELD_LABEL; + break; + case 'u': + fields |= FIELD_USED | FIELD_FREE; + break; default: return usage (); } @@ -172,60 +393,25 @@ int main (int argc, char *argv[]) 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; }