📄 ftpdlib.c
字号:
* RETURNS: N/A
*
* ERRNO: N/A
*
* INTERNAL:
* The server task is deleted by the server shutdown routine. Adding a newly
* created client session to the list of active clients is performed atomically
* with respect to the shutdown routine. However, accepting control connections
* is not a critical section, since closing the initial socket used in the
* listen() call also closes any later connections which are still open.
*
* NOMANUAL
*/
LOCAL void ftpdTask (void)
{
int newSock;
FAST FTPD_SESSION_DATA *pSlot;
int on = 1;
int addrLen;
struct sockaddr_in addr;
char a_ip_addr [INET_ADDR_LEN]; /* ascii ip address of client */
ftpdNumTasks = 0;
/* The following loop halts if this task is deleted. */
FOREVER
{
/* Wait for a new incoming connection. */
addrLen = sizeof (struct sockaddr);
ftpdDebugMsg ("waiting for a new client connection...\n",0,0,0,0);
newSock = accept (ftpdServerSock, (struct sockaddr *) &addr, &addrLen);
if (newSock < 0)
{
ftpdDebugMsg ("cannot accept a new connection\n",0,0,0,0);
break;
}
/*
* Register a new FTP client session. This process is a critical
* section with the server shutdown routine. If an error occurs,
* the reply must be sent over the control connection to the peer
* before the semaphore is released. Otherwise, this task could
* be deleted and no response would be sent, possibly causing
* the new client to hang indefinitely.
*/
semTake (ftpsMutexSem, WAIT_FOREVER);
setsockopt (newSock, SOL_SOCKET, SO_KEEPALIVE, (char *) &on,
sizeof (on));
inet_ntoa_b (addr.sin_addr, a_ip_addr);
ftpdDebugMsg ("accepted a new client connection from %s\n",
(int) a_ip_addr, 0, 0, 0);
/* Create a new session entry for this connection, if possible. */
pSlot = ftpdSessionAdd ();
if (pSlot == NULL) /* Maximum number of connections reached. */
{
/* Send transient failure report to client. */
ftpdCmdSend (pSlot, newSock, 421,
"Session limit reached, closing control connection",
0, 0, 0, 0, 0, 0);
ftpdDebugMsg ("cannot get a new connection slot\n",0,0,0,0);
close (newSock);
semGive (ftpsMutexSem);
continue;
}
pSlot->cmdSock = newSock;
/* Save the control address and assign the default data address. */
bcopy ( (char *)&addr, (char *)&pSlot->peerAddr,
sizeof (struct sockaddr_in));
bcopy ( (char *)&addr, (char *)&pSlot->dataAddr,
sizeof (struct sockaddr_in));
pSlot->dataAddr.sin_port = htons (FTP_DATA_PORT);
/* Create a task name. */
sprintf (ftpdWorkTaskName, "tFtpdServ%d", ftpdNumTasks);
/* Spawn a secondary task to process FTP requests for this session. */
ftpdDebugMsg ("creating a new server task %s...\n",
(int) ftpdWorkTaskName, 0, 0, 0);
if (taskSpawn (ftpdWorkTaskName, ftpdWorkTaskPriority,
ftpdWorkTaskOptions, ftpdWorkTaskStackSize,
ftpdWorkTask, (int) pSlot,
0, 0, 0, 0, 0, 0, 0, 0, 0) == ERROR)
{
/* Send transient failure report to client. */
ftpdCmdSend (pSlot, newSock, 421,
"Service not available, closing control connection",
0, 0, 0, 0, 0, 0);
ftpdSessionDelete (pSlot);
ftpdDebugMsg ("cannot create a new work task\n",0,0,0,0);
semGive (ftpsMutexSem);
continue;
}
ftpdDebugMsg ("done.\n",0,0,0,0);
/* Session added - end of critical section with shutdown routine. */
semGive (ftpsMutexSem);
}
/* Fatal error - update state of server. */
ftpsActive = FALSE;
return;
}
/*******************************************************************************
*
* ftpdInit - initialize the FTP server task
*
* This routine installs the password verification routine indicated by
* <pLoginRtn> and establishes a control connection for the primary FTP
* server task, which it then creates. It is called automatically during
* system startup if INCLUDE_FTP_SERVER is defined. The primary server task
* supports simultaneous client sessions, up to the limit specified by the
* global variable 'ftpsMaxClients'. The default value allows a maximum of
* four simultaneous connections. The <stackSize> argument specifies the stack
* size for the primary server task. It is set to the value specified in the
* 'ftpdWorkTaskStackSize' global variable by default.
*
* RETURNS:
* OK if server started, or ERROR otherwise.
*
* ERRNO: N/A
*/
STATUS ftpdInit
(
FUNCPTR pLoginRtn, /* user verification routine, or NULL */
int stackSize /* task stack size, or 0 for default */
)
{
int on = 1;
struct sockaddr_in ctrlAddr;
if (ftpsActive)
return (OK);
loginVerifyRtn = pLoginRtn;
ftpsShutdownFlag = FALSE;
ftpsCurrentClients = 0;
/* Create the FTP server control socket. */
ftpdServerSock = socket (AF_INET, SOCK_STREAM, 0);
if (ftpdServerSock < 0)
return (ERROR);
/* Create data structures for managing client connections. */
lstInit (&ftpsSessionList);
ftpsMutexSem = semMCreate (SEM_Q_FIFO | SEM_DELETE_SAFE);
if (ftpsMutexSem == NULL)
{
close (ftpdServerSock);
return (ERROR);
}
ftpsSignalSem = semBCreate (SEM_Q_FIFO, SEM_EMPTY);
if (ftpsSignalSem == NULL)
{
close (ftpdServerSock);
semDelete (ftpsMutexSem);
return (ERROR);
}
/* Setup control connection for client requests. */
ctrlAddr.sin_family = AF_INET;
ctrlAddr.sin_addr.s_addr = INADDR_ANY;
ctrlAddr.sin_port = htons (FTP_DAEMON_PORT);
if (setsockopt (ftpdServerSock, SOL_SOCKET, SO_REUSEADDR,
(char *) &on, sizeof (on)) < 0)
{
close (ftpdServerSock);
semDelete (ftpsMutexSem);
semDelete (ftpsSignalSem);
return (ERROR);
}
if (bind (ftpdServerSock, (struct sockaddr *) &ctrlAddr,
sizeof (ctrlAddr)) < 0)
{
close (ftpdServerSock);
semDelete (ftpsMutexSem);
semDelete (ftpsSignalSem);
return (ERROR);
}
if (listen (ftpdServerSock, 5) < 0)
{
close (ftpdServerSock);
semDelete (ftpsMutexSem);
semDelete (ftpsSignalSem);
return (ERROR);
}
/* Create a FTP server task to receive client requests. */
ftpdTaskId = taskSpawn ("tFtpdTask", ftpdTaskPriority - 1, ftpdTaskOptions,
stackSize == 0 ? ftpdWorkTaskStackSize : stackSize,
(FUNCPTR) ftpdTask, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
if (ftpdTaskId == ERROR)
{
ftpdDebugMsg ("ERROR: ftpdTask cannot be created\n",0,0,0,0);
close (ftpdServerSock);
semDelete (ftpsMutexSem);
semDelete (ftpsSignalSem);
return (ERROR);
}
ftpsActive = TRUE;
ftpdDebugMsg ("ftpdTask created\n",0,0,0,0);
return (OK);
}
/*******************************************************************************
*
* ftpdDelete - terminate the FTP server task
*
* This routine halts the FTP server and closes the control connection. All
* client sessions are removed after completing any commands in progress.
* When this routine executes, no further client connections will be accepted
* until the server is restarted. This routine is not reentrant and must not
* be called from interrupt level.
*
* NOTE: If any file transfer operations are in progress when this routine is
* executed, the transfers will be aborted, possibly leaving incomplete files
* on the destination host.
*
* RETURNS: OK if shutdown completed, or ERROR otherwise.
*
* ERRNO: N/A
*
* INTERNAL
* This routine is synchronized with the deletion routine for a client session
* so that the exit of the client tasks can be detected. It also shares a
* critical section with the creation of client sessions to prevent orphaned
* tasks, which would occur if a session were added after this routine had
* shut down all known clients.
*/
STATUS ftpdDelete (void)
{
BOOL serverActive = FALSE;
FTPD_SESSION_DATA * pData;
if (! ftpsActive) /* Automatic success if server is not running. */
return (OK);
/*
* Remove the FTP server task to prevent additional sessions from starting.
* The exclusion semaphore guarantees a stable list of active clients.
*/
semTake (ftpsMutexSem, WAIT_FOREVER);
taskDelete (ftpdTaskId);
ftpdTaskId = -1;
if (ftpsCurrentClients != 0)
serverActive = TRUE;
/*
* Set the shutdown flag so that any secondary server tasks will exit
* as soon as possible. Once the FTP server has started, this routine is
* the only writer of the flag and the secondary tasks are the only
* readers. To avoid unnecessary blocking, the secondary tasks do not
* guard access to this flag when checking for a pending shutdown during
* normal processing. Those tasks do protect access to this flag during
* their cleanup routine to prevent a race condition which would result
* in incorrect use of the signalling semaphore.
*/
ftpsShutdownFlag = TRUE;
/*
* Close the command sockets of any active sessions to prevent further
* activity. If the session is waiting for a command from a socket,
* the close will trigger the session exit.
*/
pData = (FTPD_SESSION_DATA *)lstFirst (&ftpsSessionList);
while (pData != NULL)
{
ftpdSockFree (&pData->cmdSock);
pData = (FTPD_SESSION_DATA *)lstNext (&pData->node);
}
semGive (ftpsMutexSem);
/* Wait for all secondary tasks to exit. */
if (serverActive)
{
/*
* When a shutdown is in progress, the cleanup routine of the last
* client task to exit gives the signalling semaphore.
*/
semTake (ftpsSignalSem, WAIT_FOREVER);
}
/*
* Remove the original socket - this occurs after all secondary tasks
* have exited to avoid prematurely closing their control sockets.
*/
ftpdSockFree (&ftpdServerSock);
/* Remove the protection and signalling semaphores and list of clients. */
lstFree (&ftpsSessionList); /* Sanity check - should already be empty. */
semDelete (ftpsMutexSem);
semDelete (ftpsSignalSem);
ftpsActive = FALSE;
return (OK);
}
/*******************************************************************************
*
* ftpdSessionAdd - add a new entry to the ftpd session slot list
*
* Each of the incoming FTP sessions is associated with an entry in the
* FTP server's session list which records session-specific context for each
* control connection established by the FTP clients. This routine creates and
* initializes a new entry in the session list, unless the needed memory is not
* available or the upper limit for simultaneous connections is reached.
*
* RETURNS: A pointer to the session list entry, or NULL of none available.
*
* ERRNO: N/A
*
* NOMANUAL
*
* INTERNAL
* This routine executes within a critical section of the primary FTP server
* task, so mutual exclusion is already present when adding entries to the
* client list and updating the shared variables indicating the current number
* of connected clients.
*/
LOCAL FTPD_SESSION_DATA *ftpdSessionAdd (void)
{
FAST FTPD_SESSION_DATA *pSlot;
if (ftpsCurrentClients == ftpsMaxClients)
return (NULL);
/* get memory for the new session entry */
pSlot = (FTPD_SESSION_DATA *) KHEAP_ALLOC(sizeof (FTPD_SESSION_DATA));
if (pSlot == NULL)
{
return (NULL);
}
bzero ((char *)pSlot, sizeof(FTPD_SESSION_DATA));
/* initialize key fields in the newly acquired slot */
pSlot->dataSock = FTPD_SOCK_FREE;
pSlot->cmdSock = FTPD_SOCK_FREE;
pSlot->cmdSockError = OK;
pSlot->status = FTPD_STREAM_MODE | FTPD_ASCII_TYPE | FTPD_NO_RECORD_STRU;
pSlot->byteCount = 0;
/* assign the default directory for this guy */
ioDefPathGet (pSlot->curDirName);
/* Add new entry to the list of active sessions. */
lstAdd (&ftpsSessionList, &pSlot->node);
ftpdNumTasks++;
ftpsCurrentClients++;
return (pSlot);
}
/*******************************************************************************
*
* ftpdSessionDelete - remove an entry from the FTP session list
*
* This routine removes the session-specific context from the session list
* after the client exits or a fatal error occurs.
*
* RETURNS: N/A
*
* ERRNO: N/A
*
* NOMANUAL
*
* INTERNAL
* Unless an error occurs, this routine is only called in response to a
* pending server shutdown, indicated by the shutdown flag. Even if the
* shutdown flag is not detected and this routine is called because of an
* error, the appropriate signal will still be sent to any pending shutdown
* routine. The shutdown routine will only return after any active client
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -