source: rtems/cpukit/telnetd/telnetd.c @ f004b2b8

5
Last change on this file since f004b2b8 was f004b2b8, checked in by Sebastian Huber <sebastian.huber@…>, on 10/02/18 at 08:22:15

Use rtems_task_exit()

Update #3530.
Update #3533.

  • Property mode set to 100644
File size: 11.6 KB
Line 
1/***********************************************************/
2/*
3 *
4 *  The telnet DAEMON
5 *
6 *  Author: 17,may 2001
7 *
8 *   WORK: fernando.ruiz@ctv.es
9 *   HOME: correo@fernando-ruiz.com
10 *
11 * After start the net you can start this daemon.
12 * It uses the previously inited pseudo-terminales (pty.c)
13 * getting a new terminal with getpty(). This function
14 * gives a terminal name passing a opened socket like parameter.
15 *
16 * With register_telnetd() you add a new command in the shell to start
17 * this daemon interactively. (Login in /dev/console of course)
18 *
19 * Sorry but OOB is not still implemented. (This is the first version)
20 *
21 * Till Straumann <strauman@slac.stanford.edu>
22 *  - made the 'shell' interface more generic, i.e. it is now
23 *    possible to have 'telnetd' run an arbitrary 'shell'
24 *    program.
25 *
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>
33 *
34 * The license and distribution terms for this file may be
35 * found in the file LICENSE in this distribution or at
36 * http://www.rtems.org/license/LICENSE.
37 */
38
39#ifdef HAVE_CONFIG_H
40#include "config.h"
41#endif
42
43#include <sys/socket.h>
44#include <netinet/in.h>
45#include <arpa/inet.h>
46#include <unistd.h>
47#include <stdlib.h>
48#include <stdio.h>
49#include <string.h>
50#include <syslog.h>
51
52#include <rtems.h>
53#include <rtems/error.h>
54#include <rtems/pty.h>
55#include <rtems/shell.h>
56#include <rtems/telnetd.h>
57#include <rtems/userenv.h>
58
59#ifdef RTEMS_NETWORKING
60#include <rtems/rtems_bsdnet.h>
61#endif
62
63#define PARANOIA
64
65typedef struct telnetd_context telnetd_context;
66
67struct shell_args {
68  rtems_pty_context  pty;
69  char               peername[16];
70  telnetd_context   *ctx;
71};
72
73struct telnetd_context {
74  rtems_telnetd_config_table config;
75  uint16_t                   active_clients;
76};
77
78typedef union uni_sa {
79  struct sockaddr_in sin;
80  struct sockaddr     sa;
81} uni_sa;
82
83static int sockpeername(int sock, char *buf, int bufsz);
84
85rtems_id telnetd_dflt_spawn(
86  const char *name,
87  unsigned priority,
88  unsigned stackSize,
89  void (*fn)(void*),
90  void *fnarg
91);
92
93/***********************************************************/
94static telnetd_context telnetd_instance;
95
96/*
97 * chrisj: this variable was global and with no declared interface in a header
98 *         file and with no means to set it so I have stopped it being global;
99 *         if this breaks any user they will have be to provide a formal
100 *         interface to get this change reverted.
101 */
102static const rtems_id (*telnetd_spawn_task)(
103  const char *,
104  unsigned,
105  unsigned,
106  void (*)(void*),
107  void *
108) = telnetd_dflt_spawn;
109
110static struct shell_args *grab_a_Connection(
111  telnetd_context *ctx,
112  int des_socket,
113  uni_sa *srv,
114  char *peername,
115  int sz
116)
117{
118  struct shell_args *args;
119  socklen_t size_adr = sizeof(srv->sin);
120  int acp_sock;
121
122  if (ctx->active_clients >= ctx->config.client_maximum) {
123    return NULL;
124  }
125
126  args = malloc(sizeof(*args));
127  if (args == NULL) {
128    perror("telnetd:malloc");
129    return NULL;
130  }
131
132  acp_sock = accept(des_socket,&srv->sa,&size_adr);
133  if (acp_sock<0) {
134    perror("telnetd:accept");
135    free(args);
136    return NULL;
137  };
138
139  if (telnet_get_pty(&args->pty, acp_sock) == NULL) {
140    syslog( LOG_DAEMON | LOG_ERR, "telnetd: unable to obtain PTY");
141    /* NOTE: failing 'do_get_pty()' closed the socket */
142    free(args);
143    return NULL;
144  }
145
146  if (sockpeername(acp_sock, peername, sz))
147    strncpy(peername, "<UNKNOWN>", sz);
148
149#ifdef PARANOIA
150  syslog(LOG_DAEMON | LOG_INFO,
151      "telnetd: accepted connection from %s on %s",
152      peername,
153      args->pty.name);
154#endif
155
156  ++ctx->active_clients;
157  args->ctx = ctx;
158  return args;
159}
160
161
162static void release_a_Connection(telnetd_context *ctx, const char *devname,
163  const char *peername, FILE **pstd, int n)
164{
165
166#ifdef PARANOIA
167  syslog( LOG_DAEMON | LOG_INFO,
168      "telnetd: releasing connection from %s on %s",
169      peername,
170      devname );
171#endif
172
173  --ctx->active_clients;
174
175  while (--n>=0)
176    if (pstd[n]) fclose(pstd[n]);
177
178  unlink(devname);
179}
180
181static int sockpeername(int sock, char *buf, int bufsz)
182{
183  uni_sa peer;
184  int    rval = sock < 0;
185  socklen_t len  = sizeof(peer.sin);
186
187  if ( !rval )
188    rval = getpeername(sock, &peer.sa, &len);
189
190  if ( !rval )
191    rval = !inet_ntop( AF_INET, &peer.sin.sin_addr, buf, bufsz );
192
193  return rval;
194}
195
196static void
197spawned_shell(void *arg);
198
199/***********************************************************/
200static void
201rtems_task_telnetd(void *task_argument)
202{
203  int                des_socket;
204  uni_sa             srv;
205  char               peername[16];
206  int                i=1;
207  int                size_adr;
208  struct shell_args *arg = NULL;
209  rtems_id           task_id;
210  telnetd_context   *ctx = task_argument;
211
212  if ((des_socket=socket(PF_INET,SOCK_STREAM,0))<0) {
213    perror("telnetd:socket");
214    rtems_task_exit();
215  };
216  setsockopt(des_socket,SOL_SOCKET,SO_KEEPALIVE,&i,sizeof(i));
217
218  memset(&srv,0,sizeof(srv));
219  srv.sin.sin_family=AF_INET;
220  srv.sin.sin_port=htons(23);
221  size_adr=sizeof(srv.sin);
222  if ((bind(des_socket,&srv.sa,size_adr))<0) {
223    perror("telnetd:bind");
224    close(des_socket);
225    rtems_task_exit();
226  };
227  if ((listen(des_socket,5))<0) {
228    perror("telnetd:listen");
229          close(des_socket);
230    rtems_task_exit();
231  };
232
233  /* we don't redirect stdio as this probably
234   * was started from the console anyway ..
235   */
236  do {
237    if (ctx->config.keep_stdio) {
238      bool start = true;
239      char device_name [32];
240
241      ttyname_r( 1, device_name, sizeof( device_name));
242      if (ctx->config.login_check != NULL) {
243        start = rtems_shell_login_prompt(
244          stdin,
245          stderr,
246          device_name,
247          ctx->config.login_check
248        );
249      }
250      if (start) {
251        ctx->config.command( device_name, ctx->config.arg);
252      } else {
253        syslog(
254          LOG_AUTHPRIV | LOG_WARNING,
255          "telnetd: to many wrong passwords entered from %s",
256          device_name
257        );
258      }
259    } else {
260      arg = grab_a_Connection(ctx, des_socket, &srv, peername,
261        sizeof(peername));
262
263      if (arg == NULL) {
264        /* if something went wrong, sleep for some time */
265        sleep(10);
266        continue;
267      }
268
269      strncpy(arg->peername, peername, sizeof(arg->peername));
270
271      task_id = telnetd_spawn_task(
272        arg->pty.name,
273        ctx->config.priority,
274        ctx->config.stack_size,
275        spawned_shell,
276        arg
277      );
278      if (task_id == RTEMS_ID_NONE) {
279        FILE *dummy;
280
281        if ( telnetd_spawn_task != telnetd_dflt_spawn ) {
282          fprintf(stderr,"Telnetd: Unable to spawn child task\n");
283        }
284
285        /* hmm - the pty driver slot can only be
286         * released by opening and subsequently
287         * closing the PTY - this also closes
288         * the underlying socket. So we mock up
289         * a stream...
290         */
291
292        if ( !(dummy=fopen(arg->pty.name,"r+")) )
293          perror("Unable to dummy open the pty, losing a slot :-(");
294        release_a_Connection(ctx, arg->pty.name, peername, &dummy, 1);
295        free(arg);
296        sleep(2); /* don't accept connections too fast */
297      }
298    }
299  } while(1);
300
301  /* TODO: how to free the connection semaphore? But then -
302   *       stopping the daemon is probably only needed during
303   *       development/debugging.
304   *       Finalizer code should collect all the connection semaphore
305   *       counts and eventually clean up...
306   */
307  close(des_socket);
308}
309
310rtems_status_code rtems_telnetd_start(const rtems_telnetd_config_table* config)
311{
312  telnetd_context *ctx = &telnetd_instance;
313  rtems_id task_id;
314
315  if (config->command == NULL) {
316    fprintf(stderr, "telnetd setup with invalid command\n");
317    return RTEMS_IO_ERROR;
318  }
319
320  if (ctx->config.command != NULL) {
321    fprintf(stderr, "telnetd already started\n");
322    return RTEMS_RESOURCE_IN_USE;
323  }
324
325  ctx->config = *config;
326
327  /* Check priority */
328#ifdef RTEMS_NETWORKING
329  if (ctx->config.priority == 0) {
330    ctx->config.priority = rtems_bsdnet_config.network_task_priority;
331  }
332#endif
333  if (ctx->config.priority == 0) {
334    ctx->config.priority = 100;
335  }
336
337  /* Check stack size */
338  if (ctx->config.stack_size == 0) {
339    ctx->config.stack_size = (size_t)32 * 1024;
340  }
341
342  if (ctx->config.client_maximum == 0) {
343    ctx->config.client_maximum = 5;
344  }
345
346  /* Spawn task */
347  task_id = telnetd_spawn_task(
348    "TNTD",
349    ctx->config.priority,
350    ctx->config.stack_size,
351    rtems_task_telnetd,
352    ctx
353  );
354  if (task_id == RTEMS_ID_NONE) {
355    ctx->config.command = NULL;
356    return RTEMS_IO_ERROR;
357  }
358
359  /* Print status */
360  if (!ctx->config.keep_stdio) {
361    fprintf(
362      stderr,
363      "telnetd started with stacksize = %u and priority = %d\n",
364      (unsigned) ctx->config.stack_size,
365      (unsigned) ctx->config.priority
366    );
367  }
368
369  return RTEMS_SUCCESSFUL;
370}
371
372/* utility wrapper */
373static void
374spawned_shell(void *targ)
375{
376  rtems_status_code    sc;
377  FILE                *nstd[3]={0};
378  FILE                *ostd[3]={ stdin, stdout, stderr };
379  int                  i=0;
380  struct shell_args  *arg = targ;
381  telnetd_context    *ctx = arg->ctx;
382  bool login_failed = false;
383  bool start = true;
384
385  sc=rtems_libio_set_private_env();
386
387  /* newlib hack/workaround. Before we change stdin/out/err we must make
388         * sure the internal data are initialized (fileno(stdout) has this sideeffect).
389   * This should probably be done from RTEMS' libc support layer...
390   * (T.S., newlibc-1.13; 2005/10)
391         */
392
393  fileno(stdout);
394
395  if (RTEMS_SUCCESSFUL != sc) {
396    rtems_error(sc,"rtems_libio_set_private_env");
397    goto cleanup;
398  }
399
400  /* redirect stdio */
401  for (i=0; i<3; i++) {
402    if ( !(nstd[i]=fopen(arg->pty.name,"r+")) ) {
403      perror("unable to open stdio");
404      goto cleanup;
405    }
406  }
407
408  stdin  = nstd[0];
409  stdout = nstd[1];
410  stderr = nstd[2];
411
412  /* call their routine */
413  if (ctx->config.login_check != NULL) {
414    start = rtems_shell_login_prompt(
415      stdin,
416      stderr,
417      arg->pty.name,
418      ctx->config.login_check
419    );
420    login_failed = !start;
421  }
422  if (start) {
423    ctx->config.command( arg->pty.name, ctx->config.arg);
424  }
425
426  stdin  = ostd[0];
427  stdout = ostd[1];
428  stderr = ostd[2];
429
430  if (login_failed) {
431    syslog(
432      LOG_AUTHPRIV | LOG_WARNING,
433      "telnetd: to many wrong passwords entered from %s",
434      arg->peername
435    );
436  }
437
438cleanup:
439  release_a_Connection(ctx, arg->pty.name, arg->peername, nstd, i);
440  free(arg);
441}
442
443struct wrap_delete_args {
444  void (*t)(void *);
445  void           *a;
446};
447
448static rtems_task
449wrap_delete(rtems_task_argument arg)
450{
451  struct wrap_delete_args     *pwa = (struct wrap_delete_args *)arg;
452  register void              (*t)(void *) = pwa->t;
453  register void               *a   = pwa->a;
454
455  /* free argument before calling function (which may never return if
456   * they choose to delete themselves)
457   */
458  free(pwa);
459  t(a);
460  rtems_task_exit();
461}
462
463rtems_id
464telnetd_dflt_spawn(const char *name, unsigned int priority, unsigned int stackSize, void (*fn)(void *), void* fnarg)
465{
466  rtems_status_code        sc;
467  rtems_id                 task_id = RTEMS_ID_NONE;
468  char                     nm[4] = {'X','X','X','X' };
469  struct wrap_delete_args *pwa = malloc(sizeof(*pwa));
470
471  strncpy(nm, name, 4);
472
473  if ( !pwa ) {
474    perror("Telnetd: no memory\n");
475    return RTEMS_ID_NONE;
476  }
477
478  pwa->t = fn;
479  pwa->a = fnarg;
480
481  if ((sc=rtems_task_create(
482    rtems_build_name(nm[0], nm[1], nm[2], nm[3]),
483      (rtems_task_priority)priority,
484      stackSize,
485      RTEMS_DEFAULT_MODES,
486      RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT,
487      &task_id)) ||
488    (sc=rtems_task_start(
489      task_id,
490      wrap_delete,
491      (rtems_task_argument)pwa))) {
492        free(pwa);
493        rtems_error(sc,"Telnetd: spawning task failed");
494        return RTEMS_ID_NONE;
495  }
496  return task_id;
497}
Note: See TracBrowser for help on using the repository browser.