2020-04-29 19:22:58 (UTC-03:00)
Marcel Rodrigues <marcelgmr@gmail.com>
first commit: list status once and wait for q keypress
diff --git a/isv.c b/isv.c new file mode 100644 index 0000000..c95986e --- /dev/null +++ b/isv.c @@ -0,0 +1,187 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include <time.h> +#include <stdio.h> +#include <fcntl.h> +#include <termios.h> +#include <dirent.h> + +#define DEFAULT_BASE_DIR "/service" +#define MAX_NAME 32 +#define MAX_SERV 64 + +static struct service { + bool active; /* false when a "down" file is present */ + pid_t pid, log_pid; + unsigned long uptime; /* in seconds */ + char name[MAX_NAME]; +} services[MAX_SERV]; + +static char path[4096]; +static int name_col_width; + +int32_t +read_lei32(unsigned char *buf) +{ + int32_t n = buf[3]; + n <<= 8; n += buf[2]; + n <<= 8; n += buf[1]; + n <<= 8; n += buf[0]; + return n; +} + +uint64_t +read_beu64(unsigned char *buf) +{ + uint64_t n = buf[0]; + n <<= 8; n += buf[1]; + n <<= 8; n += buf[2]; + n <<= 8; n += buf[3]; + n <<= 8; n += buf[4]; + n <<= 8; n += buf[5]; + n <<= 8; n += buf[6]; + n <<= 8; n += buf[7]; + return n; +} + +int +sprint_uptime(char buf[9], unsigned long seconds) +{ + char suffix = 's'; + unsigned long value = seconds; + + if (value >= 60) { + value /= 60; + suffix = 'm'; + } + if (value >= 60) { + value /= 60; + suffix = 'h'; + } + if (value >= 24) { + value /= 24; + suffix = 'd'; + } + return snprintf(buf, 9, "%5lu %c", value, suffix); +} + +void +load_services(const char *base_dir, int nservices) +{ + int i, fd; + unsigned char stt[18]; + uint64_t when; + struct stat st; + + for (i = 0; i < nservices; i++) { + strcpy(path, base_dir); + strcat(path, "/"); + strcat(path, services[i].name); + strcat(path, "/supervise/status"); + fd = open(path, O_RDONLY); + read(fd, stt, sizeof stt); + close(fd); + services[i].pid = read_lei32(&stt[12]); + when = read_beu64(&stt[0]); + services[i].uptime = time(NULL) + 4611686018427387914ULL - when; + strcpy(path, base_dir); + strcat(path, "/"); + strcat(path, services[i].name); + strcat(path, "/log/supervise/status"); + fd = open(path, O_RDONLY); + read(fd, stt, sizeof stt); + close(fd); + services[i].log_pid = read_lei32(&stt[12]); + strcpy(path, base_dir); + strcat(path, "down"); + services[i].active = !!stat(path, &st); + } +} + +void +show_services(int nservices) +{ + int i; + char uptime_str[8]; + + printf("%*s active run log uptime\n", name_col_width, "name"); + for (i = 0; i < nservices; i++) { + sprint_uptime(uptime_str, services[i].uptime); + printf("%*s %6s %5d %5d %s\n", + name_col_width, services[i].name, + services[i].active ? "yes" : "no", + services[i].pid, services[i].log_pid, + uptime_str); + } +} + +int +main(int argc, char *argv[]) +{ + DIR *dir; + const char *base_dir = NULL; + struct dirent *entry; + int nservices; + int name_size; + struct winsize term_size; + struct termios term_prev, term_raw; + char byte; + + if (!isatty(0)) + return 1; /* quit if stdin is not a terminal */ + if (argc == 2) + base_dir = argv[1]; + else + base_dir = getenv("SVDIR"); + if (base_dir == NULL) + base_dir = DEFAULT_BASE_DIR; + if ((dir = opendir(base_dir)) == NULL) { + fprintf(stderr, "could not read directory '%s'\n", base_dir); + return 1; + } + nservices = 0; + name_col_width = 4; + while ((entry = readdir(dir)) != NULL) { + if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) + continue; + strncpy(services[nservices].name, entry->d_name, MAX_NAME-1); + name_size = strlen(entry->d_name); + if (name_size > name_col_width && name_col_width < MAX_NAME) + name_col_width = name_size; + nservices++; + if (nservices == MAX_SERV) + break; + } + closedir(dir); + if (nservices == 0) { + fprintf(stderr, "no services in '%s'\n", base_dir); + return 1; + } + ioctl(0, TIOCGWINSZ, &term_size); + if (term_size.ws_col < (name_col_width + 29) || term_size.ws_row < (nservices + 3)) { + fprintf(stderr, "sorry, terminal too small\n"); + return 1; + } + tcgetattr(0, &term_prev); + term_raw = term_prev; + term_raw.c_lflag &= ~(ECHO | ICANON); + term_raw.c_cc[VMIN] = 0x00; + term_raw.c_cc[VTIME] = 0x01; /* in deciseconds */ + tcsetattr(0, TCSAFLUSH, &term_raw); + load_services(base_dir, nservices); + show_services(nservices); + while (1) { + if (read(0, &byte, 1) == 1) { + if (byte == 'q') + break; + } + } + tcsetattr(0, TCSAFLUSH, &term_prev); + return 0; +}