When I have to work on Linux systems I usually miss many nice FreeBSD tools such as these for example to name the few:

sockstat

gstat

top -b -o res

top -m io -o total

usbconfig

rcorder

beadm/ bectl

idprio/ rtprio

… but sometimes – which rarely happens – Linux has some very useful tool that is not available on FreeBSD. An example of such tool is lsblk(8) that does one thing and does it quite well – lists block devices and their contents. It has some problems like listing a disk that is entirely used under ZFS pool on which lsblk(8) displays two partitions instead of information about ZFS just being there – but we all know how much in some circles the CDDL licensed ZFS is unloved in that GPL world.

Example lsblk(8) output from Linux system:

$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sr0 11:0 1 1024M 0 rom sda 8:0 0 931.5G 0 disk |-sda1 8:1 0 500M 0 part /boot `-sda2 8:2 0 931G 0 part |-vg_local-lv_root (dm-0) 253:0 0 50G 0 lvm / |-vg_local-lv_swap (dm-1) 253:1 0 17.7G 0 lvm [SWAP] `-vg_local-lv_home (dm-2) 253:2 0 1.8T 0 lvm /home sdc 8:32 0 232.9G 0 disk `-sdc1 8:33 0 232.9G 0 part `-md1 9:1 0 232.9G 0 raid10 /data sdd 8:48 0 232.9G 0 disk `-sdd1 8:49 0 232.9G 0 part `-md1 9:1 0 232.9G 0 raid10 /data

What FreeBSD offers in this department? The camcontrol(8) and geom(8) commands are available. You can also use gpart(8) command to list partitions. Below you will find output of these commands from my single disk laptop. Please note that because of WordPress limitations I need to change all > < characters to ] [ ones in the commands outputs.

# camcontrol devlist [Samsung SSD 860 EVO mSATA 1TB RVT41B6Q] at scbus1 target 0 lun 0 (ada0,pass0) % geom disk list Geom name: ada0 Providers: 1. Name: ada0 Mediasize: 1000204886016 (932G) Sectorsize: 512 Mode: r1w1e2 descr: Samsung SSD 860 EVO mSATA 1TB lunid: 5002538e402b4ddd ident: S41PNB0K303632D rotationrate: 0 fwsectors: 63 fwheads: 1 # gpart show => 40 1953525088 ada0 GPT (932G) 40 409600 1 efi (200M) 409640 1024 2 freebsd-boot (512K) 410664 984 - free - (492K) 411648 1953112064 3 freebsd-zfs (931G) 1953523712 1416 - free - (708K)

They provide needed information in acceptable manner but only on systems with small amount of disks. What if you would like to display a summary of all system drives contents? This is where lsblk.sh comes handy. While lsblk(8) has many interesting features like --perms / --scsi / --inverse modes I focused to provide only the basic feature – to list the system block devices and their contents. As I have long and pleasing experience with writing shell scripts such as sysutils/beadm or sysutils/automount I though that writing lsblk.sh may be a good idea. I actually ‘open-sourced’ or should I say shared that project/idea in 2016 in this thread lsblk(8) Command for FreeBSD on FreeBSD Forums but lack of time really slowed that ‘side project’ development pace. I finally got back to it to finish it.

The lsblk.sh is generally small and simple shell script which tales less then 400 SLOC.

Here is example output of lsblk.sh command from my single disk laptop.

% lsblk.sh DEVICE MAJ:MIN SIZE TYPE LABEL MOUNT ada0 0:5b 932G GPT - - ada0p1 0:64 200M efi efiboot0 [UNMOUNTED] ada0p2 0:65 512K freebsd-boot gptboot0 - [FREE] -:- 492K - - - ada0p3 0:66 931G freebsd-zfs zfs0 [ZFS] [FREE] -:- 708K - - -

Same output in graphical window.

Below you will find an example lsblk.sh output from server with two system SSD drives ( da0 / da1 ) and two HDD data drives ( da2 / da3 ).

# lsblk.sh DEVICE MAJ:MIN SIZE TYPE LABEL MOUNT da0 0:be 224G GPT - - da0p1 0:15a 200M efi efiboot0 [UNMOUNTED] da0p2 0:15b 512K freebsd-boot gptboot0 - [FREE] -:- 492K - - - da0p3 0:15c 2.0G freebsd-swap swap0 [UNMOUNTED] da0p4 0:15d 221G freebsd-zfs zfs0 [ZFS] [FREE] -:- 580K - - - da1 0:bf 224G GPT - - da1p1 0:16a 200M efi efiboot1 [UNMOUNTED] da1p2 0:16b 512K freebsd-boot gptboot1 - [FREE] -:- 492K - - - da1p3 0:16c 2.0G freebsd-swap swap1 [UNMOUNTED] da1p4 0:16d 221G freebsd-zfs zfs1 [ZFS] [FREE] -:- 580K - - - da2 0:c0 11T GPT - - da2p1 0:16e 11T freebsd-zfs - [ZFS] [FREE] -:- 1.0G - - - da3 0:c1 11T GPT - - da3p1 0:16f 11T freebsd-zfs - [ZFS] [FREE] -:- 1.0G - - -

Below you will find other examples from other systems I have tested lsblk.sh on.

While lsblk.sh is not the fastest script on Earth (because of all the needed parsing) it does its job quite well. If you would like to install it in your system just type the command below:

# fetch -o /usr/local/bin/lsblk https://raw.githubusercontent.com/vermaden/scripts/master/lsblk.sh # chmod +x /usr/local/bin/lsblk # hash -r || rehash # lsblk

If I got time which other original Linux lsblk(8) subcommand/option/argument is worth adding to the lsblk.sh script? 🙂

Regards.

UPDATE 1 – Added USAGE/HELP Information

Just added some usage information that can be displayed by specifying one of these as argument:

h

-h

--h

help

-help

--help

IMHO writing man page for such simple utility is needless. I think I will create dedicated man page when lsblk.sh tool will grow in size and options to comparable with the Linux lsblk(8) equivalent. Here is how it looks.

# lsblk.sh --help usage: BASIC USAGE INFORMATION ======================= # lsblk.sh [DISK] example(s): LIST ALL BLOCK DEVICES IN SYSTEM -------------------------------- # lsblk.sh DEVICE MAJ:MIN SIZE TYPE LABEL MOUNT ada0 0:5b 932G GPT - - ada0p1 0:64 200M efi efiboot0 [UNMOUNTED] ada0p2 0:65 512K freebsd-boot gptboot0 - [FREE] -:- 492K - - - ada0p3 0:66 931G freebsd-zfs zfs0 [ZFS] LIST ONLY da1 BLOCK DEVICE -------------------------- # lsblk.sh da1 DEVICE MAJ:MIN SIZE TYPE LABEL MOUNT da1 0:80 2.0G MBR - - da1s1 0:80 2.0G freebsd - - da1s1a 0:81 1.0G freebsd-ufs root / da1s1b 0:82 1.0G freebsd-swap swap SWAP hint(s): DISPLAY ALL DISKS IN SYSTEM --------------------------- # sysctl kern.disks kern.disks: ada0 da0 da1

Regards.

UPDATE 2 – Code Reorganization and 75% Rewrite

… at least this is what git(1) tries to tell me after commit message.

% git commit (...) [master 12fd4aa] Rework entire flow. Split code into functions. Add many useful comments. In other words its 2.0 version. 1 file changed, 494 insertions(+), 505 deletions(-) rewrite lsblk.sh (75%)

After several productive hours new incarnation of lsblk.sh is now available.

It has similar SLOC but its now smaller by a quarter … while doing more and with better accuracy. Great example why “less is more.”

% wc scripts/lsblk.sh.OLD 491 2201 19721 scripts/lsblk.sh.OLD % wc scripts/lsblk.sh 494 1871 15472 scripts/lsblk.sh

Things that does not have simple solution are described below.

One of them is ‘double’ label for FAT filesystems. We have both /dev/gpt/efiboot0 label and FAT label is named EFISYS . We have to choose something here. As not all FAT filesystems have label I have chosen the GPT label.

% glabel status | grep ada0p1 gpt/efiboot0 N/A ada0p1 msdosfs/EFISYS N/A ada0p1

I was also not able to cover FUSE mounts. When you mount – for example – the /dev/da0 device as NTFS (with ntfs-3g ) or exFAT (with mount.exfat ) there is no visible difference in mount(8) output.

% mount -t fusefs /dev/fuse on /mnt/ntfs (fusefs) /dev/fuse on /mnt/exfat (fusefs)

When I mount such filesystem by my daemon (like sysutils/automount ) I keep track of what device have been mounted to which directory in the /var/run/automount.state file. Then when I get the detach event for /dev/da0 device I know what to u(n)mount … but when I only have /dev/fuse device its just not possible.

… or maybe YOU know any way of extracting information from /dev/fuse (or generally from FUSE) what device is mounted where?

Now little presentation after update.

Here are various non ZFS filesystems mounted.

% mount -t nozfs devfs on /dev (devfs, local, multilabel) linprocfs on /compat/linux/proc (linprocfs, local) tmpfs on /compat/linux/dev/shm (tmpfs, local) /dev/label/ASD on /mnt/tmp (msdosfs, local) /dev/fuse on /mnt/ntfs (fusefs) /dev/md0s1f on /mnt/ufs.other (ufs, local) /dev/gpt/OTHER on /mnt/fat.other (msdosfs, local) /dev/md0s1a on /mnt/ufs (ufs, local)

… and here is how now lsblk.sh displays them.

% lsblk.sh DEVICE MAJ:MIN SIZE TYPE LABEL MOUNT ada0 0:56 932G GPT - - ada0p1 0:64 200M efi gpt/efiboot0 - ada0p2 0:65 512K freebsd-boot gpt/gptboot0 - [FREE] -:- 492K - - - ada0p3 0:66 931G freebsd-zfs - [ZFS] [FREE] -:- 708K - - - md0 0:28f 1.0G MBR - - md0s1 0:294 512M freebsd - - md0s1a 0:29a 100M freebsd-ufs root /mnt/ufs md0s1b 0:29b 32M freebsd-swap label/swap SWAP md0s1e 0:29c 64M freebsd-ufs - - md0s1f 0:29d 316M freebsd-ufs - /mnt/ufs.other md0s2 0:296 256M ntfs - - md0s3 0:297 256M fat32 msdosfs/ONE - md1 0:2a4 1.0G msdosfs LARGE md2 0:298 2.0G GPT - - md2p1 0:29f 2.0G ms-basic-data gpt/OTHER /mnt/fat.other

I used some file based memory devices for this. Now by default lsblk.sh also displays memory disks contents.

% mdconfig.sh -l md0 vnode 1024M /home/vermaden/FILE md2 vnode 2048M /home/vermaden/FILE.GPT md1 vnode 1024M /home/vermaden/FILER

Here is how it looks in the xterm(1) terminal.

Regards.

UPDATE 3 – Added geli(8) Support

I thought that adding geli(8) support may be useful. The latest lsblk.sh now avoids code duplication for MOUNT and LABEL detection (moved into single unified function). Also added more comments for code readability and some minor fixes … and its again smaller 🙂

% wc lsblk.sh.1.0 491 2201 19721 lsblk.sh.1.0 % wc lsblk.sh.2.0 493 1861 15415 lsblk.sh.2.0 % wc lsblk.sh 488 1820 15332 lsblk.sh

About 40% (according to git commit was changed this time (191 insertions and 196 deletions).

# git commit (...) [master ec9985a] Add geli(8) support. Avoid code duplication and move MOUNT/LABEL detection into function. More comments. Minor fixes. 1 file changed, 191 insertions(+), 196 deletions(-)

Also forgot to mention that now lsblk.sh thanks to smart optimizations (like not doing things twice and aggregating grep(1) | awk(1) pipes into single awk(1) queries) runs 3 times faster then the initial version 🙂

New output with geli(8) support below.

Regards.

UPDATE 4 – Added fuse(8) Support

As I wrote in the UPDATE 2 keeping track of what is mounted and where under fuse(8) is very hard as all mounted devices magically become /dev/fuse after mount is done.

After little research I found that this information (what really is mounted where by using fuse(8) interface under FreeBSD) is available after mounting procfs filesystem under /proc . You just need to cat cmdline entry for all PIDs of ntfs-3g . Its not perfect but the information at least is available.

# mount -t procfs proc /proc # ps ax | grep ntfs-3g 45995 - Is 0:00.00 ntfs-3g /dev/md1s2 /mnt/ntfs 59607 - Is 0:00.00 ntfs-3g /dev/md3 /mnt/ntfs.another 83323 - Is 0:00.00 ntfs-3g /dev/md3 /mnt/ntfs.another # pgrep ntfs-3g 59607 83323 45995 % pgrep ntfs-3g | while read I; do cat /proc/$I/cmdline; echo; done ntfs-3g/dev/md3/mnt/ntfs.another ntfs-3g/dev/md3/mnt/ntfs.another ntfs-3g/dev/md1s2/mnt/ntfs

This was the code prototype that worked for fuse(8) mountpoints detection.

if [ -e /proc/0/status ] then FUSE_MOUNTS=$( while read PID do cat /proc/${PID}/cmdline echo done << ________EOF $( pgrep ntfs-3g ) ________EOF ) FUSE_MOUNTS=$( echo "${FUSE_MOUNTS}" | sort -u ) FUSE_MOUNTS=$( echo "${FUSE_MOUNTS}" | sed 's|ntfs-3g||g' ) FUSE_CHECKS=$( echo "${FUSE_MOUNTS}" | grep /dev/${TARGET}/ ) if [ "${FUSE_CHECKS}" != "" ] then MOUNT=$( echo "${FUSE_CHECKS}" | sed "s|/dev/${TARGET}||g" ) fi fi fi

… and I have just realized that I found new (better) way of getting that information without mounting /proc filesystem – all you need to do is to display the ntfs-3g processes with their command line arguments, for example like that:

% ps -p $( pgrep ntfs-3g | tr '

' ',' | sed '$s/.$//' ) -o command | sed 1d ntfs-3g /dev/md1s2 /mnt/ntfs ntfs-3g /dev/md3 /mnt/ntfs.another ntfs-3g /dev/md3 /mnt/ntfs.another

So after I also thought that its only for NTFS ( ntfs-3g(8) process) I also added exFAT support by searching for mount.exfat PIDs as well. The fuse(8) mount point detection works now for both NTFS and exFAT filesystems … and code to support it is even shorter.

# TRY fuse(8) MOUNTS FROM PROCESSES if [ "${MOUNT_FOUND}" != "1" ] then FUSE_PIDS=$( pgrep mount.exfat ntfs-3g | tr '

' ',' | sed '$s/.$//' ) FUSE_MOUNTS=$( ps -p "${FUSE_PIDS}" -o command | sed 1d | sort -u ) MOUNT=$( echo "${FUSE_MOUNTS}" | grep "/dev/${TARGET} " | awk '{print $3}' ) fi

I also changed how MAJOR and MINOR numbers are displayed – from HEX to DEC – as it is on Linux. The FreeBSD’s ls(1) from Base System displays these as HEX – for example you will get 0x2af value:

% ls -l /dev/md4 crw-rw---- 1 root operator 0x2af 2019.09.29 05:18 /dev/md4

But do the same with GNU equivalent by using gls(1) from FreeBSD Ports (from sysutils/coreutils package) and it shows MAJOR and MINOR in DEC values. The gls(1) is just ls(1) from the Linux world but as ls(1) name is already ‘taken’ by FreeBSD’s Base System tool the FreeBSD developers/maintainers add ‘g’ letter (for GNU) to distinguish them.

% gls -l /dev/md4 crw-rw---- 1 root 2, 175 2019-09-29 05:18 /dev/md4

… and they are also easier/faster to get with stat(1) tool.

MAJ=$( stat -f "%Hr" /dev/${DEV} ) MIN=$( stat -f "%Lr" /dev/${DEV} )

Latest lsblk.sh looks like that now.

… that is why I did not (yet) added lsblk.sh to the FreeBSD Ports. Several new versions with important features span across just two days 🙂

Regards.

UPDATE 5 – Another 69% Rewrite

After messing with gpart(8) more I found that using its -p flag which is a game changer. The difference is that with -p flag it displays names along partitions – its no longer needed to find the PREFIX and ‘create’ partition names.

Default gpart(8) output.

# gpart show md0 => 63 2097089 md0 MBR (1.0G) 63 1048576 1 freebsd (512M) 1048639 524288 2 ntfs (256M) 1572927 524225 3 fat32 (256M)

Output of gpart(8) with -p flag.

# gpart show -p md0 => 63 2097089 md0 MBR (1.0G) 63 1048576 md0s1 freebsd (512M) 1048639 524288 md0s2 ntfs (256M) 1572927 524225 md0s3 fat32 (256M)

That discovery implicated a quite large rewrite of lsblk.sh . The git commit estimates this as 69% code rewrite.

# git commit (...) (...) 1 file changed, 487 insertions(+), 501 deletions(-) rewrite lsblk.sh (69%)

The latest lsblk.sh has now these features:

Previous bugs fixed.

Detects exFAT labels.

Is now 20% faster.

Has less 10% SLOC.

Has less 15% of code.

Handles bsdlabel(8) on entire device properly.

on entire device properly. Handles exFAT on entire device properly.

The difference in code is shown below.

# wc lsblk.sh 487 1791 13705 lsblk.sh # wc lsblk.sh.OLD 544 1931 16170 lsblk.sh.OLD

Latest lsblk.sh looks as usual but I now use ‘ - ‘ instead of ‘ [UNMOUNTED] ‘ one.

EOF