Have server generate index instead, add service icons and banner, add config variables to handle web refresh and status refresh, properly shut down services on close
This commit is contained in:
39
src/main.c
39
src/main.c
@@ -21,6 +21,7 @@ static void signal_handler(int signo) {
|
|||||||
struct PreService {
|
struct PreService {
|
||||||
char* id;
|
char* id;
|
||||||
char* name;
|
char* name;
|
||||||
|
char* icon;
|
||||||
char* text;
|
char* text;
|
||||||
char* endpoints;
|
char* endpoints;
|
||||||
uint32_t backoff;
|
uint32_t backoff;
|
||||||
@@ -34,12 +35,19 @@ static int config_handler(void* user, const char* section, const char* name, con
|
|||||||
} else if (strcmp(name, "webroot") == 0) {
|
} else if (strcmp(name, "webroot") == 0) {
|
||||||
webroot = malloc(strlen(value)+1);
|
webroot = malloc(strlen(value)+1);
|
||||||
strcpy(webroot, value);
|
strcpy(webroot, value);
|
||||||
|
} else if (strcmp(name, "banner") == 0) {
|
||||||
|
daemon_banner = malloc(strlen(value)+1);
|
||||||
|
strcpy(daemon_banner, value);
|
||||||
} else if (strcmp(name, "listen") == 0) {
|
} else if (strcmp(name, "listen") == 0) {
|
||||||
listen_port = atoi(value);
|
listen_port = atoi(value);
|
||||||
} else if (strcmp(name, "backoff") == 0) {
|
} else if (strcmp(name, "backoff") == 0) {
|
||||||
int val = atoi(value);
|
int result = atoi(value);
|
||||||
if (val < 1) val = 1;
|
global_backoff = result > 1 ? result : 1;
|
||||||
global_backoff = val;
|
} else if (strcmp(name, "web_refresh") == 0) {
|
||||||
|
int result = atoi(value);
|
||||||
|
web_refresh = result > 1 ? result : 1;
|
||||||
|
} else if (strcmp(name, "status_refresh") == 0) {
|
||||||
|
status_refresh = atoi(value);
|
||||||
} else {
|
} else {
|
||||||
printf("Unknown configuration entry \'%s\'\n", name);
|
printf("Unknown configuration entry \'%s\'\n", name);
|
||||||
}
|
}
|
||||||
@@ -70,6 +78,9 @@ static int config_handler(void* user, const char* section, const char* name, con
|
|||||||
} else if (strcmp(name, "text") == 0) {
|
} else if (strcmp(name, "text") == 0) {
|
||||||
current_service->text = malloc(strlen(value)+1);
|
current_service->text = malloc(strlen(value)+1);
|
||||||
strcpy(current_service->text, value);
|
strcpy(current_service->text, value);
|
||||||
|
} else if (strcmp(name, "icon") == 0) {
|
||||||
|
current_service->icon = malloc(strlen(value)+1);
|
||||||
|
strcpy(current_service->icon, value);
|
||||||
} else if (strcmp(name, "address") == 0) {
|
} else if (strcmp(name, "address") == 0) {
|
||||||
current_service->endpoints = malloc(strlen(value)+1);
|
current_service->endpoints = malloc(strlen(value)+1);
|
||||||
strcpy(current_service->endpoints, value);
|
strcpy(current_service->endpoints, value);
|
||||||
@@ -146,6 +157,7 @@ int main(int argc, char** argv) {
|
|||||||
struct Service* service = malloc(8192);
|
struct Service* service = malloc(8192);
|
||||||
service->id = preservice->id;
|
service->id = preservice->id;
|
||||||
service->name = preservice->name;
|
service->name = preservice->name;
|
||||||
|
service->icon = preservice->icon;
|
||||||
service->text = preservice->text;
|
service->text = preservice->text;
|
||||||
service->last_state = DOWN;
|
service->last_state = DOWN;
|
||||||
service->backoff = preservice->backoff == 0 ? global_backoff : preservice->backoff;
|
service->backoff = preservice->backoff == 0 ? global_backoff : preservice->backoff;
|
||||||
@@ -171,15 +183,34 @@ int main(int argc, char** argv) {
|
|||||||
fprintf(stderr, "Failed to start HTTP daemon\n");
|
fprintf(stderr, "Failed to start HTTP daemon\n");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
int last_doc_generated = 0;
|
||||||
running = 1;
|
running = 1;
|
||||||
while (daemon != NULL && running == 1) {
|
while (daemon != NULL && running == 1) {
|
||||||
|
time_t now = time(NULL);
|
||||||
|
if (queued_time <= now - status_refresh) {
|
||||||
|
queued_time = now;
|
||||||
|
report_queued = 1;
|
||||||
|
}
|
||||||
if (report_queued > 0) {
|
if (report_queued > 0) {
|
||||||
report_queued = 0;
|
report_queued = 0;
|
||||||
report();
|
report();
|
||||||
} else sleep(1);
|
}
|
||||||
|
if (last_doc_generated <= now - web_refresh) {
|
||||||
|
index_doc = generate_index();
|
||||||
|
last_doc_generated = now;
|
||||||
|
}
|
||||||
|
sleep(1);
|
||||||
}
|
}
|
||||||
printf("\nShutting down services...\n");
|
printf("\nShutting down services...\n");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
MHD_stop_daemon(daemon);
|
MHD_stop_daemon(daemon);
|
||||||
|
printf("Waiting for services to shut down...");
|
||||||
|
fflush(stdout);
|
||||||
|
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);
|
||||||
|
deinit_service(&service);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
157
src/main.h
157
src/main.h
@@ -19,38 +19,39 @@
|
|||||||
#define PAGE_ASSET 1
|
#define PAGE_ASSET 1
|
||||||
#define PAGE_ERROR 2
|
#define PAGE_ERROR 2
|
||||||
|
|
||||||
mxml_node_t* doc;
|
mxml_node_t* index_doc;
|
||||||
|
|
||||||
uint8_t running = 0;
|
uint8_t running = 0;
|
||||||
char* daemon_name = "Stethoscope";
|
char* daemon_name = "Stethoscope";
|
||||||
uint16_t listen_port = 7800;
|
uint16_t listen_port = 7800;
|
||||||
char* webroot = "share/";
|
char* webroot = "share/";
|
||||||
|
char* daemon_banner = "";
|
||||||
uint32_t global_backoff = 5;
|
uint32_t global_backoff = 5;
|
||||||
|
uint32_t web_refresh = 30;
|
||||||
char* cached_index;
|
uint32_t status_refresh = 30;
|
||||||
|
|
||||||
int get_asset(char** buffer, const char* path, size_t* size) {
|
int get_asset(char** buffer, const char* path, size_t* size) {
|
||||||
*size = 0;
|
*size = 0;
|
||||||
if (strlen(webroot) + strlen(path) + 2 > PATH_MAX)
|
if (strlen(webroot) + strlen(path) + 2 > PATH_MAX)
|
||||||
return 403;
|
return 404;
|
||||||
|
|
||||||
char full_path[PATH_MAX];
|
char full_path[PATH_MAX];
|
||||||
snprintf(full_path, sizeof(full_path), "%s%s", webroot, path);
|
snprintf(full_path, sizeof(full_path), "%s%s", webroot, path);
|
||||||
|
|
||||||
char resolved_path[PATH_MAX];
|
char resolved_path[PATH_MAX];
|
||||||
if (realpath(full_path, resolved_path) == NULL)
|
if (realpath(full_path, resolved_path) == NULL)
|
||||||
return 403;
|
return 404;
|
||||||
|
|
||||||
char resolved_base[PATH_MAX];
|
char resolved_base[PATH_MAX];
|
||||||
if (realpath(webroot, resolved_base) == NULL)
|
if (realpath(webroot, resolved_base) == NULL)
|
||||||
return 403;
|
return 404;
|
||||||
|
|
||||||
size_t resolved_base_len = strlen(resolved_base);
|
size_t resolved_base_len = strlen(resolved_base);
|
||||||
if (strncmp(resolved_path, resolved_base, resolved_base_len) != 0)
|
if (strncmp(resolved_path, resolved_base, resolved_base_len) != 0)
|
||||||
return 400;
|
return 404;
|
||||||
|
|
||||||
if (resolved_path[resolved_base_len] != '\0' && resolved_path[resolved_base_len] != '/')
|
if (resolved_path[resolved_base_len] != '\0' && resolved_path[resolved_base_len] != '/')
|
||||||
return 400;
|
return 404;
|
||||||
|
|
||||||
FILE* asset = fopen(resolved_path, "rb");
|
FILE* asset = fopen(resolved_path, "rb");
|
||||||
if (!asset) return 404;
|
if (!asset) return 404;
|
||||||
@@ -114,6 +115,51 @@ static enum MHD_Result http_response(void *cls, struct MHD_Connection *conn, con
|
|||||||
} else response_code = 400;
|
} else response_code = 400;
|
||||||
|
|
||||||
if (response_code != 200) {
|
if (response_code != 200) {
|
||||||
|
mxml_node_t* doc = mxmlNewXML("1.0");
|
||||||
|
if (strcmp(uri, "/") == 0 && response_code == 0) {
|
||||||
|
doc = index_doc;
|
||||||
|
response_code = 200;
|
||||||
|
} else {
|
||||||
|
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* body = mxmlNewElement(html, "body");
|
||||||
|
|
||||||
|
mxml_node_t* center = mxmlNewElement(body, "center");
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
mxml_node_t* generate_index() {
|
||||||
mxml_node_t* doc = mxmlNewXML("1.0");
|
mxml_node_t* doc = mxmlNewXML("1.0");
|
||||||
mxml_node_t* html = mxmlNewElement(doc, "html");
|
mxml_node_t* html = mxmlNewElement(doc, "html");
|
||||||
mxmlElementSetAttr(html, "xmlns", "http://www.w3.org/1999/xhtml");
|
mxmlElementSetAttr(html, "xmlns", "http://www.w3.org/1999/xhtml");
|
||||||
@@ -140,7 +186,11 @@ static enum MHD_Result http_response(void *cls, struct MHD_Connection *conn, con
|
|||||||
|
|
||||||
mxml_node_t* center = mxmlNewElement(body, "center");
|
mxml_node_t* center = mxmlNewElement(body, "center");
|
||||||
|
|
||||||
if (strcmp(uri, "/") == 0 && response_code == 0) {
|
if (daemon_banner != 0 && strlen(daemon_banner) > 0) {
|
||||||
|
mxml_node_t* banner = mxmlNewElement(center, "img");
|
||||||
|
mxmlElementSetAttr(banner, "src", daemon_banner);
|
||||||
|
}
|
||||||
|
|
||||||
mxml_node_t* header = mxmlNewElement(center, "h1");
|
mxml_node_t* header = mxmlNewElement(center, "h1");
|
||||||
mxmlNewText(header, 0, daemon_name);
|
mxmlNewText(header, 0, daemon_name);
|
||||||
|
|
||||||
@@ -152,21 +202,6 @@ static enum MHD_Result http_response(void *cls, struct MHD_Connection *conn, con
|
|||||||
|
|
||||||
mxml_node_t* colgroup = mxmlNewElement(table, "colgroup");
|
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;
|
int col_count = 1;
|
||||||
long now = microtime();
|
long now = microtime();
|
||||||
long earliest_time = 0;
|
long earliest_time = 0;
|
||||||
@@ -181,6 +216,43 @@ static enum MHD_Result http_response(void *cls, struct MHD_Connection *conn, con
|
|||||||
} else col_count++;
|
} else col_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
mxmlElementSetAttr(header_row, "class", "title");
|
||||||
|
mxml_node_t* header_cell = mxmlNewElement(header_row, "td");
|
||||||
|
mxmlElementSetAttr(header_cell, "align", "left");
|
||||||
|
char* span_str = (char*)malloc(50);
|
||||||
|
snprintf(span_str, 50, "%d", col_count);
|
||||||
|
mxmlElementSetAttr(header_cell, "colspan", span_str);
|
||||||
|
|
||||||
|
if (service->text != 0 && strlen(service->text) > 0) {
|
||||||
|
mxml_node_t* details = mxmlNewElement(header_cell, "details");
|
||||||
|
mxmlElementSetAttr(details, "open", "1");
|
||||||
|
mxml_node_t* summary = mxmlNewElement(details, "summary");
|
||||||
|
if (service->icon != 0 && strlen(service->icon) > 0) {
|
||||||
|
mxml_node_t* icon = mxmlNewElement(summary, "img");
|
||||||
|
mxmlElementSetAttr(icon, "src", service->icon);
|
||||||
|
}
|
||||||
|
mxml_node_t* title_bold = mxmlNewElement(summary, "b");
|
||||||
|
mxmlNewText(title_bold, service->icon != 0, service->name);
|
||||||
|
mxmlNewText(details, 0, service->text);
|
||||||
|
} else {
|
||||||
|
if (service->icon != 0 && strlen(service->icon) > 0) {
|
||||||
|
mxml_node_t* icon = mxmlNewElement(header_cell, "img");
|
||||||
|
mxmlElementSetAttr(icon, "src", service->icon);
|
||||||
|
}
|
||||||
|
mxml_node_t* title_bold = mxmlNewElement(header_cell, "b");
|
||||||
|
mxmlNewText(title_bold, service->icon != 0, service->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
mxml_node_t* status_row = mxmlNewElement(table, "tr");
|
||||||
|
table_rows[row_index] = (struct XMLServiceRow){ service, status_row };
|
||||||
|
row_index++;
|
||||||
|
}
|
||||||
|
|
||||||
long last_time = earliest_time;
|
long last_time = earliest_time;
|
||||||
long total_time = now - earliest_time;
|
long total_time = now - earliest_time;
|
||||||
Iterator peroid_it = vector_begin(&status_timeline);
|
Iterator peroid_it = vector_begin(&status_timeline);
|
||||||
@@ -214,32 +286,19 @@ static enum MHD_Result http_response(void *cls, struct MHD_Connection *conn, con
|
|||||||
if (row_struct->service != 0) {
|
if (row_struct->service != 0) {
|
||||||
mxml_node_t* cell = mxmlNewElement(row_struct->row, "td");
|
mxml_node_t* cell = mxmlNewElement(row_struct->row, "td");
|
||||||
mxmlElementSetAttr(cell, "_status", state_text(srvstat->state));
|
mxmlElementSetAttr(cell, "_status", state_text(srvstat->state));
|
||||||
char* ping_str = (char*)malloc(50);
|
mxmlElementSetAttrf(cell, "_ping", "%.02f ms", srvstat->ping);
|
||||||
snprintf(ping_str, 50, "%f ms", srvstat->ping);
|
|
||||||
mxmlElementSetAttr(cell, "_ping", ping_str);
|
long peroid_time_s = peroid->time / 1000;
|
||||||
|
char* time_str = (char*)malloc(20);
|
||||||
|
strftime(time_str, 20, "%a %b %d %I:%M %P", localtime(&peroid_time_s));
|
||||||
|
mxmlElementSetAttr(cell, "_time", time_str);
|
||||||
|
|
||||||
|
mxmlElementSetAttrf(cell, "_toptext", "%s - %.02f ms", state_text(srvstat->state), srvstat->ping);
|
||||||
|
mxmlElementSetAttr(cell, "_bottomtext", time_str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mxmlNewElement(colgroup, "col");
|
||||||
response_code = 200;
|
return doc;
|
||||||
} 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
|
#endif
|
||||||
Reference in New Issue
Block a user