From 1e97b69fcd25a82027241c360d1035fa905a21e1 Mon Sep 17 00:00:00 2001 From: anth64 Date: Fri, 30 Jan 2026 22:07:40 +0100 Subject: [PATCH] feat: add comprehensive error handling with typed error codes Add strict error handling for stk initialization and platform operations. stk_init() now fails fast on critical errors and returns typed error codes that users can handle programmatically. Changes: - Add STK_INIT_* error codes for init failures (memory, tmpdir, watch) - Add STK_PLATFORM_* error codes for platform operation results - Check platform_mkdir() and fail if temp directory cannot be created - Check platform_directory_watch_start() and fail if watch cannot initialize - Add error_cleanup path in platform_directory_watch_start() to properly free resources on critical Windows failures. - Replace all platform error checks with STK_PLATFORM_OPERATION_SUCCESS - Log FATAL errors with when critical operations fail - Add warning logs for non-critical failures Critical failures now return specific error codes: - STK_INIT_MEMORY_ERROR: Module memory allocation failed - STK_INIT_TMPDIR_ERROR: Cannot create temp directory - STK_INIT_WATCH_ERROR: Cannot start directory watching Individual module load failures remain non-fatal and are handled logged. --- include/stk.h | 15 +++++++++++++- src/platform.c | 51 +++++++++++++++++++++++++++++++---------------- src/stk.c | 54 +++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 93 insertions(+), 27 deletions(-) diff --git a/include/stk.h b/include/stk.h index 27c1e3b..1103158 100644 --- a/include/stk.h +++ b/include/stk.h @@ -10,12 +10,25 @@ #define STK_PATH_MAX 256 #define STK_PATH_MAX_OS 4096 -/* Modules */ +/* Initialization return codes */ +#define STK_INIT_SUCCESS 0 +#define STK_INIT_MEMORY_ERROR 1 +#define STK_INIT_TMPDIR_ERROR 2 +#define STK_INIT_WATCH_ERROR 3 + +/* Module loading return codes */ #define STK_MOD_INIT_SUCCESS 0 #define STK_MOD_INIT_FAILURE 1 #define STK_MOD_LIBRARY_LOAD_ERROR 2 #define STK_MOD_SYMBOL_NOT_FOUND_ERROR 3 +/* Platform return codes */ +#define STK_PLATFORM_OPERATION_SUCCESS 0 +#define STK_PLATFORM_FILE_COPY_ERROR 1 +#define STK_PLATFORM_MKDIR_ERROR 2 +#define STK_PLATFORM_REMOVE_DIR_ERROR 3 +#define STK_PLATFORM_REMOVE_FILE_ERROR 4 + #if defined(__linux__) || defined(_WIN32) #define STK_EVENT_BUFFER 4096 #endif diff --git a/src/platform.c b/src/platform.c index 74c2375..f678feb 100644 --- a/src/platform.c +++ b/src/platform.c @@ -80,28 +80,32 @@ typedef struct { } platform_watch_context_t; #endif -int platform_mkdir(const char *path) +uint8_t platform_mkdir(const char *path) { #ifdef _WIN32 - return CreateDirectoryA(path, NULL) ? 0 : -1; + return CreateDirectoryA(path, NULL) ? STK_PLATFORM_OPERATION_SUCCESS + : STK_PLATFORM_MKDIR_ERROR; #else - return mkdir(path, 0755); + return mkdir(path, 0755) == 0 ? STK_PLATFORM_OPERATION_SUCCESS + : STK_PLATFORM_MKDIR_ERROR; #endif } int platform_remove_file(const char *path) { #ifdef _WIN32 - return DeleteFileA(path) ? 0 : -1; + return DeleteFileA(path) ? STK_PLATFORM_OPERATION_SUCCESS + : STK_PLATFORM_REMOVE_FILE_ERROR; #else - return unlink(path); + return unlink(path) == 0 ? STK_PLATFORM_OPERATION_SUCCESS + : STK_PLATFORM_REMOVE_FILE_ERROR; #endif } -int platform_copy_file(const char *from, const char *to) +uint8_t platform_copy_file(const char *from, const char *to) { char buf[STK_PATH_MAX_OS]; - int ret = -1; + int ret = STK_PLATFORM_FILE_COPY_ERROR; #ifdef _WIN32 sprintf(buf, "%s.tmp", to); if (CopyFileA(from, buf, FALSE)) { @@ -125,7 +129,7 @@ int platform_copy_file(const char *from, const char *to) while ((n = fread(buf, 1, sizeof(buf), src)) > 0) fwrite(buf, 1, n, dst); - ret = 0; + ret = STK_PLATFORM_OPERATION_SUCCESS; cleanup: if (src) @@ -137,7 +141,7 @@ cleanup: return ret; } -int platform_remove_dir(const char *path) +uint8_t platform_remove_dir(const char *path) { #ifdef _WIN32 WIN32_FIND_DATAA fd; @@ -161,7 +165,8 @@ int platform_remove_dir(const char *path) FindClose(h); remove_dir: - return RemoveDirectoryA(path) ? 0 : -1; + return RemoveDirectoryA(path) ? STK_PLATFORM_OPERATION_SUCCESS + : STK_PLATFORM_REMOVE_DIR_ERROR; #else DIR *dir; struct dirent *entry; @@ -169,7 +174,7 @@ remove_dir: dir = opendir(path); if (!dir) - return -1; + return STK_PLATFORM_REMOVE_DIR_ERROR; loop: entry = readdir(dir); @@ -185,7 +190,8 @@ loop: loop_end: closedir(dir); - return rmdir(path); + return rmdir(path) == 0 ? STK_PLATFORM_OPERATION_SUCCESS + : STK_PLATFORM_REMOVE_DIR_ERROR; #endif } @@ -426,6 +432,7 @@ void *platform_directory_watch_start(const char *path) return NULL; strncpy(ctx->path, path, STK_PATH_MAX - 1); + #ifdef _WIN32 ctx->watch.change_handle = CreateFileA(path, FILE_LIST_DIRECTORY, @@ -433,12 +440,12 @@ void *platform_directory_watch_start(const char *path) NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (ctx->watch.change_handle == INVALID_HANDLE_VALUE) - goto done; + goto error_cleanup; sprintf(s, "%s\\*", path); h = FindFirstFileA(s, &fd); if (h == INVALID_HANDLE_VALUE) - goto done; + goto error_cleanup; do { if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && @@ -452,11 +459,11 @@ void *platform_directory_watch_start(const char *path) ctx->snaps = malloc(count * sizeof(platform_snapshot_t)); if (!ctx->snaps) - goto done; + goto error_cleanup; h = FindFirstFileA(s, &fd); if (h == INVALID_HANDLE_VALUE) - goto done; + goto error_cleanup; do { if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && @@ -472,7 +479,6 @@ void *platform_directory_watch_start(const char *path) ctx->count = i; #else - ctx->watch.k.kq = kqueue(); ctx->watch.k.dir_fd = open(path, O_RDONLY); d = opendir(path); @@ -531,6 +537,17 @@ bsd_setup: done: #endif return ctx; + +#ifdef _WIN32 +error_cleanup: + if (ctx) { + if (ctx->watch.change_handle != INVALID_HANDLE_VALUE) + CloseHandle(ctx->watch.change_handle); + free(ctx->snaps); + free(ctx); + } + return NULL; +#endif #endif } diff --git a/src/stk.c b/src/stk.c index 037271c..15a8f58 100644 --- a/src/stk.c +++ b/src/stk.c @@ -75,11 +75,28 @@ int stk_init(void) char tmp_path[STK_PATH_MAX_OS]; int load_result; - platform_mkdir(stk_tmp_dir); + if (platform_mkdir(stk_tmp_dir) != STK_PLATFORM_OPERATION_SUCCESS) { + char (*test_scan)[STK_PATH_MAX]; + size_t test_count; + + test_scan = + platform_directory_init_scan(stk_tmp_dir, &test_count); + if (test_scan) + free(test_scan); + if (!test_scan && test_count == 0) { + stk_log(stderr, + "[stk] FATAL: Cannot create temp directory: %s", + stk_tmp_dir); + return STK_INIT_TMPDIR_ERROR; + } + } + files = platform_directory_init_scan(stk_mod_dir, &file_count); - if (file_count > 0 && stk_module_init_memory(file_count) != 0) - return -1; + if (file_count > 0 && stk_module_init_memory(file_count) != 0) { + stk_log(stderr, "[stk] FATAL: Memory allocation failed"); + return STK_INIT_MEMORY_ERROR; + } if (!files) goto scanned; @@ -88,7 +105,8 @@ int stk_init(void) build_path(full_path, sizeof(full_path), stk_mod_dir, files[i]); build_path(tmp_path, sizeof(tmp_path), stk_tmp_dir, files[i]); - if (platform_copy_file(full_path, tmp_path) != 0) { + if (platform_copy_file(full_path, tmp_path) != + STK_PLATFORM_OPERATION_SUCCESS) { stk_log(stderr, "[stk] Failed to copy %s to temp directory", files[i]); @@ -113,12 +131,20 @@ int stk_init(void) scanned: watch_handle = platform_directory_watch_start(stk_mod_dir); + if (!watch_handle) { + stk_log(stderr, + "[stk] FATAL: Cannot start directory watch on %s", + stk_mod_dir); + stk_module_unload_all(); + return STK_INIT_WATCH_ERROR; + } + stk_log(stdout, "[stk] stk v%s initialized! Loaded %lu mod%s from %s/", STK_VERSION_STRING, module_count, module_count != 1 ? "s" : "", stk_mod_dir); stk_initialized = 1; - return 0; + return STK_INIT_SUCCESS; } void stk_shutdown(void) @@ -129,7 +155,14 @@ void stk_shutdown(void) } stk_module_unload_all(); - platform_remove_dir(stk_tmp_dir); + + if (platform_remove_dir(stk_tmp_dir) != + STK_PLATFORM_OPERATION_SUCCESS) { + stk_log(stderr, + "[stk] Warning: failed to remove temp directory %s", + stk_tmp_dir); + } + stk_initialized = 0; stk_log(stdout, "[stk] stk shutdown"); } @@ -220,7 +253,8 @@ begin_operations: build_path(tmp_path, sizeof(tmp_path), stk_tmp_dir, file_list[file_index]); - if (platform_copy_file(full_path, tmp_path) != 0) { + if (platform_copy_file(full_path, tmp_path) != + STK_PLATFORM_OPERATION_SUCCESS) { stk_log(stderr, "[stk] Failed to copy %s for reload", file_list[file_index]); continue; @@ -244,7 +278,8 @@ begin_operations: build_path(tmp_path, sizeof(tmp_path), stk_tmp_dir, file_list[file_index]); - if (platform_copy_file(full_path, tmp_path) != 0) { + if (platform_copy_file(full_path, tmp_path) != + STK_PLATFORM_OPERATION_SUCCESS) { stk_log(stderr, "[stk] Failed to copy %s for loading", file_list[file_index]); continue; @@ -275,7 +310,8 @@ append_modules: build_path(tmp_path, sizeof(tmp_path), stk_tmp_dir, file_list[file_index]); - if (platform_copy_file(full_path, tmp_path) != 0) { + if (platform_copy_file(full_path, tmp_path) != + STK_PLATFORM_OPERATION_SUCCESS) { stk_log(stderr, "[stk] Failed to copy %s for loading", file_list[file_index]); continue;