14 Commits
Author SHA1 Message Date
Lennart Martens 1b2df4f3fa Merge branch 'main' of https://github.com/Lennart1978/drinfo 2025-11-22 17:58:02 +01:00
Lennart Martens 589e1d7b32 Added -j option for JSON output
Added -n option to disable color output
Added -s option to sort drives by size, usage, mount point, or name
Refactored code
2025-11-22 17:56:12 +01:00
Lennart Martens e495d158a2 Delete .github/workflows/c-cpp.yml 2025-07-12 20:27:22 +02:00
Lennart Martens 30d6ad50d8 Added additional information to the output.
Update to version 1.0.4
2025-07-04 18:34:15 +02:00
Lennart Martens d11a083db9 Refactor code to use more modern C features and improve readability.
Add man page for drinfo.
2025-06-30 03:19:06 +02:00
Lennart Martens 0f75769eb4 Update screenshot 2025-06-29 21:57:20 +02:00
Lennart Martens 825c49a796 Version 1.0.2 2025-06-29 21:47:59 +02:00
Lennart Martens 04a196dab6 Show biggest drive first (sorted by capacity) 2025-06-29 21:45:44 +02:00
Lennart Martens 9f04f782b0 Changed "Physical Drive" to "Local Drive" 2025-06-29 21:31:42 +02:00
Lennart Martens 7267af7a57 Merge branch 'main' of https://github.com/Lennart1978/drinfo 2025-06-29 18:09:42 +02:00
Lennart Martens 8ada6ea6f1 Corrected terminal width adaption 2025-06-29 18:09:05 +02:00
Lennart Martens f715474b61 Update c-cpp.yml 2025-06-29 12:36:10 +02:00
Lennart Martens 835be26eda Update c-cpp.yml 2025-06-29 12:29:12 +02:00
Lennart Martens 767340195f Create c-cpp.yml 2025-06-29 12:27:31 +02:00
5 changed files with 668 additions and 135 deletions

No files matched your search

+4 -1
View File
@@ -12,9 +12,12 @@ clean:
rm -f $(TARGET) rm -f $(TARGET)
install: $(TARGET) install: $(TARGET)
sudo cp $(TARGET) /usr/local/bin/ install -Dm755 drinfo /usr/local/bin/drinfo
install -Dm644 drinfo.1 /usr/share/man/man1/drinfo.1
sudo mandb > /dev/null 2>&1
uninstall: uninstall:
sudo rm -f /usr/local/bin/$(TARGET) sudo rm -f /usr/local/bin/$(TARGET)
sudo rm -f /usr/share/man/man1/$(TARGET).1
.PHONY: all clean install uninstall .PHONY: all clean install uninstall
+26 -3
View File
@@ -10,20 +10,43 @@ 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) - **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 - **Human-Readable Sizes**: Displays sizes in B, KB, MB, GB, TB format
- **Terminal Responsive**: Adapts to terminal width for optimal display - **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)
- **JSON Output**: Export drive information in JSON format for easy parsing
- **Sorting**: Sort drives by size, usage, mount point, or name
- **No Color Mode**: Disable ANSI colors for scripts or logs
## Build ## Usage
```bash
drinfo [OPTIONS]
```
### Options
- `-h, --help`: Show help message
- `-v, --version`: Show program version
- `-j, --json`: Output in JSON format
- `-n, --no-color`: Disable color output
- `-s, --sort TYPE`: Sort drives by TYPE (`size`, `usage`, `mount`, `name`)
## Build executable: drinfo
```bash ```bash
make make
``` ```
## install ## install with man page
```bash ```bash
sudo make install sudo make install
``` ```
## Uninstall
```bash
sudo make uninstall
```
## Screenshot ## Screenshot
![screenshot](screenshot.jpg) ![screenshot](screenshot.jpg)
+52
View File
@@ -0,0 +1,52 @@
.TH DRINFO 1 "November 2025" "Version 1.2.0" "User Commands"
.SH NAME
drinfo \- display information about drives and their storage usage
.SH SYNOPSIS
.B drinfo
.RI [ OPTION ]
.SH DESCRIPTION
.B drinfo
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).
.P
It provides a visual representation of disk usage with gradient colored progress bars (green -> yellow -> red).
.SH OPTIONS
.TP
.BR -h , --help
Show help message and exit.
.TP
.BR -v , --version
Show program version and exit.
.TP
.BR -j , --json
Output drive information in JSON format. This is useful for parsing the output in scripts or other programs.
.TP
.BR -n , --no-color
Disable ANSI color codes in the output. Use this option when redirecting output to a file or when running in a terminal that does not support colors.
.TP
.BR -s , --sort \ \fITYPE\fP
Sort the drives by the specified \fITYPE\fP. Available types are:
.RS
.TP
.B size
Sort by total size (descending). This is the default.
.TP
.B usage
Sort by usage percentage (descending).
.TP
.B mount
Sort alphabetically by mount point.
.TP
.B name
Sort alphabetically by device name.
.RE
.SH AUTHOR
Lennart Martens <monkeynator78@gmail.com>
.SH LICENSE
MIT License
.SH SEE ALSO
.BR df (1),
.BR lsblk (8)
+586 -131
View File
@@ -11,10 +11,14 @@
#include <time.h> #include <time.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <dirent.h> #include <dirent.h>
#include <stdbool.h>
#include <errno.h>
#include <sys/types.h>
#include <getopt.h>
// Constants for terminal and display // Constants for terminal and display
#define TERM_FALLBACK_WIDTH 80 #define TERM_FALLBACK_WIDTH 80
#define VERSION "1.0.0" #define VERSION "1.2.0"
#define COLOR_BUFFER_SIZE 32 #define COLOR_BUFFER_SIZE 32
#define MAX_UNITS 5 #define MAX_UNITS 5
#define BYTES_PER_KB 1024.0 #define BYTES_PER_KB 1024.0
@@ -39,7 +43,7 @@
#define BARLINE_BUFFER_EXTRA 16 #define BARLINE_BUFFER_EXTRA 16
// Constants for terminal width calculations // Constants for terminal width calculations
#define TERMINAL_WIDTH_PERCENTAGE 80 #define TERMINAL_WIDTH_PERCENTAGE 4
#define TERMINAL_WIDTH_DIVISOR 5 #define TERMINAL_WIDTH_DIVISOR 5
#define MAX_BOX_WIDTH 120 #define MAX_BOX_WIDTH 120
#define MIN_BOX_WIDTH 40 #define MIN_BOX_WIDTH 40
@@ -67,15 +71,99 @@
#define BACKGROUND_COLOR_FORMAT "\033[48;2;%d;%d;%dm" #define BACKGROUND_COLOR_FORMAT "\033[48;2;%d;%d;%dm"
#define BLUE_TEXT_FORMAT "\033[38;2;%d;%d;%dm" #define BLUE_TEXT_FORMAT "\033[38;2;%d;%d;%dm"
#define RESET_FORMAT "\033[0m" #define RESET_FORMAT "\033[0m"
#define BOLD_YELLOW_FORMAT "\033[1;33m"
#define RESET_COLOR_FORMAT "\033[0m"
// Constants for file system types // Constants for file system types
#define MOUNT_TABLE_PATH "/proc/mounts" #define MOUNT_TABLE_PATH "/proc/mounts"
#define GVFS_BASE_PATH "/run/user/%d/gvfs" #define GVFS_BASE_PATH "/run/user/%d/gvfs"
// Maximum number of drives to handle
#define MAX_DRIVES 100
// Global options
bool opt_json = false;
bool opt_no_color = false;
enum { SORT_SIZE, SORT_USAGE, SORT_MOUNT, SORT_NAME } opt_sort = SORT_SIZE;
// Color strings (can be disabled)
const char *c_bold_yellow = "\033[1;33m";
const char *c_reset = "\033[0m";
// Constants for file system types to skip
const char *skip_filesystems[] = {
"proc", "sysfs", "devpts", "tmpfs", "devtmpfs", "securityfs",
"cgroup", "cgroup2", "pstore", "efivarfs", "autofs", "debugfs",
"tracefs", "configfs", "fusectl", "fuse.gvfsd-fuse", "binfmt_misc",
"fuse.portal"};
// Structure to hold drive information
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];
unsigned long long total_bytes;
unsigned long long used_bytes;
unsigned long long available_bytes;
double usage_percent;
const char *drive_type;
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 char colorbuf[COLOR_BUFFER_SIZE]; // For bar colors
// Function to compare drives by total capacity (descending order)
int compare_drives_by_capacity(const void *a, const void *b)
{
const drive_info_t *drive_a = (const drive_info_t *)a;
const drive_info_t *drive_b = (const drive_info_t *)b;
if (drive_b->total_bytes > drive_a->total_bytes)
return 1;
if (drive_b->total_bytes < drive_a->total_bytes)
return -1;
return 0;
}
// Function to compare drives by usage (descending order)
int compare_drives_by_usage(const void *a, const void *b)
{
const drive_info_t *drive_a = (const drive_info_t *)a;
const drive_info_t *drive_b = (const drive_info_t *)b;
if (drive_b->usage_percent > drive_a->usage_percent)
return 1;
if (drive_b->usage_percent < drive_a->usage_percent)
return -1;
return 0;
}
// Function to compare drives by mount point (alphabetical)
int compare_drives_by_mount(const void *a, const void *b)
{
const drive_info_t *drive_a = (const drive_info_t *)a;
const drive_info_t *drive_b = (const drive_info_t *)b;
return strcmp(drive_a->mount_point, drive_b->mount_point);
}
// Function to compare drives by device name (alphabetical)
int compare_drives_by_name(const void *a, const void *b)
{
const drive_info_t *drive_a = (const drive_info_t *)a;
const drive_info_t *drive_b = (const drive_info_t *)b;
return strcmp(drive_a->device, drive_b->device);
}
// Function to display help text // Function to display help text
void show_help(const char *program_name) void show_help(const char *program_name)
{ {
@@ -84,8 +172,11 @@ void show_help(const char *program_name)
printf("Display information about available drives and their storage space.\n"); printf("Display information about available drives and their storage space.\n");
printf("\n"); printf("\n");
printf("Options:\n"); printf("Options:\n");
printf(" -h, --help Show this help message\n"); printf(" -h, --help Show this help message\n");
printf(" -v, --version Show program version\n"); printf(" -v, --version Show program version\n");
printf(" -j, --json Output in JSON format\n");
printf(" -n, --no-color Disable color output\n");
printf(" -s, --sort TYPE Sort drives by TYPE (size, usage, mount, name)\n");
printf("\n"); printf("\n");
printf("This program is licensed under the MIT License.\n"); printf("This program is licensed under the MIT License.\n");
printf("https://github.com/lennart1978/drinfo\n"); printf("https://github.com/lennart1978/drinfo\n");
@@ -140,7 +231,7 @@ double calculate_usage_percent(unsigned long long total, unsigned long long avai
return ((double)used / total) * PERCENTAGE_MULTIPLIER; return ((double)used / total) * PERCENTAGE_MULTIPLIER;
} }
int is_physical_device(const char *fsname) bool is_physical_device(const char *fsname)
{ {
// Check for /dev/sd*, /dev/nvme*, /dev/hd* // Check for /dev/sd*, /dev/nvme*, /dev/hd*
return (strncmp(fsname, "/dev/sd", DEV_SD_PREFIX_LEN) == 0 || return (strncmp(fsname, "/dev/sd", DEV_SD_PREFIX_LEN) == 0 ||
@@ -148,7 +239,7 @@ int is_physical_device(const char *fsname)
strncmp(fsname, "/dev/hd", DEV_HD_PREFIX_LEN) == 0); strncmp(fsname, "/dev/hd", DEV_HD_PREFIX_LEN) == 0);
} }
int is_network_device(const char *fsname) bool is_network_device(const char *fsname)
{ {
// Check for network file systems // Check for network file systems
return (strncmp(fsname, "//", NETWORK_PATH_PREFIX_LEN) == 0 || // SMB/CIFS shares return (strncmp(fsname, "//", NETWORK_PATH_PREFIX_LEN) == 0 || // SMB/CIFS shares
@@ -156,7 +247,7 @@ int is_network_device(const char *fsname)
strstr(fsname, ":") != NULL); // NFS and other network protocols strstr(fsname, ":") != NULL); // NFS and other network protocols
} }
int is_network_filesystem(const char *fstype) bool is_network_filesystem(const char *fstype)
{ {
// Check for network file system types // Check for network file system types
return (strcmp(fstype, "nfs") == 0 || return (strcmp(fstype, "nfs") == 0 ||
@@ -170,7 +261,7 @@ int is_network_filesystem(const char *fstype)
strncmp(fstype, "fuse.", FUSE_PREFIX_LEN) == 0); // Other FUSE-based network file systems strncmp(fstype, "fuse.", FUSE_PREFIX_LEN) == 0); // Other FUSE-based network file systems
} }
int is_appimage_or_temp(const char *fsname, const char *mountpoint) bool is_appimage_or_temp(const char *fsname, const char *mountpoint)
{ {
// Filter out AppImages and temporary mounts // Filter out AppImages and temporary mounts
return (strstr(fsname, ".AppImage") != NULL || return (strstr(fsname, ".AppImage") != NULL ||
@@ -181,6 +272,11 @@ int is_appimage_or_temp(const char *fsname, const char *mountpoint)
// Helper for true-color gradient (red-yellow-green) // Helper for true-color gradient (red-yellow-green)
void get_bar_color(int idx, int max, char *buffer, size_t size) void get_bar_color(int idx, int max, char *buffer, size_t size)
{ {
if (opt_no_color) {
buffer[0] = '\0';
return;
}
// New gradient: 0% = green (0,255,0), 50% = yellow (255,255,0), 100% = red (255,0,0) // New gradient: 0% = green (0,255,0), 50% = yellow (255,255,0), 100% = red (255,0,0)
float ratio = (float)idx / (float)(max - 1); float ratio = (float)idx / (float)(max - 1);
int r, g, b; int r, g, b;
@@ -218,28 +314,15 @@ int visible_length(const char *s)
return len; return len;
} }
// Adjusted: calculate max visible line length for block
int max_visible_line_length(const char *lines[], int n)
{
int max = 0;
for (int i = 0; i < n; ++i)
{
int len = visible_length(lines[i]);
if (len > max)
max = len;
}
return max;
}
// Function to check if a directory contains cloud storage // Function to check if a directory contains cloud storage
int is_cloud_storage_directory(const char *path) bool is_cloud_storage_directory(const char *path)
{ {
DIR *dir = opendir(path); DIR *dir = opendir(path);
if (!dir) if (!dir)
return 0; return false;
struct dirent *entry; struct dirent *entry;
int has_cloud_storage = 0; bool has_cloud_storage = false;
while ((entry = readdir(dir)) != NULL) while ((entry = readdir(dir)) != NULL)
{ {
@@ -254,7 +337,7 @@ int is_cloud_storage_directory(const char *path)
strstr(entry->d_name, "onedrive") != NULL || strstr(entry->d_name, "onedrive") != NULL ||
strstr(entry->d_name, "mega") != NULL)) strstr(entry->d_name, "mega") != NULL))
{ {
has_cloud_storage = 1; has_cloud_storage = true;
break; break;
} }
} }
@@ -263,8 +346,88 @@ int is_cloud_storage_directory(const char *path)
return has_cloud_storage; return has_cloud_storage;
} }
// Function to fill drive_info_t structure with common data
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,
unsigned long long total_bytes,
unsigned long long used_bytes,
unsigned long long available_bytes,
double usage_percent,
const char *drive_type,
char *progress_bar,
bool is_cloud_storage,
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';
strncpy(drive->filesystem, filesystem, sizeof(drive->filesystem) - 1);
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);
drive->total_bytes = total_bytes;
drive->used_bytes = used_bytes;
drive->available_bytes = available_bytes;
drive->usage_percent = usage_percent;
drive->drive_type = drive_type;
drive->progress_bar = progress_bar;
drive->is_cloud_storage = is_cloud_storage;
if (cloud_service_name)
{
strncpy(drive->cloud_service_name, cloud_service_name, sizeof(drive->cloud_service_name) - 1);
drive->cloud_service_name[sizeof(drive->cloud_service_name) - 1] = '\0';
}
else
{
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 // Function to get cloud storage info from GVFS
void get_cloud_storage_info(const char *gvfs_path, int *drive_count) void get_cloud_storage_info(const char *gvfs_path, drive_info_t *drives, int *drive_count)
{ {
DIR *dir = opendir(gvfs_path); DIR *dir = opendir(gvfs_path);
if (!dir) if (!dir)
@@ -272,7 +435,7 @@ void get_cloud_storage_info(const char *gvfs_path, int *drive_count)
struct dirent *entry; struct dirent *entry;
while ((entry = readdir(dir)) != NULL) while ((entry = readdir(dir)) != NULL && *drive_count < MAX_DRIVES)
{ {
// Use stat if d_type is not available // Use stat if d_type is not available
struct stat st; struct stat st;
@@ -324,7 +487,7 @@ void get_cloud_storage_info(const char *gvfs_path, int *drive_count)
int text_length = strlen(percent_text); int text_length = strlen(percent_text);
int text_start = filled_length > text_length ? (filled_length - text_length) / 2 : 0; int text_start = filled_length > text_length ? (filled_length - text_length) / 2 : 0;
// Create progress bar (same logic as before) // Create progress bar
size_t bar_bufsize = bar_length * MAX_BAR_BUFFER_MULTIPLIER + 1; size_t bar_bufsize = bar_length * MAX_BAR_BUFFER_MULTIPLIER + 1;
char *bar = malloc(bar_bufsize); char *bar = malloc(bar_bufsize);
if (!bar) if (!bar)
@@ -337,22 +500,35 @@ void get_cloud_storage_info(const char *gvfs_path, int *drive_count)
{ {
if (i >= text_start && i < text_start + text_length && i < filled_length) if (i >= text_start && i < text_start + text_length && i < filled_length)
{ {
get_bar_color(i, bar_length, colorbuf, sizeof(colorbuf)); if (!opt_no_color) {
int r, g, b; get_bar_color(i, bar_length, colorbuf, sizeof(colorbuf));
sscanf(colorbuf, COLOR_FORMAT, &r, &g, &b); int r, g, b;
char tmp[MAX_TEMP_BUFFER_LENGTH]; sscanf(colorbuf, COLOR_FORMAT, &r, &g, &b);
snprintf(tmp, sizeof(tmp), BACKGROUND_COLOR_FORMAT BLUE_TEXT_FORMAT "%c" RESET_FORMAT, r, g, b, BLUE_TEXT_R, BLUE_TEXT_G, BLUE_TEXT_B, percent_text[i - text_start]); char tmp[MAX_TEMP_BUFFER_LENGTH];
strncat(bar, tmp, bar_bufsize - strlen(bar) - 1); snprintf(tmp, sizeof(tmp), BACKGROUND_COLOR_FORMAT BLUE_TEXT_FORMAT "%c" RESET_FORMAT, r, g, b, BLUE_TEXT_R, BLUE_TEXT_G, BLUE_TEXT_B, percent_text[i - text_start]);
strncat(bar, tmp, bar_bufsize - strlen(bar) - 1);
} else {
char tmp[2] = {percent_text[i - text_start], '\0'};
strncat(bar, tmp, bar_bufsize - strlen(bar) - 1);
}
} }
else if (i < filled_length) else if (i < filled_length)
{ {
get_bar_color(i, bar_length, colorbuf, sizeof(colorbuf)); if (!opt_no_color) {
strncat(bar, colorbuf, bar_bufsize - strlen(bar) - 1); get_bar_color(i, bar_length, colorbuf, sizeof(colorbuf));
strncat(bar, "█" RESET_FORMAT, bar_bufsize - strlen(bar) - 1); strncat(bar, colorbuf, bar_bufsize - strlen(bar) - 1);
strncat(bar, "█" RESET_FORMAT, bar_bufsize - strlen(bar) - 1);
} else {
strncat(bar, "█", bar_bufsize - strlen(bar) - 1);
}
} }
else else
{ {
strncat(bar, "\033[48;2;64;64;64m\033[38;2;160;160;160m░\033[0m", bar_bufsize - strlen(bar) - 1); if (!opt_no_color) {
strncat(bar, "\033[48;2;64;64;64m\033[38;2;160;160;160m░\033[0m", bar_bufsize - strlen(bar) - 1);
} else {
strncat(bar, "░", bar_bufsize - strlen(bar) - 1);
}
} }
} }
@@ -375,20 +551,16 @@ void get_cloud_storage_info(const char *gvfs_path, int *drive_count)
service_name = "MEGA"; service_name = "MEGA";
} }
// Output // Store information in drive_info_t structure
printf(" " BOLD_YELLOW_FORMAT "Network Drive %d (%s)" RESET_COLOR_FORMAT "\n", *drive_count + 1, service_name); drive_info_t *drive = &drives[*drive_count];
printf(" Mount point: %s\n", full_path); fill_drive_info(drive, full_path, "fuse.gvfsd-fuse", entry->d_name,
printf(" Filesystem: %s\n", "fuse.gvfsd-fuse"); NULL, NULL,
printf(" Device: %s\n", entry->d_name); total_str, used_str, available_str,
printf(" Total size: %s\n", total_str); total_bytes, used_bytes, available_bytes,
printf(" Used: %s\n", used_str); usage_percent, "Network Drive", bar, true, service_name, NULL,
printf(" Available: %s\n", available_str); (unsigned long long)fs_info.f_blocks, (unsigned long long)fs_info.f_files,
(double)fs_info.f_files / (double)fs_info.f_blocks);
int bar_visible_len = visible_length(bar);
int bar_padding = content_width - bar_visible_len;
printf(" %s%*s\n", bar, bar_padding, "");
free(bar);
(*drive_count)++; (*drive_count)++;
} }
} }
@@ -396,58 +568,194 @@ void get_cloud_storage_info(const char *gvfs_path, int *drive_count)
closedir(dir); closedir(dir);
} }
int main(int argc, char *argv[]) void get_uuid_and_label(const char *device, char *uuid, size_t uuid_size, char *label, size_t label_size)
{ {
if (argc > 1) uuid[0] = '\0';
label[0] = '\0';
char resolved_device[PATH_MAX];
if (!realpath(device, resolved_device))
{ {
if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) // Wenn das Gerät nicht auflösbar ist, abbrechen
{ return;
show_help(argv[0]);
return 0;
}
else if (strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0)
{
show_version();
return 0;
}
} }
printf("\n"); // 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);
}
void print_json(drive_info_t *drives, int count) {
printf("[\n");
for (int i = 0; i < count; i++) {
drive_info_t *d = &drives[i];
printf(" {\n");
printf(" \"device\": \"%s\",\n", d->device);
printf(" \"mount_point\": \"%s\",\n", d->mount_point);
printf(" \"filesystem\": \"%s\",\n", d->filesystem);
printf(" \"total_bytes\": %llu,\n", d->total_bytes);
printf(" \"used_bytes\": %llu,\n", d->used_bytes);
printf(" \"available_bytes\": %llu,\n", d->available_bytes);
printf(" \"usage_percent\": %.1f,\n", d->usage_percent);
printf(" \"type\": \"%s\",\n", d->drive_type);
printf(" \"is_cloud\": %s,\n", d->is_cloud_storage ? "true" : "false");
printf(" \"cloud_service\": \"%s\",\n", d->cloud_service_name);
printf(" \"uuid\": \"%s\",\n", d->uuid);
printf(" \"label\": \"%s\",\n", d->label);
printf(" \"mount_options\": \"%s\",\n", d->mount_options);
printf(" \"total_inodes\": %llu,\n", d->total_inodes);
printf(" \"used_inodes\": %llu,\n", d->used_inodes);
printf(" \"inode_usage\": %.1f\n", d->inode_usage);
if (i < count - 1) printf(" },\n");
else printf(" }\n");
}
printf("]\n");
}
void discover_drives(drive_info_t *drives, int *drive_count) {
*drive_count = 0;
// Open the mount table // Open the mount table
FILE *mtab = setmntent(MOUNT_TABLE_PATH, "r"); FILE *mtab = setmntent(MOUNT_TABLE_PATH, "r");
if (mtab == NULL) if (mtab == NULL)
{ {
perror("Error opening mount table"); perror("Error opening mount table");
return 1; return;
} }
struct mntent *entry; struct mntent *entry;
int drive_count = 0;
// Loop through all mount points // Loop through all mount points and collect drive information
while ((entry = getmntent(mtab)) != NULL) while ((entry = getmntent(mtab)) != NULL && *drive_count < MAX_DRIVES)
{ {
// Skip special file systems // Skip special file systems
if (strcmp(entry->mnt_type, "proc") == 0 || const int skip_count = sizeof(skip_filesystems) / sizeof(skip_filesystems[0]);
strcmp(entry->mnt_type, "sysfs") == 0 ||
strcmp(entry->mnt_type, "devpts") == 0 || bool should_skip = false;
strcmp(entry->mnt_type, "tmpfs") == 0 || for (int i = 0; i < skip_count; i++)
strcmp(entry->mnt_type, "devtmpfs") == 0 || {
strcmp(entry->mnt_type, "securityfs") == 0 || if (strcmp(entry->mnt_type, skip_filesystems[i]) == 0)
strcmp(entry->mnt_type, "cgroup") == 0 || {
strcmp(entry->mnt_type, "cgroup2") == 0 || should_skip = true;
strcmp(entry->mnt_type, "pstore") == 0 || break;
strcmp(entry->mnt_type, "efivarfs") == 0 || }
strcmp(entry->mnt_type, "autofs") == 0 || }
strcmp(entry->mnt_type, "debugfs") == 0 ||
strcmp(entry->mnt_type, "tracefs") == 0 || if (should_skip)
strcmp(entry->mnt_type, "configfs") == 0 ||
strcmp(entry->mnt_type, "fusectl") == 0 ||
strcmp(entry->mnt_type, "fuse.gvfsd-fuse") == 0 ||
strcmp(entry->mnt_type, "binfmt_misc") == 0)
{ {
continue; continue;
} }
// Show physical drives and network drives // Show physical drives and network drives
if (!is_physical_device(entry->mnt_fsname) && if (!is_physical_device(entry->mnt_fsname) &&
!is_network_device(entry->mnt_fsname) && !is_network_device(entry->mnt_fsname) &&
@@ -480,6 +788,12 @@ int main(int argc, char *argv[])
format_bytes(used_bytes, used_str, sizeof(used_str)); format_bytes(used_bytes, used_str, sizeof(used_str));
format_bytes(available_bytes, available_str, sizeof(available_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 // Target width: 80% of terminal width or max. 120 characters
int terminal_width = get_terminal_width(); int terminal_width = get_terminal_width();
int box_width = terminal_width * TERMINAL_WIDTH_PERCENTAGE / TERMINAL_WIDTH_DIVISOR; int box_width = terminal_width * TERMINAL_WIDTH_PERCENTAGE / TERMINAL_WIDTH_DIVISOR;
@@ -506,47 +820,50 @@ int main(int argc, char *argv[])
if (!bar) if (!bar)
{ {
perror("malloc"); perror("malloc");
exit(1); continue;
} }
bar[0] = '\0'; bar[0] = '\0';
for (int i = 0; i < bar_length; i++) for (int i = 0; i < bar_length; i++)
{ {
if (i >= text_start && i < text_start + text_length && i < filled_length) if (i >= text_start && i < text_start + text_length && i < filled_length)
{ {
get_bar_color(i, bar_length, colorbuf, sizeof(colorbuf)); if (!opt_no_color) {
int r, g, b; get_bar_color(i, bar_length, colorbuf, sizeof(colorbuf));
sscanf(colorbuf, COLOR_FORMAT, &r, &g, &b); int r, g, b;
char tmp[MAX_TEMP_BUFFER_LENGTH]; sscanf(colorbuf, COLOR_FORMAT, &r, &g, &b);
snprintf(tmp, sizeof(tmp), BACKGROUND_COLOR_FORMAT BLUE_TEXT_FORMAT "%c" RESET_FORMAT, r, g, b, BLUE_TEXT_R, BLUE_TEXT_G, BLUE_TEXT_B, percent_text[i - text_start]); char tmp[MAX_TEMP_BUFFER_LENGTH];
strncat(bar, tmp, bar_bufsize - strlen(bar) - 1); snprintf(tmp, sizeof(tmp), BACKGROUND_COLOR_FORMAT BLUE_TEXT_FORMAT "%c" RESET_FORMAT, r, g, b, BLUE_TEXT_R, BLUE_TEXT_G, BLUE_TEXT_B, percent_text[i - text_start]);
strncat(bar, tmp, bar_bufsize - strlen(bar) - 1);
} else {
char tmp[2] = {percent_text[i - text_start], '\0'};
strncat(bar, tmp, bar_bufsize - strlen(bar) - 1);
}
} }
else if (i < filled_length) else if (i < filled_length)
{ {
get_bar_color(i, bar_length, colorbuf, sizeof(colorbuf)); if (!opt_no_color) {
strncat(bar, colorbuf, bar_bufsize - strlen(bar) - 1); get_bar_color(i, bar_length, colorbuf, sizeof(colorbuf));
strncat(bar, "█" RESET_FORMAT, bar_bufsize - strlen(bar) - 1); strncat(bar, colorbuf, bar_bufsize - strlen(bar) - 1);
strncat(bar, "█" RESET_FORMAT, bar_bufsize - strlen(bar) - 1);
} else {
strncat(bar, "█", bar_bufsize - strlen(bar) - 1);
}
} }
else else
{ {
strncat(bar, "\033[48;2;64;64;64m\033[38;2;160;160;160m░\033[0m", bar_bufsize - strlen(bar) - 1); if (!opt_no_color) {
strncat(bar, "\033[48;2;64;64;64m\033[38;2;160;160;160m░\033[0m", bar_bufsize - strlen(bar) - 1);
} else {
strncat(bar, "░", bar_bufsize - strlen(bar) - 1);
}
} }
} }
size_t barline_bufsize = bar_bufsize + BARLINE_BUFFER_EXTRA;
char *barline = malloc(barline_bufsize);
if (!barline)
{
perror("malloc");
free(bar);
exit(1);
}
snprintf(barline, barline_bufsize, "[%-*s]", bar_length, ""); // Placeholder for padding
snprintf(barline, barline_bufsize, "[%-*s]", bar_length, ""); // for padding
// Content // Determine drive type
const char *drive_type; const char *drive_type;
if (is_physical_device(entry->mnt_fsname)) if (is_physical_device(entry->mnt_fsname))
{ {
drive_type = "Physical Drive"; drive_type = "Local Drive";
} }
else if (is_network_filesystem(entry->mnt_type) || is_network_device(entry->mnt_fsname)) else if (is_network_filesystem(entry->mnt_type) || is_network_device(entry->mnt_fsname))
{ {
@@ -556,21 +873,21 @@ int main(int argc, char *argv[])
{ {
drive_type = "Other Drive"; drive_type = "Other Drive";
} }
printf(" " BOLD_YELLOW_FORMAT "%s %d" RESET_COLOR_FORMAT "\n", drive_type, drive_count + 1);
printf(" Mount point: %s\n", entry->mnt_dir);
printf(" Filesystem: %s\n", entry->mnt_type);
printf(" Device: %s\n", entry->mnt_fsname);
printf(" Total size: %s\n", total_str);
printf(" Used: %s\n", used_str);
printf(" Available: %s\n", available_str);
// Progress bar
int bar_visible_len = visible_length(bar);
int bar_padding = content_width - bar_visible_len;
printf(" %s%*s\n", bar, bar_padding, "");
free(bar); // UUID und Label ermitteln
free(barline); char uuid[128], label[128];
drive_count++; 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, entry->mnt_opts,
total_inodes, used_inodes, inode_usage);
(*drive_count)++;
} }
endmntent(mtab); endmntent(mtab);
@@ -580,16 +897,154 @@ int main(int argc, char *argv[])
snprintf(gvfs_path, sizeof(gvfs_path), GVFS_BASE_PATH, getuid()); snprintf(gvfs_path, sizeof(gvfs_path), GVFS_BASE_PATH, getuid());
if (is_cloud_storage_directory(gvfs_path)) if (is_cloud_storage_directory(gvfs_path))
{ {
get_cloud_storage_info(gvfs_path, &drive_count); get_cloud_storage_info(gvfs_path, drives, drive_count);
}
}
int main(int argc, char *argv[])
{
static struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'v'},
{"json", no_argument, 0, 'j'},
{"no-color", no_argument, 0, 'n'},
{"sort", required_argument, 0, 's'},
{0, 0, 0, 0}
};
int opt;
int option_index = 0;
while ((opt = getopt_long(argc, argv, "hvjns:", long_options, &option_index)) != -1)
{
switch (opt)
{
case 'h':
show_help(argv[0]);
return 0;
case 'v':
show_version();
return 0;
case 'j':
opt_json = true;
break;
case 'n':
opt_no_color = true;
break;
case 's':
if (strcmp(optarg, "size") == 0) opt_sort = SORT_SIZE;
else if (strcmp(optarg, "usage") == 0) opt_sort = SORT_USAGE;
else if (strcmp(optarg, "mount") == 0) opt_sort = SORT_MOUNT;
else if (strcmp(optarg, "name") == 0) opt_sort = SORT_NAME;
else {
fprintf(stderr, "Invalid sort option: %s\n", optarg);
return 1;
}
break;
default:
return 1;
}
} }
if (drive_count == 0) if (opt_no_color) {
{ c_bold_yellow = "";
printf("No drives found.\n"); c_reset = "";
} }
else
{ if (!opt_json) {
printf("A total of %d drives found.\n", drive_count); printf("\n");
}
// Array to store all drive information
drive_info_t drives[MAX_DRIVES];
int drive_count = 0;
discover_drives(drives, &drive_count);
// Sort drives
switch (opt_sort) {
case SORT_SIZE:
qsort(drives, drive_count, sizeof(drive_info_t), compare_drives_by_capacity);
break;
case SORT_USAGE:
qsort(drives, drive_count, sizeof(drive_info_t), compare_drives_by_usage);
break;
case SORT_MOUNT:
qsort(drives, drive_count, sizeof(drive_info_t), compare_drives_by_mount);
break;
case SORT_NAME:
qsort(drives, drive_count, sizeof(drive_info_t), compare_drives_by_name);
break;
}
if (opt_json) {
print_json(drives, drive_count);
} else {
// Display sorted drives
for (int i = 0; i < drive_count; i++)
{
drive_info_t *drive = &drives[i];
// Display drive information
if (drive->is_cloud_storage)
{
printf(" %sNetwork Drive %d (%s)%s\n", c_bold_yellow, i + 1, drive->cloud_service_name, c_reset);
}
else
{
printf(" %s%s %d%s\n", c_bold_yellow, drive->drive_type, i + 1, c_reset);
}
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();
int box_width = terminal_width * TERMINAL_WIDTH_PERCENTAGE / TERMINAL_WIDTH_DIVISOR;
if (box_width > MAX_BOX_WIDTH)
box_width = MAX_BOX_WIDTH;
if (box_width < MIN_BOX_WIDTH)
box_width = MIN_BOX_WIDTH;
int content_width = box_width - FRAME_PADDING;
int bar_visible_len = visible_length(drive->progress_bar);
int bar_padding = content_width - bar_visible_len;
printf(" %s%*s\n", drive->progress_bar, bar_padding, "");
// Free allocated memory
free(drive->progress_bar);
}
if (drive_count == 0)
{
printf("No drives found.\n");
}
else
{
printf("A total of %d drives found.\n", drive_count);
}
} }
return 0; return 0;
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 321 KiB

After

Width:  |  Height:  |  Size: 244 KiB