mirror of
https://github.com/m1ngsama/TNT.git
synced 2026-02-08 08:54:05 +00:00
refactor: optimize rendering, log loading, and restructure tests
This commit is contained in:
parent
aa2b842d03
commit
bd4695b329
15 changed files with 285 additions and 110 deletions
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
|
|
@ -33,11 +33,13 @@ jobs:
|
||||||
- name: Build with AddressSanitizer
|
- name: Build with AddressSanitizer
|
||||||
run: make asan
|
run: make asan
|
||||||
|
|
||||||
- name: Run basic tests
|
- name: Run comprehensive tests
|
||||||
run: |
|
run: |
|
||||||
timeout 10 ./tnt &
|
make test
|
||||||
sleep 2
|
cd tests
|
||||||
pkill tnt || true
|
./test_security_features.sh
|
||||||
|
# Skipping anonymous access test in CI as it requires interactive pty handling which might be flaky
|
||||||
|
# ./test_anonymous_access.sh
|
||||||
|
|
||||||
- name: Check for memory leaks
|
- name: Check for memory leaks
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
|
|
|
||||||
6
Makefile
6
Makefile
|
|
@ -39,6 +39,7 @@ $(OBJ_DIR):
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(OBJ_DIR) $(TARGET)
|
rm -rf $(OBJ_DIR) $(TARGET)
|
||||||
|
rm -f tests/*.log tests/host_key* tests/messages.log
|
||||||
@echo "Clean complete"
|
@echo "Clean complete"
|
||||||
|
|
||||||
install: $(TARGET)
|
install: $(TARGET)
|
||||||
|
|
@ -69,6 +70,11 @@ check:
|
||||||
@command -v cppcheck >/dev/null 2>&1 && cppcheck --enable=warning,performance --quiet src/ || echo "cppcheck not installed"
|
@command -v cppcheck >/dev/null 2>&1 && cppcheck --enable=warning,performance --quiet src/ || echo "cppcheck not installed"
|
||||||
@command -v clang-tidy >/dev/null 2>&1 && clang-tidy src/*.c -- -Iinclude $(INCLUDES) || echo "clang-tidy not installed"
|
@command -v clang-tidy >/dev/null 2>&1 && clang-tidy src/*.c -- -Iinclude $(INCLUDES) || echo "clang-tidy not installed"
|
||||||
|
|
||||||
|
# Test
|
||||||
|
test: all
|
||||||
|
@echo "Running tests..."
|
||||||
|
@cd tests && ./test_basic.sh
|
||||||
|
|
||||||
# Show build info
|
# Show build info
|
||||||
info:
|
info:
|
||||||
@echo "Compiler: $(CC)"
|
@echo "Compiler: $(CC)"
|
||||||
|
|
|
||||||
|
|
@ -96,8 +96,8 @@ tnt.service systemd unit
|
||||||
## Test
|
## Test
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./test_basic.sh # functional
|
make test # run comprehensive test suite
|
||||||
./test_stress.sh 50 # 50 clients
|
# Individual tests are in tests/ directory
|
||||||
```
|
```
|
||||||
|
|
||||||
## Docs
|
## Docs
|
||||||
|
|
|
||||||
10
TODO.md
Normal file
10
TODO.md
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
# TODO
|
||||||
|
|
||||||
|
## Maintenance
|
||||||
|
- [ ] Replace deprecated `libssh` functions in `src/ssh_server.c`:
|
||||||
|
- `ssh_message_auth_password` (deprecated in newer libssh)
|
||||||
|
- `ssh_message_channel_request_pty_width`
|
||||||
|
- `ssh_message_channel_request_pty_height`
|
||||||
|
|
||||||
|
## Future Features
|
||||||
|
- [ ] Implement robust command handling for non-interactive SSH exec requests.
|
||||||
|
|
@ -9,6 +9,9 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
|
/* Project Metadata */
|
||||||
|
#define TNT_VERSION "1.0.0"
|
||||||
|
|
||||||
/* Configuration constants */
|
/* Configuration constants */
|
||||||
#define DEFAULT_PORT 2222
|
#define DEFAULT_PORT 2222
|
||||||
#define MAX_MESSAGES 100
|
#define MAX_MESSAGES 100
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ typedef struct client {
|
||||||
bool show_help;
|
bool show_help;
|
||||||
char command_input[256];
|
char command_input[256];
|
||||||
char command_output[2048];
|
char command_output[2048];
|
||||||
|
char exec_command[256];
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
bool connected;
|
bool connected;
|
||||||
int ref_count; /* Reference count for safe cleanup */
|
int ref_count; /* Reference count for safe cleanup */
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,9 @@ int utf8_strlen(const char *str);
|
||||||
/* Remove last UTF-8 character from string */
|
/* Remove last UTF-8 character from string */
|
||||||
void utf8_remove_last_char(char *str);
|
void utf8_remove_last_char(char *str);
|
||||||
|
|
||||||
|
/* Remove last word from string (mimic Ctrl+W) */
|
||||||
|
void utf8_remove_last_word(char *str);
|
||||||
|
|
||||||
/* Validate a UTF-8 byte sequence */
|
/* Validate a UTF-8 byte sequence */
|
||||||
bool utf8_is_valid_sequence(const char *bytes, int len);
|
bool utf8_is_valid_sequence(const char *bytes, int len);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ void message_init(void) {
|
||||||
/* Nothing to initialize for now */
|
/* Nothing to initialize for now */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Load messages from log file */
|
/* Load messages from log file - Optimized for large files */
|
||||||
int message_load(message_t **messages, int max_messages) {
|
int message_load(message_t **messages, int max_messages) {
|
||||||
/* Always allocate the message array */
|
/* Always allocate the message array */
|
||||||
message_t *msg_array = calloc(max_messages, sizeof(message_t));
|
message_t *msg_array = calloc(max_messages, sizeof(message_t));
|
||||||
|
|
@ -23,56 +23,75 @@ int message_load(message_t **messages, int max_messages) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char line[2048];
|
/* Seek to end */
|
||||||
int count = 0;
|
if (fseek(fp, 0, SEEK_END) != 0) {
|
||||||
|
|
||||||
/* Use a ring buffer approach - keep only last max_messages */
|
|
||||||
/* First, count total lines and seek to appropriate position */
|
|
||||||
/* Use dynamic allocation to handle large log files */
|
|
||||||
long *file_pos = NULL;
|
|
||||||
int pos_capacity = 1000;
|
|
||||||
int line_count = 0;
|
|
||||||
int start_index = 0;
|
|
||||||
|
|
||||||
/* Allocate initial position array */
|
|
||||||
file_pos = malloc(pos_capacity * sizeof(long));
|
|
||||||
if (!file_pos) {
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
*messages = msg_array;
|
*messages = msg_array;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Record file positions */
|
long file_size = ftell(fp);
|
||||||
while (fgets(line, sizeof(line), fp)) {
|
if (file_size == 0) {
|
||||||
/* Expand array if needed */
|
fclose(fp);
|
||||||
if (line_count >= pos_capacity) {
|
*messages = msg_array;
|
||||||
int new_capacity = pos_capacity * 2;
|
return 0;
|
||||||
long *new_pos = realloc(file_pos, new_capacity * sizeof(long));
|
}
|
||||||
if (!new_pos) {
|
|
||||||
/* Out of memory, stop scanning */
|
/* Scan backwards to find the start position */
|
||||||
|
int newlines_found = 0;
|
||||||
|
long pos = file_size - 1;
|
||||||
|
/* Skip the very last byte if it's a newline */
|
||||||
|
if (pos >= 0) {
|
||||||
|
/* Read last char */
|
||||||
|
fseek(fp, pos, SEEK_SET);
|
||||||
|
if (fgetc(fp) == '\n') {
|
||||||
|
pos--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read backwards in chunks for performance */
|
||||||
|
#define CHUNK_SIZE 4096
|
||||||
|
char chunk[CHUNK_SIZE];
|
||||||
|
|
||||||
|
while (pos >= 0 && newlines_found < max_messages) {
|
||||||
|
long read_size = (pos >= CHUNK_SIZE) ? CHUNK_SIZE : (pos + 1);
|
||||||
|
long read_pos = pos - read_size + 1;
|
||||||
|
|
||||||
|
fseek(fp, read_pos, SEEK_SET);
|
||||||
|
if (fread(chunk, 1, read_size, fp) != (size_t)read_size) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
file_pos = new_pos;
|
|
||||||
pos_capacity = new_capacity;
|
/* Scan chunk backwards */
|
||||||
|
for (int i = read_size - 1; i >= 0; i--) {
|
||||||
|
if (chunk[i] == '\n') {
|
||||||
|
newlines_found++;
|
||||||
|
if (newlines_found >= max_messages) {
|
||||||
|
/* Found our start point: one char after this newline */
|
||||||
|
fseek(fp, read_pos + i + 1, SEEK_SET);
|
||||||
|
goto read_messages;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
file_pos[line_count++] = ftell(fp) - strlen(line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Determine where to start reading */
|
pos -= read_size;
|
||||||
if (line_count > max_messages) {
|
}
|
||||||
start_index = line_count - max_messages;
|
|
||||||
fseek(fp, file_pos[start_index], SEEK_SET);
|
/* If we got here, we reached start of file or didn't find enough newlines */
|
||||||
} else {
|
|
||||||
fseek(fp, 0, SEEK_SET);
|
fseek(fp, 0, SEEK_SET);
|
||||||
start_index = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now read the messages */
|
read_messages:;
|
||||||
|
char line[2048];
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
/* Now read forward */
|
||||||
while (fgets(line, sizeof(line), fp) && count < max_messages) {
|
while (fgets(line, sizeof(line), fp) && count < max_messages) {
|
||||||
/* Check for oversized lines */
|
/* Check for oversized lines */
|
||||||
size_t line_len = strlen(line);
|
size_t line_len = strlen(line);
|
||||||
if (line_len >= sizeof(line) - 1) {
|
if (line_len >= sizeof(line) - 1) {
|
||||||
fprintf(stderr, "Warning: Skipping oversized line in messages.log\n");
|
/* Skip remainder of line */
|
||||||
|
int c;
|
||||||
|
while ((c = fgetc(fp)) != '\n' && c != EOF);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,7 +128,6 @@ int message_load(message_t **messages, int max_messages) {
|
||||||
time_t msg_time = mktime(&tm);
|
time_t msg_time = mktime(&tm);
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
if (msg_time > now + 86400 || msg_time < now - 31536000 * 10) {
|
if (msg_time > now + 86400 || msg_time < now - 31536000 * 10) {
|
||||||
/* Skip messages more than 1 day in future or 10 years in past */
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,7 +139,6 @@ int message_load(message_t **messages, int max_messages) {
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(file_pos);
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
*messages = msg_array;
|
*messages = msg_array;
|
||||||
return count;
|
return count;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
/* Suppress libssh deprecation warnings for legacy server API */
|
||||||
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||||
|
|
||||||
/* Global SSH bind instance */
|
/* Global SSH bind instance */
|
||||||
static ssh_bind g_sshbind = NULL;
|
static ssh_bind g_sshbind = NULL;
|
||||||
|
|
||||||
|
|
@ -425,7 +428,7 @@ static int read_username(client_t *client) {
|
||||||
if (pos < MAX_USERNAME_LEN - 1) {
|
if (pos < MAX_USERNAME_LEN - 1) {
|
||||||
username[pos++] = b;
|
username[pos++] = b;
|
||||||
username[pos] = '\0';
|
username[pos] = '\0';
|
||||||
client_send(client, &b, 1);
|
client_send(client, (char *)&b, 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* UTF-8 multi-byte */
|
/* UTF-8 multi-byte */
|
||||||
|
|
@ -558,6 +561,20 @@ static void execute_command(client_t *client) {
|
||||||
|
|
||||||
/* Handle client key press - returns true if key was consumed */
|
/* Handle client key press - returns true if key was consumed */
|
||||||
static bool handle_key(client_t *client, unsigned char key, char *input) {
|
static bool handle_key(client_t *client, unsigned char key, char *input) {
|
||||||
|
/* Handle Ctrl+C (Exit or switch to NORMAL) */
|
||||||
|
if (key == 3) {
|
||||||
|
if (client->mode != MODE_NORMAL) {
|
||||||
|
client->mode = MODE_NORMAL;
|
||||||
|
client->command_input[0] = '\0';
|
||||||
|
client->show_help = false;
|
||||||
|
tui_render_screen(client);
|
||||||
|
} else {
|
||||||
|
/* In NORMAL mode, Ctrl+C exits */
|
||||||
|
client->connected = false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* Handle help screen */
|
/* Handle help screen */
|
||||||
if (client->show_help) {
|
if (client->show_help) {
|
||||||
if (key == 'q' || key == 27) {
|
if (key == 'q' || key == 27) {
|
||||||
|
|
@ -603,7 +620,7 @@ static bool handle_key(client_t *client, unsigned char key, char *input) {
|
||||||
client->scroll_pos = 0;
|
client->scroll_pos = 0;
|
||||||
tui_render_screen(client);
|
tui_render_screen(client);
|
||||||
return true; /* Key consumed */
|
return true; /* Key consumed */
|
||||||
} else if (key == '\r') { /* Enter */
|
} else if (key == '\r' || key == '\n') { /* Enter */
|
||||||
if (input[0] != '\0') {
|
if (input[0] != '\0') {
|
||||||
message_t msg = {
|
message_t msg = {
|
||||||
.timestamp = time(NULL),
|
.timestamp = time(NULL),
|
||||||
|
|
@ -622,6 +639,18 @@ static bool handle_key(client_t *client, unsigned char key, char *input) {
|
||||||
tui_render_input(client, input);
|
tui_render_input(client, input);
|
||||||
}
|
}
|
||||||
return true; /* Key consumed */
|
return true; /* Key consumed */
|
||||||
|
} else if (key == 23) { /* Ctrl+W (Delete Word) */
|
||||||
|
if (input[0] != '\0') {
|
||||||
|
utf8_remove_last_word(input);
|
||||||
|
tui_render_input(client, input);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (key == 21) { /* Ctrl+U (Delete Line) */
|
||||||
|
if (input[0] != '\0') {
|
||||||
|
input[0] = '\0';
|
||||||
|
tui_render_input(client, input);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -690,6 +719,18 @@ static bool handle_key(client_t *client, unsigned char key, char *input) {
|
||||||
tui_render_screen(client);
|
tui_render_screen(client);
|
||||||
}
|
}
|
||||||
return true; /* Key consumed */
|
return true; /* Key consumed */
|
||||||
|
} else if (key == 23) { /* Ctrl+W (Delete Word) */
|
||||||
|
if (client->command_input[0] != '\0') {
|
||||||
|
utf8_remove_last_word(client->command_input);
|
||||||
|
tui_render_screen(client);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (key == 21) { /* Ctrl+U (Delete Line) */
|
||||||
|
if (client->command_input[0] != '\0') {
|
||||||
|
client->command_input[0] = '\0';
|
||||||
|
tui_render_screen(client);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -711,6 +752,20 @@ void* client_handle_session(void *arg) {
|
||||||
client->help_lang = LANG_ZH;
|
client->help_lang = LANG_ZH;
|
||||||
client->connected = true;
|
client->connected = true;
|
||||||
|
|
||||||
|
/* Check for exec command */
|
||||||
|
if (client->exec_command[0] != '\0') {
|
||||||
|
if (strcmp(client->exec_command, "exit") == 0) {
|
||||||
|
/* Just exit */
|
||||||
|
ssh_channel_request_send_exit_status(client->channel, 0);
|
||||||
|
goto cleanup;
|
||||||
|
} else {
|
||||||
|
/* Unknown command */
|
||||||
|
client_printf(client, "Command not supported: %s\r\nOnly 'exit' is supported in non-interactive mode.\r\n", client->exec_command);
|
||||||
|
ssh_channel_request_send_exit_status(client->channel, 1);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Read username */
|
/* Read username */
|
||||||
if (read_username(client) < 0) {
|
if (read_username(client) < 0) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
@ -759,9 +814,6 @@ void* client_handle_session(void *arg) {
|
||||||
|
|
||||||
unsigned char b = buf[0];
|
unsigned char b = buf[0];
|
||||||
|
|
||||||
/* Ctrl+C */
|
|
||||||
if (b == 3) break;
|
|
||||||
|
|
||||||
/* Handle special keys - returns true if key was consumed */
|
/* Handle special keys - returns true if key was consumed */
|
||||||
bool key_consumed = handle_key(client, b, input);
|
bool key_consumed = handle_key(client, b, input);
|
||||||
|
|
||||||
|
|
@ -933,7 +985,6 @@ static ssh_channel handle_channel_open(ssh_session session) {
|
||||||
/* Handle PTY request and get terminal size */
|
/* Handle PTY request and get terminal size */
|
||||||
static int handle_pty_request(ssh_channel channel, client_t *client) {
|
static int handle_pty_request(ssh_channel channel, client_t *client) {
|
||||||
ssh_message message;
|
ssh_message message;
|
||||||
int pty_received = 0;
|
|
||||||
int shell_received = 0;
|
int shell_received = 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
@ -952,9 +1003,8 @@ static int handle_pty_request(ssh_channel channel, client_t *client) {
|
||||||
|
|
||||||
ssh_message_channel_request_reply_success(message);
|
ssh_message_channel_request_reply_success(message);
|
||||||
ssh_message_free(message);
|
ssh_message_free(message);
|
||||||
pty_received = 1;
|
|
||||||
|
|
||||||
/* Don't return yet, wait for shell request */
|
/* Don't return yet, wait for shell/exec request */
|
||||||
if (shell_received) {
|
if (shell_received) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -964,12 +1014,21 @@ static int handle_pty_request(ssh_channel channel, client_t *client) {
|
||||||
ssh_message_channel_request_reply_success(message);
|
ssh_message_channel_request_reply_success(message);
|
||||||
ssh_message_free(message);
|
ssh_message_free(message);
|
||||||
shell_received = 1;
|
shell_received = 1;
|
||||||
|
/* Shell requested, we are done */
|
||||||
/* If we got PTY, we're done */
|
|
||||||
if (pty_received) {
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
} else if (ssh_message_subtype(message) == SSH_CHANNEL_REQUEST_EXEC) {
|
||||||
|
/* Handle exec request (e.g. "ssh user@host command") */
|
||||||
|
const char *cmd = ssh_message_channel_request_command(message);
|
||||||
|
if (cmd) {
|
||||||
|
strncpy(client->exec_command, cmd, sizeof(client->exec_command) - 1);
|
||||||
|
client->exec_command[sizeof(client->exec_command) - 1] = '\0';
|
||||||
}
|
}
|
||||||
continue;
|
/* For now, just allow it and treat like shell start */
|
||||||
|
ssh_message_channel_request_reply_success(message);
|
||||||
|
ssh_message_free(message);
|
||||||
|
shell_received = 1;
|
||||||
|
return 0;
|
||||||
|
|
||||||
} else if (ssh_message_subtype(message) == SSH_CHANNEL_REQUEST_WINDOW_CHANGE) {
|
} else if (ssh_message_subtype(message) == SSH_CHANNEL_REQUEST_WINDOW_CHANGE) {
|
||||||
/* Handle terminal resize - this should be handled during session, not here */
|
/* Handle terminal resize - this should be handled during session, not here */
|
||||||
|
|
@ -982,9 +1041,9 @@ static int handle_pty_request(ssh_channel channel, client_t *client) {
|
||||||
|
|
||||||
ssh_message_reply_default(message);
|
ssh_message_reply_default(message);
|
||||||
ssh_message_free(message);
|
ssh_message_free(message);
|
||||||
} while (!pty_received || !shell_received);
|
} while (!shell_received);
|
||||||
|
|
||||||
return (pty_received && shell_received) ? 0 : -1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize SSH server */
|
/* Initialize SSH server */
|
||||||
|
|
|
||||||
34
src/tui.c
34
src/tui.c
|
|
@ -61,8 +61,8 @@ void tui_render_screen(client_t *client) {
|
||||||
|
|
||||||
/* Now render using snapshot (no lock held) */
|
/* Now render using snapshot (no lock held) */
|
||||||
|
|
||||||
/* Clear and move to top */
|
/* Move to top (Home) - Do NOT clear screen to prevent flicker */
|
||||||
pos += snprintf(buffer + pos, sizeof(buffer) - pos, ANSI_CLEAR ANSI_HOME);
|
pos += snprintf(buffer + pos, sizeof(buffer) - pos, ANSI_HOME);
|
||||||
|
|
||||||
/* Title bar */
|
/* Title bar */
|
||||||
const char *mode_str = (client->mode == MODE_INSERT) ? "INSERT" :
|
const char *mode_str = (client->mode == MODE_INSERT) ? "INSERT" :
|
||||||
|
|
@ -82,22 +82,21 @@ void tui_render_screen(client_t *client) {
|
||||||
for (int i = 0; i < padding; i++) {
|
for (int i = 0; i < padding; i++) {
|
||||||
buffer[pos++] = ' ';
|
buffer[pos++] = ' ';
|
||||||
}
|
}
|
||||||
pos += snprintf(buffer + pos, sizeof(buffer) - pos, ANSI_RESET "\r\n");
|
pos += snprintf(buffer + pos, sizeof(buffer) - pos, ANSI_RESET "\033[K\r\n");
|
||||||
|
|
||||||
/* Render messages from snapshot */
|
/* Render messages from snapshot */
|
||||||
if (msg_snapshot) {
|
if (msg_snapshot) {
|
||||||
for (int i = 0; i < snapshot_count; i++) {
|
for (int i = 0; i < snapshot_count; i++) {
|
||||||
char msg_line[1024];
|
char msg_line[1024];
|
||||||
message_format(&msg_snapshot[i], msg_line, sizeof(msg_line), client->width);
|
message_format(&msg_snapshot[i], msg_line, sizeof(msg_line), client->width);
|
||||||
pos += snprintf(buffer + pos, sizeof(buffer) - pos, "%s\r\n", msg_line);
|
pos += snprintf(buffer + pos, sizeof(buffer) - pos, "%s\033[K\r\n", msg_line);
|
||||||
}
|
}
|
||||||
free(msg_snapshot);
|
free(msg_snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fill empty lines */
|
/* Fill empty lines and clear them */
|
||||||
for (int i = snapshot_count; i < msg_height; i++) {
|
for (int i = snapshot_count; i < msg_height; i++) {
|
||||||
buffer[pos++] = '\r';
|
pos += snprintf(buffer + pos, sizeof(buffer) - pos, "\033[K\r\n");
|
||||||
buffer[pos++] = '\n';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Separator - use box drawing character */
|
/* Separator - use box drawing character */
|
||||||
|
|
@ -107,21 +106,20 @@ void tui_render_screen(client_t *client) {
|
||||||
memcpy(buffer + pos, line_char, len);
|
memcpy(buffer + pos, line_char, len);
|
||||||
pos += len;
|
pos += len;
|
||||||
}
|
}
|
||||||
buffer[pos++] = '\r';
|
pos += snprintf(buffer + pos, sizeof(buffer) - pos, "\033[K\r\n");
|
||||||
buffer[pos++] = '\n';
|
|
||||||
|
|
||||||
/* Status/Input line */
|
/* Status/Input line */
|
||||||
if (client->mode == MODE_INSERT) {
|
if (client->mode == MODE_INSERT) {
|
||||||
pos += snprintf(buffer + pos, sizeof(buffer) - pos, "> ");
|
pos += snprintf(buffer + pos, sizeof(buffer) - pos, "> \033[K");
|
||||||
} else if (client->mode == MODE_NORMAL) {
|
} else if (client->mode == MODE_NORMAL) {
|
||||||
int total = msg_count;
|
int total = msg_count;
|
||||||
int scroll_pos = client->scroll_pos + 1;
|
int scroll_pos = client->scroll_pos + 1;
|
||||||
if (total == 0) scroll_pos = 0;
|
if (total == 0) scroll_pos = 0;
|
||||||
pos += snprintf(buffer + pos, sizeof(buffer) - pos,
|
pos += snprintf(buffer + pos, sizeof(buffer) - pos,
|
||||||
"-- NORMAL -- (%d/%d)", scroll_pos, total);
|
"-- NORMAL -- (%d/%d)\033[K", scroll_pos, total);
|
||||||
} else if (client->mode == MODE_COMMAND) {
|
} else if (client->mode == MODE_COMMAND) {
|
||||||
pos += snprintf(buffer + pos, sizeof(buffer) - pos,
|
pos += snprintf(buffer + pos, sizeof(buffer) - pos,
|
||||||
":%s", client->command_input);
|
":%s\033[K", client->command_input);
|
||||||
}
|
}
|
||||||
|
|
||||||
client_send(client, buffer, pos);
|
client_send(client, buffer, pos);
|
||||||
|
|
@ -224,7 +222,9 @@ const char* tui_get_help_text(help_lang_t lang) {
|
||||||
" ESC - Enter NORMAL mode\n"
|
" ESC - Enter NORMAL mode\n"
|
||||||
" Enter - Send message\n"
|
" Enter - Send message\n"
|
||||||
" Backspace - Delete character\n"
|
" Backspace - Delete character\n"
|
||||||
" Ctrl+C - Exit chat\n"
|
" Ctrl+W - Delete last word\n"
|
||||||
|
" Ctrl+U - Delete line\n"
|
||||||
|
" Ctrl+C - Enter NORMAL mode\n"
|
||||||
"\n"
|
"\n"
|
||||||
"NORMAL MODE KEYS:\n"
|
"NORMAL MODE KEYS:\n"
|
||||||
" i - Return to INSERT mode\n"
|
" i - Return to INSERT mode\n"
|
||||||
|
|
@ -240,6 +240,8 @@ const char* tui_get_help_text(help_lang_t lang) {
|
||||||
" Enter - Execute command\n"
|
" Enter - Execute command\n"
|
||||||
" ESC - Cancel, return to NORMAL\n"
|
" ESC - Cancel, return to NORMAL\n"
|
||||||
" Backspace - Delete character\n"
|
" Backspace - Delete character\n"
|
||||||
|
" Ctrl+W - Delete last word\n"
|
||||||
|
" Ctrl+U - Delete line\n"
|
||||||
"\n"
|
"\n"
|
||||||
"AVAILABLE COMMANDS:\n"
|
"AVAILABLE COMMANDS:\n"
|
||||||
" list, users, who - Show online users\n"
|
" list, users, who - Show online users\n"
|
||||||
|
|
@ -266,7 +268,9 @@ const char* tui_get_help_text(help_lang_t lang) {
|
||||||
" ESC - 进入 NORMAL 模式\n"
|
" ESC - 进入 NORMAL 模式\n"
|
||||||
" Enter - 发送消息\n"
|
" Enter - 发送消息\n"
|
||||||
" Backspace - 删除字符\n"
|
" Backspace - 删除字符\n"
|
||||||
" Ctrl+C - 退出聊天\n"
|
" Ctrl+W - 删除上个单词\n"
|
||||||
|
" Ctrl+U - 删除整行\n"
|
||||||
|
" Ctrl+C - 进入 NORMAL 模式\n"
|
||||||
"\n"
|
"\n"
|
||||||
"NORMAL 模式按键:\n"
|
"NORMAL 模式按键:\n"
|
||||||
" i - 返回 INSERT 模式\n"
|
" i - 返回 INSERT 模式\n"
|
||||||
|
|
@ -282,6 +286,8 @@ const char* tui_get_help_text(help_lang_t lang) {
|
||||||
" Enter - 执行命令\n"
|
" Enter - 执行命令\n"
|
||||||
" ESC - 取消,返回 NORMAL 模式\n"
|
" ESC - 取消,返回 NORMAL 模式\n"
|
||||||
" Backspace - 删除字符\n"
|
" Backspace - 删除字符\n"
|
||||||
|
" Ctrl+W - 删除上个单词\n"
|
||||||
|
" Ctrl+U - 删除整行\n"
|
||||||
"\n"
|
"\n"
|
||||||
"可用命令:\n"
|
"可用命令:\n"
|
||||||
" list, users, who - 显示在线用户\n"
|
" list, users, who - 显示在线用户\n"
|
||||||
|
|
|
||||||
20
src/utf8.c
20
src/utf8.c
|
|
@ -136,6 +136,26 @@ void utf8_remove_last_char(char *str) {
|
||||||
str[i] = '\0';
|
str[i] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Remove last word from string (mimic Ctrl+W) */
|
||||||
|
void utf8_remove_last_word(char *str) {
|
||||||
|
int len = strlen(str);
|
||||||
|
if (len == 0) return;
|
||||||
|
|
||||||
|
int i = len;
|
||||||
|
|
||||||
|
/* Skip trailing spaces */
|
||||||
|
while (i > 0 && str[i - 1] == ' ') {
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Skip non-spaces (the word) */
|
||||||
|
while (i > 0 && str[i - 1] != ' ') {
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
|
||||||
|
str[i] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
/* Validate a UTF-8 byte sequence */
|
/* Validate a UTF-8 byte sequence */
|
||||||
bool utf8_is_valid_sequence(const char *bytes, int len) {
|
bool utf8_is_valid_sequence(const char *bytes, int len) {
|
||||||
if (len <= 0 || len > 4 || !bytes) {
|
if (len <= 0 || len > 4 || !bytes) {
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,39 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Test anonymous SSH access
|
# Test anonymous SSH access
|
||||||
|
|
||||||
|
BIN="../tnt"
|
||||||
|
PORT=${PORT:-2222}
|
||||||
|
|
||||||
|
if [ ! -f "$BIN" ]; then
|
||||||
|
echo "Error: Binary $BIN not found."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Starting TNT server on port $PORT..."
|
||||||
|
$BIN -p $PORT > /dev/null 2>&1 &
|
||||||
|
SERVER_PID=$!
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
kill $SERVER_PID 2>/dev/null
|
||||||
|
wait 2>/dev/null
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
echo "Testing anonymous SSH access to TNT server..."
|
echo "Testing anonymous SSH access to TNT server..."
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Test 1: Connection with any username and password
|
# Test 1: Connection with any username and password
|
||||||
echo "Test 1: Connection with any username (should succeed)"
|
echo "Test 1: Connection with any username (should succeed)"
|
||||||
timeout 5 expect -c '
|
timeout 10 expect -c "
|
||||||
spawn ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2223 testuser@localhost
|
spawn ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p $PORT testuser@localhost
|
||||||
expect {
|
expect {
|
||||||
"password:" {
|
\"password:\" {
|
||||||
send "anypassword\r"
|
send \"anypassword\r\"
|
||||||
expect {
|
expect {
|
||||||
"请输入用户名:" {
|
\"请输入用户名\" {
|
||||||
send "TestUser\r"
|
send \"TestUser\r\"
|
||||||
send "\003"
|
send \"\003\"
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
timeout { exit 1 }
|
timeout { exit 1 }
|
||||||
|
|
@ -22,27 +41,28 @@ expect {
|
||||||
}
|
}
|
||||||
timeout { exit 1 }
|
timeout { exit 1 }
|
||||||
}
|
}
|
||||||
' 2>&1 | grep -q "请输入用户名"
|
" 2>&1 | grep -q "请输入用户名"
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
echo "✓ Test 1 PASSED: Can connect with any password"
|
echo "✓ Test 1 PASSED: Can connect with any password"
|
||||||
else
|
else
|
||||||
echo "✗ Test 1 FAILED: Cannot connect with any password"
|
echo "✗ Test 1 FAILED: Cannot connect with any password"
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Test 2: Connection should work without special SSH options
|
# Test 2: Connection should work with empty password
|
||||||
echo "Test 2: Simple connection (standard SSH command)"
|
echo "Test 2: Simple connection (standard SSH command)"
|
||||||
timeout 5 expect -c '
|
timeout 10 expect -c "
|
||||||
spawn ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2223 anonymous@localhost
|
spawn ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p $PORT anonymous@localhost
|
||||||
expect {
|
expect {
|
||||||
"password:" {
|
\"password:\" {
|
||||||
send "\r"
|
send \"\r\"
|
||||||
expect {
|
expect {
|
||||||
"请输入用户名:" {
|
\"请输入用户名\" {
|
||||||
send "\r"
|
send \"\r\"
|
||||||
send "\003"
|
send \"\003\"
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
timeout { exit 1 }
|
timeout { exit 1 }
|
||||||
|
|
@ -50,13 +70,15 @@ expect {
|
||||||
}
|
}
|
||||||
timeout { exit 1 }
|
timeout { exit 1 }
|
||||||
}
|
}
|
||||||
' 2>&1 | grep -q "请输入用户名"
|
" 2>&1 | grep -q "请输入用户名"
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
echo "✓ Test 2 PASSED: Can connect with empty password"
|
echo "✓ Test 2 PASSED: Can connect with empty password"
|
||||||
else
|
else
|
||||||
echo "✗ Test 2 FAILED: Cannot connect with empty password"
|
echo "✗ Test 2 FAILED: Cannot connect with empty password"
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Anonymous access test completed."
|
echo "Anonymous access test completed."
|
||||||
|
exit 0
|
||||||
|
|
@ -15,8 +15,16 @@ trap cleanup EXIT
|
||||||
|
|
||||||
echo "=== TNT Basic Tests ==="
|
echo "=== TNT Basic Tests ==="
|
||||||
|
|
||||||
|
# Path to binary
|
||||||
|
BIN="../tnt"
|
||||||
|
|
||||||
|
if [ ! -f "$BIN" ]; then
|
||||||
|
echo "Error: Binary $BIN not found. Run make first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Start server
|
# Start server
|
||||||
./tnt -p $PORT >test.log 2>&1 &
|
$BIN -p $PORT >test.log 2>&1 &
|
||||||
SERVER_PID=$!
|
SERVER_PID=$!
|
||||||
sleep 2
|
sleep 2
|
||||||
|
|
||||||
|
|
@ -41,7 +49,7 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Test 3: Message logging
|
# Test 3: Message logging
|
||||||
echo "test message" | timeout 5 ssh -o StrictHostKeyChecking=no \
|
(echo "testuser"; echo "test message"; sleep 1) | timeout 5 ssh -o StrictHostKeyChecking=no \
|
||||||
-o UserKnownHostsFile=/dev/null -p $PORT localhost >/dev/null 2>&1 &
|
-o UserKnownHostsFile=/dev/null -p $PORT localhost >/dev/null 2>&1 &
|
||||||
sleep 3
|
sleep 3
|
||||||
if [ -f messages.log ]; then
|
if [ -f messages.log ]; then
|
||||||
|
|
@ -27,7 +27,7 @@ fail() {
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
pkill -f "^\./tnt" 2>/dev/null || true
|
pkill -f "^\.\./tnt" 2>/dev/null || true
|
||||||
sleep 1
|
sleep 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -37,10 +37,16 @@ echo -e "${YELLOW}========================================${NC}"
|
||||||
echo -e "${YELLOW}TNT Security Features Test Suite${NC}"
|
echo -e "${YELLOW}TNT Security Features Test Suite${NC}"
|
||||||
echo -e "${YELLOW}========================================${NC}"
|
echo -e "${YELLOW}========================================${NC}"
|
||||||
|
|
||||||
|
BIN="../tnt"
|
||||||
|
if [ ! -f "$BIN" ]; then
|
||||||
|
echo "Error: Binary $BIN not found."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Test 1: 4096-bit RSA Key Generation
|
# Test 1: 4096-bit RSA Key Generation
|
||||||
print_test "1. RSA 4096-bit Key Generation"
|
print_test "1. RSA 4096-bit Key Generation"
|
||||||
rm -f host_key
|
rm -f host_key
|
||||||
./tnt &
|
$BIN &
|
||||||
PID=$!
|
PID=$!
|
||||||
sleep 8 # Wait for key generation
|
sleep 8 # Wait for key generation
|
||||||
kill $PID 2>/dev/null || true
|
kill $PID 2>/dev/null || true
|
||||||
|
|
@ -69,19 +75,19 @@ fi
|
||||||
print_test "2. Environment Variable Configuration"
|
print_test "2. Environment Variable Configuration"
|
||||||
|
|
||||||
# Test bind address
|
# Test bind address
|
||||||
TNT_BIND_ADDR=127.0.0.1 timeout 3 ./tnt 2>&1 | grep -q "TNT chat server" && \
|
TNT_BIND_ADDR=127.0.0.1 timeout 3 $BIN 2>&1 | grep -q "TNT chat server" && \
|
||||||
pass "TNT_BIND_ADDR configuration works" || fail "TNT_BIND_ADDR not working"
|
pass "TNT_BIND_ADDR configuration works" || fail "TNT_BIND_ADDR not working"
|
||||||
|
|
||||||
# Test with access token set (just verify it starts)
|
# Test with access token set (just verify it starts)
|
||||||
TNT_ACCESS_TOKEN="test123" timeout 3 ./tnt 2>&1 | grep -q "TNT chat server" && \
|
TNT_ACCESS_TOKEN="test123" timeout 3 $BIN 2>&1 | grep -q "TNT chat server" && \
|
||||||
pass "TNT_ACCESS_TOKEN configuration accepted" || fail "TNT_ACCESS_TOKEN not working"
|
pass "TNT_ACCESS_TOKEN configuration accepted" || fail "TNT_ACCESS_TOKEN not working"
|
||||||
|
|
||||||
# Test max connections configuration
|
# Test max connections configuration
|
||||||
TNT_MAX_CONNECTIONS=10 timeout 3 ./tnt 2>&1 | grep -q "TNT chat server" && \
|
TNT_MAX_CONNECTIONS=10 timeout 3 $BIN 2>&1 | grep -q "TNT chat server" && \
|
||||||
pass "TNT_MAX_CONNECTIONS configuration accepted" || fail "TNT_MAX_CONNECTIONS not working"
|
pass "TNT_MAX_CONNECTIONS configuration accepted" || fail "TNT_MAX_CONNECTIONS not working"
|
||||||
|
|
||||||
# Test rate limit toggle
|
# Test rate limit toggle
|
||||||
TNT_RATE_LIMIT=0 timeout 3 ./tnt 2>&1 | grep -q "TNT chat server" && \
|
TNT_RATE_LIMIT=0 timeout 3 $BIN 2>&1 | grep -q "TNT chat server" && \
|
||||||
pass "TNT_RATE_LIMIT configuration accepted" || fail "TNT_RATE_LIMIT not working"
|
pass "TNT_RATE_LIMIT configuration accepted" || fail "TNT_RATE_LIMIT not working"
|
||||||
|
|
||||||
sleep 1
|
sleep 1
|
||||||
|
|
@ -101,7 +107,7 @@ newline
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Start server and let it load messages
|
# Start server and let it load messages
|
||||||
./tnt &
|
$BIN &
|
||||||
PID=$!
|
PID=$!
|
||||||
sleep 3
|
sleep 3
|
||||||
kill $PID 2>/dev/null || true
|
kill $PID 2>/dev/null || true
|
||||||
|
|
@ -120,8 +126,8 @@ print_test "4. UTF-8 Input Validation"
|
||||||
cat > test_utf8.c <<'EOF'
|
cat > test_utf8.c <<'EOF'
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "include/utf8.h"
|
#include "../include/utf8.h"
|
||||||
#include "include/common.h"
|
#include "../include/common.h"
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
// Valid UTF-8 sequences
|
// Valid UTF-8 sequences
|
||||||
|
|
@ -154,7 +160,7 @@ int main() {
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
if gcc -I. -o test_utf8 test_utf8.c src/utf8.c 2>/dev/null; then
|
if gcc -I../include -o test_utf8 test_utf8.c ../src/utf8.c 2>/dev/null; then
|
||||||
if ./test_utf8; then
|
if ./test_utf8; then
|
||||||
pass "UTF-8 validation function works correctly"
|
pass "UTF-8 validation function works correctly"
|
||||||
else
|
else
|
||||||
|
|
@ -168,12 +174,12 @@ rm -f test_utf8.c
|
||||||
|
|
||||||
# Test 5: Buffer Safety with AddressSanitizer
|
# Test 5: Buffer Safety with AddressSanitizer
|
||||||
print_test "5. Buffer Overflow Protection (ASAN Build)"
|
print_test "5. Buffer Overflow Protection (ASAN Build)"
|
||||||
if make clean >/dev/null 2>&1 && make asan >/dev/null 2>&1; then
|
if make -C .. clean >/dev/null 2>&1 && make -C .. asan >/dev/null 2>&1; then
|
||||||
# Just verify it compiles - actual ASAN testing needs runtime
|
# Just verify it compiles - actual ASAN testing needs runtime
|
||||||
if [ -f tnt ]; then
|
if [ -f ../tnt ]; then
|
||||||
pass "AddressSanitizer build successful"
|
pass "AddressSanitizer build successful"
|
||||||
# Restore normal build
|
# Restore normal build
|
||||||
make clean >/dev/null 2>&1 && make >/dev/null 2>&1
|
make -C .. clean >/dev/null 2>&1 && make -C .. >/dev/null 2>&1
|
||||||
else
|
else
|
||||||
fail "AddressSanitizer build failed"
|
fail "AddressSanitizer build failed"
|
||||||
fi
|
fi
|
||||||
|
|
@ -184,8 +190,8 @@ fi
|
||||||
# Test 6: Concurrent Safety
|
# Test 6: Concurrent Safety
|
||||||
print_test "6. Concurrency Safety (Data Structure Integrity)"
|
print_test "6. Concurrency Safety (Data Structure Integrity)"
|
||||||
# This test verifies the code compiles with thread sanitizer flags
|
# This test verifies the code compiles with thread sanitizer flags
|
||||||
if gcc -fsanitize=thread -g -O1 -Iinclude -I/opt/homebrew/opt/libssh/include \
|
if gcc -fsanitize=thread -g -O1 -I../include -I/opt/homebrew/opt/libssh/include \
|
||||||
-c src/chat_room.c -o /tmp/test_tsan.o 2>/dev/null; then
|
-c ../src/chat_room.c -o /tmp/test_tsan.o 2>/dev/null; then
|
||||||
pass "Code compiles with ThreadSanitizer (concurrency checks enabled)"
|
pass "Code compiles with ThreadSanitizer (concurrency checks enabled)"
|
||||||
rm -f /tmp/test_tsan.o
|
rm -f /tmp/test_tsan.o
|
||||||
else
|
else
|
||||||
|
|
@ -200,7 +206,7 @@ for i in $(seq 1 2000); do
|
||||||
echo "2026-01-22T$(printf "%02d" $((i/100))):$(printf "%02d" $((i%60))):00Z|user$i|message $i" >> messages.log
|
echo "2026-01-22T$(printf "%02d" $((i/100))):$(printf "%02d" $((i%60))):00Z|user$i|message $i" >> messages.log
|
||||||
done
|
done
|
||||||
|
|
||||||
./tnt &
|
$BIN &
|
||||||
PID=$!
|
PID=$!
|
||||||
sleep 4
|
sleep 4
|
||||||
kill $PID 2>/dev/null || true
|
kill $PID 2>/dev/null || true
|
||||||
|
|
@ -5,9 +5,15 @@
|
||||||
PORT=${PORT:-2222}
|
PORT=${PORT:-2222}
|
||||||
CLIENTS=${1:-10}
|
CLIENTS=${1:-10}
|
||||||
DURATION=${2:-30}
|
DURATION=${2:-30}
|
||||||
|
BIN="../tnt"
|
||||||
|
|
||||||
|
if [ ! -f "$BIN" ]; then
|
||||||
|
echo "Error: Binary $BIN not found."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Starting TNT server on port $PORT..."
|
echo "Starting TNT server on port $PORT..."
|
||||||
./tnt -p $PORT &
|
$BIN -p $PORT &
|
||||||
SERVER_PID=$!
|
SERVER_PID=$!
|
||||||
sleep 2
|
sleep 2
|
||||||
|
|
||||||
|
|
@ -35,4 +41,10 @@ kill $SERVER_PID 2>/dev/null
|
||||||
wait
|
wait
|
||||||
|
|
||||||
echo "Stress test complete"
|
echo "Stress test complete"
|
||||||
ps aux | grep tnt | grep -v grep && echo "WARNING: tnt process still running"
|
if ps aux | grep tnt | grep -v grep > /dev/null; then
|
||||||
|
echo "WARNING: tnt process still running"
|
||||||
|
else
|
||||||
|
echo "Server shutdown confirmed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
Loading…
Reference in a new issue