diff --git a/Makefile b/Makefile index 1714a85..5369c1f 100644 --- a/Makefile +++ b/Makefile @@ -14,8 +14,10 @@ clean: install: $(TARGET) install -Dm755 drinfo /usr/local/bin/drinfo install -Dm644 drinfo.1 /usr/share/man/man1/drinfo.1 + sudo mandb > /dev/null 2>&1 uninstall: sudo rm -f /usr/local/bin/$(TARGET) + sudo rm -f /usr/share/man/man1/$(TARGET).1 .PHONY: all clean install uninstall \ No newline at end of file diff --git a/README.md b/README.md index a19371a..13c9bb2 100644 --- a/README.md +++ b/README.md @@ -10,20 +10,26 @@ A lightweight command-line tool to monitor disk usage on Linux systems with beau - **Colorful Progress Bars**: Visual representation of disk usage with gradient colors (green → yellow → red) - **Human-Readable Sizes**: Displays sizes in B, KB, MB, GB, TB format - **Terminal Responsive**: Adapts to terminal width for optimal display -- **Detailed Information**: Shows mount point, filesystem type, device path, and usage statistics +- **Detailed Information**: Shows mount point, filesystem type, device path, UUID, label, mount options, used, available and inodes + SMART status (only as root) -## Build +## Build executable: drinfo ```bash make ``` -## install +## install with man page ```bash sudo make install ``` +## Uninstall + +```bash +sudo make uninstall +``` + ## Screenshot ![screenshot](screenshot.jpg) diff --git a/drinfo.1 b/drinfo.1 index b9f3215..7cc2d79 100644 --- a/drinfo.1 +++ b/drinfo.1 @@ -1,4 +1,4 @@ -.TH DRINFO 1 "June 2024" "Version 1.0.2" "User Commands" +.TH DRINFO 1 "June 2024" "Version 1.0.4" "User Commands" .SH NAME drinfo \- display information about drives and their storage usage .SH SYNOPSIS @@ -6,7 +6,8 @@ drinfo \- display information about drives and their storage usage .RI [ OPTION ] .SH DESCRIPTION .B drinfo -lists all detected local and network drives and shows their storage usage graphically in the terminal. +lists all detected local and network drives and Shows mount point, filesystem type, device path, +UUID, label, mount options, used, available and inodes + SMART status (only as root). .SH OPTIONS .TP diff --git a/main.c b/main.c index 6ced750..23203d2 100644 --- a/main.c +++ b/main.c @@ -12,10 +12,12 @@ #include #include #include +#include +#include // Constants for terminal and display #define TERM_FALLBACK_WIDTH 80 -#define VERSION "1.0.2" +#define VERSION "1.0.4" #define COLOR_BUFFER_SIZE 32 #define MAX_UNITS 5 #define BYTES_PER_KB 1024.0 @@ -82,7 +84,8 @@ const char *skip_filesystems[] = { "proc", "sysfs", "devpts", "tmpfs", "devtmpfs", "securityfs", "cgroup", "cgroup2", "pstore", "efivarfs", "autofs", "debugfs", - "tracefs", "configfs", "fusectl", "fuse.gvfsd-fuse", "binfmt_misc"}; + "tracefs", "configfs", "fusectl", "fuse.gvfsd-fuse", "binfmt_misc", + "fuse.portal"}; // Structure to hold drive information typedef struct @@ -90,6 +93,8 @@ typedef struct char mount_point[MAX_PATH_LENGTH]; char filesystem[MAX_SIZE_STR_LENGTH]; char device[MAX_PATH_LENGTH]; + char uuid[128]; + char label[128]; char total_str[MAX_SIZE_STR_LENGTH]; char used_str[MAX_SIZE_STR_LENGTH]; char available_str[MAX_SIZE_STR_LENGTH]; @@ -101,6 +106,10 @@ typedef struct char *progress_bar; bool is_cloud_storage; char cloud_service_name[MAX_SIZE_STR_LENGTH]; + char mount_options[MAX_TEMP_BUFFER_LENGTH]; + unsigned long long total_inodes; + unsigned long long used_inodes; + double inode_usage; } drive_info_t; char colorbuf[COLOR_BUFFER_SIZE]; // For bar colors @@ -310,6 +319,8 @@ void fill_drive_info(drive_info_t *drive, const char *mount_point, const char *filesystem, const char *device, + const char *uuid, + const char *label, const char *total_str, const char *used_str, const char *available_str, @@ -320,7 +331,11 @@ void fill_drive_info(drive_info_t *drive, const char *drive_type, char *progress_bar, bool is_cloud_storage, - const char *cloud_service_name) + const char *cloud_service_name, + const char *mount_options, + unsigned long long total_inodes, + unsigned long long used_inodes, + double inode_usage) { strncpy(drive->mount_point, mount_point, sizeof(drive->mount_point) - 1); drive->mount_point[sizeof(drive->mount_point) - 1] = '\0'; @@ -328,6 +343,24 @@ void fill_drive_info(drive_info_t *drive, drive->filesystem[sizeof(drive->filesystem) - 1] = '\0'; strncpy(drive->device, device, sizeof(drive->device) - 1); drive->device[sizeof(drive->device) - 1] = '\0'; + if (uuid) + { + strncpy(drive->uuid, uuid, sizeof(drive->uuid) - 1); + drive->uuid[sizeof(drive->uuid) - 1] = '\0'; + } + else + { + drive->uuid[0] = '\0'; + } + if (label) + { + strncpy(drive->label, label, sizeof(drive->label) - 1); + drive->label[sizeof(drive->label) - 1] = '\0'; + } + else + { + drive->label[0] = '\0'; + } snprintf(drive->total_str, sizeof(drive->total_str), "%s", total_str); snprintf(drive->used_str, sizeof(drive->used_str), "%s", used_str); snprintf(drive->available_str, sizeof(drive->available_str), "%s", available_str); @@ -347,6 +380,18 @@ void fill_drive_info(drive_info_t *drive, { drive->cloud_service_name[0] = '\0'; } + if (mount_options) + { + strncpy(drive->mount_options, mount_options, sizeof(drive->mount_options) - 1); + drive->mount_options[sizeof(drive->mount_options) - 1] = '\0'; + } + else + { + drive->mount_options[0] = '\0'; + } + drive->total_inodes = total_inodes; + drive->used_inodes = used_inodes; + drive->inode_usage = inode_usage; } // Function to get cloud storage info from GVFS @@ -464,9 +509,12 @@ void get_cloud_storage_info(const char *gvfs_path, drive_info_t *drives, int *dr // Store information in drive_info_t structure drive_info_t *drive = &drives[*drive_count]; fill_drive_info(drive, full_path, "fuse.gvfsd-fuse", entry->d_name, + NULL, NULL, total_str, used_str, available_str, total_bytes, used_bytes, available_bytes, - usage_percent, "Network Drive", bar, true, service_name); + usage_percent, "Network Drive", bar, true, service_name, NULL, + (unsigned long long)fs_info.f_blocks, (unsigned long long)fs_info.f_files, + (double)fs_info.f_files / (double)fs_info.f_blocks); (*drive_count)++; } @@ -475,6 +523,133 @@ void get_cloud_storage_info(const char *gvfs_path, drive_info_t *drives, int *dr closedir(dir); } +void get_uuid_and_label(const char *device, char *uuid, size_t uuid_size, char *label, size_t label_size) +{ + uuid[0] = '\0'; + label[0] = '\0'; + + char resolved_device[PATH_MAX]; + if (!realpath(device, resolved_device)) + { + // Wenn das Gerät nicht auflösbar ist, abbrechen + return; + } + + // UUID suchen + DIR *uuid_dir = opendir("/dev/disk/by-uuid/"); + if (uuid_dir) + { + struct dirent *entry; + char full_path[PATH_MAX]; + char resolved_link[PATH_MAX]; + while ((entry = readdir(uuid_dir)) != NULL) + { + if (entry->d_name[0] == '.') + continue; + snprintf(full_path, sizeof(full_path), "/dev/disk/by-uuid/%s", entry->d_name); + if (realpath(full_path, resolved_link)) + { + if (strcmp(resolved_link, resolved_device) == 0) + { + strncpy(uuid, entry->d_name, uuid_size - 1); + uuid[uuid_size - 1] = '\0'; + break; + } + } + } + closedir(uuid_dir); + } + + // Label suchen + DIR *label_dir = opendir("/dev/disk/by-label/"); + if (label_dir) + { + struct dirent *entry; + char full_path[PATH_MAX]; + char resolved_link[PATH_MAX]; + while ((entry = readdir(label_dir)) != NULL) + { + if (entry->d_name[0] == '.') + continue; + snprintf(full_path, sizeof(full_path), "/dev/disk/by-label/%s", entry->d_name); + if (realpath(full_path, resolved_link)) + { + if (strcmp(resolved_link, resolved_device) == 0) + { + strncpy(label, entry->d_name, label_size - 1); + label[label_size - 1] = '\0'; + break; + } + } + } + closedir(label_dir); + } +} + +// Helper function: Query SMART status (only for root and physical devices) +void get_smart_status(const char *device, char *status, size_t status_size) +{ + status[0] = '\0'; + if (geteuid() != 0) + return; + // Allow all /dev/sd*, /dev/nvme*, /dev/hd* (including partitions) + if (!( + strncmp(device, "/dev/sd", 7) == 0 || + strncmp(device, "/dev/nvme", 9) == 0 || + strncmp(device, "/dev/hd", 7) == 0)) + return; + + char cmd[256], line[256]; + snprintf(cmd, sizeof(cmd), "smartctl -H %s 2>/dev/null", device); + FILE *fp = popen(cmd, "r"); + if (!fp) + return; + while (fgets(line, sizeof(line), fp)) + { + if (strstr(line, "SMART overall-health self-assessment test result") || strstr(line, "SMART Health Status")) + { + char *p = strchr(line, ':'); + if (p) + { + p++; + while (*p == ' ' || *p == '\t') + p++; + strncpy(status, p, status_size - 1); + status[status_size - 1] = '\0'; + char *nl = strchr(status, '\n'); + if (nl) + *nl = '\0'; + break; + } + } + if (strstr(line, "PASSED")) + { + strncpy(status, "PASSED", status_size - 1); + status[status_size - 1] = '\0'; + break; + } + if (strstr(line, "FAILED")) + { + strncpy(status, "FAILED", status_size - 1); + status[status_size - 1] = '\0'; + break; + } + if (strstr(line, "UNKNOWN")) + { + strncpy(status, "UNKNOWN", status_size - 1); + status[status_size - 1] = '\0'; + break; + } + if (strstr(line, "NOT AVAILABLE")) + { + strncpy(status, "NOT AVAILABLE", status_size - 1); + status[status_size - 1] = '\0'; + break; + } + } + pclose(fp); +} + int main(int argc, char *argv[]) { if (argc > 1) @@ -560,6 +735,12 @@ int main(int argc, char *argv[]) format_bytes(used_bytes, used_str, sizeof(used_str)); format_bytes(available_bytes, available_str, sizeof(available_str)); + // Inode-Infos + unsigned long long total_inodes = fs_info.f_files; + unsigned long long free_inodes = fs_info.f_favail; + unsigned long long used_inodes = total_inodes > 0 ? total_inodes - free_inodes : 0; + double inode_usage = (total_inodes > 0) ? ((double)used_inodes / total_inodes) * 100.0 : 0.0; + // Target width: 80% of terminal width or max. 120 characters int terminal_width = get_terminal_width(); int box_width = terminal_width * TERMINAL_WIDTH_PERCENTAGE / TERMINAL_WIDTH_DIVISOR; @@ -627,12 +808,18 @@ int main(int argc, char *argv[]) drive_type = "Other Drive"; } + // UUID und Label ermitteln + char uuid[128], label[128]; + get_uuid_and_label(entry->mnt_fsname, uuid, sizeof(uuid), label, sizeof(label)); + // Store information in drive_info_t structure drive_info_t *drive = &drives[drive_count]; fill_drive_info(drive, entry->mnt_dir, entry->mnt_type, entry->mnt_fsname, + uuid, label, total_str, used_str, available_str, total_bytes, used_bytes, available_bytes, - usage_percent, drive_type, bar, false, NULL); + usage_percent, drive_type, bar, false, NULL, entry->mnt_opts, + total_inodes, used_inodes, inode_usage); drive_count++; } @@ -667,9 +854,28 @@ int main(int argc, char *argv[]) printf(" Mount point: %s\n", drive->mount_point); printf(" Filesystem: %s\n", drive->filesystem); printf(" Device: %s\n", drive->device); + printf(" UUID: %s\n", drive->uuid[0] ? drive->uuid : "-"); + printf(" Label: %s\n", drive->label[0] ? drive->label : "-"); + printf(" Mount options: %s\n", drive->mount_options); printf(" Total size: %s\n", drive->total_str); printf(" Used: %s\n", drive->used_str); printf(" Available: %s\n", drive->available_str); + printf(" Inodes: %llu/%llu (%.1f%% used)\n", drive->used_inodes, drive->total_inodes, drive->inode_usage); + + // SMART status only for root and physical devices + if (geteuid() == 0 && !drive->is_cloud_storage && strcmp(drive->drive_type, "Local Drive") == 0) + { + char smart_status[128]; + get_smart_status(drive->device, smart_status, sizeof(smart_status)); + if (smart_status[0]) + { + printf(" SMART: %s\n", smart_status); + } + else + { + printf(" SMART: No data\n"); + } + } // Progress bar int terminal_width = get_terminal_width(); diff --git a/screenshot.jpg b/screenshot.jpg index 23ab84e..8a3bcfd 100644 Binary files a/screenshot.jpg and b/screenshot.jpg differ