Changeset 0dc303f in rtems


Ignore:
Timestamp:
Oct 10, 2018, 8:40:21 AM (7 months ago)
Author:
Sebastian Huber <sebastian.huber@…>
Branches:
master
Children:
26b58b7e
Parents:
0f0e130
git-author:
Sebastian Huber <sebastian.huber@…> (10/10/18 08:40:21)
git-committer:
Sebastian Huber <sebastian.huber@…> (10/11/18 07:08:06)
Message:

telnetd: Create sessions at start

Update #3543.

Files:
5 edited

Legend:

Unmodified
Added
Removed
  • cpukit/include/rtems/pty.h

    r0f0e130 r0dc303f  
    3535} rtems_pty_context;
    3636
    37 char *telnet_get_pty(rtems_pty_context *ctx, int socket);
     37const char *rtems_pty_initialize(rtems_pty_context *pty, uintptr_t unique);
     38
     39RTEMS_INLINE_ROUTINE const char *rtems_pty_get_path(const rtems_pty_context *pty)
     40{
     41  return pty->name;
     42}
     43
     44void rtems_pty_close_socket(rtems_pty_context *pty);
     45
     46void rtems_pty_set_socket(rtems_pty_context *pty, int socket);
    3847
    3948#ifdef __cplusplus
  • cpukit/telnetd/pty.c

    r0f0e130 r0dc303f  
    9595}
    9696
    97 /* This procedure returns the devname for a pty slot free.
    98  * If not slot availiable (field socket>=0)
    99  *  then the socket argument is closed
    100  */
    101 
    102 char *telnet_get_pty(rtems_pty_context *pty, int socket)
     97const char *rtems_pty_initialize(rtems_pty_context *pty, uintptr_t unique)
    10398{
    10499  rtems_status_code sc;
    105   struct timeval t;
    106100
    107101  memset(pty, 0, sizeof(*pty));
    108   snprintf(pty->name, sizeof(pty->name), "/dev/pty%" PRIuPTR, (uintptr_t)pty);
     102  (void)snprintf(pty->name, sizeof(pty->name), "/dev/pty%" PRIuPTR, unique);
    109103  rtems_termios_device_context_initialize(&pty->base, "pty");
    110   pty->socket = socket;
     104  pty->socket = -1;
    111105  sc = rtems_termios_device_install(pty->name, &pty_handler, NULL, &pty->base);
    112106  if (sc != RTEMS_SUCCESSFUL) {
    113     close(socket);
    114107    return NULL;
    115108  }
     109
     110  return pty->name;
     111}
     112
     113void rtems_pty_close_socket(rtems_pty_context *pty)
     114{
     115  if (pty->socket >= 0) {
     116    (void)close(pty->socket);
     117    pty->socket = -1;
     118  }
     119}
     120
     121void rtems_pty_set_socket(rtems_pty_context *pty, int socket)
     122{
     123  struct timeval t;
     124
     125  rtems_pty_close_socket(pty);
     126  pty->socket = socket;
    116127
    117128  /* set a long polling interval to save CPU time */
     
    122133  /* inform the client that we will echo */
    123134  send_iac(pty, IAC_WILL, 1);
    124 
    125   return pty->name;
    126135}
    127136
  • cpukit/telnetd/telnetd.c

    r0f0e130 r0dc303f  
    2424 *    program.
    2525 *
    26  * Copyright (c) 2009 embedded brains GmbH and others.
    27  *
    28  * embedded brains GmbH
    29  * Obere Lagerstr. 30
    30  * D-82178 Puchheim
    31  * Germany
    32  * <rtems@embedded-brains.de>
     26 * Copyright (c) 2009, 2018 embedded brains GmbH and others.
     27 *
     28 *   embedded brains GmbH
     29 *   Dornierstr. 4
     30 *   D-82178 Puchheim
     31 *   Germany
     32 *   <rtems@embedded-brains.de>
    3333 *
    3434 * The license and distribution terms for this file may be
     
    4141#endif
    4242
     43#include <sys/queue.h>
    4344#include <sys/socket.h>
    4445#include <netinet/in.h>
     
    5556#include <rtems/shell.h>
    5657#include <rtems/telnetd.h>
     58#include <rtems/thread.h>
    5759#include <rtems/userenv.h>
    5860
     
    6163#endif
    6264
    63 #define PARANOIA
     65#define TELNETD_EVENT_SUCCESS RTEMS_EVENT_0
     66
     67#define TELNETD_EVENT_ERROR RTEMS_EVENT_1
    6468
    6569typedef struct telnetd_context telnetd_context;
    6670
    6771typedef struct telnetd_session {
    68   rtems_pty_context  pty;
    69   char               peername[16];
    70   telnetd_context   *ctx;
     72  rtems_pty_context            pty;
     73  char                         peername[16];
     74  telnetd_context             *ctx;
     75  rtems_id                     task_id;
     76  LIST_ENTRY(telnetd_session)  link;
    7177} telnetd_session;
    7278
    7379struct telnetd_context {
    74   rtems_telnetd_config_table config;
    75   int                        server_socket;
    76   uint16_t                   active_clients;
     80  rtems_telnetd_config_table   config;
     81  int                          server_socket;
     82  rtems_id                     task_id;
     83  rtems_mutex                  mtx;
     84  LIST_HEAD(, telnetd_session) free_sessions;
     85  telnetd_session              sessions[RTEMS_ZERO_LENGTH_ARRAY];
    7786};
    7887
    79 typedef union uni_sa {
     88typedef union {
    8089  struct sockaddr_in sin;
    81   struct sockaddr     sa;
    82 } uni_sa;
    83 
    84 static telnetd_session *grab_a_Connection(telnetd_context *ctx)
    85 {
     90  struct sockaddr    sa;
     91} telnetd_address;
     92
     93RTEMS_NO_RETURN static void telnetd_session_fatal_error(
     94  const telnetd_context *ctx
     95)
     96{
     97  (void)rtems_event_send(ctx->task_id, TELNETD_EVENT_ERROR);
     98  rtems_task_exit();
     99}
     100
     101static bool telnetd_login(telnetd_context *ctx, telnetd_session *session)
     102{
     103  bool success;
     104
     105  if (ctx->config.login_check == NULL) {
     106    return true;
     107  }
     108
     109  success = rtems_shell_login_prompt(
     110    stdin,
     111    stderr,
     112    session->pty.name,
     113    ctx->config.login_check
     114  );
     115
     116  if (!success) {
     117    syslog(
     118      LOG_AUTHPRIV | LOG_WARNING,
     119      "telnetd: too many wrong passwords entered from %s",
     120      session->peername
     121    );
     122  }
     123
     124  return success;
     125}
     126
     127static void telnetd_session_task(rtems_task_argument arg)
     128{
     129  rtems_status_code sc;
    86130  telnetd_session *session;
    87   uni_sa peer;
    88   socklen_t address_len;
    89   int acp_sock;
    90 
    91   if (ctx->active_clients >= ctx->config.client_maximum) {
    92     return NULL;
    93   }
    94 
    95   session = malloc(sizeof(*session));
    96   if (session == NULL) {
    97     perror("telnetd:malloc");
    98     return NULL;
    99   }
    100 
    101   address_len = sizeof(peer.sin);
    102   acp_sock = accept(ctx->server_socket, &peer.sa, &address_len);
    103   if (acp_sock<0) {
    104     perror("telnetd:accept");
    105     free(session);
    106     return NULL;
    107   };
    108 
    109   if (telnet_get_pty(&session->pty, acp_sock) == NULL) {
    110     syslog( LOG_DAEMON | LOG_ERR, "telnetd: unable to obtain PTY");
    111     /* NOTE: failing 'do_get_pty()' closed the socket */
    112     free(session);
    113     return NULL;
    114   }
    115 
    116   if (
    117     inet_ntop(
    118       AF_INET,
    119       &peer.sin.sin_addr,
    120       session->peername,
    121       sizeof(session->peername)
    122     ) == NULL
    123   ) {
    124     strlcpy(session->peername, "<UNKNOWN>", sizeof(session->peername));
    125   }
    126 
    127 #ifdef PARANOIA
    128   syslog(LOG_DAEMON | LOG_INFO,
     131  telnetd_context *ctx;
     132  const char *path;
     133
     134  session = (telnetd_session *) arg;
     135  ctx = session->ctx;
     136
     137  sc = rtems_libio_set_private_env();
     138  if (sc != RTEMS_SUCCESSFUL) {
     139    telnetd_session_fatal_error(ctx);
     140  }
     141
     142  path = rtems_pty_get_path(&session->pty);
     143
     144  stdin = fopen(path, "r+");
     145  if (stdin == NULL) {
     146    telnetd_session_fatal_error(ctx);
     147  }
     148
     149  stdout = fopen(path, "r+");
     150  if (stdout == NULL) {
     151    telnetd_session_fatal_error(ctx);
     152  }
     153
     154  stderr = fopen(path, "r+");
     155  if (stderr == NULL) {
     156    telnetd_session_fatal_error(ctx);
     157  }
     158
     159  (void)rtems_event_send(ctx->task_id, TELNETD_EVENT_SUCCESS);
     160
     161  while (true) {
     162    rtems_event_set events;
     163
     164    (void)rtems_event_system_receive(
     165      RTEMS_EVENT_SYSTEM_SERVER,
     166      RTEMS_WAIT | RTEMS_EVENT_ALL,
     167      RTEMS_NO_TIMEOUT,
     168      &events
     169    );
     170
     171    syslog(
     172      LOG_DAEMON | LOG_INFO,
    129173      "telnetd: accepted connection from %s on %s",
    130174      session->peername,
    131       session->pty.name);
    132 #endif
    133 
    134   ++ctx->active_clients;
    135   session->ctx = ctx;
    136   return session;
    137 }
    138 
    139 
    140 static void release_a_Connection(
    141   telnetd_context *ctx,
    142   telnetd_session *session,
    143   FILE **pstd,
    144   int n
    145 )
    146 {
    147 #ifdef PARANOIA
    148   syslog(
    149     LOG_DAEMON | LOG_INFO,
    150     "telnetd: releasing connection from %s on %s",
    151     session->peername,
    152     session->pty.name
    153   );
    154 #endif
    155 
    156   --ctx->active_clients;
    157 
    158   while (--n>=0)
    159     if (pstd[n]) fclose(pstd[n]);
    160 
    161   unlink(session->pty.name);
    162 }
    163 
    164 static rtems_id telnetd_spawn_task(
    165   rtems_name name,
    166   rtems_task_priority priority,
    167   size_t stack_size,
    168   rtems_task_entry entry,
    169   void *arg
    170 )
    171 {
    172   rtems_status_code sc;
    173   rtems_id task_id;
    174 
    175   sc = rtems_task_create(
    176     name,
    177     priority,
    178     stack_size,
    179     RTEMS_DEFAULT_MODES,
    180     RTEMS_FLOATING_POINT,
    181     &task_id
    182   );
    183   if (sc != RTEMS_SUCCESSFUL) {
    184     return RTEMS_ID_NONE;
    185   }
    186 
    187   (void)rtems_task_start(task_id, entry, (rtems_task_argument) arg);
    188   return task_id;
    189 }
    190 
    191 static void
    192 telnetd_session_task(rtems_task_argument arg);
    193 
    194 /***********************************************************/
    195 static void
    196 telnetd_server_task(rtems_task_argument arg)
    197 {
    198   telnetd_session   *session = NULL;
    199   rtems_id           task_id;
    200   telnetd_context   *ctx = (telnetd_context *) arg;
    201 
    202   /* we don't redirect stdio as this probably
    203    * was started from the console anyway ..
    204    */
    205   do {
    206     session = grab_a_Connection(ctx);
     175      path
     176    );
     177
     178    if (telnetd_login(ctx, session)) {
     179      (*ctx->config.command)(session->pty.name, ctx->config.arg);
     180    }
     181
     182    syslog(
     183      LOG_DAEMON | LOG_INFO,
     184      "telnetd: releasing connection from %s on %s",
     185      session->peername,
     186      path
     187    );
     188
     189    rtems_pty_close_socket(&session->pty);
     190
     191    rtems_mutex_lock(&ctx->mtx);
     192    LIST_INSERT_HEAD(&ctx->free_sessions, session, link);
     193    rtems_mutex_unlock(&ctx->mtx);
     194  }
     195}
     196
     197static void telnetd_sleep_after_error(void)
     198{
     199      /* If something went wrong, sleep for some time */
     200      rtems_task_wake_after(10 * rtems_clock_get_ticks_per_second());
     201}
     202
     203static void telnetd_server_task(rtems_task_argument arg)
     204{
     205  telnetd_context *ctx;
     206
     207  ctx = (telnetd_context *) arg;
     208
     209  while (true) {
     210    telnetd_address peer;
     211    socklen_t address_len;
     212    int session_socket;
     213    telnetd_session *session;
     214
     215    address_len = sizeof(peer.sin);
     216    session_socket = accept(ctx->server_socket, &peer.sa, &address_len);
     217    if (session_socket < 0) {
     218      syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot accept session");
     219      telnetd_sleep_after_error();
     220      continue;
     221    };
     222
     223    rtems_mutex_lock(&ctx->mtx);
     224    session = LIST_FIRST(&ctx->free_sessions);
    207225
    208226    if (session == NULL) {
    209       /* if something went wrong, sleep for some time */
    210       sleep(10);
     227      rtems_mutex_unlock(&ctx->mtx);
     228
     229      (void)close(session_socket);
     230      syslog(LOG_DAEMON | LOG_ERR, "telnetd: no free session available");
     231      telnetd_sleep_after_error();
    211232      continue;
    212233    }
    213234
    214     task_id = telnetd_spawn_task(
    215       rtems_build_name('T', 'N', 'T', 'a'),
    216       ctx->config.priority,
    217       ctx->config.stack_size,
    218       telnetd_session_task,
    219       session
    220     );
    221     if (task_id == RTEMS_ID_NONE) {
    222       FILE *dummy;
    223 
    224       /* hmm - the pty driver slot can only be
    225        * released by opening and subsequently
    226        * closing the PTY - this also closes
    227        * the underlying socket. So we mock up
    228        * a stream...
    229        */
    230 
    231       if ( !(dummy=fopen(session->pty.name,"r+")) )
    232         perror("Unable to dummy open the pty, losing a slot :-(");
    233       release_a_Connection(ctx, session, &dummy, 1);
    234       free(session);
    235       sleep(2); /* don't accept connections too fast */
    236     }
    237   } while(1);
     235    LIST_REMOVE(session, link);
     236    rtems_mutex_unlock(&ctx->mtx);
     237
     238    rtems_pty_set_socket(&session->pty, session_socket);
     239
     240    if (
     241      inet_ntop(
     242        AF_INET,
     243        &peer.sin.sin_addr,
     244        session->peername,
     245        sizeof(session->peername)
     246      ) == NULL
     247    ) {
     248      strlcpy(session->peername, "<UNKNOWN>", sizeof(session->peername));
     249    }
     250
     251    (void)rtems_event_system_send(session->task_id, RTEMS_EVENT_SYSTEM_SERVER);
     252  }
    238253}
    239254
    240255static void telnetd_destroy_context(telnetd_context *ctx)
    241256{
     257  telnetd_session *session;
     258
     259  LIST_FOREACH(session, &ctx->free_sessions, link) {
     260    if (session->task_id != 0) {
     261      (void)rtems_task_delete(session->task_id);
     262    }
     263
     264    (void)unlink(rtems_pty_get_path(&session->pty));
     265  }
     266
    242267  if (ctx->server_socket >= 0) {
    243     close(ctx->server_socket);
    244   }
    245 
     268    (void)close(ctx->server_socket);
     269  }
     270
     271  rtems_mutex_destroy(&ctx->mtx);
    246272  free(ctx);
    247273}
     
    249275static rtems_status_code telnetd_create_server_socket(telnetd_context *ctx)
    250276{
    251   uni_sa srv;
     277  telnetd_address srv;
    252278  socklen_t address_len;
    253279  int enable;
     
    286312}
    287313
     314static rtems_status_code telnetd_create_session_tasks(telnetd_context *ctx)
     315{
     316  uint16_t i;
     317
     318  ctx->task_id = rtems_task_self();
     319
     320  for (i = 0; i < ctx->config.client_maximum; ++i) {
     321    telnetd_session *session;
     322    rtems_status_code sc;
     323    const char *path;
     324    rtems_event_set events;
     325
     326    session = &ctx->sessions[i];
     327    session->ctx = ctx;
     328    rtems_mutex_init(&ctx->mtx, "Telnet");
     329    LIST_INSERT_HEAD(&ctx->free_sessions, session, link);
     330
     331    sc = rtems_task_create(
     332      rtems_build_name('T', 'N', 'T', 'a' + i % 26),
     333      ctx->config.priority,
     334      ctx->config.stack_size,
     335      RTEMS_DEFAULT_MODES,
     336      RTEMS_FLOATING_POINT,
     337      &session->task_id
     338    );
     339    if (sc != RTEMS_SUCCESSFUL) {
     340      syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create session task");
     341      return RTEMS_UNSATISFIED;
     342    }
     343
     344    path = rtems_pty_initialize(&session->pty, i);
     345    if (path == NULL) {
     346      syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create session PTY");
     347      return RTEMS_UNSATISFIED;
     348    }
     349
     350    (void)rtems_task_start(
     351      session->task_id,
     352      telnetd_session_task,
     353      (rtems_task_argument) session
     354    );
     355
     356    (void)rtems_event_receive(
     357      TELNETD_EVENT_SUCCESS | TELNETD_EVENT_ERROR,
     358      RTEMS_WAIT | RTEMS_EVENT_ANY,
     359      RTEMS_NO_TIMEOUT,
     360      &events
     361    );
     362
     363    if ((events & TELNETD_EVENT_ERROR) != 0) {
     364      syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot initialize session task");
     365      return RTEMS_UNSATISFIED;
     366    }
     367  }
     368
     369  return RTEMS_SUCCESSFUL;
     370}
     371
    288372rtems_status_code rtems_telnetd_start(const rtems_telnetd_config_table* config)
    289373{
    290374  telnetd_context *ctx;
    291   rtems_id task_id;
    292375  rtems_status_code sc;
     376  uint16_t client_maximum;
    293377
    294378  if (config->command == NULL) {
     
    297381  }
    298382
    299   ctx = calloc(1, sizeof(*ctx));
     383  if (config->client_maximum == 0) {
     384    client_maximum = 5;
     385  } else {
     386    client_maximum = config->client_maximum;
     387  }
     388
     389  ctx = calloc(
     390    1,
     391    sizeof(*ctx) + client_maximum * sizeof(ctx->sessions[0])
     392  );
    300393  if (ctx == NULL) {
    301394    syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot allocate server context");
     
    304397
    305398  ctx->config = *config;
     399  ctx->config.client_maximum = client_maximum;
    306400  ctx->server_socket = -1;
     401  LIST_INIT(&ctx->free_sessions);
    307402
    308403  /* Check priority */
     
    321416  }
    322417
    323   if (ctx->config.client_maximum == 0) {
    324     ctx->config.client_maximum = 5;
    325   }
    326 
    327418  sc = telnetd_create_server_socket(ctx);
    328419  if (sc != RTEMS_SUCCESSFUL) {
     
    331422  }
    332423
    333   task_id = telnetd_spawn_task(
     424  sc = telnetd_create_session_tasks(ctx);
     425  if (sc != RTEMS_SUCCESSFUL) {
     426    telnetd_destroy_context(ctx);
     427    return sc;
     428  }
     429
     430  sc = rtems_task_create(
    334431    rtems_build_name('T', 'N', 'T', 'D'),
    335432    ctx->config.priority,
    336433    RTEMS_MINIMUM_STACK_SIZE,
    337     telnetd_server_task,
    338     ctx
     434    RTEMS_DEFAULT_MODES,
     435    RTEMS_FLOATING_POINT,
     436    &ctx->task_id
    339437  );
    340   if (task_id == RTEMS_ID_NONE) {
    341     ctx->config.command = NULL;
     438  if (sc != RTEMS_SUCCESSFUL) {
    342439    syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create server task");
    343440    telnetd_destroy_context(ctx);
     
    345442  }
    346443
     444  (void)rtems_task_start(
     445    ctx->task_id,
     446    telnetd_server_task,
     447    (rtems_task_argument) ctx
     448  );
     449
    347450  syslog(LOG_DAEMON | LOG_INFO, "telnetd: started successfully");
    348451  return RTEMS_SUCCESSFUL;
    349452}
    350 
    351 /* utility wrapper */
    352 static void
    353 telnetd_session_task(rtems_task_argument arg)
    354 {
    355   rtems_status_code    sc;
    356   FILE                *nstd[3]={0};
    357   FILE                *ostd[3]={ stdin, stdout, stderr };
    358   int                  i=0;
    359   telnetd_session    *session = (telnetd_session *) arg;
    360   telnetd_context    *ctx = session->ctx;
    361   bool login_failed = false;
    362   bool start = true;
    363 
    364   sc=rtems_libio_set_private_env();
    365 
    366   /* newlib hack/workaround. Before we change stdin/out/err we must make
    367          * sure the internal data are initialized (fileno(stdout) has this sideeffect).
    368    * This should probably be done from RTEMS' libc support layer...
    369    * (T.S., newlibc-1.13; 2005/10)
    370          */
    371 
    372   fileno(stdout);
    373 
    374   if (RTEMS_SUCCESSFUL != sc) {
    375     rtems_error(sc,"rtems_libio_set_private_env");
    376     goto cleanup;
    377   }
    378 
    379   /* redirect stdio */
    380   for (i=0; i<3; i++) {
    381     if ( !(nstd[i]=fopen(session->pty.name,"r+")) ) {
    382       perror("unable to open stdio");
    383       goto cleanup;
    384     }
    385   }
    386 
    387   stdin  = nstd[0];
    388   stdout = nstd[1];
    389   stderr = nstd[2];
    390 
    391   /* call their routine */
    392   if (ctx->config.login_check != NULL) {
    393     start = rtems_shell_login_prompt(
    394       stdin,
    395       stderr,
    396       session->pty.name,
    397       ctx->config.login_check
    398     );
    399     login_failed = !start;
    400   }
    401   if (start) {
    402     ctx->config.command( session->pty.name, ctx->config.arg);
    403   }
    404 
    405   stdin  = ostd[0];
    406   stdout = ostd[1];
    407   stderr = ostd[2];
    408 
    409   if (login_failed) {
    410     syslog(
    411       LOG_AUTHPRIV | LOG_WARNING,
    412       "telnetd: to many wrong passwords entered from %s",
    413       session->peername
    414     );
    415   }
    416 
    417 cleanup:
    418   release_a_Connection(ctx, session, nstd, i);
    419   free(session);
    420 }
  • testsuites/libtests/telnetd01/init.c

    r0f0e130 r0dc303f  
    106106#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
    107107
    108 #define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 32
     108#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS (3 + 1 + 5 * 4)
    109109
    110 #define CONFIGURE_MAXIMUM_TASKS 7
     110#define CONFIGURE_MAXIMUM_TASKS 8
     111
     112#define CONFIGURE_MAXIMUM_POSIX_KEYS 1
    111113
    112114#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
  • testsuites/libtests/telnetd01/telnetd01.scn

    r0f0e130 r0dc303f  
    55*** TEST TOOLS: 7.3.0 20180125 (RTEMS 5, RSB 9670d7541e0621915e521fe76e7bb33de8cee661, Newlib d13c84eb07e35984bf7a974cd786a6cdac29e6b9)
    66syslog: telnetd: configuration with invalid command
    7 Telnetd: spawning task failed (status: RTEMS_INVALID_PRIORITY)
    8 syslog: telnetd: cannot create server task
     7syslog: telnetd: cannot create session task
    98syslog: telnetd: started successfully
    10 syslog: telnetd: already started
     9syslog: telnetd: cannot bind server socket
    1110
    1211*** END OF TEST TELNETD 1 ***
Note: See TracChangeset for help on using the changeset viewer.