/* retoor <retoor@molodetz.nl> */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <getopt.h>
#include <sys/select.h>
#include "terminal.h"
#include "system.h"
#include "cpu.h"
#include "memory.h"
#include "process.h"
#include "network.h"
#include "disk.h"
#include "filesystem.h"
#include "display.h"
#define TAB_CPU 0
#define TAB_MEMORY 1
#define TAB_NETWORK 2
#define TAB_DISK 3
#define TAB_PROCESS 4
#define TAB_COUNT 5
static volatile int running = 1;
static Terminal term;
static void signal_handler(int sig) {
(void)sig;
running = 0;
}
static void resize_handler(int sig) {
(void)sig;
terminal_get_size(&term);
}
static void print_help(const char *program) {
printf("rtop - Terminal-based system monitor\n\n");
printf("Usage: %s [OPTIONS]\n\n", program);
printf("Options:\n");
printf(" -d, --delay SECS Update interval in seconds (default: 1.0)\n");
printf(" -r, --rotate SECS Auto-rotate tabs every SECS seconds (0=off, default: 0)\n");
printf(" -s, --sort MODE Sort by: cpu (default), mem\n");
printf(" -h, --help Show this help message\n");
printf(" -v, --version Show version\n");
printf("\nNavigation:\n");
printf(" Left/Right arrows Switch tabs\n");
printf(" 1-5 Jump to tab\n");
printf(" q Quit\n");
}
static void print_version(void) {
printf("rtop 1.0.0\n");
}
static int read_key(void) {
char buf[8];
int n = read(STDIN_FILENO, buf, sizeof(buf));
if (n <= 0) return 0;
if (n == 1) return buf[0];
if (n >= 3 && buf[0] == 27 && buf[1] == '[') {
switch (buf[2]) {
case 'A': return 1001;
case 'B': return 1002;
case 'C': return 1003;
case 'D': return 1004;
}
}
return buf[0];
}
int main(int argc, char *argv[]) {
double refresh_interval = 1.0;
double rotate_interval = 0.0;
int sort_by_mem = 0;
int current_tab = TAB_OVERVIEW;
static struct option long_options[] = {
{"delay", required_argument, 0, 'd'},
{"rotate", required_argument, 0, 'r'},
{"sort", required_argument, 0, 's'},
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'v'},
{0, 0, 0, 0}
};
int opt;
while ((opt = getopt_long(argc, argv, "d:r:s:hv", long_options, NULL)) != -1) {
switch (opt) {
case 'd':
refresh_interval = atof(optarg);
if (refresh_interval < 0.1) refresh_interval = 0.1;
if (refresh_interval > 10.0) refresh_interval = 10.0;
break;
case 'r':
rotate_interval = atof(optarg);
if (rotate_interval < 0) rotate_interval = 0;
break;
case 's':
if (strcmp(optarg, "mem") == 0 || strcmp(optarg, "memory") == 0) {
sort_by_mem = 1;
}
break;
case 'h':
print_help(argv[0]);
return 0;
case 'v':
print_version();
return 0;
default:
print_help(argv[0]);
return 1;
}
}
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGWINCH, resize_handler);
if (terminal_init(&term) != 0) {
fprintf(stderr, "Failed to initialize terminal\n");
return 1;
}
SystemInfo sys;
CpuInfo cpu;
MemoryInfo mem;
ProcessList procs;
NetworkInfo net;
DiskInfo disk;
FilesystemInfo fs;
if (system_info_init(&sys) != 0 ||
cpu_info_init(&cpu) != 0 ||
memory_info_init(&mem) != 0 ||
process_list_init(&procs) != 0 ||
network_info_init(&net) != 0 ||
disk_info_init(&disk) != 0 ||
filesystem_info_init(&fs) != 0) {
terminal_cleanup(&term);
fprintf(stderr, "Failed to initialize system info\n");
return 1;
}
struct timeval tv;
double rotate_elapsed = 0.0;
while (running) {
system_info_update(&sys);
cpu_info_update(&cpu, refresh_interval);
memory_info_update(&mem, refresh_interval);
process_list_update(&procs, mem.total, refresh_interval, cpu.num_cores);
network_info_update(&net, refresh_interval);
disk_info_update(&disk, refresh_interval);
filesystem_info_update(&fs);
if (sort_by_mem) {
process_list_sort_by_mem(&procs);
} else {
process_list_sort_by_cpu(&procs);
}
if (rotate_interval > 0) {
rotate_elapsed += refresh_interval;
if (rotate_elapsed >= rotate_interval) {
current_tab = (current_tab + 1) % TAB_COUNT;
rotate_elapsed = 0.0;
}
}
terminal_get_size(&term);
terminal_buffer_clear(&term);
terminal_buffer_append(&term, "\033[2J\033[H");
display_header(&term, &sys);
display_tabs(&term, current_tab);
switch (current_tab) {
case TAB_CPU:
display_cpu_detail(&term, &cpu);
break;
case TAB_MEMORY:
display_memory_detail(&term, &mem, &procs);
break;
case TAB_NETWORK:
display_network_detail(&term, &net);
break;
case TAB_DISK:
display_disk_detail(&term, &disk, &fs);
break;
case TAB_PROCESS:
display_process_detail(&term, &procs);
break;
}
display_footer(&term, refresh_interval);
terminal_buffer_flush(&term);
fd_set fds;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
tv.tv_sec = (long)refresh_interval;
tv.tv_usec = (long)((refresh_interval - tv.tv_sec) * 1000000);
int ret = select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
if (ret > 0) {
int key = read_key();
if (key == 'q' || key == 'Q') {
running = 0;
} else if (key == 1003) {
current_tab = (current_tab + 1) % TAB_COUNT;
rotate_elapsed = 0.0;
} else if (key == 1004) {
current_tab = (current_tab - 1 + TAB_COUNT) % TAB_COUNT;
rotate_elapsed = 0.0;
} else if (key >= '1' && key <= '5') {
current_tab = key - '1';
rotate_elapsed = 0.0;
}
}
}
terminal_cleanup(&term);
return 0;
}