From cc1e38b74144864681592302b3fca5af3e5ef0ad Mon Sep 17 00:00:00 2001 From: arf20 Date: Fri, 24 Oct 2025 23:22:59 +0200 Subject: wip uptime --- events.log | 2 + index.htm.tmpl | 2 +- main.c | 4 +- monitor.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++----------- monitor.cfg | 8 +-- monitor.h | 1 + 6 files changed, 160 insertions(+), 42 deletions(-) diff --git a/events.log b/events.log index 3c3ddf5..1affe2b 100644 --- a/events.log +++ b/events.log @@ -71,4 +71,6 @@ http,2025-08-26 11:46:20,down http,2025-08-26 11:51:24,up http,2025-08-26 14:39:43,down http,2025-08-26 14:44:45,up +http,2025-10-24 10:49:20,down +http,2025-10-24 12:38:19,up diff --git a/index.htm.tmpl b/index.htm.tmpl index cf90884..6637f05 100644 --- a/index.htm.tmpl +++ b/index.htm.tmpl @@ -36,7 +36,7 @@ table, th, td {

Incidents - + %s
ResolvedStartedDuration
ServiceResolvedStartedDuration
diff --git a/main.c b/main.c index fdfc5cd..8c0405f 100644 --- a/main.c +++ b/main.c @@ -40,7 +40,7 @@ enum MHD_Result answer_to_connection( time_t time_now = time(NULL); struct tm *tm_now = gmtime(&time_now); static char timestr[256]; - strftime(timestr, 256, "%Y-%m-%d %H-%M-%S", tm_now); + strftime(timestr, 256, "%Y-%m-%d %H:%M:%S", tm_now); printf("[%s] [webserver] %s %s %s: ", timestr, inet_ntoa((*coninfo)->sin_addr), method, url); @@ -51,7 +51,7 @@ enum MHD_Result answer_to_connection( if (strcmp(method, "GET") == 0 && strcmp(url, "/") == 0) { snprintf(buff, RES_BUFF, index_format_template, - monitor_generate_status_html(), "(incidents)"); + monitor_generate_status_html(), monitor_generate_incidents_html()); response = MHD_create_response_from_buffer(strlen(buff), (void*)buff, MHD_RESPMEM_PERSISTENT); diff --git a/monitor.c b/monitor.c index 41bcc61..e6165a3 100644 --- a/monitor.c +++ b/monitor.c @@ -38,25 +38,58 @@ typedef struct { status_t status; event_t *events; - size_t event_size, event_capacity; + size_t events_size, events_capacity; } target_t; +/* baked */ +typedef struct { + time_t started_time; + time_t duration_time; + int resolved; + char *service; + char *started; + char *duration; +} incident_t; + static target_t targets[INIT_VEC_CAPACITY]; -static size_t target_n = 0; +static size_t targets_n = 0; +/* ordered*/ +static incident_t *incidents = NULL; +static size_t incidents_size = 0, incidents_capacity = 0; static char timestr[256]; - static void target_events_push(target_t *target, event_t event) { - if (target->event_size + 1 > target->event_capacity) - target->events = realloc(target->events, 2 * target->event_capacity); + if (target->events_size + 1 > target->events_capacity) + target->events = realloc(target->events, + 2 * sizeof(event_t) * target->events_capacity); + + target->events[target->events_size++] = event; +} + +static void +incidents_push_ordered(const incident_t *incident) +{ + if (incidents_size + 1 > incidents_capacity) + incidents = realloc(incidents, + 2 * sizeof(incident_t) * incidents_capacity); + + size_t i = 0; + while (incidents[i].started_time < incident->started_time + && i < incidents_size) + i++; + /* incidents[i].started_time >= incident.started_time */ + memmove(&incidents[i + 1], &incidents[i], + (incidents_size - i) * sizeof(incident_t)); - target->events[target->event_size++] = event; + incidents[i] = *incident; + + incidents_size++; } static size_t @@ -91,7 +124,7 @@ target_events_load(target_t *target, const char *logbuff) { continue; struct tm event_time; - strptime(time, "%Y-%m-%d %H-%M-%S", &event_time); + strptime(time, "%Y-%m-%d %H:%M:%S", &event_time); event_t event = { mktime(&event_time), @@ -106,6 +139,48 @@ target_events_load(target_t *target, const char *logbuff) { return n; } +/* assume events start with down */ +void +incidents_render() +{ + char buff[256]; + + for (size_t i = 0; i < targets_n; i++) { + /* iterate through downs */ + for (size_t j = 0; j < targets[i].events_size; j++) { + if (targets[i].events[j].status != STATUS_DOWN) + continue; + /* next must be up */ + int resolved = 1; + if (targets[i].events_size == j + 1 || + targets[i].events[j + 1].status != STATUS_UP) + resolved = 0; + + time_t start = targets[i].events[j].time; + time_t duration = targets[i].events[j + 1].time - start; + + snprintf(buff, 256, "%ldh %ldm %lds", duration / 3600, + (duration / 60) % 60, duration % 60); + char *durationstr = strdup(buff); + + struct tm *tm_start = gmtime(&start); + strftime(buff, 256, "%Y-%m-%d %H:%M:%S", tm_start); + char *startstr = strdup(buff); + + incident_t incident = { + targets[i].events[j].time, + duration, + resolved, + targets[i].name, + startstr, + durationstr + }; + + incidents_push_ordered(&incident); + } + } +} + int monitor_init(const char *cfg_path, const char *log_path) { /* read monitor log */ @@ -152,35 +227,41 @@ monitor_init(const char *cfg_path, const char *log_path) { } if (strcmp(type, "reach") == 0) - targets[target_n].type = TYPE_REACH; + targets[targets_n].type = TYPE_REACH; else if (strcmp(type, "dns") == 0) - targets[target_n].type = TYPE_DNS; + targets[targets_n].type = TYPE_DNS; else if (strcmp(type, "web") == 0) - targets[target_n].type = TYPE_WEB; + targets[targets_n].type = TYPE_WEB; - targets[target_n].name = strdup(name); - targets[target_n].target = strdup(target); - targets[target_n].status = STATUS_DOWN; + targets[targets_n].name = strdup(name); + targets[targets_n].target = strdup(target); + targets[targets_n].status = STATUS_DOWN; /* read monitor logs */ - targets[target_n].event_capacity = INIT_VEC_CAPACITY; - targets[target_n].event_size = 0; - targets[target_n].events = malloc(INIT_VEC_CAPACITY * sizeof(event_t)); + targets[targets_n].events_capacity = INIT_VEC_CAPACITY; + targets[targets_n].events_size = 0; + targets[targets_n].events = malloc(INIT_VEC_CAPACITY * sizeof(event_t)); - size_t event_n = target_events_load(&targets[target_n], logbuff); + size_t event_n = target_events_load(&targets[targets_n], logbuff); printf("\t%s: %s,%s %ld events\n", - targets[target_n].name, - type_str[targets[target_n].type], - targets[target_n].target, + targets[targets_n].name, + type_str[targets[targets_n].type], + targets[targets_n].target, event_n ); - target_n++; + targets_n++; } fclose(cfgf); + incidents = malloc(sizeof(incident_t) * INIT_VEC_CAPACITY); + incidents_capacity = INIT_VEC_CAPACITY; + incidents_size = 0; + + incidents_render(); + CURLcode res = curl_global_init(CURL_GLOBAL_ALL); if (res) { fprintf(stderr, "Error initializing cURL: %s\n", @@ -188,7 +269,26 @@ monitor_init(const char *cfg_path, const char *log_path) { return -1; } - return 0; + return 0; +} + +const char * +target_uptime(const target_t *target) +{ + static char buff[256]; + + event_t last_event = target->events[target->events_size - 1]; + + if (last_event.status == STATUS_DOWN) { + snprintf(buff, 256, "-"); + return buff; + } + + time_t uptime = time(NULL) - last_event.time; + snprintf(buff, 256, "%ldd %ldh %ldm %lds", + uptime / (3600*24), (uptime / 3600) % 24, (uptime / 60) % 60, uptime % 60); + + return buff; } const char * @@ -203,15 +303,15 @@ monitor_generate_status_html() char *pos = buff; - for (size_t i = 0; i < target_n; i++) { - pos += snprintf(pos, BUFF_SIZE, - "%s%s%s%s%s%s\n", - type_str[targets[i].type], - targets[i].target, - status_html[targets[i].status], - "", - "", - "" + for (size_t i = 0; i < targets_n; i++) { + pos += snprintf(pos, BUFF_SIZE - (pos - buff), + "%s%s%s%s%s%s\n", + type_str[targets[i].type], /* type */ + targets[i].target, /* target */ + status_html[targets[i].status], /* status */ + target_uptime(&targets[i]), /* uptime */ + "", /* %up month */ + "" /* %up total */ ); } @@ -223,8 +323,23 @@ const char * monitor_generate_incidents_html() { static char buff[BUFF_SIZE]; - - snprintf(buff, BUFF_SIZE, ""); + + static const char *resolvedstr[] = { + "unresolved", + "resolved" + }; + + char *pos = buff; + + for (int i = incidents_size - 1; i >= 0; i--) { + pos += snprintf(pos, BUFF_SIZE - (pos - buff), + "%s%s%s%s\n", + incidents[i].service, + resolvedstr[incidents[i].resolved], + incidents[i].started, + incidents[i].duration + ); + } return buff; } @@ -323,7 +438,7 @@ monitor_check() static size_t check_num = 0; time_t time_now = time(NULL); struct tm *tm_now = gmtime(&time_now); - strftime(timestr, 256, "%Y-%m-%d %H-%M-%S", tm_now); + strftime(timestr, 256, "%Y-%m-%d %H:%M:%S", tm_now); static const int (*check_funcs[])(const char *) = { check_reach, @@ -331,7 +446,7 @@ monitor_check() check_http }; - for (size_t i = 0; i < target_n; i++) { + for (size_t i = 0; i < targets_n; i++) { printf("[%s] [monitor] check #%ld %s: ", timestr, check_num, targets[i].name); targets[i].status = check_funcs[targets[i].type](targets[i].target); diff --git a/monitor.cfg b/monitor.cfg index ee4c4a1..cc85d17 100644 --- a/monitor.cfg +++ b/monitor.cfg @@ -1,7 +1,7 @@ # Monitor config # type,name,target -reach,ipv4,10.0.0.0 -dns,dns,arf20.co -web,http,http://arf20.com:4321 -web,https,https://arf20.co +reach,ipv4,2.59.235.35 +dns,dns,arf20.com +web,http,http://arf20.com +web,https,https://arf20.com diff --git a/monitor.h b/monitor.h index b9ab3c9..8c2529e 100644 --- a/monitor.h +++ b/monitor.h @@ -3,6 +3,7 @@ int monitor_init(const char *cfg_file, const char *log_file); const char *monitor_generate_status_html(); +const char *monitor_generate_incidents_html(); void monitor_check(); #endif /* _MONITOR_H */ -- cgit v1.2.3