summaryrefslogtreecommitdiffstats
path: root/cpukit/ftpd
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@OARcorp.com>1999-03-16 01:51:53 +0000
committerJoel Sherrill <joel.sherrill@OARcorp.com>1999-03-16 01:51:53 +0000
commit6d0e13c3bd56e1f158bb8691e6ca244b160616d1 (patch)
tree67054831a8020fbb7de15d0375d1b79e01062e63 /cpukit/ftpd
parent504a8c9d16a064fdb744a36b155fe041d3dfdfa0 (diff)
downloadrtems-6d0e13c3bd56e1f158bb8691e6ca244b160616d1.tar.bz2
Added ftpd server from Jake Janovetz <janovetz@tempest.ece.uiuc.edu>.
Diffstat (limited to 'cpukit/ftpd')
-rw-r--r--cpukit/ftpd/ftpd.c1067
-rw-r--r--cpukit/ftpd/ftpd.h26
2 files changed, 1093 insertions, 0 deletions
diff --git a/cpukit/ftpd/ftpd.c b/cpukit/ftpd/ftpd.c
new file mode 100644
index 0000000000..121a5b7762
--- /dev/null
+++ b/cpukit/ftpd/ftpd.c
@@ -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.");
+}
diff --git a/cpukit/ftpd/ftpd.h b/cpukit/ftpd/ftpd.h
new file mode 100644
index 0000000000..56bae5a043
--- /dev/null
+++ b/cpukit/ftpd/ftpd.h
@@ -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__ */
+