implemented system monitor

simple system monitor to view cpu usage, ram and so on. this is not a
finished implementation but i just made refreshing work fine. used
alternative screen buffer, and buffered output which build the entire
display in memory and then outputs once.

there are still many bugs, like the list isnt displaying processes
sorted, or maybe not working at all. some of the border characters are
misaligned, and there is alot more usefull info that could be added.

if there is anything in specific that you would want to see in this
command, or in the program in general, just hit me up on github and
request it, and ill add it as fast as i can.

--marcus
This commit is contained in:
2025-05-31 15:02:18 +02:00
parent 2e6f7da67c
commit c53e22efef
26 changed files with 444 additions and 5 deletions

BIN
aliases.o

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -38,7 +38,7 @@ char *builtin_str[] = {
"bookmark", "bookmarks", "goto", "unbookmark", "focus_timer",
"weather", "grep", "grep-text", "ripgrep", "fzf",
"clip", "echo", "theme", "loc", "git_status",
"gg", "ls", "stats",
"gg", "ls", "stats", "monitor",
};
// Array of function pointers to built-in command implementations
@@ -53,7 +53,7 @@ int (*builtin_func[])(char **) = {
&lsh_ripgrep, &lsh_fzf_native, &lsh_clip, &lsh_echo,
&lsh_theme, &lsh_loc, &lsh_git_status, &lsh_gg,
lsh_dir,
&lsh_stats,
&lsh_stats, &builtin_monitor,
};
/**

View File

@@ -7,6 +7,7 @@
#define BUILTINS_H
#include "common.h"
#include "system_monitor.h"
#include <time.h>
// Syntax highlighting definitions using ANSI colors
@@ -85,9 +86,8 @@ void lsh_add_to_history(const char *command);
// Get number of builtin commands
int lsh_num_builtins(void);
// Expose the builtin command strings and function pointers
extern char *builtin_str[];
// Expose the builtin command strings and function pointers
extern char *builtin_str[];
extern int (*builtin_func[])(char **);
/**

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
filters.o

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
grep.o

Binary file not shown.

Binary file not shown.

BIN
main.o

Binary file not shown.

Binary file not shown.

BIN
ripgrep.o

Binary file not shown.

BIN
shell

Binary file not shown.

BIN
shell.o

Binary file not shown.

Binary file not shown.

390
system_monitor.c Normal file
View File

@@ -0,0 +1,390 @@
#include "system_monitor.h"
#include "common.h"
static struct termios old_termios;
int builtin_monitor(char **args) {
SystemStats stats;
ProcessInfo processes[50];
int proc_count;
int refresh_rate = 1; // seconds
if (args[1] != NULL && strcmp(args[1], "--help") == 0) {
printf("monitor: Real-time system monitoring dashboard\n");
printf("Usage: monitor [refresh_rate]\n");
printf("Press 'q' to quit, 'r' to refresh immediately\n");
return 1;
}
if (args[1] != NULL) {
refresh_rate = atoi(args[1]);
if (refresh_rate < 1) refresh_rate = 1;
}
// Setup terminal for raw input
tcgetattr(STDIN_FILENO, &old_termios);
struct termios new_termios = old_termios;
new_termios.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &new_termios);
// Make stdin non-blocking
int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
// Enter alternative screen buffer
printf("\033[?1049h");
hide_cursor();
fflush(stdout);
while (1) {
get_system_stats(&stats);
proc_count = get_process_info(processes, 50);
display_dashboard(&stats, processes, proc_count);
// Check for user input
if (kbhit()) {
char c = getchar();
if (c == 'q' || c == 'Q') break;
if (c == 'r' || c == 'R') continue; // refresh immediately
}
sleep(refresh_rate);
}
// Restore terminal
show_cursor();
// Exit alternative screen buffer
printf("\033[?1049l");
tcsetattr(STDIN_FILENO, TCSANOW, &old_termios);
fcntl(STDIN_FILENO, F_SETFL, flags);
fflush(stdout);
return 1;
}
void display_dashboard(SystemStats *stats, ProcessInfo *processes, int proc_count) {
char buffer[256];
char display_buffer[8192]; // Large buffer for entire display
char progress_buf[50];
char mem_used_str[32], mem_total_str[32];
char disk_read_str[32], disk_write_str[32];
char net_rx_str[32], net_tx_str[32];
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
// Format all the data first
float mem_percent = (float)stats->memory_used / stats->memory_total * 100;
format_bytes(stats->memory_used, mem_used_str);
format_bytes(stats->memory_total, mem_total_str);
format_bytes(stats->disk_read, disk_read_str);
format_bytes(stats->disk_write, disk_write_str);
format_bytes(stats->net_rx, net_rx_str);
format_bytes(stats->net_tx, net_tx_str);
// Build entire display in buffer
int pos = sprintf(display_buffer, "\033[H"); // Move to home position
pos += sprintf(display_buffer + pos,
"╔══════════════════════════════════════════════════════════════════════════════╗\n"
"║ SYSTEM MONITOR DASHBOARD ║\n"
"║ %02d:%02d:%02d %02d/%02d/%04d ║\n"
"╠══════════════════════════════════════════════════════════════════════════════╣\n",
tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec,
tm_info->tm_mday, tm_info->tm_mon + 1, tm_info->tm_year + 1900);
// CPU Usage
format_progress_bar((int)stats->cpu_percent, 40, progress_buf);
pos += sprintf(display_buffer + pos, "║ CPU Usage: %s %5.1f%% ║\n", progress_buf, stats->cpu_percent);
// Memory Usage
format_progress_bar((int)mem_percent, 40, progress_buf);
pos += sprintf(display_buffer + pos, "║ Memory: %s %5.1f%% ║\n", progress_buf, mem_percent);
pos += sprintf(display_buffer + pos, "║ Used: %-15s / %-15s ║\n",
mem_used_str, mem_total_str);
pos += sprintf(display_buffer + pos,
"║ Disk I/O: Read: %-20s ║\n"
"║ Write: %-20s ║\n"
"║ Network: RX: %-20s ║\n"
"║ TX: %-20s ║\n"
"╠══════════════════════════════════════════════════════════════════════════════╣\n"
"║ TOP PROCESSES ║\n"
"╠═══════╦══════════════════════════════╦═══════╦══════════╦═══════════════════╣\n"
"║ PID ║ NAME ║ STATE ║ CPU%% ║ MEMORY ║\n"
"╠═══════╬══════════════════════════════╬═══════╬══════════╬═══════════════════╣\n",
disk_read_str, disk_write_str, net_rx_str, net_tx_str);
for (int i = 0; i < proc_count && i < 10; i++) {
format_bytes(processes[i].memory, buffer);
pos += sprintf(display_buffer + pos, "║ %5d ║ %-28s ║ %c ║ %6.1f%% ║ %17s ║\n",
processes[i].pid,
processes[i].name,
processes[i].state,
processes[i].cpu_percent,
buffer);
}
pos += sprintf(display_buffer + pos,
"╚═══════╩══════════════════════════════╩═══════╩══════════╩═══════════════════╝\n"
"Press 'q' to quit, 'r' to refresh ");
// Output entire buffer at once
printf("%s", display_buffer);
fflush(stdout);
}
void get_system_stats(SystemStats *stats) {
FILE *fp;
char buffer[1024];
static unsigned long prev_idle = 0, prev_total = 0;
static unsigned long prev_disk_read = 0, prev_disk_write = 0;
static unsigned long prev_net_rx = 0, prev_net_tx = 0;
// CPU Usage
fp = fopen("/proc/stat", "r");
if (fp) {
fgets(buffer, sizeof(buffer), fp);
unsigned long user, nice, system, idle, iowait, irq, softirq;
sscanf(buffer, "cpu %lu %lu %lu %lu %lu %lu %lu",
&user, &nice, &system, &idle, &iowait, &irq, &softirq);
unsigned long total = user + nice + system + idle + iowait + irq + softirq;
unsigned long total_diff = total - prev_total;
unsigned long idle_diff = idle - prev_idle;
if (total_diff > 0) {
stats->cpu_percent = 100.0 * (total_diff - idle_diff) / total_diff;
} else {
stats->cpu_percent = 0.0;
}
prev_total = total;
prev_idle = idle;
fclose(fp);
}
// Memory Usage - Read from /proc/meminfo for accuracy
fp = fopen("/proc/meminfo", "r");
if (fp) {
unsigned long mem_total = 0, mem_free = 0, mem_available = 0;
unsigned long buffers = 0, cached = 0;
while (fgets(buffer, sizeof(buffer), fp)) {
if (sscanf(buffer, "MemTotal: %lu kB", &mem_total) == 1) {
stats->memory_total = mem_total * 1024;
} else if (sscanf(buffer, "MemAvailable: %lu kB", &mem_available) == 1) {
// MemAvailable accounts for buffers/cache that can be reclaimed
stats->memory_used = (mem_total - mem_available) * 1024;
} else if (sscanf(buffer, "MemFree: %lu kB", &mem_free) == 1) {
// Fallback if MemAvailable is not available
if (mem_available == 0) {
mem_free = mem_free;
}
} else if (sscanf(buffer, "Buffers: %lu kB", &buffers) == 1) {
// For fallback calculation
} else if (sscanf(buffer, "Cached: %lu kB", &cached) == 1) {
// For fallback calculation
}
}
// If MemAvailable wasn't found, calculate manually
if (mem_available == 0 && mem_total > 0) {
stats->memory_used = (mem_total - mem_free - buffers - cached) * 1024;
}
fclose(fp);
}
// Disk I/O
fp = fopen("/proc/diskstats", "r");
if (fp) {
unsigned long read_sectors = 0, write_sectors = 0;
while (fgets(buffer, sizeof(buffer), fp)) {
unsigned long r_sectors, w_sectors;
char device[32];
if (sscanf(buffer, "%*d %*d %31s %*d %*d %lu %*d %*d %*d %lu",
device, &r_sectors, &w_sectors) == 3) {
if (strncmp(device, "sd", 2) == 0 || strncmp(device, "nvme", 4) == 0) {
read_sectors += r_sectors;
write_sectors += w_sectors;
}
}
}
stats->disk_read = (read_sectors - prev_disk_read) * 512;
stats->disk_write = (write_sectors - prev_disk_write) * 512;
prev_disk_read = read_sectors;
prev_disk_write = write_sectors;
fclose(fp);
}
// Network I/O
fp = fopen("/proc/net/dev", "r");
if (fp) {
fgets(buffer, sizeof(buffer), fp); // skip header
fgets(buffer, sizeof(buffer), fp); // skip header
unsigned long rx_bytes = 0, tx_bytes = 0;
while (fgets(buffer, sizeof(buffer), fp)) {
char interface[32];
unsigned long rx, tx;
if (sscanf(buffer, " %31[^:]: %lu %*d %*d %*d %*d %*d %*d %*d %lu",
interface, &rx, &tx) == 3) {
if (strcmp(interface, "lo") != 0) { // skip loopback
rx_bytes += rx;
tx_bytes += tx;
}
}
}
stats->net_rx = rx_bytes - prev_net_rx;
stats->net_tx = tx_bytes - prev_net_tx;
prev_net_rx = rx_bytes;
prev_net_tx = tx_bytes;
fclose(fp);
}
}
int get_process_info(ProcessInfo *processes, int max_processes) {
DIR *proc_dir;
struct dirent *entry;
FILE *fp;
char path[256];
char buffer[1024];
int count = 0;
proc_dir = opendir("/proc");
if (!proc_dir) return 0;
while ((entry = readdir(proc_dir)) != NULL && count < max_processes) {
if (!isdigit(entry->d_name[0])) continue;
int pid = atoi(entry->d_name);
processes[count].pid = pid;
// Get process name and state
snprintf(path, sizeof(path), "/proc/%d/stat", pid);
fp = fopen(path, "r");
if (fp) {
if (fgets(buffer, sizeof(buffer), fp)) {
char *name_start = strchr(buffer, '(');
char *name_end = strrchr(buffer, ')');
if (name_start && name_end) {
int name_len = name_end - name_start - 1;
if (name_len > 255) name_len = 255;
strncpy(processes[count].name, name_start + 1, name_len);
processes[count].name[name_len] = '\0';
char *state_pos = name_end + 2;
processes[count].state = *state_pos;
}
}
fclose(fp);
}
// Get memory usage
snprintf(path, sizeof(path), "/proc/%d/status", pid);
fp = fopen(path, "r");
if (fp) {
while (fgets(buffer, sizeof(buffer), fp)) {
if (strncmp(buffer, "VmRSS:", 6) == 0) {
unsigned long mem_kb;
sscanf(buffer, "VmRSS: %lu kB", &mem_kb);
processes[count].memory = mem_kb * 1024;
break;
}
}
fclose(fp);
}
processes[count].cpu_percent = 0.0; // Simplified for this example
count++;
}
closedir(proc_dir);
// Sort by memory usage (descending)
for (int i = 0; i < count - 1; i++) {
for (int j = i + 1; j < count; j++) {
if (processes[i].memory < processes[j].memory) {
ProcessInfo temp = processes[i];
processes[i] = processes[j];
processes[j] = temp;
}
}
}
return count;
}
void clear_screen(void) {
printf("\033[2J\033[H");
fflush(stdout);
}
void move_cursor(int row, int col) {
printf("\033[%d;%dH", row, col);
fflush(stdout);
}
void hide_cursor(void) {
printf("\033[?25l");
fflush(stdout);
}
void show_cursor(void) {
printf("\033[?25h");
fflush(stdout);
}
int kbhit(void) {
int ch = getchar();
if (ch != EOF) {
ungetc(ch, stdin);
return 1;
}
return 0;
}
void draw_progress_bar(int percentage, int width) {
int filled = (percentage * width) / 100;
printf("[");
for (int i = 0; i < width; i++) {
if (i < filled) {
printf("█");
} else {
printf(" ");
}
}
printf("]");
}
void format_progress_bar(int percentage, int width, char *buffer) {
int filled = (percentage * width) / 100;
int pos = 0;
buffer[pos++] = '[';
for (int i = 0; i < width; i++) {
if (i < filled) {
buffer[pos++] = '#'; // Use # instead of Unicode block
} else {
buffer[pos++] = ' ';
}
}
buffer[pos++] = ']';
buffer[pos] = '\0';
}
void format_bytes(unsigned long bytes, char *buffer) {
const char *units[] = {"B", "KB", "MB", "GB", "TB"};
int unit = 0;
double size = bytes;
while (size >= 1024 && unit < 4) {
size /= 1024;
unit++;
}
if (unit == 0) {
snprintf(buffer, 64, "%lu %s", bytes, units[unit]);
} else {
snprintf(buffer, 64, "%.1f %s", size, units[unit]);
}
}

49
system_monitor.h Normal file
View File

@@ -0,0 +1,49 @@
#ifndef SYSTEM_MONITOR_H
#define SYSTEM_MONITOR_H
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/statvfs.h>
#include <sys/sysinfo.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
#include <ctype.h>
typedef struct {
float cpu_percent;
unsigned long memory_used;
unsigned long memory_total;
unsigned long disk_read;
unsigned long disk_write;
unsigned long net_rx;
unsigned long net_tx;
int process_count;
} SystemStats;
typedef struct {
int pid;
char name[256];
float cpu_percent;
unsigned long memory;
char state;
} ProcessInfo;
int builtin_monitor(char **args);
void display_dashboard(SystemStats *stats, ProcessInfo *processes,
int proc_count);
void get_system_stats(SystemStats *stats);
int get_process_info(ProcessInfo *processes, int max_processes);
void clear_screen(void);
void move_cursor(int row, int col);
void hide_cursor(void);
void show_cursor(void);
int kbhit(void);
void draw_progress_bar(int percentage, int width);
void format_progress_bar(int percentage, int width, char *buffer);
void format_bytes(unsigned long bytes, char *buffer);
#endif

BIN
system_monitor.o Normal file

Binary file not shown.

Binary file not shown.

BIN
themes.o

Binary file not shown.

BIN
timer.o

Binary file not shown.

BIN
weather.o

Binary file not shown.