source: rtems/cpukit/telnetd/telnetd.c @ 0413b14

5
Last change on this file since 0413b14 was 0413b14, checked in by Sebastian Huber <sebastian.huber@…>, on 09/26/18 at 07:06:43

telnetd: Remove superfluous global variable

Update #3528.

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