diff --git a/Makefile b/Makefile index 2f4f0ea..459e158 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ OBJECTS = $(SOURCES:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o) DEPS = $(OBJECTS:.o=.d) $(CTL_OBJECTS:.o=.d) TARGET = tnt CTL_TARGET = tntctl -CTL_OBJECTS = $(OBJ_DIR)/tntctl.o $(OBJ_DIR)/tntctl_text.o $(OBJ_DIR)/exec_catalog.o $(OBJ_DIR)/common.o $(OBJ_DIR)/i18n.o +CTL_OBJECTS = $(OBJ_DIR)/tntctl.o $(OBJ_DIR)/tntctl_text.o $(OBJ_DIR)/exec_catalog.o $(OBJ_DIR)/common.o $(OBJ_DIR)/config_defaults.o $(OBJ_DIR)/i18n.o TARGETS = $(TARGET) $(CTL_TARGET) PREFIX ?= /usr/local diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 7761def..c338cc1 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -7,6 +7,8 @@ `vX.Y.Z` tag must match `TNT_VERSION` before release assets are built. - Added `make package-publish-check` for verifying Arch/Homebrew source checksums against the final GitHub source archive after a tag exists. +- Added a `config_defaults` module and unit coverage for runtime default + values, env keys, and accepted numeric ranges. - Added a dedicated `tntctl_text` module with unit coverage for local `tntctl` help and validation diagnostics. - Documented the stable SSH exec interface contract, including exit statuses diff --git a/include/common.h b/include/common.h index 93b5b55..962ec2a 100644 --- a/include/common.h +++ b/include/common.h @@ -11,6 +11,8 @@ #include #include +#include "config_defaults.h" + /* Project Metadata */ #define TNT_VERSION "1.0.1" @@ -23,7 +25,6 @@ #define TNT_EXIT_CONFIG 78 /* Configuration constants */ -#define DEFAULT_PORT 2222 #define MAX_MESSAGES 100 #define MAX_USERNAME_LEN 64 #define MAX_MESSAGE_LEN 1024 @@ -31,13 +32,17 @@ #define MAX_COMMAND_OUTPUT_LEN 8192 #define CLIENT_OUTBOX_CAPACITY (128 * 1024) #define CLIENT_OUTBOX_FLUSH_BUDGET 32768 -#define DEFAULT_MAX_CLIENTS 64 -#define MAX_CONFIGURED_CLIENTS 1024 #define LOG_FILE "messages.log" #define MAX_LOG_SIZE (10 * 1024 * 1024) /* 10 MiB */ #define HOST_KEY_FILE "host_key" #define TNT_DEFAULT_STATE_DIR "." -#define DEFAULT_IDLE_TIMEOUT 1800 /* 30 minutes */ + +/* Backward-compatible names for older modules while config_defaults owns the + * actual runtime defaults and accepted ranges. */ +#define DEFAULT_PORT TNT_DEFAULT_PORT +#define DEFAULT_MAX_CLIENTS TNT_DEFAULT_MAX_CONNECTIONS +#define MAX_CONFIGURED_CLIENTS TNT_MAX_CONFIGURED_CLIENTS +#define DEFAULT_IDLE_TIMEOUT TNT_DEFAULT_IDLE_TIMEOUT /* ANSI color codes */ #define ANSI_RESET "\033[0m" diff --git a/include/config_defaults.h b/include/config_defaults.h new file mode 100644 index 0000000..72786dd --- /dev/null +++ b/include/config_defaults.h @@ -0,0 +1,47 @@ +#ifndef CONFIG_DEFAULTS_H +#define CONFIG_DEFAULTS_H + +#include + +#define TNT_STRINGIFY_VALUE(value) #value +#define TNT_STRINGIFY(value) TNT_STRINGIFY_VALUE(value) + +#define TNT_DEFAULT_PORT 2222 +#define TNT_DEFAULT_PORT_TEXT TNT_STRINGIFY(TNT_DEFAULT_PORT) +#define TNT_DEFAULT_MAX_CONNECTIONS 64 +#define TNT_DEFAULT_MAX_CONN_PER_IP 5 +#define TNT_DEFAULT_MAX_CONN_RATE_PER_IP 10 +#define TNT_DEFAULT_RATE_LIMIT_ENABLED 1 +#define TNT_DEFAULT_IDLE_TIMEOUT 1800 + +#define TNT_MIN_PORT 1 +#define TNT_MAX_PORT 65535 +#define TNT_MIN_CONFIGURED_CLIENTS 1 +#define TNT_MAX_CONFIGURED_CLIENTS 1024 +#define TNT_MIN_RATE_LIMIT_ENABLED 0 +#define TNT_MAX_RATE_LIMIT_ENABLED 1 +#define TNT_MIN_IDLE_TIMEOUT 0 +#define TNT_MAX_IDLE_TIMEOUT 86400 +#define TNT_MIN_SSH_LOG_LEVEL 0 +#define TNT_MAX_SSH_LOG_LEVEL 4 + +typedef struct { + const char *env_name; + int fallback; + int min_value; + int max_value; +} tnt_int_config_spec_t; + +extern const tnt_int_config_spec_t TNT_CONFIG_PORT; +extern const tnt_int_config_spec_t TNT_CONFIG_MAX_CONNECTIONS; +extern const tnt_int_config_spec_t TNT_CONFIG_MAX_CONN_PER_IP; +extern const tnt_int_config_spec_t TNT_CONFIG_MAX_CONN_RATE_PER_IP; +extern const tnt_int_config_spec_t TNT_CONFIG_RATE_LIMIT; +extern const tnt_int_config_spec_t TNT_CONFIG_IDLE_TIMEOUT; +extern const tnt_int_config_spec_t TNT_CONFIG_SSH_LOG_LEVEL; + +int tnt_config_env_int(const tnt_int_config_spec_t *spec); +bool tnt_config_parse_int(const char *value, const tnt_int_config_spec_t *spec, + int *out); + +#endif /* CONFIG_DEFAULTS_H */ diff --git a/src/chat_room.c b/src/chat_room.c index ccddeea..e903497 100644 --- a/src/chat_room.c +++ b/src/chat_room.c @@ -1,11 +1,11 @@ #include "chat_room.h" +#include "config_defaults.h" /* Global chat room instance */ chat_room_t *g_room = NULL; static int room_capacity_from_env(void) { - return env_int("TNT_MAX_CONNECTIONS", DEFAULT_MAX_CLIENTS, 1, - MAX_CONFIGURED_CLIENTS); + return tnt_config_env_int(&TNT_CONFIG_MAX_CONNECTIONS); } /* Initialize chat room */ diff --git a/src/cli_text.c b/src/cli_text.c index 87f1e3a..c04c60f 100644 --- a/src/cli_text.c +++ b/src/cli_text.c @@ -1,5 +1,6 @@ #include "cli_text.h" +#include "config_defaults.h" #include "i18n.h" void cli_text_append_help(char *buffer, size_t buf_size, size_t *pos, @@ -12,7 +13,7 @@ void cli_text_append_help(char *buffer, size_t buf_size, size_t *pos, " -d, --state-dir DIR Store host key and logs in DIR\n" " --bind ADDR Bind to ADDR (default: 0.0.0.0)\n" " --public-host HOST Show HOST in startup connection hints\n" - " --max-connections N Global connection limit (default: 64)\n" + " --max-connections N Global connection limit (default: %d)\n" " --max-conn-per-ip N Per-IP concurrent session limit\n" " --max-conn-rate-per-ip N Per-IP connection-rate limit\n" " --rate-limit 0|1 Disable/enable rate-based blocking\n" @@ -28,9 +29,9 @@ void cli_text_append_help(char *buffer, size_t buf_size, size_t *pos, " TNT_STATE_DIR State directory\n" " TNT_ACCESS_TOKEN Require this password for SSH auth\n" " TNT_LANG UI language: en or zh (default: locale)\n" - " TNT_MAX_CONNECTIONS Global connection limit (default: 64)\n" + " TNT_MAX_CONNECTIONS Global connection limit (default: %d)\n" " TNT_RATE_LIMIT Set to 0 to disable rate limiting\n" - " TNT_IDLE_TIMEOUT Idle disconnect timeout in seconds (default: 1800)\n", + " TNT_IDLE_TIMEOUT Idle disconnect timeout in seconds (default: %d)\n", "tnt %s - 匿名 SSH 聊天服务器\n\n" "用法: %s [options]\n\n" "选项:\n" @@ -38,7 +39,7 @@ void cli_text_append_help(char *buffer, size_t buf_size, size_t *pos, " -d, --state-dir DIR 将主机密钥和日志存放在 DIR\n" " --bind ADDR 绑定到 ADDR (默认: 0.0.0.0)\n" " --public-host HOST 在启动提示中显示 HOST\n" - " --max-connections N 全局连接数限制 (默认: 64)\n" + " --max-connections N 全局连接数限制 (默认: %d)\n" " --max-conn-per-ip N 单 IP 并发会话限制\n" " --max-conn-rate-per-ip N 单 IP 连接速率限制\n" " --rate-limit 0|1 禁用/启用速率封禁\n" @@ -54,16 +55,19 @@ void cli_text_append_help(char *buffer, size_t buf_size, size_t *pos, " TNT_STATE_DIR 状态目录\n" " TNT_ACCESS_TOKEN 要求 SSH 认证使用此密码\n" " TNT_LANG UI 语言: en 或 zh (默认跟随 locale)\n" - " TNT_MAX_CONNECTIONS 全局连接数限制 (默认: 64)\n" + " TNT_MAX_CONNECTIONS 全局连接数限制 (默认: %d)\n" " TNT_RATE_LIMIT 设为 0 可禁用速率限制\n" - " TNT_IDLE_TIMEOUT 空闲断开时间,单位秒 (默认: 1800)\n" + " TNT_IDLE_TIMEOUT 空闲断开时间,单位秒 (默认: %d)\n" ); const char *program = (program_name && program_name[0] != '\0') ? program_name : "tnt"; buffer_appendf(buffer, buf_size, pos, i18n_string(help_format, lang), - TNT_VERSION, program, DEFAULT_PORT); + TNT_VERSION, program, TNT_DEFAULT_PORT, + TNT_DEFAULT_MAX_CONNECTIONS, + TNT_DEFAULT_MAX_CONNECTIONS, + TNT_DEFAULT_IDLE_TIMEOUT); } const char *cli_text_invalid_port_format(ui_lang_t lang) { diff --git a/src/config_defaults.c b/src/config_defaults.c new file mode 100644 index 0000000..16b0432 --- /dev/null +++ b/src/config_defaults.c @@ -0,0 +1,80 @@ +#include "config_defaults.h" +#include "common.h" + +#include + +const tnt_int_config_spec_t TNT_CONFIG_PORT = { + "PORT", + TNT_DEFAULT_PORT, + TNT_MIN_PORT, + TNT_MAX_PORT, +}; + +const tnt_int_config_spec_t TNT_CONFIG_MAX_CONNECTIONS = { + "TNT_MAX_CONNECTIONS", + TNT_DEFAULT_MAX_CONNECTIONS, + TNT_MIN_CONFIGURED_CLIENTS, + TNT_MAX_CONFIGURED_CLIENTS, +}; + +const tnt_int_config_spec_t TNT_CONFIG_MAX_CONN_PER_IP = { + "TNT_MAX_CONN_PER_IP", + TNT_DEFAULT_MAX_CONN_PER_IP, + TNT_MIN_CONFIGURED_CLIENTS, + TNT_MAX_CONFIGURED_CLIENTS, +}; + +const tnt_int_config_spec_t TNT_CONFIG_MAX_CONN_RATE_PER_IP = { + "TNT_MAX_CONN_RATE_PER_IP", + TNT_DEFAULT_MAX_CONN_RATE_PER_IP, + TNT_MIN_CONFIGURED_CLIENTS, + TNT_MAX_CONFIGURED_CLIENTS, +}; + +const tnt_int_config_spec_t TNT_CONFIG_RATE_LIMIT = { + "TNT_RATE_LIMIT", + TNT_DEFAULT_RATE_LIMIT_ENABLED, + TNT_MIN_RATE_LIMIT_ENABLED, + TNT_MAX_RATE_LIMIT_ENABLED, +}; + +const tnt_int_config_spec_t TNT_CONFIG_IDLE_TIMEOUT = { + "TNT_IDLE_TIMEOUT", + TNT_DEFAULT_IDLE_TIMEOUT, + TNT_MIN_IDLE_TIMEOUT, + TNT_MAX_IDLE_TIMEOUT, +}; + +const tnt_int_config_spec_t TNT_CONFIG_SSH_LOG_LEVEL = { + "TNT_SSH_LOG_LEVEL", + 0, + TNT_MIN_SSH_LOG_LEVEL, + TNT_MAX_SSH_LOG_LEVEL, +}; + +int tnt_config_env_int(const tnt_int_config_spec_t *spec) { + if (!spec) { + return 0; + } + return env_int(spec->env_name, spec->fallback, spec->min_value, + spec->max_value); +} + +bool tnt_config_parse_int(const char *value, const tnt_int_config_spec_t *spec, + int *out) { + char *end = NULL; + long val; + + if (!value || value[0] == '\0' || !spec || !out) { + return false; + } + + val = strtol(value, &end, 10); + if (!end || *end != '\0' || val < spec->min_value || + val > spec->max_value) { + return false; + } + + *out = (int)val; + return true; +} diff --git a/src/input.c b/src/input.c index a29c902..49b8bb4 100644 --- a/src/input.c +++ b/src/input.c @@ -2,6 +2,7 @@ #include "chat_room.h" #include "client.h" #include "commands.h" +#include "config_defaults.h" #include "common.h" #include "exec.h" #include "history_view.h" @@ -20,11 +21,11 @@ #include #include -static int g_idle_timeout = DEFAULT_IDLE_TIMEOUT; +static int g_idle_timeout = TNT_DEFAULT_IDLE_TIMEOUT; static ui_lang_t g_default_ui_lang = UI_LANG_EN; void input_init(void) { - g_idle_timeout = env_int("TNT_IDLE_TIMEOUT", DEFAULT_IDLE_TIMEOUT, 0, 86400); + g_idle_timeout = tnt_config_env_int(&TNT_CONFIG_IDLE_TIMEOUT); g_default_ui_lang = i18n_default_ui_lang(); } diff --git a/src/main.c b/src/main.c index b1f8058..c7a9e19 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,6 @@ #include "chat_room.h" #include "cli_text.h" +#include "config_defaults.h" #include "common.h" #include "i18n.h" #include "message.h" @@ -19,24 +20,6 @@ static void signal_handler(int sig) { _exit(0); } -static bool parse_int_arg(const char *value, int min_val, int max_val, - int *out) { - char *end = NULL; - long val; - - if (!value || value[0] == '\0' || !out) { - return false; - } - - val = strtol(value, &end, 10); - if (!end || *end != '\0' || val < min_val || val > max_val) { - return false; - } - - *out = (int)val; - return true; -} - static bool is_config_token(const char *value) { const unsigned char *p = (const unsigned char *)value; @@ -60,16 +43,16 @@ static int set_env_option(const char *name, const char *value) { return 0; } -static int set_numeric_env_option(const char *env_name, const char *opt_name, - const char *value, int min_val, - int max_val, ui_lang_t lang) { +static int set_numeric_env_option(const tnt_int_config_spec_t *spec, + const char *opt_name, const char *value, + ui_lang_t lang) { int parsed; - if (!parse_int_arg(value, min_val, max_val, &parsed)) { + if (!tnt_config_parse_int(value, spec, &parsed)) { fprintf(stderr, cli_text_invalid_value_format(lang), opt_name, value); return TNT_EXIT_USAGE; } - if (set_env_option(env_name, value) != 0) { + if (set_env_option(spec->env_name, value) != 0) { return TNT_EXIT_ERROR; } return TNT_EXIT_OK; @@ -86,21 +69,11 @@ static bool require_option_arg(int argc, char **argv, int index, } int main(int argc, char **argv) { - int port = DEFAULT_PORT; + int port = tnt_config_env_int(&TNT_CONFIG_PORT); ui_lang_t lang = i18n_default_ui_lang(); const char *log_check_path = NULL; const char *log_recover_path = NULL; - /* Environment provides defaults; command-line flags override it. */ - const char *port_env = getenv("PORT"); - if (port_env && port_env[0] != '\0') { - char *end; - long val = strtol(port_env, &end, 10); - if (*end == '\0' && val > 0 && val <= 65535) { - port = (int)val; - } - } - /* Parse command line arguments */ for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--port") == 0) { @@ -108,7 +81,7 @@ int main(int argc, char **argv) { if (!require_option_arg(argc, argv, i, lang)) { return TNT_EXIT_USAGE; } - if (!parse_int_arg(argv[i + 1], 1, 65535, &val)) { + if (!tnt_config_parse_int(argv[i + 1], &TNT_CONFIG_PORT, &val)) { fprintf(stderr, cli_text_invalid_port_format(lang), argv[i + 1]); return TNT_EXIT_USAGE; @@ -154,9 +127,8 @@ int main(int argc, char **argv) { if (!require_option_arg(argc, argv, i, lang)) { return TNT_EXIT_USAGE; } - int rc = set_numeric_env_option("TNT_MAX_CONNECTIONS", argv[i], - argv[i + 1], 1, - MAX_CONFIGURED_CLIENTS, lang); + int rc = set_numeric_env_option(&TNT_CONFIG_MAX_CONNECTIONS, + argv[i], argv[i + 1], lang); if (rc != TNT_EXIT_OK) { return rc; } @@ -165,9 +137,8 @@ int main(int argc, char **argv) { if (!require_option_arg(argc, argv, i, lang)) { return TNT_EXIT_USAGE; } - int rc = set_numeric_env_option("TNT_MAX_CONN_PER_IP", argv[i], - argv[i + 1], 1, - MAX_CONFIGURED_CLIENTS, lang); + int rc = set_numeric_env_option(&TNT_CONFIG_MAX_CONN_PER_IP, + argv[i], argv[i + 1], lang); if (rc != TNT_EXIT_OK) { return rc; } @@ -176,9 +147,8 @@ int main(int argc, char **argv) { if (!require_option_arg(argc, argv, i, lang)) { return TNT_EXIT_USAGE; } - int rc = set_numeric_env_option("TNT_MAX_CONN_RATE_PER_IP", - argv[i], argv[i + 1], 1, - MAX_CONFIGURED_CLIENTS, lang); + int rc = set_numeric_env_option(&TNT_CONFIG_MAX_CONN_RATE_PER_IP, + argv[i], argv[i + 1], lang); if (rc != TNT_EXIT_OK) { return rc; } @@ -187,8 +157,8 @@ int main(int argc, char **argv) { if (!require_option_arg(argc, argv, i, lang)) { return TNT_EXIT_USAGE; } - int rc = set_numeric_env_option("TNT_RATE_LIMIT", argv[i], - argv[i + 1], 0, 1, lang); + int rc = set_numeric_env_option(&TNT_CONFIG_RATE_LIMIT, argv[i], + argv[i + 1], lang); if (rc != TNT_EXIT_OK) { return rc; } @@ -197,8 +167,8 @@ int main(int argc, char **argv) { if (!require_option_arg(argc, argv, i, lang)) { return TNT_EXIT_USAGE; } - int rc = set_numeric_env_option("TNT_IDLE_TIMEOUT", argv[i], - argv[i + 1], 0, 86400, lang); + int rc = set_numeric_env_option(&TNT_CONFIG_IDLE_TIMEOUT, argv[i], + argv[i + 1], lang); if (rc != TNT_EXIT_OK) { return rc; } @@ -207,8 +177,8 @@ int main(int argc, char **argv) { if (!require_option_arg(argc, argv, i, lang)) { return TNT_EXIT_USAGE; } - int rc = set_numeric_env_option("TNT_SSH_LOG_LEVEL", argv[i], - argv[i + 1], 0, 4, lang); + int rc = set_numeric_env_option(&TNT_CONFIG_SSH_LOG_LEVEL, + argv[i], argv[i + 1], lang); if (rc != TNT_EXIT_OK) { return rc; } diff --git a/src/ratelimit.c b/src/ratelimit.c index c898bcb..098d77a 100644 --- a/src/ratelimit.c +++ b/src/ratelimit.c @@ -1,4 +1,5 @@ #include "ratelimit.h" +#include "config_defaults.h" #include "common.h" #include #include @@ -27,16 +28,20 @@ static pthread_mutex_t g_rate_limit_lock = PTHREAD_MUTEX_INITIALIZER; static int g_total_connections = 0; static pthread_mutex_t g_conn_count_lock = PTHREAD_MUTEX_INITIALIZER; -static int g_max_connections = 64; -static int g_max_conn_per_ip = 5; -static int g_max_conn_rate_per_ip = 10; -static int g_rate_limit_enabled = 1; +static int g_max_connections = TNT_DEFAULT_MAX_CONNECTIONS; +static int g_max_conn_per_ip = TNT_DEFAULT_MAX_CONN_PER_IP; +static int g_max_conn_rate_per_ip = TNT_DEFAULT_MAX_CONN_RATE_PER_IP; +static int g_rate_limit_enabled = TNT_DEFAULT_RATE_LIMIT_ENABLED; void ratelimit_init(void) { - g_max_connections = env_int("TNT_MAX_CONNECTIONS", 64, 1, 1024); - g_max_conn_per_ip = env_int("TNT_MAX_CONN_PER_IP", 5, 1, 1024); - g_max_conn_rate_per_ip = env_int("TNT_MAX_CONN_RATE_PER_IP", 10, 1, 1024); - g_rate_limit_enabled = env_int("TNT_RATE_LIMIT", 1, 0, 1); + g_max_connections = + tnt_config_env_int(&TNT_CONFIG_MAX_CONNECTIONS); + g_max_conn_per_ip = + tnt_config_env_int(&TNT_CONFIG_MAX_CONN_PER_IP); + g_max_conn_rate_per_ip = + tnt_config_env_int(&TNT_CONFIG_MAX_CONN_RATE_PER_IP); + g_rate_limit_enabled = + tnt_config_env_int(&TNT_CONFIG_RATE_LIMIT); } /* Caller MUST hold g_rate_limit_lock. */ diff --git a/src/ssh_server.c b/src/ssh_server.c index c64c263..d560b2e 100644 --- a/src/ssh_server.c +++ b/src/ssh_server.c @@ -1,6 +1,7 @@ #include "ssh_server.h" #include "bootstrap.h" #include "commands.h" +#include "config_defaults.h" #include "exec.h" #include "input.h" #include "ratelimit.h" @@ -23,7 +24,7 @@ /* Global SSH bind instance */ static ssh_bind g_sshbind = NULL; -static int g_listen_port = DEFAULT_PORT; +static int g_listen_port = TNT_DEFAULT_PORT; static time_t g_server_start_time = 0; diff --git a/src/tntctl.c b/src/tntctl.c index e9d9672..ea026a9 100644 --- a/src/tntctl.c +++ b/src/tntctl.c @@ -1,4 +1,5 @@ #include "common.h" +#include "config_defaults.h" #include "exec_catalog.h" #include "i18n.h" #include "tntctl_text.h" @@ -145,7 +146,7 @@ static int run_ssh(char **ssh_argv) { } int main(int argc, char **argv) { - const char *port = "2222"; + const char *port = TNT_DEFAULT_PORT_TEXT; const char *login = NULL; const char *host_key_checking = NULL; const char *known_hosts = NULL; diff --git a/src/tntctl_text.c b/src/tntctl_text.c index 3863026..ac2f49e 100644 --- a/src/tntctl_text.c +++ b/src/tntctl_text.c @@ -1,5 +1,6 @@ #include "tntctl_text.h" +#include "config_defaults.h" #include "exec_catalog.h" #include "i18n.h" @@ -61,7 +62,7 @@ void tntctl_text_append_usage(char *buffer, size_t buf_size, size_t *pos, "Usage: tntctl [options] host command [args...]\n" "\n" "Options:\n" - " -p, --port PORT SSH port (default: 2222)\n" + " -p, --port PORT SSH port (default: " TNT_DEFAULT_PORT_TEXT ")\n" " -l, --login USER SSH login name for exec identity\n" " --host-key-checking MODE\n" " OpenSSH host-key mode: yes, accept-new, no\n" @@ -74,7 +75,7 @@ void tntctl_text_append_usage(char *buffer, size_t buf_size, size_t *pos, "用法: tntctl [options] host command [args...]\n" "\n" "选项:\n" - " -p, --port PORT SSH 端口 (默认: 2222)\n" + " -p, --port PORT SSH 端口 (默认: " TNT_DEFAULT_PORT_TEXT ")\n" " -l, --login USER SSH 登录名,用作 exec 身份\n" " --host-key-checking MODE\n" " OpenSSH 主机密钥模式: yes, accept-new, no\n" diff --git a/tests/unit/Makefile b/tests/unit/Makefile index bf78445..1e2d9ec 100644 --- a/tests/unit/Makefile +++ b/tests/unit/Makefile @@ -14,6 +14,7 @@ UTF8_SRC = ../../src/utf8.c MESSAGE_SRC = ../../src/message.c MESSAGE_LOG_SRC = ../../src/message_log.c COMMON_SRC = ../../src/common.c +CONFIG_DEFAULTS_SRC = ../../src/config_defaults.c COMMAND_CATALOG_SRC = ../../src/command_catalog.c CLI_TEXT_SRC = ../../src/cli_text.c TNTCTL_TEXT_SRC = ../../src/tntctl_text.c @@ -27,7 +28,7 @@ HELP_TEXT_SRC = ../../src/help_text.c MANUAL_TEXT_SRC = ../../src/manual_text.c RATELIMIT_SRC = ../../src/ratelimit.c -TESTS = test_utf8 test_message test_chat_room test_history_view test_i18n test_system_message test_command_catalog test_exec_catalog test_help_text test_manual_text test_cli_text test_tntctl_text test_ratelimit +TESTS = test_utf8 test_message test_chat_room test_history_view test_i18n test_system_message test_command_catalog test_exec_catalog test_help_text test_manual_text test_cli_text test_tntctl_text test_ratelimit test_config_defaults .PHONY: all clean run @@ -39,7 +40,7 @@ test_utf8: test_utf8.c $(UTF8_SRC) test_message: test_message.c $(MESSAGE_SRC) $(MESSAGE_LOG_SRC) $(UTF8_SRC) $(COMMON_SRC) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) -test_chat_room: test_chat_room.c $(CHAT_ROOM_SRC) $(MESSAGE_SRC) $(MESSAGE_LOG_SRC) $(UTF8_SRC) $(COMMON_SRC) +test_chat_room: test_chat_room.c $(CHAT_ROOM_SRC) $(MESSAGE_SRC) $(MESSAGE_LOG_SRC) $(UTF8_SRC) $(COMMON_SRC) $(CONFIG_DEFAULTS_SRC) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) test_history_view: test_history_view.c $(HISTORY_VIEW_SRC) @@ -69,7 +70,10 @@ test_cli_text: test_cli_text.c $(CLI_TEXT_SRC) $(COMMON_SRC) test_tntctl_text: test_tntctl_text.c $(TNTCTL_TEXT_SRC) $(EXEC_CATALOG_SRC) $(COMMON_SRC) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) -test_ratelimit: test_ratelimit.c $(RATELIMIT_SRC) $(COMMON_SRC) +test_ratelimit: test_ratelimit.c $(RATELIMIT_SRC) $(COMMON_SRC) $(CONFIG_DEFAULTS_SRC) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +test_config_defaults: test_config_defaults.c $(CONFIG_DEFAULTS_SRC) $(COMMON_SRC) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) run: all @@ -111,6 +115,9 @@ run: all @echo "" @echo "=== Running Rate Limit Tests ===" ./test_ratelimit + @echo "" + @echo "=== Running Config Defaults Tests ===" + ./test_config_defaults clean: rm -f $(TESTS) *.o test_messages.log diff --git a/tests/unit/test_config_defaults.c b/tests/unit/test_config_defaults.c new file mode 100644 index 0000000..b4a04ea --- /dev/null +++ b/tests/unit/test_config_defaults.c @@ -0,0 +1,66 @@ +#include "config_defaults.h" + +#include +#include +#include + +#define TEST(name) static void test_##name(void) +#define RUN_TEST(name) do { \ + printf("Running %s... ", #name); \ + test_##name(); \ + printf("ok\n"); \ +} while (0) + +TEST(specs_expose_runtime_defaults) { + assert(TNT_CONFIG_PORT.fallback == TNT_DEFAULT_PORT); + assert(TNT_CONFIG_MAX_CONNECTIONS.fallback == + TNT_DEFAULT_MAX_CONNECTIONS); + assert(TNT_CONFIG_MAX_CONN_PER_IP.fallback == + TNT_DEFAULT_MAX_CONN_PER_IP); + assert(TNT_CONFIG_MAX_CONN_RATE_PER_IP.fallback == + TNT_DEFAULT_MAX_CONN_RATE_PER_IP); + assert(TNT_CONFIG_RATE_LIMIT.fallback == + TNT_DEFAULT_RATE_LIMIT_ENABLED); + assert(TNT_CONFIG_IDLE_TIMEOUT.fallback == TNT_DEFAULT_IDLE_TIMEOUT); + assert(TNT_CONFIG_PORT.min_value == TNT_MIN_PORT); + assert(TNT_CONFIG_PORT.max_value == TNT_MAX_PORT); +} + +TEST(parse_uses_spec_ranges) { + int out = 0; + + assert(tnt_config_parse_int("2222", &TNT_CONFIG_PORT, &out)); + assert(out == 2222); + assert(!tnt_config_parse_int("0", &TNT_CONFIG_PORT, &out)); + assert(!tnt_config_parse_int("65536", &TNT_CONFIG_PORT, &out)); + assert(!tnt_config_parse_int("abc", &TNT_CONFIG_PORT, &out)); + assert(!tnt_config_parse_int("", &TNT_CONFIG_PORT, &out)); + + assert(tnt_config_parse_int("0", &TNT_CONFIG_IDLE_TIMEOUT, &out)); + assert(out == 0); + assert(!tnt_config_parse_int("86401", &TNT_CONFIG_IDLE_TIMEOUT, &out)); +} + +TEST(env_reader_uses_fallback_and_range) { + unsetenv(TNT_CONFIG_MAX_CONNECTIONS.env_name); + assert(tnt_config_env_int(&TNT_CONFIG_MAX_CONNECTIONS) == + TNT_DEFAULT_MAX_CONNECTIONS); + + setenv(TNT_CONFIG_MAX_CONNECTIONS.env_name, "128", 1); + assert(tnt_config_env_int(&TNT_CONFIG_MAX_CONNECTIONS) == 128); + + setenv(TNT_CONFIG_MAX_CONNECTIONS.env_name, "0", 1); + assert(tnt_config_env_int(&TNT_CONFIG_MAX_CONNECTIONS) == + TNT_DEFAULT_MAX_CONNECTIONS); + + unsetenv(TNT_CONFIG_MAX_CONNECTIONS.env_name); +} + +int main(void) { + printf("Running config defaults unit tests...\n\n"); + RUN_TEST(specs_expose_runtime_defaults); + RUN_TEST(parse_uses_spec_ranges); + RUN_TEST(env_reader_uses_fallback_and_range); + printf("\nAll 3 tests passed!\n"); + return 0; +}