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

4.104.114.9
Last change on this file since c0cc985 was c0cc985, checked in by Ralf Corsepius <ralf.corsepius@…>, on Aug 2, 2008 at 6:34:08 AM

Add missing prototypes.
Remove unused local declations.

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