[PATCH 5/6] telnetd: Create sessions at start
Sebastian Huber
sebastian.huber at embedded-brains.de
Wed Oct 10 12:12:32 UTC 2018
Update #3543.
---
cpukit/include/rtems/pty.h | 11 +-
cpukit/telnetd/pty.c | 33 +-
cpukit/telnetd/telnetd.c | 476 +++++++++++++++-------------
testsuites/libtests/telnetd01/init.c | 6 +-
testsuites/libtests/telnetd01/telnetd01.scn | 5 +-
5 files changed, 291 insertions(+), 240 deletions(-)
diff --git a/cpukit/include/rtems/pty.h b/cpukit/include/rtems/pty.h
index d8f886d76a..94b66d63c4 100644
--- a/cpukit/include/rtems/pty.h
+++ b/cpukit/include/rtems/pty.h
@@ -34,7 +34,16 @@ typedef struct {
char name[sizeof("/dev/pty18446744073709551615")];
} rtems_pty_context;
-char *telnet_get_pty(rtems_pty_context *ctx, int socket);
+const char *rtems_pty_initialize(rtems_pty_context *pty, uintptr_t unique);
+
+RTEMS_INLINE_ROUTINE const char *rtems_pty_get_path(const rtems_pty_context *pty)
+{
+ return pty->name;
+}
+
+void rtems_pty_close_socket(rtems_pty_context *pty);
+
+void rtems_pty_set_socket(rtems_pty_context *pty, int socket);
#ifdef __cplusplus
}
diff --git a/cpukit/telnetd/pty.c b/cpukit/telnetd/pty.c
index 7a47fddbc9..8bf550d313 100644
--- a/cpukit/telnetd/pty.c
+++ b/cpukit/telnetd/pty.c
@@ -94,26 +94,37 @@ int send_iac(rtems_pty_context *pty, unsigned char mode, unsigned char option)
return write(pty->socket, buf, sizeof(buf));
}
-/* This procedure returns the devname for a pty slot free.
- * If not slot availiable (field socket>=0)
- * then the socket argument is closed
- */
-
-char *telnet_get_pty(rtems_pty_context *pty, int socket)
+const char *rtems_pty_initialize(rtems_pty_context *pty, uintptr_t unique)
{
rtems_status_code sc;
- struct timeval t;
memset(pty, 0, sizeof(*pty));
- snprintf(pty->name, sizeof(pty->name), "/dev/pty%" PRIuPTR, (uintptr_t)pty);
+ snprintf(pty->name, sizeof(pty->name), "/dev/pty%" PRIuPTR, (uintptr_t)unique);
rtems_termios_device_context_initialize(&pty->base, "pty");
- pty->socket = socket;
+ pty->socket = -1;
sc = rtems_termios_device_install(pty->name, &pty_handler, NULL, &pty->base);
if (sc != RTEMS_SUCCESSFUL) {
- close(socket);
return NULL;
}
+ return pty->name;
+}
+
+void rtems_pty_close_socket(rtems_pty_context *pty)
+{
+ if (pty->socket >= 0) {
+ (void)close(pty->socket);
+ pty->socket = -1;
+ }
+}
+
+void rtems_pty_set_socket(rtems_pty_context *pty, int socket)
+{
+ struct timeval t;
+
+ rtems_pty_close_socket(pty);
+ pty->socket = socket;
+
/* set a long polling interval to save CPU time */
t.tv_sec=2;
t.tv_usec=00000;
@@ -121,8 +132,6 @@ char *telnet_get_pty(rtems_pty_context *pty, int socket)
/* inform the client that we will echo */
send_iac(pty, IAC_WILL, 1);
-
- return pty->name;
}
/*-----------------------------------------------------------*/
diff --git a/cpukit/telnetd/telnetd.c b/cpukit/telnetd/telnetd.c
index aadf33deb7..9f2dc25c93 100644
--- a/cpukit/telnetd/telnetd.c
+++ b/cpukit/telnetd/telnetd.c
@@ -23,13 +23,13 @@
* possible to have 'telnetd' run an arbitrary 'shell'
* program.
*
- * Copyright (c) 2009 embedded brains GmbH and others.
+ * Copyright (c) 2009, 2018 embedded brains GmbH and others.
*
- * embedded brains GmbH
- * Obere Lagerstr. 30
- * D-82178 Puchheim
- * Germany
- * <rtems at embedded-brains.de>
+ * embedded brains GmbH
+ * Dornierstr. 4
+ * D-82178 Puchheim
+ * Germany
+ * <rtems at embedded-brains.de>
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
@@ -40,6 +40,7 @@
#include "config.h"
#endif
+#include <sys/queue.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
@@ -54,201 +55,226 @@
#include <rtems/pty.h>
#include <rtems/shell.h>
#include <rtems/telnetd.h>
+#include <rtems/thread.h>
#include <rtems/userenv.h>
#ifdef RTEMS_NETWORKING
#include <rtems/rtems_bsdnet.h>
#endif
-#define PARANOIA
+#define TELNETD_EVENT_SUCCESS RTEMS_EVENT_0
+
+#define TELNETD_EVENT_ERROR RTEMS_EVENT_1
typedef struct telnetd_context telnetd_context;
typedef struct telnetd_session {
- rtems_pty_context pty;
- char peername[16];
- telnetd_context *ctx;
+ rtems_pty_context pty;
+ char peername[16];
+ telnetd_context *ctx;
+ rtems_id task_id;
+ LIST_ENTRY(telnetd_session) link;
} telnetd_session;
struct telnetd_context {
- rtems_telnetd_config_table config;
- int server_socket;
- uint16_t active_clients;
+ rtems_telnetd_config_table config;
+ int server_socket;
+ rtems_id task_id;
+ rtems_mutex mtx;
+ LIST_HEAD(, telnetd_session) free_sessions;
+ telnetd_session sessions[RTEMS_ZERO_LENGTH_ARRAY];
};
-typedef union uni_sa {
+typedef union {
struct sockaddr_in sin;
- struct sockaddr sa;
-} uni_sa;
+ struct sockaddr sa;
+} telnetd_address;
-static telnetd_session *grab_a_Connection(telnetd_context *ctx)
+RTEMS_NO_RETURN static void telnetd_session_fatal_error(
+ const telnetd_context *ctx
+)
{
- telnetd_session *session;
- uni_sa peer;
- socklen_t address_len;
- int acp_sock;
+ (void)rtems_event_send(ctx->task_id, TELNETD_EVENT_ERROR);
+ rtems_task_exit();
+}
- if (ctx->active_clients >= ctx->config.client_maximum) {
- return NULL;
+static bool telnetd_login(telnetd_context *ctx, telnetd_session *session)
+{
+ bool success;
+
+ if (ctx->config.login_check == NULL) {
+ return true;
}
- session = malloc(sizeof(*session));
- if (session == NULL) {
- perror("telnetd:malloc");
- return NULL;
+ success = rtems_shell_login_prompt(
+ stdin,
+ stderr,
+ session->pty.name,
+ ctx->config.login_check
+ );
+
+ if (!success) {
+ syslog(
+ LOG_AUTHPRIV | LOG_WARNING,
+ "telnetd: too many wrong passwords entered from %s",
+ session->peername
+ );
}
- address_len = sizeof(peer.sin);
- acp_sock = accept(ctx->server_socket, &peer.sa, &address_len);
- if (acp_sock<0) {
- perror("telnetd:accept");
- free(session);
- return NULL;
- };
+ return success;
+}
- if (telnet_get_pty(&session->pty, acp_sock) == NULL) {
- syslog( LOG_DAEMON | LOG_ERR, "telnetd: unable to obtain PTY");
- /* NOTE: failing 'do_get_pty()' closed the socket */
- free(session);
- return NULL;
+static void telnetd_session_task(rtems_task_argument arg)
+{
+ rtems_status_code sc;
+ telnetd_session *session;
+ telnetd_context *ctx;
+ const char *path;
+
+ session = (telnetd_session *) arg;
+ ctx = session->ctx;
+
+ sc = rtems_libio_set_private_env();
+ if (sc != RTEMS_SUCCESSFUL) {
+ telnetd_session_fatal_error(ctx);
}
- if (
- inet_ntop(
- AF_INET,
- &peer.sin.sin_addr,
- session->peername,
- sizeof(session->peername)
- ) == NULL
- ) {
- strlcpy(session->peername, "<UNKNOWN>", sizeof(session->peername));
+ path = rtems_pty_get_path(&session->pty);
+
+ stdin = fopen(path, "r+");
+ if (stdin == NULL) {
+ telnetd_session_fatal_error(ctx);
}
-#ifdef PARANOIA
- syslog(LOG_DAEMON | LOG_INFO,
- "telnetd: accepted connection from %s on %s",
- session->peername,
- session->pty.name);
-#endif
+ stdout = fopen(path, "r+");
+ if (stdout == NULL) {
+ telnetd_session_fatal_error(ctx);
+ }
- ++ctx->active_clients;
- session->ctx = ctx;
- return session;
-}
+ stderr = fopen(path, "r+");
+ if (stderr == NULL) {
+ telnetd_session_fatal_error(ctx);
+ }
+ (void)rtems_event_send(ctx->task_id, TELNETD_EVENT_SUCCESS);
-static void release_a_Connection(
- telnetd_context *ctx,
- telnetd_session *session,
- FILE **pstd,
- int n
-)
-{
-#ifdef PARANOIA
- syslog(
- LOG_DAEMON | LOG_INFO,
- "telnetd: releasing connection from %s on %s",
- session->peername,
- session->pty.name
- );
-#endif
+ while (true) {
+ rtems_event_set events;
- --ctx->active_clients;
+ (void)rtems_event_system_receive(
+ RTEMS_EVENT_SYSTEM_SERVER,
+ RTEMS_WAIT | RTEMS_EVENT_ALL,
+ RTEMS_NO_TIMEOUT,
+ &events
+ );
- while (--n>=0)
- if (pstd[n]) fclose(pstd[n]);
+ syslog(
+ LOG_DAEMON | LOG_INFO,
+ "telnetd: accepted connection from %s on %s",
+ session->peername,
+ path
+ );
- unlink(session->pty.name);
-}
+ if (telnetd_login(ctx, session)) {
+ (*ctx->config.command)(session->pty.name, ctx->config.arg);
+ }
-static rtems_id telnetd_spawn_task(
- rtems_name name,
- rtems_task_priority priority,
- size_t stack_size,
- rtems_task_entry entry,
- void *arg
-)
-{
- rtems_status_code sc;
- rtems_id task_id;
+ syslog(
+ LOG_DAEMON | LOG_INFO,
+ "telnetd: releasing connection from %s on %s",
+ session->peername,
+ path
+ );
- sc = rtems_task_create(
- name,
- priority,
- stack_size,
- RTEMS_DEFAULT_MODES,
- RTEMS_FLOATING_POINT,
- &task_id
- );
- if (sc != RTEMS_SUCCESSFUL) {
- return RTEMS_ID_NONE;
- }
+ rtems_pty_close_socket(&session->pty);
- (void)rtems_task_start(task_id, entry, (rtems_task_argument) arg);
- return task_id;
+ rtems_mutex_lock(&ctx->mtx);
+ LIST_INSERT_HEAD(&ctx->free_sessions, session, link);
+ rtems_mutex_unlock(&ctx->mtx);
+ }
}
-static void
-telnetd_session_task(rtems_task_argument arg);
+static void telnetd_sleep_after_error(void)
+{
+ /* If something went wrong, sleep for some time */
+ sleep(10);
+}
-/***********************************************************/
-static void
-telnetd_server_task(rtems_task_argument arg)
+static void telnetd_server_task(rtems_task_argument arg)
{
- telnetd_session *session = NULL;
- rtems_id task_id;
- telnetd_context *ctx = (telnetd_context *) arg;
+ telnetd_context *ctx;
- /* we don't redirect stdio as this probably
- * was started from the console anyway ..
- */
- do {
- session = grab_a_Connection(ctx);
+ ctx = (telnetd_context *) arg;
+
+ while (true) {
+ telnetd_address peer;
+ socklen_t address_len;
+ int session_socket;
+ telnetd_session *session;
+
+ address_len = sizeof(peer.sin);
+ session_socket = accept(ctx->server_socket, &peer.sa, &address_len);
+ if (session_socket < 0) {
+ syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot accept session");
+ telnetd_sleep_after_error();
+ continue;
+ };
+
+ rtems_mutex_lock(&ctx->mtx);
+ session = LIST_FIRST(&ctx->free_sessions);
if (session == NULL) {
- /* if something went wrong, sleep for some time */
- sleep(10);
+ rtems_mutex_unlock(&ctx->mtx);
+
+ (void)close(session_socket);
+ syslog(LOG_DAEMON | LOG_ERR, "telnetd: no free session available");
+ telnetd_sleep_after_error();
continue;
}
- task_id = telnetd_spawn_task(
- rtems_build_name('T', 'N', 'T', 'a'),
- ctx->config.priority,
- ctx->config.stack_size,
- telnetd_session_task,
- session
- );
- if (task_id == RTEMS_ID_NONE) {
- FILE *dummy;
-
- /* hmm - the pty driver slot can only be
- * released by opening and subsequently
- * closing the PTY - this also closes
- * the underlying socket. So we mock up
- * a stream...
- */
-
- if ( !(dummy=fopen(session->pty.name,"r+")) )
- perror("Unable to dummy open the pty, losing a slot :-(");
- release_a_Connection(ctx, session, &dummy, 1);
- free(session);
- sleep(2); /* don't accept connections too fast */
+ LIST_REMOVE(session, link);
+ rtems_mutex_unlock(&ctx->mtx);
+
+ rtems_pty_set_socket(&session->pty, session_socket);
+
+ if (
+ inet_ntop(
+ AF_INET,
+ &peer.sin.sin_addr,
+ session->peername,
+ sizeof(session->peername)
+ ) == NULL
+ ) {
+ strlcpy(session->peername, "<UNKNOWN>", sizeof(session->peername));
}
- } while(1);
+
+ (void)rtems_event_system_send(session->task_id, RTEMS_EVENT_SYSTEM_SERVER);
+ }
}
static void telnetd_destroy_context(telnetd_context *ctx)
{
+ telnetd_session *session;
+
+ LIST_FOREACH(session, &ctx->free_sessions, link) {
+ if (session->task_id != 0) {
+ (void)rtems_task_delete(session->task_id);
+ }
+
+ (void)unlink(rtems_pty_get_path(&session->pty));
+ }
+
if (ctx->server_socket >= 0) {
- close(ctx->server_socket);
+ (void)close(ctx->server_socket);
}
+ rtems_mutex_destroy(&ctx->mtx);
free(ctx);
}
static rtems_status_code telnetd_create_server_socket(telnetd_context *ctx)
{
- uni_sa srv;
+ telnetd_address srv;
socklen_t address_len;
int enable;
@@ -285,25 +311,94 @@ static rtems_status_code telnetd_create_server_socket(telnetd_context *ctx)
return RTEMS_SUCCESSFUL;
}
+static rtems_status_code telnetd_create_session_tasks(telnetd_context *ctx)
+{
+ uint16_t i;
+
+ ctx->task_id = rtems_task_self();
+
+ for (i = 0; i < ctx->config.client_maximum; ++i) {
+ telnetd_session *session;
+ rtems_status_code sc;
+ const char *path;
+ rtems_event_set events;
+
+ session = &ctx->sessions[i];
+ session->ctx = ctx;
+ rtems_mutex_init(&ctx->mtx, "Telnet");
+ LIST_INSERT_HEAD(&ctx->free_sessions, session, link);
+
+ sc = rtems_task_create(
+ rtems_build_name('T', 'N', 'T', 'a' + i % 26),
+ ctx->config.priority,
+ ctx->config.stack_size,
+ RTEMS_DEFAULT_MODES,
+ RTEMS_FLOATING_POINT,
+ &session->task_id
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create session task");
+ return RTEMS_UNSATISFIED;
+ }
+
+ path = rtems_pty_initialize(&session->pty, i);
+ if (path == NULL) {
+ syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create session PTY");
+ return RTEMS_UNSATISFIED;
+ }
+
+ (void)rtems_task_start(
+ session->task_id,
+ telnetd_session_task,
+ (rtems_task_argument) session
+ );
+
+ (void)rtems_event_receive(
+ TELNETD_EVENT_SUCCESS | TELNETD_EVENT_ERROR,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events
+ );
+
+ if ((events & TELNETD_EVENT_ERROR) != 0) {
+ syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot initialize session task");
+ return RTEMS_UNSATISFIED;
+ }
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
rtems_status_code rtems_telnetd_start(const rtems_telnetd_config_table* config)
{
telnetd_context *ctx;
- rtems_id task_id;
rtems_status_code sc;
+ uint16_t client_maximum;
if (config->command == NULL) {
syslog(LOG_DAEMON | LOG_ERR, "telnetd: configuration with invalid command");
return RTEMS_INVALID_ADDRESS;
}
- ctx = calloc(1, sizeof(*ctx));
+ if (config->client_maximum == 0) {
+ client_maximum = 5;
+ } else {
+ client_maximum = config->client_maximum;
+ }
+
+ ctx = calloc(
+ 1,
+ sizeof(*ctx) + client_maximum * sizeof(ctx->sessions[0])
+ );
if (ctx == NULL) {
syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot allocate server context");
return RTEMS_UNSATISFIED;
}
ctx->config = *config;
+ ctx->config.client_maximum = client_maximum;
ctx->server_socket = -1;
+ LIST_INIT(&ctx->free_sessions);
/* Check priority */
#ifdef RTEMS_NETWORKING
@@ -320,101 +415,38 @@ rtems_status_code rtems_telnetd_start(const rtems_telnetd_config_table* config)
ctx->config.stack_size = (size_t)32 * 1024;
}
- if (ctx->config.client_maximum == 0) {
- ctx->config.client_maximum = 5;
+ sc = telnetd_create_server_socket(ctx);
+ if (sc != RTEMS_SUCCESSFUL) {
+ telnetd_destroy_context(ctx);
+ return sc;
}
- sc = telnetd_create_server_socket(ctx);
+ sc = telnetd_create_session_tasks(ctx);
if (sc != RTEMS_SUCCESSFUL) {
telnetd_destroy_context(ctx);
return sc;
}
- task_id = telnetd_spawn_task(
+ sc = rtems_task_create(
rtems_build_name('T', 'N', 'T', 'D'),
ctx->config.priority,
RTEMS_MINIMUM_STACK_SIZE,
- telnetd_server_task,
- ctx
+ RTEMS_DEFAULT_MODES,
+ RTEMS_FLOATING_POINT,
+ &ctx->task_id
);
- if (task_id == RTEMS_ID_NONE) {
- ctx->config.command = NULL;
+ if (sc != RTEMS_SUCCESSFUL) {
syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create server task");
telnetd_destroy_context(ctx);
return RTEMS_UNSATISFIED;
}
+ (void)rtems_task_start(
+ ctx->task_id,
+ telnetd_server_task,
+ (rtems_task_argument) ctx
+ );
+
syslog(LOG_DAEMON | LOG_INFO, "telnetd: started successfully");
return RTEMS_SUCCESSFUL;
}
-
-/* utility wrapper */
-static void
-telnetd_session_task(rtems_task_argument arg)
-{
- rtems_status_code sc;
- FILE *nstd[3]={0};
- FILE *ostd[3]={ stdin, stdout, stderr };
- int i=0;
- telnetd_session *session = (telnetd_session *) arg;
- telnetd_context *ctx = session->ctx;
- bool login_failed = false;
- bool start = true;
-
- sc=rtems_libio_set_private_env();
-
- /* newlib hack/workaround. Before we change stdin/out/err we must make
- * sure the internal data are initialized (fileno(stdout) has this sideeffect).
- * This should probably be done from RTEMS' libc support layer...
- * (T.S., newlibc-1.13; 2005/10)
- */
-
- fileno(stdout);
-
- if (RTEMS_SUCCESSFUL != sc) {
- rtems_error(sc,"rtems_libio_set_private_env");
- goto cleanup;
- }
-
- /* redirect stdio */
- for (i=0; i<3; i++) {
- if ( !(nstd[i]=fopen(session->pty.name,"r+")) ) {
- perror("unable to open stdio");
- goto cleanup;
- }
- }
-
- stdin = nstd[0];
- stdout = nstd[1];
- stderr = nstd[2];
-
- /* call their routine */
- if (ctx->config.login_check != NULL) {
- start = rtems_shell_login_prompt(
- stdin,
- stderr,
- session->pty.name,
- ctx->config.login_check
- );
- login_failed = !start;
- }
- if (start) {
- ctx->config.command( session->pty.name, ctx->config.arg);
- }
-
- stdin = ostd[0];
- stdout = ostd[1];
- stderr = ostd[2];
-
- if (login_failed) {
- syslog(
- LOG_AUTHPRIV | LOG_WARNING,
- "telnetd: to many wrong passwords entered from %s",
- session->peername
- );
- }
-
-cleanup:
- release_a_Connection(ctx, session, nstd, i);
- free(session);
-}
diff --git a/testsuites/libtests/telnetd01/init.c b/testsuites/libtests/telnetd01/init.c
index f2243d9dd8..e7c2540840 100644
--- a/testsuites/libtests/telnetd01/init.c
+++ b/testsuites/libtests/telnetd01/init.c
@@ -105,9 +105,11 @@ static rtems_task Init(rtems_task_argument argument)
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
-#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 32
+#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS (3 + 1 + 5 * 4)
-#define CONFIGURE_MAXIMUM_TASKS 7
+#define CONFIGURE_MAXIMUM_TASKS 8
+
+#define CONFIGURE_MAXIMUM_POSIX_KEYS 1
#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
diff --git a/testsuites/libtests/telnetd01/telnetd01.scn b/testsuites/libtests/telnetd01/telnetd01.scn
index 2dc2c60846..31439da5a0 100644
--- a/testsuites/libtests/telnetd01/telnetd01.scn
+++ b/testsuites/libtests/telnetd01/telnetd01.scn
@@ -4,9 +4,8 @@
*** TEST BUILD: RTEMS_NETWORKING
*** TEST TOOLS: 7.3.0 20180125 (RTEMS 5, RSB 9670d7541e0621915e521fe76e7bb33de8cee661, Newlib d13c84eb07e35984bf7a974cd786a6cdac29e6b9)
syslog: telnetd: configuration with invalid command
-Telnetd: spawning task failed (status: RTEMS_INVALID_PRIORITY)
-syslog: telnetd: cannot create server task
+syslog: telnetd: cannot create session task
syslog: telnetd: started successfully
-syslog: telnetd: already started
+syslog: telnetd: cannot bind server socket
*** END OF TEST TELNETD 1 ***
--
2.16.4
More information about the devel
mailing list