Commit Diff


commit - b0a1b4203e231ca2b6ec3ab5a88efda868349631
commit + 1678d261f49117252b51dacb99182e9f99322b83
blob - 4eb647aabbd55b501e8ff215a50ac233388b669c
blob + 21907b7b862acc36432e04968bc6798b2a865704
--- ChangeLog.md
+++ ChangeLog.md
@@ -9,13 +9,15 @@ and this project adheres to [Semantic Versioning](http
 ### Added
 - Accept "disk" arguments.
 - Show RAID disks
+  - Show status of RAID disk in "COMMENT"
+- Show status of RAID volume in "COMMENT"
 - Options:
   - `-a` print all fields
   - `-i` don't print fancy unicode characters
   - `-U` print the DUID
   
 ### Changed
-- Merge "LABEL" and "MOUNT" fields into "LABEL/MOUNT"
+- Merge "LABEL" and "MOUNT" fields into "COMMENT"
   because disks can't be mounted but partitions can,
   and disks can have a label but partitions don't
   (at least through the disklabel interface).
blob - 1ec4f487a9b69ba4bd77d67fff959873d6511dfe
blob + 2a5244779f7c4299c46fa3ac1a55dc5930ebefbb
--- README.md
+++ README.md
@@ -22,5 +22,6 @@ doas make unroot
 - [ ] `lsblk sd0a`
 - [ ] `lsblk -f duid,size`
 - [ ] Add an option to make the output script-friendly (like -c for CSV).
-- [ ] Maybe: Display the child device of a RAID partition
+- [x] Maybe: Display the child device of a RAID partition
 - [x] Show the DUID
+- [ ] Explain the exact meaning of every field in lsblk.8
blob - 5522b5feb7c45bc31c82dc06e8a5d292b28b637a
blob + 24e2ce488861a53567d572c5cfcafe75002df2f0
--- lsblk.8
+++ lsblk.8
@@ -16,7 +16,7 @@
 lsblk \- list block devices
 .SH SYNOPSIS
 .B lsblk
-[\fB-ainUuV\fR]
+[\fB-abinUuV\fR]
 [\fIdisk ...\fR]
 .SH DESCRIPTION
 The
@@ -34,6 +34,10 @@ prints version information to stdout, then exit.
 .B \-a
 print all fields.
 .TP
+.B \-b
+don't scan for RAID volumes using
+.BR bio (4) .
+.TP
 .B \-i
 don't print fancy unicode characters, only print ASCII.
 .TP
@@ -70,8 +74,13 @@ The following line lists all fields of sd0:
 .RS 5
 lsblk -a sd0
 
+.SH FIELDS
+.B TODO
+Explain the exact meaning of every field.
+
 .SH SEE ALSO
 .BR disklabel (8) ,
+.BR bio (4) ,
 .BR df (1)
 
 .SH HISTORY
blob - 9c892b22b21731c19c377f1438944009c056a939
blob + 0b44817852780b41c6aeaf88eb235a5eee5b675c
--- lsblk.c
+++ lsblk.c
@@ -125,14 +125,15 @@ enum {
     FIELD_USED      = 0x08,
     FIELD_FREE      = 0x10,
     FIELD_TYPE      = 0x20,
-    FIELD_LMNT      = 0x40,
+    FIELD_COMMENT   = 0x40,
 
-    FIELD_DEFAULT   = FIELD_NAME | FIELD_SIZE | FIELD_TYPE | FIELD_LMNT,
+    FIELD_DEFAULT   = FIELD_NAME | FIELD_SIZE | FIELD_TYPE | FIELD_COMMENT,
 };
 
 enum {
     OPT_NOHEADER    = 0x01,
     OPT_NOUNICODE   = 0x02,
+    OPT_NOBIO       = 0x04,
 };
 
 struct my_diskinfo;
@@ -144,7 +145,8 @@ struct my_partinfo {
     uint64_t free;
     const char *fstype;
     const char *mount;
-    const struct my_diskinfo *sub;
+    const struct my_diskinfo *sub; // If this is part of a RAID
+    const char *raidstatus; // Only available if (sub != NULL)
 };
 
 struct my_diskinfo {
@@ -156,6 +158,7 @@ struct my_diskinfo {
     u_char duid[8];
     uint8_t num_parts;
     struct my_partinfo parts[MAXPARTITIONS];
+    const char *raidstatus; // If this is a RAID device
 };
 
 static void print_header (int fields)
@@ -178,8 +181,8 @@ static void print_header (int fields)
     if (fields & FIELD_TYPE)
         printf ("%-8s ", "TYPE");
 
-    if (fields & FIELD_LMNT)
-        printf ("LABEL/MOUNT ");
+    if (fields & FIELD_COMMENT)
+        printf ("COMMENT ");
 
     putchar ('\n');
 }
@@ -191,7 +194,7 @@ static void print_duid (const u_char *duid)
     }
 }
 
-static void print_disk (const struct my_diskinfo *disk, int fields, int options, bool sub);
+static void print_disk (const struct my_diskinfo *disk, int fields, int options, const char *raidstatus);
 static void print_part (
     const struct my_diskinfo *disk,
     const struct my_partinfo *part,
@@ -231,22 +234,22 @@ static void print_part (
     if (fields & FIELD_TYPE)
         printf ("%-8.16s ", part->fstype);
 
-    if (fields & FIELD_LMNT && part->mount)
+    if (fields & FIELD_COMMENT && part->mount)
         printf ("%s ", part->mount);
 
     putchar ('\n');
 
     if (part->sub) {
         printf ("│ └─");
-        print_disk (part->sub, fields, options, true);
+        print_disk (part->sub, fields, options, part->raidstatus);
     }
 }
 
-static void print_disk (const struct my_diskinfo *disk, int fields, int options, bool sub)
+static void print_disk (const struct my_diskinfo *disk, int fields, int options, const char *raidstatus)
 {
     if (fields & FIELD_NAME) {
         printf ("%s ", disk->name);
-        if (!sub)
+        if (!raidstatus)
             printf ("    ");
     }
 
@@ -268,12 +271,19 @@ static void print_disk (const struct my_diskinfo *disk
     if (fields & FIELD_TYPE)
         printf ("%-8.16s ", disk->type);
 
-    if (fields & FIELD_LMNT)
-        printf ("%.16s ", disk->label);
+    if (fields & FIELD_COMMENT) {
+        if (raidstatus) {
+            printf ("%s ", raidstatus);
+        } else if (disk->raidstatus) {
+            printf ("%.16s (%s) ", disk->label, disk->raidstatus);
+        } else {
+            printf ("%.16s ", disk->label);
+        }
+    }
 
     putchar ('\n');
 
-    if (!sub) {
+    if (!raidstatus) {
         for (uint8_t i = 0; i < disk->num_parts; ++i)
             print_part (disk, &disk->parts[i], fields, options, i == (disk->num_parts - 1));
     }
@@ -363,8 +373,35 @@ static struct my_diskinfo read_disk (const char *name)
     return disk;
 }
 
+static const char *bd_statusstr (int status) {
+    switch (status) {
+    case BIOC_SDONLINE:     return BIOC_SDONLINE_S;
+    case BIOC_SDOFFLINE:    return BIOC_SDOFFLINE_S;
+    case BIOC_SDFAILED:     return BIOC_SDFAILED_S;
+    case BIOC_SDREBUILD:    return BIOC_SDREBUILD_S;
+    case BIOC_SDHOTSPARE:   return BIOC_SDHOTSPARE_S;
+    case BIOC_SDUNUSED:     return BIOC_SDUNUSED_S;
+    case BIOC_SDSCRUB:      return BIOC_SDSCRUB_S;
+    case BIOC_SDINVALID:    return BIOC_SDINVALID_S;
+    default:                return "Unknown";
+    }
+}
+
+static const char *bv_statusstr (int status) {
+    switch (status) {
+    case BIOC_SVONLINE:     return BIOC_SVONLINE_S;
+    case BIOC_SVOFFLINE:    return BIOC_SVOFFLINE_S;
+    case BIOC_SVDEGRADED:   return BIOC_SVDEGRADED_S;
+    case BIOC_SVBUILDING:   return BIOC_SVBUILDING_S;
+    case BIOC_SVSCRUB:      return BIOC_SVSCRUB_S;
+    case BIOC_SVREBUILD:    return BIOC_SVREBUILD_S;
+    case BIOC_SVINVALID:    return BIOC_SVINVALID_S;
+    default:                return "Unknown";
+    }
+}
+
 static void read_raid (
-    const struct my_diskinfo *disk,
+    struct my_diskinfo *disk,
     struct my_diskinfo *disks,
     size_t num_disks
 ) {
@@ -382,50 +419,66 @@ static void read_raid (
     if (ioctl (fd, BIOCINQ, &bi) == -1)
         goto ret;
 
-    for (int i = 0; i < bi.bi_nodisk; ++i) {
-        struct bioc_disk bd;
-        const char *vendor;
-        size_t len_vendor;
-        char letter;
-
-        bzero (&bd, sizeof bd);
-        memcpy (&bd.bd_bio, &bi.bi_bio, sizeof bi.bi_bio);
-        bd.bd_volid = 0;
-        bd.bd_diskid = i;
+    for (int i = 0; i < bi.bi_novol; ++i) {
+        struct bioc_vol bv;
 
-        if (ioctl (fd, BIOCDISK, &bd) == -1) {
-            warn ("read_raid(%s): BIOCDISK(%d)", disk->name, i);
+        bzero (&bv, sizeof bv);
+        memcpy (&bv.bv_bio, &bi.bi_bio, sizeof bi.bi_bio);
+        bv.bv_volid = i;
+
+        if (ioctl (fd, BIOCVOL, &bv) == -1) {
+            warn ("read_raid(%s): BIOCVOL(%d)", disk->name, i);
             continue;
         }
 
-        vendor = bd.bd_vendor;
-        len_vendor = strlen (vendor);
-        if (len_vendor != 4) {
-            warnx ("read_raid(%s): unexpected vendor string: '%.32s'", disk->name, vendor);
+        if (strcmp (disk->name, bv.bv_dev) != 0)
             continue;
-        }
-        letter = vendor[len_vendor - 1];
 
-        for (size_t j = 0; j < num_disks; ++j) {
-            if (!memcmp (vendor, disks[j].name, 3)) {
-                for (size_t k = 0; k < disks[j].num_parts; ++k) {
-                    if (letter == disks[j].parts[k].letter) {
-                        disks[j].parts[k].sub = disk;
-                        goto found;
+        disk->raidstatus = bv_statusstr (bv.bv_status);
+
+        for (int j = 0; j < bv.bv_nodisk; ++j) {
+            struct bioc_disk bd;
+            size_t len_vendor;
+            char letter;
+
+            bzero (&bd, sizeof bd);
+            memcpy (&bd.bd_bio, &bi.bi_bio, sizeof bi.bi_bio);
+            bd.bd_volid = i;
+            bd.bd_diskid = j;
+
+            if (ioctl (fd, BIOCDISK, &bd) == -1) {
+                warn ("read_raid(%s): BIOCDISK(%d, %d)", disk->name, i, j);
+                continue;
+            }
+
+            len_vendor = strlen (bd.bd_vendor);
+            if (len_vendor != 4) {
+                warnx ("read_raid(%s): unexpected vendor string: %.32s", disk->name, bd.bd_vendor);
+                continue;
+            }
+            letter = bd.bd_vendor[len_vendor - 1];
+
+            for (size_t k = 0; k < num_disks; ++k) {
+                if (!memcmp (bd.bd_vendor, disks[k].name, 3)) {
+                    for (size_t l = 0; l < disks[k].num_parts; ++l) {
+                        if (letter == disks[k].parts[l].letter) {
+                            disks[k].parts[l].sub = disk;
+                            disks[k].parts[l].raidstatus = bd_statusstr(bd.bd_status);
+                            goto found;
+                        }
                     }
                 }
             }
+        found:;
         }
-    found:;
     }
-
 ret:
     close (fd);
 }
 
 static int usage (void)
 {
-    fputs ("Usage: lsblk [-ainUuV] [disk...]\n", stderr);
+    fputs ("Usage: lsblk [-abinUuV] [disk...]\n", stderr);
     return 1;
 }
 
@@ -441,11 +494,14 @@ int main (int argc, char *argv[])
     if (unveil (NULL, NULL) == -1)
         err (1, "unveil()");
 
-    while ((option = getopt (argc, argv, ":ainUuV")) != -1) {
+    while ((option = getopt (argc, argv, ":abinUuV")) != -1) {
         switch (option) {
         case 'a':
             fields = -1;
             break;
+        case 'b':
+            options |= OPT_NOBIO;
+            break;
         case 'i':
             options |= OPT_NOUNICODE;
             break;
@@ -505,8 +561,10 @@ int main (int argc, char *argv[])
 
     free (names);
 
-    for (size_t i = 0; i < num_disks; ++i) {
-        read_raid (&disks[i], disks, num_disks);
+    if (!(options & OPT_NOBIO)) {
+        for (size_t i = 0; i < num_disks; ++i) {
+            read_raid (&disks[i], disks, num_disks);
+        }
     }
 
     if (!(options & OPT_NOHEADER))