#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;
}