mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 18:58:40 +00:00
add lsblk disk listing command
This commit is contained in:
parent
e48f3674c7
commit
9809e54e77
1 changed files with 322 additions and 0 deletions
322
src/userland/sys/lsblk.c
Normal file
322
src/userland/sys/lsblk.c
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <syscall.h>
|
||||
|
||||
#define LSBLK_MAX_DISKS 32
|
||||
#define LSBLK_SECTOR_SIZE 512ULL
|
||||
#define LSBLK_KB 1024ULL
|
||||
#define LSBLK_MB (1024ULL * 1024ULL)
|
||||
#define LSBLK_GB (1024ULL * 1024ULL * 1024ULL)
|
||||
|
||||
static int streq(const char *a, const char *b) {
|
||||
return strcmp(a, b) == 0;
|
||||
}
|
||||
|
||||
static int starts_with(const char *s, const char *prefix) {
|
||||
while (*prefix) {
|
||||
if (*s++ != *prefix++) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *device_name_arg(const char *arg) {
|
||||
if (starts_with(arg, "/dev/")) return arg + 5;
|
||||
return arg;
|
||||
}
|
||||
|
||||
static void format_size(uint64_t bytes, char *out, size_t out_len, int compact) {
|
||||
const char *sep = compact ? "" : " ";
|
||||
uint64_t unit = 1;
|
||||
const char *suffix = "B";
|
||||
|
||||
if (bytes >= LSBLK_GB) {
|
||||
unit = LSBLK_GB;
|
||||
suffix = "GB";
|
||||
} else if (bytes >= LSBLK_MB) {
|
||||
unit = LSBLK_MB;
|
||||
suffix = "MB";
|
||||
} else if (bytes >= LSBLK_KB) {
|
||||
unit = LSBLK_KB;
|
||||
suffix = "KB";
|
||||
}
|
||||
|
||||
if (unit == 1) {
|
||||
snprintf(out, out_len, "%llu%s%s", (unsigned long long)bytes, sep, suffix);
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t whole = bytes / unit;
|
||||
uint64_t rem = bytes % unit;
|
||||
uint64_t tenth = (rem * 10ULL + unit / 2ULL) / unit;
|
||||
|
||||
if (tenth >= 10ULL) {
|
||||
whole++;
|
||||
tenth = 0;
|
||||
}
|
||||
|
||||
if (tenth == 0) {
|
||||
snprintf(out, out_len, "%llu%s%s", (unsigned long long)whole, sep, suffix);
|
||||
} else {
|
||||
snprintf(out, out_len, "%llu.%llu%s%s", (unsigned long long)whole, (unsigned long long)tenth, sep, suffix);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t disk_size_bytes(const disk_info_t *d) {
|
||||
return (uint64_t)d->total_sectors * LSBLK_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
static int is_child_partition(const disk_info_t *disk, const disk_info_t *part) {
|
||||
size_t len;
|
||||
|
||||
if (disk->is_partition || !part->is_partition) return 0;
|
||||
|
||||
len = strlen(disk->devname);
|
||||
if (strncmp(part->devname, disk->devname, len) != 0) return 0;
|
||||
|
||||
return part->devname[len] >= '0' && part->devname[len] <= '9';
|
||||
}
|
||||
|
||||
static int child_count(const disk_info_t *disk, disk_info_t *items, int count) {
|
||||
int children = 0;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (is_child_partition(disk, &items[i])) children++;
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
static void print_tree_device(const disk_info_t *d, const char *branch) {
|
||||
char size[24];
|
||||
const char *type = d->is_partition ? "part" : "disk";
|
||||
|
||||
format_size(disk_size_bytes(d), size, sizeof(size), 0);
|
||||
|
||||
if (d->is_partition) {
|
||||
if (branch[0]) printf("%s %-8s %8s %s", branch, d->devname, size, type);
|
||||
else printf("/dev/%-8s %8s %s", d->devname, size, type);
|
||||
if (d->is_fat32) printf(" FAT32");
|
||||
if (d->label[0]) printf(" %s", d->label);
|
||||
if (d->is_esp) printf(" [ESP]");
|
||||
printf("\n");
|
||||
} else {
|
||||
printf("/dev/%-8s %8s %s\n", d->devname, size, type);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_tree_disk(const disk_info_t *disk, disk_info_t *items, int count) {
|
||||
int children = child_count(disk, items, count);
|
||||
int seen = 0;
|
||||
|
||||
print_tree_device(disk, "");
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!is_child_partition(disk, &items[i])) continue;
|
||||
seen++;
|
||||
print_tree_device(&items[i], seen == children ? "└─" : "├─");
|
||||
}
|
||||
}
|
||||
|
||||
static void print_raw_device(const disk_info_t *d) {
|
||||
char size[24];
|
||||
|
||||
format_size(disk_size_bytes(d), size, sizeof(size), 1);
|
||||
printf("/dev/%s %s %s", d->devname, size, d->is_partition ? "part" : "disk");
|
||||
|
||||
if (d->is_partition) {
|
||||
if (d->is_fat32) printf(" FAT32");
|
||||
if (d->label[0]) printf(" %s", d->label);
|
||||
if (d->is_esp) printf(" ESP");
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void print_raw_disk(const disk_info_t *disk, disk_info_t *items, int count) {
|
||||
print_raw_device(disk);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (is_child_partition(disk, &items[i])) print_raw_device(&items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void json_string(const char *s) {
|
||||
putchar('"');
|
||||
|
||||
while (*s) {
|
||||
if (*s == '"' || *s == '\\') {
|
||||
putchar('\\');
|
||||
putchar(*s);
|
||||
} else if (*s == '\n') {
|
||||
printf("\\n");
|
||||
} else {
|
||||
putchar(*s);
|
||||
}
|
||||
s++;
|
||||
}
|
||||
|
||||
putchar('"');
|
||||
}
|
||||
|
||||
static void json_device_fields(const disk_info_t *d) {
|
||||
char size[24];
|
||||
char name[24];
|
||||
|
||||
format_size(disk_size_bytes(d), size, sizeof(size), 0);
|
||||
snprintf(name, sizeof(name), "/dev/%s", d->devname);
|
||||
|
||||
printf("\"name\":");
|
||||
json_string(name);
|
||||
printf(",\"size\":");
|
||||
json_string(size);
|
||||
printf(",\"type\":");
|
||||
json_string(d->is_partition ? "part" : "disk");
|
||||
printf(",\"fstype\":");
|
||||
json_string(d->is_fat32 ? "FAT32" : "");
|
||||
printf(",\"label\":");
|
||||
json_string(d->label);
|
||||
printf(",\"flags\":[");
|
||||
if (d->is_esp) json_string("ESP");
|
||||
printf("]");
|
||||
}
|
||||
|
||||
static void print_json_partition(const disk_info_t *d) {
|
||||
printf("{");
|
||||
json_device_fields(d);
|
||||
printf("}");
|
||||
}
|
||||
|
||||
static void print_json_disk(const disk_info_t *disk, disk_info_t *items, int count) {
|
||||
int seen = 0;
|
||||
|
||||
printf("{");
|
||||
json_device_fields(disk);
|
||||
printf(",\"children\":[");
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!is_child_partition(disk, &items[i])) continue;
|
||||
if (seen > 0) printf(",");
|
||||
print_json_partition(&items[i]);
|
||||
seen++;
|
||||
}
|
||||
|
||||
printf("]}");
|
||||
}
|
||||
|
||||
static int load_disks(disk_info_t *items, int max) {
|
||||
int total = sys_disk_get_count();
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < total && count < max; i++) {
|
||||
if (sys_disk_get_info(i, &items[count]) == 0) count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void usage(void) {
|
||||
printf("Usage: lsblk [-r] [--json] [/dev/DEVICE]\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
disk_info_t items[LSBLK_MAX_DISKS];
|
||||
const char *filter = NULL;
|
||||
int raw = 0;
|
||||
int json = 0;
|
||||
int count;
|
||||
int printed = 0;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (streq(argv[i], "-r")) {
|
||||
raw = 1;
|
||||
} else if (streq(argv[i], "--json")) {
|
||||
json = 1;
|
||||
} else if (streq(argv[i], "-h") || streq(argv[i], "--help")) {
|
||||
usage();
|
||||
return 0;
|
||||
} else if (argv[i][0] == '-') {
|
||||
printf("lsblk: unknown option: %s\n", argv[i]);
|
||||
usage();
|
||||
return 1;
|
||||
} else if (!filter) {
|
||||
filter = device_name_arg(argv[i]);
|
||||
} else {
|
||||
printf("lsblk: only one device filter is supported\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (raw && json) {
|
||||
printf("lsblk: -r and --json cannot be used together\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
count = load_disks(items, LSBLK_MAX_DISKS);
|
||||
|
||||
if (json) {
|
||||
printf("{\"devices\":[");
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (items[i].is_partition) continue;
|
||||
if (filter && !streq(items[i].devname, filter)) continue;
|
||||
if (printed > 0) printf(",");
|
||||
print_json_disk(&items[i], items, count);
|
||||
printed++;
|
||||
}
|
||||
|
||||
if (filter && printed == 0) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!items[i].is_partition || !streq(items[i].devname, filter)) continue;
|
||||
print_json_partition(&items[i]);
|
||||
printed++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("]}\n");
|
||||
} else if (raw) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (items[i].is_partition) continue;
|
||||
if (filter && !streq(items[i].devname, filter)) continue;
|
||||
print_raw_disk(&items[i], items, count);
|
||||
printed++;
|
||||
}
|
||||
|
||||
if (filter && printed == 0) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!items[i].is_partition || !streq(items[i].devname, filter)) continue;
|
||||
print_raw_device(&items[i]);
|
||||
printed++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (items[i].is_partition) continue;
|
||||
if (filter && !streq(items[i].devname, filter)) continue;
|
||||
print_tree_disk(&items[i], items, count);
|
||||
printed++;
|
||||
}
|
||||
|
||||
if (filter && printed == 0) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!items[i].is_partition || !streq(items[i].devname, filter)) continue;
|
||||
print_tree_device(&items[i], "");
|
||||
printed++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (printed == 0 && !json) {
|
||||
if (filter) printf("lsblk: /dev/%s not found\n", filter);
|
||||
else printf("lsblk: no block devices found\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (printed == 0 && filter) return 1;
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in a new issue