Index: c/src/lib/libnetworking/rtems_servers/Makefile.in
===================================================================
--- c/src/lib/libnetworking/rtems_servers/Makefile.in	(revision 6d0e13c3bd56e1f158bb8691e6ca244b160616d1)
+++ c/src/lib/libnetworking/rtems_servers/Makefile.in	(revision 6d0e13c3bd56e1f158bb8691e6ca244b160616d1)
@@ -0,0 +1,53 @@
+#
+#  $Id$
+#
+
+@SET_MAKE@
+srcdir = @srcdir@
+VPATH = @srcdir@
+RTEMS_ROOT = @top_srcdir@
+PROJECT_ROOT = @PROJECT_ROOT@
+
+INSTALL = @INSTALL@
+
+LIBNAME=lib.a
+LIB=${ARCH}/${LIBNAME}
+
+# C and C++ source names, if any, go here -- minus the .c or .cc
+C_PIECES=sghostname issetugid \
+        rtems_glue rtems_syscall rtems_bootp \
+        rtems_showmbuf rtems_showroute \
+        rtems_showifstat rtems_showipstat rtems_showicmpstat \
+        rtems_showtcpstat rtems_showudpstat rtems_select
+C_FILES=$(C_PIECES:%=%.c)
+C_O_FILES=$(C_PIECES:%=${ARCH}/%.o)
+
+SRCS=$(C_FILES)
+OBJS=$(C_O_FILES)
+
+include $(RTEMS_ROOT)/make/custom/$(RTEMS_BSP).cfg
+include $(RTEMS_ROOT)/make/lib.cfg
+
+#
+# Add local stuff here using +=
+#
+
+DEFINES  += -D_COMPILING_BSD_KERNEL_ -DKERNEL -DINET -DNFS \
+    -DDIAGNOSTIC -DBOOTP_COMPAT
+CPPFLAGS +=
+CFLAGS   +=
+
+#
+# Add your list of files to delete here.  The config files
+#  already know how to delete some stuff, so you may want
+#  to just run 'make clean' first to see what gets missed.
+#  'make clobber' already includes 'make clean'
+#
+
+CLEAN_ADDITIONS += $(LIB)
+CLOBBER_ADDITIONS +=
+
+all:	${ARCH} $(LIB)
+
+$(LIB): $(SRCS) ${OBJS}
+	$(make-library)
Index: c/src/lib/libnetworking/rtems_servers/ftpd.c
===================================================================
--- c/src/lib/libnetworking/rtems_servers/ftpd.c	(revision 6d0e13c3bd56e1f158bb8691e6ca244b160616d1)
+++ c/src/lib/libnetworking/rtems_servers/ftpd.c	(revision 6d0e13c3bd56e1f158bb8691e6ca244b160616d1)
@@ -0,0 +1,1067 @@
+/*
+ *  FTP Server Daemon
+ *
+ *  Submitted by: Jake Janovetz <janovetz@tempest.ece.uiuc.edu>
+ *
+ *  $Id$
+ */
+
+/*
+ * Current state:
+ *   To untar, put as "untar"
+ * CWD uses chdir
+ *   This is bad due to global setting of chdir.
+ *
+ * Stored files come into RAM and are saved later.  This is an artifact
+ *   of a previous implementation (no filesystem -- had to do stuff with
+ *   the "files" later).  This can be eliminated once all of Jake's stuff
+ *   is moved to devices/filesystems.
+ *
+ * CLOSE(S) doesn't seem to work.  This causes problems in 
+ *          several areas.  It lets too many file descriptors pile up
+ *          and it doesn't seem to flush the stream.
+ *
+ * Is 'recv' what I want to use to get commands from the control port?
+ *
+ */
+
+/**************************************************************************
+ *                                 ftpd.c                                 *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This file contains the daemon which services requests that appear   *
+ *    on the 'FTP' port.  This server is compatible with FTP, but it      *
+ *    also provides services specific to the Erithacus system.            *
+ *    This server is started at boot-time and runs forever.               *
+ *                                                                        *
+ *    Organization:                                                       *
+ *                                                                        *
+ *       The FTP daemon is started upon boot.  It runs all the time       *
+ *       and waits for connections on the known FTP port (21).  When      *
+ *       a connection is made, it starts a 'session' task.  That          *
+ *       session then interacts with the remote host.  When the session   *
+ *       is complete, the session task deletes itself.  The daemon still  *
+ *       runs, however.                                                   *
+ *                                                                        *
+ *    Implementation Notes:                                               *
+ *                                                                        *
+ *       The 'current working directory' implementation utilizes the      *
+ *       RTEMS filesystem cwd.  This is no good since other processes     *
+ *       inherit the same cwd.                                            *
+ *                                                                        *
+ *                                                                        *
+ * Supported commands are:                                                *
+ *                                                                        *
+ * RETR xxx     - Sends a file from the client.                           *
+ * STOR xxx     - Receives a file from the client.  xxx = filename.       *
+ * LIST xxx     - Sends a file list to the client.                        *
+ *                (LIST xxx isn't working yet...)                         *
+ * USER         - Does nothing.                                           *
+ * PASS         - Does nothing.                                           *
+ * SYST         - Replies with the system type (`RTEMS').                 *
+ * DELE xxx     - Delete file xxx.                                        *
+ * MKD xxx      - Create directory xxx.                                   *
+ * RMD xxx      - Remove directory xxx.                                   *
+ * PWD          - Print working directory.                                *
+ * CWD xxx      - Change working directory.                               *
+ * SITE CHMOD xxx yyy - Change permissions on file yyy to xxx.            *
+ * PORT a,b,c,d,x,y   - Setup for a data port to IP address a.b.c.d       *
+ *                      and port (x*256 + y).                             *
+ *                                                                        *
+ *                                                                        *
+ *                                                                        *
+ * The public routines contained in this file are:                        *
+ *                                                                        *
+ *    FTPD_Start - Starts the server daemon, then returns to its caller.  *
+ *                                                                        *
+ *                                                                        *
+ * The private routines contained in this file are:                       *
+ *                                                                        *
+ *    FTPD_SendReply    - Sends a reply code and text through the control *
+ *                        port.                                           *
+ *    FTPD_CommandStore - Performs the "STOR" command.                    *
+ *    FTPD_CommandList  - Performs the "LIST" command.                    *
+ *    FTPD_CommandPort  - Opens a data port (the "PORT" command).         *
+ *    FTPD_ParseCommand - Parses an incoming command.                     *
+ *    FTPD_Session      - Begins a service session.                       *
+ *    FTPD_Daemon       - Listens on the FTP port for service requests.   *
+ *                                                                        *
+ *                                                                        *
+ *------------------------------------------------------------------------*
+ *                                                                        *
+ * Jake Janovetz                                                          *
+ * University of Illinois                                                 *
+ * 1406 West Green Street                                                 *
+ * Urbana IL  61801                                                       *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+
+/* Revision Control Information:
+ *
+ * $Source$
+ * $Id$
+ * $Log$
+ * Revision 1.3  1998/05/19 21:28:17  erithacus
+ * Update control socket to file I/O.
+ *
+ * Revision 1.2  1998/05/19 20:13:50  erithacus
+ * Remodeled to be entirely reentrant.
+ *
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.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 <arpa/ftp.h>
+#include <netinet/in.h>
+
+#include "ftpd.h"
+#include "untar.h"
+
+
+/**************************************************************************
+ * Meanings of first and second digits of reply codes:
+ *
+ * Reply:  Description:
+ *-------- --------------
+ *  1yz    Positive preliminary reply.  The action is being started but
+ *         expect another reply before sending another command.
+ *  2yz    Positive completion reply.  A new command can be sent.
+ *  3yz    Positive intermediate reply.  The command has been accpeted
+ *         but another command must be sent.
+ *  4yz    Transient negative completion reply.  The requested action did
+ *         not take place, but the error condition is temporary so the
+ *         command can be reissued later.
+ *  5yz    Permanent negative completion reply.  The command was not  
+ *         accepted and should not be retried.
+ *-------------------------------------------------------------------------
+ *  x0z    Syntax errors.
+ *  x1z    Information.
+ *  x2z    Connections.  Replies referring to the control or data
+ *         connections.
+ *  x3z    Authentication and accounting.  Replies for the login or
+ *         accounting commands.
+ *  x4z    Unspecified.
+ *  x5z    Filesystem status.
+ *************************************************************************/
+
+
+/**************************************************************************
+ * Maximum buffer size for use by the transfer protocol.
+ *  This will be eliminated when the filesystem is complete enough that
+ *  we don't have to store the received data until we have something to
+ *  do with it.
+ *************************************************************************/
+#define FTPD_MAX_RECEIVESIZE  (512*1024)
+
+/**************************************************************************
+ * SessionInfo structure.
+ *
+ * The following structure is allocated for each session.  The pointer
+ * to this structure is contained in the tasks notepad entry.
+ *************************************************************************/
+typedef struct
+{
+   struct sockaddr_in  data_addr;   /* Data address for PORT commands */
+   int                 ctrl_sock;   /* Control connection socker */
+   char                cwd[255];    /* Current working directory */
+                                    /* Login -- future use -- */
+   int                 xfer_mode;   /* Transfer mode (ASCII/binary) */
+} FTPD_SessionInfo_t;
+
+
+#define FTPD_WELCOME_MESSAGE \
+   "Welcome to the RTEMS FTP server.\n" \
+   "\n" \
+   "Login accepted.\n"
+
+
+/**************************************************************************
+ * Function: FTPD_SendReply                                               *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This procedure sends a reply to the client via the control          *
+ *    connection.                                                         *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    int  code  - The 3-digit reply code.                                *
+ *    char *text - Reply text.                                            *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static void
+FTPD_SendReply(int code, char *text)
+{
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+   char                str[80];
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   /***********************************************************************
+    * code must be a 3-digit number.
+    **********************************************************************/
+   if ((code < 100) || (code > 999))
+   {
+      syslog(LOG_ERR, "ftpd: Code not 3-digits.");
+      return;
+   }
+
+   /***********************************************************************
+    * If a text reply exists, add it to the reply data.
+    **********************************************************************/
+   if (text != NULL)
+   {
+      sprintf(str, "%d %.70s\r\n", code, text);
+   }
+   else
+   {
+      sprintf(str, "%d\r\n", code);
+   }
+   send(info->ctrl_sock, str, strlen(str), 0);
+}
+
+
+/**************************************************************************
+ * Function: FTPD_CommandRetrieve                                         *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This performs the "RETR" command.  A data connection must already   *
+ *    be open (via the "PORT" command.)  Here, we send the data to the    *
+ *    connection.                                                         *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    char *filename   - Source filename.                                 *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    int  - 0 for reply sent.                                            *
+ *           1 for no reply sent.                                         *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  04/29/98 - Creation (JWJ)                                             *
+ *************************************************************************/
+static int
+FTPD_CommandRetrieve(char *filename)
+{
+   int                 s;
+   int                 n;
+
+   FILE                *fp;
+   unsigned char       *bufr;
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+
+   if ((fp = fopen(filename, "r")) == NULL)
+   {
+      FTPD_SendReply(450, "Error opening file.");
+      return(0);
+   }
+
+   bufr = (unsigned char *)malloc(BUFSIZ);
+   if (bufr == NULL)
+   {
+      FTPD_SendReply(440, "Server error - malloc fail.");
+      fclose(fp);
+      return(0);
+   }
+
+   /***********************************************************************
+    * Connect to the data connection (PORT made in an earlier PORT call).
+    **********************************************************************/
+   FTPD_SendReply(150, "BINARY data connection.");
+   s = socket(AF_INET, SOCK_STREAM, 0);
+   if (connect(s, (struct sockaddr *)&info->data_addr,
+               sizeof(struct sockaddr)) < 0)
+   {
+      FTPD_SendReply(420, "Server error - could not connect socket.");
+      free(bufr);
+      fclose(fp);
+      close(s);
+      return(1);
+   }
+
+   /***********************************************************************
+    * Send the data over the ether.
+    **********************************************************************/
+   while ((n = fread(bufr, 1, BUFSIZ, fp)) != 0)
+   {
+      send(s, bufr, n, 0);
+   }
+
+   if (feof(fp))
+   {
+      FTPD_SendReply(210, "File sent successfully.");
+   }
+   else
+   {
+      FTPD_SendReply(450, "Retrieve failed.");
+   }
+
+   if (close(s) != 0)
+   {
+      syslog(LOG_ERR, "ftpd: Error closing data socket");
+   }
+
+   free(bufr);
+   fclose(fp);
+   return(0);
+}
+
+
+/**************************************************************************
+ * Function: FTPD_CommandStore                                            *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This performs the "STOR" command.  A data connection must already   *
+ *    be open (via the "PORT" command.)  Here, we get the data from the   *
+ *    connection and figure out what to do with it.                       *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    char *filename   - Destination filename.                            *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    int  - 0 for success.                                               *
+ *           1 for failure.                                               *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static int
+FTPD_CommandStore(char *filename)
+{
+   char                *bufr;
+   char                *bigBufr;
+   int                 s;
+   int                 n;
+   unsigned long       size = 0;
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   bufr = (char *)malloc(BUFSIZ * sizeof(char));
+   if (bufr == NULL)
+   {
+      FTPD_SendReply(440, "Server error - malloc fail.");
+      return(1);
+   }
+
+   bigBufr = (char *)malloc(FTPD_MAX_RECEIVESIZE * sizeof(char));
+   if (bigBufr == NULL)
+   {
+      FTPD_SendReply(440, "Server error - malloc fail.");
+      free(bufr);
+      return(1);
+   }
+
+   FTPD_SendReply(150, "BINARY data connection.");
+
+   s = socket(AF_INET, SOCK_STREAM, 0);
+   if (connect(s, (struct sockaddr *)&info->data_addr,
+               sizeof(struct sockaddr)) < 0)
+   {
+      free(bufr);
+      free(bigBufr);
+      close(s);
+      return(1);
+   }
+
+
+   /***********************************************************************
+    * File: "/dev/null" just throws the data away.
+    **********************************************************************/
+   if (!strncmp("/dev/null", filename, 9))
+   {
+      while ((n = read(s, bufr, BUFSIZ)) > 0);
+   }
+   else
+   {
+      /***********************************************************************
+       * Retrieve the file into our buffer space.
+       **********************************************************************/
+      size = 0;
+      while ((n = read(s, bufr, BUFSIZ)) > 0)
+      {
+         if (size + n > FTPD_MAX_RECEIVESIZE)
+         {
+            FTPD_SendReply(440, "Server error - Buffer size exceeded.");
+            free(bufr);
+            free(bigBufr);
+            close(s);
+            return(1);
+         }
+         memcpy(&bigBufr[size], bufr, n);
+         size += n;
+      }
+   }
+   free(bufr);
+   close(s);
+
+
+   /***********************************************************************
+    * Figure out what to do with the data we just received.
+    **********************************************************************/
+   if (!strncmp("untar", filename, 5))
+   {
+      Untar_FromMemory(bigBufr, size);
+      FTPD_SendReply(210, "Untar successful.");
+   }
+   else
+   {
+      FILE   *fp;
+      size_t len;
+      size_t written;
+
+      fp = fopen(filename, "w");
+      if (fp == NULL)
+      {
+         FTPD_SendReply(440, "Could not open file.");
+         free(bigBufr);
+         return(1);
+      }
+      
+      n = 0;
+      written = 0;
+      while (n<size)
+      {
+         len = ((size-n>BUFSIZ)?(BUFSIZ):(size-n));
+         written = fwrite(&bigBufr[n], 1, len, fp);
+         n += written;
+         if (written != len)
+         {
+            break;
+         }
+      }
+      fclose(fp);
+
+      if (n == size)
+      {
+         FTPD_SendReply(226, "Transfer complete.");
+      }
+      else
+      {
+         FTPD_SendReply(440, "Error during write.");
+         free(bigBufr);
+         return(1);
+      }
+   }
+
+   free(bigBufr);
+   return(0);
+}
+
+
+/**************************************************************************
+ * Function: FTPD_CommandList                                             *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    Sends a file list through a data connection.  The data              *
+ *    connection must have already been opened with the "PORT" command.   *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    char *fname  - File (or directory) to list.                         *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static void
+FTPD_CommandList(char *fname)
+{
+   int                 s;
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+   DIR                 *dirp;
+   struct dirent       *dp;
+   char                dirline[255];
+   struct stat         stat_buf;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   FTPD_SendReply(150, "ASCII data connection for LIST.");
+
+   s = socket(AF_INET, SOCK_STREAM, 0);
+   if (connect(s, (struct sockaddr *)&info->data_addr,
+               sizeof(struct sockaddr)) < 0)
+   {
+      syslog(LOG_ERR, "ftpd: Error connecting to data socket.");
+      return;
+   }
+
+   if ((dirp = opendir(fname)) == NULL)
+   {
+      sprintf(dirline, "%s: No such file or directory.%s\n",
+              fname, (info->xfer_mode==TYPE_A)?("\r"):(""));
+      send(s, dirline, strlen(dirline), 0);
+      close(s);
+      FTPD_SendReply(226, "Transfer complete.");
+      return;
+   }
+   while ((dp = readdir(dirp)) != NULL)
+   {
+      if (stat(dp->d_name, &stat_buf) == 0)
+      {
+         sprintf(dirline, "%c%c%c%c%c%c%c%c%c%c  %5d %5d %11d  %s%s\n",
+                 (S_ISLNK(stat_buf.st_mode)?('l'):
+                    (S_ISDIR(stat_buf.st_mode)?('d'):('-'))),
+                 (stat_buf.st_mode & S_IRUSR)?('r'):('-'),
+                 (stat_buf.st_mode & S_IWUSR)?('w'):('-'),
+                 (stat_buf.st_mode & S_IXUSR)?('x'):('-'),
+                 (stat_buf.st_mode & S_IRGRP)?('r'):('-'),
+                 (stat_buf.st_mode & S_IWGRP)?('w'):('-'),
+                 (stat_buf.st_mode & S_IXGRP)?('x'):('-'),
+                 (stat_buf.st_mode & S_IROTH)?('r'):('-'),
+                 (stat_buf.st_mode & S_IWOTH)?('w'):('-'),
+                 (stat_buf.st_mode & S_IXOTH)?('x'):('-'),
+                 (int)stat_buf.st_uid,
+                 (int)stat_buf.st_gid,
+                 (int)stat_buf.st_size,
+                 dp->d_name,
+                 (info->xfer_mode==TYPE_A)?("\r"):(""));
+         send(s, dirline, strlen(dirline), 0);
+      }
+   }
+   closedir(dirp);
+
+   close(s);
+   FTPD_SendReply(226, "Transfer complete.");
+}
+
+
+/*
+ * Cheesy way to change directories
+ */
+static void
+FTPD_CWD(char *dir)
+{
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   if (chdir(dir) == 0)
+   {
+      FTPD_SendReply(250, "CWD command successful.");
+   }
+   else
+   {
+      FTPD_SendReply(550, "CWD command failed.");
+   }
+}
+
+
+/**************************************************************************
+ * Function: FTPD_CommandPort                                             *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This procedure opens up a data port given the IP address of the     *
+ *    remote machine and the port on the remote machine.  This connection *
+ *    will then be used to transfer data between the hosts.               *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    char *bufr - Arguments to the "PORT" command.                       *
+ *                                                                        *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static void
+FTPD_CommandPort(char *bufr)
+{
+   char                *ip;
+   char                *port;
+   int                 ip0, ip1, ip2, ip3, port0, port1;
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   sscanf(bufr, "%d,%d,%d,%d,%d,%d", &ip0, &ip1, &ip2, &ip3, &port0, &port1);
+   ip = (char *)&(info->data_addr.sin_addr);
+   ip[0] = ip0 & 0xff;
+   ip[1] = ip1 & 0xff;
+   ip[2] = ip2 & 0xff;
+   ip[3] = ip3 & 0xff;
+   port = (char *)&(info->data_addr.sin_port);
+   port[0] = port0 & 0xff;
+   port[1] = port1 & 0xff;
+   info->data_addr.sin_family = AF_INET;
+}
+
+
+/**************************************************************************
+ * Function: FTPD_ParseCommand                                            *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    Here, we parse the commands that have come through the control      *
+ *    connection.                                                         *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    char *bufr     - Pointer to the buffer which contains the command   *
+ *                     text.                                              *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static void
+FTPD_ParseCommand(char *bufr)
+{
+   char fname[255];
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   if (!strncmp("PORT", bufr, 4))
+   {
+      FTPD_SendReply(200, "PORT command successful.");
+      FTPD_CommandPort(&bufr[5]);
+   }
+   else if (!strncmp("RETR", bufr, 4))
+   {
+      sscanf(&bufr[5], "%254s", fname);
+      FTPD_CommandRetrieve(fname);
+   }
+   else if (!strncmp("STOR", bufr, 4))
+   {
+      sscanf(&bufr[5], "%254s", fname);
+      FTPD_CommandStore(fname);
+   }
+   else if (!strncmp("LIST", bufr, 4))
+   {
+      if (bufr[5] == '\n')
+      {
+         FTPD_CommandList(".");
+      }
+      else
+      {
+         sscanf(&bufr[5], "%254s", fname);
+         FTPD_CommandList(fname);
+      }
+   }
+   else if (!strncmp("USER", bufr, 4))
+   {
+      FTPD_SendReply(230, "User logged in.");
+   }
+   else if (!strncmp("SYST", bufr, 4))
+   {
+      FTPD_SendReply(240, "RTEMS");
+   }
+   else if (!strncmp("TYPE", bufr, 4))
+   {
+      if (bufr[5] == 'I')
+      {
+         info->xfer_mode = TYPE_I;
+         FTPD_SendReply(200, "Type set to I.");
+      }
+      else if (bufr[5] == 'A')
+      {
+         info->xfer_mode = TYPE_A;
+         FTPD_SendReply(200, "Type set to A.");
+      } 
+      else
+      {
+         info->xfer_mode = TYPE_I;
+         FTPD_SendReply(504, "Type not implemented.  Set to I.");
+      }
+   }
+   else if (!strncmp("PASS", bufr, 4))
+   {
+      FTPD_SendReply(230, "User logged in.");
+   }
+   else if (!strncmp("DELE", bufr, 4))
+   {
+      sscanf(&bufr[4], "%254s", fname);
+      if (unlink(fname) == 0)
+      {
+         FTPD_SendReply(257, "DELE successful.");
+      }
+      else
+      {
+         FTPD_SendReply(550, "DELE failed.");
+      }
+   }
+   else if (!strncmp("SITE CHMOD", bufr, 10))
+   {
+      int mask;
+
+      sscanf(&bufr[11], "%o %254s", &mask, fname);
+      if (chmod(fname, (mode_t)mask) == 0)
+      {
+         FTPD_SendReply(257, "CHMOD successful.");
+      }
+      else
+      {
+         FTPD_SendReply(550, "CHMOD failed.");
+      }
+   }
+   else if (!strncmp("RMD", bufr, 3))
+   {
+      sscanf(&bufr[4], "%254s", fname);
+      if (rmdir(fname) == 0)
+      {
+         FTPD_SendReply(257, "RMD successful.");
+      }
+      else
+      {
+         FTPD_SendReply(550, "RMD failed.");
+      }
+   }
+   else if (!strncmp("MKD", bufr, 3))
+   {
+      sscanf(&bufr[4], "%254s", fname);
+      if (mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
+      {
+         FTPD_SendReply(257, "MKD successful.");
+      }
+      else
+      {
+         FTPD_SendReply(550, "MKD failed.");
+      }
+   }
+   else if (!strncmp("CWD", bufr, 3))
+   {
+      sscanf(&bufr[4], "%254s", fname);
+      FTPD_CWD(fname);
+   }
+   else if (!strncmp("PWD", bufr, 3))
+   {
+      char *cwd = getcwd(0, 0);
+      sprintf(bufr, "\"%s\" is the current directory.", cwd);
+      FTPD_SendReply(250, bufr);
+      free(cwd);
+   }
+   else
+   {
+      FTPD_SendReply(500, "Unrecognized/unsupported command.");
+   }
+}
+
+/**************************************************************************
+ * Function: FTPD_Session                                                 *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This task is started when the FTP daemon gets a service request     *
+ *    from a remote machine.  Here, we watch for commands that will       *
+ *    come through the "control" connection.  These commands are then     *
+ *    parsed and executed until the connection is closed, either          *
+ *    unintentionally or intentionally with the "QUIT" command.           *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    rtems_task_argument arg - The FTPD_Daemon task passes the socket    *
+ *                              which serves as the control connection.   *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static void
+FTPD_Session(rtems_task_argument arg)
+{
+   char                cmd[256];
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   FTPD_SendReply(220, "Erithacus FTP server (Version 1.0) ready.");
+
+   /***********************************************************************
+    * Set initial directory to "/".
+    **********************************************************************/
+   strcpy(info->cwd, "/");
+   info->xfer_mode = TYPE_A;
+   while (1)
+   {
+      if (recv(info->ctrl_sock, cmd, 256, 0) == -1)
+      {
+         syslog(LOG_INFO, "ftpd: Connection aborted.");
+         break;
+      }
+
+      if (!strncmp("QUIT", cmd, 4))
+      {
+         FTPD_SendReply(221, "Goodbye.");
+         break;
+      }
+      else
+      {
+         FTPD_ParseCommand(cmd);
+      }
+   }
+
+   if (close(info->ctrl_sock) < 0)
+   {
+      syslog(LOG_ERR, "ftpd: Could not close session.");
+   }
+
+
+   /* Least we can do is put the CWD back to /. */
+   chdir("/");
+
+   /***********************************************************************
+    * Free up the allocated SessionInfo struct and exit.
+    **********************************************************************/
+   free(info);
+   sc = rtems_task_delete(RTEMS_SELF);
+   syslog(LOG_ERR, "ftpd: Task deletion failed: %s",
+          rtems_status_text(sc));
+}
+
+
+/**************************************************************************
+ * Function: FTPD_Daemon                                                  *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This task runs in the background forever.  It waits for service     *
+ *    requests on the FTP port (port 21).  When a request is received,    *
+ *    it opens a new session to handle those requests until the           *
+ *    connection is closed.                                               *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static void
+FTPD_Daemon()
+{
+   int                 s;
+   int                 s1;
+   int                 addrLen;
+   struct sockaddr_in  remoteAddr;
+   struct sockaddr_in  localAddr;
+   char                sessionID;
+   rtems_task_priority priority;
+   rtems_status_code   sc;
+   rtems_id            tid;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sessionID = 'a';
+
+   s = socket(AF_INET, SOCK_STREAM, 0);
+   if (s < 0)
+   {
+      perror("Creating socket");
+   }
+
+   localAddr.sin_family      = AF_INET;
+   localAddr.sin_port        = FTPD_CONTROL_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)
+   {
+      perror("Binding control socket");
+   }
+
+   if (listen(s, 2) < 0)
+   {
+      perror("Listening on control socket");
+   }
+
+   while (1)
+   {
+      /********************************************************************
+       * Allocate a SessionInfo structure for the session task.
+       *******************************************************************/
+      info = (FTPD_SessionInfo_t *)malloc(sizeof(FTPD_SessionInfo_t));
+      if (info == NULL)
+      {
+         syslog(LOG_ERR, "ftpd: Could not allocate session info struct.");
+         rtems_panic("Malloc fail.");
+      }
+
+      /********************************************************************
+       * Accept on the socket and start the session task.
+       *******************************************************************/
+      addrLen = sizeof(remoteAddr);
+      s1 = accept(s, (struct sockaddr *)&remoteAddr, &addrLen);
+      if (s1 < 0)
+      {
+         perror("Accepting control connection");
+      }
+
+      rtems_task_set_priority(RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &priority);
+      sc = rtems_task_create(rtems_build_name('F', 'T', 'P', sessionID),
+                             priority, 8*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, "ftpd: Could not create FTPD session: %s", 
+                rtems_status_text(sc));
+      }
+
+      if (sessionID == 'z')
+      {
+         sessionID = 'a';
+      }
+      else
+      {
+         sessionID++;
+      }
+
+      /********************************************************************
+       * Send the socket on to the new session.
+       *******************************************************************/
+      info->ctrl_sock = s1;
+      sc = rtems_task_set_note(tid, RTEMS_NOTEPAD_0,
+                               (rtems_unsigned32)info);
+      sc = rtems_task_start(tid, FTPD_Session, 0);
+      if (sc != RTEMS_SUCCESSFUL)
+      {
+         syslog(LOG_ERR, "ftpd: Could not start FTPD session: %s",
+                rtems_status_text(sc));
+      }
+   }
+}
+
+
+/**************************************************************************
+ * Function: FTPD_Start                                                   *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    Here, we start the FTPD task which waits for FTP requests and       *
+ *    services them.  This procedure returns to its caller once the       *
+ *    task is started.                                                    *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    rtems_task_priority priority - Priority to assign to this task.     *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+void
+FTPD_Start(rtems_task_priority priority)
+{
+   rtems_status_code   sc;
+   rtems_id            tid;
+
+
+   sc = rtems_task_create(rtems_build_name('F', 'T', 'P', 'D'),
+                          priority, 8*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, "ftpd: Could not create FTP daemon: %s",
+             rtems_status_text(sc));
+   }
+
+   sc = rtems_task_start(tid, FTPD_Daemon, 0);
+   if (sc != RTEMS_SUCCESSFUL)
+   {
+      syslog(LOG_ERR, "ftpd: Could not start FTP daemon: %s",
+             rtems_status_text(sc));
+   }   
+
+   syslog(LOG_INFO, "ftpd: FTP daemon started.");
+}
Index: c/src/lib/libnetworking/rtems_servers/ftpd.h
===================================================================
--- c/src/lib/libnetworking/rtems_servers/ftpd.h	(revision 6d0e13c3bd56e1f158bb8691e6ca244b160616d1)
+++ c/src/lib/libnetworking/rtems_servers/ftpd.h	(revision 6d0e13c3bd56e1f158bb8691e6ca244b160616d1)
@@ -0,0 +1,26 @@
+/*
+ *  FTP Server Information
+ *
+ *  $Id$
+ */
+
+#ifndef __FTPD_H__
+#define __FTPD_H__
+
+
+#define FTPD_CONTROL_PORT   21
+
+/*
+ * Reply codes.
+ */
+
+#define PRELIM          1       /* positive preliminary */
+#define COMPLETE        2       /* positive completion */
+#define CONTINUE        3       /* positive intermediate */
+#define TRANSIENT       4       /* transient negative completion */
+#define ERROR           5       /* permanent negative completion */
+
+void rtems_ftpd_sTart(rtems_task_priority priority);
+
+#endif  /* __FTPD_H__ */
+
Index: c/src/libnetworking/rtems_servers/Makefile.in
===================================================================
--- c/src/libnetworking/rtems_servers/Makefile.in	(revision 6d0e13c3bd56e1f158bb8691e6ca244b160616d1)
+++ c/src/libnetworking/rtems_servers/Makefile.in	(revision 6d0e13c3bd56e1f158bb8691e6ca244b160616d1)
@@ -0,0 +1,53 @@
+#
+#  $Id$
+#
+
+@SET_MAKE@
+srcdir = @srcdir@
+VPATH = @srcdir@
+RTEMS_ROOT = @top_srcdir@
+PROJECT_ROOT = @PROJECT_ROOT@
+
+INSTALL = @INSTALL@
+
+LIBNAME=lib.a
+LIB=${ARCH}/${LIBNAME}
+
+# C and C++ source names, if any, go here -- minus the .c or .cc
+C_PIECES=sghostname issetugid \
+        rtems_glue rtems_syscall rtems_bootp \
+        rtems_showmbuf rtems_showroute \
+        rtems_showifstat rtems_showipstat rtems_showicmpstat \
+        rtems_showtcpstat rtems_showudpstat rtems_select
+C_FILES=$(C_PIECES:%=%.c)
+C_O_FILES=$(C_PIECES:%=${ARCH}/%.o)
+
+SRCS=$(C_FILES)
+OBJS=$(C_O_FILES)
+
+include $(RTEMS_ROOT)/make/custom/$(RTEMS_BSP).cfg
+include $(RTEMS_ROOT)/make/lib.cfg
+
+#
+# Add local stuff here using +=
+#
+
+DEFINES  += -D_COMPILING_BSD_KERNEL_ -DKERNEL -DINET -DNFS \
+    -DDIAGNOSTIC -DBOOTP_COMPAT
+CPPFLAGS +=
+CFLAGS   +=
+
+#
+# Add your list of files to delete here.  The config files
+#  already know how to delete some stuff, so you may want
+#  to just run 'make clean' first to see what gets missed.
+#  'make clobber' already includes 'make clean'
+#
+
+CLEAN_ADDITIONS += $(LIB)
+CLOBBER_ADDITIONS +=
+
+all:	${ARCH} $(LIB)
+
+$(LIB): $(SRCS) ${OBJS}
+	$(make-library)
Index: c/src/libnetworking/rtems_servers/ftpd.c
===================================================================
--- c/src/libnetworking/rtems_servers/ftpd.c	(revision 6d0e13c3bd56e1f158bb8691e6ca244b160616d1)
+++ c/src/libnetworking/rtems_servers/ftpd.c	(revision 6d0e13c3bd56e1f158bb8691e6ca244b160616d1)
@@ -0,0 +1,1067 @@
+/*
+ *  FTP Server Daemon
+ *
+ *  Submitted by: Jake Janovetz <janovetz@tempest.ece.uiuc.edu>
+ *
+ *  $Id$
+ */
+
+/*
+ * Current state:
+ *   To untar, put as "untar"
+ * CWD uses chdir
+ *   This is bad due to global setting of chdir.
+ *
+ * Stored files come into RAM and are saved later.  This is an artifact
+ *   of a previous implementation (no filesystem -- had to do stuff with
+ *   the "files" later).  This can be eliminated once all of Jake's stuff
+ *   is moved to devices/filesystems.
+ *
+ * CLOSE(S) doesn't seem to work.  This causes problems in 
+ *          several areas.  It lets too many file descriptors pile up
+ *          and it doesn't seem to flush the stream.
+ *
+ * Is 'recv' what I want to use to get commands from the control port?
+ *
+ */
+
+/**************************************************************************
+ *                                 ftpd.c                                 *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This file contains the daemon which services requests that appear   *
+ *    on the 'FTP' port.  This server is compatible with FTP, but it      *
+ *    also provides services specific to the Erithacus system.            *
+ *    This server is started at boot-time and runs forever.               *
+ *                                                                        *
+ *    Organization:                                                       *
+ *                                                                        *
+ *       The FTP daemon is started upon boot.  It runs all the time       *
+ *       and waits for connections on the known FTP port (21).  When      *
+ *       a connection is made, it starts a 'session' task.  That          *
+ *       session then interacts with the remote host.  When the session   *
+ *       is complete, the session task deletes itself.  The daemon still  *
+ *       runs, however.                                                   *
+ *                                                                        *
+ *    Implementation Notes:                                               *
+ *                                                                        *
+ *       The 'current working directory' implementation utilizes the      *
+ *       RTEMS filesystem cwd.  This is no good since other processes     *
+ *       inherit the same cwd.                                            *
+ *                                                                        *
+ *                                                                        *
+ * Supported commands are:                                                *
+ *                                                                        *
+ * RETR xxx     - Sends a file from the client.                           *
+ * STOR xxx     - Receives a file from the client.  xxx = filename.       *
+ * LIST xxx     - Sends a file list to the client.                        *
+ *                (LIST xxx isn't working yet...)                         *
+ * USER         - Does nothing.                                           *
+ * PASS         - Does nothing.                                           *
+ * SYST         - Replies with the system type (`RTEMS').                 *
+ * DELE xxx     - Delete file xxx.                                        *
+ * MKD xxx      - Create directory xxx.                                   *
+ * RMD xxx      - Remove directory xxx.                                   *
+ * PWD          - Print working directory.                                *
+ * CWD xxx      - Change working directory.                               *
+ * SITE CHMOD xxx yyy - Change permissions on file yyy to xxx.            *
+ * PORT a,b,c,d,x,y   - Setup for a data port to IP address a.b.c.d       *
+ *                      and port (x*256 + y).                             *
+ *                                                                        *
+ *                                                                        *
+ *                                                                        *
+ * The public routines contained in this file are:                        *
+ *                                                                        *
+ *    FTPD_Start - Starts the server daemon, then returns to its caller.  *
+ *                                                                        *
+ *                                                                        *
+ * The private routines contained in this file are:                       *
+ *                                                                        *
+ *    FTPD_SendReply    - Sends a reply code and text through the control *
+ *                        port.                                           *
+ *    FTPD_CommandStore - Performs the "STOR" command.                    *
+ *    FTPD_CommandList  - Performs the "LIST" command.                    *
+ *    FTPD_CommandPort  - Opens a data port (the "PORT" command).         *
+ *    FTPD_ParseCommand - Parses an incoming command.                     *
+ *    FTPD_Session      - Begins a service session.                       *
+ *    FTPD_Daemon       - Listens on the FTP port for service requests.   *
+ *                                                                        *
+ *                                                                        *
+ *------------------------------------------------------------------------*
+ *                                                                        *
+ * Jake Janovetz                                                          *
+ * University of Illinois                                                 *
+ * 1406 West Green Street                                                 *
+ * Urbana IL  61801                                                       *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+
+/* Revision Control Information:
+ *
+ * $Source$
+ * $Id$
+ * $Log$
+ * Revision 1.3  1998/05/19 21:28:17  erithacus
+ * Update control socket to file I/O.
+ *
+ * Revision 1.2  1998/05/19 20:13:50  erithacus
+ * Remodeled to be entirely reentrant.
+ *
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.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 <arpa/ftp.h>
+#include <netinet/in.h>
+
+#include "ftpd.h"
+#include "untar.h"
+
+
+/**************************************************************************
+ * Meanings of first and second digits of reply codes:
+ *
+ * Reply:  Description:
+ *-------- --------------
+ *  1yz    Positive preliminary reply.  The action is being started but
+ *         expect another reply before sending another command.
+ *  2yz    Positive completion reply.  A new command can be sent.
+ *  3yz    Positive intermediate reply.  The command has been accpeted
+ *         but another command must be sent.
+ *  4yz    Transient negative completion reply.  The requested action did
+ *         not take place, but the error condition is temporary so the
+ *         command can be reissued later.
+ *  5yz    Permanent negative completion reply.  The command was not  
+ *         accepted and should not be retried.
+ *-------------------------------------------------------------------------
+ *  x0z    Syntax errors.
+ *  x1z    Information.
+ *  x2z    Connections.  Replies referring to the control or data
+ *         connections.
+ *  x3z    Authentication and accounting.  Replies for the login or
+ *         accounting commands.
+ *  x4z    Unspecified.
+ *  x5z    Filesystem status.
+ *************************************************************************/
+
+
+/**************************************************************************
+ * Maximum buffer size for use by the transfer protocol.
+ *  This will be eliminated when the filesystem is complete enough that
+ *  we don't have to store the received data until we have something to
+ *  do with it.
+ *************************************************************************/
+#define FTPD_MAX_RECEIVESIZE  (512*1024)
+
+/**************************************************************************
+ * SessionInfo structure.
+ *
+ * The following structure is allocated for each session.  The pointer
+ * to this structure is contained in the tasks notepad entry.
+ *************************************************************************/
+typedef struct
+{
+   struct sockaddr_in  data_addr;   /* Data address for PORT commands */
+   int                 ctrl_sock;   /* Control connection socker */
+   char                cwd[255];    /* Current working directory */
+                                    /* Login -- future use -- */
+   int                 xfer_mode;   /* Transfer mode (ASCII/binary) */
+} FTPD_SessionInfo_t;
+
+
+#define FTPD_WELCOME_MESSAGE \
+   "Welcome to the RTEMS FTP server.\n" \
+   "\n" \
+   "Login accepted.\n"
+
+
+/**************************************************************************
+ * Function: FTPD_SendReply                                               *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This procedure sends a reply to the client via the control          *
+ *    connection.                                                         *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    int  code  - The 3-digit reply code.                                *
+ *    char *text - Reply text.                                            *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static void
+FTPD_SendReply(int code, char *text)
+{
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+   char                str[80];
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   /***********************************************************************
+    * code must be a 3-digit number.
+    **********************************************************************/
+   if ((code < 100) || (code > 999))
+   {
+      syslog(LOG_ERR, "ftpd: Code not 3-digits.");
+      return;
+   }
+
+   /***********************************************************************
+    * If a text reply exists, add it to the reply data.
+    **********************************************************************/
+   if (text != NULL)
+   {
+      sprintf(str, "%d %.70s\r\n", code, text);
+   }
+   else
+   {
+      sprintf(str, "%d\r\n", code);
+   }
+   send(info->ctrl_sock, str, strlen(str), 0);
+}
+
+
+/**************************************************************************
+ * Function: FTPD_CommandRetrieve                                         *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This performs the "RETR" command.  A data connection must already   *
+ *    be open (via the "PORT" command.)  Here, we send the data to the    *
+ *    connection.                                                         *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    char *filename   - Source filename.                                 *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    int  - 0 for reply sent.                                            *
+ *           1 for no reply sent.                                         *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  04/29/98 - Creation (JWJ)                                             *
+ *************************************************************************/
+static int
+FTPD_CommandRetrieve(char *filename)
+{
+   int                 s;
+   int                 n;
+
+   FILE                *fp;
+   unsigned char       *bufr;
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+
+   if ((fp = fopen(filename, "r")) == NULL)
+   {
+      FTPD_SendReply(450, "Error opening file.");
+      return(0);
+   }
+
+   bufr = (unsigned char *)malloc(BUFSIZ);
+   if (bufr == NULL)
+   {
+      FTPD_SendReply(440, "Server error - malloc fail.");
+      fclose(fp);
+      return(0);
+   }
+
+   /***********************************************************************
+    * Connect to the data connection (PORT made in an earlier PORT call).
+    **********************************************************************/
+   FTPD_SendReply(150, "BINARY data connection.");
+   s = socket(AF_INET, SOCK_STREAM, 0);
+   if (connect(s, (struct sockaddr *)&info->data_addr,
+               sizeof(struct sockaddr)) < 0)
+   {
+      FTPD_SendReply(420, "Server error - could not connect socket.");
+      free(bufr);
+      fclose(fp);
+      close(s);
+      return(1);
+   }
+
+   /***********************************************************************
+    * Send the data over the ether.
+    **********************************************************************/
+   while ((n = fread(bufr, 1, BUFSIZ, fp)) != 0)
+   {
+      send(s, bufr, n, 0);
+   }
+
+   if (feof(fp))
+   {
+      FTPD_SendReply(210, "File sent successfully.");
+   }
+   else
+   {
+      FTPD_SendReply(450, "Retrieve failed.");
+   }
+
+   if (close(s) != 0)
+   {
+      syslog(LOG_ERR, "ftpd: Error closing data socket");
+   }
+
+   free(bufr);
+   fclose(fp);
+   return(0);
+}
+
+
+/**************************************************************************
+ * Function: FTPD_CommandStore                                            *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This performs the "STOR" command.  A data connection must already   *
+ *    be open (via the "PORT" command.)  Here, we get the data from the   *
+ *    connection and figure out what to do with it.                       *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    char *filename   - Destination filename.                            *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    int  - 0 for success.                                               *
+ *           1 for failure.                                               *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static int
+FTPD_CommandStore(char *filename)
+{
+   char                *bufr;
+   char                *bigBufr;
+   int                 s;
+   int                 n;
+   unsigned long       size = 0;
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   bufr = (char *)malloc(BUFSIZ * sizeof(char));
+   if (bufr == NULL)
+   {
+      FTPD_SendReply(440, "Server error - malloc fail.");
+      return(1);
+   }
+
+   bigBufr = (char *)malloc(FTPD_MAX_RECEIVESIZE * sizeof(char));
+   if (bigBufr == NULL)
+   {
+      FTPD_SendReply(440, "Server error - malloc fail.");
+      free(bufr);
+      return(1);
+   }
+
+   FTPD_SendReply(150, "BINARY data connection.");
+
+   s = socket(AF_INET, SOCK_STREAM, 0);
+   if (connect(s, (struct sockaddr *)&info->data_addr,
+               sizeof(struct sockaddr)) < 0)
+   {
+      free(bufr);
+      free(bigBufr);
+      close(s);
+      return(1);
+   }
+
+
+   /***********************************************************************
+    * File: "/dev/null" just throws the data away.
+    **********************************************************************/
+   if (!strncmp("/dev/null", filename, 9))
+   {
+      while ((n = read(s, bufr, BUFSIZ)) > 0);
+   }
+   else
+   {
+      /***********************************************************************
+       * Retrieve the file into our buffer space.
+       **********************************************************************/
+      size = 0;
+      while ((n = read(s, bufr, BUFSIZ)) > 0)
+      {
+         if (size + n > FTPD_MAX_RECEIVESIZE)
+         {
+            FTPD_SendReply(440, "Server error - Buffer size exceeded.");
+            free(bufr);
+            free(bigBufr);
+            close(s);
+            return(1);
+         }
+         memcpy(&bigBufr[size], bufr, n);
+         size += n;
+      }
+   }
+   free(bufr);
+   close(s);
+
+
+   /***********************************************************************
+    * Figure out what to do with the data we just received.
+    **********************************************************************/
+   if (!strncmp("untar", filename, 5))
+   {
+      Untar_FromMemory(bigBufr, size);
+      FTPD_SendReply(210, "Untar successful.");
+   }
+   else
+   {
+      FILE   *fp;
+      size_t len;
+      size_t written;
+
+      fp = fopen(filename, "w");
+      if (fp == NULL)
+      {
+         FTPD_SendReply(440, "Could not open file.");
+         free(bigBufr);
+         return(1);
+      }
+      
+      n = 0;
+      written = 0;
+      while (n<size)
+      {
+         len = ((size-n>BUFSIZ)?(BUFSIZ):(size-n));
+         written = fwrite(&bigBufr[n], 1, len, fp);
+         n += written;
+         if (written != len)
+         {
+            break;
+         }
+      }
+      fclose(fp);
+
+      if (n == size)
+      {
+         FTPD_SendReply(226, "Transfer complete.");
+      }
+      else
+      {
+         FTPD_SendReply(440, "Error during write.");
+         free(bigBufr);
+         return(1);
+      }
+   }
+
+   free(bigBufr);
+   return(0);
+}
+
+
+/**************************************************************************
+ * Function: FTPD_CommandList                                             *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    Sends a file list through a data connection.  The data              *
+ *    connection must have already been opened with the "PORT" command.   *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    char *fname  - File (or directory) to list.                         *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static void
+FTPD_CommandList(char *fname)
+{
+   int                 s;
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+   DIR                 *dirp;
+   struct dirent       *dp;
+   char                dirline[255];
+   struct stat         stat_buf;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   FTPD_SendReply(150, "ASCII data connection for LIST.");
+
+   s = socket(AF_INET, SOCK_STREAM, 0);
+   if (connect(s, (struct sockaddr *)&info->data_addr,
+               sizeof(struct sockaddr)) < 0)
+   {
+      syslog(LOG_ERR, "ftpd: Error connecting to data socket.");
+      return;
+   }
+
+   if ((dirp = opendir(fname)) == NULL)
+   {
+      sprintf(dirline, "%s: No such file or directory.%s\n",
+              fname, (info->xfer_mode==TYPE_A)?("\r"):(""));
+      send(s, dirline, strlen(dirline), 0);
+      close(s);
+      FTPD_SendReply(226, "Transfer complete.");
+      return;
+   }
+   while ((dp = readdir(dirp)) != NULL)
+   {
+      if (stat(dp->d_name, &stat_buf) == 0)
+      {
+         sprintf(dirline, "%c%c%c%c%c%c%c%c%c%c  %5d %5d %11d  %s%s\n",
+                 (S_ISLNK(stat_buf.st_mode)?('l'):
+                    (S_ISDIR(stat_buf.st_mode)?('d'):('-'))),
+                 (stat_buf.st_mode & S_IRUSR)?('r'):('-'),
+                 (stat_buf.st_mode & S_IWUSR)?('w'):('-'),
+                 (stat_buf.st_mode & S_IXUSR)?('x'):('-'),
+                 (stat_buf.st_mode & S_IRGRP)?('r'):('-'),
+                 (stat_buf.st_mode & S_IWGRP)?('w'):('-'),
+                 (stat_buf.st_mode & S_IXGRP)?('x'):('-'),
+                 (stat_buf.st_mode & S_IROTH)?('r'):('-'),
+                 (stat_buf.st_mode & S_IWOTH)?('w'):('-'),
+                 (stat_buf.st_mode & S_IXOTH)?('x'):('-'),
+                 (int)stat_buf.st_uid,
+                 (int)stat_buf.st_gid,
+                 (int)stat_buf.st_size,
+                 dp->d_name,
+                 (info->xfer_mode==TYPE_A)?("\r"):(""));
+         send(s, dirline, strlen(dirline), 0);
+      }
+   }
+   closedir(dirp);
+
+   close(s);
+   FTPD_SendReply(226, "Transfer complete.");
+}
+
+
+/*
+ * Cheesy way to change directories
+ */
+static void
+FTPD_CWD(char *dir)
+{
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   if (chdir(dir) == 0)
+   {
+      FTPD_SendReply(250, "CWD command successful.");
+   }
+   else
+   {
+      FTPD_SendReply(550, "CWD command failed.");
+   }
+}
+
+
+/**************************************************************************
+ * Function: FTPD_CommandPort                                             *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This procedure opens up a data port given the IP address of the     *
+ *    remote machine and the port on the remote machine.  This connection *
+ *    will then be used to transfer data between the hosts.               *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    char *bufr - Arguments to the "PORT" command.                       *
+ *                                                                        *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static void
+FTPD_CommandPort(char *bufr)
+{
+   char                *ip;
+   char                *port;
+   int                 ip0, ip1, ip2, ip3, port0, port1;
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   sscanf(bufr, "%d,%d,%d,%d,%d,%d", &ip0, &ip1, &ip2, &ip3, &port0, &port1);
+   ip = (char *)&(info->data_addr.sin_addr);
+   ip[0] = ip0 & 0xff;
+   ip[1] = ip1 & 0xff;
+   ip[2] = ip2 & 0xff;
+   ip[3] = ip3 & 0xff;
+   port = (char *)&(info->data_addr.sin_port);
+   port[0] = port0 & 0xff;
+   port[1] = port1 & 0xff;
+   info->data_addr.sin_family = AF_INET;
+}
+
+
+/**************************************************************************
+ * Function: FTPD_ParseCommand                                            *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    Here, we parse the commands that have come through the control      *
+ *    connection.                                                         *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    char *bufr     - Pointer to the buffer which contains the command   *
+ *                     text.                                              *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static void
+FTPD_ParseCommand(char *bufr)
+{
+   char fname[255];
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   if (!strncmp("PORT", bufr, 4))
+   {
+      FTPD_SendReply(200, "PORT command successful.");
+      FTPD_CommandPort(&bufr[5]);
+   }
+   else if (!strncmp("RETR", bufr, 4))
+   {
+      sscanf(&bufr[5], "%254s", fname);
+      FTPD_CommandRetrieve(fname);
+   }
+   else if (!strncmp("STOR", bufr, 4))
+   {
+      sscanf(&bufr[5], "%254s", fname);
+      FTPD_CommandStore(fname);
+   }
+   else if (!strncmp("LIST", bufr, 4))
+   {
+      if (bufr[5] == '\n')
+      {
+         FTPD_CommandList(".");
+      }
+      else
+      {
+         sscanf(&bufr[5], "%254s", fname);
+         FTPD_CommandList(fname);
+      }
+   }
+   else if (!strncmp("USER", bufr, 4))
+   {
+      FTPD_SendReply(230, "User logged in.");
+   }
+   else if (!strncmp("SYST", bufr, 4))
+   {
+      FTPD_SendReply(240, "RTEMS");
+   }
+   else if (!strncmp("TYPE", bufr, 4))
+   {
+      if (bufr[5] == 'I')
+      {
+         info->xfer_mode = TYPE_I;
+         FTPD_SendReply(200, "Type set to I.");
+      }
+      else if (bufr[5] == 'A')
+      {
+         info->xfer_mode = TYPE_A;
+         FTPD_SendReply(200, "Type set to A.");
+      } 
+      else
+      {
+         info->xfer_mode = TYPE_I;
+         FTPD_SendReply(504, "Type not implemented.  Set to I.");
+      }
+   }
+   else if (!strncmp("PASS", bufr, 4))
+   {
+      FTPD_SendReply(230, "User logged in.");
+   }
+   else if (!strncmp("DELE", bufr, 4))
+   {
+      sscanf(&bufr[4], "%254s", fname);
+      if (unlink(fname) == 0)
+      {
+         FTPD_SendReply(257, "DELE successful.");
+      }
+      else
+      {
+         FTPD_SendReply(550, "DELE failed.");
+      }
+   }
+   else if (!strncmp("SITE CHMOD", bufr, 10))
+   {
+      int mask;
+
+      sscanf(&bufr[11], "%o %254s", &mask, fname);
+      if (chmod(fname, (mode_t)mask) == 0)
+      {
+         FTPD_SendReply(257, "CHMOD successful.");
+      }
+      else
+      {
+         FTPD_SendReply(550, "CHMOD failed.");
+      }
+   }
+   else if (!strncmp("RMD", bufr, 3))
+   {
+      sscanf(&bufr[4], "%254s", fname);
+      if (rmdir(fname) == 0)
+      {
+         FTPD_SendReply(257, "RMD successful.");
+      }
+      else
+      {
+         FTPD_SendReply(550, "RMD failed.");
+      }
+   }
+   else if (!strncmp("MKD", bufr, 3))
+   {
+      sscanf(&bufr[4], "%254s", fname);
+      if (mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
+      {
+         FTPD_SendReply(257, "MKD successful.");
+      }
+      else
+      {
+         FTPD_SendReply(550, "MKD failed.");
+      }
+   }
+   else if (!strncmp("CWD", bufr, 3))
+   {
+      sscanf(&bufr[4], "%254s", fname);
+      FTPD_CWD(fname);
+   }
+   else if (!strncmp("PWD", bufr, 3))
+   {
+      char *cwd = getcwd(0, 0);
+      sprintf(bufr, "\"%s\" is the current directory.", cwd);
+      FTPD_SendReply(250, bufr);
+      free(cwd);
+   }
+   else
+   {
+      FTPD_SendReply(500, "Unrecognized/unsupported command.");
+   }
+}
+
+/**************************************************************************
+ * Function: FTPD_Session                                                 *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This task is started when the FTP daemon gets a service request     *
+ *    from a remote machine.  Here, we watch for commands that will       *
+ *    come through the "control" connection.  These commands are then     *
+ *    parsed and executed until the connection is closed, either          *
+ *    unintentionally or intentionally with the "QUIT" command.           *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    rtems_task_argument arg - The FTPD_Daemon task passes the socket    *
+ *                              which serves as the control connection.   *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static void
+FTPD_Session(rtems_task_argument arg)
+{
+   char                cmd[256];
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   FTPD_SendReply(220, "Erithacus FTP server (Version 1.0) ready.");
+
+   /***********************************************************************
+    * Set initial directory to "/".
+    **********************************************************************/
+   strcpy(info->cwd, "/");
+   info->xfer_mode = TYPE_A;
+   while (1)
+   {
+      if (recv(info->ctrl_sock, cmd, 256, 0) == -1)
+      {
+         syslog(LOG_INFO, "ftpd: Connection aborted.");
+         break;
+      }
+
+      if (!strncmp("QUIT", cmd, 4))
+      {
+         FTPD_SendReply(221, "Goodbye.");
+         break;
+      }
+      else
+      {
+         FTPD_ParseCommand(cmd);
+      }
+   }
+
+   if (close(info->ctrl_sock) < 0)
+   {
+      syslog(LOG_ERR, "ftpd: Could not close session.");
+   }
+
+
+   /* Least we can do is put the CWD back to /. */
+   chdir("/");
+
+   /***********************************************************************
+    * Free up the allocated SessionInfo struct and exit.
+    **********************************************************************/
+   free(info);
+   sc = rtems_task_delete(RTEMS_SELF);
+   syslog(LOG_ERR, "ftpd: Task deletion failed: %s",
+          rtems_status_text(sc));
+}
+
+
+/**************************************************************************
+ * Function: FTPD_Daemon                                                  *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This task runs in the background forever.  It waits for service     *
+ *    requests on the FTP port (port 21).  When a request is received,    *
+ *    it opens a new session to handle those requests until the           *
+ *    connection is closed.                                               *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static void
+FTPD_Daemon()
+{
+   int                 s;
+   int                 s1;
+   int                 addrLen;
+   struct sockaddr_in  remoteAddr;
+   struct sockaddr_in  localAddr;
+   char                sessionID;
+   rtems_task_priority priority;
+   rtems_status_code   sc;
+   rtems_id            tid;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sessionID = 'a';
+
+   s = socket(AF_INET, SOCK_STREAM, 0);
+   if (s < 0)
+   {
+      perror("Creating socket");
+   }
+
+   localAddr.sin_family      = AF_INET;
+   localAddr.sin_port        = FTPD_CONTROL_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)
+   {
+      perror("Binding control socket");
+   }
+
+   if (listen(s, 2) < 0)
+   {
+      perror("Listening on control socket");
+   }
+
+   while (1)
+   {
+      /********************************************************************
+       * Allocate a SessionInfo structure for the session task.
+       *******************************************************************/
+      info = (FTPD_SessionInfo_t *)malloc(sizeof(FTPD_SessionInfo_t));
+      if (info == NULL)
+      {
+         syslog(LOG_ERR, "ftpd: Could not allocate session info struct.");
+         rtems_panic("Malloc fail.");
+      }
+
+      /********************************************************************
+       * Accept on the socket and start the session task.
+       *******************************************************************/
+      addrLen = sizeof(remoteAddr);
+      s1 = accept(s, (struct sockaddr *)&remoteAddr, &addrLen);
+      if (s1 < 0)
+      {
+         perror("Accepting control connection");
+      }
+
+      rtems_task_set_priority(RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &priority);
+      sc = rtems_task_create(rtems_build_name('F', 'T', 'P', sessionID),
+                             priority, 8*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, "ftpd: Could not create FTPD session: %s", 
+                rtems_status_text(sc));
+      }
+
+      if (sessionID == 'z')
+      {
+         sessionID = 'a';
+      }
+      else
+      {
+         sessionID++;
+      }
+
+      /********************************************************************
+       * Send the socket on to the new session.
+       *******************************************************************/
+      info->ctrl_sock = s1;
+      sc = rtems_task_set_note(tid, RTEMS_NOTEPAD_0,
+                               (rtems_unsigned32)info);
+      sc = rtems_task_start(tid, FTPD_Session, 0);
+      if (sc != RTEMS_SUCCESSFUL)
+      {
+         syslog(LOG_ERR, "ftpd: Could not start FTPD session: %s",
+                rtems_status_text(sc));
+      }
+   }
+}
+
+
+/**************************************************************************
+ * Function: FTPD_Start                                                   *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    Here, we start the FTPD task which waits for FTP requests and       *
+ *    services them.  This procedure returns to its caller once the       *
+ *    task is started.                                                    *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    rtems_task_priority priority - Priority to assign to this task.     *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+void
+FTPD_Start(rtems_task_priority priority)
+{
+   rtems_status_code   sc;
+   rtems_id            tid;
+
+
+   sc = rtems_task_create(rtems_build_name('F', 'T', 'P', 'D'),
+                          priority, 8*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, "ftpd: Could not create FTP daemon: %s",
+             rtems_status_text(sc));
+   }
+
+   sc = rtems_task_start(tid, FTPD_Daemon, 0);
+   if (sc != RTEMS_SUCCESSFUL)
+   {
+      syslog(LOG_ERR, "ftpd: Could not start FTP daemon: %s",
+             rtems_status_text(sc));
+   }   
+
+   syslog(LOG_INFO, "ftpd: FTP daemon started.");
+}
Index: c/src/libnetworking/rtems_servers/ftpd.h
===================================================================
--- c/src/libnetworking/rtems_servers/ftpd.h	(revision 6d0e13c3bd56e1f158bb8691e6ca244b160616d1)
+++ c/src/libnetworking/rtems_servers/ftpd.h	(revision 6d0e13c3bd56e1f158bb8691e6ca244b160616d1)
@@ -0,0 +1,26 @@
+/*
+ *  FTP Server Information
+ *
+ *  $Id$
+ */
+
+#ifndef __FTPD_H__
+#define __FTPD_H__
+
+
+#define FTPD_CONTROL_PORT   21
+
+/*
+ * Reply codes.
+ */
+
+#define PRELIM          1       /* positive preliminary */
+#define COMPLETE        2       /* positive completion */
+#define CONTINUE        3       /* positive intermediate */
+#define TRANSIENT       4       /* transient negative completion */
+#define ERROR           5       /* permanent negative completion */
+
+void rtems_ftpd_sTart(rtems_task_priority priority);
+
+#endif  /* __FTPD_H__ */
+
Index: cpukit/ftpd/ftpd.c
===================================================================
--- cpukit/ftpd/ftpd.c	(revision 6d0e13c3bd56e1f158bb8691e6ca244b160616d1)
+++ cpukit/ftpd/ftpd.c	(revision 6d0e13c3bd56e1f158bb8691e6ca244b160616d1)
@@ -0,0 +1,1067 @@
+/*
+ *  FTP Server Daemon
+ *
+ *  Submitted by: Jake Janovetz <janovetz@tempest.ece.uiuc.edu>
+ *
+ *  $Id$
+ */
+
+/*
+ * Current state:
+ *   To untar, put as "untar"
+ * CWD uses chdir
+ *   This is bad due to global setting of chdir.
+ *
+ * Stored files come into RAM and are saved later.  This is an artifact
+ *   of a previous implementation (no filesystem -- had to do stuff with
+ *   the "files" later).  This can be eliminated once all of Jake's stuff
+ *   is moved to devices/filesystems.
+ *
+ * CLOSE(S) doesn't seem to work.  This causes problems in 
+ *          several areas.  It lets too many file descriptors pile up
+ *          and it doesn't seem to flush the stream.
+ *
+ * Is 'recv' what I want to use to get commands from the control port?
+ *
+ */
+
+/**************************************************************************
+ *                                 ftpd.c                                 *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This file contains the daemon which services requests that appear   *
+ *    on the 'FTP' port.  This server is compatible with FTP, but it      *
+ *    also provides services specific to the Erithacus system.            *
+ *    This server is started at boot-time and runs forever.               *
+ *                                                                        *
+ *    Organization:                                                       *
+ *                                                                        *
+ *       The FTP daemon is started upon boot.  It runs all the time       *
+ *       and waits for connections on the known FTP port (21).  When      *
+ *       a connection is made, it starts a 'session' task.  That          *
+ *       session then interacts with the remote host.  When the session   *
+ *       is complete, the session task deletes itself.  The daemon still  *
+ *       runs, however.                                                   *
+ *                                                                        *
+ *    Implementation Notes:                                               *
+ *                                                                        *
+ *       The 'current working directory' implementation utilizes the      *
+ *       RTEMS filesystem cwd.  This is no good since other processes     *
+ *       inherit the same cwd.                                            *
+ *                                                                        *
+ *                                                                        *
+ * Supported commands are:                                                *
+ *                                                                        *
+ * RETR xxx     - Sends a file from the client.                           *
+ * STOR xxx     - Receives a file from the client.  xxx = filename.       *
+ * LIST xxx     - Sends a file list to the client.                        *
+ *                (LIST xxx isn't working yet...)                         *
+ * USER         - Does nothing.                                           *
+ * PASS         - Does nothing.                                           *
+ * SYST         - Replies with the system type (`RTEMS').                 *
+ * DELE xxx     - Delete file xxx.                                        *
+ * MKD xxx      - Create directory xxx.                                   *
+ * RMD xxx      - Remove directory xxx.                                   *
+ * PWD          - Print working directory.                                *
+ * CWD xxx      - Change working directory.                               *
+ * SITE CHMOD xxx yyy - Change permissions on file yyy to xxx.            *
+ * PORT a,b,c,d,x,y   - Setup for a data port to IP address a.b.c.d       *
+ *                      and port (x*256 + y).                             *
+ *                                                                        *
+ *                                                                        *
+ *                                                                        *
+ * The public routines contained in this file are:                        *
+ *                                                                        *
+ *    FTPD_Start - Starts the server daemon, then returns to its caller.  *
+ *                                                                        *
+ *                                                                        *
+ * The private routines contained in this file are:                       *
+ *                                                                        *
+ *    FTPD_SendReply    - Sends a reply code and text through the control *
+ *                        port.                                           *
+ *    FTPD_CommandStore - Performs the "STOR" command.                    *
+ *    FTPD_CommandList  - Performs the "LIST" command.                    *
+ *    FTPD_CommandPort  - Opens a data port (the "PORT" command).         *
+ *    FTPD_ParseCommand - Parses an incoming command.                     *
+ *    FTPD_Session      - Begins a service session.                       *
+ *    FTPD_Daemon       - Listens on the FTP port for service requests.   *
+ *                                                                        *
+ *                                                                        *
+ *------------------------------------------------------------------------*
+ *                                                                        *
+ * Jake Janovetz                                                          *
+ * University of Illinois                                                 *
+ * 1406 West Green Street                                                 *
+ * Urbana IL  61801                                                       *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+
+/* Revision Control Information:
+ *
+ * $Source$
+ * $Id$
+ * $Log$
+ * Revision 1.3  1998/05/19 21:28:17  erithacus
+ * Update control socket to file I/O.
+ *
+ * Revision 1.2  1998/05/19 20:13:50  erithacus
+ * Remodeled to be entirely reentrant.
+ *
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.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 <arpa/ftp.h>
+#include <netinet/in.h>
+
+#include "ftpd.h"
+#include "untar.h"
+
+
+/**************************************************************************
+ * Meanings of first and second digits of reply codes:
+ *
+ * Reply:  Description:
+ *-------- --------------
+ *  1yz    Positive preliminary reply.  The action is being started but
+ *         expect another reply before sending another command.
+ *  2yz    Positive completion reply.  A new command can be sent.
+ *  3yz    Positive intermediate reply.  The command has been accpeted
+ *         but another command must be sent.
+ *  4yz    Transient negative completion reply.  The requested action did
+ *         not take place, but the error condition is temporary so the
+ *         command can be reissued later.
+ *  5yz    Permanent negative completion reply.  The command was not  
+ *         accepted and should not be retried.
+ *-------------------------------------------------------------------------
+ *  x0z    Syntax errors.
+ *  x1z    Information.
+ *  x2z    Connections.  Replies referring to the control or data
+ *         connections.
+ *  x3z    Authentication and accounting.  Replies for the login or
+ *         accounting commands.
+ *  x4z    Unspecified.
+ *  x5z    Filesystem status.
+ *************************************************************************/
+
+
+/**************************************************************************
+ * Maximum buffer size for use by the transfer protocol.
+ *  This will be eliminated when the filesystem is complete enough that
+ *  we don't have to store the received data until we have something to
+ *  do with it.
+ *************************************************************************/
+#define FTPD_MAX_RECEIVESIZE  (512*1024)
+
+/**************************************************************************
+ * SessionInfo structure.
+ *
+ * The following structure is allocated for each session.  The pointer
+ * to this structure is contained in the tasks notepad entry.
+ *************************************************************************/
+typedef struct
+{
+   struct sockaddr_in  data_addr;   /* Data address for PORT commands */
+   int                 ctrl_sock;   /* Control connection socker */
+   char                cwd[255];    /* Current working directory */
+                                    /* Login -- future use -- */
+   int                 xfer_mode;   /* Transfer mode (ASCII/binary) */
+} FTPD_SessionInfo_t;
+
+
+#define FTPD_WELCOME_MESSAGE \
+   "Welcome to the RTEMS FTP server.\n" \
+   "\n" \
+   "Login accepted.\n"
+
+
+/**************************************************************************
+ * Function: FTPD_SendReply                                               *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This procedure sends a reply to the client via the control          *
+ *    connection.                                                         *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    int  code  - The 3-digit reply code.                                *
+ *    char *text - Reply text.                                            *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static void
+FTPD_SendReply(int code, char *text)
+{
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+   char                str[80];
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   /***********************************************************************
+    * code must be a 3-digit number.
+    **********************************************************************/
+   if ((code < 100) || (code > 999))
+   {
+      syslog(LOG_ERR, "ftpd: Code not 3-digits.");
+      return;
+   }
+
+   /***********************************************************************
+    * If a text reply exists, add it to the reply data.
+    **********************************************************************/
+   if (text != NULL)
+   {
+      sprintf(str, "%d %.70s\r\n", code, text);
+   }
+   else
+   {
+      sprintf(str, "%d\r\n", code);
+   }
+   send(info->ctrl_sock, str, strlen(str), 0);
+}
+
+
+/**************************************************************************
+ * Function: FTPD_CommandRetrieve                                         *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This performs the "RETR" command.  A data connection must already   *
+ *    be open (via the "PORT" command.)  Here, we send the data to the    *
+ *    connection.                                                         *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    char *filename   - Source filename.                                 *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    int  - 0 for reply sent.                                            *
+ *           1 for no reply sent.                                         *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  04/29/98 - Creation (JWJ)                                             *
+ *************************************************************************/
+static int
+FTPD_CommandRetrieve(char *filename)
+{
+   int                 s;
+   int                 n;
+
+   FILE                *fp;
+   unsigned char       *bufr;
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+
+   if ((fp = fopen(filename, "r")) == NULL)
+   {
+      FTPD_SendReply(450, "Error opening file.");
+      return(0);
+   }
+
+   bufr = (unsigned char *)malloc(BUFSIZ);
+   if (bufr == NULL)
+   {
+      FTPD_SendReply(440, "Server error - malloc fail.");
+      fclose(fp);
+      return(0);
+   }
+
+   /***********************************************************************
+    * Connect to the data connection (PORT made in an earlier PORT call).
+    **********************************************************************/
+   FTPD_SendReply(150, "BINARY data connection.");
+   s = socket(AF_INET, SOCK_STREAM, 0);
+   if (connect(s, (struct sockaddr *)&info->data_addr,
+               sizeof(struct sockaddr)) < 0)
+   {
+      FTPD_SendReply(420, "Server error - could not connect socket.");
+      free(bufr);
+      fclose(fp);
+      close(s);
+      return(1);
+   }
+
+   /***********************************************************************
+    * Send the data over the ether.
+    **********************************************************************/
+   while ((n = fread(bufr, 1, BUFSIZ, fp)) != 0)
+   {
+      send(s, bufr, n, 0);
+   }
+
+   if (feof(fp))
+   {
+      FTPD_SendReply(210, "File sent successfully.");
+   }
+   else
+   {
+      FTPD_SendReply(450, "Retrieve failed.");
+   }
+
+   if (close(s) != 0)
+   {
+      syslog(LOG_ERR, "ftpd: Error closing data socket");
+   }
+
+   free(bufr);
+   fclose(fp);
+   return(0);
+}
+
+
+/**************************************************************************
+ * Function: FTPD_CommandStore                                            *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This performs the "STOR" command.  A data connection must already   *
+ *    be open (via the "PORT" command.)  Here, we get the data from the   *
+ *    connection and figure out what to do with it.                       *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    char *filename   - Destination filename.                            *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    int  - 0 for success.                                               *
+ *           1 for failure.                                               *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static int
+FTPD_CommandStore(char *filename)
+{
+   char                *bufr;
+   char                *bigBufr;
+   int                 s;
+   int                 n;
+   unsigned long       size = 0;
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   bufr = (char *)malloc(BUFSIZ * sizeof(char));
+   if (bufr == NULL)
+   {
+      FTPD_SendReply(440, "Server error - malloc fail.");
+      return(1);
+   }
+
+   bigBufr = (char *)malloc(FTPD_MAX_RECEIVESIZE * sizeof(char));
+   if (bigBufr == NULL)
+   {
+      FTPD_SendReply(440, "Server error - malloc fail.");
+      free(bufr);
+      return(1);
+   }
+
+   FTPD_SendReply(150, "BINARY data connection.");
+
+   s = socket(AF_INET, SOCK_STREAM, 0);
+   if (connect(s, (struct sockaddr *)&info->data_addr,
+               sizeof(struct sockaddr)) < 0)
+   {
+      free(bufr);
+      free(bigBufr);
+      close(s);
+      return(1);
+   }
+
+
+   /***********************************************************************
+    * File: "/dev/null" just throws the data away.
+    **********************************************************************/
+   if (!strncmp("/dev/null", filename, 9))
+   {
+      while ((n = read(s, bufr, BUFSIZ)) > 0);
+   }
+   else
+   {
+      /***********************************************************************
+       * Retrieve the file into our buffer space.
+       **********************************************************************/
+      size = 0;
+      while ((n = read(s, bufr, BUFSIZ)) > 0)
+      {
+         if (size + n > FTPD_MAX_RECEIVESIZE)
+         {
+            FTPD_SendReply(440, "Server error - Buffer size exceeded.");
+            free(bufr);
+            free(bigBufr);
+            close(s);
+            return(1);
+         }
+         memcpy(&bigBufr[size], bufr, n);
+         size += n;
+      }
+   }
+   free(bufr);
+   close(s);
+
+
+   /***********************************************************************
+    * Figure out what to do with the data we just received.
+    **********************************************************************/
+   if (!strncmp("untar", filename, 5))
+   {
+      Untar_FromMemory(bigBufr, size);
+      FTPD_SendReply(210, "Untar successful.");
+   }
+   else
+   {
+      FILE   *fp;
+      size_t len;
+      size_t written;
+
+      fp = fopen(filename, "w");
+      if (fp == NULL)
+      {
+         FTPD_SendReply(440, "Could not open file.");
+         free(bigBufr);
+         return(1);
+      }
+      
+      n = 0;
+      written = 0;
+      while (n<size)
+      {
+         len = ((size-n>BUFSIZ)?(BUFSIZ):(size-n));
+         written = fwrite(&bigBufr[n], 1, len, fp);
+         n += written;
+         if (written != len)
+         {
+            break;
+         }
+      }
+      fclose(fp);
+
+      if (n == size)
+      {
+         FTPD_SendReply(226, "Transfer complete.");
+      }
+      else
+      {
+         FTPD_SendReply(440, "Error during write.");
+         free(bigBufr);
+         return(1);
+      }
+   }
+
+   free(bigBufr);
+   return(0);
+}
+
+
+/**************************************************************************
+ * Function: FTPD_CommandList                                             *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    Sends a file list through a data connection.  The data              *
+ *    connection must have already been opened with the "PORT" command.   *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    char *fname  - File (or directory) to list.                         *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static void
+FTPD_CommandList(char *fname)
+{
+   int                 s;
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+   DIR                 *dirp;
+   struct dirent       *dp;
+   char                dirline[255];
+   struct stat         stat_buf;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   FTPD_SendReply(150, "ASCII data connection for LIST.");
+
+   s = socket(AF_INET, SOCK_STREAM, 0);
+   if (connect(s, (struct sockaddr *)&info->data_addr,
+               sizeof(struct sockaddr)) < 0)
+   {
+      syslog(LOG_ERR, "ftpd: Error connecting to data socket.");
+      return;
+   }
+
+   if ((dirp = opendir(fname)) == NULL)
+   {
+      sprintf(dirline, "%s: No such file or directory.%s\n",
+              fname, (info->xfer_mode==TYPE_A)?("\r"):(""));
+      send(s, dirline, strlen(dirline), 0);
+      close(s);
+      FTPD_SendReply(226, "Transfer complete.");
+      return;
+   }
+   while ((dp = readdir(dirp)) != NULL)
+   {
+      if (stat(dp->d_name, &stat_buf) == 0)
+      {
+         sprintf(dirline, "%c%c%c%c%c%c%c%c%c%c  %5d %5d %11d  %s%s\n",
+                 (S_ISLNK(stat_buf.st_mode)?('l'):
+                    (S_ISDIR(stat_buf.st_mode)?('d'):('-'))),
+                 (stat_buf.st_mode & S_IRUSR)?('r'):('-'),
+                 (stat_buf.st_mode & S_IWUSR)?('w'):('-'),
+                 (stat_buf.st_mode & S_IXUSR)?('x'):('-'),
+                 (stat_buf.st_mode & S_IRGRP)?('r'):('-'),
+                 (stat_buf.st_mode & S_IWGRP)?('w'):('-'),
+                 (stat_buf.st_mode & S_IXGRP)?('x'):('-'),
+                 (stat_buf.st_mode & S_IROTH)?('r'):('-'),
+                 (stat_buf.st_mode & S_IWOTH)?('w'):('-'),
+                 (stat_buf.st_mode & S_IXOTH)?('x'):('-'),
+                 (int)stat_buf.st_uid,
+                 (int)stat_buf.st_gid,
+                 (int)stat_buf.st_size,
+                 dp->d_name,
+                 (info->xfer_mode==TYPE_A)?("\r"):(""));
+         send(s, dirline, strlen(dirline), 0);
+      }
+   }
+   closedir(dirp);
+
+   close(s);
+   FTPD_SendReply(226, "Transfer complete.");
+}
+
+
+/*
+ * Cheesy way to change directories
+ */
+static void
+FTPD_CWD(char *dir)
+{
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   if (chdir(dir) == 0)
+   {
+      FTPD_SendReply(250, "CWD command successful.");
+   }
+   else
+   {
+      FTPD_SendReply(550, "CWD command failed.");
+   }
+}
+
+
+/**************************************************************************
+ * Function: FTPD_CommandPort                                             *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This procedure opens up a data port given the IP address of the     *
+ *    remote machine and the port on the remote machine.  This connection *
+ *    will then be used to transfer data between the hosts.               *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    char *bufr - Arguments to the "PORT" command.                       *
+ *                                                                        *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static void
+FTPD_CommandPort(char *bufr)
+{
+   char                *ip;
+   char                *port;
+   int                 ip0, ip1, ip2, ip3, port0, port1;
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   sscanf(bufr, "%d,%d,%d,%d,%d,%d", &ip0, &ip1, &ip2, &ip3, &port0, &port1);
+   ip = (char *)&(info->data_addr.sin_addr);
+   ip[0] = ip0 & 0xff;
+   ip[1] = ip1 & 0xff;
+   ip[2] = ip2 & 0xff;
+   ip[3] = ip3 & 0xff;
+   port = (char *)&(info->data_addr.sin_port);
+   port[0] = port0 & 0xff;
+   port[1] = port1 & 0xff;
+   info->data_addr.sin_family = AF_INET;
+}
+
+
+/**************************************************************************
+ * Function: FTPD_ParseCommand                                            *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    Here, we parse the commands that have come through the control      *
+ *    connection.                                                         *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    char *bufr     - Pointer to the buffer which contains the command   *
+ *                     text.                                              *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static void
+FTPD_ParseCommand(char *bufr)
+{
+   char fname[255];
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   if (!strncmp("PORT", bufr, 4))
+   {
+      FTPD_SendReply(200, "PORT command successful.");
+      FTPD_CommandPort(&bufr[5]);
+   }
+   else if (!strncmp("RETR", bufr, 4))
+   {
+      sscanf(&bufr[5], "%254s", fname);
+      FTPD_CommandRetrieve(fname);
+   }
+   else if (!strncmp("STOR", bufr, 4))
+   {
+      sscanf(&bufr[5], "%254s", fname);
+      FTPD_CommandStore(fname);
+   }
+   else if (!strncmp("LIST", bufr, 4))
+   {
+      if (bufr[5] == '\n')
+      {
+         FTPD_CommandList(".");
+      }
+      else
+      {
+         sscanf(&bufr[5], "%254s", fname);
+         FTPD_CommandList(fname);
+      }
+   }
+   else if (!strncmp("USER", bufr, 4))
+   {
+      FTPD_SendReply(230, "User logged in.");
+   }
+   else if (!strncmp("SYST", bufr, 4))
+   {
+      FTPD_SendReply(240, "RTEMS");
+   }
+   else if (!strncmp("TYPE", bufr, 4))
+   {
+      if (bufr[5] == 'I')
+      {
+         info->xfer_mode = TYPE_I;
+         FTPD_SendReply(200, "Type set to I.");
+      }
+      else if (bufr[5] == 'A')
+      {
+         info->xfer_mode = TYPE_A;
+         FTPD_SendReply(200, "Type set to A.");
+      } 
+      else
+      {
+         info->xfer_mode = TYPE_I;
+         FTPD_SendReply(504, "Type not implemented.  Set to I.");
+      }
+   }
+   else if (!strncmp("PASS", bufr, 4))
+   {
+      FTPD_SendReply(230, "User logged in.");
+   }
+   else if (!strncmp("DELE", bufr, 4))
+   {
+      sscanf(&bufr[4], "%254s", fname);
+      if (unlink(fname) == 0)
+      {
+         FTPD_SendReply(257, "DELE successful.");
+      }
+      else
+      {
+         FTPD_SendReply(550, "DELE failed.");
+      }
+   }
+   else if (!strncmp("SITE CHMOD", bufr, 10))
+   {
+      int mask;
+
+      sscanf(&bufr[11], "%o %254s", &mask, fname);
+      if (chmod(fname, (mode_t)mask) == 0)
+      {
+         FTPD_SendReply(257, "CHMOD successful.");
+      }
+      else
+      {
+         FTPD_SendReply(550, "CHMOD failed.");
+      }
+   }
+   else if (!strncmp("RMD", bufr, 3))
+   {
+      sscanf(&bufr[4], "%254s", fname);
+      if (rmdir(fname) == 0)
+      {
+         FTPD_SendReply(257, "RMD successful.");
+      }
+      else
+      {
+         FTPD_SendReply(550, "RMD failed.");
+      }
+   }
+   else if (!strncmp("MKD", bufr, 3))
+   {
+      sscanf(&bufr[4], "%254s", fname);
+      if (mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
+      {
+         FTPD_SendReply(257, "MKD successful.");
+      }
+      else
+      {
+         FTPD_SendReply(550, "MKD failed.");
+      }
+   }
+   else if (!strncmp("CWD", bufr, 3))
+   {
+      sscanf(&bufr[4], "%254s", fname);
+      FTPD_CWD(fname);
+   }
+   else if (!strncmp("PWD", bufr, 3))
+   {
+      char *cwd = getcwd(0, 0);
+      sprintf(bufr, "\"%s\" is the current directory.", cwd);
+      FTPD_SendReply(250, bufr);
+      free(cwd);
+   }
+   else
+   {
+      FTPD_SendReply(500, "Unrecognized/unsupported command.");
+   }
+}
+
+/**************************************************************************
+ * Function: FTPD_Session                                                 *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This task is started when the FTP daemon gets a service request     *
+ *    from a remote machine.  Here, we watch for commands that will       *
+ *    come through the "control" connection.  These commands are then     *
+ *    parsed and executed until the connection is closed, either          *
+ *    unintentionally or intentionally with the "QUIT" command.           *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    rtems_task_argument arg - The FTPD_Daemon task passes the socket    *
+ *                              which serves as the control connection.   *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static void
+FTPD_Session(rtems_task_argument arg)
+{
+   char                cmd[256];
+   rtems_status_code   sc;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sc = rtems_task_get_note(RTEMS_SELF, RTEMS_NOTEPAD_0,
+                            (rtems_unsigned32 *)&info);
+
+   FTPD_SendReply(220, "Erithacus FTP server (Version 1.0) ready.");
+
+   /***********************************************************************
+    * Set initial directory to "/".
+    **********************************************************************/
+   strcpy(info->cwd, "/");
+   info->xfer_mode = TYPE_A;
+   while (1)
+   {
+      if (recv(info->ctrl_sock, cmd, 256, 0) == -1)
+      {
+         syslog(LOG_INFO, "ftpd: Connection aborted.");
+         break;
+      }
+
+      if (!strncmp("QUIT", cmd, 4))
+      {
+         FTPD_SendReply(221, "Goodbye.");
+         break;
+      }
+      else
+      {
+         FTPD_ParseCommand(cmd);
+      }
+   }
+
+   if (close(info->ctrl_sock) < 0)
+   {
+      syslog(LOG_ERR, "ftpd: Could not close session.");
+   }
+
+
+   /* Least we can do is put the CWD back to /. */
+   chdir("/");
+
+   /***********************************************************************
+    * Free up the allocated SessionInfo struct and exit.
+    **********************************************************************/
+   free(info);
+   sc = rtems_task_delete(RTEMS_SELF);
+   syslog(LOG_ERR, "ftpd: Task deletion failed: %s",
+          rtems_status_text(sc));
+}
+
+
+/**************************************************************************
+ * Function: FTPD_Daemon                                                  *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    This task runs in the background forever.  It waits for service     *
+ *    requests on the FTP port (port 21).  When a request is received,    *
+ *    it opens a new session to handle those requests until the           *
+ *    connection is closed.                                               *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+static void
+FTPD_Daemon()
+{
+   int                 s;
+   int                 s1;
+   int                 addrLen;
+   struct sockaddr_in  remoteAddr;
+   struct sockaddr_in  localAddr;
+   char                sessionID;
+   rtems_task_priority priority;
+   rtems_status_code   sc;
+   rtems_id            tid;
+   FTPD_SessionInfo_t  *info = NULL;
+
+
+   sessionID = 'a';
+
+   s = socket(AF_INET, SOCK_STREAM, 0);
+   if (s < 0)
+   {
+      perror("Creating socket");
+   }
+
+   localAddr.sin_family      = AF_INET;
+   localAddr.sin_port        = FTPD_CONTROL_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)
+   {
+      perror("Binding control socket");
+   }
+
+   if (listen(s, 2) < 0)
+   {
+      perror("Listening on control socket");
+   }
+
+   while (1)
+   {
+      /********************************************************************
+       * Allocate a SessionInfo structure for the session task.
+       *******************************************************************/
+      info = (FTPD_SessionInfo_t *)malloc(sizeof(FTPD_SessionInfo_t));
+      if (info == NULL)
+      {
+         syslog(LOG_ERR, "ftpd: Could not allocate session info struct.");
+         rtems_panic("Malloc fail.");
+      }
+
+      /********************************************************************
+       * Accept on the socket and start the session task.
+       *******************************************************************/
+      addrLen = sizeof(remoteAddr);
+      s1 = accept(s, (struct sockaddr *)&remoteAddr, &addrLen);
+      if (s1 < 0)
+      {
+         perror("Accepting control connection");
+      }
+
+      rtems_task_set_priority(RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &priority);
+      sc = rtems_task_create(rtems_build_name('F', 'T', 'P', sessionID),
+                             priority, 8*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, "ftpd: Could not create FTPD session: %s", 
+                rtems_status_text(sc));
+      }
+
+      if (sessionID == 'z')
+      {
+         sessionID = 'a';
+      }
+      else
+      {
+         sessionID++;
+      }
+
+      /********************************************************************
+       * Send the socket on to the new session.
+       *******************************************************************/
+      info->ctrl_sock = s1;
+      sc = rtems_task_set_note(tid, RTEMS_NOTEPAD_0,
+                               (rtems_unsigned32)info);
+      sc = rtems_task_start(tid, FTPD_Session, 0);
+      if (sc != RTEMS_SUCCESSFUL)
+      {
+         syslog(LOG_ERR, "ftpd: Could not start FTPD session: %s",
+                rtems_status_text(sc));
+      }
+   }
+}
+
+
+/**************************************************************************
+ * Function: FTPD_Start                                                   *
+ **************************************************************************
+ * Description:                                                           *
+ *                                                                        *
+ *    Here, we start the FTPD task which waits for FTP requests and       *
+ *    services them.  This procedure returns to its caller once the       *
+ *    task is started.                                                    *
+ *                                                                        *
+ *                                                                        *
+ * Inputs:                                                                *
+ *                                                                        *
+ *    rtems_task_priority priority - Priority to assign to this task.     *
+ *                                                                        *
+ * Output:                                                                *
+ *                                                                        *
+ *    none                                                                *
+ *                                                                        *
+ **************************************************************************
+ * Change History:                                                        *
+ *  12/01/97 - Creation (JWJ)                                             *
+ *************************************************************************/
+void
+FTPD_Start(rtems_task_priority priority)
+{
+   rtems_status_code   sc;
+   rtems_id            tid;
+
+
+   sc = rtems_task_create(rtems_build_name('F', 'T', 'P', 'D'),
+                          priority, 8*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, "ftpd: Could not create FTP daemon: %s",
+             rtems_status_text(sc));
+   }
+
+   sc = rtems_task_start(tid, FTPD_Daemon, 0);
+   if (sc != RTEMS_SUCCESSFUL)
+   {
+      syslog(LOG_ERR, "ftpd: Could not start FTP daemon: %s",
+             rtems_status_text(sc));
+   }   
+
+   syslog(LOG_INFO, "ftpd: FTP daemon started.");
+}
Index: cpukit/ftpd/ftpd.h
===================================================================
--- cpukit/ftpd/ftpd.h	(revision 6d0e13c3bd56e1f158bb8691e6ca244b160616d1)
+++ cpukit/ftpd/ftpd.h	(revision 6d0e13c3bd56e1f158bb8691e6ca244b160616d1)
@@ -0,0 +1,26 @@
+/*
+ *  FTP Server Information
+ *
+ *  $Id$
+ */
+
+#ifndef __FTPD_H__
+#define __FTPD_H__
+
+
+#define FTPD_CONTROL_PORT   21
+
+/*
+ * Reply codes.
+ */
+
+#define PRELIM          1       /* positive preliminary */
+#define COMPLETE        2       /* positive completion */
+#define CONTINUE        3       /* positive intermediate */
+#define TRANSIENT       4       /* transient negative completion */
+#define ERROR           5       /* permanent negative completion */
+
+void rtems_ftpd_sTart(rtems_task_priority priority);
+
+#endif  /* __FTPD_H__ */
+
