⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ftpdlib.c

📁 cpc-1631的BSP包for VxWorks操作系统
💻 C
📖 第 1 页 / 共 5 页
字号:
* 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 + -