source: rtems/cpukit/telnetd/telnetd.c @ 1fae7b43

4.104.115
Last change on this file since 1fae7b43 was 1fae7b43, checked in by Joel Sherrill <joel.sherrill@…>, on 10/15/08 at 17:37:16

2008-10-15 Joel Sherrill <joel.sherrill@…>

PR 1331/networking

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