source: rtems/cpukit/ftpd/ftpd.c @ eef45d1

4.115
Last change on this file since eef45d1 was eef45d1, checked in by Sebastian Huber <sebastian.huber@…>, on 10/26/11 at 10:20:54

2011-10-26 Sebastian Huber <sebastian.huber@…>

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