mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-06-26 04:34:38 +08:00
Tighten CLI option diagnostics
This commit is contained in:
parent
797ecbb992
commit
f0499c32f6
8 changed files with 165 additions and 31 deletions
3
Makefile
3
Makefile
|
|
@ -25,7 +25,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
|
||||
CTL_OBJECTS = $(OBJ_DIR)/tntctl.o $(OBJ_DIR)/exec_catalog.o $(OBJ_DIR)/common.o
|
||||
TARGETS = $(TARGET) $(CTL_TARGET)
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
|
|
@ -122,6 +122,7 @@ unit-test:
|
|||
|
||||
script-test: all
|
||||
@echo "Running script tests..."
|
||||
@cd tests && ./test_cli_options.sh
|
||||
@cd tests && ./test_logrotate.sh
|
||||
@cd tests && ./test_message_log_tool.sh
|
||||
|
||||
|
|
|
|||
|
|
@ -48,9 +48,12 @@ PORT=3333 tnt # via env var
|
|||
### Connecting
|
||||
|
||||
```sh
|
||||
ssh -p 2222 chat.example.com
|
||||
ssh -p 2222 localhost
|
||||
```
|
||||
|
||||
For a deployed server, replace `localhost` with your public host, for example
|
||||
`chat.example.com`.
|
||||
|
||||
**Anonymous access by default**: Users can connect with ANY username/password (or empty password). No SSH keys required. Perfect for public chat servers.
|
||||
|
||||
## Usage
|
||||
|
|
|
|||
|
|
@ -100,6 +100,13 @@
|
|||
source release.
|
||||
- Release documentation now creates the local tag before strict release checks,
|
||||
matching the strict gate's tag-at-HEAD requirement.
|
||||
- Startup option parsing now reports missing values for `--bind`, `-p`,
|
||||
`--idle-timeout`, and related flags with the localized
|
||||
"option requires argument" diagnostic instead of treating the option as
|
||||
unknown.
|
||||
- `tntctl` now reuses the SSH exec command matcher for local command
|
||||
validation, so `tntctl host --help` reaches the server-side exec help alias
|
||||
instead of being rejected locally.
|
||||
- Split UI-language parsing from localized text lookup: `src/i18n.c` now owns
|
||||
locale/code parsing, while `src/i18n_text.c` owns the table-driven text
|
||||
catalog with coverage checks for every message ID.
|
||||
|
|
@ -121,6 +128,8 @@
|
|||
reducing duplicate command knowledge in `src/exec.c`.
|
||||
- Replaced hard-coded `chat.m1ng.space` examples with `chat.example.com` so
|
||||
public documentation does not imply a specific production host.
|
||||
- First-run connection examples now use `localhost`, keeping
|
||||
`chat.example.com` for deployed public-host examples.
|
||||
- Moved SSH exec usage text and argument-shape checks into `exec_catalog`, so
|
||||
`src/exec.c` no longer duplicates `--json` and required-message validation.
|
||||
- Moved interactive command usage text and first-pass argument-shape checks
|
||||
|
|
|
|||
|
|
@ -37,9 +37,11 @@ tnt -p 2222 -d /var/lib/tnt
|
|||
## Connect
|
||||
|
||||
```sh
|
||||
ssh -p 2222 chat.example.com
|
||||
ssh -p 2222 localhost
|
||||
```
|
||||
|
||||
For a deployed server, replace `localhost` with your public host.
|
||||
|
||||
Default access rules:
|
||||
|
||||
- Any SSH username is accepted.
|
||||
|
|
@ -199,9 +201,11 @@ tnt
|
|||
### 连接
|
||||
|
||||
```sh
|
||||
ssh -p 2222 chat.example.com
|
||||
ssh -p 2222 localhost
|
||||
```
|
||||
|
||||
部署到公网后,将 `localhost` 替换为你的域名。
|
||||
|
||||
默认情况下,任意 SSH 用户名和空密码都可以连接。进入后 TNT 会询问显示名称。
|
||||
|
||||
### 常用操作
|
||||
|
|
|
|||
67
src/main.c
67
src/main.c
|
|
@ -75,6 +75,16 @@ static int set_numeric_env_option(const char *env_name, const char *opt_name,
|
|||
return TNT_EXIT_OK;
|
||||
}
|
||||
|
||||
static bool require_option_arg(int argc, char **argv, int index,
|
||||
ui_lang_t lang) {
|
||||
if (index + 1 >= argc || argv[index + 1][0] == '\0') {
|
||||
fprintf(stderr, cli_text_option_requires_arg_format(lang),
|
||||
argv[index]);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int port = DEFAULT_PORT;
|
||||
ui_lang_t lang = i18n_default_ui_lang();
|
||||
|
|
@ -93,9 +103,11 @@ int main(int argc, char **argv) {
|
|||
|
||||
/* Parse command line arguments */
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if ((strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--port") == 0) &&
|
||||
i + 1 < argc) {
|
||||
if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--port") == 0) {
|
||||
int val;
|
||||
if (!require_option_arg(argc, argv, i, lang)) {
|
||||
return TNT_EXIT_USAGE;
|
||||
}
|
||||
if (!parse_int_arg(argv[i + 1], 1, 65535, &val)) {
|
||||
fprintf(stderr, cli_text_invalid_port_format(lang),
|
||||
argv[i + 1]);
|
||||
|
|
@ -103,18 +115,19 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
port = val;
|
||||
i++;
|
||||
} else if ((strcmp(argv[i], "-d") == 0 ||
|
||||
strcmp(argv[i], "--state-dir") == 0) && i + 1 < argc) {
|
||||
if (argv[i + 1][0] == '\0') {
|
||||
fprintf(stderr, cli_text_invalid_value_format(lang),
|
||||
argv[i], argv[i + 1]);
|
||||
} else if (strcmp(argv[i], "-d") == 0 ||
|
||||
strcmp(argv[i], "--state-dir") == 0) {
|
||||
if (!require_option_arg(argc, argv, i, lang)) {
|
||||
return TNT_EXIT_USAGE;
|
||||
}
|
||||
if (set_env_option("TNT_STATE_DIR", argv[i + 1]) != 0) {
|
||||
return TNT_EXIT_ERROR;
|
||||
}
|
||||
i++;
|
||||
} else if (strcmp(argv[i], "--bind") == 0 && i + 1 < argc) {
|
||||
} else if (strcmp(argv[i], "--bind") == 0) {
|
||||
if (!require_option_arg(argc, argv, i, lang)) {
|
||||
return TNT_EXIT_USAGE;
|
||||
}
|
||||
if (!is_config_token(argv[i + 1])) {
|
||||
fprintf(stderr, cli_text_invalid_value_format(lang),
|
||||
argv[i], argv[i + 1]);
|
||||
|
|
@ -124,7 +137,10 @@ int main(int argc, char **argv) {
|
|||
return TNT_EXIT_ERROR;
|
||||
}
|
||||
i++;
|
||||
} else if (strcmp(argv[i], "--public-host") == 0 && i + 1 < argc) {
|
||||
} else if (strcmp(argv[i], "--public-host") == 0) {
|
||||
if (!require_option_arg(argc, argv, i, lang)) {
|
||||
return TNT_EXIT_USAGE;
|
||||
}
|
||||
if (!is_config_token(argv[i + 1])) {
|
||||
fprintf(stderr, cli_text_invalid_value_format(lang),
|
||||
argv[i], argv[i + 1]);
|
||||
|
|
@ -134,8 +150,10 @@ int main(int argc, char **argv) {
|
|||
return TNT_EXIT_ERROR;
|
||||
}
|
||||
i++;
|
||||
} else if (strcmp(argv[i], "--max-connections") == 0 &&
|
||||
i + 1 < argc) {
|
||||
} else if (strcmp(argv[i], "--max-connections") == 0) {
|
||||
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);
|
||||
|
|
@ -143,8 +161,10 @@ int main(int argc, char **argv) {
|
|||
return rc;
|
||||
}
|
||||
i++;
|
||||
} else if (strcmp(argv[i], "--max-conn-per-ip") == 0 &&
|
||||
i + 1 < argc) {
|
||||
} else if (strcmp(argv[i], "--max-conn-per-ip") == 0) {
|
||||
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);
|
||||
|
|
@ -152,8 +172,10 @@ int main(int argc, char **argv) {
|
|||
return rc;
|
||||
}
|
||||
i++;
|
||||
} else if (strcmp(argv[i], "--max-conn-rate-per-ip") == 0 &&
|
||||
i + 1 < argc) {
|
||||
} else if (strcmp(argv[i], "--max-conn-rate-per-ip") == 0) {
|
||||
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);
|
||||
|
|
@ -161,21 +183,30 @@ int main(int argc, char **argv) {
|
|||
return rc;
|
||||
}
|
||||
i++;
|
||||
} else if (strcmp(argv[i], "--rate-limit") == 0 && i + 1 < argc) {
|
||||
} else if (strcmp(argv[i], "--rate-limit") == 0) {
|
||||
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);
|
||||
if (rc != TNT_EXIT_OK) {
|
||||
return rc;
|
||||
}
|
||||
i++;
|
||||
} else if (strcmp(argv[i], "--idle-timeout") == 0 && i + 1 < argc) {
|
||||
} else if (strcmp(argv[i], "--idle-timeout") == 0) {
|
||||
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);
|
||||
if (rc != TNT_EXIT_OK) {
|
||||
return rc;
|
||||
}
|
||||
i++;
|
||||
} else if (strcmp(argv[i], "--ssh-log-level") == 0 && i + 1 < argc) {
|
||||
} else if (strcmp(argv[i], "--ssh-log-level") == 0) {
|
||||
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);
|
||||
if (rc != TNT_EXIT_OK) {
|
||||
|
|
|
|||
11
src/tntctl.c
11
src/tntctl.c
|
|
@ -1,4 +1,5 @@
|
|||
#include "common.h"
|
||||
#include "exec_catalog.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
|
@ -73,15 +74,7 @@ static bool is_host_key_checking_mode(const char *value) {
|
|||
}
|
||||
|
||||
static bool is_known_exec_command(const char *command) {
|
||||
return command &&
|
||||
(strcmp(command, "health") == 0 ||
|
||||
strcmp(command, "stats") == 0 ||
|
||||
strcmp(command, "users") == 0 ||
|
||||
strcmp(command, "tail") == 0 ||
|
||||
strcmp(command, "dump") == 0 ||
|
||||
strcmp(command, "post") == 0 ||
|
||||
strcmp(command, "help") == 0 ||
|
||||
strcmp(command, "exit") == 0);
|
||||
return exec_catalog_match(command, NULL, NULL);
|
||||
}
|
||||
|
||||
static int build_remote_command(char *buffer, size_t buf_size, int argc,
|
||||
|
|
|
|||
82
tests/test_cli_options.sh
Executable file
82
tests/test_cli_options.sh
Executable file
|
|
@ -0,0 +1,82 @@
|
|||
#!/bin/sh
|
||||
# CLI option parsing regression tests.
|
||||
|
||||
BIN="../tnt"
|
||||
PASS=0
|
||||
FAIL=0
|
||||
|
||||
pass() {
|
||||
echo "✓ $1"
|
||||
PASS=$((PASS + 1))
|
||||
}
|
||||
|
||||
fail() {
|
||||
echo "✗ $1"
|
||||
if [ -n "$2" ]; then
|
||||
printf '%s\n' "$2"
|
||||
fi
|
||||
FAIL=$((FAIL + 1))
|
||||
}
|
||||
|
||||
if [ ! -f "$BIN" ]; then
|
||||
echo "Error: Binary $BIN not found. Run make first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
expect_missing_arg() {
|
||||
opt="$1"
|
||||
output=$("$BIN" "$opt" 2>&1)
|
||||
status=$?
|
||||
|
||||
if [ "$status" -eq 64 ] &&
|
||||
printf '%s\n' "$output" | grep -q "Option requires argument: $opt"; then
|
||||
pass "$opt reports missing argument"
|
||||
else
|
||||
fail "$opt missing argument diagnostic unexpected" "$output"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "=== TNT CLI Option Tests ==="
|
||||
|
||||
for opt in \
|
||||
-p \
|
||||
--port \
|
||||
-d \
|
||||
--state-dir \
|
||||
--bind \
|
||||
--public-host \
|
||||
--max-connections \
|
||||
--max-conn-per-ip \
|
||||
--max-conn-rate-per-ip \
|
||||
--rate-limit \
|
||||
--idle-timeout \
|
||||
--ssh-log-level \
|
||||
--log-check \
|
||||
--log-recover
|
||||
do
|
||||
expect_missing_arg "$opt"
|
||||
done
|
||||
|
||||
ZH_OUTPUT=$(TNT_LANG=zh "$BIN" --bind 2>&1)
|
||||
ZH_STATUS=$?
|
||||
if [ "$ZH_STATUS" -eq 64 ] &&
|
||||
printf '%s\n' "$ZH_OUTPUT" | grep -q '选项需要参数: --bind'; then
|
||||
pass "missing argument diagnostic follows TNT_LANG"
|
||||
else
|
||||
fail "localized missing argument diagnostic unexpected" "$ZH_OUTPUT"
|
||||
fi
|
||||
|
||||
BAD_PORT_OUTPUT=$("$BIN" --port abc 2>&1)
|
||||
BAD_PORT_STATUS=$?
|
||||
if [ "$BAD_PORT_STATUS" -eq 64 ] &&
|
||||
printf '%s\n' "$BAD_PORT_OUTPUT" | grep -q 'Invalid port: abc'; then
|
||||
pass "invalid port still reports invalid value"
|
||||
else
|
||||
fail "invalid port diagnostic unexpected" "$BAD_PORT_OUTPUT"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "PASSED: $PASS"
|
||||
echo "FAILED: $FAIL"
|
||||
[ "$FAIL" -eq 0 ] && echo "All tests passed" || echo "Some tests failed"
|
||||
exit "$FAIL"
|
||||
|
|
@ -119,6 +119,17 @@ else
|
|||
FAIL=$((FAIL + 1))
|
||||
fi
|
||||
|
||||
run_ok "remote help alias is accepted" "$BIN" example.com --help
|
||||
grep -q '^--help$' "$SSH_LOG"
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✓ --help after host is forwarded as exec help"
|
||||
PASS=$((PASS + 1))
|
||||
else
|
||||
echo "✗ remote --help command unexpected"
|
||||
cat "$SSH_LOG"
|
||||
FAIL=$((FAIL + 1))
|
||||
fi
|
||||
|
||||
PATH="$FAKE_BIN:$PATH" TNTCTL_SSH_LOG="$SSH_LOG" "$BIN" example.com users --xml >/dev/null 2>&1
|
||||
REMOTE_STATUS=$?
|
||||
if [ "$REMOTE_STATUS" -eq 64 ]; then
|
||||
|
|
|
|||
Loading…
Reference in a new issue