How many protocol stacks are free above RTEMS ?

Jake Janovetz janovetz at uiuc.edu
Wed Dec 13 15:22:26 UTC 2000


Sure...  Here it is.  It only does configuration of the 
ethernet settings on my device, but at least it contains a
function (Telnetd_GetString) which strips out the IAC commands
that telnet specifies.  Perhaps that could be useful.  :-)

I wanted to rewrite some of the routines that RTEMS uses
for monitor purposes (CPU statistics, etc) so that they can
dump their information to an arbitrary (FILE *), but never
got around to it.  Too many other things on the plate.

   Jake


On Wed, Dec 13, 2000 at 10:39:24AM +0200, Antti P Miettinen wrote:
> "Jake Janovetz" <janovetz at uiuc.edu> writes:
> > TelnetD is something I've written, but since RTEMS doesn't really have
> >         a default shell, it is fairly specific to the application.
> 
> Would you like to share this? Being able to get rtems monitor prompt
> through telnet would probably be something that could fit into RTEMS
> distribution.
> 
> -- 
> Antti P Miettinen
> Nokia Networks
-------------- next part --------------
/**************************************************************************
 *                              telnetd.c                                 *
 **************************************************************************
 * Description:                                                           *
 *                                                                        *
 *    Herein contains the telnet daemon.  A TCP/IP-based system for       *
 *    setting and querying certain configuration parameters and executing *
 *    commands.                                                           *
 *                                                                        *
 *                                                                        *
 * The public routines contained in this file are:                        *
 *                                                                        *
 *    Telnetd_Start - Starts the telnet daemon.                           *
 *                                                                        *
 * The private routines contained in this file are:                       *
 *                                                                        *
 *    Telnetd_Daemon - Background task that waits for telnet connections. *
 *    Telnetd_Session - Interactive session started for each telnet       *
 *                      session.                                          *
 *    Telnetd_GetString - Handles getting strings from the connection     *
 *                        and processing telnet IAC commands.             *
 *                                                                        *
 **************************************************************************
 * Change History:                                                        *
 *  01/01/99 - Creation (JWJ)                                             *
 *************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#include <rtems.h>
#include <rtems/rtems_bsdnet.h>
#include <rtems/error.h>
#include <syslog.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/telnet.h>

#include "init.h"
#include "monitor.h"
#include "telnetd.h"


static char help_text[] =
   "\nCantante Control Commands:\n"
   "--------------------------\n"
   "ipaddr a.b.c.d            - Set device IP address.\n" \
   "netmask a.b.c.d           - Set netmask.\n" \
   "gwaddr a.b.c.d            - Set gateway IP address.\n" \
   "mp3addr a.b.c.d           - Set MP3 server IP address.\n" \
   "logaddr a.b.c.d           - Set log host IP address (optional).\n" \
   "deviceid x                - Set device ID.\n" \
   "location \"place\"          - Set device location (use quotes).\n" \
   "save                      - Save settings.\n" \
   "exit                      - Close the telnet session.\n"
   "reboot                    - Reboot.\n"
   "\n" \
   "'?' for help, '/' to list current settings.\n\n"
   "\n";

/**************************************************************************
 * Maximum receive buffer size for images.
 *************************************************************************/
#define TELNETD_MAX_RECEIVESIZE   (512*1024)

#define TELNETD_WELCOME_MSG "\n\nCantante telnetd v1.1\n" \
               "Cantante MPEG Layer III Player\n" \
               "Copyright (c) 1998-2000  Jake Janovetz\n\n"

struct session_info_t
{
   FILE  *sock_fp;
   struct CantanteSettings current_settings;
   struct CantanteSettings tentative_settings;
};


/**************************************************************************
 * Function: Telnetd_GetString                                            *
 **************************************************************************
 * Description:                                                           *
 *                                                                        *
 *    Here we retrieve a string from the given file descriptor.  We also  *
 *    handle IAC commands that come through the connection.               *
 *    The command is supposed to 'look' and 'feel' like fgets.            *
 *                                                                        *
 * Similar to fgets, but with the following important differences:        *
 *  + Eliminates telnet IAC commands from input stream.                   *
 *  + If the line exceeds n-1 characters, the remaining characters        *
 *    are truncated.  The returned string is still terminated with the \n.*
 *                                                                        *
 *                                                                        *
 * Inputs:                                                                *
 *                                                                        *
 *    char *buf - Pointer to a buffer where the string will go.           *
 *    int n - Maximum number of characters to receive into buf.           *
 *    FILE *fp - File pointer describing the input stream.                *
 *                                                                        *
 * Output:                                                                *
 *                                                                        *
 *    char * - Pointer to the retrieved string.                           *
 *                                                                        *
 **************************************************************************
 * Change History:                                                        *
 *  04/03/98 - Creation (JWJ)                                             *
 *************************************************************************/
static char *
Telnetd_GetString(char *buf, int n, FILE *fp)
{
   char *s;
   int  c;

   n--;
   s = buf;
   while (1)
   {
      c = fgetc(fp);

      if (c == EOF)
         return(NULL);

      if (c == IAC)
      {
         c = fgetc(fp);
         c = fgetc(fp);
         continue;
      }
      *s++ = (char)c;
      n--;

      if (c == '\n')
      {
         *s = 0;
         break;
      }
      if (n == 0)
      { 
         s--;
         n++;
      }
   }
   return(buf);
}


static int
Telnetd_ScanIP(char *str, int *a, int *b, int *c, int *d)
{
   if (sscanf(str, "%d.%d.%d.%d", a, b, c, d) != 4)
      return(-1);

   if ((*a < 0)   || (*b < 0)   || (*c < 0)   || (*d < 0) ||
       (*a > 255) || (*b > 255) || (*c > 255) || (*d > 255))
      return(-1);

   return(0);
}


/**************************************************************************
 * Function: Telnetd_Session                                              *
 **************************************************************************
 * Description:                                                           *
 *                                                                        *
 *    This task is started when the telnet daemon gets a service request  *
 *    from a remote machine.  This is the actual code for the session.    *
 *                                                                        *
 *                                                                        *
 * Inputs:                                                                *
 *                                                                        *
 *    rtems_task_argument arg - (not used)                                *
 *                                                                        *
 * Output:                                                                *
 *                                                                        *
 *    none                                                                *
 *                                                                        *
 **************************************************************************
 * Change History:                                                        *
 *  04/03/98 - Creation (JWJ)                                             *
 *************************************************************************/
static void
Telnetd_Session(rtems_task_argument arg)
{
   rtems_status_code     sc;
   struct session_info_t *info = NULL;
   char                  cmd[256];
   int                   a, b, c, d;
   int                   done;
   FILE                  *fp;
   struct CantanteSettings  current_settings;
   struct CantanteSettings  tentative_settings;


   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
                            (rtems_unsigned32 *)&info);
   if (sc != RTEMS_SUCCESSFUL)
   {
      syslog(LOG_ERR, "telnetd: Could not retrieve notepad entry: %s",
             strerror(errno));
      sc = rtems_task_delete(RTEMS_SELF);
   }


   /***********************************************************************
    * Retrieve current settings.
    **********************************************************************/
   Monitor_ReadCantanteSettings(&current_settings);
   tentative_settings = current_settings;

   /***********************************************************************
    * Print the telnet welcome message.
    **********************************************************************/
   fp = info->sock_fp;
   fprintf(fp, TELNETD_WELCOME_MSG);

   done = 0;
   while (!done)
   {
      fprintf(fp, "cantante> ");
      fflush(fp);
      if (Telnetd_GetString(cmd, 80, fp) == NULL)
         break;

      if (cmd[0] == '?')
      {
         fprintf(fp, help_text);
         fflush(fp);
      }
      else if (cmd[0] == '/')
      {
         Monitor_DisplaySettings(fp, current_settings,
                                 tentative_settings);
         fflush(fp);
      }
      else if (!strncmp(cmd, "ipaddr", 6))
      {
         if (Telnetd_ScanIP(&cmd[7], &a, &b, &c, &d) != 0)
            fprintf(fp, "   Error: Malformed IP address.\n");
         else
         {
            tentative_settings.ipaddr[0] = a;
            tentative_settings.ipaddr[1] = b;
            tentative_settings.ipaddr[2] = c;
            tentative_settings.ipaddr[3] = d;
         }
      }
      else if (!strncmp(cmd, "netmask", 7))
      {
         if (Telnetd_ScanIP(&cmd[8], &a, &b, &c, &d) != 0)
            fprintf(fp, "   Error: Malformed netmask.\n");
         else
         {
            tentative_settings.netmask[0] = a;
            tentative_settings.netmask[1] = b;
            tentative_settings.netmask[2] = c;
            tentative_settings.netmask[3] = d;
         }
      }
      else if (!strncmp(cmd, "gwaddr", 6))
      {
         if (Telnetd_ScanIP(&cmd[7], &a, &b, &c, &d) != 0)
            fprintf(fp, "   Error: Malformed gateway address.\n");
         else
         {
            tentative_settings.gwaddr[0] = a;
            tentative_settings.gwaddr[1] = b;
            tentative_settings.gwaddr[2] = c;
            tentative_settings.gwaddr[3] = d;
         }
      }
      else if (!strncmp(cmd, "mp3addr", 7))
      {
         if (Telnetd_ScanIP(&cmd[8], &a, &b, &c, &d) != 0)
            fprintf(fp, "   Error: Malformed IP address.\n");
         else
         {
            tentative_settings.mp3addr[0] = a;
            tentative_settings.mp3addr[1] = b;
            tentative_settings.mp3addr[2] = c;
            tentative_settings.mp3addr[3] = d;
         }
      }
      else if (!strncmp(cmd, "logaddr", 7))
      {
         if (Telnetd_ScanIP(&cmd[8], &a, &b, &c, &d) != 0)
            fprintf(fp, "   Error: Malformed IP address.\n");
         else
         {
            tentative_settings.logaddr[0] = a;
            tentative_settings.logaddr[1] = b;
            tentative_settings.logaddr[2] = c;
            tentative_settings.logaddr[3] = d;
         }
      }
      else if (!strncmp(cmd, "deviceid", 8))
      {
         if (sscanf(&cmd[9], "%d", &a) != 1)
            fprintf(fp, "   Error: Must specify an integer device ID.\n");
         else
            tentative_settings.deviceID = a;
      }
      else if (!strncmp(cmd, "location", 8))
      {
         char str[30];

         if (sscanf(&cmd[9], "\"%15[a-zA-Z0-9!@#$%%^&'*()/,._ -]\"", str) != 1)
            fprintf(fp, "   Error: Bad location string format.\n");
         else
         {
            strncpy(tentative_settings.location, str, 16);
            tentative_settings.location[16] = '\0';
         }
      }
      else if (!strncmp(cmd, "save", 4))
      {
         fprintf(fp, "Saving ethernet settings...\n");
         fflush(fp);
         Monitor_WriteCantanteSettings(&tentative_settings);
         Monitor_ReadCantanteSettings(&current_settings);
      }
      else if (!strncmp(cmd, "exit", 4))
         done = 1;
      else if (!strncmp(cmd, "quit", 4))
         done = 1;
      else if (!strncmp(cmd, "reboot", 6))
      {
         fclose(fp);
         rtems_task_wake_after(10);
         rtems_panic(NULL);
      }
      else
         fprintf(fp, "\n'?' for help, '/' to list current settings.\n");
   }
   fprintf(fp, "Goodbye.\n");
   fflush(fp);

   if (fclose(fp) < 0)
   {
      syslog(LOG_ERR, "telnetd: Could not close connection: %s",
             strerror(errno));
   }
   free(info);
   sc = rtems_task_delete(RTEMS_SELF);
   syslog(LOG_ERR, "telnetd: Session task deletion failed: %s",
          rtems_status_text(sc));
}


/**************************************************************************
 * Function: Telnetd_Daemon                                               *
 **************************************************************************
 * Description:                                                           *
 *                                                                        *
 *                                                                        *
 *    This task runs in the background.  It waits for service requests    *
 *    on a well-known port.  When a request is received, it opens a new   *
 *    telnet session.                                                     *
 *                                                                        *
 *                                                                        *
 * Inputs:                                                                *
 *                                                                        *
 *    rtems_task_argument arg - not used.                                 *
 *                                                                        *
 * Output:                                                                *
 *                                                                        *
 *    none                                                                *
 *                                                                        *
 **************************************************************************
 * Change History:                                                        *
 *  04/03/98 - Creation (JWJ)                                             *
 *************************************************************************/
static void
Telnetd_Daemon(rtems_task_argument arg)
{
   int                   s;
   int                   s1;
   int                   addrlen;
   char                  sessionID;
   rtems_task_priority   priority;
   rtems_status_code     sc;
   rtems_id              tid;
   struct sockaddr_in    localAddr, remoteAddr;
   struct session_info_t *info;


   sessionID = 'a';

   /***********************************************************************
    * Create, bind, and listen on a selected port (normally the standard
    * telnet port).
    **********************************************************************/
   s = socket(AF_INET, SOCK_STREAM, 0);
   if (s < 0)
   {
      syslog(LOG_ERR, "telnetd: Could not create socket: %s",
             strerror(errno));
   }

   localAddr.sin_family      = AF_INET;
   localAddr.sin_port        = TELNETD_PORT;
   localAddr.sin_addr.s_addr = INADDR_ANY;
   memset(localAddr.sin_zero, 0, sizeof(localAddr.sin_zero));

   if (bind(s, (struct sockaddr *)&localAddr, sizeof(localAddr)) < 0)
   {
      syslog(LOG_ERR, "telnetd: Could not bind socket: %s", strerror(errno));
   }
   if (listen(s, 2) < 0)
   {
      syslog(LOG_ERR, "telnetd: Could not listen on socket: %s",
             strerror(errno));
   }

   /***********************************************************************
    * Enter an endless loop where we just accept connections from remote
    * hosts.  Each connection accepted causes a new telnet session to 
    * be created.
    **********************************************************************/
   while (1)
   {
      info = (struct session_info_t *)r_malloc(sizeof(struct session_info_t),
                                               "Telnetd_Daemon");

      addrlen = sizeof(remoteAddr);
      s1 = accept(s, (struct sockaddr *)&remoteAddr, &addrlen);
      if (s1 < 0)
      {
         syslog(LOG_ERR, "telnetd: Could not accept connection: %s",
                strerror(errno));
      }

      rtems_task_set_priority(RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &priority);
      sc = rtems_task_create(rtems_build_name('T', 'N', 'S', sessionID),
                             priority, 8*1024,
                             RTEMS_PREEMPT | RTEMS_NO_TIMESLICE |
                             RTEMS_NO_ASR | RTEMS_INTERRUPT_LEVEL(0),
                             RTEMS_FLOATING_POINT | RTEMS_LOCAL,
                             &tid);
      if (sc != RTEMS_SUCCESSFUL)
      {
         syslog(LOG_ERR, "telnetd: Could not create telnet session: %s\n",
                rtems_status_text(sc));
      }

      if (sessionID == 'z')
         sessionID = 'a';
      else
         sessionID++;

      if ((info->sock_fp = fdopen(s1, "r+")) == NULL)
      {
         close(s1);
         syslog(LOG_ERR, "telnetd: Could not create file pointer: %s",
                rtems_status_text(sc));
      }

      sc = rtems_task_set_note(tid, RTEMS_NOTEPAD_0,
                               (rtems_unsigned32)info);
      if (sc != RTEMS_SUCCESSFUL)
      {
         syslog(LOG_ERR, "telnetd: Could not set telnet session notepad: %s\n",
                rtems_status_text(sc));
      }

      sc = rtems_task_start(tid, Telnetd_Session, 0);
      if (sc != RTEMS_SUCCESSFUL)
      {
         syslog(LOG_ERR, "telnetd: Could not start telnet session: %s\n",
                rtems_status_text(sc));
      }
   }
}


/**************************************************************************
 * Function: Telnetd_Start                                                *
 **************************************************************************
 * Description:                                                           *
 *                                                                        *
 *    Here, we start the Telnetd task which waits for telnet requests and *
 *    services them.  This procedure returns to its caller once the       *
 *    task is started.                                                    *
 *                                                                        *
 *                                                                        *
 * Inputs:                                                                *
 *                                                                        *
 *    rtems_task_priority priority - Priority to assign to the daemons.   *
 *                                                                        *
 * Output:                                                                *
 *                                                                        *
 *    none                                                                *
 *                                                                        *
 **************************************************************************
 * Change History:                                                        *
 *  04/03/98 - Creation (JWJ)                                             *
 *************************************************************************/
void
Telnetd_Start(rtems_task_priority priority)
{
   rtems_status_code    sc;
   rtems_id             tid;


   /***********************************************************************
    * The daemon is a task that listens for connections to a certain
    * port.  That's all it does...
    **********************************************************************/
   sc = rtems_task_create(rtems_build_name('T', 'N', 'D', ' '),
                          priority, 4*1024, 
                          RTEMS_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR |
                          RTEMS_INTERRUPT_LEVEL(0),
                          RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
                          &tid);
   if (sc != RTEMS_SUCCESSFUL)
   {
      syslog(LOG_ERR, "telnetd: Could not create telnetd: %s\n",
             rtems_status_text(sc));
   }

   sc = rtems_task_start(tid, Telnetd_Daemon, 0);
   if (sc != RTEMS_SUCCESSFUL)
   {
      syslog(LOG_ERR, "telnetd: Could not start telnetd: %s\n",
             rtems_status_text(sc));
   }
}


-------------- next part --------------
#ifndef __TELNETD_H__
#define __TELNETD_H__

#define TELNETD_PORT   23

void Telnetd_Start(rtems_task_priority priority);

#endif   /* __TELNETD_H__ */


More information about the users mailing list