12 Commits

Author SHA1 Message Date
anth64 3f7f216c92 chore: bump version to 0.1.1 and update documentation 2026-02-14 17:25:20 +01:00
anth64 70d50dda92 fix(logging): correct log level severity order
Reverse enum so lower values = less severe, higher = more severe.
This makes the filter check (level < min_log_level) work correctly.

- DEBUG (0) - least severe, filtered by default
- INFO (1) - default minimum level
- WARN (2) - warnings and above
- ERROR (3) - most severe, always shown

Fixes incorrect filtering where ERROR/WARN were being blocked.
2026-02-14 17:22:39 +01:00
anth64 70e9ec2fc3 chore: bump version to 0.1.0 and update documentation
- Update stk_version.h: 0.0.4 → 0.1.0
- Add CHANGELOG entry for 0.1.0 release
  - C89 compliance fixes
  - Flags bitfield system
  - Enhanced logging system
- Update README project status and API reference
- Document new logging functions and configuration
2026-02-14 17:06:50 +01:00
anth64 110b7ffca8 feat(logging)!: add enhanced logging system
BREAKING CHANGE: stk_log() signature changed to require log level

- Add log levels (ERROR, WARN, INFO, DEBUG) with runtime filtering
- Add timestamps (yyyy-mm-dd HH:MM:SS.mmm format)
- Add stk_set_log_output(), stk_set_log_prefix(), stk_set_log_level()
- Platform-specific timestamp: GetLocalTime (Windows), gettimeofday (POSIX)
- Consolidate duplicate platform includes into POSIX common block
- Setting log output to NULL disables logging.

Default: INFO level, "stk" prefix, stdout output. All internal messages
use appropriate severity levels. Single byte flag controls enable/disable.
2026-02-14 16:59:56 +01:00
anth64 26fb19a7f5 feat(core): add flags bitfield system
- Replace stk_initialized with stk_flags bitfield
- Add STK_FLAG_INITIALIZED (0x01) and STK_FLAG_LOGGING_ENABLED (0x02)
- Add stk_set_logging_enabled() and stk_is_logging_enabled() API
- Single byte for all boolean state and settings

Logging enabled by default, packs all flags into one byte for efficiency.
2026-02-14 12:39:13 +01:00
anth64 bcb1795218 fix(core)!: enforce strict C89 compliance
BREAKING CHANGE: Public API now uses unsigned char instead of uint8_t

- Remove stdint.h dependency (C99 feature, not C89, I am a fucking idiot)
- Replace uint8_t with unsigned char throughout codebase
- Affects stk_init() return type and internal functions
- Corrects unintended C99 dependency, restoring intended C89 compliance
2026-02-14 11:41:41 +01:00
anth64 fb0d8adb8f chore: bump version to 0.0.4 2026-02-11 00:22:59 +01:00
anth64 2c4d27f915 fix(linux): prevent segfault from invalid module indices during rapid reloads
When spamming file changes rapidly, inotify can report stale UNLOAD/RELOAD
events for modules that were already unloaded by previous events in the same
poll cycle. This caused is_mod_loaded() to return -1, which was then cast to
size_t (18446744073709551615) and used as an array index, causing segfaults.

Additionally, event counts were calculated before validation, causing loops to
run more iterations than valid indices were populated, reading garbage values.

Changes:
- stk.c: Check if is_mod_loaded() returns valid index (>= 0) before adding
  to unload/reload lists
- stk.c: Reset and recalculate counts after populating arrays with only valid
  indices to prevent loop overrun
- Skip processing events for modules that are no longer loaded

This completes the Linux stability fixes started in v0.0.2.
2026-02-11 00:17:33 +01:00
anth64 96fb957991 chore: bump version to 0.0.3 2026-02-09 23:03:35 +01:00
anth64 0cbee45ad2 fix(platform): replace strncpy with memmove to silence -Wrestrict warning 2026-02-09 22:53:42 +01:00
anth64 142a61a843 chore: bump version to 0.0.2 2026-02-09 22:32:55 +01:00
anth64 f83f2051ef fix(linux): prevent segfaults during rapid module reloads
Fixes multiple issues causing segfaults when hot-reloading modules
on Linux, particularly when file changes are detected rapidly:

- Enable is_file_ready() check on Linux to prevent loading partially-written
  shared libraries (previously only used on Windows/BSD)
- Fix event deduplication on Linux to actually remove duplicate inotify events
  instead of just marking them, preventing double-free on same module
- Reorder reload operations to unload old module only after successfully
  copying new version, avoiding invalid state when copy fails

Changes:
- platform.c: Remove __linux__ guards around is_file_ready() function
- platform.c: Add compaction step after deduplication to remove -1 entries
- stk.c: Move module unload to after platform_copy_file() in reload loop

These changes make Linux hot-reload as robust as Windows/BSD implementations.
2026-02-09 22:25:02 +01:00
10 changed files with 405 additions and 182 deletions
+73 -1
View File
@@ -7,6 +7,73 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.1.1] - 2026-02-15
### Fixed
- **Logging**: Corrected log level severity order in enum
- Reversed order so DEBUG (0) < INFO (1) < WARN (2) < ERROR (3)
- Fixes filtering logic where ERROR/WARN were incorrectly blocked
- Default INFO level now properly shows INFO, WARN, and ERROR while filtering DEBUG
## [0.1.0] - 2026-02-15
### Fixed
- **C89 Compliance**: Removed stdint.h dependency (C99 feature)
- Replaced all uint8_t with unsigned char throughout codebase
- Ensures strict C89 compliance for maximum portability
### Added
- **Flags system**: Centralized bitfield for boolean state and settings
- Replaces stk_initialized with stk_flags for efficient memory usage
- Single byte packs all boolean flags (STK_FLAG_INITIALIZED, STK_FLAG_LOGGING_ENABLED)
- Runtime-changeable logging control via stk_set_logging_enabled()
- **Enhanced logging system**: Complete rewrite with modern features
- Log levels: ERROR, WARN, INFO, DEBUG with runtime filtering
- Timestamps: yyyy-mm-dd HH:MM:SS.mmm format (platform-specific implementation)
- Configurable log output stream (stk_set_log_output)
- Configurable log prefix (stk_set_log_prefix, defaults to "stk")
- Configurable minimum log level (stk_set_log_level, defaults to INFO)
- Platform abstraction for timestamps (GetLocalTime on Windows, gettimeofday on POSIX)
### Changed
- **BREAKING**: stk_log() signature changed from stk_log(FILE *fp, ...) to stk_log(stk_log_level_t level, ...)
- **BREAKING**: All uint8_t types replaced with unsigned char
- All internal STK messages now use appropriate log levels
- Setting log output to NULL disabls logging
### Notes
- This release completes Phase 1 logging improvements
- Dependency management still in progress for Phase 1 completion
## [0.0.4] - 2026-02-11
### Fixed
- **Linux**: Fixed segfault from invalid module indices during extremely rapid file changes
- Added validation check to skip stale UNLOAD/RELOAD events for already-unloaded modules
- Prevents is_mod_loaded() returning -1 from being used as array index (SIZE_MAX)
- Fixed event count mismatch where loops would run more iterations than valid indices populated
- Completes the Linux hot-reload stability fixes from v0.0.2
## [0.0.3] - 2026-02-10
### Fixed
- **Compilation**: Fixed GCC `-Wrestrict` warning in Linux directory watching code by replacing `strncpy` with `memmove` for overlapping memory operations
- Ensures clean compilation without warnings while maintaining identical runtime behavior
- Uses semantically correct function for moving memory within the same buffer
## [0.0.2] - 2026-02-09
### Fixed
- **Linux**: Fixed segfaults during rapid module reloads when file changes are detected in quick succession
- Enabled file readiness checks on Linux (previously only used on Windows/BSD) to prevent loading partially-written shared libraries
- Fixed inotify event deduplication to actually remove duplicate events instead of just marking them
- Reordered reload operations to only unload old module after successfully copying new version, preventing invalid state when copy fails
- **All platforms**: Improved reload safety by deferring module unload until after successful file copy
### Changed
- Made `is_file_ready()` check available on all Unix platforms (was previously excluded on Linux)
## [0.0.1] - 2026-02-01
### Added
@@ -25,5 +92,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Dependency management and versioning not yet implemented
- API is unstable and subject to change in future releases
[Unreleased]: https://github.com/anth64/stk/compare/v0.0.1...HEAD
[Unreleased]: https://github.com/anth64/stk/compare/v0.1.1...HEAD
[0.1.1]: https://github.com/anth64/stk/releases/tag/v0.1.1
[0.1.0]: https://github.com/anth64/stk/releases/tag/v0.1.0
[0.0.4]: https://github.com/anth64/stk/releases/tag/v0.0.4
[0.0.3]: https://github.com/anth64/stk/releases/tag/v0.0.3
[0.0.2]: https://github.com/anth64/stk/releases/tag/v0.0.2
[0.0.1]: https://github.com/anth64/stk/releases/tag/v0.0.1
+15 -5
View File
@@ -156,7 +156,7 @@ stk_init();
### API Reference
#### Initialization
- `uint8_t stk_init(void)` - Initialize stk, returns `STK_INIT_SUCCESS` on success
- `unsigned char stk_init(void)` - Initialize stk, returns `STK_INIT_SUCCESS` on success
- `void stk_shutdown(void)` - Shutdown and cleanup all modules
#### Runtime
@@ -169,21 +169,31 @@ stk_init();
- `void stk_set_module_init_fn(const char *name)` - Set module init function name
- `void stk_set_module_shutdown_fn(const char *name)` - Set module shutdown function name
#### Logging
- `void stk_set_logging_enabled(unsigned char enabled)` - Enable/disable all logging
- `unsigned char stk_is_logging_enabled(void)` - Query logging state
- `void stk_set_log_output(FILE *fp)` - Set log output stream (default: stdout, NULL disables)
- `void stk_set_log_prefix(const char *prefix)` - Set log prefix (default: "stk")
- `void stk_set_log_level(stk_log_level_t level)` - Set minimum log level (default: INFO)
**Log Levels:** `STK_LOG_ERROR`, `STK_LOG_WARN`, `STK_LOG_INFO`, `STK_LOG_DEBUG`
---
## Project Status
**Current Version:** 0.0.1 (Pre-release)
**Current Version:** 0.1.1 (Pre-release)
This is an early release proving the core hot-reload foundation. Phase 1 is still in progress.
This release brings C89 compliance fixes and a complete logging system rewrite with levels, timestamps, and runtime configuration.
### What Works
- Cross-platform module loading and hot-reloading
- File watching (inotify/kqueue/FindFirstFile)
- Basic error handling
- Robust hot-reload even during extremely rapid file changes
- Enhanced logging with levels, timestamps, and filtering
- Runtime-configurable logging behavior
### In Progress (Phase 1)
- Complete logging system (log levels, verbosity, output configuration)
- Module metadata (name, version, description)
- Dependency management and versioning
+8 -2
View File
@@ -2,10 +2,10 @@
#define STK_H
#include "stk_version.h"
#include <stdint.h>
#include <stdlib.h>
/* Buffers */
#define STK_LOG_PREFIX_BUFFER 64
#define STK_MOD_DIR_BUFFER 256
#define STK_MOD_ID_BUFFER 64
#define STK_PATH_MAX 256
@@ -31,6 +31,10 @@
#define STK_PLATFORM_REMOVE_DIR_ERROR 3
#define STK_PLATFORM_REMOVE_FILE_ERROR 4
/* Settings flags */
#define STK_FLAG_INITIALIZED 0x01
#define STK_FLAG_LOGGING_ENABLED 0x02
#if defined(__linux__) || defined(_WIN32)
#define STK_EVENT_BUFFER 4096
#endif
@@ -55,7 +59,7 @@ typedef enum {
STK_MOD_RELOAD
} stk_module_event_t;
uint8_t stk_init(void);
unsigned char stk_init(void);
void stk_shutdown(void);
size_t stk_module_count(void);
size_t stk_poll(void);
@@ -63,6 +67,8 @@ void stk_set_mod_dir(const char *path);
void stk_set_tmp_dir_name(const char *name);
void stk_set_module_init_fn(const char *name);
void stk_set_module_shutdown_fn(const char *name);
void stk_set_logging_enabled(unsigned char enabled);
unsigned char stk_is_logging_enabled(void);
#ifdef __cplusplus
}
+12 -1
View File
@@ -7,7 +7,18 @@
extern "C" {
#endif
void stk_log(FILE *fp, const char *fmt, ...);
typedef enum {
STK_LOG_DEBUG,
STK_LOG_INFO,
STK_LOG_WARN,
STK_LOG_ERROR
} stk_log_level_t;
void stk_set_log_output(FILE *fp);
void stk_set_log_prefix(const char *prefix);
void stk_set_log_level(stk_log_level_t min_level);
void stk_log(stk_log_level_t level, const char *fmt, ...);
#ifdef __cplusplus
}
+1 -1
View File
@@ -2,7 +2,7 @@
#define STK_VERSION_H
#define STK_VERSION_MAJOR 0
#define STK_VERSION_MINOR 0
#define STK_VERSION_MINOR 1
#define STK_VERSION_PATCH 1
#define STK_STRINGIFY_HELPER(x) #x
+8 -9
View File
@@ -1,6 +1,5 @@
#include "platform.h"
#include "stk.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -18,7 +17,7 @@ void **stk_handles = NULL;
stk_init_mod_func *stk_inits = NULL;
stk_shutdown_mod_func *stk_shutdowns = NULL;
extern uint8_t stk_initialized;
extern unsigned char stk_flags;
static char stk_mod_init_name[STK_MOD_FUNC_NAME_BUFFER] = "stk_mod_init";
static char stk_mod_shutdown_name[STK_MOD_FUNC_NAME_BUFFER] =
@@ -43,7 +42,7 @@ void extract_module_id(const char *path, char *out_id)
*dot = '\0';
}
uint8_t is_valid_module_file(const char *filename)
unsigned char is_valid_module_file(const char *filename)
{
const char *ext;
size_t name_len;
@@ -72,7 +71,7 @@ int is_mod_loaded(const char *module_name)
return -1;
}
uint8_t stk_module_load(const char *path, int index)
unsigned char stk_module_load(const char *path, int index)
{
void *handle;
stk_init_mod_func init_func;
@@ -121,7 +120,7 @@ uint8_t stk_module_load(const char *path, int index)
return STK_MOD_INIT_SUCCESS;
}
uint8_t stk_module_load_init(const char *path, int index)
unsigned char stk_module_load_init(const char *path, int index)
{
int result;
result = stk_module_load(path, index);
@@ -155,7 +154,7 @@ void stk_module_free_memory(void)
stk_shutdowns = NULL;
}
uint8_t stk_module_init_memory(size_t capacity)
unsigned char stk_module_init_memory(size_t capacity)
{
stk_module_ids = malloc(capacity * sizeof(*stk_module_ids));
stk_handles = malloc(capacity * sizeof(void *));
@@ -170,7 +169,7 @@ uint8_t stk_module_init_memory(size_t capacity)
return STK_INIT_SUCCESS;
}
uint8_t stk_module_realloc_memory(size_t new_capacity)
unsigned char stk_module_realloc_memory(size_t new_capacity)
{
char (*new_module_ids)[STK_MOD_ID_BUFFER] = NULL;
void **new_handles = NULL;
@@ -254,7 +253,7 @@ void stk_module_unload_all(void)
void stk_set_module_init_fn(const char *name)
{
if (!name || stk_initialized)
if (!name || (stk_flags & STK_FLAG_INITIALIZED))
return;
strncpy(stk_mod_init_name, name, STK_MOD_FUNC_NAME_BUFFER - 1);
@@ -263,7 +262,7 @@ void stk_set_module_init_fn(const char *name)
void stk_set_module_shutdown_fn(const char *name)
{
if (!name || stk_initialized)
if (!name || (stk_flags & STK_FLAG_INITIALIZED))
return;
strncpy(stk_mod_shutdown_name, name, STK_MOD_FUNC_NAME_BUFFER - 1);
+59 -31
View File
@@ -1,39 +1,34 @@
#include "stk.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef _WIN32
#include <dirent.h>
#include <dlfcn.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#endif
#if defined(__linux__)
#include <dirent.h>
#include <dlfcn.h>
#include <sys/inotify.h>
#include <sys/stat.h>
#include <unistd.h>
#elif defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <dirent.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <sys/event.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#endif
int is_mod_loaded(const char *module_name);
uint8_t is_valid_module_file(const char *filename);
unsigned char is_valid_module_file(const char *filename);
void extract_module_id(const char *path, char *out_id);
#ifndef __linux__
static uint8_t is_file_ready(const char *dir_path, const char *filename)
static unsigned char is_file_ready(const char *dir_path, const char *filename)
{
char full_path[STK_PATH_MAX_OS];
#ifdef _WIN32
@@ -80,6 +75,7 @@ static uint8_t is_file_ready(const char *dir_path, const char *filename)
#endif
}
#ifndef __linux__
typedef struct {
char filename[STK_PATH_MAX];
#ifdef _WIN32
@@ -108,7 +104,7 @@ typedef struct {
} platform_watch_context_t;
#endif
uint8_t platform_mkdir(const char *path)
unsigned char platform_mkdir(const char *path)
{
#ifdef _WIN32
return CreateDirectoryA(path, NULL) ? STK_PLATFORM_OPERATION_SUCCESS
@@ -130,7 +126,7 @@ int platform_remove_file(const char *path)
#endif
}
uint8_t platform_copy_file(const char *from, const char *to)
unsigned char platform_copy_file(const char *from, const char *to)
{
char buf[STK_PATH_MAX_OS];
int ret = STK_PLATFORM_FILE_COPY_ERROR;
@@ -146,7 +142,6 @@ uint8_t platform_copy_file(const char *from, const char *to)
FILE *src = NULL, *dst = NULL;
char tmp_path[STK_PATH_MAX_OS];
size_t n;
#ifndef __linux__
char dir_path[STK_PATH_MAX_OS];
const char *filename;
@@ -163,7 +158,6 @@ uint8_t platform_copy_file(const char *from, const char *to)
if (!is_file_ready(dir_path, filename))
goto done;
#endif
sprintf(tmp_path, "%s.tmp", to);
@@ -205,7 +199,7 @@ done:
return ret;
}
uint8_t platform_remove_dir(const char *path)
unsigned char platform_remove_dir(const char *path)
{
#ifdef _WIN32
WIN32_FIND_DATAA fd;
@@ -286,11 +280,10 @@ void *platform_get_symbol(void *h, const char *s)
#endif
}
char (*platform_directory_init_scan(const char *dir_path,
size_t *out_count))[STK_PATH_MAX]
{
char (*platform_directory_init_scan(const char *dir_path, size_t *out_count))
[STK_PATH_MAX] {
size_t count = 0, i = 0, name_len;
char(*list)[STK_PATH_MAX] = NULL;
char (*list)[STK_PATH_MAX] = NULL;
#ifdef _WIN32
WIN32_FIND_DATAA fd;
HANDLE h;
@@ -338,9 +331,9 @@ char (*platform_directory_init_scan(const char *dir_path,
FindClose(h);
goto exit;
create_and_exit:
create_and_exit:
platform_mkdir(dir_path);
exit:
exit:
*out_count = i;
return list;
#else
@@ -353,7 +346,7 @@ exit:
if (!d)
goto create_and_exit;
count_loop:
count_loop:
e = readdir(d);
if (!e)
goto count_done;
@@ -368,7 +361,7 @@ count_loop:
count++;
goto count_loop;
count_done:
count_done:
if (count == 0)
goto close_and_exit;
@@ -377,7 +370,7 @@ count_done:
if (!list)
goto close_and_exit;
fill_loop:
fill_loop:
e = readdir(d);
if (!e || i >= count)
goto close_and_exit;
@@ -397,17 +390,17 @@ fill_loop:
list[i - 1][name_len] = '\0';
goto fill_loop;
create_and_exit:
create_and_exit:
platform_mkdir(dir_path);
*out_count = 0;
return NULL;
close_and_exit:
close_and_exit:
closedir(d);
*out_count = i;
return list;
#endif
}
}
#if !defined(__linux__) && !defined(_WIN32)
static void update_watches(platform_watch_context_t *ctx)
@@ -665,7 +658,7 @@ stk_module_event_t *platform_directory_watch_check(
int fd = (int)(long)handle;
char buf[STK_EVENT_BUFFER];
ssize_t len;
size_t index = 0, count = 0, i;
size_t index = 0, count = 0, i, write_index;
stk_module_event_t *evs;
char *ptr, *end;
struct inotify_event *e;
@@ -763,6 +756,19 @@ stk_module_event_t *platform_directory_watch_check(
}
}
write_index = 0;
for (i = 0; i < index; ++i) {
if (evs[i] != -1) {
if (write_index != i) {
evs[write_index] = evs[i];
memmove((*file_list)[write_index],
(*file_list)[i], STK_PATH_MAX);
}
write_index++;
}
}
index = write_index;
*out_count = index;
return evs;
@@ -1000,3 +1006,25 @@ no_change:
return NULL;
#endif
}
void platform_get_timestamp(char *buffer, size_t size)
{
#ifdef _WIN32
SYSTEMTIME st;
GetLocalTime(&st);
sprintf(buffer, "%04d-%02d-%02d %02d:%02d:%02d.%03d", st.wYear,
st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond,
st.wMilliseconds);
#else
struct timeval tv;
struct tm *tm_info;
gettimeofday(&tv, NULL);
tm_info = localtime(&tv.tv_sec);
sprintf(buffer, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
tm_info->tm_year + 1900, tm_info->tm_mon + 1, tm_info->tm_mday,
tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec,
(int)(tv.tv_usec / 1000));
#endif
}
+63 -39
View File
@@ -1,7 +1,6 @@
#include "stk.h"
#include "platform.h"
#include "stk_log.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -16,7 +15,7 @@ extern char (*stk_module_ids)[STK_MOD_ID_BUFFER];
extern size_t module_count;
uint8_t stk_initialized = 0;
unsigned char stk_flags = STK_FLAG_LOGGING_ENABLED;
static char stk_mod_dir[STK_PATH_MAX_OS] = "mods";
static char stk_tmp_name[STK_MOD_ID_BUFFER] = ".tmp";
@@ -30,18 +29,18 @@ void platform_directory_watch_stop(void *handle);
stk_module_event_t *platform_directory_watch_check(
void *handle, char (**file_list)[STK_PATH_MAX], size_t *out_count,
char (*loaded_module_ids)[STK_MOD_ID_BUFFER], const size_t loaded_count);
uint8_t platform_mkdir(const char *path);
uint8_t platform_copy_file(const char *from, const char *to);
uint8_t platform_remove_dir(const char *path);
unsigned char platform_mkdir(const char *path);
unsigned char platform_copy_file(const char *from, const char *to);
unsigned char platform_remove_dir(const char *path);
void extract_module_id(const char *path, char *out_id);
int is_mod_loaded(const char *module_id);
size_t stk_module_count(void);
uint8_t stk_module_load(const char *path, int index);
uint8_t stk_module_load_init(const char *path, int index);
uint8_t stk_module_init_memory(size_t capacity);
uint8_t stk_module_realloc_memory(size_t new_capacity);
unsigned char stk_module_load(const char *path, int index);
unsigned char stk_module_load_init(const char *path, int index);
unsigned char stk_module_init_memory(size_t capacity);
unsigned char stk_module_realloc_memory(size_t new_capacity);
void stk_module_unload(size_t index);
void stk_module_unload_all(void);
@@ -70,7 +69,7 @@ static const char *stk_error_string(int error_code)
}
}
uint8_t stk_init(void)
unsigned char stk_init(void)
{
char (*files)[STK_PATH_MAX] = NULL;
size_t file_count, i, successful_loads = 0;
@@ -87,7 +86,7 @@ uint8_t stk_init(void)
if (test_scan)
free(test_scan);
if (!test_scan && test_count == 0) {
stk_log(stderr,
stk_log(STK_LOG_ERROR,
"FATAL: Cannot create temp directory: %s",
stk_tmp_dir);
return STK_INIT_TMPDIR_ERROR;
@@ -97,7 +96,7 @@ uint8_t stk_init(void)
files = platform_directory_init_scan(stk_mod_dir, &file_count);
if (file_count > 0 && stk_module_init_memory(file_count) != 0) {
stk_log(stderr, "FATAL: Memory allocation failed");
stk_log(STK_LOG_ERROR, "FATAL: Memory allocation failed");
return STK_INIT_MEMORY_ERROR;
}
@@ -110,7 +109,8 @@ uint8_t stk_init(void)
if (platform_copy_file(full_path, tmp_path) !=
STK_PLATFORM_OPERATION_SUCCESS) {
stk_log(stderr, "Failed to copy %s to temp directory",
stk_log(STK_LOG_ERROR,
"Failed to copy %s to temp directory",
files[i]);
continue;
}
@@ -118,7 +118,7 @@ uint8_t stk_init(void)
load_result = stk_module_load_init(tmp_path, successful_loads);
if (load_result != STK_MOD_INIT_SUCCESS) {
stk_log(stderr, "Failed to load module %s: %s",
stk_log(STK_LOG_ERROR, "Failed to load module %s: %s",
files[i], stk_error_string(load_result));
} else {
successful_loads++;
@@ -134,17 +134,18 @@ uint8_t stk_init(void)
scanned:
watch_handle = platform_directory_watch_start(stk_mod_dir);
if (!watch_handle) {
stk_log(stderr, "FATAL: Cannot start directory watch on %s",
stk_log(STK_LOG_ERROR,
"FATAL: Cannot start directory watch on %s",
stk_mod_dir);
stk_module_unload_all();
return STK_INIT_WATCH_ERROR;
}
stk_log(stdout, "stk v%s initialized! Loaded %lu mod%s from %s/",
stk_log(STK_LOG_INFO, "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;
stk_flags |= STK_FLAG_INITIALIZED;
return STK_INIT_SUCCESS;
}
@@ -159,12 +160,13 @@ void stk_shutdown(void)
if (platform_remove_dir(stk_tmp_dir) !=
STK_PLATFORM_OPERATION_SUCCESS) {
stk_log(stderr, "Warning: failed to remove temp directory %s",
stk_log(STK_LOG_WARN,
"Warning: failed to remove temp directory %s",
stk_tmp_dir);
}
stk_initialized = 0;
stk_log(stdout, "stk shutdown");
stk_flags &= ~STK_FLAG_INITIALIZED;
stk_log(STK_LOG_INFO, "stk shutdown");
}
size_t stk_poll(void)
@@ -172,8 +174,7 @@ size_t stk_poll(void)
char (*file_list)[STK_PATH_MAX] = NULL;
stk_module_event_t *events = NULL;
size_t i, file_count = 0, reload_count = 0, load_count = 0,
unload_count = 0, reload_index = 0, load_index = 0,
unload_index = 0;
unload_count = 0;
int *reloaded_mod_indices = NULL, *reloaded_mod_file_indices = NULL,
*unloaded_mod_indices = NULL, *loaded_mod_indices = NULL;
size_t remaining_loads, new_capacity, holes_to_fill;
@@ -208,20 +209,31 @@ size_t stk_poll(void)
unloaded_mod_indices = malloc(unload_count * sizeof(int));
loaded_mod_indices = malloc(load_count * sizeof(int));
reload_count = 0;
unload_count = 0;
load_count = 0;
for (i = 0; i < file_count; ++i) {
int mod_index;
extract_module_id(file_list[i], mod_id);
switch (events[i]) {
case STK_MOD_LOAD:
loaded_mod_indices[load_index++] = i;
loaded_mod_indices[load_count++] = i;
break;
case STK_MOD_RELOAD:
reloaded_mod_file_indices[reload_index] = i;
reloaded_mod_indices[reload_index++] =
is_mod_loaded(mod_id);
mod_index = is_mod_loaded(mod_id);
if (mod_index >= 0) {
reloaded_mod_file_indices[reload_count] = i;
reloaded_mod_indices[reload_count] = mod_index;
reload_count++;
}
break;
case STK_MOD_UNLOAD:
unloaded_mod_indices[unload_index++] =
is_mod_loaded(mod_id);
mod_index = is_mod_loaded(mod_id);
if (mod_index >= 0) {
unloaded_mod_indices[unload_count] = mod_index;
unload_count++;
}
break;
}
}
@@ -242,9 +254,6 @@ begin_operations:
for (i = 0; i < unload_count; ++i)
stk_module_unload(unloaded_mod_indices[i]);
for (i = 0; i < reload_count; ++i)
stk_module_unload(reloaded_mod_indices[i]);
for (i = 0; i < reload_count; ++i) {
int file_index = reloaded_mod_file_indices[i];
int mod_index = reloaded_mod_indices[i];
@@ -256,14 +265,16 @@ begin_operations:
if (platform_copy_file(full_path, tmp_path) !=
STK_PLATFORM_OPERATION_SUCCESS) {
stk_log(stderr, "Failed to copy %s for reload",
stk_log(STK_LOG_ERROR, "Failed to copy %s for reload",
file_list[file_index]);
continue;
}
stk_module_unload(mod_index);
load_result = stk_module_load(tmp_path, mod_index);
if (load_result != STK_MOD_INIT_SUCCESS) {
stk_log(stderr, "Failed to reload module %s: %s",
stk_log(STK_LOG_ERROR, "Failed to reload module %s: %s",
file_list[file_index],
stk_error_string(load_result));
}
@@ -281,14 +292,14 @@ begin_operations:
if (platform_copy_file(full_path, tmp_path) !=
STK_PLATFORM_OPERATION_SUCCESS) {
stk_log(stderr, "Failed to copy %s for loading",
stk_log(STK_LOG_ERROR, "Failed to copy %s for loading",
file_list[file_index]);
continue;
}
load_result = stk_module_load(tmp_path, target_index);
if (load_result != STK_MOD_INIT_SUCCESS) {
stk_log(stderr, "Failed to load module %s: %s",
stk_log(STK_LOG_ERROR, "Failed to load module %s: %s",
file_list[file_index],
stk_error_string(load_result));
}
@@ -313,7 +324,7 @@ append_modules:
if (platform_copy_file(full_path, tmp_path) !=
STK_PLATFORM_OPERATION_SUCCESS) {
stk_log(stderr, "Failed to copy %s for loading",
stk_log(STK_LOG_ERROR, "Failed to copy %s for loading",
file_list[file_index]);
continue;
}
@@ -321,7 +332,7 @@ append_modules:
load_result = stk_module_load(tmp_path, module_count +
successful_appends);
if (load_result != STK_MOD_INIT_SUCCESS) {
stk_log(stderr, "Failed to load module %s: %s",
stk_log(STK_LOG_ERROR, "Failed to load module %s: %s",
file_list[file_index],
stk_error_string(load_result));
} else {
@@ -372,7 +383,7 @@ finish_poll:
void stk_set_mod_dir(const char *path)
{
if (!path || stk_initialized)
if (!path || (stk_flags & STK_FLAG_INITIALIZED))
return;
strncpy(stk_mod_dir, path, STK_PATH_MAX_OS - 1);
@@ -390,7 +401,7 @@ void stk_set_mod_dir(const char *path)
void stk_set_tmp_dir_name(const char *name)
{
if (!name || stk_initialized)
if (!name || (stk_flags & STK_FLAG_INITIALIZED))
return;
strncpy(stk_tmp_name, name, STK_MOD_ID_BUFFER - 1);
@@ -403,3 +414,16 @@ void stk_set_tmp_dir_name(const char *name)
strncat(stk_tmp_dir, stk_tmp_name,
STK_PATH_MAX_OS - strlen(stk_tmp_dir) - 1);
}
void stk_set_logging_enabled(unsigned char enabled)
{
if (enabled)
stk_flags |= STK_FLAG_LOGGING_ENABLED;
else
stk_flags &= ~STK_FLAG_LOGGING_ENABLED;
}
unsigned char stk_is_logging_enabled(void)
{
return (stk_flags & STK_FLAG_LOGGING_ENABLED) != 0;
}
+77 -5
View File
@@ -1,14 +1,86 @@
#include "stk_log.h"
#include "stk.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
void stk_log(FILE *fp, const char *fmt, ...)
#define STK_LOG_TIMESTAMP_BUFFER 32
extern unsigned char stk_flags;
void platform_get_timestamp(char *buffer, size_t size);
static FILE *log_output = NULL;
static char log_prefix[STK_LOG_PREFIX_BUFFER] = "stk";
static stk_log_level_t min_log_level = STK_LOG_INFO;
static const char *get_level_string(stk_log_level_t level)
{
va_list args;
va_start(args, fmt);
char *level_str = "";
switch (level) {
case STK_LOG_ERROR:
level_str = "ERROR";
break;
case STK_LOG_WARN:
level_str = "WARN";
break;
case STK_LOG_INFO:
level_str = "INFO";
break;
case STK_LOG_DEBUG:
level_str = "DEBUG";
break;
}
vfprintf(fp, fmt, args);
fputc('\n', fp);
return level_str;
}
void stk_set_log_output(FILE *fp)
{
if (fp == NULL)
stk_flags &= ~STK_FLAG_LOGGING_ENABLED;
log_output = fp;
}
void stk_set_log_prefix(const char *prefix)
{
if (!prefix) {
log_prefix[0] = '\0';
return;
}
strncpy(log_prefix, prefix, STK_LOG_PREFIX_BUFFER - 1);
log_prefix[STK_LOG_PREFIX_BUFFER - 1] = '\0';
}
void stk_set_log_level(stk_log_level_t level) { min_log_level = level; }
void stk_log(stk_log_level_t level, const char *fmt, ...)
{
FILE *output;
const char *level_str;
char timestamp[STK_LOG_TIMESTAMP_BUFFER];
va_list args;
if (!(stk_flags & STK_FLAG_LOGGING_ENABLED))
return;
if (level < min_log_level)
return;
output = log_output ? log_output : stdout;
level_str = get_level_string(level);
platform_get_timestamp(timestamp, sizeof(timestamp));
if (log_prefix[0] != '\0')
fprintf(output, "%s [%s] [%s] ", timestamp, log_prefix,
level_str);
else
fprintf(output, "%s [%s] ", timestamp, level_str);
va_start(args, fmt);
vfprintf(output, fmt, args);
va_end(args);
fputc('\n', output);
}
+5 -4
View File
@@ -31,8 +31,8 @@ void inthand(int signum)
int main(int argc, char **argv)
{
uint8_t init_result;
uint64_t iterations = 0;
unsigned char init_result;
size_t iterations = 0;
printf("stk test - CTRL+C to exit\n");
@@ -55,12 +55,13 @@ int main(int argc, char **argv)
while (!stop) {
size_t events = stk_poll();
if (events > 0)
printf("Poll: %lu module event(s) detected\n", (unsigned long) events);
printf("Poll: %lu module event(s) detected\n",
(unsigned long)events);
iterations++;
if (iterations % 5 == 0) {
printf("Still running... (iteration %lu)\n",
(unsigned long) iterations);
(unsigned long)iterations);
}
#ifdef _WIN32
Sleep(1000);