From a7f8ebea09646d7f7896a4dd9d5ab0aa64cea7f8 Mon Sep 17 00:00:00 2001 From: kamkow1 Date: Sun, 22 Jun 2025 14:57:24 +0200 Subject: [PATCH] Static assets finally --- baked.c | 4 +- baked.h | 2 +- build.c | 2 +- main.c | 200 +++++++++++++++++++++++++++++++++++++++++++----------- routes.c | 31 +++++---- routes.h | 4 +- watcher.c | 2 +- 7 files changed, 185 insertions(+), 60 deletions(-) diff --git a/baked.c b/baked.c index d13b42c..e6e0679 100644 --- a/baked.c +++ b/baked.c @@ -73,10 +73,10 @@ bool get_baked_resource_path(char *key, char *buf, size_t size) return false; } -void baked_resource_each(void (*f)(Baked_Resource *resource)) +void baked_resource_each(void (*f)(Baked_Resource *resource, void *udata), void *udata) { for (size_t i = 0; i < shlen(baked_resources); i++) { - f(&baked_resources[i]); + f(&baked_resources[i], udata); } } diff --git a/baked.h b/baked.h index b666522..e33870d 100644 --- a/baked.h +++ b/baked.h @@ -29,6 +29,6 @@ typedef struct { void init_baked_resources(void); void free_baked_resources(void); bool get_baked_resource_path(char *key, char *buf, size_t size); -void baked_resource_each(void (*f)(Baked_Resource *resource)); +void baked_resource_each(void (*f)(Baked_Resource *resource, void *udata), void *udata); #endif // BAKED_H_ diff --git a/build.c b/build.c index 71a180a..2bd7dfc 100644 --- a/build.c +++ b/build.c @@ -88,7 +88,7 @@ int main(int argc, char ** argv) } #if MY_DEBUG - CMD("cc", "-fPIC", "-ggdb", "-I.", "-DMY_DEBUG=1", "-D_GNU_SOURCE", "-DGEBS_NO_PREFIX", + CMD("cc", "-fsanitize=address", "-fPIC", "-ggdb", "-I.", "-DMY_DEBUG=1", "-D_GNU_SOURCE", "-DGEBS_NO_PREFIX", "-DINCBIN_PREFIX=", "-DINCBIN_STYLE=INCBIN_STYLE_SNAKE", "-Wl,-z,execstack", "-o", "./aboba", "./main.c", "./routes.c", "./baked.c", "./timer.c", "./mongoose.o", "./cJSON/cJSON.c", "./md5-c/md5.c", diff --git a/main.c b/main.c index c439db3..898908a 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,8 @@ #include #include +#include +#include +#include #define GEBS_IMPLEMENTATION #include "gebs/gebs.h" @@ -11,8 +14,14 @@ #include "baked.h" #include "timer.h" #include "CONFIG.h" +#include "clonestr.h" -Route *route_hashtable = NULL; +Route *route_hashtable = nil; +/* char *etc_dump_path = nil; */ +struct { char *value; pthread_mutex_t lock; } etc_dump_path = { + .value = nil, + .lock = PTHREAD_MUTEX_INITIALIZER, +}; void run_in_thread(void *(*f)(void *), void *p) { @@ -33,7 +42,38 @@ void *route_thread_function(void *param) if (r <= 0) { Route_Result result = {0}; result.status_code = 400; + result.type = ROUTE_RESULT_DYNAMIC; + /* list_append(&result.headers, clonestr("Content-Type: text/plain")); */ + sb_append_nstr(&result.body, "Could not parse HTTP request"); + sb_finish(&result.body); mg_wakeup(data->mgr, data->conn_id, &result, sizeof(result)); + free(data->message.buf); + free(data); + return nil; + } + + if (mg_match(http_msg.uri, mg_str("/etc/*"), nil)) { + Route_Result result = {0}; + result.type = ROUTE_RESULT_STATIC; + + char *p = malloc(http_msg.uri.len+1); + strncpy(p, http_msg.uri.buf, http_msg.uri.len); + p[http_msg.uri.len] = 0; + + char *file = basename(p); + char *full_path = malloc(PATH_MAX); + + pthread_mutex_lock(&etc_dump_path.lock); + snprintf(full_path, PATH_MAX, "%s/%s", etc_dump_path.value, file); + pthread_mutex_unlock(&etc_dump_path.lock); + + sb_append_nstr(&result.body, full_path); + sb_finish(&result.body); + free(full_path); + free(p); + mg_wakeup(data->mgr, data->conn_id, &result, sizeof(result)); + free(data->message.buf); + free(data); return nil; } @@ -80,69 +120,146 @@ void event_handler(struct mg_connection *conn, int ev, void *ev_data) } sb_finish(&sb); - if (result->type == ROUTE_RESULT_TEXT) { + if (result->type == ROUTE_RESULT_DYNAMIC) { mg_http_reply(conn, result->status_code, sb.items, "%s", result->body.items); - } else if (result->type == ROUTE_RESULT_BINARY) { - char *reply = fmt( - "HTTP/1.1 %d OK\r\n" - "%s" - "Content-Length: %zu\r\n" - "\r\n", - result->status_code, - sb.items, - result->body.count - ); - printf("%s\n", reply); - mg_printf(conn, "%s", reply); - mg_send(conn, result->body.items, result->body.count); + } else if (result->type == ROUTE_RESULT_STATIC) { + char *path = result->body.items; + mg_http_serve_file(conn, ev_data, path, &(struct mg_http_serve_opts){0}); } } } + +void route_hashtable_put_blogs(Baked_Resource *resource, void *udata) +{ + if ((strlen(resource->key) >= strlen("blog-")) + && strncmp(resource->key, "blog-", strlen("blog-")) == 0) { + char path[MG_PATH_MAX]; + snprintf(path, MG_PATH_MAX, "/%s", resource->key); + shput(route_hashtable, path, &route_generic_blog); + route_hashtable[shgeti(route_hashtable, path)].context_data = (void *)resource; + } +} + void init_route_hashtable(void) { shdefault(route_hashtable, &route_page_not_found); shput(route_hashtable, "/", &route_home); shput(route_hashtable, "/page-missing", &route_page_not_found); - shput(route_hashtable, "/simple.css", &route_simple_css); - shput(route_hashtable, "/favicon.ico", &route_favicon); + shput(route_hashtable, "/blog", &route_blog); #if MY_DEBUG - shput(route_hashtable, "/hotreload.js", &route_hotreload_js); shput(route_hashtable, "/build-id", &route_build_id); #endif - shput(route_hashtable, "/me.jpg", &route_me_jpg); - shput(route_hashtable, "/blog", &route_blog); - void put_blogs(Baked_Resource *resource) - { - if ((strlen(resource->key) >= strlen("blog-")) - && strncmp(resource->key, "blog-", strlen("blog-")) == 0) { - char *path = malloc(MG_PATH_MAX); - snprintf(path, MG_PATH_MAX, "/%s", resource->key); - shput(route_hashtable, path, &route_generic_blog); - route_hashtable[shgeti(route_hashtable, path)].context_data = (void *)resource; - } - } - - baked_resource_each(&put_blogs); + baked_resource_each(&route_hashtable_put_blogs, nil); } void free_route_hashtable(void) { - for (size_t i = 0; i < shlen(route_hashtable); i++) { - if (strlen(route_hashtable[i].key) >= strlen("blog-") - && strncmp(route_hashtable[i].key, "blog-", strlen("blog-")) == 0) { - free(route_hashtable[i].key); - } - } shfree(route_hashtable); } +char *init_etc_dump(void) +{ + char template[] = "/tmp/aboba-etc.XXXXXX"; + char *etc_dump1 = mkdtemp(template); + char *etc_dump = malloc(strlen(etc_dump1)+1); + strcpy(etc_dump, etc_dump1); + + if (etc_dump == nil) { + LOGE("Could not create etc dump\n"); + return nil; + } + LOGI("etc dump dir is %s\n", etc_dump); + + return etc_dump; +} + +int etc_dump_rm_cb(const char *path, + discard const struct stat *st, + discard int type, + discard struct FTW *ftw) +{ + return remove(path); +} + +bool free_etc_dump(char *etc_dump) +{ + LOGI("Removing etc dump %s\n", etc_dump); + bool ok = nftw(etc_dump, etc_dump_rm_cb, FOPEN_MAX, FTW_DEPTH | FTW_MOUNT | FTW_PHYS) != -1; + if (!ok) { + LOGE("Could not remove %s\n", etc_dump); + } + free(etc_dump); + return ok; +} + +// https://stackoverflow.com/questions/2180079/how-can-i-copy-a-file-on-unix-using-c +int cp(const char* source, const char* destination) +{ + int result = 0; + int input, output; + if ((input = open(source, O_RDONLY)) == -1) + { + return -1; + } + if ((output = creat(destination, 0660)) == -1) + { + close(input); + return -1; + } + + // sendfile will work with non-socket output (i.e. regular file) under + // Linux 2.6.33+ and some other unixy systems. + struct stat file_stat = {0}; + result = fstat(input, &file_stat); + off_t copied = 0; + while (result == 0 && copied < file_stat.st_size) { + ssize_t written = sendfile(output, input, &copied, SSIZE_MAX); + copied += written; + if (written == -1) { + result = -1; + } + } + + close(input); + close(output); + + return result; +} + +void populate_etc_dump(char *etc_dump) +{ + static char *files[] = { + "favicon.ico", "hotreload.js", + "simple.css", "me.jpg", + }; + + for (size_t i = 0; i < sizeof(files)/sizeof(files[0]); i++) { + char path[PATH_MAX]; + get_baked_resource_path(files[i], path, sizeof(path)); + char dest[PATH_MAX]; + snprintf(dest, sizeof(dest), "%s/%s", etc_dump, files[i]); + cp(path, dest); + } +} + +volatile bool alive = true; + +void graceful_shutdown(int no) { alive = false; } + int main(int argc, char ** argv) { + signal(SIGINT, &graceful_shutdown); start_timer(); init_baked_resources(); + pthread_mutex_lock(&etc_dump_path.lock); + if ((etc_dump_path.value = init_etc_dump()) == nil) { + return 1; + } + populate_etc_dump(etc_dump_path.value); + pthread_mutex_unlock(&etc_dump_path.lock); mg_log_set(MG_LL_DEBUG); struct mg_mgr mgr; @@ -153,13 +270,16 @@ int main(int argc, char ** argv) mg_wakeup_init(&mgr); mg_http_listen(&mgr, CONFIG_LISTEN_URL, &event_handler, NULL); - for (;;) { + while (alive) { mg_mgr_poll(&mgr, 1000); scratch_arena_reset(); } mg_mgr_free(&mgr); - + + pthread_mutex_lock(&etc_dump_path.lock); + free_etc_dump(etc_dump_path.value); + pthread_mutex_unlock(&etc_dump_path.lock); free_baked_resources(); return 0; diff --git a/routes.c b/routes.c index 4ce1aff..21e70b0 100644 --- a/routes.c +++ b/routes.c @@ -16,7 +16,7 @@ void make_internal_server_error(Route_Result *result) { result->status_code = 500; - result->type = ROUTE_RESULT_TEXT; + result->type = ROUTE_RESULT_DYNAMIC; list_append(&result->headers, "Content-Type: text/plain"); sb_append_nstr(&result->body, INTERNAL_SERVER_ERROR_MSG); sb_finish(&result->body); @@ -28,7 +28,7 @@ void make_application_json(Route_Result *result, int code, cJSON *root) defer { free(root_text); } result->status_code = code; - result->type = ROUTE_RESULT_TEXT; + result->type = ROUTE_RESULT_DYNAMIC; list_append(&result->headers, clonestr("Content-Type: application/json")); sb_append_nstr(&result->body, root_text); sb_finish(&result->body); @@ -39,7 +39,7 @@ void make_binary(Route_Result *result, char *content_type, int code, char *data, char type[100]; snprintf(type, sizeof(type), "Content-Type: %s", content_type); result->status_code = code; - result->type = ROUTE_RESULT_BINARY; + result->type = ROUTE_RESULT_DYNAMIC; list_append(&result->headers, clonestr(type)); for (size_t i = 0; i < size; i++) { sb_append_char(&result->body, data[i]); @@ -53,7 +53,7 @@ void make_text(Route_Result *result, char *subtype, int code, char *in) snprintf(type, sizeof(type), "Content-Type: text/%s", subtype); result->status_code = code; - result->type = ROUTE_RESULT_TEXT; + result->type = ROUTE_RESULT_DYNAMIC; list_append(&result->headers, clonestr(type)); sb_append_nstr(&result->body, in); sb_finish(&result->body); @@ -323,6 +323,15 @@ ROUTE_HANDLER(generic_blog) } } +void collect_blogs(Baked_Resource *resource, void *udata) +{ + struct { Arena *tmp; String_Builder *sb; } *ud = udata; + if ((strlen(resource->key) >= strlen("blog-")) + && strncmp(resource->key, "blog-", strlen("blog-")) == 0) { + sb_append_nstr_alloc(ud->tmp, ud->sb, fmt("
  • %s
  • ", resource->key, resource->key)); + } +} + ROUTE_HANDLER(blog) { NString_List env = {0}; @@ -335,15 +344,11 @@ ROUTE_HANDLER(blog) defer { arena_destroy(&tmp); } String_Builder sb = {0}; - void collect_blogs(Baked_Resource *resource) - { - if ((strlen(resource->key) >= strlen("blog-")) - && strncmp(resource->key, "blog-", strlen("blog-")) == 0) { - sb_append_nstr_alloc(&tmp, &sb, fmt("
  • %s
  • ", resource->key, resource->key)); - } - } - - baked_resource_each(&collect_blogs); + struct { Arena *tmp; String_Builder *sb; } collect_blogs_data = { + .tmp = &tmp, + .sb = &sb, + }; + baked_resource_each(&collect_blogs, &collect_blogs_data); sb_finish_alloc(&tmp, &sb); list_append(&env, fmt("-DBLOG_POSTS=%s", sb.items)); diff --git a/routes.h b/routes.h index 3a5f7d5..6a1dd50 100644 --- a/routes.h +++ b/routes.h @@ -6,8 +6,8 @@ typedef struct { enum { - ROUTE_RESULT_TEXT, - ROUTE_RESULT_BINARY, + ROUTE_RESULT_DYNAMIC, + ROUTE_RESULT_STATIC, } type; int status_code; NString_List headers; diff --git a/watcher.c b/watcher.c index c0230b6..0562711 100644 --- a/watcher.c +++ b/watcher.c @@ -183,7 +183,7 @@ int main(int argc, char ** argv) if (cmd_pid != -1) { LOGI("Killing %d\n", cmd_pid); - kill(cmd_pid, SIGKILL); + kill(cmd_pid, SIGINT); } cmd_pid = cmd_run_async(&cmd);