source: rtems/cpukit/ftpd/ftpd.c @ 479a28e0

5
Last change on this file since 479a28e0 was 479a28e0, checked in by Sebastian Huber <sebastian.huber@…>, on 10/05/18 at 12:54:49

ftpd: Avoid use of uninitialized memory

Update #3530.

  • Property mode set to 100644
File size: 51.0 KB
RevLine 
[85e24a3]1/* FIXME: 1. Parse command is a hack.  We can do better.
[86933ce0]2 *        2. OSV: hooks support seems to be bad, as it requires storing of
[38371dbe]3 *           entire input file in memory.  Seem to be better to change it to
[3f777d0e]4 *           something more reasonable, like having
5 *           'hook_write(void const *buf, int count)' routine that will be
6 *           called multiple times while file is being received.
[86933ce0]7 *        3. OSV: Remove hack with "/dev/null"?
[85e24a3]8 *
[3f777d0e]9 *  FTP Server Daemon
[6d0e13c]10 *
11 *  Submitted by: Jake Janovetz <janovetz@tempest.ece.uiuc.edu>
12 *
[3f777d0e]13 *  Changed by:   Sergei Organov <osv@javad.ru> (OSV)
[86933ce0]14 *                Arnout Vandecappelle <arnout@mind.be> (AV)
15 *                Sebastien Bourdeauducq <sebastien@milkymist.org> (MM)
16 *               
[38371dbe]17 *
18 *  Changes:
19 *
[ff49ffe]20 *    2010-12-02        Sebastien Bourdeauducq <sebastien@milkymist.org>
21 *
22 *      * Support spaces in filenames
23 *
[86933ce0]24 *    2010-04-29        Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
25 *
26 *      * Added USER/PASS authentication.
27 *
[07fbfced]28 *    2001-01-31        Sergei Organov <osv@javad.ru>
29 *
30 *      * Hacks with current dir and root dir removed in favor of new libio
31 *        support for task-local current and root directories.
32 *
33 *    2001-01-30        Sergei Organov <osv@javad.ru>
34 *
35 *      * Bug in `close_data_socket()' introduced by previous change fixed.
36 *      * `command_pasv()' changed to set timeout on socket we are listening on
37 *        and code fixed to don't close socket twice on error.
38 *      * `serr()' changed to clear `errno'.
39 *      * `data_socket()' changed to clear `errno' before `bind()'.
40 *      * `session()' changed to clear `errno' before processing session.
41 *
42 *    2001-01-29        Sergei Organov <osv@javad.ru>
43 *
44 *      * `close_data_socket()' fixed to close both active and passive sockets
45 *      * Initialize info->data_socket to -1 in `daemon()'
46 *      * Initialize `fname' to empty string  in `exec_command()'
47 *
[38371dbe]48 *    2001-01-22        Sergei Organov <osv@javad.ru>
49 *
50 *      * Timeouts on sockets implemented. 'idle' field added to
51 *        configuration. No timeout by default to keep backward compatibility.
52 *        Note: SITE IDLE command not implemented yet.
53 *      * Basic global access control implemented. 'access' field added to
54 *        configuration. No access limitations by default to keep backward
55 *        compatibility.
56 *
57 *    2001-01-17        Sergei Organov <osv@javad.ru>
58 *
59 *      * Anchor data socket for active mode (using self IP and port 20.)
60 *      * Fixed default data port support (still not tested).
61 *      * Don't allow IP address different from originating host in
62 *        PORT command to improve security.
63 *      * Fixed bug in MDTM command.
64 *      * Check for correctness of parsing of argument in command_port().
65 *      * Fixed squeeze_path() to don't allow names like 'NAME/smth' where
66 *        'NAME' is not a directory.
67 *      * Command parsing a little bit improved: command names are now
68 *        converted to upper-case to be more compatible with RFC (command
69 *        names are not case-sensitive.)
70 *      * Reformat comments so that they have RTEMS look-and-feel.
71 *
72 *    2001-01-16        Sergei Organov <osv@javad.ru>
73 *
74 *      * Fixed DELE, SITE CHMOD, RMD, MKD broken by previous changes
75 *      * True ASCII mode implemented (doesn't work for hooks and /dev/null)
76 *      * Passive mode implemented, PASV command added.
77 *      * Default port for data connection could be used (untested, can't find
78 *        ftp client that doesn't send PORT command)
79 *      * SYST reply changed to UNIX, as former RTEMS isn't registered name.
80 *      * Reply codes reviewed and fixed.
81 *
82 *    2001-01-08        Sergei Organov <osv@javad.ru>
83 *
84 *      * use pool of pre-created threads to handle sessions
85 *      * LIST output now similar to what "/bin/ls -al" would output, thus
[3f777d0e]86 *        FTP clients could parse it.
[38371dbe]87 *      * LIST NAME now works (both for files and directories)
88 *      * keep track of CWD for every session separately
89 *      * ability to specify root directory name in configuration table
90 *      * options sent in commands are ignored, thus LIST -al FILE works
91 *      * added support for NLST, CDUP and MDTM commands
92 *      * buffers are allocated on stack instead of heap where possible
93 *      * drop using of task notepad to pass parameters - use function
[3f777d0e]94 *        arguments instead
[38371dbe]95 *      * various bug-fixes, e.g., use of PF_INET in socket() instead of
[3f777d0e]96 *        AF_INET, use snprintf() instead of sprintf() everywhere for safety,
97 *        etc.
[6d0e13c]98 */
99
[38371dbe]100/*************************************************************************
101 *                                 ftpd.c
102 *************************************************************************
103 * Description:
104 *
105 *    This file contains the daemon which services requests that appear
106 *    on the FTP port.  This server is compatible with FTP, but it
107 *    also provides 'hooks' to make it usable in situations where files
108 *    are not used/necessary.  Once the server is started, it runs
109 *    forever.
110 *
111 *
112 *    Organization:
113 *
114 *       The FTP daemon is started upon boot along with a (configurable)
115 *       number of tasks to handle sessions.  It runs all the time and
116 *       waits for connections on the known FTP port (21).  When
117 *       a connection is made, it wakes-up a 'session' task.  That
118 *       session then interacts with the remote host.  When the session
119 *       is complete, the session task goes to sleep.  The daemon still
120 *       runs, however.
121 *
122 *
123 * Supported commands are:
124 *
125 * RETR xxx     - Sends a file from the client.
126 * STOR xxx     - Receives a file from the client.  xxx = filename.
127 * LIST xxx     - Sends a file list to the client.
128 * NLST xxx     - Sends a file list to the client.
129 * USER         - Does nothing.
130 * PASS         - Does nothing.
131 * SYST         - Replies with the system type (`RTEMS').
132 * DELE xxx     - Delete file xxx.
133 * MKD xxx      - Create directory xxx.
134 * RMD xxx      - Remove directory xxx.
135 * PWD          - Print working directory.
136 * CWD xxx      - Change working directory.
137 * CDUP         - Change to upper directory.
138 * SITE CHMOD xxx yyy - Change permissions on file yyy to xxx.
139 * PORT a,b,c,d,x,y   - Setup for a data port to IP address a.b.c.d
140 *                      and port (x*256 + y).
141 * MDTM xxx     - Send file modification date/time to the client.
142 *                xxx = filename.
143 * PASV         - Use passive mode data connection.
144 *
145 *
146 * The public routines contained in this file are:
147 *
148 *    rtems_initialize_ftpd - Initializes and starts the server daemon,
149 *                            then returns to its caller.
150 *
151 *------------------------------------------------------------------------
152 * Jake Janovetz
153 * University of Illinois
154 * 1406 West Green Street
155 * Urbana IL  61801
156 *************************************************************************
157 * Change History:
158 *  12/01/97   - Creation (JWJ)
159 *  2001-01-08 - Changes by OSV
[86933ce0]160 *  2010-04-29 - Authentication added by AV
[6d0e13c]161 *************************************************************************/
162
[38371dbe]163/*************************************************************************
[6d0e13c]164 * Meanings of first and second digits of reply codes:
165 *
166 * Reply:  Description:
167 *-------- --------------
168 *  1yz    Positive preliminary reply.  The action is being started but
169 *         expect another reply before sending another command.
170 *  2yz    Positive completion reply.  A new command can be sent.
[38371dbe]171 *  3yz    Positive intermediate reply.  The command has been accepted
[6d0e13c]172 *         but another command must be sent.
173 *  4yz    Transient negative completion reply.  The requested action did
174 *         not take place, but the error condition is temporary so the
175 *         command can be reissued later.
[3f777d0e]176 *  5yz    Permanent negative completion reply.  The command was not
[6d0e13c]177 *         accepted and should not be retried.
178 *-------------------------------------------------------------------------
179 *  x0z    Syntax errors.
180 *  x1z    Information.
181 *  x2z    Connections.  Replies referring to the control or data
182 *         connections.
183 *  x3z    Authentication and accounting.  Replies for the login or
184 *         accounting commands.
185 *  x4z    Unspecified.
186 *  x5z    Filesystem status.
187 *************************************************************************/
188
[2add452]189#if HAVE_CONFIG_H
190#include "config.h"
191#endif
192
[3f777d0e]193#include <stdio.h>
194#include <stdlib.h>
195#include <string.h>
196#include <unistd.h>
197#include <fcntl.h>
198#include <dirent.h>
199#include <errno.h>
[38371dbe]200#include <ctype.h>
[55a36b7]201#include <inttypes.h>
[4fed5ac]202#include <sched.h>
[6d0e13c]203
[3f777d0e]204#include <rtems.h>
205#include <rtems/error.h>
[51a30a4]206#include <rtems/ftpd.h>
[07fbfced]207#include <rtems/libio.h>
[0a593c2d]208#include <rtems/thread.h>
[19ed3cf]209#include <rtems/userenv.h>
[3f777d0e]210#include <syslog.h>
211
212#include <sys/types.h>
213#include <sys/socket.h>
214#include <arpa/ftp.h>
215#include <netinet/in.h>
216
217
218#ifdef __GNUC__
219/* change to #if 1 to disable syslog entirely */
220#if 0
221#undef  syslog
222#define syslog(a, b, ...) while(0){}
223#endif
224#endif
225
226#define FTPD_SERVER_MESSAGE  "RTEMS FTP server (Version 1.1-JWJ) ready."
227
[38371dbe]228#define FTPD_SYSTYPE "UNIX Type: L8"
[3f777d0e]229
230/* Seem to be unused */
231#if 0
232#define FTPD_WELCOME_MESSAGE \
233   "Welcome to the RTEMS FTP server.\n" \
234   "\n" \
235   "Login accepted.\n"
236#endif
237
238/* Event to be used by session tasks for waiting */
239enum
240{
241  FTPD_RTEMS_EVENT = RTEMS_EVENT_1
242};
243
244/* Configuration table */
[b771cb4]245static struct rtems_ftpd_configuration *ftpd_config;
[3f777d0e]246
247/* this is not prototyped in strict ansi mode */
248FILE *fdopen (int fildes, const char *mode);
249
[64adc13]250/*SessionInfo structure.
[6d0e13c]251 *
[3f777d0e]252 * The following structure is allocated for each session.
253 */
[6d0e13c]254typedef struct
255{
[38371dbe]256  struct sockaddr_in  ctrl_addr;   /* Control connection self address */
257  struct sockaddr_in  data_addr;   /* Data address set by PORT command */
258  struct sockaddr_in  def_addr;    /* Default address for data */
259  int                 use_default; /* 1 - use default address for data */
[3f777d0e]260  FILE                *ctrl_fp;    /* File pointer for control connection */
[38371dbe]261  int                 ctrl_socket; /* Socket for ctrl connection */
262  int                 pasv_socket; /* Socket for PASV connection */
263  int                 data_socket; /* Socket for data connection */
264  int                 idle;        /* Timeout in seconds */
[3f777d0e]265  int                 xfer_mode;   /* Transfer mode (ASCII/binary) */
266  rtems_id            tid;         /* Task id */
[86933ce0]267  char                *user;       /* user name (0 if not supplied) */
[51da629]268  char                user_buf[256]; /* user name buffer */
[86933ce0]269  bool                auth;        /* true if user/pass was valid, false if not or not supplied */
[6d0e13c]270} FTPD_SessionInfo_t;
271
272
[3f777d0e]273/*
274 * TaskPool structure.
275 */
276typedef struct
277{
[0a593c2d]278  FTPD_SessionInfo_t       *info;
279  FTPD_SessionInfo_t       **queue;
280  int                      count;
281  int                      head;
282  int                      tail;
283  rtems_mutex              mutex;
284  rtems_counting_semaphore sem;
[3f777d0e]285} FTPD_TaskPool_t;
286
287/*
288 * Task pool instance.
289 */
290static FTPD_TaskPool_t task_pool;
291
292/*
[07fbfced]293 * Root directory
[3f777d0e]294 */
[07fbfced]295
296static char const* ftpd_root = "/";
[38371dbe]297
298/*
299 * Default idle timeout for sockets in seconds.
300 */
301static int ftpd_timeout = 0;
[3f777d0e]302
[38371dbe]303/*
304 * Global access flags.
305 */
306static int ftpd_access = 0;
[3f777d0e]307
[4fed5ac]308static void
309yield(void)
310{
311/*
312 * If we build not for the legacy network stack, then we use the libbsd.  In
313 * the libbsd there is no global network stack semaphore which provides round
314 * robin fairness for threads of equal priority.
315 */
316#ifndef RTEMS_NETWORKING
[eaa1709]317  sched_yield();
[4fed5ac]318#endif
319}
320
[64adc13]321/*
[3f777d0e]322 * serr
323 *
[38371dbe]324 * Return error string corresponding to current 'errno'.
[3f777d0e]325 *
326 */
327static char const*
328serr(void)
329{
[07fbfced]330  int err = errno;
331  errno = 0;
332  return strerror(err);
[3f777d0e]333}
334
[64adc13]335/*
[38371dbe]336 * Utility routines for access control.
337 *
338 */
[3f777d0e]339
[38371dbe]340static int
341can_read(void)
342{
343  return (ftpd_access & FTPD_NO_READ) == 0;
344}
345
346static int
347can_write(void)
348{
349  return (ftpd_access & FTPD_NO_WRITE) == 0;
350}
351
[64adc13]352/*
[3f777d0e]353 * Task pool management routines
[38371dbe]354 *
[3f777d0e]355 */
356
357
[64adc13]358/*
[3f777d0e]359 * task_pool_done
360 *
361 * Cleanup task pool.
362 *
363 * Input parameters:
364 *   count - number of entries in task pool to cleanup
365 *
366 * Output parameters:
367 *   NONE
368 *
369 */
370static void
371task_pool_done(int count)
372{
373  int i;
374  for(i = 0; i < count; ++i)
375    rtems_task_delete(task_pool.info[i].tid);
[e761fb4]376  free(task_pool.info);
377  free(task_pool.queue);
[0a593c2d]378  rtems_mutex_destroy(&task_pool.mutex);
379  rtems_counting_semaphore_destroy(&task_pool.sem);
[3f777d0e]380  task_pool.info = 0;
381  task_pool.queue = 0;
382  task_pool.count = 0;
383}
384
[64adc13]385/*
[3f777d0e]386 * task_pool_init
387 *
388 * Initialize task pool.
389 *
390 * Input parameters:
391 *   count    - number of entries in task pool to create
392 *   priority - priority tasks are started with
393 *
394 * Output parameters:
395 *   returns 1 on success, 0 on failure.
396 *
397 */
[38371dbe]398static void session(rtems_task_argument arg); /* Forward declare */
399
[3f777d0e]400static int
401task_pool_init(int count, rtems_task_priority priority)
402{
403  int i;
404  rtems_status_code sc;
405  char id = 'a';
406
407  task_pool.count = 0;
408  task_pool.head = task_pool.tail = 0;
[0a593c2d]409
410  rtems_mutex_init(&task_pool.mutex, "FTPD");
411  rtems_counting_semaphore_init(&task_pool.sem, "FTPD", (unsigned int) count);
[3f777d0e]412
413  task_pool.info = (FTPD_SessionInfo_t*)
414    malloc(sizeof(FTPD_SessionInfo_t) * count);
415  task_pool.queue = (FTPD_SessionInfo_t**)
416    malloc(sizeof(FTPD_SessionInfo_t*) * count);
417  if (NULL == task_pool.info || NULL == task_pool.queue)
418  {
419    task_pool_done(0);
420    syslog(LOG_ERR, "ftpd: Not enough memory");
421    return 0;
422  }
423
424  for(i = 0; i < count; ++i)
425  {
426    FTPD_SessionInfo_t *info = &task_pool.info[i];
427    sc = rtems_task_create(rtems_build_name('F', 'T', 'P', id),
428      priority, FTPD_STACKSIZE,
429      RTEMS_PREEMPT | RTEMS_NO_TIMESLICE |
430      RTEMS_NO_ASR | RTEMS_INTERRUPT_LEVEL(0),
[08586e5]431      RTEMS_FLOATING_POINT | RTEMS_LOCAL,
[3f777d0e]432      &info->tid);
433    if (sc == RTEMS_SUCCESSFUL)
434    {
435      sc = rtems_task_start(
436        info->tid, session, (rtems_task_argument)info);
437      if (sc != RTEMS_SUCCESSFUL)
438        task_pool_done(i);
439    }
440    else
441      task_pool_done(i + 1);
442    if (sc != RTEMS_SUCCESSFUL)
443    {
444      syslog(LOG_ERR, "ftpd: Could not create/start FTPD session: %s",
445        rtems_status_text(sc));
446      return 0;
447    }
448    task_pool.queue[i] = task_pool.info + i;
449    if (++id > 'z')
450      id = 'a';
451  }
452  task_pool.count = count;
453  return 1;
454}
455
[64adc13]456/*
[3f777d0e]457 * task_pool_obtain
458 *
459 * Obtain free task from task pool.
460 *
461 * Input parameters:
462 *   NONE
463 *
464 * Output parameters:
465 *   returns pointer to the corresponding SessionInfo structure on success,
466 *           NULL if there are no free tasks in the pool.
467 *
468 */
469static FTPD_SessionInfo_t*
[5bc8a75]470task_pool_obtain(void)
[3f777d0e]471{
472  FTPD_SessionInfo_t* info = 0;
[0a593c2d]473  rtems_counting_semaphore_wait(&task_pool.sem);
474  rtems_mutex_lock(&task_pool.mutex);
475  info = task_pool.queue[task_pool.head];
476  if(++task_pool.head >= task_pool.count)
477    task_pool.head = 0;
478  rtems_mutex_unlock(&task_pool.mutex);
[3f777d0e]479  return info;
480}
481
[64adc13]482/*
[3f777d0e]483 * task_pool_release
484 *
485 * Return task obtained by 'obtain()' back to the task pool.
486 *
487 * Input parameters:
488 *   info  - pointer to corresponding SessionInfo structure.
489 *
490 * Output parameters:
491 *   NONE
492 *
493 */
494static void
495task_pool_release(FTPD_SessionInfo_t* info)
496{
[0a593c2d]497  rtems_mutex_lock(&task_pool.mutex);
[3f777d0e]498  task_pool.queue[task_pool.tail] = info;
499  if(++task_pool.tail >= task_pool.count)
500    task_pool.tail = 0;
[0a593c2d]501  rtems_mutex_unlock(&task_pool.mutex);
502  rtems_counting_semaphore_post(&task_pool.sem);
[3f777d0e]503}
504
505/*
506 * End of task pool routines
507 */
508
[64adc13]509/*
[38371dbe]510 * Function: send_reply
511 *
512 *
513 *    This procedure sends a reply to the client via the control
514 *    connection.
515 *
516 *
517 * Input parameters:
518 *   code  - 3-digit reply code.
519 *   text  - Reply text.
520 *
521 * Output parameters:
522 *   NONE
523 */
524static void
525send_reply(FTPD_SessionInfo_t  *info, int code, char *text)
526{
[1343dfa3]527  text = text != NULL ? text : "";
528  fprintf(info->ctrl_fp, "%d %.70s\r\n", code, text);
[38371dbe]529  fflush(info->ctrl_fp);
530}
531
532
[64adc13]533/*
[38371dbe]534 * close_socket
535 *
536 * Close socket.
537 *
538 * Input parameters:
539 *   s - socket descriptor.
540 *   seconds - number of seconds the timeout should be,
541 *             if >= 0 - infinite timeout (no timeout).
542 *
543 * Output parameters:
544 *   returns 1 on success, 0 on failure.
545 */
546static int
547set_socket_timeout(int s, int seconds)
548{
549  int res = 0;
550  struct timeval tv;
551  int len = sizeof(tv);
552
553  if(seconds < 0)
554    seconds = 0;
555  tv.tv_usec = 0;
556  tv.tv_sec  = seconds;
557  if(0 != setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, len))
558    syslog(LOG_ERR, "ftpd: Can't set send timeout on socket: %s.", serr());
559  else if(0 != setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, len))
560    syslog(LOG_ERR, "ftpd: Can't set receive timeout on socket: %s.", serr());
561  else
562    res = 1;
563  return res;
564}
565
[64adc13]566/*
[3f777d0e]567 * close_socket
568 *
569 * Close socket.
570 *
571 * Input parameters:
572 *   s - socket descriptor to be closed.
573 *
574 * Output parameters:
575 *   returns 1 on success, 0 on failure
576 */
577static int
578close_socket(int s)
579{
580  if (0 <= s)
581  {
582    if (0 != close(s))
583    {
584      shutdown(s, 2);
585      if (0 != close(s))
586        return 0;
587    }
588  }
589  return 1;
590}
591
[64adc13]592/*
[38371dbe]593 * data_socket
594 *
595 * Create data socket for session.
596 *
597 * Input parameters:
598 *   info - corresponding SessionInfo structure
599 *
600 * Output parameters:
601 *   returns socket descriptor, or -1 if failure
602 *
603 */
604static int
605data_socket(FTPD_SessionInfo_t *info)
606{
607  int s = info->pasv_socket;
608  if(0 > s)
609  {
610    int on = 1;
611    s = socket(PF_INET, SOCK_STREAM, 0);
612    if(0 > s)
613      send_reply(info, 425, "Can't create data socket.");
614    else if(0 > setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
615    {
616      close_socket(s);
617      s = -1;
618    }
619    else
620    {
621      struct sockaddr_in data_source;
622      int tries;
623
624      /* anchor socket to avoid multi-homing problems */
625      data_source = info->ctrl_addr;
626      data_source.sin_port = htons(20); /* ftp-data port */
627      for(tries = 1; tries < 10; ++tries)
628      {
[07fbfced]629        errno = 0;
[38371dbe]630        if(bind(s, (struct sockaddr *)&data_source, sizeof(data_source)) >= 0)
631          break;
632        if (errno != EADDRINUSE)
633          tries = 10;
634        else
635          rtems_task_wake_after(tries * 10);
636      }
637      if(tries >= 10)
638      {
639        send_reply(info, 425, "Can't bind data socket.");
640        close_socket(s);
641        s = -1;
642      }
643      else
644      {
645        struct sockaddr_in *data_dest =
646          (info->use_default) ? &info->def_addr : &info->data_addr;
647        if(0 > connect(s, (struct sockaddr *)data_dest, sizeof(*data_dest)))
648        {
649          send_reply(info, 425, "Can't connect data socket.");
650          close_socket(s);
651          s = -1;
652        }
653      }
654    }
655  }
656  info->data_socket = s;
657  info->use_default = 1;
658  if(s >= 0)
659    set_socket_timeout(s, info->idle);
660  return s;
661}
662
[64adc13]663/*
[38371dbe]664 * close_data_socket
665 *
666 * Close data socket for session.
667 *
668 * Input parameters:
669 *   info - corresponding SessionInfo structure
670 *
671 * Output parameters:
672 *   NONE
673 *
674 */
675static void
676close_data_socket(FTPD_SessionInfo_t *info)
677{
[07fbfced]678  /* As at most one data socket could be open simultaneously and in some cases
679     data_socket == pasv_socket, we select socket to close, then close it. */
680  int s = info->data_socket;
[38371dbe]681  if(0 > s)
[07fbfced]682    s = info->pasv_socket;
[38371dbe]683  if(!close_socket(s))
684    syslog(LOG_ERR, "ftpd: Error closing data socket.");
685  info->data_socket = -1;
686  info->pasv_socket = -1;
687  info->use_default = 1;
688}
689
[64adc13]690/*
[3f777d0e]691 * close_stream
692 *
[38371dbe]693 * Close control stream of session.
[3f777d0e]694 *
695 * Input parameters:
696 *   info - corresponding SessionInfo structure
697 *
698 * Output parameters:
699 *   NONE
700 *
701 */
702static void
703close_stream(FTPD_SessionInfo_t* info)
704{
705  if (NULL != info->ctrl_fp)
706  {
707    if (0 != fclose(info->ctrl_fp))
708    {
709      syslog(LOG_ERR, "ftpd: Could not close control stream: %s", serr());
710    }
711    else
[38371dbe]712      info->ctrl_socket = -1;
[3f777d0e]713  }
714
[38371dbe]715  if (!close_socket(info->ctrl_socket))
[3f777d0e]716    syslog(LOG_ERR, "ftpd: Could not close control socket: %s", serr());
717
718  info->ctrl_fp = NULL;
[38371dbe]719  info->ctrl_socket = -1;
[3f777d0e]720}
[6d0e13c]721
722
[64adc13]723/*
[3f777d0e]724 * send_mode_reply
725 *
726 * Sends BINARY/ASCII reply string depending on current transfer mode.
727 *
728 * Input parameters:
729 *   info - corresponding SessionInfo structure
730 *
731 * Output parameters:
732 *   NONE
733 *
734 */
735static void
736send_mode_reply(FTPD_SessionInfo_t *info)
737{
738  if(info->xfer_mode == TYPE_I)
739    send_reply(info, 150, "Opening BINARY mode data connection.");
740  else
741    send_reply(info, 150, "Opening ASCII mode data connection.");
[6d0e13c]742}
743
[64adc13]744/*
[38371dbe]745 * command_retrieve
[3f777d0e]746 *
[38371dbe]747 * Perform the "RETR" command (send file to client).
[3f777d0e]748 *
749 * Input parameters:
750 *   info - corresponding SessionInfo structure
[38371dbe]751 *   char *filename  - source filename.
[3f777d0e]752 *
753 * Output parameters:
[38371dbe]754 *   NONE
[3f777d0e]755 *
756 */
[38371dbe]757static void
758command_retrieve(FTPD_SessionInfo_t  *info, char const *filename)
[6d0e13c]759{
[3f777d0e]760  int                 s = -1;
761  int                 fd = -1;
762  char                buf[FTPD_DATASIZE];
[e7fd0524]763  struct stat         stat_buf;
[3f777d0e]764  int                 res = 0;
[38371dbe]765
[86933ce0]766  if(!can_read() || !info->auth)
[38371dbe]767  {
768    send_reply(info, 550, "Access denied.");
769    return;
770  }
[3f777d0e]771
[07fbfced]772  if (0 > (fd = open(filename, O_RDONLY)))
[3f777d0e]773  {
[38371dbe]774    send_reply(info, 550, "Error opening file.");
775    return;
[3f777d0e]776  }
777
[e7fd0524]778  if (fstat(fd, &stat_buf) == 0 && S_ISDIR(stat_buf.st_mode))
779  {
780    if (-1 != fd)
781      close(fd);
782    send_reply(info, 550, "Is a directory.");
783    return;
784  }
785
[3f777d0e]786  send_mode_reply(info);
787
788  s = data_socket(info);
[38371dbe]789
[3f777d0e]790  if (0 <= s)
791  {
[38371dbe]792    int n = -1;
793
794    if(info->xfer_mode == TYPE_I)
795    {
796      while ((n = read(fd, buf, FTPD_DATASIZE)) > 0)
797      {
798        if(send(s, buf, n, 0) != n)
799          break;
[4fed5ac]800        yield();
[38371dbe]801      }
802    }
803    else if (info->xfer_mode == TYPE_A)
[3f777d0e]804    {
[38371dbe]805      int rest = 0;
806      while (rest == 0 && (n = read(fd, buf, FTPD_DATASIZE)) > 0)
807      {
808        char const* e = buf;
809        char const* b;
810        int i;
811        rest = n;
812        do
813        {
814          char lf = '\0';
815
816          b = e;
817          for(i = 0; i < rest; ++i, ++e)
818          {
819            if(*e == '\n')
820            {
821              lf = '\n';
822              break;
823            }
824          }
825          if(send(s, b, i, 0) != i)
826            break;
827          if(lf == '\n')
828          {
829            if(send(s, "\r\n", 2, 0) != 2)
830              break;
831            ++e;
832            ++i;
833          }
834        }
835        while((rest -= i) > 0);
[4fed5ac]836        yield();
[38371dbe]837      }
[3f777d0e]838    }
839
840    if (0 == n)
841    {
842      if (0 == close(fd))
843      {
844        fd = -1;
845        res = 1;
846      }
847    }
848  }
[6d0e13c]849
[3f777d0e]850  if (-1 != fd)
851    close(fd);
[6d0e13c]852
[38371dbe]853  if (0 == res)
854    send_reply(info, 451, "File read error.");
855  else
856    send_reply(info, 226, "Transfer complete.");
857
858  close_data_socket(info);
[6d0e13c]859
[38371dbe]860  return;
[6d0e13c]861}
862
863
[64adc13]864/*
[07fbfced]865 * discard
866 *
867 * Analog of `write' routine that just discards passed data
868 *
869 * Input parameters:
870 *   fd    - file descriptor (ignored)
871 *   buf   - data to write (ignored)
872 *   count - number of bytes in `buf'
873 *
874 * Output parameters:
875 *   returns `count'
876 *
877 */
878static ssize_t
879discard(int fd, void const* buf, size_t count)
880{
881  (void)fd;
882  (void)buf;
883  return count;
884}
885
[64adc13]886/*
[38371dbe]887 * command_store
888 *
889 * Performs the "STOR" command (receive data from client).
890 *
891 * Input parameters:
892 *   info - corresponding SessionInfo structure
893 *   char *filename   - Destination filename.
894 *
895 * Output parameters:
896 *   NONE
897 */
898static void
899command_store(FTPD_SessionInfo_t *info, char const *filename)
[6d0e13c]900{
[3f777d0e]901  int                    s;
902  int                    n;
903  unsigned long          size = 0;
904  struct rtems_ftpd_hook *usehook = NULL;
905  char                   buf[FTPD_DATASIZE];
[38371dbe]906  int                    res = 1;
907  int                    bare_lfs = 0;
[07fbfced]908  int                    null = 0;
909  typedef ssize_t (*WriteProc)(int, void const*, size_t);
910  WriteProc              wrt = &write;
[3f777d0e]911
[86933ce0]912  if(!can_write() || !info->auth)
[38371dbe]913  {
914    send_reply(info, 550, "Access denied.");
915    return;
916  }
917
[3f777d0e]918  send_mode_reply(info);
919
920  s = data_socket(info);
921  if(0 > s)
[38371dbe]922    return;
[3f777d0e]923
[07fbfced]924  null = !strcmp("/dev/null", filename);
[3f777d0e]925  if (null)
926  {
[38371dbe]927    /* File "/dev/null" just throws data away.
[07fbfced]928     *  FIXME: this is hack.  Using `/dev/null' filesystem entry would be
929     *  better.
[38371dbe]930     */
[07fbfced]931    wrt = &discard;
[3f777d0e]932  }
[07fbfced]933
[b771cb4]934  if (!null && ftpd_config->hooks != NULL)
[3f777d0e]935  {
[38371dbe]936
937    /* Search our list of hooks to see if we need to do something special. */
[3f777d0e]938    struct rtems_ftpd_hook *hook;
939    int i;
940
941    i = 0;
[b771cb4]942    hook = &ftpd_config->hooks[i++];
[3f777d0e]943    while (hook->filename != NULL)
944    {
[a8c33268]945      if (!strcmp(hook->filename, filename))
[85e24a3]946      {
[3f777d0e]947        usehook = hook;
948        break;
[85e24a3]949      }
[b771cb4]950      hook = &ftpd_config->hooks[i++];
[3f777d0e]951    }
952  }
953
954  if (usehook != NULL)
955  {
956    /*
957     * OSV: FIXME: Small buffer could be used and hook routine
[38371dbe]958     * called multiple times instead.  Alternatively, the support could be
[3f777d0e]959     * removed entirely in favor of configuring RTEMS pseudo-device with
960     * given name.
961     */
962
[b771cb4]963    char   *bigBufr;
964    size_t  filesize = ftpd_config->max_hook_filesize + 1;
[3f777d0e]965
[38371dbe]966    /*
[3f777d0e]967     * Allocate space for our "file".
[38371dbe]968     */
[3f777d0e]969    bigBufr = (char *)malloc(filesize);
970    if (bigBufr == NULL)
971    {
[38371dbe]972      send_reply(info, 451, "Local resource failure: malloc.");
973      close_data_socket(info);
974      return;
[3f777d0e]975    }
976
[38371dbe]977    /*
[3f777d0e]978     * Retrieve the file into our buffer space.
[38371dbe]979     */
[3f777d0e]980    size = 0;
[38371dbe]981    while ((n = recv(s, bigBufr + size, filesize - size, 0)) > 0)
[3f777d0e]982    {
983      size += n;
984    }
985    if (size >= filesize)
986    {
[38371dbe]987      send_reply(info, 451, "File too long: buffer size exceeded.");
[3f777d0e]988      free(bigBufr);
[38371dbe]989      close_data_socket(info);
990      return;
[3f777d0e]991    }
992
[38371dbe]993    /*
[3f777d0e]994     * Call our hook.
[38371dbe]995     */
996    res = (usehook->hook_function)(bigBufr, size) == 0;
997    free(bigBufr);
998    if(!res)
[3f777d0e]999    {
[38371dbe]1000      send_reply(info, 451, "File processing failed.");
1001      close_data_socket(info);
1002      return;
[3f777d0e]1003    }
1004  }
1005  else
1006  {
[07fbfced]1007    /* Data transfer to regular file or /dev/null. */
1008    int fd = 0;
1009
1010    if(!null)
1011      fd = creat(filename,
1012        S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
[3f777d0e]1013
1014    if (0 > fd)
1015    {
[38371dbe]1016      send_reply(info, 550, "Error creating file.");
1017      close_data_socket(info);
1018      return;
[3f777d0e]1019    }
[85e24a3]1020
[38371dbe]1021    if(info->xfer_mode == TYPE_I)
[3f777d0e]1022    {
[38371dbe]1023      while ((n = recv(s, buf, FTPD_DATASIZE, 0)) > 0)
[6d0e13c]1024      {
[07fbfced]1025        if (wrt(fd, buf, n) != n)
[38371dbe]1026        {
1027          res = 0;
1028          break;
1029        }
[4fed5ac]1030        yield();
[6d0e13c]1031      }
[3f777d0e]1032    }
[38371dbe]1033    else if(info->xfer_mode == TYPE_A)
[3f777d0e]1034    {
[38371dbe]1035      int rest = 0;
1036      int pended_cr = 0;
1037      while (res && rest == 0 && (n = recv(s, buf, FTPD_DATASIZE, 0)) > 0)
1038      {
1039        char const* e = buf;
1040        char const* b;
1041        int i;
1042
1043        rest = n;
1044        if(pended_cr && *e != '\n')
1045        {
1046          char const lf = '\r';
1047          pended_cr = 0;
[07fbfced]1048          if(wrt(fd, &lf, 1) != 1)
[38371dbe]1049          {
1050            res = 0;
1051            break;
1052          }
1053        }
1054        do
1055        {
1056          int count;
1057          int sub = 0;
1058
1059          b = e;
1060          for(i = 0; i < rest; ++i, ++e)
1061          {
1062            int pcr = pended_cr;
1063            pended_cr = 0;
1064            if(*e == '\r')
1065            {
1066              pended_cr = 1;
1067            }
1068            else if(*e == '\n')
1069            {
1070              if(pcr)
1071              {
1072                sub = 2;
1073                ++i;
1074                ++e;
1075                break;
1076              }
1077              ++bare_lfs;
1078            }
1079          }
1080          if(res == 0)
1081            break;
1082          count = i - sub - pended_cr;
[07fbfced]1083          if(count > 0 && wrt(fd, b, count) != count)
[38371dbe]1084          {
1085            res = 0;
1086            break;
1087          }
[07fbfced]1088          if(sub == 2 && wrt(fd, e - 1, 1) != 1)
[38371dbe]1089            res = 0;
1090        }
1091        while((rest -= i) > 0);
[4fed5ac]1092        yield();
[38371dbe]1093      }
1094    }
1095
1096    if (0 > close(fd) || res == 0)
1097    {
1098      send_reply(info, 452, "Error writing file.");
1099      close_data_socket(info);
1100      return;
[3f777d0e]1101    }
1102  }
1103
[38371dbe]1104  if (bare_lfs > 0)
1105  {
1106    snprintf(buf, FTPD_BUFSIZE,
1107      "Transfer complete. WARNING! %d bare linefeeds received in ASCII mode.",
1108      bare_lfs);
1109    send_reply(info, 226, buf);
1110  }
1111  else
1112    send_reply(info, 226, "Transfer complete.");
1113  close_data_socket(info);
1114
[3f777d0e]1115}
[6d0e13c]1116
[3f777d0e]1117
[64adc13]1118/*
[3f777d0e]1119 * send_dirline
1120 *
1121 * Sends one line of LIST command reply corresponding to single file.
1122 *
1123 * Input parameters:
1124 *   s - socket descriptor to send data to
[38371dbe]1125 *   wide - if 0, send only file name.  If not 0, send 'stat' info as well in
[3f777d0e]1126 *          "ls -l" format.
1127 *   curTime - current time
1128 *   path - path to be prepended to what is given by 'add'
1129 *   add  - path to be appended to what is given by 'path', the resulting path
1130 *          is then passed to 'stat()' routine
1131 *   name - file name to be reported in output
1132 *   buf  - buffer for temporary data
1133 *
1134 * Output parameters:
[38371dbe]1135 *   returns 0 on failure, 1 on success
[3f777d0e]1136 *
1137 */
[38371dbe]1138static int
[3f777d0e]1139send_dirline(int s, int wide, time_t curTime, char const* path,
1140  char const* add, char const* fname, char* buf)
1141{
1142  if(wide)
1143  {
1144    struct stat stat_buf;
1145
1146    int plen = strlen(path);
1147    int alen = strlen(add);
1148    if(plen == 0)
1149    {
1150      buf[plen++] = '/';
1151      buf[plen] = '\0';
1152    }
1153    else
1154    {
1155      strcpy(buf, path);
1156      if(alen > 0 && buf[plen - 1] != '/')
[85e24a3]1157      {
[3f777d0e]1158        buf[plen++] = '/';
1159        if(plen >= FTPD_BUFSIZE)
[38371dbe]1160          return 0;
[3f777d0e]1161        buf[plen] = '\0';
[85e24a3]1162      }
[3f777d0e]1163    }
1164    if(plen + alen >= FTPD_BUFSIZE)
[38371dbe]1165      return 0;
[3f777d0e]1166    strcpy(buf + plen, add);
1167
1168    if (stat(buf, &stat_buf) == 0)
1169    {
1170      int len;
1171      struct tm bt;
1172      time_t tf = stat_buf.st_mtime;
1173      enum { SIZE = 80 };
[dc936a4]1174      time_t SIX_MONTHS = (365L*24L*60L*60L)/2L;
[3f777d0e]1175      char timeBuf[SIZE];
1176      gmtime_r(&tf, &bt);
1177      if(curTime > tf + SIX_MONTHS || tf > curTime + SIX_MONTHS)
1178        strftime (timeBuf, SIZE, "%b %d  %Y", &bt);
[85e24a3]1179      else
[3f777d0e]1180        strftime (timeBuf, SIZE, "%b %d %H:%M", &bt);
1181
1182      len = snprintf(buf, FTPD_BUFSIZE,
1183        "%c%c%c%c%c%c%c%c%c%c  1 %5d %5d %11u %s %s\r\n",
1184        (S_ISLNK(stat_buf.st_mode)?('l'):
1185          (S_ISDIR(stat_buf.st_mode)?('d'):('-'))),
1186        (stat_buf.st_mode & S_IRUSR)?('r'):('-'),
1187        (stat_buf.st_mode & S_IWUSR)?('w'):('-'),
1188        (stat_buf.st_mode & S_IXUSR)?('x'):('-'),
1189        (stat_buf.st_mode & S_IRGRP)?('r'):('-'),
1190        (stat_buf.st_mode & S_IWGRP)?('w'):('-'),
1191        (stat_buf.st_mode & S_IXGRP)?('x'):('-'),
1192        (stat_buf.st_mode & S_IROTH)?('r'):('-'),
1193        (stat_buf.st_mode & S_IWOTH)?('w'):('-'),
1194        (stat_buf.st_mode & S_IXOTH)?('x'):('-'),
1195        (int)stat_buf.st_uid,
1196        (int)stat_buf.st_gid,
1197        (int)stat_buf.st_size,
1198        timeBuf,
1199        fname
1200      );
1201
[38371dbe]1202      if(send(s, buf, len, 0) != len)
1203        return 0;
[3f777d0e]1204    }
1205  }
1206  else
1207  {
1208    int len = snprintf(buf, FTPD_BUFSIZE, "%s\r\n", fname);
[38371dbe]1209    if(send(s, buf, len, 0) != len)
1210      return 0;
[3f777d0e]1211  }
[38371dbe]1212  return 1;
[6d0e13c]1213}
1214
[64adc13]1215/*
[38371dbe]1216 * command_list
1217 *
1218 * Send file list to client.
1219 *
1220 * Input parameters:
1221 *   info - corresponding SessionInfo structure
1222 *   char *fname  - File (or directory) to list.
1223 *
1224 * Output parameters:
1225 *   NONE
1226 */
[6d0e13c]1227static void
[3f777d0e]1228command_list(FTPD_SessionInfo_t *info, char const *fname, int wide)
[6d0e13c]1229{
[3f777d0e]1230  int                 s;
1231  DIR                 *dirp = 0;
1232  struct dirent       *dp = 0;
1233  struct stat         stat_buf;
1234  char                buf[FTPD_BUFSIZE];
1235  time_t curTime;
[38371dbe]1236  int sc = 1;
[3f777d0e]1237
[86933ce0]1238  if(!info->auth)
1239  {
1240    send_reply(info, 550, "Access denied.");
1241    return;
1242  }
1243
[3f777d0e]1244  send_reply(info, 150, "Opening ASCII mode data connection for LIST.");
1245
1246  s = data_socket(info);
1247  if(0 > s)
1248  {
1249    syslog(LOG_ERR, "ftpd: Error connecting to data socket.");
1250    return;
1251  }
1252
1253  if(fname[0] == '\0')
1254    fname = ".";
1255
[07fbfced]1256  if (0 > stat(fname, &stat_buf))
[3f777d0e]1257  {
1258    snprintf(buf, FTPD_BUFSIZE,
1259      "%s: No such file or directory.\r\n", fname);
1260    send(s, buf, strlen(buf), 0);
1261  }
[07fbfced]1262  else if (S_ISDIR(stat_buf.st_mode) && (NULL == (dirp = opendir(fname))))
[3f777d0e]1263  {
1264    snprintf(buf, FTPD_BUFSIZE,
1265      "%s: Can not open directory.\r\n", fname);
1266    send(s, buf, strlen(buf), 0);
1267  }
1268  else
1269  {
1270    time(&curTime);
[38371dbe]1271    if(!dirp && *fname)
[07fbfced]1272      sc = sc && send_dirline(s, wide, curTime, fname, "", fname, buf);
[3f777d0e]1273    else {
1274      /* FIXME: need "." and ".." only when '-a' option is given */
[07fbfced]1275      sc = sc && send_dirline(s, wide, curTime, fname, "", ".", buf);
1276      sc = sc && send_dirline(s, wide, curTime, fname,
1277        (strcmp(fname, ftpd_root) ? ".." : ""), "..", buf);
[38371dbe]1278      while (sc && (dp = readdir(dirp)) != NULL)
1279        sc = sc &&
[07fbfced]1280          send_dirline(s, wide, curTime, fname, dp->d_name, dp->d_name, buf);
[3f777d0e]1281    }
1282  }
1283
1284  if(dirp)
1285    closedir(dirp);
[38371dbe]1286  close_data_socket(info);
1287
1288  if(sc)
1289    send_reply(info, 226, "Transfer complete.");
1290  else
1291    send_reply(info, 426, "Connection aborted.");
[6d0e13c]1292}
1293
1294
[64adc13]1295/*
[07fbfced]1296 * command_cwd
[3f777d0e]1297 *
[07fbfced]1298 * Change current working directory.
[3f777d0e]1299 *
1300 * Input parameters:
1301 *   info - corresponding SessionInfo structure
1302 *   dir  - directory name passed in CWD command
1303 *
1304 * Output parameters:
[07fbfced]1305 *   NONE
[3f777d0e]1306 *
[6d0e13c]1307 */
1308static void
[07fbfced]1309command_cwd(FTPD_SessionInfo_t  *info, char *dir)
[6d0e13c]1310{
[86933ce0]1311  if(!info->auth)
1312  {
1313    send_reply(info, 550, "Access denied.");
1314    return;
1315  }
1316
[07fbfced]1317  if(chdir(dir) == 0)
[3f777d0e]1318    send_reply(info, 250, "CWD command successful.");
1319  else
1320    send_reply(info, 550, "CWD command failed.");
1321}
[6d0e13c]1322
1323
[64adc13]1324/*
[07fbfced]1325 * command_pwd
1326 *
1327 * Send current working directory to client.
1328 *
1329 * Input parameters:
1330 *   info - corresponding SessionInfo structure
1331 *
1332 * Output parameters:
1333 *   NONE
1334 */
1335static void
1336command_pwd(FTPD_SessionInfo_t  *info)
1337{
1338  char buf[FTPD_BUFSIZE];
1339  char const* cwd;
1340  errno = 0;
1341  buf[0] = '"';
[86933ce0]1342
1343  if(!info->auth)
1344  {
1345    send_reply(info, 550, "Access denied.");
1346    return;
1347  }
1348
[07fbfced]1349  cwd = getcwd(buf + 1, FTPD_BUFSIZE - 4);
1350  if(cwd)
1351  {
1352    int len = strlen(cwd);
1353    static char const txt[] = "\" is the current directory.";
1354    int size = sizeof(txt);
1355    if(len + size + 1 >= FTPD_BUFSIZE)
1356      size = FTPD_BUFSIZE - len - 2;
1357    memcpy(buf + len + 1, txt, size);
1358    buf[len + size] = '\0';
1359    send_reply(info, 250, buf);
1360  }
1361  else {
1362    snprintf(buf, FTPD_BUFSIZE, "Error: %s.", serr());
1363    send_reply(info, 452, buf);
1364  }
1365}
[6d0e13c]1366
[64adc13]1367/*
[3f777d0e]1368 * command_mdtm
1369 *
[38371dbe]1370 * Handle FTP MDTM command (send file modification time to client)/
[3f777d0e]1371 *
1372 * Input parameters:
1373 *   info - corresponding SessionInfo structure
1374 *   fname - file name passed in MDTM command
1375 *
1376 * Output parameters:
1377 *   info->cwd is set to new CWD value.
1378 */
1379static void
1380command_mdtm(FTPD_SessionInfo_t  *info, char const* fname)
1381{
1382  struct stat stbuf;
1383  char buf[FTPD_BUFSIZE];
1384
[86933ce0]1385  if(!info->auth)
1386  {
1387    send_reply(info, 550, "Access denied.");
1388    return;
1389  }
1390
[07fbfced]1391  if (0 > stat(fname, &stbuf))
[3f777d0e]1392  {
1393    snprintf(buf, FTPD_BUFSIZE, "%s: %s.", fname, serr());
1394    send_reply(info, 550, buf);
1395  }
1396  else
1397  {
1398    struct tm *t = gmtime(&stbuf.st_mtime);
1399    snprintf(buf, FTPD_BUFSIZE, "%04d%02d%02d%02d%02d%02d",
1400      1900 + t->tm_year,
1401      t->tm_mon+1, t->tm_mday,
1402      t->tm_hour, t->tm_min, t->tm_sec);
1403    send_reply(info, 213, buf);
1404  }
[6d0e13c]1405}
1406
[55a36b7]1407static void
1408command_size(FTPD_SessionInfo_t *info, char const* fname)
1409{
1410  struct stat stbuf;
1411  char buf[FTPD_BUFSIZE];
1412
1413  if(!info->auth)
1414  {
1415    send_reply(info, 550, "Access denied.");
1416    return;
1417  }
1418
1419  if (info->xfer_mode != TYPE_I || 0 > stat(fname, &stbuf) || stbuf.st_size < 0)
1420  {
1421    send_reply(info, 550, "Could not get file size.");
1422  }
1423  else
1424  {
1425    snprintf(buf, FTPD_BUFSIZE, "%" PRIuMAX, (uintmax_t) stbuf.st_size);
1426    send_reply(info, 213, buf);
1427  }
1428}
1429
[64adc13]1430/*
[38371dbe]1431 * command_port
1432 *
1433 * This procedure fills address for data connection given the IP address and
1434 * port of the remote machine.
1435 *
1436 * Input parameters:
1437 *   info - corresponding SessionInfo structure
1438 *   args - arguments to the "PORT" command.
1439 *
1440 * Output parameters:
1441 *   info->data_addr is set according to arguments of the PORT command.
1442 *   info->use_default is set to 0 on success, 1 on failure.
1443 */
[6d0e13c]1444static void
[38371dbe]1445command_port(FTPD_SessionInfo_t *info, char const *args)
[6d0e13c]1446{
[38371dbe]1447  enum { NUM_FIELDS = 6 };
1448  unsigned int a[NUM_FIELDS];
1449  int n;
1450
1451  close_data_socket(info);
1452
1453  n = sscanf(args, "%u,%u,%u,%u,%u,%u", a+0, a+1, a+2, a+3, a+4, a+5);
1454  if(NUM_FIELDS == n)
1455  {
1456    int i;
[d030c4e7]1457    union {
1458      uint8_t b[NUM_FIELDS];
1459      struct {
1460        uint32_t ip;
1461        uint16_t port;
[3316af8]1462      } u ;
[d030c4e7]1463    } ip_info;
[38371dbe]1464
1465    for(i = 0; i < NUM_FIELDS; ++i)
1466    {
1467      if(a[i] > 255)
1468        break;
[d030c4e7]1469      ip_info.b[i] = (uint8_t)a[i];
[38371dbe]1470    }
1471
1472    if(i == NUM_FIELDS)
1473    {
1474      /* Note: while it contradicts with RFC959, we don't allow PORT command
1475       * to specify IP address different than those of the originating client
1476       * for the sake of safety. */
[3316af8]1477      if (ip_info.u.ip == info->def_addr.sin_addr.s_addr)
[38371dbe]1478      {
[3316af8]1479        info->data_addr.sin_addr.s_addr = ip_info.u.ip;
1480        info->data_addr.sin_port        = ip_info.u.port;
[38371dbe]1481        info->data_addr.sin_family      = AF_INET;
1482        memset(info->data_addr.sin_zero, 0, sizeof(info->data_addr.sin_zero));
1483
1484        info->use_default = 0;
1485        send_reply(info, 200, "PORT command successful.");
1486        return; /* success */
1487      }
1488      else
1489      {
1490        send_reply(info, 425, "Address doesn't match peer's IP.");
1491        return;
1492      }
1493    }
1494  }
1495  send_reply(info, 501, "Syntax error.");
1496}
1497
1498
[64adc13]1499/*
[38371dbe]1500 * command_pasv
1501 *
1502 * Handle FTP PASV command.
1503 * Open socket, listen for and accept connection on it.
1504 *
1505 * Input parameters:
1506 *   info - corresponding SessionInfo structure
1507 *
1508 * Output parameters:
1509 *   info->pasv_socket is set to the descriptor of the data socket
1510 */
1511static void
1512command_pasv(FTPD_SessionInfo_t *info)
1513{
1514  int s = -1;
1515  int err = 1;
1516
1517  close_data_socket(info);
1518
1519  s = socket(PF_INET, SOCK_STREAM, 0);
1520  if (s < 0)
1521    syslog(LOG_ERR, "ftpd: Error creating PASV socket: %s", serr());
1522  else
1523  {
1524    struct sockaddr_in addr;
[ea78ecad]1525    socklen_t addrLen = sizeof(addr);
[38371dbe]1526
1527    addr = info->ctrl_addr;
1528    addr.sin_port = htons(0);
1529
1530    if (0 > bind(s, (struct sockaddr *)&addr, addrLen))
1531      syslog(LOG_ERR, "ftpd: Error binding PASV socket: %s", serr());
1532    else if (0 > listen(s, 1))
1533      syslog(LOG_ERR, "ftpd: Error listening on PASV socket: %s", serr());
[07fbfced]1534    else if(set_socket_timeout(s, info->idle))
[38371dbe]1535    {
1536      char buf[FTPD_BUFSIZE];
1537      unsigned char const *ip, *p;
1538
1539      getsockname(s, (struct sockaddr *)&addr, &addrLen);
1540      ip = (unsigned char const*)&(addr.sin_addr);
1541      p  = (unsigned char const*)&(addr.sin_port);
1542      snprintf(buf, FTPD_BUFSIZE, "Entering passive mode (%u,%u,%u,%u,%u,%u).",
1543        ip[0], ip[1], ip[2], ip[3], p[0], p[1]);
1544      send_reply(info, 227, buf);
1545
1546      info->pasv_socket = accept(s, (struct sockaddr *)&addr, &addrLen);
1547      if (0 > info->pasv_socket)
1548        syslog(LOG_ERR, "ftpd: Error accepting PASV connection: %s", serr());
1549      else
[07fbfced]1550      {
1551        close_socket(s);
1552        s = -1;
[38371dbe]1553        err = 0;
[07fbfced]1554      }
[38371dbe]1555    }
1556  }
1557  if(err)
1558  {
1559    /* (OSV) The note is from FreeBSD FTPD.
1560     * Note: a response of 425 is not mentioned as a possible response to
1561     * the PASV command in RFC959.  However, it has been blessed as a
1562     * legitimate response by Jon Postel in a telephone conversation
1563     * with Rick Adams on 25 Jan 89. */
1564    send_reply(info, 425, "Can't open passive connection.");
1565    close_socket(s);
1566  }
[6d0e13c]1567}
1568
1569
[64adc13]1570/*
[3f777d0e]1571 * skip_options
1572 *
1573 * Utility routine to skip options (if any) from input command.
1574 *
1575 * Input parameters:
1576 *   p  - pointer to pointer to command
1577 *
1578 * Output parameters:
1579 *   p  - is changed to point to first non-option argument
1580 */
1581static void
1582skip_options(char **p)
1583{
1584  char* buf = *p;
[38371dbe]1585  char* last = NULL;
[3f777d0e]1586  while(1) {
[bab5c5fa]1587    while(isspace((unsigned char)*buf))
[3f777d0e]1588      ++buf;
1589    if(*buf == '-') {
1590      if(*++buf == '-') { /* `--' should terminate options */
[bab5c5fa]1591        if(isspace((unsigned char)*++buf)) {
[38371dbe]1592          last = buf;
1593          do ++buf;
[bab5c5fa]1594          while(isspace((unsigned char)*buf));
[38371dbe]1595          break;
1596        }
[3f777d0e]1597      }
[bab5c5fa]1598      while(*buf && !isspace((unsigned char)*buf))
[3f777d0e]1599        ++buf;
[38371dbe]1600      last = buf;
[3f777d0e]1601    }
1602    else
1603      break;
1604  }
[38371dbe]1605  if(last)
1606    *last = '\0';
[3f777d0e]1607  *p = buf;
1608}
1609
[64adc13]1610/*
[38371dbe]1611 * split_command
1612 *
1613 * Split command into command itself, options, and arguments. Command itself
1614 * is converted to upper case.
1615 *
1616 * Input parameters:
1617 *   buf - initial command string
1618 *
1619 * Output parameter:
1620 *   buf  - is modified by inserting '\0' at ends of split entities
1621 *   cmd  - upper-cased command code
1622 *   opts - string containing all the options
1623 *   args - string containing all the arguments
1624 */
[438a893]1625static void
[38371dbe]1626split_command(char *buf, char **cmd, char **opts, char **args)
1627{
1628  char* eoc;
1629  char* p = buf;
[bab5c5fa]1630  while(isspace((unsigned char)*p))
[38371dbe]1631    ++p;
1632  *cmd = p;
[bab5c5fa]1633  while(*p && !isspace((unsigned char)*p))
[38371dbe]1634  {
[bab5c5fa]1635    *p = toupper((unsigned char)*p);
[38371dbe]1636    ++p;
1637  }
1638  eoc = p;
1639  if(*p)
1640    *p++ = '\0';
[bab5c5fa]1641  while(isspace((unsigned char)*p))
[38371dbe]1642    ++p;
1643  *opts = p;
1644  skip_options(&p);
1645  *args = p;
1646  if(*opts == p)
1647    *opts = eoc;
1648  while(*p && *p != '\r' && *p != '\n')
1649    ++p;
1650  if(*p)
1651    *p++ = '\0';
1652}
1653
[64adc13]1654/*
[38371dbe]1655 * exec_command
1656 *
1657 * Parse and execute FTP command.
1658 *
1659 * FIXME: This section is somewhat of a hack.  We should have a better
1660 *        way to parse commands.
1661 *
1662 * Input parameters:
1663 *   info - corresponding SessionInfo structure
1664 *   cmd  - command to be executed (upper-case)
1665 *   args - arguments of the command
1666 *
1667 * Output parameters:
1668 *    NONE
1669 */
[6d0e13c]1670static void
[07fbfced]1671exec_command(FTPD_SessionInfo_t *info, char* cmd, char* args)
[6d0e13c]1672{
[3f777d0e]1673  char fname[FTPD_BUFSIZE];
[38371dbe]1674  int wrong_command = 0;
[3f777d0e]1675
[07fbfced]1676  fname[0] = '\0';
1677
[38371dbe]1678  if (!strcmp("PORT", cmd))
[3f777d0e]1679  {
[38371dbe]1680    command_port(info, args);
[3f777d0e]1681  }
[38371dbe]1682  else if (!strcmp("PASV", cmd))
[3f777d0e]1683  {
[38371dbe]1684    command_pasv(info);
1685  }
1686  else if (!strcmp("RETR", cmd))
1687  {
[ff49ffe]1688    strncpy(fname, args, 254);
[3f777d0e]1689    command_retrieve(info, fname);
1690  }
[38371dbe]1691  else if (!strcmp("STOR", cmd))
[3f777d0e]1692  {
[ff49ffe]1693    strncpy(fname, args, 254);
[3f777d0e]1694    command_store(info, fname);
1695  }
[38371dbe]1696  else if (!strcmp("LIST", cmd))
[3f777d0e]1697  {
[ff49ffe]1698    strncpy(fname, args, 254);
[3f777d0e]1699    command_list(info, fname, 1);
1700  }
[38371dbe]1701  else if (!strcmp("NLST", cmd))
[3f777d0e]1702  {
[ff49ffe]1703    strncpy(fname, args, 254);
[3f777d0e]1704    command_list(info, fname, 0);
1705  }
[38371dbe]1706  else if (!strcmp("MDTM", cmd))
[3f777d0e]1707  {
[ff49ffe]1708    strncpy(fname, args, 254);
[3f777d0e]1709    command_mdtm(info, fname);
1710  }
[55a36b7]1711  else if (!strcmp("SIZE", cmd))
1712  {
1713    strncpy(fname, args, 254);
1714    command_size(info, fname);
1715  }
[38371dbe]1716  else if (!strcmp("SYST", cmd))
[3f777d0e]1717  {
[38371dbe]1718    send_reply(info, 215, FTPD_SYSTYPE);
[3f777d0e]1719  }
[38371dbe]1720  else if (!strcmp("TYPE", cmd))
[3f777d0e]1721  {
[38371dbe]1722    if (args[0] == 'I')
[3f777d0e]1723    {
1724      info->xfer_mode = TYPE_I;
1725      send_reply(info, 200, "Type set to I.");
1726    }
[38371dbe]1727    else if (args[0] == 'A')
[3f777d0e]1728    {
1729      info->xfer_mode = TYPE_A;
1730      send_reply(info, 200, "Type set to A.");
1731    }
1732    else
1733    {
1734      info->xfer_mode = TYPE_I;
1735      send_reply(info, 504, "Type not implemented.  Set to I.");
1736    }
1737  }
[86933ce0]1738  else if (!strcmp("USER", cmd))
[3f777d0e]1739  {
[51da629]1740    strlcpy(info->user_buf, args, sizeof(info->user_buf));
1741    info->user = info->user_buf;
[b771cb4]1742    if (ftpd_config->login &&
1743      !ftpd_config->login(info->user, NULL)) {
[86933ce0]1744      info->auth = false;
1745      send_reply(info, 331, "User name okay, need password.");
1746    } else {
1747      info->auth = true;
1748      send_reply(info, 230, "User logged in.");
1749    }
1750  }
1751  else if (!strcmp("PASS", cmd))
1752  {
1753    if (!info->user) {
1754      send_reply(info, 332, "Need account to log in");
1755    } else {
[b771cb4]1756      if (ftpd_config->login &&
[dcf42bb2]1757        !ftpd_config->login(info->user, args)) {
[86933ce0]1758        info->auth = false;
1759        send_reply(info, 530, "Not logged in.");
1760      } else {
1761        info->auth = true;
1762        send_reply(info, 230, "User logged in.");
1763      }
1764    }
[3f777d0e]1765  }
[38371dbe]1766  else if (!strcmp("DELE", cmd))
[3f777d0e]1767  {
[86933ce0]1768    if(!can_write() || !info->auth)
[38371dbe]1769    {
1770      send_reply(info, 550, "Access denied.");
1771    }
1772    else if (
[ff49ffe]1773      strncpy(fname, args, 254) &&
[07fbfced]1774      unlink(fname) == 0)
[3f777d0e]1775    {
1776      send_reply(info, 257, "DELE successful.");
1777    }
1778    else
1779    {
1780      send_reply(info, 550, "DELE failed.");
1781    }
1782  }
[38371dbe]1783  else if (!strcmp("SITE", cmd))
[3f777d0e]1784  {
[38371dbe]1785    char* opts;
1786    split_command(args, &cmd, &opts, &args);
1787    if(!strcmp("CHMOD", cmd))
[3f777d0e]1788    {
[38371dbe]1789      int mask;
1790
[86933ce0]1791      if(!can_write() || !info->auth)
[38371dbe]1792      {
1793        send_reply(info, 550, "Access denied.");
1794      }
[ff49ffe]1795      else {
1796        char *c;
1797        c = strchr(args, ' ');
1798        if((c != NULL) && (sscanf(args, "%o", &mask) == 1) && strncpy(fname, c+1, 254)
1799          && (chmod(fname, (mode_t)mask) == 0))
1800          send_reply(info, 257, "CHMOD successful.");
1801        else
1802          send_reply(info, 550, "CHMOD failed.");
[38371dbe]1803      }
[3f777d0e]1804    }
1805    else
[38371dbe]1806      wrong_command = 1;
[3f777d0e]1807  }
[38371dbe]1808  else if (!strcmp("RMD", cmd))
[3f777d0e]1809  {
[86933ce0]1810    if(!can_write() || !info->auth)
[38371dbe]1811    {
1812      send_reply(info, 550, "Access denied.");
1813    }
1814    else if (
[ff49ffe]1815      strncpy(fname, args, 254) &&
[07fbfced]1816      rmdir(fname) == 0)
[3f777d0e]1817    {
1818      send_reply(info, 257, "RMD successful.");
1819    }
1820    else
1821    {
1822      send_reply(info, 550, "RMD failed.");
1823    }
1824  }
[38371dbe]1825  else if (!strcmp("MKD", cmd))
[3f777d0e]1826  {
[86933ce0]1827    if(!can_write() || !info->auth)
[38371dbe]1828    {
1829      send_reply(info, 550, "Access denied.");
1830    }
1831    else if (
[ff49ffe]1832      strncpy(fname, args, 254) &&
[07fbfced]1833      mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
[3f777d0e]1834    {
1835      send_reply(info, 257, "MKD successful.");
1836    }
1837    else
1838    {
1839      send_reply(info, 550, "MKD failed.");
1840    }
1841  }
[38371dbe]1842  else if (!strcmp("CWD", cmd))
[3f777d0e]1843  {
[ff49ffe]1844    strncpy(fname, args, 254);
[07fbfced]1845    command_cwd(info, fname);
[3f777d0e]1846  }
[38371dbe]1847  else if (!strcmp("CDUP", cmd))
[3f777d0e]1848  {
[07fbfced]1849    command_cwd(info, "..");
[3f777d0e]1850  }
[38371dbe]1851  else if (!strcmp("PWD", cmd))
[3f777d0e]1852  {
[07fbfced]1853    command_pwd(info);
[3f777d0e]1854  }
1855  else
[38371dbe]1856    wrong_command = 1;
1857
1858  if(wrong_command)
1859    send_reply(info, 500, "Command not understood.");
[6d0e13c]1860}
1861
[85e24a3]1862
[64adc13]1863/*
[38371dbe]1864 * session
1865 *
1866 * This task handles single session.  It is waked up when the FTP daemon gets a
1867 * service request from a remote machine.  Here, we watch for commands that
1868 * will come through the control connection.  These commands are then parsed
1869 * and executed until the connection is closed, either unintentionally or
1870 * intentionally with the "QUIT" command.
1871 *
1872 * Input parameters:
1873 *   arg - pointer to corresponding SessionInfo.
1874 *
1875 * Output parameters:
1876 *   NONE
1877 */
[6d0e13c]1878static void
[3f777d0e]1879session(rtems_task_argument arg)
[6d0e13c]1880{
[38371dbe]1881  FTPD_SessionInfo_t  *const info = (FTPD_SessionInfo_t  *)arg;
[07fbfced]1882  int chroot_made = 0;
1883
1884  rtems_libio_set_private_env();
1885
1886  /* chroot() can fail here because the directory may not exist yet. */
1887  chroot_made = chroot(ftpd_root) == 0;
[38371dbe]1888
1889  while(1)
1890  {
1891    rtems_event_set set;
[1b937c6]1892    int rv;
[6d0e13c]1893
[3f777d0e]1894    rtems_event_receive(FTPD_RTEMS_EVENT, RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT,
1895      &set);
[6d0e13c]1896
[07fbfced]1897    chroot_made = chroot_made || chroot(ftpd_root) == 0;
1898
[1b937c6]1899    rv = chroot_made ? chdir("/") : -1;
[07fbfced]1900
[1b937c6]1901    errno = 0;
[6d0e13c]1902
[1b937c6]1903    if (rv == 0)
[3f777d0e]1904    {
[1b937c6]1905      send_reply(info, 220, FTPD_SERVER_MESSAGE);
[38371dbe]1906
[1b937c6]1907      while (1)
[6d0e13c]1908      {
[1b937c6]1909        char buf[FTPD_BUFSIZE];
1910        char *cmd, *opts, *args;
[6d0e13c]1911
[1b937c6]1912        if (fgets(buf, FTPD_BUFSIZE, info->ctrl_fp) == NULL)
1913        {
1914          syslog(LOG_INFO, "ftpd: Connection aborted.");
1915          break;
1916        }
[38371dbe]1917
[1b937c6]1918        split_command(buf, &cmd, &opts, &args);
1919
1920        if (!strcmp("QUIT", cmd))
1921        {
1922          send_reply(info, 221, "Goodbye.");
1923          break;
1924        }
1925        else
1926        {
1927          exec_command(info, cmd, args);
1928        }
[6d0e13c]1929      }
[3f777d0e]1930    }
[1b937c6]1931    else
1932    {
1933      send_reply(info, 421, "Service not available, closing control connection.");
1934    }
[6d0e13c]1935
[995610e1]1936    /*
1937     * Go back to the root directory.  A use case is to release a current
1938     * directory in a mounted file system on dynamic media, e.g. USB stick.
1939     * The return value can be ignored since the next session will try do the
1940     * operation again and an error check is performed in this case.
1941     */
1942    chdir("/");
1943
[3f777d0e]1944    /* Close connection and put ourselves back into the task pool. */
[38371dbe]1945    close_data_socket(info);
[3f777d0e]1946    close_stream(info);
1947    task_pool_release(info);
1948  }
[6d0e13c]1949}
1950
1951
[64adc13]1952/*
[e3f8ba57]1953 * ftpd_daemon
[38371dbe]1954 *
1955 * This task runs forever.  It waits for service requests on the FTP port
1956 * (port 21 by default).  When a request is received, it opens a new session
1957 * to handle those requests until the connection is closed.
1958 *
1959 * Input parameters:
1960 *   NONE
1961 *
1962 * Output parameters:
1963 *   NONE
1964 */
[6d0e13c]1965static void
[e3f8ba57]1966ftpd_daemon(rtems_task_argument args RTEMS_UNUSED)
[6d0e13c]1967{
[3f777d0e]1968  int                 s;
[ea78ecad]1969  socklen_t           addrLen;
[38371dbe]1970  struct sockaddr_in  addr;
[3f777d0e]1971
[479a28e0]1972  memset(&addr, 0, sizeof(addr));
[38371dbe]1973  addr.sin_family      = AF_INET;
[b771cb4]1974  addr.sin_port        = htons(ftpd_config->port);
[38371dbe]1975  addr.sin_addr.s_addr = htonl(INADDR_ANY);
[3f777d0e]1976
[479a28e0]1977  s = socket(PF_INET, SOCK_STREAM, 0);
1978  if (s < 0)
1979    syslog(LOG_ERR, "ftpd: Error creating control socket: %s", serr());
1980  else if (0 > bind(s, (struct sockaddr *)&addr, sizeof(addr)))
[3f777d0e]1981    syslog(LOG_ERR, "ftpd: Error binding control socket: %s", serr());
[38371dbe]1982  else if (0 > listen(s, 1))
[3f777d0e]1983    syslog(LOG_ERR, "ftpd: Error listening on control socket: %s", serr());
[38371dbe]1984  else while (1)
[3f777d0e]1985  {
1986    int ss;
[38371dbe]1987    addrLen = sizeof(addr);
1988    ss = accept(s, (struct sockaddr *)&addr, &addrLen);
[3f777d0e]1989    if (0 > ss)
1990      syslog(LOG_ERR, "ftpd: Error accepting control connection: %s", serr());
[38371dbe]1991    else if(!set_socket_timeout(ss, ftpd_timeout))
1992      close_socket(ss);
[3f777d0e]1993    else
1994    {
[479a28e0]1995      FTPD_SessionInfo_t *info;
1996
[3f777d0e]1997      info = task_pool_obtain();
1998      if (NULL == info)
[6d0e13c]1999      {
[3f777d0e]2000        close_socket(ss);
[6d0e13c]2001      }
2002      else
2003      {
[38371dbe]2004        info->ctrl_socket = ss;
2005        if ((info->ctrl_fp = fdopen(info->ctrl_socket, "r+")) == NULL)
[3f777d0e]2006        {
2007          syslog(LOG_ERR, "ftpd: fdopen() on socket failed: %s", serr());
2008          close_stream(info);
2009          task_pool_release(info);
2010        }
2011        else
2012        {
[38371dbe]2013          /* Initialize corresponding SessionInfo structure */
2014          info->def_addr = addr;
2015          if(0 > getsockname(ss, (struct sockaddr *)&addr, &addrLen))
2016          {
2017            syslog(LOG_ERR, "ftpd: getsockname(): %s", serr());
2018            close_stream(info);
2019            task_pool_release(info);
2020          }
2021          else
2022          {
2023            info->use_default = 1;
2024            info->ctrl_addr  = addr;
2025            info->pasv_socket = -1;
[07fbfced]2026            info->data_socket = -1;
2027            info->xfer_mode   = TYPE_A;
[38371dbe]2028            info->data_addr.sin_port =
2029              htons(ntohs(info->ctrl_addr.sin_port) - 1);
2030            info->idle = ftpd_timeout;
[86933ce0]2031            info->user = NULL;
[b771cb4]2032            if (ftpd_config->login)
[86933ce0]2033              info->auth = false;
2034            else
2035              info->auth = true;
[38371dbe]2036            /* Wakeup the session task.  The task will call task_pool_release
2037               after it closes connection. */
2038            rtems_event_send(info->tid, FTPD_RTEMS_EVENT);
2039          }
[3f777d0e]2040        }
[85e24a3]2041      }
[3f777d0e]2042    }
2043  }
[f004b2b8]2044  rtems_task_exit();
[6d0e13c]2045}
2046
2047
[64adc13]2048/*
[38371dbe]2049 * rtems_ftpd_start
2050 *
2051 * Here, we start the FTPD task which waits for FTP requests and services
2052 * them.  This procedure returns to its caller once the task is started.
2053 *
2054 *
2055 * Input parameters:
[b771cb4]2056 *    config: constant initial setup.
[38371dbe]2057 * Output parameters:
2058 *    returns RTEMS_SUCCESSFUL on successful start of the daemon.
2059 */
[85e24a3]2060int
[b771cb4]2061rtems_ftpd_start(const struct rtems_ftpd_configuration* config)
[6d0e13c]2062{
[3f777d0e]2063  rtems_status_code   sc;
2064  rtems_id            tid;
2065  rtems_task_priority priority;
2066  int count;
2067
[b771cb4]2068  if (ftpd_config != NULL)
2069    return RTEMS_RESOURCE_IN_USE;
2070
2071  ftpd_config = malloc(sizeof(*ftpd_config));
2072  if (ftpd_config == NULL)
2073    return RTEMS_NO_MEMORY;
2074
2075  *ftpd_config = *config;
2076
2077  if (ftpd_config->port == 0)
[3f777d0e]2078  {
[b771cb4]2079    ftpd_config->port = FTPD_CONTROL_PORT;
[3f777d0e]2080  }
2081
[b771cb4]2082  if (ftpd_config->priority == 0)
[3f777d0e]2083  {
[b771cb4]2084    ftpd_config->priority = 40;
[3f777d0e]2085  }
[b771cb4]2086  priority = ftpd_config->priority;
[3f777d0e]2087
[b771cb4]2088  ftpd_timeout = ftpd_config->idle;
[38371dbe]2089  if (ftpd_timeout < 0)
2090    ftpd_timeout = 0;
[b771cb4]2091  ftpd_config->idle = ftpd_timeout;
[38371dbe]2092
[b771cb4]2093  ftpd_access = ftpd_config->access;
[38371dbe]2094
[796bb3f]2095  ftpd_root = "/";
[b771cb4]2096  if (ftpd_config->root && ftpd_config->root[0] == '/' )
2097    ftpd_root = ftpd_config->root;
[796bb3f]2098
[b771cb4]2099  ftpd_config->root = ftpd_root;
[796bb3f]2100
[b771cb4]2101  if (ftpd_config->tasks_count <= 0)
2102    ftpd_config->tasks_count = 1;
2103  count = ftpd_config->tasks_count;
[3f777d0e]2104
2105  if (!task_pool_init(count, priority))
2106  {
2107    syslog(LOG_ERR, "ftpd: Could not initialize task pool.");
2108    return RTEMS_UNSATISFIED;
2109  }
2110
2111  sc = rtems_task_create(rtems_build_name('F', 'T', 'P', 'D'),
[eef45d1]2112    priority, RTEMS_MINIMUM_STACK_SIZE,
[3f777d0e]2113    RTEMS_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR |
2114    RTEMS_INTERRUPT_LEVEL(0),
[32b5b23]2115    RTEMS_FLOATING_POINT | RTEMS_LOCAL,
[3f777d0e]2116    &tid);
2117
2118  if (sc == RTEMS_SUCCESSFUL)
2119  {
[e3f8ba57]2120    sc = rtems_task_start(tid, ftpd_daemon, 0);
[3f777d0e]2121    if (sc != RTEMS_SUCCESSFUL)
2122      rtems_task_delete(tid);
2123  }
2124
2125  if (sc != RTEMS_SUCCESSFUL)
2126  {
2127    task_pool_done(count);
2128    syslog(LOG_ERR, "ftpd: Could not create/start FTP daemon: %s",
2129      rtems_status_text(sc));
2130    return RTEMS_UNSATISFIED;
2131  }
2132
[b771cb4]2133  if (ftpd_config->verbose)
2134    syslog(LOG_INFO, "ftpd: FTP daemon started (%d session%s max)",
2135           count, ((count > 1) ? "s" : ""));
[07fbfced]2136
[3f777d0e]2137  return RTEMS_SUCCESSFUL;
[6d0e13c]2138}
Note: See TracBrowser for help on using the repository browser.