|
/* 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;
|
|
}
|