Lightweight disk identity, mount, and S.M.A.R.T. extraction toolkit
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
Sergey Kiselev 9002d15a55 SAT: implement 12-byte fallback recovery for legacy JMicron bridges 3 months ago
examples examples/tree_view.pl: Fix typo 3 months ago
include SRC: fix indents in sources 3 months ago
modules Perl: fix runtime undefined symbol errors in ports staging 3 months ago
src SAT: implement 12-byte fallback recovery for legacy JMicron bridges 3 months ago
.gitignore Initial commit 3 months ago
LICENSE Fix LICENSE typo 3 months ago
Makefile Build: fix race condition in parallel make for ports framework 3 months ago
README.md Docs: correct Perl example in README.md 3 months ago

README.md

bsdiskinfo

A lightweight, modular, and dependency-free FreeBSD C library along with high-performance native Lua bindings for extracting precise hardware disk identity properties, active filesystem mounts, and raw S.M.A.R.T. metrics.

The architecture strictly separates the system-level extraction engine from execution layers, allowing easy integration with multiple runtime environments (Lua, Python, Go, etc.) without inflating overhead or calling external binaries.

Features

  • Advanced Identity Verification: Direct hardware query via libcam taskfiles. Successfully bypasses standard USB bridge constraints using SCSI SAT ATA PASS-THROUGH (16) to pull authentic models, serial numbers, firmware versions, WWN identifiers, and native sector geometry configs.
  • Granular Type Classification: Employs robust heuristics to accurately identify target media storage profiles (ssd, hdd, nvme, sdcard, or standard usb drive tokens).
  • Fast Mount Traversal: Scans active filesystem topologies directly via kernel getfsstat wrappers with non-blocking configurations, safely mitigating hung network storage paths.
  • Isolated S.M.A.R.T. Matrix Layout: Exposes full 30-slot attribute maps to user space, eliminating raw hex-parsing complexities inside scripting architectures.

Repository Structure

bsdiskinfo
├── examples
│   ├── tree_view.lua        # Complex hardware tree mapping layout example
│   └── tree_view.pl
├── include
│   └── bsdiskinfo.h         # Public standalone C API header
├── modules
│   ├── lua
│   │   ├── lua_bsdiskinfo.c # High-performance C-to-Lua binding translator 
│   │   └── Makefile         # Dynamic Lua module build configurations
│   └── perl
│       ├── lib
│       │   └── bsdiskinfo.pm # Public Perl module interface and POD docs
│       ├── bsdiskinfo.xs     # Core C-to-Perl XS translator bridge
│       └── Makefile.PL       # Perl ExtUtils::MakeMaker build script
├── src
│   ├── cli.c                # POSIX-compliant administrative CLI testbed
│   ├── libbsdiskinfo.c      # Core library implementation layers
│   └── smart.h              # Shared structural S.M.A.R.T. hardware definitions
├── LICENSE                  # BSD 2-Clause Simplified License
└── Makefile                 # Native FreeBSD root pmake script

Building and Installation

The build workflow utilizes native FreeBSD bmake engines and standard pkg-config routines to automatically discover installed platform toolchains.

Build Everything (Library + CLI + Lua Module)

make

Build Library Only (Headless C Environment)

make WITHOUT_CLI=1 WITHOUT_LUA=1

Build Targeting a Specific Lua Environment

make LUA_VER=5.1

Build without Perl XS module bindings

make WITHOUT_PERL=1

Build targeting a specific custom Perl layout

cd modules/perl && perl Makefile.PL PREFIX=/my/path && make

Standard System Deployment

sudo make install

Installs components under /usr/local/ paths (lib/, include/, sbin/, and corresponding Lua runtime folders).

Installation via FreeBSD Ports (Custom Overlay)

The project is packaged for FreeBSD through a dedicated, standalone ports repository overlay. If you manage a local ports overlay or want to build a clean native package via poudriere/bmake, add the port definition from the tracking tree repository:

To build and install the toolkit from the overlay manually:

# Navigate to the port directory inside your overlay tree
cd /usr/ports/devel/bsdiskinfo

# Configure, build, and install with selected bindings (Lua/Perl)
sudo make config
sudo make install clean

Usage Profiles

Binary CLI Diagnostic Tool

You can invoke the isolated diagnostics executable directly via administrative flags (-i identity, -m mounts, -s S.M.A.R.T.):

# Print identity and active storage mount points for a block device node
./bsdiskinfo-cli -im ada0

Lua Scripting Layer Integration

A production-ready implementation mapping out the whole physical ecosystem using devel/lua-sysctl looks like this:

local bsdiskinfo = require("bsdiskinfo")
local sysctl = require("sysctl")

local disks_str = sysctl.get("kern.disks") or ""
for disk in disks_str:gmatch("%S+") do
    local props = bsdiskinfo.get_properties(disk)
    if props then
        print(string.format("Drive: /dev/%s [Model: %s, Serial: %s, Type: %s]", 
            disk, props.model, props.serial, props.type))
            
        -- Process mount objects safely
        local mounts = bsdiskinfo.get_mounts(disk) or {}
        for _, mnt in ipairs(mounts) do
            print(string.format("  └─ Mounted partition %s at %s", mnt.device, mnt.path))
        end
    end
end

(See more extensive processing inside examples/tree_view.lua).

Perl5 Scripting Layer Integration

A production-ready Perl 5 execution layer maps out the storage ecosystem using high-performance native XS structures. For direct kernel MIB tree traversal without spawning shell processes, the sysutils/p5-BSD-Sysctl port is highly recommended as a companion dependency.

The integration utilizes standard UTF-8 stream handling to seamlessly draw complex nested topology layouts:

#!/usr/bin/env perl

use strict;
use warnings;
use utf8;
use open qw/:std :encoding(utf8)/; # Ensures proper UTF-8 printing bounds

use bsdiskinfo;
use BSD::Sysctl; # Native FreeBSD kernel MIB traversal bindings

# Query available physical drives straight from the kernel tree
my $disks_str = BSD::Sysctl::sysctl('kern.disks') || "";

for my $disk (sort (split /\s+/,$disks_str)) {
    # Protect routing logic by verifying character device existence
    next unless bsdiskinfo::exists($disk);

    my $props = get_properties($disk);
    if ($props) {
        print "Drive: /dev/$disk [Model: $props->{model}, Type: $props->{type}]\n";

        # Traverse active slices and mounted partitions
        my $mounts = get_mounts($disk) || [];
        for my $mnt (@$mounts) {
            print "  └─ Partition $mnt->{device} mounted at $mnt->{path} ($mnt->{fstype})\n";
        }
    }
}

(See examples/tree_view.pl for an expanded implementation containing full raw S.M.A.R.T. matrix unpacking).

Device Access & Permissions

By default, raw character device nodes under /dev/ (e.g., /dev/ada*, /dev/da*) are strictly accessible only by the root user in FreeBSD. Running the binary CLI tool or calling the Lua module as an unprivileged user will fail during transport library initialization (Permission denied).

To safely grant a specific non-root user or daemon group (e.g., operator) execution rights to query hardware parameters without escalating system-wide privileges, configure the native FreeBSD devfs rules subsystem.

1. Append Rules to /etc/devfs.rules

Create or modify the local rulesets configuration file to assign proper ownership and read/write accessibility maps to storage targets:

[local_devices=10]
add path 'ada*' mode 0660 group operator
add path 'da*'   mode 0660 group operator
add path 'nvme*' mode 0660 group operator

2. Enable the Ruleset inside /etc/rc.conf

Instruct the kernel subsystem init profiles to mount the default active /dev structure bound strictly against your custom ruleset index sequence:

devfs_system_ruleset="local_devices"

3. Apply Changes Immediately

Restart the system-wide device filesystem management daemon wrapper layer to evaluate state mutations on the fly:

sudo service devfs restart

Ensure your unprivileged target runtime user account belongs to the configured destination group mapping bounds (e.g., pw groupmod operator -m myuser).

License

This project is licensed under the terms of the BSD 2-Clause Simplified License. See LICENSE for details.