mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-06-26 05:54:38 +08:00
refactor: extract client module (PR2-M6)
Move client_t I/O and lifecycle out of ssh_server.c into a dedicated
module. ssh_server.c is now down to the listening socket, host key
setup, ssh_server_init / ssh_server_start, and the accept loop.
Migrated to client.{c,h}:
- client_send, client_printf
- client_addref, client_release (and the ssh_session / ssh_channel /
channel_cb teardown that fires on the final release)
- client_install_channel_callbacks
- client_channel_window_change / _eof / _close (the post-bootstrap
callbacks that target the client_t)
The client_t struct definition stays in ssh_server.h so we don't have
to revisit every existing #include chain. bootstrap, commands, exec,
input, and tui pick up the new client.h alongside their existing
ssh_server.h include.
ssh_server.c shrinks from 402 to 249 lines (-153).
Final per-module size after PR2:
ssh_server.c 249 accept loop + ssh_server_init/start + host key
bootstrap.c 493 per-connection SSH handshake / auth / channel
client.c 162 client_t I/O + lifecycle + channel callbacks
input.c 637 username read + handle_key + main loop +
notify_mentions + idle timeout
commands.c 269 vim ':' command dispatcher
exec.c 453 SSH exec subcommand dispatcher
ratelimit.c 197 IP rate limit + connection counters
tui.c 614 screen rendering
chat_room.c 151 room + client list
message.c 350 message log load/save/search
utf8.c 250 UTF-8 width / validation
common.c 133 buffer_*, env_int, is_valid_username,
sanitize_terminal_size, tnt_state_*
main.c 109 process entry
Total: ~4 000 lines of C across 13 files, no file over 650 lines, every
file is single-purpose. Behaviour is preserved: the migrated code is
byte-for-byte identical.
This commit is contained in:
parent
3b8a1d18d8
commit
7897d8820e
9 changed files with 201 additions and 171 deletions
34
include/client.h
Normal file
34
include/client.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef CLIENT_H
|
||||
#define CLIENT_H
|
||||
|
||||
#include "ssh_server.h" /* for client_t */
|
||||
|
||||
/* Send `len` bytes to the client over its SSH channel. Serialised on
|
||||
* client->io_lock so concurrent senders don't interleave. Returns 0 on
|
||||
* success, -1 if the channel is gone or a partial write fails. */
|
||||
int client_send(client_t *client, const char *data, size_t len);
|
||||
|
||||
/* printf-style wrapper around client_send(). The formatted string must
|
||||
* fit in 2048 bytes; truncation or encoding errors return -1. */
|
||||
int client_printf(client_t *client, const char *fmt, ...);
|
||||
|
||||
/* Reference counting for safe cross-thread cleanup.
|
||||
*
|
||||
* Lifecycle: bootstrap_run() creates the client_t with ref_count = 1
|
||||
* (the "main" ref), then adds a second ref before installing the channel
|
||||
* callbacks (the "callback" ref) so the client outlives any in-flight
|
||||
* eof / close / window-change callback invocation. The interactive
|
||||
* session releases both refs in its cleanup path; the final release
|
||||
* frees the SSH session, channel, callback struct, and the client_t. */
|
||||
void client_addref(client_t *client);
|
||||
void client_release(client_t *client);
|
||||
|
||||
/* Install the post-bootstrap channel callbacks (window-change, eof, close)
|
||||
* that target this client_t. Caller MUST have already added one
|
||||
* client_addref() to keep the client alive across in-flight callback
|
||||
* invocations; the matching client_release() happens during cleanup in
|
||||
* input_run_session(). Returns 0 on success, -1 on failure (in which
|
||||
* case the caller still owns both refs and must release them). */
|
||||
int client_install_channel_callbacks(client_t *client);
|
||||
|
||||
#endif /* CLIENT_H */
|
||||
|
|
@ -45,24 +45,6 @@ int ssh_server_init(int port);
|
|||
/* Start SSH server (blocking) */
|
||||
int ssh_server_start(int listen_fd);
|
||||
|
||||
/* Send data to client */
|
||||
int client_send(client_t *client, const char *data, size_t len);
|
||||
|
||||
/* Send formatted string to client */
|
||||
int client_printf(client_t *client, const char *fmt, ...);
|
||||
|
||||
/* Reference counting helpers */
|
||||
void client_addref(client_t *client);
|
||||
void client_release(client_t *client);
|
||||
|
||||
/* Install the post-bootstrap channel callbacks (window-change, eof, close)
|
||||
* that target this client_t. Caller MUST have already added one
|
||||
* client_addref() to keep the client alive across in-flight callback
|
||||
* invocations; the matching client_release() happens during cleanup in
|
||||
* input_run_session(). Returns 0 on success, -1 on failure (in which
|
||||
* case the caller still owns both refs and must release them). */
|
||||
int client_install_channel_callbacks(client_t *client);
|
||||
|
||||
/* Read-only accessor for the server start time (used by exec stats). */
|
||||
time_t ssh_server_start_time(void);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "bootstrap.h"
|
||||
#include "client.h"
|
||||
#include "common.h"
|
||||
#include "input.h"
|
||||
#include "ratelimit.h"
|
||||
|
|
|
|||
162
src/client.c
Normal file
162
src/client.c
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
#include "client.h"
|
||||
#include "common.h"
|
||||
#include <libssh/callbacks.h>
|
||||
#include <libssh/libssh.h>
|
||||
#include <libssh/server.h>
|
||||
#include <pthread.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Send data to client via SSH channel */
|
||||
int client_send(client_t *client, const char *data, size_t len) {
|
||||
size_t total = 0;
|
||||
|
||||
if (!client || !data) return -1;
|
||||
|
||||
pthread_mutex_lock(&client->io_lock);
|
||||
|
||||
if (!client->connected || !client->channel) {
|
||||
pthread_mutex_unlock(&client->io_lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (total < len) {
|
||||
size_t remaining = len - total;
|
||||
uint32_t chunk = (remaining > 32768) ? 32768 : (uint32_t)remaining;
|
||||
int sent = ssh_channel_write(client->channel, data + total, chunk);
|
||||
if (sent <= 0) {
|
||||
pthread_mutex_unlock(&client->io_lock);
|
||||
return -1;
|
||||
}
|
||||
total += (size_t)sent;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&client->io_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void client_addref(client_t *client) {
|
||||
if (!client) return;
|
||||
pthread_mutex_lock(&client->ref_lock);
|
||||
client->ref_count++;
|
||||
pthread_mutex_unlock(&client->ref_lock);
|
||||
}
|
||||
|
||||
void client_release(client_t *client) {
|
||||
if (!client) return;
|
||||
|
||||
pthread_mutex_lock(&client->ref_lock);
|
||||
client->ref_count--;
|
||||
int count = client->ref_count;
|
||||
pthread_mutex_unlock(&client->ref_lock);
|
||||
|
||||
if (count == 0) {
|
||||
/* Safe to free now */
|
||||
if (client->channel && client->channel_cb) {
|
||||
ssh_remove_channel_callbacks(client->channel, client->channel_cb);
|
||||
}
|
||||
if (client->channel) {
|
||||
ssh_channel_close(client->channel);
|
||||
ssh_channel_free(client->channel);
|
||||
}
|
||||
if (client->session) {
|
||||
ssh_disconnect(client->session);
|
||||
ssh_free(client->session);
|
||||
}
|
||||
if (client->channel_cb) {
|
||||
free(client->channel_cb);
|
||||
}
|
||||
pthread_mutex_destroy(&client->io_lock);
|
||||
pthread_mutex_destroy(&client->ref_lock);
|
||||
free(client);
|
||||
}
|
||||
}
|
||||
|
||||
/* Send formatted string to client */
|
||||
int client_printf(client_t *client, const char *fmt, ...) {
|
||||
char buffer[2048];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int len = vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
/* Check for buffer overflow or encoding error */
|
||||
if (len < 0 || len >= (int)sizeof(buffer)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return client_send(client, buffer, len);
|
||||
}
|
||||
|
||||
static int client_channel_window_change(ssh_session session, ssh_channel channel,
|
||||
int width, int height,
|
||||
int pxwidth, int pxheight,
|
||||
void *userdata) {
|
||||
(void)session;
|
||||
(void)channel;
|
||||
(void)pxwidth;
|
||||
(void)pxheight;
|
||||
|
||||
client_t *client = (client_t *)userdata;
|
||||
if (!client) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
int w = width;
|
||||
int h = height;
|
||||
sanitize_terminal_size(&w, &h);
|
||||
client->width = w;
|
||||
client->height = h;
|
||||
client->redraw_pending = true;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
static void client_channel_eof(ssh_session session, ssh_channel channel,
|
||||
void *userdata) {
|
||||
(void)session;
|
||||
(void)channel;
|
||||
|
||||
client_t *client = (client_t *)userdata;
|
||||
if (client) {
|
||||
client->connected = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void client_channel_close(ssh_session session, ssh_channel channel,
|
||||
void *userdata) {
|
||||
(void)session;
|
||||
(void)channel;
|
||||
|
||||
client_t *client = (client_t *)userdata;
|
||||
if (client) {
|
||||
client->connected = false;
|
||||
}
|
||||
}
|
||||
|
||||
int client_install_channel_callbacks(client_t *client) {
|
||||
if (!client || !client->channel) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
client->channel_cb = calloc(1, sizeof(struct ssh_channel_callbacks_struct));
|
||||
if (!client->channel_cb) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssh_callbacks_init(client->channel_cb);
|
||||
client->channel_cb->userdata = client;
|
||||
client->channel_cb->channel_eof_function = client_channel_eof;
|
||||
client->channel_cb->channel_close_function = client_channel_close;
|
||||
client->channel_cb->channel_pty_window_change_function =
|
||||
client_channel_window_change;
|
||||
|
||||
if (ssh_set_channel_callbacks(client->channel, client->channel_cb) != SSH_OK) {
|
||||
free(client->channel_cb);
|
||||
client->channel_cb = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
#include "commands.h"
|
||||
#include "chat_room.h"
|
||||
#include "client.h"
|
||||
#include "common.h"
|
||||
#include "message.h"
|
||||
#include "tui.h"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "exec.h"
|
||||
#include "chat_room.h"
|
||||
#include "client.h"
|
||||
#include "common.h"
|
||||
#include "input.h"
|
||||
#include "message.h"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "input.h"
|
||||
#include "chat_room.h"
|
||||
#include "client.h"
|
||||
#include "commands.h"
|
||||
#include "common.h"
|
||||
#include "exec.h"
|
||||
|
|
|
|||
153
src/ssh_server.c
153
src/ssh_server.c
|
|
@ -122,159 +122,6 @@ static int setup_host_key(ssh_bind sshbind) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Send data to client via SSH channel */
|
||||
int client_send(client_t *client, const char *data, size_t len) {
|
||||
size_t total = 0;
|
||||
|
||||
if (!client || !data) return -1;
|
||||
|
||||
pthread_mutex_lock(&client->io_lock);
|
||||
|
||||
if (!client->connected || !client->channel) {
|
||||
pthread_mutex_unlock(&client->io_lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (total < len) {
|
||||
size_t remaining = len - total;
|
||||
uint32_t chunk = (remaining > 32768) ? 32768 : (uint32_t)remaining;
|
||||
int sent = ssh_channel_write(client->channel, data + total, chunk);
|
||||
if (sent <= 0) {
|
||||
pthread_mutex_unlock(&client->io_lock);
|
||||
return -1;
|
||||
}
|
||||
total += (size_t)sent;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&client->io_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void client_addref(client_t *client) {
|
||||
if (!client) return;
|
||||
pthread_mutex_lock(&client->ref_lock);
|
||||
client->ref_count++;
|
||||
pthread_mutex_unlock(&client->ref_lock);
|
||||
}
|
||||
|
||||
void client_release(client_t *client) {
|
||||
if (!client) return;
|
||||
|
||||
pthread_mutex_lock(&client->ref_lock);
|
||||
client->ref_count--;
|
||||
int count = client->ref_count;
|
||||
pthread_mutex_unlock(&client->ref_lock);
|
||||
|
||||
if (count == 0) {
|
||||
/* Safe to free now */
|
||||
if (client->channel && client->channel_cb) {
|
||||
ssh_remove_channel_callbacks(client->channel, client->channel_cb);
|
||||
}
|
||||
if (client->channel) {
|
||||
ssh_channel_close(client->channel);
|
||||
ssh_channel_free(client->channel);
|
||||
}
|
||||
if (client->session) {
|
||||
ssh_disconnect(client->session);
|
||||
ssh_free(client->session);
|
||||
}
|
||||
if (client->channel_cb) {
|
||||
free(client->channel_cb);
|
||||
}
|
||||
pthread_mutex_destroy(&client->io_lock);
|
||||
pthread_mutex_destroy(&client->ref_lock);
|
||||
free(client);
|
||||
}
|
||||
}
|
||||
|
||||
/* Send formatted string to client */
|
||||
int client_printf(client_t *client, const char *fmt, ...) {
|
||||
char buffer[2048];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int len = vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
/* Check for buffer overflow or encoding error */
|
||||
if (len < 0 || len >= (int)sizeof(buffer)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return client_send(client, buffer, len);
|
||||
}
|
||||
|
||||
static int client_channel_window_change(ssh_session session, ssh_channel channel,
|
||||
int width, int height,
|
||||
int pxwidth, int pxheight,
|
||||
void *userdata) {
|
||||
(void)session;
|
||||
(void)channel;
|
||||
(void)pxwidth;
|
||||
(void)pxheight;
|
||||
|
||||
client_t *client = (client_t *)userdata;
|
||||
if (!client) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
int w = width;
|
||||
int h = height;
|
||||
sanitize_terminal_size(&w, &h);
|
||||
client->width = w;
|
||||
client->height = h;
|
||||
client->redraw_pending = true;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
static void client_channel_eof(ssh_session session, ssh_channel channel,
|
||||
void *userdata) {
|
||||
(void)session;
|
||||
(void)channel;
|
||||
|
||||
client_t *client = (client_t *)userdata;
|
||||
if (client) {
|
||||
client->connected = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void client_channel_close(ssh_session session, ssh_channel channel,
|
||||
void *userdata) {
|
||||
(void)session;
|
||||
(void)channel;
|
||||
|
||||
client_t *client = (client_t *)userdata;
|
||||
if (client) {
|
||||
client->connected = false;
|
||||
}
|
||||
}
|
||||
|
||||
int client_install_channel_callbacks(client_t *client) {
|
||||
if (!client || !client->channel) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
client->channel_cb = calloc(1, sizeof(struct ssh_channel_callbacks_struct));
|
||||
if (!client->channel_cb) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssh_callbacks_init(client->channel_cb);
|
||||
client->channel_cb->userdata = client;
|
||||
client->channel_cb->channel_eof_function = client_channel_eof;
|
||||
client->channel_cb->channel_close_function = client_channel_close;
|
||||
client->channel_cb->channel_pty_window_change_function =
|
||||
client_channel_window_change;
|
||||
|
||||
if (ssh_set_channel_callbacks(client->channel, client->channel_cb) != SSH_OK) {
|
||||
free(client->channel_cb);
|
||||
client->channel_cb = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Initialize SSH server */
|
||||
int ssh_server_init(int port) {
|
||||
/* Initialize rate-limit / connection-count subsystem */
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "tui.h"
|
||||
#include "client.h"
|
||||
#include "ssh_server.h"
|
||||
#include "chat_room.h"
|
||||
#include "utf8.h"
|
||||
|
|
|
|||
Loading…
Reference in a new issue