Re: [PATCH] util-linux: add minimal lsblk implementation
Denys Vlasenko via busybox <[email protected]>
| Newsgroups | gmane.linux.busybox |
|---|---|
| Message-ID | <CAK1hOcPN7rvfbyRR+NRbf71QZA+E5UKZ_anOJq-ZoK-Te2wELg@mail.gmail.com> |
On Thu, Jan 29, 2026 at 10:12 PM Osama Abdelkader via busybox <[email protected]> wrote: > > 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) { Why not get rid of while() loop and just do one search? is_prefixed_with(buf, "DEVTYPE=") || strstr(buf, "\nDEVTYPE=") > > + char *end; > > + type = xstrdup(p + 8); > > + /* Remove newline */ > > + end = strchr(type, '\n'); > > + if (end) *end = '\0'; > > + break; Instead, you can: p += 8; end = strchrnul(type, '\n'); *end++ = '\0'; memmove(buf, p, end - p); return xrealloc(buf, end - p); If you do this, "if (!type)' later is always true and doesn't need to be checked. > > + } > > + 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"); We don't need to always malloc the value. Just return constant (not-allocated) strings. > > +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; > > +} Check whether find_mount_point() can be used for this. > > +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]); > > +} The above can be done with smart_ulltoa5() > > +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, ""); Variable 'opt' is unused. > > + 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); strdup'ing is not needed > > + 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"); util-linux's lsblk shows partitions too. They can be found if you scan "/sys/class/block" instead. (It also doesn't show "loop" - maybe shows only if it's attached?) > > + if (!dir) > > + bb_simple_perror_msg_and_die("/sys/block"); Use xopendir() > > + 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); if (ENABLE_FEATURE_CLEAN_UP) closedir(dir); > > + /* 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"); This can be combined into fewer printf's > > + free(devices[i].name); > > + free(devices[i].type); > > + if (devices[i].mountpoint) > > + free(devices[i].mountpoint); Just do not bother. (Allows optimization where strings are *sometimes* malloced). > > + } > > + > > + if (devices) > > + free(devices); > > + > > + return fflush_all(); fflush_stdout_and_exit_SUCCESS(); _______________________________________________ busybox mailing list [email protected] https://lists.busybox.net/mailman/listinfo/busybox