//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG #include #include #include #include #include #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!"); }