mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-06-26 05:44:38 +08:00
Generate tntctl command list from exec catalog
This commit is contained in:
parent
f2be702a15
commit
8affea2508
10 changed files with 99 additions and 37 deletions
|
|
@ -37,6 +37,8 @@
|
||||||
and server survival stay responsive.
|
and server survival stay responsive.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
- `tntctl --help` now gets its exec command list from `exec_catalog`, reducing
|
||||||
|
duplicate command metadata between the local wrapper and SSH exec mode.
|
||||||
- Updated `tnt(1)` to document the current TUI search and pager keys, and
|
- Updated `tnt(1)` to document the current TUI search and pager keys, and
|
||||||
added script coverage to keep active help surfaces free of removed support
|
added script coverage to keep active help surfaces free of removed support
|
||||||
commands.
|
commands.
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ typedef enum {
|
||||||
TNT_EXEC_COMMAND_TAIL,
|
TNT_EXEC_COMMAND_TAIL,
|
||||||
TNT_EXEC_COMMAND_DUMP,
|
TNT_EXEC_COMMAND_DUMP,
|
||||||
TNT_EXEC_COMMAND_POST,
|
TNT_EXEC_COMMAND_POST,
|
||||||
TNT_EXEC_COMMAND_EXIT
|
TNT_EXEC_COMMAND_EXIT,
|
||||||
|
TNT_EXEC_COMMAND_COUNT
|
||||||
} tnt_exec_command_id_t;
|
} tnt_exec_command_id_t;
|
||||||
|
|
||||||
bool exec_catalog_match(const char *line, tnt_exec_command_id_t *id,
|
bool exec_catalog_match(const char *line, tnt_exec_command_id_t *id,
|
||||||
|
|
@ -19,6 +20,8 @@ bool exec_catalog_match(const char *line, tnt_exec_command_id_t *id,
|
||||||
bool exec_catalog_args_valid(tnt_exec_command_id_t id, const char *args);
|
bool exec_catalog_args_valid(tnt_exec_command_id_t id, const char *args);
|
||||||
void exec_catalog_append_help(char *buffer, size_t buf_size, size_t *pos,
|
void exec_catalog_append_help(char *buffer, size_t buf_size, size_t *pos,
|
||||||
ui_lang_t lang);
|
ui_lang_t lang);
|
||||||
|
void exec_catalog_append_command_list(char *buffer, size_t buf_size,
|
||||||
|
size_t *pos);
|
||||||
void exec_catalog_append_usage(char *buffer, size_t buf_size, size_t *pos,
|
void exec_catalog_append_usage(char *buffer, size_t buf_size, size_t *pos,
|
||||||
tnt_exec_command_id_t id, ui_lang_t lang);
|
tnt_exec_command_id_t id, ui_lang_t lang);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TNTCTL_TEXT_USAGE,
|
|
||||||
TNTCTL_TEXT_INVALID_PORT,
|
TNTCTL_TEXT_INVALID_PORT,
|
||||||
TNTCTL_TEXT_INVALID_LOGIN,
|
TNTCTL_TEXT_INVALID_LOGIN,
|
||||||
TNTCTL_TEXT_INVALID_HOST_KEY_MODE,
|
TNTCTL_TEXT_INVALID_HOST_KEY_MODE,
|
||||||
|
|
@ -23,6 +22,8 @@ typedef enum {
|
||||||
TNTCTL_TEXT_COUNT
|
TNTCTL_TEXT_COUNT
|
||||||
} tntctl_text_id_t;
|
} tntctl_text_id_t;
|
||||||
|
|
||||||
|
void tntctl_text_append_usage(char *buffer, size_t buf_size, size_t *pos,
|
||||||
|
ui_lang_t lang);
|
||||||
const char *tntctl_text(ui_lang_t lang, tntctl_text_id_t id);
|
const char *tntctl_text(ui_lang_t lang, tntctl_text_id_t id);
|
||||||
|
|
||||||
#endif /* TNTCTL_TEXT_H */
|
#endif /* TNTCTL_TEXT_H */
|
||||||
|
|
|
||||||
|
|
@ -517,6 +517,8 @@ int exec_dispatch(client_t *client) {
|
||||||
return exec_command_post(client, args);
|
return exec_command_post(client, args);
|
||||||
case TNT_EXEC_COMMAND_EXIT:
|
case TNT_EXEC_COMMAND_EXIT:
|
||||||
return TNT_EXIT_OK;
|
return TNT_EXIT_OK;
|
||||||
|
case TNT_EXEC_COMMAND_COUNT:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,26 @@ void exec_catalog_append_help(char *buffer, size_t buf_size, size_t *pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void exec_catalog_append_command_list(char *buffer, size_t buf_size,
|
||||||
|
size_t *pos) {
|
||||||
|
bool seen[TNT_EXEC_COMMAND_COUNT] = {0};
|
||||||
|
size_t count = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof(entries) / sizeof(entries[0]); i++) {
|
||||||
|
tnt_exec_command_id_t id = entries[i].id;
|
||||||
|
|
||||||
|
if (id < 0 || id >= TNT_EXEC_COMMAND_COUNT || seen[id]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (count > 0) {
|
||||||
|
buffer_appendf(buffer, buf_size, pos, ", ");
|
||||||
|
}
|
||||||
|
buffer_appendf(buffer, buf_size, pos, "%s", entries[i].name);
|
||||||
|
seen[id] = true;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void exec_catalog_append_usage(char *buffer, size_t buf_size, size_t *pos,
|
void exec_catalog_append_usage(char *buffer, size_t buf_size, size_t *pos,
|
||||||
tnt_exec_command_id_t id, ui_lang_t lang) {
|
tnt_exec_command_id_t id, ui_lang_t lang) {
|
||||||
const exec_catalog_entry_t *entry = entry_for_id(id);
|
const exec_catalog_entry_t *entry = entry_for_id(id);
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,12 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
static void print_usage(FILE *stream, ui_lang_t lang) {
|
static void print_usage(FILE *stream, ui_lang_t lang) {
|
||||||
fputs(tntctl_text(lang, TNTCTL_TEXT_USAGE), stream);
|
char output[2048];
|
||||||
|
size_t pos = 0;
|
||||||
|
|
||||||
|
output[0] = '\0';
|
||||||
|
tntctl_text_append_usage(output, sizeof(output), &pos, lang);
|
||||||
|
fputs(output, stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_error(ui_lang_t lang, tntctl_text_id_t id) {
|
static void print_error(ui_lang_t lang, tntctl_text_id_t id) {
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,9 @@
|
||||||
#include "tntctl_text.h"
|
#include "tntctl_text.h"
|
||||||
|
|
||||||
|
#include "exec_catalog.h"
|
||||||
#include "i18n.h"
|
#include "i18n.h"
|
||||||
|
|
||||||
static const i18n_string_t text_catalog[TNTCTL_TEXT_COUNT] = {
|
static const i18n_string_t text_catalog[TNTCTL_TEXT_COUNT] = {
|
||||||
[TNTCTL_TEXT_USAGE] = I18N_STRING(
|
|
||||||
"Usage: tntctl [options] host command [args...]\n"
|
|
||||||
"\n"
|
|
||||||
"Options:\n"
|
|
||||||
" -p, --port PORT SSH port (default: 2222)\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"
|
|
||||||
" --known-hosts FILE OpenSSH known_hosts file\n"
|
|
||||||
" -V, --version Print version and exit\n"
|
|
||||||
" -h, --help Print this help and exit\n"
|
|
||||||
"\n"
|
|
||||||
"Commands mirror the TNT SSH exec interface: health, stats, users,\n"
|
|
||||||
"tail, dump, post, help, and exit.\n",
|
|
||||||
"用法: tntctl [options] host command [args...]\n"
|
|
||||||
"\n"
|
|
||||||
"选项:\n"
|
|
||||||
" -p, --port PORT SSH 端口 (默认: 2222)\n"
|
|
||||||
" -l, --login USER SSH 登录名,用作 exec 身份\n"
|
|
||||||
" --host-key-checking MODE\n"
|
|
||||||
" OpenSSH 主机密钥模式: yes, accept-new, no\n"
|
|
||||||
" --known-hosts FILE OpenSSH known_hosts 文件\n"
|
|
||||||
" -V, --version 输出版本并退出\n"
|
|
||||||
" -h, --help 输出此帮助并退出\n"
|
|
||||||
"\n"
|
|
||||||
"命令对应 TNT SSH exec 接口: health, stats, users,\n"
|
|
||||||
"tail, dump, post, help 和 exit.\n"
|
|
||||||
),
|
|
||||||
[TNTCTL_TEXT_INVALID_PORT] = I18N_STRING(
|
[TNTCTL_TEXT_INVALID_PORT] = I18N_STRING(
|
||||||
"invalid port", "端口无效"
|
"invalid port", "端口无效"
|
||||||
),
|
),
|
||||||
|
|
@ -79,7 +52,45 @@ static const i18n_string_t text_catalog[TNTCTL_TEXT_COUNT] = {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
typedef char text_catalog_must_cover_enum[
|
typedef char text_catalog_must_cover_enum[
|
||||||
sizeof(text_catalog) / sizeof(text_catalog[0]) == TNTCTL_TEXT_COUNT ? 1 : -1];
|
sizeof(text_catalog) / sizeof(text_catalog[0]) == TNTCTL_TEXT_COUNT ? 1 : -1
|
||||||
|
];
|
||||||
|
|
||||||
|
void tntctl_text_append_usage(char *buffer, size_t buf_size, size_t *pos,
|
||||||
|
ui_lang_t lang) {
|
||||||
|
static const i18n_string_t before_commands = I18N_STRING(
|
||||||
|
"Usage: tntctl [options] host command [args...]\n"
|
||||||
|
"\n"
|
||||||
|
"Options:\n"
|
||||||
|
" -p, --port PORT SSH port (default: 2222)\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"
|
||||||
|
" --known-hosts FILE OpenSSH known_hosts file\n"
|
||||||
|
" -V, --version Print version and exit\n"
|
||||||
|
" -h, --help Print this help and exit\n"
|
||||||
|
"\n"
|
||||||
|
"Commands:\n"
|
||||||
|
" ",
|
||||||
|
"用法: tntctl [options] host command [args...]\n"
|
||||||
|
"\n"
|
||||||
|
"选项:\n"
|
||||||
|
" -p, --port PORT SSH 端口 (默认: 2222)\n"
|
||||||
|
" -l, --login USER SSH 登录名,用作 exec 身份\n"
|
||||||
|
" --host-key-checking MODE\n"
|
||||||
|
" OpenSSH 主机密钥模式: yes, accept-new, no\n"
|
||||||
|
" --known-hosts FILE OpenSSH known_hosts 文件\n"
|
||||||
|
" -V, --version 输出版本并退出\n"
|
||||||
|
" -h, --help 输出此帮助并退出\n"
|
||||||
|
"\n"
|
||||||
|
"命令:\n"
|
||||||
|
" "
|
||||||
|
);
|
||||||
|
|
||||||
|
buffer_appendf(buffer, buf_size, pos, "%s",
|
||||||
|
i18n_string(before_commands, lang));
|
||||||
|
exec_catalog_append_command_list(buffer, buf_size, pos);
|
||||||
|
buffer_appendf(buffer, buf_size, pos, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
const char *tntctl_text(ui_lang_t lang, tntctl_text_id_t id) {
|
const char *tntctl_text(ui_lang_t lang, tntctl_text_id_t id) {
|
||||||
if (id < 0 || id >= TNTCTL_TEXT_COUNT) {
|
if (id < 0 || id >= TNTCTL_TEXT_COUNT) {
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ test_manual_text: test_manual_text.c $(MANUAL_TEXT_SRC) $(COMMAND_CATALOG_SRC) $
|
||||||
test_cli_text: test_cli_text.c $(CLI_TEXT_SRC) $(COMMON_SRC)
|
test_cli_text: test_cli_text.c $(CLI_TEXT_SRC) $(COMMON_SRC)
|
||||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
test_tntctl_text: test_tntctl_text.c $(TNTCTL_TEXT_SRC)
|
test_tntctl_text: test_tntctl_text.c $(TNTCTL_TEXT_SRC) $(EXEC_CATALOG_SRC) $(COMMON_SRC)
|
||||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
test_ratelimit: test_ratelimit.c $(RATELIMIT_SRC) $(COMMON_SRC)
|
test_ratelimit: test_ratelimit.c $(RATELIMIT_SRC) $(COMMON_SRC)
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,16 @@ TEST(generates_localized_usage) {
|
||||||
assert(strcmp(en, "dump: usage: dump [N] | dump -n N\n") == 0);
|
assert(strcmp(en, "dump: usage: dump [N] | dump -n N\n") == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(generates_unique_command_list) {
|
||||||
|
char output[256] = {0};
|
||||||
|
size_t pos = 0;
|
||||||
|
|
||||||
|
exec_catalog_append_command_list(output, sizeof(output), &pos);
|
||||||
|
|
||||||
|
assert(strcmp(output,
|
||||||
|
"help, health, users, stats, tail, dump, post, exit") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
printf("Running exec catalog unit tests...\n\n");
|
printf("Running exec catalog unit tests...\n\n");
|
||||||
|
|
||||||
|
|
@ -131,6 +141,7 @@ int main(void) {
|
||||||
RUN_TEST(matches_exec_commands_and_args);
|
RUN_TEST(matches_exec_commands_and_args);
|
||||||
RUN_TEST(validates_argument_shapes);
|
RUN_TEST(validates_argument_shapes);
|
||||||
RUN_TEST(generates_localized_usage);
|
RUN_TEST(generates_localized_usage);
|
||||||
|
RUN_TEST(generates_unique_command_list);
|
||||||
|
|
||||||
printf("\n✓ All %d tests passed!\n", tests_passed);
|
printf("\n✓ All %d tests passed!\n", tests_passed);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -16,15 +16,22 @@
|
||||||
static int tests_passed = 0;
|
static int tests_passed = 0;
|
||||||
|
|
||||||
TEST(usage_matches_language) {
|
TEST(usage_matches_language) {
|
||||||
const char *en = tntctl_text(UI_LANG_EN, TNTCTL_TEXT_USAGE);
|
char en[2048] = {0};
|
||||||
const char *zh = tntctl_text(UI_LANG_ZH, TNTCTL_TEXT_USAGE);
|
char zh[2048] = {0};
|
||||||
|
size_t en_pos = 0;
|
||||||
|
size_t zh_pos = 0;
|
||||||
|
|
||||||
|
tntctl_text_append_usage(en, sizeof(en), &en_pos, UI_LANG_EN);
|
||||||
|
tntctl_text_append_usage(zh, sizeof(zh), &zh_pos, UI_LANG_ZH);
|
||||||
|
|
||||||
assert(strstr(en, "Usage: tntctl [options] host command [args...]") != NULL);
|
assert(strstr(en, "Usage: tntctl [options] host command [args...]") != NULL);
|
||||||
assert(strstr(en, "--host-key-checking MODE") != NULL);
|
assert(strstr(en, "--host-key-checking MODE") != NULL);
|
||||||
assert(strstr(en, "health, stats, users") != NULL);
|
assert(strstr(en,
|
||||||
|
"help, health, users, stats, tail, dump, post, exit") != NULL);
|
||||||
assert(strstr(zh, "用法: tntctl [options] host command [args...]") != NULL);
|
assert(strstr(zh, "用法: tntctl [options] host command [args...]") != NULL);
|
||||||
assert(strstr(zh, "OpenSSH 主机密钥模式") != NULL);
|
assert(strstr(zh, "OpenSSH 主机密钥模式") != NULL);
|
||||||
assert(strstr(zh, "health, stats, users") != NULL);
|
assert(strstr(zh,
|
||||||
|
"help, health, users, stats, tail, dump, post, exit") != NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(errors_match_language) {
|
TEST(errors_match_language) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue