Re: [PATCH] util-linux: add minimal lsblk implementation

Osama Abdelkader via busybox <[email protected]>
Newsgroups gmane.linux.busybox
Message-ID <aXvNIqxisIsGo2g3@osama>
On Thu, Dec 04, 2025 at 10:08:46PM +0100, Osama Abdelkader wrote:
> Add a simple lsblk utility that lists information about block devices.
> Reads from /sys/block to enumerate devices and displays their size,
> type, and mount point.
> 
> Features:
> - Lists all block devices or specific devices
> - Shows device size in human-readable format (B, K, M, G, T, P)
> - Shows device type (disk, loop, rom, etc.)
> - Shows mount point if device is mounted
> - Sorts devices alphabetically
> - Minimal implementation (~2.5 kb)
> - NOFORK applet for efficiency
> 
> Signed-off-by: Osama Abdelkader <[email protected]>
> ---
>  util-linux/lsblk.c | 219 +++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 219 insertions(+)
>  create mode 100644 util-linux/lsblk.c
> 
> diff --git a/util-linux/lsblk.c b/util-linux/lsblk.c
> new file mode 100644
> index 000000000..47f217fb8
> --- /dev/null
> +++ b/util-linux/lsblk.c
> @@ -0,0 +1,219 @@
> +/* vi: set sw=4 ts=4: */
> +/*
> + * Mini lsblk implementation for busybox
> + *
> + * Licensed under GPLv2 or later, see file LICENSE in this source tree.
> + */
> +//config:config LSBLK
> +//config:	bool "lsblk (2.5 kb)"
> +//config:	default y
> +//config:	help
> +//config:	List information about all available or specified block devices.
> +
> +//applet:IF_LSBLK(APPLET_NOFORK(lsblk, lsblk, BB_DIR_USR_BIN, BB_SUID_DROP, lsblk))
> +
> +//kbuild:lib-$(CONFIG_LSBLK) += lsblk.o
> +
> +//usage:#define lsblk_trivial_usage
> +//usage:       "[BLOCKDEVICE...]"
> +//usage:#define lsblk_full_usage "\n\n"
> +//usage:       "List information about all available or specified block devices"
> +
> +#include "libbb.h"
> +#include <mntent.h>
> +
> +/* This is a NOFORK applet. Be very careful! */
> +
> +struct blockdev_info {
> +	char *name;
> +	unsigned long long size;
> +	char *type;
> +	char *mountpoint;
> +};
> +
> +static unsigned long long read_size_from_sysfs(const char *devname)
> +{
> +	char path[256];
> +	char buf[64];
> +	ssize_t len;
> +	unsigned long long size = 0;
> +
> +	snprintf(path, sizeof(path), "/sys/block/%s/size", devname);
> +	len = open_read_close(path, buf, sizeof(buf) - 1);
> +	if (len > 0) {
> +		buf[len] = '\0';
> +		/* Remove trailing newline if present */
> +		if (buf[len - 1] == '\n')
> +			buf[len - 1] = '\0';
> +		size = bb_strtoull(buf, NULL, 10);
> +		/* size is in 512-byte sectors, convert to bytes */
> +		size *= 512;
> +	}
> +	return size;
> +}
> +
> +static char *get_device_type(const char *devname)
> +{
> +	char path[256];
> +	char *buf, *type = NULL;
> +	size_t len;
> +
> +	/* Try to read from uevent */
> +	snprintf(path, sizeof(path), "/sys/block/%s/uevent", devname);
> +	buf = xmalloc_open_read_close(path, &len);
> +	if (buf) {
> +		char *p = buf;
> +		while (*p) {
> +			if (strncmp(p, "DEVTYPE=", 8) == 0) {
> +				char *end;
> +				type = xstrdup(p + 8);
> +				/* Remove newline */
> +				end = strchr(type, '\n');
> +				if (end) *end = '\0';
> +				break;
> +			}
> +			p = strchr(p, '\n');
> +			if (!p) break;
> +			p++;
> +		}
> +		free(buf);
> +	}
> +
> +	/* Fallback: guess from device name */
> +	if (!type) {
> +		if (strncmp(devname, "loop", 4) == 0)
> +			type = xstrdup("loop");
> +		else if (strncmp(devname, "ram", 3) == 0)
> +			type = xstrdup("ram");
> +		else if (strncmp(devname, "nvme", 4) == 0 || strncmp(devname, "sd", 2) == 0 ||
> +		         strncmp(devname, "hd", 2) == 0 || strncmp(devname, "vd", 2) == 0)
> +			type = xstrdup("disk");
> +		else
> +			type = xstrdup("disk");
> +	}
> +
> +	return type;
> +}
> +
> +static char *get_mountpoint(const char *devname)
> +{
> +	char devpath[256];
> +	struct mntent *mnt;
> +	FILE *mtab;
> +	char *mountpoint = NULL;
> +
> +	snprintf(devpath, sizeof(devpath), "/dev/%s", devname);
> +	mtab = setmntent(bb_path_mtab_file, "r");
> +	if (mtab) {
> +		while ((mnt = getmntent(mtab)) != NULL) {
> +			if (strcmp(mnt->mnt_fsname, devpath) == 0) {
> +				mountpoint = xstrdup(mnt->mnt_dir);
> +				break;
> +			}
> +		}
> +		endmntent(mtab);
> +	}
> +
> +	return mountpoint;
> +}
> +
> +static void print_size(unsigned long long size)
> +{
> +	const char *units[] = {"B", "K", "M", "G", "T", "P"};
> +	int unit_idx = 0;
> +	double dsize = size;
> +
> +	while (dsize >= 1024.0 && unit_idx < 5) {
> +		dsize /= 1024.0;
> +		unit_idx++;
> +	}
> +
> +	if (unit_idx == 0)
> +		printf("%llu", size);
> +	else
> +		printf("%.1f%c", dsize, units[unit_idx][0]);
> +}
> +
> +static int compare_devices(const void *a, const void *b)
> +{
> +	const struct blockdev_info *da = (const struct blockdev_info *)a;
> +	const struct blockdev_info *db = (const struct blockdev_info *)b;
> +	return strcmp(da->name, db->name);
> +}
> +
> +int lsblk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
> +int lsblk_main(int argc UNUSED_PARAM, char **argv)
> +{
> +	DIR *dir;
> +	struct dirent *entry;
> +	struct blockdev_info *devices = NULL;
> +	int count = 0;
> +	int i;
> +	unsigned opt;
> +
> +	opt = getopt32(argv, "");
> +	argv += optind;
> +
> +	/* If specific devices are requested, process them */
> +	if (*argv) {
> +		while (*argv) {
> +			char *devname = *argv;
> +			/* Remove /dev/ prefix if present */
> +			if (strncmp(devname, "/dev/", 5) == 0)
> +				devname += 5;
> +
> +			devices = xrealloc_vector(devices, 4, count);
> +			devices[count].name = xstrdup(devname);
> +			devices[count].size = read_size_from_sysfs(devname);
> +			devices[count].type = get_device_type(devname);
> +			devices[count].mountpoint = get_mountpoint(devname);
> +			count++;
> +			argv++;
> +		}
> +	} else {
> +		/* Read all devices from /sys/block */
> +		dir = opendir("/sys/block");
> +		if (!dir)
> +			bb_simple_perror_msg_and_die("/sys/block");
> +
> +		while ((entry = readdir(dir)) != NULL) {
> +			if (DOT_OR_DOTDOT(entry->d_name))
> +				continue;
> +
> +			devices = xrealloc_vector(devices, 4, count);
> +			devices[count].name = xstrdup(entry->d_name);
> +			devices[count].size = read_size_from_sysfs(entry->d_name);
> +			devices[count].type = get_device_type(entry->d_name);
> +			devices[count].mountpoint = get_mountpoint(entry->d_name);
> +			count++;
> +		}
> +		closedir(dir);
> +	}
> +
> +	/* Sort devices by name */
> +	qsort(devices, count, sizeof(struct blockdev_info), compare_devices);
> +
> +	/* Print header */
> +	printf("%-15s %8s %-6s %s\n", "NAME", "SIZE", "TYPE", "MOUNTPOINT");
> +
> +	/* Print devices */
> +	for (i = 0; i < count; i++) {
> +		printf("%-15s ", devices[i].name);
> +		print_size(devices[i].size);
> +		printf(" %-6s ", devices[i].type);
> +		if (devices[i].mountpoint)
> +			printf("%s", devices[i].mountpoint);
> +		printf("\n");
> +
> +		free(devices[i].name);
> +		free(devices[i].type);
> +		if (devices[i].mountpoint)
> +			free(devices[i].mountpoint);
> +	}
> +
> +	if (devices)
> +		free(devices);
> +
> +	return fflush_all();
> +}
> +
> -- 
> 2.43.0
> 

ping.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.