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

4.104.114.95
Last change on this file since edcb982 was edcb982, checked in by Joel Sherrill <joel.sherrill@…>, on 11/26/07 at 23:10:25

2007-11-26 Joel Sherrill <joel.sherrill@…>

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