245 lines
7.6 KiB
C
245 lines
7.6 KiB
C
#ifndef H_MAIN
|
|
#define H_MAIN
|
|
#include <linux/limits.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <string.h>
|
|
#include <arpa/inet.h>
|
|
#include <netinet/in.h>
|
|
#include <microhttpd.h>
|
|
|
|
#include "../deps/vector/vector.h"
|
|
#include "mxml.h"
|
|
|
|
#include "util.h"
|
|
#include "services.c"
|
|
|
|
#define PAGE_INDEX 0
|
|
#define PAGE_ASSET 1
|
|
#define PAGE_ERROR 2
|
|
|
|
mxml_node_t* doc;
|
|
|
|
uint8_t running = 0;
|
|
char* daemon_name = "Stethoscope";
|
|
uint16_t listen_port = 7800;
|
|
char* webroot = "share/";
|
|
uint32_t global_backoff = 5;
|
|
|
|
char* cached_index;
|
|
|
|
int get_asset(char** buffer, const char* path, size_t* size) {
|
|
*size = 0;
|
|
if (strlen(webroot) + strlen(path) + 2 > PATH_MAX)
|
|
return 403;
|
|
|
|
char full_path[PATH_MAX];
|
|
snprintf(full_path, sizeof(full_path), "%s%s", webroot, path);
|
|
|
|
char resolved_path[PATH_MAX];
|
|
if (realpath(full_path, resolved_path) == NULL)
|
|
return 403;
|
|
|
|
char resolved_base[PATH_MAX];
|
|
if (realpath(webroot, resolved_base) == NULL)
|
|
return 403;
|
|
|
|
size_t resolved_base_len = strlen(resolved_base);
|
|
if (strncmp(resolved_path, resolved_base, resolved_base_len) != 0)
|
|
return 400;
|
|
|
|
if (resolved_path[resolved_base_len] != '\0' && resolved_path[resolved_base_len] != '/')
|
|
return 400;
|
|
|
|
FILE* asset = fopen(resolved_path, "rb");
|
|
if (!asset) return 404;
|
|
|
|
fseek(asset, 0, SEEK_END);
|
|
*size = ftell(asset);
|
|
rewind(asset);
|
|
|
|
*buffer = (char*)malloc(*size + 1);
|
|
if (!*buffer) {
|
|
fclose(asset);
|
|
*size = 0;
|
|
return 500;
|
|
}
|
|
|
|
size_t bytes_read = fread(*buffer, 1, *size, asset);
|
|
fclose(asset);
|
|
|
|
if (bytes_read != *size) {
|
|
free(*buffer);
|
|
*size = 0;
|
|
return 500;
|
|
}
|
|
|
|
buffer[*size] = 0;
|
|
|
|
return 200;
|
|
};
|
|
|
|
struct XMLServiceRow {
|
|
struct Service* service;
|
|
mxml_node_t* row;
|
|
};
|
|
|
|
static enum MHD_Result http_response(void *cls, struct MHD_Connection *conn, const char* uri, const char* method, const char* version, const char* upload_Data, size_t* upload_data_size, void** req_cls) {
|
|
const union MHD_ConnectionInfo *info = MHD_get_connection_info(conn, MHD_CONNECTION_INFO_CLIENT_ADDRESS);
|
|
const char* client_ip = (char*)malloc(64);
|
|
uint16_t client_port = 0;
|
|
if (info != NULL) {
|
|
const struct sockaddr *addr = info->client_addr;
|
|
char ip_str[INET6_ADDRSTRLEN];
|
|
|
|
if (addr->sa_family == AF_INET) {
|
|
const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr;
|
|
client_ip = inet_ntop(AF_INET, &addr4->sin_addr, ip_str, sizeof(ip_str));
|
|
client_port = ntohs(addr4->sin_port);
|
|
} else if (addr->sa_family == AF_INET6) {
|
|
const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr;
|
|
client_ip = inet_ntop(AF_INET6, &addr6->sin6_addr, ip_str, sizeof(ip_str));
|
|
client_port = ntohs(addr6->sin6_port);
|
|
}
|
|
}
|
|
|
|
uint16_t response_code = 0;
|
|
char* page;
|
|
size_t page_size = 0;
|
|
|
|
if (strcmp(method, "GET") == 0) {
|
|
if (strcmp(uri, "/") != 0)
|
|
response_code = get_asset(&page, uri, &page_size);
|
|
} else response_code = 400;
|
|
|
|
if (response_code != 200) {
|
|
mxml_node_t* doc = mxmlNewXML("1.0");
|
|
mxml_node_t* html = mxmlNewElement(doc, "html");
|
|
mxmlElementSetAttr(html, "xmlns", "http://www.w3.org/1999/xhtml");
|
|
mxmlElementSetAttr(html, "xml:lang", "en");
|
|
mxmlElementSetAttr(html, "lang", "en");
|
|
|
|
mxml_node_t* head = mxmlNewElement(html, "head");
|
|
mxml_node_t *title = mxmlNewElement(head, "title");
|
|
mxmlNewText(title, 0, daemon_name);
|
|
|
|
mxml_node_t* meta_charset = mxmlNewElement(head, "meta");
|
|
mxmlElementSetAttr(meta_charset, "http-equiv", "Content-Type");
|
|
mxmlElementSetAttr(meta_charset, "content", "text/html; charset=utf-8");
|
|
|
|
mxml_node_t* meta_viewport = mxmlNewElement(head, "meta");
|
|
mxmlElementSetAttr(meta_viewport, "name", "viewport");
|
|
mxmlElementSetAttr(meta_viewport, "content", "width=device-width, initial-scale=1.0");
|
|
|
|
mxml_node_t* stylesheet = mxmlNewElement(head, "link");
|
|
mxmlElementSetAttr(stylesheet, "rel", "stylesheet");
|
|
mxmlElementSetAttr(stylesheet, "href", "style.css");
|
|
|
|
mxml_node_t* body = mxmlNewElement(html, "body");
|
|
|
|
mxml_node_t* center = mxmlNewElement(body, "center");
|
|
|
|
if (strcmp(uri, "/") == 0 && response_code == 0) {
|
|
mxml_node_t* header = mxmlNewElement(center, "h1");
|
|
mxmlNewText(header, 0, daemon_name);
|
|
|
|
int row_index = 0;
|
|
struct XMLServiceRow table_rows[services.size];
|
|
|
|
mxml_node_t* table = mxmlNewElement(center, "table");
|
|
mxmlElementSetAttr(table, "width", "100%");
|
|
|
|
mxml_node_t* colgroup = mxmlNewElement(table, "colgroup");
|
|
|
|
Iterator service_it = vector_begin(&services);
|
|
Iterator service_end = vector_end(&services);
|
|
for (; !iterator_equals(&service_it, &service_end); iterator_increment(&service_it)) {
|
|
struct Service* service = *(struct Service**)iterator_get(&service_it);
|
|
mxml_node_t* header_row = mxmlNewElement(table, "tr");
|
|
mxml_node_t* header_cell = mxmlNewElement(table, "th");
|
|
mxmlElementSetAttr(header_cell, "align", "left");
|
|
mxmlNewText(header_cell, 0, service->name);
|
|
|
|
mxml_node_t* status_row = mxmlNewElement(table, "tr");
|
|
|
|
table_rows[row_index] = (struct XMLServiceRow){ service, status_row };
|
|
row_index++;
|
|
}
|
|
|
|
int col_count = 1;
|
|
long now = microtime();
|
|
long earliest_time = 0;
|
|
|
|
Iterator count_it = vector_begin(&status_timeline);
|
|
Iterator peroid_end = vector_end(&status_timeline);
|
|
for (; !iterator_equals(&count_it, &peroid_end); iterator_increment(&count_it)) {
|
|
struct StatusPeroid* peroid = *(struct StatusPeroid**)iterator_get(&count_it);
|
|
if (earliest_time == 0) earliest_time = peroid->time;
|
|
else if (peroid->time <= (now - 86400000000)) {
|
|
earliest_time = peroid->time;
|
|
} else col_count++;
|
|
}
|
|
|
|
long last_time = earliest_time;
|
|
long total_time = now - earliest_time;
|
|
Iterator peroid_it = vector_begin(&status_timeline);
|
|
for (; !iterator_equals(&peroid_it, &peroid_end); iterator_increment(&peroid_it)) {
|
|
struct StatusPeroid* peroid = *(struct StatusPeroid**)iterator_get(&peroid_it);
|
|
if (peroid->time < earliest_time) continue;
|
|
|
|
long duration = peroid->time - last_time;
|
|
if (total_time > 0 && duration > 0) {
|
|
double col_size = (double)duration / total_time * 100.0L;
|
|
char* size_str = (char*)malloc(50);
|
|
snprintf(size_str, 50, "%lf%%", col_size);
|
|
|
|
mxml_node_t* col = mxmlNewElement(colgroup, "col");
|
|
mxmlElementSetAttr(col, "width", size_str);
|
|
last_time = peroid->time;
|
|
}
|
|
|
|
Iterator sit = vector_begin(&peroid->server_statuses);
|
|
Iterator send = vector_end(&peroid->server_statuses);
|
|
for (; !iterator_equals(&sit, &send); iterator_increment(&sit)) {
|
|
struct ServerStatus* srvstat = *(struct ServerStatus**)iterator_get(&sit);
|
|
|
|
struct XMLServiceRow* row_struct;
|
|
for (int x = 0; x < services.size; x++)
|
|
if (table_rows[x].service == srvstat->service) {
|
|
row_struct = &table_rows[x];
|
|
break;
|
|
}
|
|
|
|
if (row_struct->service != 0) {
|
|
mxml_node_t* cell = mxmlNewElement(row_struct->row, "td");
|
|
mxmlElementSetAttr(cell, "_status", state_text(srvstat->state));
|
|
char* ping_str = (char*)malloc(50);
|
|
snprintf(ping_str, 50, "%f ms", srvstat->ping);
|
|
mxmlElementSetAttr(cell, "_ping", ping_str);
|
|
}
|
|
}
|
|
}
|
|
|
|
response_code = 200;
|
|
} else {
|
|
mxml_node_t* error_header = mxmlNewElement(center, "h1");
|
|
char err_str[5];
|
|
snprintf(err_str, sizeof(err_str)-1, "%i", response_code);
|
|
mxmlNewText(error_header, 0, err_str);
|
|
}
|
|
|
|
page = mxmlSaveAllocString(doc, MXML_NO_CALLBACK);
|
|
page_size = strlen(page);
|
|
}
|
|
|
|
printf("[%lu] %s %s \"%s\" %s:%u %zu %zu %i\n", time(NULL), method, uri, version, client_ip, client_port, *upload_data_size, page_size, response_code);
|
|
fflush(stdout);
|
|
|
|
struct MHD_Response* response = MHD_create_response_from_buffer_static(page_size, page);
|
|
enum MHD_Result ret = MHD_queue_response(conn, response_code, response);
|
|
MHD_destroy_response(response);
|
|
return ret;
|
|
};
|
|
|
|
#endif |