You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
155 lines
3.8 KiB
155 lines
3.8 KiB
//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
|
|
|
#include <stdint.h>
|
|
#include <sys/socket.h>
|
|
#include <esp_log.h>
|
|
#include <string.h>
|
|
#include <freertos/ringbuf.h>
|
|
|
|
#include "socket_server.h"
|
|
#include "console_server.h"
|
|
#include "application.h"
|
|
#include "tasks.h"
|
|
#include "telnet_parser.h"
|
|
#include "console_ioimpl.h"
|
|
|
|
static const char *TAG = "console_srv";
|
|
|
|
Tcpd_t g_telnet_server = NULL;
|
|
TcpdClient_t telnetsrv_last_rx_client = NULL;
|
|
|
|
/**
|
|
* Send a textual message to all the connected peers
|
|
*
|
|
* @param interface
|
|
* @param packet
|
|
* @param timeout
|
|
* @return
|
|
*/
|
|
esp_err_t telnetsrv_send(TcpdClient_t client, const char *message, ssize_t len)
|
|
{
|
|
if (!g_telnet_server) {
|
|
ESP_LOGE(TAG, "server not inited");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
if (len < 0) len = strlen(message);
|
|
|
|
if (client) {
|
|
tcpd_send(client, (const uint8_t *) message, len);
|
|
} else {
|
|
tcpd_broadcast(g_telnet_server, (const uint8_t *) message, len);
|
|
}
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
/**
|
|
* Handle received bytes
|
|
*
|
|
* @param client
|
|
* @param buf
|
|
* @param nbytes
|
|
*/
|
|
static void telnetsrv_handle(TcpdClient_t client, char *buf, int nbytes)
|
|
{
|
|
void *ctx = tcpd_get_client_ctx(client);
|
|
struct console_ioimpl *io = ctx;
|
|
|
|
if (!ctx) {
|
|
ESP_LOGE(TAG, "telnet rx with no ioctx!");
|
|
return;
|
|
}
|
|
|
|
assert(CONSOLE_IOIMPL_MAGIC == io->__magic);
|
|
|
|
int rv = xRingbufferSend(io->telnet.console_stdin_ringbuf, buf, (size_t) nbytes, pdMS_TO_TICKS(100));
|
|
if (rv != pdPASS) {
|
|
ESP_LOGE(TAG, "console ringbuf overflow");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Socket read handler
|
|
*/
|
|
esp_err_t read_fn(Tcpd_t serv, TcpdClient_t client, int sockfd)
|
|
{
|
|
char buf[64];
|
|
int nbytes = read(sockfd, buf, sizeof(buf));
|
|
if (nbytes <= 0) return ESP_FAIL;
|
|
|
|
// ESP_LOGI(TAG, "Rx %d bytes", nbytes);
|
|
telnetsrv_handle(client, buf, nbytes);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
static void print_motd(console_ctx_t *ctx)
|
|
{
|
|
console_printf_ctx(ctx, COLOR_RESET, "\n"
|
|
"===================================================\n"
|
|
" ESP32 node "APP_NAME" "APP_VERSION " #" GIT_HASH "\n"
|
|
" Built " BUILD_TIMESTAMP "\n"
|
|
"\n"
|
|
" Run `ls` for a list of commands.\n"
|
|
"===================================================\n\n"
|
|
);
|
|
// show the initial prompt
|
|
console_print(ctx->prompt);
|
|
}
|
|
|
|
/**
|
|
* Socket open handler - send a MOTD
|
|
*/
|
|
static esp_err_t open_fn(Tcpd_t serv, TcpdClient_t client)
|
|
{
|
|
vTaskDelay(pdMS_TO_TICKS(100));
|
|
|
|
struct console_ioimpl *io = NULL;
|
|
esp_err_t rv = console_start_tcp(&io, NULL, client);
|
|
if (rv != ESP_OK) {
|
|
return rv;
|
|
}
|
|
assert(io);
|
|
|
|
// set telnet params, unless this is the injected stdin client
|
|
if (tcpd_get_client_fd(client) != STDIN_FILENO) {
|
|
telnet_send_will(&io->ctx, OPT_SUPPRESS_GO_AHEAD);
|
|
telnet_send_will(&io->ctx, OPT_ECHO);
|
|
telnet_send_dont(&io->ctx, OPT_ECHO);
|
|
}
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
void console_print_motd(console_ctx_t *ctx)
|
|
{
|
|
print_motd(ctx);
|
|
}
|
|
|
|
esp_err_t telnetsrv_start(uint16_t port)
|
|
{
|
|
tcpd_config_t server_config = TCPD_INIT_DEFAULT();
|
|
server_config.max_clients = 3;
|
|
server_config.task_prio = TELNET_TASK_PRIO;
|
|
server_config.task_stack = TELNET_TASK_STACK;
|
|
server_config.task_name = "TelnetSrv";
|
|
server_config.port = port;
|
|
server_config.read_fn = read_fn;
|
|
server_config.open_fn = open_fn;
|
|
// close fn is not needed, console shuts down if the FD becomes invalid (read fails)
|
|
|
|
ESP_ERROR_CHECK(tcpd_init(&server_config, &g_telnet_server));
|
|
|
|
// this deadlocks now... XXX
|
|
// cspemu_add_shutdown_handler(telnetsrv_kick_all);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
void telnetsrv_kick_all(void)
|
|
{
|
|
ESP_LOGI(TAG, "Kick all telnet clients");
|
|
tcpd_kick_all(g_telnet_server, false); // don't kick injected clients
|
|
ESP_LOGI(TAG, "Kicking done!");
|
|
}
|
|
|