4 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
6 changed files with 511 additions and 132 deletions

No files matched your search

-21
View File
@@ -1,21 +0,0 @@
name: C/C++ CI
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: make
run: make
permissions:
contents: read
pull-requests: write
+2
View File
@@ -14,8 +14,10 @@ clean:
install: $(TARGET) install: $(TARGET)
install -Dm755 drinfo /usr/local/bin/drinfo install -Dm755 drinfo /usr/local/bin/drinfo
install -Dm644 drinfo.1 /usr/share/man/man1/drinfo.1 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)
+28 -3
View File
@@ -1,4 +1,4 @@
.TH DRINFO 1 "June 2024" "Version 1.0.2" "User Commands" .TH DRINFO 1 "November 2025" "Version 1.2.0" "User Commands"
.SH NAME .SH NAME
drinfo \- display information about drives and their storage usage drinfo \- display information about drives and their storage usage
.SH SYNOPSIS .SH SYNOPSIS
@@ -6,8 +6,10 @@ drinfo \- display information about drives and their storage usage
.RI [ OPTION ] .RI [ OPTION ]
.SH DESCRIPTION .SH DESCRIPTION
.B drinfo .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).
.P
It provides a visual representation of disk usage with gradient colored progress bars (green -> yellow -> red).
.SH OPTIONS .SH OPTIONS
.TP .TP
.BR -h , --help .BR -h , --help
@@ -15,6 +17,29 @@ Show help message and exit.
.TP .TP
.BR -v , --version .BR -v , --version
Show program version and exit. 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 .SH AUTHOR
Lennart Martens <monkeynator78@gmail.com> Lennart Martens <monkeynator78@gmail.com>
+455 -105
View File
@@ -12,10 +12,13 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <dirent.h> #include <dirent.h>
#include <stdbool.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.2" #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
@@ -68,8 +71,6 @@
#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"
@@ -78,11 +79,21 @@
// Maximum number of drives to handle // Maximum number of drives to handle
#define MAX_DRIVES 100 #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 // Constants for file system types to skip
const char *skip_filesystems[] = { const char *skip_filesystems[] = {
"proc", "sysfs", "devpts", "tmpfs", "devtmpfs", "securityfs", "proc", "sysfs", "devpts", "tmpfs", "devtmpfs", "securityfs",
"cgroup", "cgroup2", "pstore", "efivarfs", "autofs", "debugfs", "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 // Structure to hold drive information
typedef struct typedef struct
@@ -90,6 +101,8 @@ typedef struct
char mount_point[MAX_PATH_LENGTH]; char mount_point[MAX_PATH_LENGTH];
char filesystem[MAX_SIZE_STR_LENGTH]; char filesystem[MAX_SIZE_STR_LENGTH];
char device[MAX_PATH_LENGTH]; char device[MAX_PATH_LENGTH];
char uuid[128];
char label[128];
char total_str[MAX_SIZE_STR_LENGTH]; char total_str[MAX_SIZE_STR_LENGTH];
char used_str[MAX_SIZE_STR_LENGTH]; char used_str[MAX_SIZE_STR_LENGTH];
char available_str[MAX_SIZE_STR_LENGTH]; char available_str[MAX_SIZE_STR_LENGTH];
@@ -101,6 +114,10 @@ typedef struct
char *progress_bar; char *progress_bar;
bool is_cloud_storage; bool is_cloud_storage;
char cloud_service_name[MAX_SIZE_STR_LENGTH]; 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; } drive_info_t;
char colorbuf[COLOR_BUFFER_SIZE]; // For bar colors char colorbuf[COLOR_BUFFER_SIZE]; // For bar colors
@@ -118,6 +135,35 @@ int compare_drives_by_capacity(const void *a, const void *b)
return 0; 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)
{ {
@@ -126,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");
@@ -223,6 +272,11 @@ bool 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;
@@ -260,19 +314,6 @@ 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
bool is_cloud_storage_directory(const char *path) bool is_cloud_storage_directory(const char *path)
{ {
@@ -310,6 +351,8 @@ void fill_drive_info(drive_info_t *drive,
const char *mount_point, const char *mount_point,
const char *filesystem, const char *filesystem,
const char *device, const char *device,
const char *uuid,
const char *label,
const char *total_str, const char *total_str,
const char *used_str, const char *used_str,
const char *available_str, const char *available_str,
@@ -320,7 +363,11 @@ void fill_drive_info(drive_info_t *drive,
const char *drive_type, const char *drive_type,
char *progress_bar, char *progress_bar,
bool is_cloud_storage, 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); strncpy(drive->mount_point, mount_point, sizeof(drive->mount_point) - 1);
drive->mount_point[sizeof(drive->mount_point) - 1] = '\0'; drive->mount_point[sizeof(drive->mount_point) - 1] = '\0';
@@ -328,6 +375,24 @@ void fill_drive_info(drive_info_t *drive,
drive->filesystem[sizeof(drive->filesystem) - 1] = '\0'; drive->filesystem[sizeof(drive->filesystem) - 1] = '\0';
strncpy(drive->device, device, sizeof(drive->device) - 1); strncpy(drive->device, device, sizeof(drive->device) - 1);
drive->device[sizeof(drive->device) - 1] = '\0'; 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->total_str, sizeof(drive->total_str), "%s", total_str);
snprintf(drive->used_str, sizeof(drive->used_str), "%s", used_str); snprintf(drive->used_str, sizeof(drive->used_str), "%s", used_str);
snprintf(drive->available_str, sizeof(drive->available_str), "%s", available_str); snprintf(drive->available_str, sizeof(drive->available_str), "%s", available_str);
@@ -347,6 +412,18 @@ void fill_drive_info(drive_info_t *drive,
{ {
drive->cloud_service_name[0] = '\0'; 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
@@ -410,7 +487,7 @@ void get_cloud_storage_info(const char *gvfs_path, drive_info_t *drives, int *dr
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)
@@ -423,22 +500,35 @@ void get_cloud_storage_info(const char *gvfs_path, drive_info_t *drives, int *dr
{ {
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);
}
} }
} }
@@ -464,9 +554,12 @@ void get_cloud_storage_info(const char *gvfs_path, drive_info_t *drives, int *dr
// Store information in drive_info_t structure // Store information in drive_info_t structure
drive_info_t *drive = &drives[*drive_count]; drive_info_t *drive = &drives[*drive_count];
fill_drive_info(drive, full_path, "fuse.gvfsd-fuse", entry->d_name, fill_drive_info(drive, full_path, "fuse.gvfsd-fuse", entry->d_name,
NULL, NULL,
total_str, used_str, available_str, total_str, used_str, available_str,
total_bytes, used_bytes, available_bytes, 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)++; (*drive_count)++;
} }
@@ -475,40 +568,175 @@ void get_cloud_storage_info(const char *gvfs_path, drive_info_t *drives, int *dr
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);
}
// Array to store all drive information // Label suchen
drive_info_t drives[MAX_DRIVES]; DIR *label_dir = opendir("/dev/disk/by-label/");
int drive_count = 0; 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;
// Loop through all mount points and collect drive information // Loop through all mount points and collect drive information
while ((entry = getmntent(mtab)) != NULL && drive_count < MAX_DRIVES) while ((entry = getmntent(mtab)) != NULL && *drive_count < MAX_DRIVES)
{ {
// Skip special file systems // Skip special file systems
const int skip_count = sizeof(skip_filesystems) / sizeof(skip_filesystems[0]); const int skip_count = sizeof(skip_filesystems) / sizeof(skip_filesystems[0]);
@@ -560,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;
@@ -593,22 +827,35 @@ int main(int argc, char *argv[])
{ {
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);
}
} }
} }
@@ -627,14 +874,20 @@ int main(int argc, char *argv[])
drive_type = "Other Drive"; 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 // Store information in drive_info_t structure
drive_info_t *drive = &drives[drive_count]; drive_info_t *drive = &drives[*drive_count];
fill_drive_info(drive, entry->mnt_dir, entry->mnt_type, entry->mnt_fsname, fill_drive_info(drive, entry->mnt_dir, entry->mnt_type, entry->mnt_fsname,
uuid, label,
total_str, used_str, available_str, total_str, used_str, available_str,
total_bytes, used_bytes, available_bytes, 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++; (*drive_count)++;
} }
endmntent(mtab); endmntent(mtab);
@@ -644,57 +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, drives, &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;
}
} }
// Sort drives by capacity (largest first) if (opt_no_color) {
qsort(drives, drive_count, sizeof(drive_info_t), compare_drives_by_capacity); c_bold_yellow = "";
c_reset = "";
}
// Display sorted drives if (!opt_json) {
for (int i = 0; i < drive_count; i++) printf("\n");
{ }
drive_info_t *drive = &drives[i];
// Display drive information // Array to store all drive information
if (drive->is_cloud_storage) 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++)
{ {
printf(" " BOLD_YELLOW_FORMAT "Network Drive %d (%s)" RESET_COLOR_FORMAT "\n", i + 1, drive->cloud_service_name); 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 else
{ {
printf(" " BOLD_YELLOW_FORMAT "%s %d" RESET_COLOR_FORMAT "\n", drive->drive_type, i + 1); printf("A total of %d drives found.\n", drive_count);
} }
printf(" Mount point: %s\n", drive->mount_point);
printf(" Filesystem: %s\n", drive->filesystem);
printf(" Device: %s\n", drive->device);
printf(" Total size: %s\n", drive->total_str);
printf(" Used: %s\n", drive->used_str);
printf(" Available: %s\n", drive->available_str);
// 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: 330 KiB

After

Width:  |  Height:  |  Size: 244 KiB