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

4.104.114.95
Last change on this file since 081583c was 081583c, checked in by Joel Sherrill <joel.sherrill@…>, on 11/06/07 at 20:50:10

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

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