aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfrosty <gabriel@bwaaa.monster>2026-08-11 03:20:47 -0400
committerfrosty <gabriel@bwaaa.monster>2026-08-11 03:20:47 -0400
commit59b76fd601e5840043b9868ae2ca2f06b4b4e7a4 (patch)
treee26f511b2cab0f3f3fc363c9ae435e0fd82c6695
parentb6951c18912c82e7863ebd6c6c77b891ad9aea0c (diff)
downloadbeaker-59b76fd601e5840043b9868ae2ca2f06b4b4e7a4.tar.gz
feat: complete logging overhaul
-rw-r--r--README.md1
-rw-r--r--src/beaker_globals.c8
-rw-r--r--src/beaker_globals.h17
-rw-r--r--src/http.c118
-rw-r--r--src/l10n.c93
-rw-r--r--src/log.c117
-rw-r--r--src/routing.c373
-rw-r--r--src/server.c505
-rw-r--r--src/template.c413
9 files changed, 935 insertions, 710 deletions
diff --git a/README.md b/README.md
index c9929b1..eee5c60 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,6 @@ $ make install INSTALL_PREFIX=/some/prefix/
## Roadmap
- Write documentation
-- Add more robust logging
- Write more examples
## Contribution
diff --git a/src/beaker_globals.c b/src/beaker_globals.c
index 3488338..2b1f056 100644
--- a/src/beaker_globals.c
+++ b/src/beaker_globals.c
@@ -1,4 +1,4 @@
-#include "beaker_globals.h"
+#include "beaker_globals.h"
RouteHandler handlers[MAX_HANDLERS];
@@ -14,8 +14,12 @@ __thread char current_request_buffer[BUFFER_SIZE];
__thread RequestInfo current_request_info = {0};
+__thread int current_response_status = 0;
+
+__thread size_t current_response_size = 0;
+
Locale *locales = NULL;
int locale_count = 0;
-int locale_capacity = 0; \ No newline at end of file
+int locale_capacity = 0;
diff --git a/src/beaker_globals.h b/src/beaker_globals.h
index 34b29d9..a4a98f0 100644
--- a/src/beaker_globals.h
+++ b/src/beaker_globals.h
@@ -3,6 +3,7 @@
#include "../beaker.h"
#include <stdatomic.h>
+#include <stddef.h>
extern RouteHandler handlers[MAX_HANDLERS];
@@ -18,10 +19,24 @@ extern __thread char current_request_buffer[BUFFER_SIZE];
extern __thread RequestInfo current_request_info;
+extern __thread int current_response_status;
+
+extern __thread size_t current_response_size;
+
+void beaker_log(const char *level, const char *format, ...);
+
+void beaker_log_errno_format(const char *level, const char *format, ...);
+
+void beaker_log_errno(const char *message);
+
+void beaker_log_request(const char *remote_addr, const char *method,
+ const char *path, int status, size_t response_size,
+ double duration_ms);
+
extern Locale *locales;
extern int locale_count;
extern int locale_capacity;
-#endif \ No newline at end of file
+#endif
diff --git a/src/http.c b/src/http.c
index 778bf51..4c93a27 100644
--- a/src/http.c
+++ b/src/http.c
@@ -1,10 +1,10 @@
-#include "../beaker.h"
-#include "beaker_globals.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <unistd.h>
+#include "../beaker.h"
+#include "beaker_globals.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
static void build_cookie_headers(char *cookie_headers_buffer,
size_t buffer_size) {
@@ -12,37 +12,47 @@ static void build_cookie_headers(char *cookie_headers_buffer,
cookie_headers_buffer[0] = '\0';
for (int i = 0; i < cookies_to_set_count; i++) {
- char single_cookie_header[MAX_VALUE_LEN * 2];
+ char single_cookie_header[MAX_VALUE_LEN * 2];
snprintf(single_cookie_header, sizeof(single_cookie_header),
"Set-Cookie: %s=%s", cookies_to_set[i].name,
cookies_to_set[i].value);
if (strlen(cookies_to_set[i].expires) > 0) {
- strncat(single_cookie_header, "; Expires=", sizeof(single_cookie_header) - strlen(single_cookie_header) - 1);
- strncat(single_cookie_header, cookies_to_set[i].expires, sizeof(single_cookie_header) - strlen(single_cookie_header) - 1);
+ strncat(single_cookie_header, "; Expires=",
+ sizeof(single_cookie_header) - strlen(single_cookie_header) - 1);
+ strncat(single_cookie_header, cookies_to_set[i].expires,
+ sizeof(single_cookie_header) - strlen(single_cookie_header) - 1);
}
if (strlen(cookies_to_set[i].path) > 0) {
- strncat(single_cookie_header, "; Path=", sizeof(single_cookie_header) - strlen(single_cookie_header) - 1);
- strncat(single_cookie_header, cookies_to_set[i].path, sizeof(single_cookie_header) - strlen(single_cookie_header) - 1);
+ strncat(single_cookie_header, "; Path=",
+ sizeof(single_cookie_header) - strlen(single_cookie_header) - 1);
+ strncat(single_cookie_header, cookies_to_set[i].path,
+ sizeof(single_cookie_header) - strlen(single_cookie_header) - 1);
}
if (cookies_to_set[i].http_only) {
- strncat(single_cookie_header, "; HttpOnly", sizeof(single_cookie_header) - strlen(single_cookie_header) - 1);
+ strncat(single_cookie_header, "; HttpOnly",
+ sizeof(single_cookie_header) - strlen(single_cookie_header) - 1);
}
if (cookies_to_set[i].secure) {
- strncat(single_cookie_header, "; Secure", sizeof(single_cookie_header) - strlen(single_cookie_header) - 1);
+ strncat(single_cookie_header, "; Secure",
+ sizeof(single_cookie_header) - strlen(single_cookie_header) - 1);
}
- strncat(single_cookie_header, "\r\n", sizeof(single_cookie_header) - strlen(single_cookie_header) - 1);
+ strncat(single_cookie_header, "\r\n",
+ sizeof(single_cookie_header) - strlen(single_cookie_header) - 1);
- if (strlen(cookie_headers_buffer) + strlen(single_cookie_header) < buffer_size) {
+ if (strlen(cookie_headers_buffer) + strlen(single_cookie_header) <
+ buffer_size) {
strncat(cookie_headers_buffer, single_cookie_header,
buffer_size - strlen(cookie_headers_buffer) - 1);
} else {
- fprintf(stderr, "[WARNING] build_cookie_headers: Cookie headers buffer full, truncating\n");
+ beaker_log(
+ "WARN",
+ "build_cookie_headers: Cookie headers buffer full, truncating\n");
break;
}
}
@@ -50,11 +60,14 @@ static void build_cookie_headers(char *cookie_headers_buffer,
void send_status(const char *status_line) {
if (current_client_socket == -1) {
- fprintf(stderr, "[ERROR] send_status: No client socket set. Cannot send response.\n");
+ beaker_log("ERROR",
+ "send_status: No client socket set. Cannot send response.\n");
return;
}
char http_response[BUFFER_SIZE];
+ current_response_status = atoi(status_line);
+ current_response_size = 0;
snprintf(http_response, sizeof(http_response),
"HTTP/1.1 %s\r\n"
"Content-Length: 0\r\n"
@@ -62,22 +75,26 @@ void send_status(const char *status_line) {
"\r\n",
status_line);
- if (send(current_client_socket, http_response, strlen(http_response), 0) < 0) {
- perror("Error sending HTTP status");
- fprintf(stderr, "[ERROR] send_status: Failed to send HTTP status.\n");
+ if (send(current_client_socket, http_response, strlen(http_response), 0) <
+ 0) {
+ beaker_log_errno_format("ERROR",
+ "send_status: Failed to send HTTP status.\n");
}
}
void send_response(const char *html) {
if (current_client_socket == -1) {
- fprintf(stderr, "[ERROR] send_response: No client socket set. Cannot send response.\n");
+ beaker_log("ERROR",
+ "send_response: No client socket set. Cannot send response.\n");
return;
}
- char http_response_header[BUFFER_SIZE * 2];
- int content_length = strlen(html);
- char cookie_headers[BUFFER_SIZE];
+ char http_response_header[BUFFER_SIZE * 2];
+ int content_length = strlen(html);
+ current_response_status = 200;
+ current_response_size = content_length;
+ char cookie_headers[BUFFER_SIZE];
build_cookie_headers(cookie_headers, sizeof(cookie_headers));
@@ -85,21 +102,21 @@ void send_response(const char *html) {
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=UTF-8\r\n"
"Content-Length: %d\r\n"
- "%s"
- "Connection: close\r\n"
- "\r\n",
+ "%s"
+ "Connection: close\r\n"
+ "\r\n",
content_length, cookie_headers);
if (send(current_client_socket, http_response_header,
strlen(http_response_header), 0) < 0) {
- perror("Error sending HTTP header");
- fprintf(stderr, "[ERROR] send_response: Failed to send HTTP header.\n");
+ beaker_log_errno_format("ERROR",
+ "send_response: Failed to send HTTP header.\n");
return;
}
if (send(current_client_socket, html, content_length, 0) < 0) {
- perror("Error sending HTML body");
- fprintf(stderr, "[ERROR] send_response: Failed to send HTML body.\n");
+ beaker_log_errno_format("ERROR",
+ "send_response: Failed to send HTML body.\n");
return;
}
@@ -109,11 +126,14 @@ void send_response(const char *html) {
void send_redirect(const char *location) {
if (current_client_socket == -1) {
- fprintf(stderr, "[ERROR] send_redirect: No client socket set. Cannot send redirect.\n");
+ beaker_log("ERROR",
+ "send_redirect: No client socket set. Cannot send redirect.\n");
return;
}
char http_response_header[BUFFER_SIZE * 2];
+ current_response_status = 302;
+ current_response_size = 0;
char cookie_headers[BUFFER_SIZE];
build_cookie_headers(cookie_headers, sizeof(cookie_headers));
@@ -128,8 +148,8 @@ void send_redirect(const char *location) {
if (send(current_client_socket, http_response_header,
strlen(http_response_header), 0) < 0) {
- perror("Error sending redirect header");
- fprintf(stderr, "[ERROR] send_redirect: Failed to send redirect header.\n");
+ beaker_log_errno_format("ERROR",
+ "send_redirect: Failed to send redirect header.\n");
return;
}
@@ -140,9 +160,10 @@ void set_cookie(const char *name, const char *value, const char *expires,
const char *path, bool http_only, bool secure) {
if (cookies_to_set_count >= MAX_COOKIES) {
- fprintf(stderr,
- "[WARNING] set_cookie: Maximum number of cookies to set reached. Cannot set cookie '%s'.\n",
- name);
+ beaker_log("WARN",
+ "set_cookie: Maximum number of cookies to set reached. Cannot "
+ "set cookie '%s'.\n",
+ name);
return;
}
@@ -178,7 +199,7 @@ char *get_cookie(const char *cookie_name) {
char *cookie_header_start = strstr(current_request_buffer, "\r\nCookie: ");
if (cookie_header_start == NULL) {
- return NULL;
+ return NULL;
}
cookie_header_start += strlen("\r\nCookie: ");
@@ -186,22 +207,23 @@ char *get_cookie(const char *cookie_name) {
char *cookie_header_end = strstr(cookie_header_start, "\r\n");
if (cookie_header_end == NULL) {
- cookie_header_end = (char *)(current_request_buffer + strlen(current_request_buffer));
+ cookie_header_end =
+ (char *)(current_request_buffer + strlen(current_request_buffer));
}
size_t cookie_str_len = cookie_header_end - cookie_header_start;
char *cookie_str = (char *)malloc(cookie_str_len + 1);
if (cookie_str == NULL) {
- perror("Failed to allocate memory for raw cookie string");
- fprintf(stderr, "[ERROR] get_cookie: Allocation failed for cookie_str.\n");
+ beaker_log_errno_format("ERROR",
+ "get_cookie: Allocation failed for cookie_str.\n");
return NULL;
}
strncpy(cookie_str, cookie_header_start, cookie_str_len);
- cookie_str[cookie_str_len] = '\0';
+ cookie_str[cookie_str_len] = '\0';
- char *token;
- char *saveptr_cookie;
+ char *token;
+ char *saveptr_cookie;
token = strtok_r(cookie_str, ";", &saveptr_cookie);
while (token != NULL) {
@@ -212,15 +234,15 @@ char *get_cookie(const char *cookie_name) {
char *equals_sign = strchr(token, '=');
if (equals_sign != NULL) {
- size_t name_len = equals_sign - token;
+ size_t name_len = equals_sign - token;
if (name_len == strlen(cookie_name) &&
strncmp(token, cookie_name, name_len) == 0) {
char *cookie_value = strdup(equals_sign + 1);
if (cookie_value == NULL) {
- perror("Failed to duplicate cookie value");
- fprintf(stderr, "[ERROR] get_cookie: Allocation failed for cookie_value.\n");
+ beaker_log_errno_format(
+ "ERROR", "get_cookie: Allocation failed for cookie_value.\n");
}
free(cookie_str);
return cookie_value;
diff --git a/src/l10n.c b/src/l10n.c
index 4d8ac30..43acff2 100644
--- a/src/l10n.c
+++ b/src/l10n.c
@@ -13,11 +13,15 @@ typedef enum {
} IniSection;
static char *trim(char *str) {
- if (str == NULL) return NULL;
- while (*str == ' ' || *str == '\t') str++;
- if (*str == '\0') return str;
+ if (str == NULL)
+ return NULL;
+ while (*str == ' ' || *str == '\t')
+ str++;
+ if (*str == '\0')
+ return str;
char *end = str + strlen(str) - 1;
- while (end > str && (*end == ' ' || *end == '\t' || *end == '\r' || *end == '\n')) {
+ while (end > str &&
+ (*end == ' ' || *end == '\t' || *end == '\r' || *end == '\n')) {
*end = '\0';
end--;
}
@@ -35,10 +39,13 @@ static void unquote_value(char *value) {
static int safe_grow_capacity(int current, int hard_max) {
int new_cap;
if (current == 0) {
- new_cap = (hard_max < INITIAL_LOCALE_KEYS_CAPACITY) ? hard_max : INITIAL_LOCALE_KEYS_CAPACITY;
+ new_cap = (hard_max < INITIAL_LOCALE_KEYS_CAPACITY)
+ ? hard_max
+ : INITIAL_LOCALE_KEYS_CAPACITY;
} else {
new_cap = current * 2;
- if (new_cap > hard_max) new_cap = hard_max;
+ if (new_cap > hard_max)
+ new_cap = hard_max;
}
return new_cap;
}
@@ -46,8 +53,8 @@ static int safe_grow_capacity(int current, int hard_max) {
static int parse_ini_file(const char *path, Locale *locale) {
FILE *fp = fopen(path, "r");
if (fp == NULL) {
- fprintf(stderr, "[ERROR] parse_ini_file: Could not open '%s': %s\n",
- path, strerror(errno));
+ beaker_log("ERROR", "parse_ini_file: Could not open '%s': %s\n", path,
+ strerror(errno));
return -1;
}
@@ -67,7 +74,8 @@ static int parse_ini_file(const char *path, Locale *locale) {
if (*trimmed == '[') {
char *close = strchr(trimmed, ']');
- if (close == NULL) continue;
+ if (close == NULL)
+ continue;
*close = '\0';
char *section_name = trimmed + 1;
section_name = trim(section_name);
@@ -83,7 +91,8 @@ static int parse_ini_file(const char *path, Locale *locale) {
}
char *eq = strchr(trimmed, '=');
- if (eq == NULL) continue;
+ if (eq == NULL)
+ continue;
*eq = '\0';
char *key = trim(trimmed);
@@ -98,24 +107,28 @@ static int parse_ini_file(const char *path, Locale *locale) {
strncpy(locale->meta.name, value, MAX_VALUE_LEN - 1);
locale->meta.name[MAX_VALUE_LEN - 1] = '\0';
} else if (strcmp(key, "Direction") == 0) {
- strncpy(locale->meta.direction, value, sizeof(locale->meta.direction) - 1);
+ strncpy(locale->meta.direction, value,
+ sizeof(locale->meta.direction) - 1);
locale->meta.direction[sizeof(locale->meta.direction) - 1] = '\0';
}
} else if (current_section == SECTION_KEYS) {
if (locale->key_count >= locale->key_capacity) {
- int new_cap = safe_grow_capacity(locale->key_capacity, MAX_LOCALE_KEYS_HARD);
+ int new_cap =
+ safe_grow_capacity(locale->key_capacity, MAX_LOCALE_KEYS_HARD);
if (new_cap <= locale->key_count) {
- fprintf(stderr,
- "[WARNING] parse_ini_file: Hard key limit (%d) reached in '%s', "
- "skipping key '%s'.\n",
- MAX_LOCALE_KEYS_HARD, path, key);
+ beaker_log("WARN",
+ "parse_ini_file: Hard key limit (%d) reached in '%s', "
+ "skipping key '%s'.\n",
+ MAX_LOCALE_KEYS_HARD, path, key);
continue;
}
- LocaleKV *new_keys = realloc(locale->keys, (size_t)new_cap * sizeof(LocaleKV));
+ LocaleKV *new_keys =
+ realloc(locale->keys, (size_t)new_cap * sizeof(LocaleKV));
if (new_keys == NULL) {
- fprintf(stderr,
- "[ERROR] parse_ini_file: Memory allocation failed for keys in '%s'.\n",
- path);
+ beaker_log(
+ "ERROR",
+ "parse_ini_file: Memory allocation failed for keys in '%s'.\n",
+ path);
continue;
}
locale->keys = new_keys;
@@ -133,9 +146,8 @@ static int parse_ini_file(const char *path, Locale *locale) {
fclose(fp);
if (locale->meta.id[0] == '\0') {
- fprintf(stderr,
- "[WARNING] parse_ini_file: No Id in [Meta] section of '%s'.\n",
- path);
+ beaker_log("WARN", "parse_ini_file: No Id in [Meta] section of '%s'.\n",
+ path);
return -1;
}
@@ -147,9 +159,9 @@ int beaker_load_locales(void) {
DIR *dir = opendir(LOCALES_DIR);
if (dir == NULL) {
- fprintf(stderr,
- "[ERROR] beaker_load_locales: Could not open directory '%s': %s\n",
- LOCALES_DIR, strerror(errno));
+ beaker_log("ERROR",
+ "beaker_load_locales: Could not open directory '%s': %s\n",
+ LOCALES_DIR, strerror(errno));
return 0;
}
@@ -165,16 +177,17 @@ int beaker_load_locales(void) {
if (locale_count >= locale_capacity) {
int new_cap = safe_grow_capacity(locale_capacity, MAX_LOCALES_HARD);
if (new_cap <= locale_count) {
- fprintf(stderr,
- "[WARNING] beaker_load_locales: Hard locale limit (%d) reached, "
- "skipping '%s'.\n",
- MAX_LOCALES_HARD, name);
+ beaker_log("WARN",
+ "beaker_load_locales: Hard locale limit (%d) reached, "
+ "skipping '%s'.\n",
+ MAX_LOCALES_HARD, name);
break;
}
Locale *new_locales = realloc(locales, (size_t)new_cap * sizeof(Locale));
if (new_locales == NULL) {
- fprintf(stderr,
- "[ERROR] beaker_load_locales: Memory allocation failed for locales.\n");
+ beaker_log(
+ "ERROR",
+ "beaker_load_locales: Memory allocation failed for locales.\n");
break;
}
locales = new_locales;
@@ -186,8 +199,8 @@ int beaker_load_locales(void) {
Locale *locale = &locales[locale_count];
if (parse_ini_file(path, locale) == 0) {
- fprintf(stderr, "[INFO] beaker_load_locales: Loaded locale '%s' from '%s'\n",
- locale->meta.id, name);
+ beaker_log("DEBUG", "beaker_load_locales: Loaded locale '%s' from '%s'",
+ locale->meta.id, name);
locale_count++;
} else {
free(locale->keys);
@@ -203,8 +216,8 @@ int beaker_load_locales(void) {
void beaker_set_locale(TemplateContext *ctx, const char *locale_id) {
if (ctx == NULL || locale_id == NULL) {
- fprintf(stderr,
- "[ERROR] beaker_set_locale: Invalid NULL input (ctx or locale_id).\n");
+ beaker_log("ERROR",
+ "beaker_set_locale: Invalid NULL input (ctx or locale_id).\n");
return;
}
@@ -216,10 +229,10 @@ void beaker_set_locale(TemplateContext *ctx, const char *locale_id) {
context_set(ctx, "__locale_name", meta->name);
context_set(ctx, "__locale_direction", meta->direction);
} else {
- fprintf(stderr,
- "[WARNING] beaker_set_locale: Locale '%s' not found. Context vars "
- "not set.\n",
- locale_id);
+ beaker_log("WARN",
+ "beaker_set_locale: Locale '%s' not found. Context vars "
+ "not set.\n",
+ locale_id);
}
}
diff --git a/src/log.c b/src/log.c
new file mode 100644
index 0000000..775bd2c
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,117 @@
+#include "beaker_globals.h"
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+
+enum {
+ LOG_LEVEL_DEBUG,
+ LOG_LEVEL_INFO,
+ LOG_LEVEL_WARN,
+ LOG_LEVEL_ERROR,
+ LOG_LEVEL_NONE
+};
+
+static int configured_log_level(void) {
+ const char *level = getenv("BEAKER_LOG_LEVEL");
+
+ if (level == NULL || strcasecmp(level, "INFO") == 0)
+ return LOG_LEVEL_INFO;
+ if (strcasecmp(level, "DEBUG") == 0)
+ return LOG_LEVEL_DEBUG;
+ if (strcasecmp(level, "WARN") == 0 || strcasecmp(level, "WARNING") == 0)
+ return LOG_LEVEL_WARN;
+ if (strcasecmp(level, "ERROR") == 0)
+ return LOG_LEVEL_ERROR;
+ if (strcasecmp(level, "NONE") == 0 || strcasecmp(level, "OFF") == 0)
+ return LOG_LEVEL_NONE;
+ return LOG_LEVEL_INFO;
+}
+
+static int message_log_level(const char *level) {
+ if (strcasecmp(level, "DEBUG") == 0)
+ return LOG_LEVEL_DEBUG;
+ if (strcasecmp(level, "INFO") == 0 || strcasecmp(level, "ACCESS") == 0)
+ return LOG_LEVEL_INFO;
+ if (strcasecmp(level, "WARN") == 0 || strcasecmp(level, "WARNING") == 0 ||
+ strcasecmp(level, "SECURITY") == 0)
+ return LOG_LEVEL_WARN;
+ return LOG_LEVEL_ERROR;
+}
+
+static void log_timestamp(char *buffer, size_t size) {
+ struct timespec now;
+ struct tm local;
+
+ clock_gettime(CLOCK_REALTIME, &now);
+ localtime_r(&now.tv_sec, &local);
+ strftime(buffer, size, "%Y-%m-%dT%H:%M:%S%z", &local);
+}
+
+static void log_safe_value(char *output, const char *input, size_t size) {
+ size_t i;
+
+ for (i = 0; i + 1 < size && input[i] != '\0'; i++) {
+ unsigned char c = input[i];
+ output[i] = c <= ' ' || c == 127 || c == '"' || c == '\\' ? '_' : c;
+ }
+ output[i] = '\0';
+}
+
+void beaker_log(const char *level, const char *format, ...) {
+ char message[BUFFER_SIZE];
+ char timestamp[32];
+ va_list args;
+
+ if (message_log_level(level) < configured_log_level())
+ return;
+ va_start(args, format);
+ vsnprintf(message, sizeof(message), format, args);
+ va_end(args);
+ size_t length = strlen(message);
+ while (length > 0 &&
+ (message[length - 1] == '\n' || message[length - 1] == '\r'))
+ message[--length] = '\0';
+ log_timestamp(timestamp, sizeof(timestamp));
+ flockfile(stderr);
+ fprintf(stderr, "%s %-6s %s\n", timestamp, level, message);
+ funlockfile(stderr);
+}
+
+void beaker_log_errno_format(const char *level, const char *format, ...) {
+ char message[BUFFER_SIZE];
+ int error = errno;
+ va_list args;
+
+ va_start(args, format);
+ vsnprintf(message, sizeof(message), format, args);
+ va_end(args);
+
+ size_t length = strlen(message);
+ while (length > 0 &&
+ (message[length - 1] == '\n' || message[length - 1] == '\r'))
+ message[--length] = '\0';
+ if (length > 0 && message[length - 1] == '.')
+ message[length - 1] = '\0';
+ beaker_log(level, "%s: %s", message, strerror(error));
+}
+
+void beaker_log_errno(const char *message) {
+ int error = errno;
+ beaker_log("ERROR", "%s: %s", message, strerror(error));
+}
+
+void beaker_log_request(const char *remote_addr, const char *method,
+ const char *path, int status, size_t response_size,
+ double duration_ms) {
+ char safe_method[16];
+ char safe_path[MAX_PATH_LEN];
+
+ log_safe_value(safe_method, method, sizeof(safe_method));
+ log_safe_value(safe_path, path, sizeof(safe_path));
+ beaker_log("ACCESS", "%s \"%s %s\" %03d %zu %.3fms", remote_addr, safe_method,
+ safe_path, status, response_size, duration_ms);
+}
diff --git a/src/routing.c b/src/routing.c
index 00886c8..9894768 100644
--- a/src/routing.c
+++ b/src/routing.c
@@ -1,12 +1,12 @@
-#include "../beaker.h"
-#include "beaker_globals.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <unistd.h>
+#include "../beaker.h"
+#include "beaker_globals.h"
#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <unistd.h>
void set_handler(const char *path, RequestHandler handler) {
@@ -20,9 +20,10 @@ void set_handler(const char *path, RequestHandler handler) {
handler_count++;
} else {
- fprintf(stderr,
- "[WARNING] set_handler: Maximum number of handlers reached. Cannot register handler for '%s'.\n",
- path);
+ beaker_log("WARN",
+ "set_handler: Maximum number of handlers reached. Cannot "
+ "register handler for '%s'.\n",
+ path);
}
}
@@ -30,9 +31,9 @@ const char *get_mime_type(const char *file_path) {
const char *ext = strrchr(file_path, '.');
if (!ext) {
- return "application/octet-stream";
+ return "application/octet-stream";
}
- ext++;
+ ext++;
if (strcmp(ext, "html") == 0 || strcmp(ext, "htm") == 0)
return "text/html";
@@ -60,211 +61,215 @@ const char *get_mime_type(const char *file_path) {
return "application/octet-stream";
}
-static int canonicalize_path(char *canonical, const char *path, size_t max_len) {
- char components[MAX_URL_PARAMS][MAX_KEY_LEN];
- int component_count = 0;
+static int canonicalize_path(char *canonical, const char *path,
+ size_t max_len) {
+ char components[MAX_URL_PARAMS][MAX_KEY_LEN];
+ int component_count = 0;
- char *path_copy = strdup(path);
- if (!path_copy) {
- return -1;
- }
+ char *path_copy = strdup(path);
+ if (!path_copy) {
+ return -1;
+ }
- char *token = strtok(path_copy, "/");
- while (token) {
- if (strcmp(token, ".") == 0) {
+ char *token = strtok(path_copy, "/");
+ while (token) {
+ if (strcmp(token, ".") == 0) {
- } else if (strcmp(token, "..") == 0) {
+ } else if (strcmp(token, "..") == 0) {
- if (component_count > 0) {
- component_count--;
- } else {
+ if (component_count > 0) {
+ component_count--;
+ } else {
- fprintf(stderr, "[SECURITY] Path traversal attempt: %s\n", path);
- free(path_copy);
- return -1;
- }
- } else if (strlen(token) > 0) {
+ beaker_log("SECURITY", "Path traversal attempt: %s\n", path);
+ free(path_copy);
+ return -1;
+ }
+ } else if (strlen(token) > 0) {
- if (component_count < MAX_URL_PARAMS) {
- strncpy(components[component_count], token, MAX_KEY_LEN - 1);
- components[component_count][MAX_KEY_LEN - 1] = '\0';
- component_count++;
- }
- }
- token = strtok(NULL, "/");
+ if (component_count < MAX_URL_PARAMS) {
+ strncpy(components[component_count], token, MAX_KEY_LEN - 1);
+ components[component_count][MAX_KEY_LEN - 1] = '\0';
+ component_count++;
+ }
}
+ token = strtok(NULL, "/");
+ }
- free(path_copy);
+ free(path_copy);
- canonical[0] = '\0';
- for (int i = 0; i < component_count; i++) {
- if (strlen(canonical) + strlen(components[i]) + 2 > max_len) {
- fprintf(stderr, "[ERROR] Canonical path too long\n");
- return -1;
- }
- strcat(canonical, "/");
- strcat(canonical, components[i]);
+ canonical[0] = '\0';
+ for (int i = 0; i < component_count; i++) {
+ if (strlen(canonical) + strlen(components[i]) + 2 > max_len) {
+ beaker_log("ERROR", "Canonical path too long\n");
+ return -1;
}
+ strcat(canonical, "/");
+ strcat(canonical, components[i]);
+ }
- if (canonical[0] == '\0') {
- strcpy(canonical, "/");
- }
+ if (canonical[0] == '\0') {
+ strcpy(canonical, "/");
+ }
- return 0;
+ return 0;
}
static bool is_safe_path_component(const char *component) {
- if (strstr(component, "..") ||
- strstr(component, "//")) {
- return false;
- }
+ if (strstr(component, "..") || strstr(component, "//")) {
+ return false;
+ }
- for (size_t i = 0; component[i]; i++) {
- if (component[i] < 32 && component[i] != '\t') {
- return false;
- }
+ for (size_t i = 0; component[i]; i++) {
+ if (component[i] < 32 && component[i] != '\t') {
+ return false;
}
+ }
- return true;
+ return true;
}
static int url_decode(char *dst, const char *src, size_t dst_size) {
- size_t i = 0, j = 0;
+ size_t i = 0, j = 0;
- while (src[i] && j < dst_size - 1) {
- if (src[i] == '%') {
+ while (src[i] && j < dst_size - 1) {
+ if (src[i] == '%') {
- if (src[i+1] && src[i+2]) {
- char hex[3] = {src[i+1], src[i+2], '\0'};
- char *endptr;
- long value = strtol(hex, &endptr, 16);
+ if (src[i + 1] && src[i + 2]) {
+ char hex[3] = {src[i + 1], src[i + 2], '\0'};
+ char *endptr;
+ long value = strtol(hex, &endptr, 16);
- if (*endptr != '\0') {
- fprintf(stderr, "[SECURITY] Invalid URL encoding: %%%s\n", hex);
- return -1;
- }
+ if (*endptr != '\0') {
+ beaker_log("SECURITY", "Invalid URL encoding: %%%s\n", hex);
+ return -1;
+ }
- if (value == 0) {
- fprintf(stderr, "[SECURITY] Null byte in URL encoding\n");
- return -1;
- }
+ if (value == 0) {
+ beaker_log("SECURITY", "Null byte in URL encoding\n");
+ return -1;
+ }
- dst[j++] = (char)value;
- i += 3;
- } else {
- fprintf(stderr, "[SECURITY] Incomplete URL encoding at end\n");
- return -1;
- }
- } else if (src[i] == '+') {
+ dst[j++] = (char)value;
+ i += 3;
+ } else {
+ beaker_log("SECURITY", "Incomplete URL encoding at end\n");
+ return -1;
+ }
+ } else if (src[i] == '+') {
- dst[j++] = ' ';
- i++;
- } else {
- dst[j++] = src[i++];
- }
+ dst[j++] = ' ';
+ i++;
+ } else {
+ dst[j++] = src[i++];
}
+ }
- dst[j] = '\0';
- return 0;
+ dst[j] = '\0';
+ return 0;
}
char *parse_request_url(const char *request_line, UrlParams *params) {
- char method[16];
- char raw_url_full[MAX_PATH_LEN];
- char http_version[16];
+ char method[16];
+ char raw_url_full[MAX_PATH_LEN];
+ char http_version[16];
- if (sscanf(request_line, "%15s %255s %15s", method, raw_url_full,
- http_version) != 3) {
- fprintf(stderr, "[ERROR] parse_request_url: Malformed request line\n");
- return NULL;
- }
+ if (sscanf(request_line, "%15s %255s %15s", method, raw_url_full,
+ http_version) != 3) {
+ beaker_log("ERROR", "parse_request_url: Malformed request line\n");
+ return NULL;
+ }
- params->count = 0;
+ params->count = 0;
- char *working_raw = strdup(raw_url_full);
- if (!working_raw) {
- perror("Failed to allocate memory for URL copy");
- return NULL;
- }
+ char *working_raw = strdup(raw_url_full);
+ if (!working_raw) {
+ beaker_log_errno("Failed to allocate memory for URL copy");
+ return NULL;
+ }
- char *query_start = strchr(working_raw, '?');
- if (query_start) {
- *query_start = '\0';
+ char *query_start = strchr(working_raw, '?');
+ if (query_start) {
+ *query_start = '\0';
+ }
- }
+ char decoded_path[MAX_PATH_LEN];
+ if (url_decode(decoded_path, working_raw, sizeof(decoded_path)) != 0) {
+ beaker_log("SECURITY", "Invalid URL encoding in path\n");
+ free(working_raw);
+ return NULL;
+ }
- char decoded_path[MAX_PATH_LEN];
- if (url_decode(decoded_path, working_raw, sizeof(decoded_path)) != 0) {
- fprintf(stderr, "[SECURITY] Invalid URL encoding in path\n");
- free(working_raw);
- return NULL;
- }
+ char canonical_path[MAX_PATH_LEN];
+ if (canonicalize_path(canonical_path, decoded_path, sizeof(canonical_path)) !=
+ 0) {
+ beaker_log("SECURITY", "Path canonicalization failed\n");
+ free(working_raw);
+ return NULL;
+ }
- char canonical_path[MAX_PATH_LEN];
- if (canonicalize_path(canonical_path, decoded_path, sizeof(canonical_path)) != 0) {
- fprintf(stderr, "[SECURITY] Path canonicalization failed\n");
+ char *path_check = strdup(canonical_path);
+ if (path_check) {
+ char *token = strtok(path_check, "/");
+ while (token) {
+ if (!is_safe_path_component(token)) {
+ beaker_log("SECURITY", "Unsafe path component: %s\n", token);
+ free(path_check);
free(working_raw);
return NULL;
+ }
+ token = strtok(NULL, "/");
}
+ free(path_check);
+ }
- char *path_check = strdup(canonical_path);
- if (path_check) {
- char *token = strtok(path_check, "/");
- while (token) {
- if (!is_safe_path_component(token)) {
- fprintf(stderr, "[SECURITY] Unsafe path component: %s\n", token);
- free(path_check);
- free(working_raw);
- return NULL;
- }
- token = strtok(NULL, "/");
- }
- free(path_check);
- }
-
- if (query_start) {
- char *query_string = query_start + 1;
+ if (query_start) {
+ char *query_string = query_start + 1;
- char *pair;
- char *saveptr;
+ char *pair;
+ char *saveptr;
- pair = strtok_r(query_string, "&", &saveptr);
- while (pair && params->count < MAX_URL_PARAMS) {
- char *equals = strchr(pair, '=');
- if (equals) {
- *equals = '\0';
+ pair = strtok_r(query_string, "&", &saveptr);
+ while (pair && params->count < MAX_URL_PARAMS) {
+ char *equals = strchr(pair, '=');
+ if (equals) {
+ *equals = '\0';
- char *raw_key = pair;
- char *raw_val = equals + 1;
+ char *raw_key = pair;
+ char *raw_val = equals + 1;
- if (url_decode(params->params[params->count].key, raw_key, MAX_KEY_LEN) == 0 &&
- url_decode(params->params[params->count].value, raw_val, MAX_VALUE_LEN) == 0) {
- params->count++;
- }
- }
- pair = strtok_r(NULL, "&", &saveptr);
+ if (url_decode(params->params[params->count].key, raw_key,
+ MAX_KEY_LEN) == 0 &&
+ url_decode(params->params[params->count].value, raw_val,
+ MAX_VALUE_LEN) == 0) {
+ params->count++;
}
+ }
+ pair = strtok_r(NULL, "&", &saveptr);
}
+ }
- char *final_path = strdup(canonical_path);
- free(working_raw);
- return final_path;
+ char *final_path = strdup(canonical_path);
+ free(working_raw);
+ return final_path;
}
-bool serve_static_file_with_mime(const char *request_path_relative_to_static, const char *mime_type) {
+bool serve_static_file_with_mime(const char *request_path_relative_to_static,
+ const char *mime_type) {
char full_static_path[MAX_PATH_LEN];
- if (request_path_relative_to_static == NULL || strlen(request_path_relative_to_static) == 0) {
- fprintf(stderr, "[ERROR] serve_static_file_with_mime: Empty path provided\n");
+ if (request_path_relative_to_static == NULL ||
+ strlen(request_path_relative_to_static) == 0) {
+ beaker_log("ERROR", "serve_static_file_with_mime: Empty path provided\n");
return false;
}
if (strstr(request_path_relative_to_static, "..") != NULL ||
strstr(request_path_relative_to_static, "//") != NULL ||
request_path_relative_to_static[0] == '/') {
- fprintf(stderr, "[SECURITY] Attempted directory traversal: %s\n",
- request_path_relative_to_static);
+ beaker_log("SECURITY", "Attempted directory traversal: %s\n",
+ request_path_relative_to_static);
send_status("403 Forbidden");
return true;
}
@@ -274,24 +279,27 @@ bool serve_static_file_with_mime(const char *request_path_relative_to_static, co
FILE *fp = fopen(full_static_path, "rb");
if (fp == NULL) {
- fprintf(stderr,
- "[ERROR] serve_static_file_with_mime: File '%s' not found or could not be opened. %s\n",
- full_static_path, strerror(errno));
+ beaker_log("ERROR",
+ "serve_static_file_with_mime: File '%s' not found or could not "
+ "be opened. %s\n",
+ full_static_path, strerror(errno));
return false;
}
struct stat st;
if (fstat(fileno(fp), &st) < 0) {
- perror("fstat error");
- fprintf(stderr, "[ERROR] serve_static_file_with_mime: fstat failed for '%s'.\n",
- full_static_path);
+ beaker_log_errno_format(
+ "ERROR", "serve_static_file_with_mime: fstat failed for '%s'.\n",
+ full_static_path);
fclose(fp);
send_status("500 Internal Server Error");
return true;
}
long file_size = st.st_size;
+ current_response_status = 200;
+ current_response_size = file_size;
if (!mime_type || mime_type[0] == '\0') {
mime_type = get_mime_type(full_static_path);
@@ -308,9 +316,10 @@ bool serve_static_file_with_mime(const char *request_path_relative_to_static, co
mime_type, file_size);
if (send(current_client_socket, http_header, strlen(http_header), 0) < 0) {
- perror("Error sending static file header");
- fprintf(stderr, "[ERROR] serve_static_file_with_mime: Failed to send header for '%s'.\n",
- full_static_path);
+ beaker_log_errno_format(
+ "ERROR",
+ "serve_static_file_with_mime: Failed to send header for '%s'.\n",
+ full_static_path);
fclose(fp);
return true;
}
@@ -319,19 +328,21 @@ bool serve_static_file_with_mime(const char *request_path_relative_to_static, co
size_t bytes_read;
bool send_error = false;
- while (!send_error && !feof(fp) && !ferror(fp) && (bytes_read = fread(file_buffer, 1, sizeof(file_buffer), fp)) > 0) {
+ while (!send_error && !feof(fp) && !ferror(fp) &&
+ (bytes_read = fread(file_buffer, 1, sizeof(file_buffer), fp)) > 0) {
if (send(current_client_socket, file_buffer, bytes_read, 0) < 0) {
- perror("Error sending static file content");
- fprintf(stderr, "[ERROR] serve_static_file_with_mime: Failed to send content for '%s'.\n",
- full_static_path);
+ beaker_log_errno_format(
+ "ERROR",
+ "serve_static_file_with_mime: Failed to send content for '%s'.\n",
+ full_static_path);
send_error = true;
}
}
if (ferror(fp)) {
- perror("Error reading static file");
- fprintf(stderr, "[ERROR] serve_static_file_with_mime: Failed to read '%s'.\n",
- full_static_path);
+ beaker_log_errno_format(
+ "ERROR", "serve_static_file_with_mime: Failed to read '%s'.\n",
+ full_static_path);
fclose(fp);
return true;
}
@@ -347,16 +358,19 @@ bool serve_static_file(const char *request_path_relative_to_static) {
bool serve_data(const char *data, size_t size, const char *mime_type) {
if (current_client_socket == -1) {
- fprintf(stderr, "[ERROR] serve_data: No client socket set. Cannot send data.\n");
+ beaker_log("ERROR",
+ "serve_data: No client socket set. Cannot send data.\n");
return false;
}
if (data == NULL || size == 0) {
- fprintf(stderr, "[ERROR] serve_data: Invalid data or size.\n");
+ beaker_log("ERROR", "serve_data: Invalid data or size.\n");
return false;
}
char http_header[BUFFER_SIZE];
+ current_response_status = 200;
+ current_response_size = size;
snprintf(http_header, sizeof(http_header),
"HTTP/1.1 200 OK\r\n"
@@ -367,17 +381,16 @@ bool serve_data(const char *data, size_t size, const char *mime_type) {
mime_type, size);
if (send(current_client_socket, http_header, strlen(http_header), 0) < 0) {
- perror("Error sending data header");
- fprintf(stderr, "[ERROR] serve_data: Failed to send header.\n");
+ beaker_log_errno_format("ERROR", "serve_data: Failed to send header.\n");
return false;
}
size_t bytes_sent = 0;
while (bytes_sent < size) {
- size_t chunk = (size - bytes_sent > BUFFER_SIZE) ? BUFFER_SIZE : (size - bytes_sent);
+ size_t chunk =
+ (size - bytes_sent > BUFFER_SIZE) ? BUFFER_SIZE : (size - bytes_sent);
if (send(current_client_socket, data + bytes_sent, chunk, 0) < 0) {
- perror("Error sending data content");
- fprintf(stderr, "[ERROR] serve_data: Failed to send content.\n");
+ beaker_log_errno_format("ERROR", "serve_data: Failed to send content.\n");
return false;
}
bytes_sent += chunk;
diff --git a/src/server.c b/src/server.c
index 85ad61a..df1db23 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1,186 +1,192 @@
-#include "../beaker.h"
-#include "beaker_globals.h"
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <unistd.h>
-#include <pthread.h>
-#include <stdlib.h>
-#include <signal.h>
+#include "../beaker.h"
+#include "beaker_globals.h"
+#include <arpa/inet.h>
+#include <errno.h>
#include <fcntl.h>
+#include <netinet/in.h>
#include <poll.h>
-#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
#define MAX_PENDING_CONNECTIONS 128
static volatile sig_atomic_t g_shutdown_requested = 0;
static void signal_handler(int sig) {
- (void)sig;
- g_shutdown_requested = 1;
+ (void)sig;
+ g_shutdown_requested = 1;
}
typedef struct {
- _Atomic(size_t) sequence;
- int socket;
+ _Atomic(size_t) sequence;
+ int socket;
} WorkSlot;
typedef struct {
- _Atomic(size_t) head;
- _Atomic(size_t) tail;
- _Atomic(int) shutdown;
- WorkSlot slots[MAX_PENDING_CONNECTIONS];
- pthread_mutex_t mutex;
- pthread_cond_t cond;
+ _Atomic(size_t) head;
+ _Atomic(size_t) tail;
+ _Atomic(int) shutdown;
+ WorkSlot slots[MAX_PENDING_CONNECTIONS];
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
} WorkQueue;
static WorkQueue g_work_queue;
static void work_queue_init(WorkQueue *queue) {
- atomic_store(&queue->head, 0);
- atomic_store(&queue->tail, 0);
- atomic_store(&queue->shutdown, 0);
- for (int i = 0; i < MAX_PENDING_CONNECTIONS; i++) {
- atomic_store(&queue->slots[i].sequence, (size_t)i);
- }
- pthread_mutex_init(&queue->mutex, NULL);
- pthread_cond_init(&queue->cond, NULL);
+ atomic_store(&queue->head, 0);
+ atomic_store(&queue->tail, 0);
+ atomic_store(&queue->shutdown, 0);
+ for (int i = 0; i < MAX_PENDING_CONNECTIONS; i++) {
+ atomic_store(&queue->slots[i].sequence, (size_t)i);
+ }
+ pthread_mutex_init(&queue->mutex, NULL);
+ pthread_cond_init(&queue->cond, NULL);
}
static void work_queue_destroy(WorkQueue *queue) {
- pthread_mutex_destroy(&queue->mutex);
- pthread_cond_destroy(&queue->cond);
+ pthread_mutex_destroy(&queue->mutex);
+ pthread_cond_destroy(&queue->cond);
}
static int work_queue_push(WorkQueue *queue, int client_socket) {
- size_t tail = atomic_load(&queue->tail);
+ size_t tail = atomic_load(&queue->tail);
- for (;;) {
- WorkSlot *slot = &queue->slots[tail % MAX_PENDING_CONNECTIONS];
- size_t seq = atomic_load(&slot->sequence);
- intptr_t diff = (intptr_t)seq - (intptr_t)tail;
+ for (;;) {
+ WorkSlot *slot = &queue->slots[tail % MAX_PENDING_CONNECTIONS];
+ size_t seq = atomic_load(&slot->sequence);
+ intptr_t diff = (intptr_t)seq - (intptr_t)tail;
- if (diff == 0) {
- if (atomic_compare_exchange_weak(&queue->tail, &tail, tail + 1)) {
- slot->socket = client_socket;
- atomic_store(&slot->sequence, tail + 1);
- pthread_cond_signal(&queue->cond);
- return 0;
- }
- } else if (diff < 0) {
- return -1;
- } else {
- tail = atomic_load(&queue->tail);
- }
+ if (diff == 0) {
+ if (atomic_compare_exchange_weak(&queue->tail, &tail, tail + 1)) {
+ slot->socket = client_socket;
+ atomic_store(&slot->sequence, tail + 1);
+ pthread_cond_signal(&queue->cond);
+ return 0;
+ }
+ } else if (diff < 0) {
+ return -1;
+ } else {
+ tail = atomic_load(&queue->tail);
}
+ }
}
static int work_queue_pop(WorkQueue *queue) {
- size_t head = atomic_load(&queue->head);
+ size_t head = atomic_load(&queue->head);
- for (;;) {
- if (atomic_load(&queue->shutdown)) return -1;
+ for (;;) {
+ if (atomic_load(&queue->shutdown))
+ return -1;
- WorkSlot *slot = &queue->slots[head % MAX_PENDING_CONNECTIONS];
- size_t seq = atomic_load(&slot->sequence);
- intptr_t diff = (intptr_t)seq - (intptr_t)(head + 1);
+ WorkSlot *slot = &queue->slots[head % MAX_PENDING_CONNECTIONS];
+ size_t seq = atomic_load(&slot->sequence);
+ intptr_t diff = (intptr_t)seq - (intptr_t)(head + 1);
- if (diff == 0) {
- if (atomic_compare_exchange_weak(&queue->head, &head, head + 1)) {
- int fd = slot->socket;
- atomic_store(&slot->sequence, head + MAX_PENDING_CONNECTIONS);
- return fd;
- }
- } else if (diff < 0) {
- pthread_mutex_lock(&queue->mutex);
- if (!atomic_load(&queue->shutdown) && atomic_load(&queue->head) == head) {
- pthread_cond_wait(&queue->cond, &queue->mutex);
- }
- pthread_mutex_unlock(&queue->mutex);
- head = atomic_load(&queue->head);
- } else {
- head = atomic_load(&queue->head);
- }
+ if (diff == 0) {
+ if (atomic_compare_exchange_weak(&queue->head, &head, head + 1)) {
+ int fd = slot->socket;
+ atomic_store(&slot->sequence, head + MAX_PENDING_CONNECTIONS);
+ return fd;
+ }
+ } else if (diff < 0) {
+ pthread_mutex_lock(&queue->mutex);
+ if (!atomic_load(&queue->shutdown) && atomic_load(&queue->head) == head) {
+ pthread_cond_wait(&queue->cond, &queue->mutex);
+ }
+ pthread_mutex_unlock(&queue->mutex);
+ head = atomic_load(&queue->head);
+ } else {
+ head = atomic_load(&queue->head);
}
+ }
}
static int get_optimal_thread_count(void) {
- long cores = sysconf(_SC_NPROCESSORS_ONLN);
- if (cores < 1) cores = 1;
- return (int)(cores * 2);
+ long cores = sysconf(_SC_NPROCESSORS_ONLN);
+ if (cores < 1)
+ cores = 1;
+ return (int)(cores * 2);
}
void handle_client_connection(int new_socket);
static void *worker_thread(void *arg) {
- (void)arg;
- while (1) {
- int client_socket = work_queue_pop(&g_work_queue);
- if (client_socket < 0) {
- break;
- }
- handle_client_connection(client_socket);
+ (void)arg;
+ while (1) {
+ int client_socket = work_queue_pop(&g_work_queue);
+ if (client_socket < 0) {
+ break;
}
- return NULL;
+ handle_client_connection(client_socket);
+ }
+ return NULL;
}
-static int initialize_server_socket(const char *ip, int port, int *server_fd_out,
+static int initialize_server_socket(const char *ip, int port,
+ int *server_fd_out,
struct sockaddr_in *address_out) {
if ((*server_fd_out = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
- perror("socket failed");
- fprintf(stderr, "[ERROR] initialize_server_socket: Failed to create socket.\n");
+ beaker_log_errno_format(
+ "ERROR", "initialize_server_socket: Failed to create socket.\n");
return -1;
}
int opt = 1;
if (setsockopt(*server_fd_out, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
- perror("setsockopt SO_REUSEADDR failed");
+ beaker_log_errno("setsockopt SO_REUSEADDR failed");
}
- // Needed for FreeBSD support. On macOS this allows multiple processes to
- // bind the same TCP port, which is surprising for a single-instance server.
- #if defined(__FreeBSD__) && defined(SO_REUSEPORT)
+// Needed for FreeBSD support. On macOS this allows multiple processes to
+// bind the same TCP port, which is surprising for a single-instance server.
+#if defined(__FreeBSD__) && defined(SO_REUSEPORT)
if (setsockopt(*server_fd_out, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt))) {
- perror("setsockopt SO_REUSEPORT failed");
- fprintf(stderr, "[ERROR] initialize_server_socket: Failed to set SO_REUSEPORT.\n");
+ beaker_log_errno_format(
+ "ERROR", "initialize_server_socket: Failed to set SO_REUSEPORT.\n");
close(*server_fd_out);
return -1;
}
- #endif
+#endif
+ address_out->sin_family = AF_INET;
+ address_out->sin_addr.s_addr = inet_addr(ip);
+ address_out->sin_port = htons(port);
- address_out->sin_family = AF_INET;
- address_out->sin_addr.s_addr = inet_addr(ip);
- address_out->sin_port = htons(port);
-
- if (bind(*server_fd_out, (struct sockaddr *)address_out, sizeof(*address_out)) < 0) {
- perror("bind failed");
- fprintf(stderr, "[ERROR] initialize_server_socket: Failed to bind socket to %s:%d.\n", ip, port);
+ if (bind(*server_fd_out, (struct sockaddr *)address_out,
+ sizeof(*address_out)) < 0) {
+ beaker_log_errno_format(
+ "ERROR", "initialize_server_socket: Failed to bind socket to %s:%d.\n",
+ ip, port);
close(*server_fd_out);
return -1;
}
if (listen(*server_fd_out, 10) < 0) {
- perror("listen failed");
- fprintf(stderr, "[ERROR] initialize_server_socket: Failed to listen on socket.\n");
+ beaker_log_errno_format(
+ "ERROR", "initialize_server_socket: Failed to listen on socket.\n");
close(*server_fd_out);
return -1;
}
int flags = fcntl(*server_fd_out, F_GETFL, 0);
if (flags < 0 || fcntl(*server_fd_out, F_SETFL, flags | O_NONBLOCK) < 0) {
- perror("fcntl O_NONBLOCK failed");
+ beaker_log_errno("fcntl O_NONBLOCK failed");
close(*server_fd_out);
return -1;
}
- printf("Beaker server listening on %s:%d\n", ip, port);
+ beaker_log("INFO", "listening on %s:%d", ip, port);
return 0;
}
@@ -195,73 +201,108 @@ static int set_socket_blocking(int fd) {
return fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
}
+static void finish_client_connection(int socket, char *requested_path,
+ const char *method, const char *path,
+ const struct timespec *started_at) {
+ struct timespec finished_at;
+ clock_gettime(CLOCK_MONOTONIC, &finished_at);
+ double duration_ms = (finished_at.tv_sec - started_at->tv_sec) * 1000.0 +
+ (finished_at.tv_nsec - started_at->tv_nsec) / 1000000.0;
+ beaker_log_request(current_request_info.remote_addr, method, path,
+ current_response_status, current_response_size,
+ duration_ms);
+ free(requested_path);
+ close(socket);
+ current_client_socket = -1;
+}
+
void handle_client_connection(int new_socket) {
- current_client_socket = new_socket;
- char buffer[BUFFER_SIZE] = {0};
+ current_client_socket = new_socket;
+ char buffer[BUFFER_SIZE] = {0};
+ char method[16] = "-";
+ char log_path[MAX_PATH_LEN] = "-";
+ char *requested_path = NULL;
+ struct timespec started_at;
+
+ clock_gettime(CLOCK_MONOTONIC, &started_at);
+ current_response_status = 0;
+ current_response_size = 0;
+ memset(&current_request_info, 0, sizeof(RequestInfo));
+
+ struct sockaddr_in client_addr;
+ socklen_t client_len = sizeof(client_addr);
+ if (getpeername(new_socket, (struct sockaddr *)&client_addr, &client_len) !=
+ 0 ||
+ inet_ntop(AF_INET, &client_addr.sin_addr,
+ current_request_info.remote_addr,
+ sizeof(current_request_info.remote_addr)) == NULL) {
+ strcpy(current_request_info.remote_addr, "-");
+ }
ssize_t bytes_read = read(new_socket, buffer, BUFFER_SIZE - 1);
if (bytes_read < 0) {
- perror("read failed");
- fprintf(stderr, "[ERROR] handle_client_connection: Failed to read from client socket.\n");
- close(new_socket);
+ beaker_log_errno_format(
+ "ERROR",
+ "handle_client_connection: Failed to read from client socket.\n");
+ finish_client_connection(new_socket, requested_path, method, log_path,
+ &started_at);
return;
}
- buffer[bytes_read] = '\0';
+ buffer[bytes_read] = '\0';
strncpy(current_request_buffer, buffer, BUFFER_SIZE - 1);
current_request_buffer[BUFFER_SIZE - 1] = '\0';
- memset(&current_request_info, 0, sizeof(RequestInfo));
-
- char request_line[MAX_PATH_LEN + 64];
- char *first_line_end = strstr(buffer, "\r\n");
+ char request_line[MAX_PATH_LEN + 64];
+ char *first_line_end = strstr(buffer, "\r\n");
if (first_line_end == NULL) {
- fprintf(stderr, "[ERROR] handle_client_connection: Invalid HTTP request: No CRLF found.\n");
+ beaker_log(
+ "ERROR",
+ "handle_client_connection: Invalid HTTP request: No CRLF found.\n");
send_status("400 Bad Request");
- close(new_socket);
+ finish_client_connection(new_socket, requested_path, method, log_path,
+ &started_at);
return;
}
size_t request_line_len = first_line_end - buffer;
if (request_line_len >= sizeof(request_line)) {
- fprintf(stderr, "[ERROR] handle_client_connection: Request line too long.\n");
+ beaker_log("ERROR", "handle_client_connection: Request line too long.\n");
send_status("400 Bad Request");
- close(new_socket);
+ finish_client_connection(new_socket, requested_path, method, log_path,
+ &started_at);
return;
}
strncpy(request_line, buffer, request_line_len);
- request_line[request_line_len] = '\0';
+ request_line[request_line_len] = '\0';
+ sscanf(request_line, "%15s %255s", method, log_path);
- UrlParams request_params;
- char *requested_path = parse_request_url(request_line, &request_params);
+ UrlParams request_params;
+ requested_path = parse_request_url(request_line, &request_params);
if (requested_path == NULL) {
- fprintf(stderr, "[ERROR] handle_client_connection: Could not parse request path. Sending 400 Bad Request.\n");
+ beaker_log("ERROR", "handle_client_connection: Could not parse request "
+ "path. Sending 400 Bad Request.\n");
send_status("400 Bad Request");
- close(new_socket);
+ finish_client_connection(new_socket, requested_path, method, log_path,
+ &started_at);
return;
}
-printf("Accessing: %s\n", requested_path);
-
- struct sockaddr_in client_addr;
- socklen_t client_len = sizeof(client_addr);
- if (getpeername(new_socket, (struct sockaddr *)&client_addr, &client_len) == 0) {
- strncpy(current_request_info.remote_addr, inet_ntoa(client_addr.sin_addr),
- sizeof(current_request_info.remote_addr) - 1);
- }
+ strncpy(log_path, requested_path, sizeof(log_path) - 1);
+ log_path[sizeof(log_path) - 1] = '\0';
bool handled = false;
if (strncmp(requested_path, "/static/", strlen("/static/")) == 0) {
if (serve_static_file(requested_path + strlen("/static/"))) {
- handled = true;
+ handled = true;
}
}
if (!handled) {
- int best_match_handler_index = -1;
- size_t best_match_len = 0;
+ int best_match_handler_index = -1;
+ size_t best_match_len = 0;
for (int i = 0; i < handler_count; i++) {
size_t handler_path_len = strlen(handlers[i].path);
@@ -281,14 +322,11 @@ printf("Accessing: %s\n", requested_path);
if (best_match_handler_index != -1) {
handlers[best_match_handler_index].handler(&request_params);
- handled = true;
+ handled = true;
}
}
if (!handled) {
- fprintf(stderr,
- "[WARNING] handle_client_connection: No handler or static file found for path '%s'. Sending 404 Not Found.\n",
- requested_path);
const char *not_found_html = "<h1>404 Not Found</h1><p>The requested URL "
"was not located on this server.</p>";
char not_found_response[BUFFER_SIZE];
@@ -300,130 +338,141 @@ printf("Accessing: %s\n", requested_path);
"\r\n%s",
strlen(not_found_html), not_found_html);
send(new_socket, not_found_response, strlen(not_found_response), 0);
+ current_response_status = 404;
+ current_response_size = strlen(not_found_html);
}
- free(requested_path);
- close(new_socket);
- current_client_socket = -1;
+ finish_client_connection(new_socket, requested_path, method, log_path,
+ &started_at);
}
int beaker_run(const char *ip, int port) {
- beaker_run_with_threads(ip, port, 0);
- return 0;
+ beaker_run_with_threads(ip, port, 0);
+ return 0;
}
void beaker_run_with_threads(const char *ip, int port, int num_workers) {
- int server_fd;
- struct sockaddr_in address;
- int addrlen = sizeof(address);
+ int server_fd;
+ struct sockaddr_in address;
+ int addrlen = sizeof(address);
- g_shutdown_requested = 0;
+ g_shutdown_requested = 0;
- struct sigaction sa;
- sa.sa_handler = signal_handler;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = 0;
- sigaction(SIGINT, &sa, NULL);
- sigaction(SIGTERM, &sa, NULL);
+ struct sigaction sa;
+ sa.sa_handler = signal_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
- if (num_workers <= 0) {
- num_workers = get_optimal_thread_count();
- }
+ if (num_workers <= 0) {
+ num_workers = get_optimal_thread_count();
+ }
- if (initialize_server_socket(ip, port, &server_fd, &address) != 0) {
- return;
- }
+ if (initialize_server_socket(ip, port, &server_fd, &address) != 0) {
+ return;
+ }
- work_queue_init(&g_work_queue);
+ work_queue_init(&g_work_queue);
- pthread_t threads[num_workers];
- for (int i = 0; i < num_workers; i++) {
- pthread_create(&threads[i], NULL, worker_thread, NULL);
- }
+ pthread_t threads[num_workers];
+ for (int i = 0; i < num_workers; i++) {
+ pthread_create(&threads[i], NULL, worker_thread, NULL);
+ }
- printf("Beaker server started with %d worker threads\n", num_workers);
+ beaker_log("DEBUG", "started %d worker threads", num_workers);
- struct pollfd pfd = { .fd = server_fd, .events = POLLIN };
+ struct pollfd pfd = {.fd = server_fd, .events = POLLIN};
- while (!g_shutdown_requested) {
- int ret = poll(&pfd, 1, 1000);
- if (ret < 0) {
- if (errno == EINTR) continue;
- perror("poll failed");
- break;
- }
- if (ret == 0) continue;
+ while (!g_shutdown_requested) {
+ int ret = poll(&pfd, 1, 1000);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ beaker_log_errno("poll failed");
+ break;
+ }
+ if (ret == 0)
+ continue;
- int new_socket;
- while ((new_socket = accept(server_fd, (struct sockaddr *)&address,
- (socklen_t *)&addrlen)) >= 0) {
- if (set_socket_blocking(new_socket) < 0) {
- perror("fcntl clear O_NONBLOCK failed");
- close(new_socket);
- continue;
- }
- if (work_queue_push(&g_work_queue, new_socket) < 0) {
- fprintf(stderr, "[WARNING] Work queue full, closing connection\n");
- const char *busy_response = "HTTP/1.1 503 Service Unavailable\r\nContent-Length: 0\r\n\r\n";
- send(new_socket, busy_response, strlen(busy_response), 0);
- close(new_socket);
- }
- }
+ int new_socket;
+ while ((new_socket = accept(server_fd, (struct sockaddr *)&address,
+ (socklen_t *)&addrlen)) >= 0) {
+ if (set_socket_blocking(new_socket) < 0) {
+ beaker_log_errno("fcntl clear O_NONBLOCK failed");
+ close(new_socket);
+ continue;
+ }
+ if (work_queue_push(&g_work_queue, new_socket) < 0) {
+ beaker_log("WARN", "work queue full; rejecting connection");
+ const char *busy_response =
+ "HTTP/1.1 503 Service Unavailable\r\nContent-Length: 0\r\n\r\n";
+ send(new_socket, busy_response, strlen(busy_response), 0);
+ close(new_socket);
+ }
}
+ }
- printf("Shutting down server...\n");
+ beaker_log("INFO", "shutting down");
- atomic_store(&g_work_queue.shutdown, 1);
- pthread_cond_broadcast(&g_work_queue.cond);
+ atomic_store(&g_work_queue.shutdown, 1);
+ pthread_cond_broadcast(&g_work_queue.cond);
- for (int i = 0; i < num_workers; i++) {
- pthread_join(threads[i], NULL);
- }
+ for (int i = 0; i < num_workers; i++) {
+ pthread_join(threads[i], NULL);
+ }
- work_queue_destroy(&g_work_queue);
- close(server_fd);
+ work_queue_destroy(&g_work_queue);
+ close(server_fd);
}
const char *beaker_get_remote_addr(void) {
- return current_request_info.remote_addr;
+ return current_request_info.remote_addr;
}
static __thread char g_header_value[MAX_VALUE_LEN];
const char *beaker_get_header(const char *name) {
- if (name == NULL) return "";
+ if (name == NULL)
+ return "";
- size_t name_len = strlen(name);
- if (name_len == 0) return "";
+ size_t name_len = strlen(name);
+ if (name_len == 0)
+ return "";
- if (strstr(name, "\r\n") != NULL) return "";
-
- char *buffer = current_request_buffer;
- char *search_end = strstr(buffer, "\r\n\r\n");
- if (search_end == NULL) search_end = buffer + strlen(buffer);
- while (buffer < search_end) {
- if (strncasecmp(buffer, name, name_len) == 0 && buffer[name_len] == ':') {
- char *value_start = buffer + name_len + 1;
- while (*value_start == ' ') value_start++;
-
- char *value_end = strstr(value_start, "\r\n");
- if (value_end == NULL) value_end = search_end;
- size_t value_len = value_end - value_start;
- if (value_len > sizeof(g_header_value)) {
- value_len = sizeof(g_header_value) - 1;
- }
- strncpy(g_header_value, value_start, value_len);
- g_header_value[value_len] = '\0';
- return g_header_value;
- }
- buffer++;
- }
-
+ if (strstr(name, "\r\n") != NULL)
return "";
+
+ char *buffer = current_request_buffer;
+ char *search_end = strstr(buffer, "\r\n\r\n");
+ if (search_end == NULL)
+ search_end = buffer + strlen(buffer);
+ while (buffer < search_end) {
+ if (strncasecmp(buffer, name, name_len) == 0 && buffer[name_len] == ':') {
+ char *value_start = buffer + name_len + 1;
+ while (*value_start == ' ')
+ value_start++;
+
+ char *value_end = strstr(value_start, "\r\n");
+ if (value_end == NULL)
+ value_end = search_end;
+ size_t value_len = value_end - value_start;
+ if (value_len > sizeof(g_header_value)) {
+ value_len = sizeof(g_header_value) - 1;
+ }
+ strncpy(g_header_value, value_start, value_len);
+ g_header_value[value_len] = '\0';
+ return g_header_value;
+ }
+ buffer++;
+ }
+
+ return "";
}
void beaker_set_request_buffer(const char *buffer) {
- if (buffer == NULL) return;
- strncpy(current_request_buffer, buffer, BUFFER_SIZE - 1);
- current_request_buffer[BUFFER_SIZE - 1] = '\0';
+ if (buffer == NULL)
+ return;
+ strncpy(current_request_buffer, buffer, BUFFER_SIZE - 1);
+ current_request_buffer[BUFFER_SIZE - 1] = '\0';
}
diff --git a/src/template.c b/src/template.c
index 8365166..fd03480 100644
--- a/src/template.c
+++ b/src/template.c
@@ -11,8 +11,7 @@ static char *render_template_segment(const char *template_segment,
static ContextVar *find_context_var(TemplateContext *ctx, const char *key) {
if (ctx == NULL || key == NULL) {
- fprintf(stderr,
- "[ERROR] find_context_var: Invalid NULL input (ctx or key).\n");
+ beaker_log("ERROR", "find_context_var: Invalid NULL input (ctx or key).\n");
return NULL;
}
for (int i = 0; i < ctx->count; i++) {
@@ -90,8 +89,8 @@ TemplateContext new_context() {
void context_set(TemplateContext *ctx, const char *key, const char *value) {
if (ctx == NULL || key == NULL || value == NULL) {
- fprintf(stderr,
- "[ERROR] context_set: Invalid NULL input (ctx, key, or value).\n");
+ beaker_log("ERROR",
+ "context_set: Invalid NULL input (ctx, key, or value).\n");
return;
}
@@ -114,25 +113,25 @@ void context_set(TemplateContext *ctx, const char *key, const char *value) {
new_var->type = CONTEXT_TYPE_STRING;
ctx->count++;
} else {
- fprintf(stderr,
- "[WARNING] context_set: TemplateContext is full. Cannot add key "
- "'%s'.\n",
- key);
+ beaker_log("WARN",
+ "context_set: TemplateContext is full. Cannot add key "
+ "'%s'.\n",
+ key);
}
}
void context_set_string_array(TemplateContext *ctx, const char *key,
char *values[], int count) {
if (ctx == NULL || key == NULL || values == NULL) {
- fprintf(stderr, "[ERROR] context_set_string_array: Invalid NULL input "
- "(ctx, key, or values).\n");
+ beaker_log("ERROR", "context_set_string_array: Invalid NULL input "
+ "(ctx, key, or values).\n");
return;
}
if (count < 0 || count > MAX_OUTER_ARRAY_ITEMS) {
- fprintf(stderr,
- "[ERROR] context_set_string_array: Invalid count %d for string "
- "array context '%s'. Max %d allowed.\n",
- count, key, MAX_OUTER_ARRAY_ITEMS);
+ beaker_log("ERROR",
+ "context_set_string_array: Invalid count %d for string "
+ "array context '%s'. Max %d allowed.\n",
+ count, key, MAX_OUTER_ARRAY_ITEMS);
return;
}
@@ -147,10 +146,10 @@ void context_set_string_array(TemplateContext *ctx, const char *key,
var->key[MAX_KEY_LEN - 1] = '\0';
ctx->count++;
} else {
- fprintf(stderr,
- "[WARNING] context_set_string_array: TemplateContext is full. "
- "Cannot add string array key '%s'.\n",
- key);
+ beaker_log("WARN",
+ "context_set_string_array: TemplateContext is full. "
+ "Cannot add string array key '%s'.\n",
+ key);
return;
}
}
@@ -158,11 +157,11 @@ void context_set_string_array(TemplateContext *ctx, const char *key,
var->type = CONTEXT_TYPE_STRING_ARRAY;
var->value.string_array_data.values = (char **)malloc(sizeof(char *) * count);
if (var->value.string_array_data.values == NULL) {
- perror("Failed to allocate memory for string array pointers");
- fprintf(stderr,
- "[ERROR] context_set_string_array: Allocation failed for values "
- "for key '%s'.\n",
- key);
+ beaker_log_errno_format(
+ "ERROR",
+ "context_set_string_array: Allocation failed for values "
+ "for key '%s'.\n",
+ key);
return;
}
var->value.string_array_data.count = count;
@@ -174,11 +173,11 @@ void context_set_string_array(TemplateContext *ctx, const char *key,
}
var->value.string_array_data.values[i] = strdup(values[i]);
if (var->value.string_array_data.values[i] == NULL) {
- perror("Failed to duplicate string for string array context");
- fprintf(stderr,
- "[ERROR] context_set_string_array: Failed to duplicate value for "
- "item %d of key '%s'.\n",
- i, key);
+ beaker_log_errno_format(
+ "ERROR",
+ "context_set_string_array: Failed to duplicate value for "
+ "item %d of key '%s'.\n",
+ i, key);
for (int j = 0; j < i; j++) {
if (var->value.string_array_data.values[j] != NULL) {
@@ -197,15 +196,15 @@ void context_set_array_of_arrays(TemplateContext *ctx, const char *key,
char **values_2d[], int outer_count,
int inner_counts[]) {
if (ctx == NULL || key == NULL || values_2d == NULL || inner_counts == NULL) {
- fprintf(stderr, "[ERROR] context_set_array_of_arrays: Invalid NULL input "
- "(ctx, key, values_2d, or inner_counts).\n");
+ beaker_log("ERROR", "context_set_array_of_arrays: Invalid NULL input "
+ "(ctx, key, values_2d, or inner_counts).\n");
return;
}
if (outer_count < 0 || outer_count > MAX_OUTER_ARRAY_ITEMS) {
- fprintf(stderr,
- "[ERROR] context_set_array_of_arrays: Invalid outer count %d for "
- "array of arrays context '%s'. Max %d allowed.\n",
- outer_count, key, MAX_OUTER_ARRAY_ITEMS);
+ beaker_log("ERROR",
+ "context_set_array_of_arrays: Invalid outer count %d for "
+ "array of arrays context '%s'. Max %d allowed.\n",
+ outer_count, key, MAX_OUTER_ARRAY_ITEMS);
return;
}
@@ -220,10 +219,10 @@ void context_set_array_of_arrays(TemplateContext *ctx, const char *key,
var->key[MAX_KEY_LEN - 1] = '\0';
ctx->count++;
} else {
- fprintf(stderr,
- "[WARNING] context_set_array_of_arrays: TemplateContext is full. "
- "Cannot add array of arrays key '%s'.\n",
- key);
+ beaker_log("WARN",
+ "context_set_array_of_arrays: TemplateContext is full. "
+ "Cannot add array of arrays key '%s'.\n",
+ key);
return;
}
}
@@ -236,11 +235,11 @@ void context_set_array_of_arrays(TemplateContext *ctx, const char *key,
if (var->value.string_2d_array_data.values == NULL ||
var->value.string_2d_array_data.inner_counts == NULL) {
- perror("Failed to allocate memory for 2D string array pointers or counts");
- fprintf(stderr,
- "[ERROR] context_set_array_of_arrays: Allocation failed for key "
- "'%s'.\n",
- key);
+ beaker_log_errno_format(
+ "ERROR",
+ "context_set_array_of_arrays: Allocation failed for key "
+ "'%s'.\n",
+ key);
free(var->value.string_2d_array_data.values);
free(var->value.string_2d_array_data.inner_counts);
var->value.string_2d_array_data.values = NULL;
@@ -253,10 +252,10 @@ void context_set_array_of_arrays(TemplateContext *ctx, const char *key,
int current_inner_count = inner_counts[i];
if (current_inner_count < 0 ||
current_inner_count > MAX_INNER_ARRAY_ITEMS) {
- fprintf(stderr,
- "[ERROR] context_set_array_of_arrays: Invalid inner count %d for "
- "item %d in 2D array '%s'. Max %d allowed.\n",
- current_inner_count, i, key, MAX_INNER_ARRAY_ITEMS);
+ beaker_log("ERROR",
+ "context_set_array_of_arrays: Invalid inner count %d for "
+ "item %d in 2D array '%s'. Max %d allowed.\n",
+ current_inner_count, i, key, MAX_INNER_ARRAY_ITEMS);
for (int k = 0; k < i; k++) {
if (var->value.string_2d_array_data.values[k] != NULL) {
@@ -279,11 +278,11 @@ void context_set_array_of_arrays(TemplateContext *ctx, const char *key,
var->value.string_2d_array_data.values[i] =
(char **)malloc(sizeof(char *) * current_inner_count);
if (var->value.string_2d_array_data.values[i] == NULL) {
- perror("Failed to allocate memory for inner string array pointers");
- fprintf(stderr,
- "[ERROR] context_set_array_of_arrays: Allocation failed for "
- "inner array %d of key '%s'.\n",
- i, key);
+ beaker_log_errno_format(
+ "ERROR",
+ "context_set_array_of_arrays: Allocation failed for "
+ "inner array %d of key '%s'.\n",
+ i, key);
for (int k = 0; k < i; k++) {
if (var->value.string_2d_array_data.values[k] != NULL) {
@@ -311,11 +310,11 @@ void context_set_array_of_arrays(TemplateContext *ctx, const char *key,
}
var->value.string_2d_array_data.values[i][j] = strdup(values_2d[i][j]);
if (var->value.string_2d_array_data.values[i][j] == NULL) {
- perror("Failed to duplicate string for inner array context");
- fprintf(stderr,
- "[ERROR] context_set_array_of_arrays: Failed to duplicate "
- "value for item [%d][%d] of key '%s'.\n",
- i, j, key);
+ beaker_log_errno_format(
+ "ERROR",
+ "context_set_array_of_arrays: Failed to duplicate "
+ "value for item [%d][%d] of key '%s'.\n",
+ i, j, key);
for (int k = 0; k <= i; k++) {
if (var->value.string_2d_array_data.values[k] != NULL) {
@@ -360,7 +359,7 @@ static char *html_escape(const char *input) {
size_t estimated_len = input_len * 5 + 1;
char *output = (char *)malloc(estimated_len);
if (output == NULL) {
- perror("Failed to allocate memory for HTML escape output");
+ beaker_log_errno("Failed to allocate memory for HTML escape output");
return NULL;
}
output[0] = '\0';
@@ -396,7 +395,8 @@ static char *html_escape(const char *input) {
estimated_len = (current_output_len + repl_len + 1) * 2;
char *new_output = (char *)realloc(output, estimated_len);
if (new_output == NULL) {
- perror("Failed to reallocate memory for HTML escape output");
+ beaker_log_errno(
+ "Failed to reallocate memory for HTML escape output");
free(output);
return NULL;
}
@@ -410,7 +410,8 @@ static char *html_escape(const char *input) {
estimated_len = (current_output_len + 1 + 1) * 2;
char *new_output = (char *)realloc(output, estimated_len);
if (new_output == NULL) {
- perror("Failed to reallocate memory for HTML escape output");
+ beaker_log_errno(
+ "Failed to reallocate memory for HTML escape output");
free(output);
return NULL;
}
@@ -426,8 +427,8 @@ static char *html_escape(const char *input) {
static void append_to_buffer(char **buffer, size_t *current_len,
size_t *max_len, const char *str_to_add) {
if (str_to_add == NULL) {
- fprintf(stderr, "[WARNING] append_to_buffer: Attempted to append NULL "
- "string. Skipping.\n");
+ beaker_log("WARN", "append_to_buffer: Attempted to append NULL "
+ "string. Skipping.\n");
return;
}
@@ -442,11 +443,11 @@ static void append_to_buffer(char **buffer, size_t *current_len,
char *new_buffer = (char *)realloc(*buffer, *max_len);
if (new_buffer == NULL) {
- perror("Failed to reallocate buffer for template rendering");
- fprintf(stderr,
- "[ERROR] append_to_buffer: Reallocation failed (requested %zu "
- "bytes).\n",
- *max_len);
+ beaker_log_errno_format(
+ "ERROR",
+ "append_to_buffer: Reallocation failed (requested %zu "
+ "bytes).\n",
+ *max_len);
free(*buffer);
*buffer = NULL;
@@ -467,23 +468,22 @@ static char *parse_indexed_tag(const char *tag_content, int *index_val) {
char *key_name = strdup(tag_content);
if (key_name == NULL) {
- perror("Failed to duplicate tag content for simple key");
- fprintf(stderr, "[ERROR] parse_indexed_tag: strdup failed for '%s'.\n",
- tag_content);
+ beaker_log_errno_format(
+ "ERROR", "parse_indexed_tag: strdup failed for '%s'.\n", tag_content);
}
return key_name;
}
const char *close_bracket = strchr(open_bracket, ']');
if (close_bracket == NULL) {
- fprintf(stderr,
- "[ERROR] parse_indexed_tag: Unclosed bracket in tag '%s'. "
- "Returning raw tag content.\n",
- tag_content);
+ beaker_log("ERROR",
+ "parse_indexed_tag: Unclosed bracket in tag '%s'. "
+ "Returning raw tag content.\n",
+ tag_content);
char *key_name = strdup(tag_content);
if (key_name == NULL) {
- perror("Failed to duplicate malformed tag content");
+ beaker_log_errno("Failed to duplicate malformed tag content");
}
return key_name;
}
@@ -491,9 +491,8 @@ static char *parse_indexed_tag(const char *tag_content, int *index_val) {
size_t key_len = open_bracket - tag_content;
char *key_name = (char *)malloc(key_len + 1);
if (key_name == NULL) {
- perror("Failed to allocate memory for key_name in parse_indexed_tag");
- fprintf(stderr,
- "[ERROR] parse_indexed_tag: Allocation failed for key_name.\n");
+ beaker_log_errno_format(
+ "ERROR", "parse_indexed_tag: Allocation failed for key_name.\n");
return NULL;
}
strncpy(key_name, tag_content, key_len);
@@ -502,9 +501,8 @@ static char *parse_indexed_tag(const char *tag_content, int *index_val) {
size_t index_str_len = close_bracket - (open_bracket + 1);
char *index_str = (char *)malloc(index_str_len + 1);
if (index_str == NULL) {
- perror("Failed to allocate memory for index_str in parse_indexed_tag");
- fprintf(stderr,
- "[ERROR] parse_indexed_tag: Allocation failed for index_str.\n");
+ beaker_log_errno_format(
+ "ERROR", "parse_indexed_tag: Allocation failed for index_str.\n");
free(key_name);
return NULL;
}
@@ -540,8 +538,7 @@ static const char *resolve_compare_value(const Condition *cond,
ContextVar *compare_var = find_context_var(ctx, cond->compare_value);
if (cond->compare_index >= 0) {
- if (compare_var != NULL &&
- compare_var->type == CONTEXT_TYPE_STRING_ARRAY &&
+ if (compare_var != NULL && compare_var->type == CONTEXT_TYPE_STRING_ARRAY &&
cond->compare_index < compare_var->value.string_array_data.count) {
compare_value =
compare_var->value.string_array_data.values[cond->compare_index];
@@ -568,7 +565,8 @@ static bool evaluate_condition(const Condition *cond, TemplateContext *ctx) {
return false;
}
var_value = "";
- } else if (var->type == CONTEXT_TYPE_STRING_ARRAY && cond->compare_index >= 0) {
+ } else if (var->type == CONTEXT_TYPE_STRING_ARRAY &&
+ cond->compare_index >= 0) {
if (cond->compare_index >= 0 &&
cond->compare_index < var->value.string_array_data.count) {
var_value = var->value.string_array_data.values[cond->compare_index];
@@ -596,8 +594,7 @@ static bool evaluate_condition(const Condition *cond, TemplateContext *ctx) {
case CONDITION_EQUAL:
case CONDITION_NOT_EQUAL: {
char compare_buf[MAX_VALUE_LEN];
- const char *compare_value =
- resolve_compare_value(cond, ctx, compare_buf);
+ const char *compare_value = resolve_compare_value(cond, ctx, compare_buf);
bool values_equal = strcmp(var_value, compare_value) == 0;
return cond->type == CONDITION_EQUAL ? values_equal : !values_equal;
}
@@ -766,11 +763,11 @@ static char *render_template_segment(const char *template_segment,
size_t initial_max_len = BUFFER_SIZE;
char *rendered_buffer = (char *)malloc(initial_max_len);
if (rendered_buffer == NULL) {
- perror("Failed to allocate initial buffer for template segment rendering");
- fprintf(stderr,
- "[ERROR] render_template_segment: Failed to allocate initial %zu "
- "bytes for rendered_buffer.\n",
- initial_max_len);
+ beaker_log_errno_format(
+ "ERROR",
+ "render_template_segment: Failed to allocate initial %zu "
+ "bytes for rendered_buffer.\n",
+ initial_max_len);
return NULL;
}
rendered_buffer[0] = '\0';
@@ -786,9 +783,9 @@ static char *render_template_segment(const char *template_segment,
if (text_len > 0) {
char *text_before_tag = (char *)malloc(text_len + 1);
if (text_before_tag == NULL) {
- perror("Failed to allocate memory for text_before_tag");
- fprintf(stderr, "[ERROR] render_template_segment: Allocation failed "
- "for text_before_tag.\n");
+ beaker_log_errno_format("ERROR",
+ "render_template_segment: Allocation failed "
+ "for text_before_tag.\n");
free(rendered_buffer);
return NULL;
}
@@ -801,8 +798,8 @@ static char *render_template_segment(const char *template_segment,
const char *end_tag = strstr(start_tag, "}}");
if (end_tag == NULL) {
- fprintf(stderr, "[ERROR] render_template_segment: Unclosed '{{' tag. "
- "Appending remaining template content as-is.\n");
+ beaker_log("ERROR", "render_template_segment: Unclosed '{{' tag. "
+ "Appending remaining template content as-is.\n");
append_to_buffer(&rendered_buffer, &current_len, &max_len, start_tag);
return rendered_buffer;
}
@@ -810,9 +807,9 @@ static char *render_template_segment(const char *template_segment,
size_t tag_content_len = end_tag - (start_tag + 2);
char *tag_content_raw = (char *)malloc(tag_content_len + 1);
if (tag_content_raw == NULL) {
- perror("Failed to allocate memory for tag_content_raw");
- fprintf(stderr, "[ERROR] render_template_segment: Allocation failed for "
- "tag_content_raw.\n");
+ beaker_log_errno_format("ERROR",
+ "render_template_segment: Allocation failed for "
+ "tag_content_raw.\n");
free(rendered_buffer);
return NULL;
}
@@ -878,9 +875,9 @@ static char *render_template_segment(const char *template_segment,
size_t loop_inner_len = loop_end_tag - loop_inner_start;
char *loop_inner_template = (char *)malloc(loop_inner_len + 1);
if (loop_inner_template == NULL) {
- perror("Failed to allocate memory for loop_inner_template");
- fprintf(stderr, "[ERROR] render_template_segment: Allocation "
- "failed for loop_inner_template.\n");
+ beaker_log_errno_format("ERROR",
+ "render_template_segment: Allocation "
+ "failed for loop_inner_template.\n");
free(rendered_buffer);
free(tag_content_raw);
return NULL;
@@ -940,9 +937,9 @@ static char *render_template_segment(const char *template_segment,
free_context(&loop_ctx);
}
} else {
- fprintf(
- stderr,
- "[IGNORE] [ERROR] render_template_segment: List variable '%s' "
+ beaker_log(
+ "DEBUG",
+ "render_template_segment: List variable '%s' "
"(type %d) is not an iterable array type for 'for' loop.\n",
list_var, list_ctx_var->type);
}
@@ -952,25 +949,26 @@ static char *render_template_segment(const char *template_segment,
continue;
} else {
- fprintf(stderr,
- "[IGNORE] [ERROR] render_template_segment: List variable "
- "'%s' not found for 'for' loop. Skipping loop block.\n",
- list_var);
+ beaker_log("DEBUG",
+ "render_template_segment: List variable "
+ "'%s' not found for 'for' loop. Skipping loop block.\n",
+ list_var);
current_pos = loop_end_tag + strlen("{{endfor}}");
free(tag_content_raw);
continue;
}
} else {
- fprintf(stderr,
- "[ERROR] render_template_segment: Malformed 'for' loop tag: "
- "'%s'. Expected 'for var in list'. Appending loop tag as-is.\n",
- trimmed_tag_content);
+ beaker_log(
+ "ERROR",
+ "render_template_segment: Malformed 'for' loop tag: "
+ "'%s'. Expected 'for var in list'. Appending loop tag as-is.\n",
+ trimmed_tag_content);
}
}
else if (strcmp(trimmed_tag_content, "endfor") == 0) {
- fprintf(stderr, "[WARNING] render_template_segment: '{{endfor}}' without "
- "matching '{{for}}'. Appending endfor tag as-is.\n");
+ beaker_log("WARN", "render_template_segment: '{{endfor}}' without "
+ "matching '{{for}}'. Appending endfor tag as-is.\n");
}
else if (strncmp(trimmed_tag_content, "if ", 3) == 0) {
@@ -992,7 +990,7 @@ static char *render_template_segment(const char *template_segment,
const char *candidates[4] = {next_if, next_else, next_elif, next_endif};
const char *earliest = NULL;
int earliest_idx = -1;
-
+
for (int i = 0; i < 4; i++) {
if (candidates[i] != NULL) {
if (earliest == NULL || candidates[i] < earliest) {
@@ -1035,18 +1033,16 @@ static char *render_template_segment(const char *template_segment,
}
if (endif_tag == NULL) {
- fprintf(stderr,
- "[ERROR] render_template_segment: Unclosed '{{if}}' tag. "
- "Skipping if block.\n");
+ beaker_log("ERROR", "render_template_segment: Unclosed '{{if}}' tag. "
+ "Skipping if block.\n");
current_pos = end_tag + 2;
free(tag_content_raw);
continue;
}
if (endif_tag == NULL) {
- fprintf(stderr,
- "[ERROR] render_template_segment: Unclosed '{{if}}' tag. "
- "Skipping if block.\n");
+ beaker_log("ERROR", "render_template_segment: Unclosed '{{if}}' tag. "
+ "Skipping if block.\n");
current_pos = end_tag + 2;
free(tag_content_raw);
continue;
@@ -1139,18 +1135,18 @@ static char *render_template_segment(const char *template_segment,
else if (strcmp(trimmed_tag_content, "elif ") == 0 ||
strcmp(trimmed_tag_content, "elif") == 0) {
- fprintf(stderr, "[WARNING] render_template_segment: '{{elif}}' without "
- "matching '{{if}}'. Appending elif tag as-is.\n");
+ beaker_log("WARN", "render_template_segment: '{{elif}}' without "
+ "matching '{{if}}'. Appending elif tag as-is.\n");
}
else if (strcmp(trimmed_tag_content, "else") == 0) {
- fprintf(stderr, "[WARNING] render_template_segment: '{{else}}' without "
- "matching '{{if}}'. Appending else tag as-is.\n");
+ beaker_log("WARN", "render_template_segment: '{{else}}' without "
+ "matching '{{if}}'. Appending else tag as-is.\n");
}
else if (strcmp(trimmed_tag_content, "endif") == 0) {
- fprintf(stderr, "[WARNING] render_template_segment: '{{endif}}' without "
- "matching '{{if}}'. Appending endif tag as-is.\n");
+ beaker_log("WARN", "render_template_segment: '{{endif}}' without "
+ "matching '{{if}}'. Appending endif tag as-is.\n");
}
else if (strncmp(trimmed_tag_content, "include ", 8) == 0) {
@@ -1162,9 +1158,9 @@ static char *render_template_segment(const char *template_segment,
size_t filename_len = filename_end - filename_start;
char *included_filename = (char *)malloc(filename_len + 1);
if (included_filename == NULL) {
- perror("Failed to allocate memory for included filename");
- fprintf(stderr, "[ERROR] render_template_segment: Allocation "
- "failed for included_filename.\n");
+ beaker_log_errno_format("ERROR",
+ "render_template_segment: Allocation "
+ "failed for included_filename.\n");
free(rendered_buffer);
free(tag_content_raw);
return NULL;
@@ -1172,13 +1168,16 @@ static char *render_template_segment(const char *template_segment,
strncpy(included_filename, filename_start, filename_len);
included_filename[filename_len] = '\0';
- if (strstr(included_filename, "..") != NULL || strchr(included_filename, '/') != NULL) {
- fprintf(stderr,
- "[SECURITY] render_template_segment: Path traversal attempt in include: %s\n",
- included_filename);
+ if (strstr(included_filename, "..") != NULL ||
+ strchr(included_filename, '/') != NULL) {
+ beaker_log("SECURITY",
+ "render_template_segment: Path traversal attempt in "
+ "include: %s\n",
+ included_filename);
free(included_filename);
- append_to_buffer(&rendered_buffer, &current_len, &max_len,
- "<!-- Include blocked: path traversal not allowed -->");
+ append_to_buffer(
+ &rendered_buffer, &current_len, &max_len,
+ "<!-- Include blocked: path traversal not allowed -->");
current_pos = end_tag + 2;
free(tag_content_raw);
continue;
@@ -1190,10 +1189,10 @@ static char *render_template_segment(const char *template_segment,
included_html);
free(included_html);
} else {
- fprintf(stderr,
- "[WARNING] render_template_segment: Failed to render "
- "included template '%s'.\n",
- included_filename);
+ beaker_log("WARN",
+ "render_template_segment: Failed to render "
+ "included template '%s'.\n",
+ included_filename);
append_to_buffer(&rendered_buffer, &current_len, &max_len,
"<!-- Failed to include ");
@@ -1206,27 +1205,26 @@ static char *render_template_segment(const char *template_segment,
free(tag_content_raw);
continue;
} else {
- fprintf(stderr,
- "[ERROR] render_template_segment: Malformed include tag "
- "'%s'. Missing closing quote.\n",
- trimmed_tag_content);
+ beaker_log("ERROR",
+ "render_template_segment: Malformed include tag "
+ "'%s'. Missing closing quote.\n",
+ trimmed_tag_content);
append_to_buffer(&rendered_buffer, &current_len, &max_len, "{{");
append_to_buffer(&rendered_buffer, &current_len, &max_len,
trimmed_tag_content);
append_to_buffer(&rendered_buffer, &current_len, &max_len, "}}");
- free(tag_content_raw);
-
- current_pos = end_tag + 2;
+ free(tag_content_raw);
- continue;
+ current_pos = end_tag + 2;
+ continue;
}
} else {
- fprintf(stderr,
- "[ERROR] render_template_segment: Malformed include tag '%s'. "
- "Expected quoted filename.\n",
- trimmed_tag_content);
+ beaker_log("ERROR",
+ "render_template_segment: Malformed include tag '%s'. "
+ "Expected quoted filename.\n",
+ trimmed_tag_content);
append_to_buffer(&rendered_buffer, &current_len, &max_len, "{{");
append_to_buffer(&rendered_buffer, &current_len, &max_len,
@@ -1239,10 +1237,10 @@ static char *render_template_segment(const char *template_segment,
const char *key_start = trimmed_tag_content + 3;
const char *key_end = strstr(key_start, "\")");
if (key_end == NULL) {
- fprintf(stderr,
- "[ERROR] render_template_segment: Malformed l() tag '%s'. "
- "Expected l(\"key\"). Appending as-is.\n",
- trimmed_tag_content);
+ beaker_log("ERROR",
+ "render_template_segment: Malformed l() tag '%s'. "
+ "Expected l(\"key\"). Appending as-is.\n",
+ trimmed_tag_content);
append_to_buffer(&rendered_buffer, &current_len, &max_len, "{{");
append_to_buffer(&rendered_buffer, &current_len, &max_len,
trimmed_tag_content);
@@ -1254,7 +1252,8 @@ static char *render_template_segment(const char *template_segment,
size_t key_len = key_end - key_start;
char l10n_key[MAX_KEY_LEN];
- if (key_len >= MAX_KEY_LEN) key_len = MAX_KEY_LEN - 1;
+ if (key_len >= MAX_KEY_LEN)
+ key_len = MAX_KEY_LEN - 1;
strncpy(l10n_key, key_start, key_len);
l10n_key[key_len] = '\0';
@@ -1297,10 +1296,10 @@ static char *render_template_segment(const char *template_segment,
}
}
} else {
- fprintf(stderr,
- "[WARNING] render_template_segment: l10n key '%s' not found "
- "for current locale. Appending key as-is.\n",
- l10n_key);
+ beaker_log("WARN",
+ "render_template_segment: l10n key '%s' not found "
+ "for current locale. Appending key as-is.\n",
+ l10n_key);
append_to_buffer(&rendered_buffer, &current_len, &max_len, l10n_key);
}
@@ -1313,7 +1312,7 @@ static char *render_template_segment(const char *template_segment,
bool is_safe = false;
char *processing_tag_content = strdup(trimmed_tag_content);
if (processing_tag_content == NULL) {
- perror("Failed to duplicate tag content for processing");
+ beaker_log_errno("Failed to duplicate tag content for processing");
free(rendered_buffer);
free(tag_content_raw);
return NULL;
@@ -1331,10 +1330,10 @@ static char *render_template_segment(const char *template_segment,
free(processing_tag_content);
if (key_name == NULL) {
- fprintf(stderr,
- "[ERROR] render_template_segment: parse_indexed_tag failed for "
- "'%s'.\n",
- trimmed_tag_content);
+ beaker_log("ERROR",
+ "render_template_segment: parse_indexed_tag failed for "
+ "'%s'.\n",
+ trimmed_tag_content);
free(rendered_buffer);
free(tag_content_raw);
return NULL;
@@ -1348,9 +1347,9 @@ static char *render_template_segment(const char *template_segment,
if (var->type == CONTEXT_TYPE_STRING) {
value_to_append = var->value.string_val;
} else {
- fprintf(
- stderr,
- "[WARNING] render_template_segment: Variable '%s' is not a "
+ beaker_log(
+ "WARN",
+ "render_template_segment: Variable '%s' is not a "
"simple string (type %d). Not rendering as simple string.\n",
key_name, var->type);
@@ -1365,24 +1364,25 @@ static char *render_template_segment(const char *template_segment,
index_val < var->value.string_array_data.count) {
value_to_append = var->value.string_array_data.values[index_val];
} else {
- fprintf(stderr,
- "[ERROR] render_template_segment: Index %d out of bounds "
- "for '%s' (count %d). Appending tag as-is.\n",
- index_val, key_name, var->value.string_array_data.count);
+ beaker_log("ERROR",
+ "render_template_segment: Index %d out of bounds "
+ "for '%s' (count %d). Appending tag as-is.\n",
+ index_val, key_name,
+ var->value.string_array_data.count);
}
} else {
- fprintf(
- stderr,
- "[ERROR] render_template_segment: Variable '%s' (type %d) is "
+ beaker_log(
+ "ERROR",
+ "render_template_segment: Variable '%s' (type %d) is "
"not a string array for indexed access. Appending tag as-is.\n",
key_name, var->type);
}
}
} else {
- fprintf(stderr,
- "[WARNING] render_template_segment: Key '%s' not found in "
- "context. Appending tag as-is.\n",
- key_name);
+ beaker_log("WARN",
+ "render_template_segment: Key '%s' not found in "
+ "context. Appending tag as-is.\n",
+ key_name);
}
if (value_to_append != NULL) {
@@ -1396,24 +1396,23 @@ static char *render_template_segment(const char *template_segment,
escaped_value);
free(escaped_value);
} else {
- fprintf(stderr,
- "[ERROR] render_template_segment: Failed to HTML escape "
- "value for key '%s'. Appending raw.\n",
- key_name);
+ beaker_log("ERROR",
+ "render_template_segment: Failed to HTML escape "
+ "value for key '%s'. Appending raw.\n",
+ key_name);
append_to_buffer(&rendered_buffer, &current_len, &max_len,
value_to_append);
}
}
}
- free(key_name);
-
- free(tag_content_raw);
+ free(key_name);
- current_pos = end_tag + 2;
+ free(tag_content_raw);
- continue;
+ current_pos = end_tag + 2;
+ continue;
}
free(tag_content_raw);
@@ -1428,9 +1427,8 @@ char *render_template(const char *template_file, TemplateContext *ctx) {
char full_path[MAX_PATH_LEN];
if (strstr(template_file, "..") != NULL) {
- fprintf(stderr,
- "[SECURITY] render_template: Path traversal attempt: %s\n",
- template_file);
+ beaker_log("SECURITY", "render_template: Path traversal attempt: %s\n",
+ template_file);
return NULL;
}
@@ -1438,20 +1436,17 @@ char *render_template(const char *template_file, TemplateContext *ctx) {
FILE *fp = fopen(full_path, "r");
if (fp == NULL) {
- perror("Error opening template file");
- fprintf(stderr,
- "[ERROR] render_template: Could not open template '%s'. %s\n",
- full_path, strerror(errno));
+ beaker_log_errno_format(
+ "ERROR", "render_template: Could not open template '%s'. %s\n",
+ full_path, strerror(errno));
return NULL;
}
fseek(fp, 0, SEEK_END);
long file_size = ftell(fp);
if (file_size < 0) {
- perror("Error getting template file size");
- fprintf(stderr,
- "[ERROR] render_template: Failed to get size of '%s'.\n",
- full_path);
+ beaker_log_errno_format(
+ "ERROR", "render_template: Failed to get size of '%s'.\n", full_path);
fclose(fp);
return NULL;
}
@@ -1465,21 +1460,19 @@ char *render_template(const char *template_file, TemplateContext *ctx) {
char *template_content = (char *)malloc((size_t)file_size + 1);
if (template_content == NULL) {
- perror("Error allocating memory for template content");
- fprintf(stderr,
- "[ERROR] render_template: Failed to allocate %ld bytes for "
- "template content.\n",
- file_size + 1);
+ beaker_log_errno_format("ERROR",
+ "render_template: Failed to allocate %ld bytes for "
+ "template content.\n",
+ file_size + 1);
fclose(fp);
return NULL;
}
size_t bytes_read = fread(template_content, 1, (size_t)file_size, fp);
if (bytes_read != (size_t)file_size) {
- perror("Error reading template file");
- fprintf(stderr,
- "[ERROR] render_template: Failed to read complete template '%s'.\n",
- full_path);
+ beaker_log_errno_format(
+ "ERROR", "render_template: Failed to read complete template '%s'.\n",
+ full_path);
free(template_content);
fclose(fp);
return NULL;