source: rtems/cpukit/ftpd/ftpd.c @ 3f777d0e

4.104.114.84.95
Last change on this file since 3f777d0e was 3f777d0e, checked in by Joel Sherrill <joel.sherrill@…>, on 01/12/01 at 13:51:56

2001-01-12 Sergei Organov <osv@…>

  • rtems_servers/ftpd.c, rtems_servers/ftpd.h: Major enhancements as listed below:
    • use pool of pre-created threads to handle sessions instead of

creating/deleting threads on the fly

  • LIST output is now similar to what "/bin/ls -al" would output, thus FTP clients such Netscape are happy with it.
  • LIST NAME now works (both for files and directories)
  • added support for NLST, CDUP, and MDTM FTP commands to make more FTP clients happy
  • keep track of CWD for every session separately
  • ability to specify root directory name for FTPD in configuration table. FTPD will then create illusion for FTP clients that this is actually root directory.
  • ignore options sent in commands, thus LIST -al FILE works and doesn't try to list "-al" directory.
  • buffers are allocated on stack instead of heap where possible to eliminate malloc/free calls (avoid possible heap fragmentation troubles).
  • drop using of task notepad to pass parameters - use function arguments instead
  • use snprintf() instead of sprintf() as the latter is unsafe
  • use of PF_INET in socket() instead of AF_INET

Here are ftp clients I've tried new FTPD with (all of them
running on Debian GNU/Linux 2.2):

Lftp 2.1.10
NcFTP 2.4.3
Netscape 4.75
ftp
mc 4.5.49

  • Property mode set to 100644
File size: 51.6 KB
Line 
1/* FIXME: 1. Parse command is a hack.  We can do better.
2 *        2. Some sort of access control?
3 *        3. OSV: Timeouts on sockets.
4 *        4. OSV: hooks support seems to be a joke, as it requires storing of
5 *           entire input file in memory. Seem to be better to change it to
6 *           something more reasonable, like having
7 *           'hook_write(void const *buf, int count)' routine that will be
8 *           called multiple times while file is being received.
9 *        5. OSV: Remove hack with "/dev/null"?
10 *        6. OSV: data_addr field of SessionInfo structure is not initialized.
11 *           This will lead to undefined behavior if FTP client doesn't send
12 *           PORT command. Seems we need to implement usage of default port
13 *           (ftp port - 1). I didn't find any FTP client that doesn't send
14 *           PORT command though.
15 *        7. OSV: while server claims TYPE ASCII command succeeds (to make
16 *           happy some clients), ASCII mode isn't actually implemented.
17 *        8. Passive Mode?
18 *
19 *  FTP Server Daemon
20 *
21 *  Submitted by: Jake Janovetz <janovetz@tempest.ece.uiuc.edu>
22 *
23 *  Changed by:   Sergei Organov <osv@javad.ru> (OSV)
24 *    Changes:
25 *      - use pool of pre-created threads to handle sessions
26 *      - LIST output now similar to what "/bin/ls -al" would output, thus
27 *        FTP clients could parse it.
28 *      - LIST NAME now works (both for files and directories)
29 *      - keep track of CWD for every session separately
30 *      - ability to specify root directory name in configuration table
31 *      - options sent in commands are ignored, thus LIST -al FILE works
32 *      - added support for NLST, CDUP and MDTM commands
33 *      - buffers are allocated on stack instead of heap where possible
34 *      - drop using of task notepad to pass parameters - use function
35 *        arguments instead
36 *      - various bug-fixes, e.g., use of PF_INET in socket() instead of
37 *        AF_INET, use snprintf() instead of sprintf() everywhere for safety,
38 *        etc.
39 *
40 *  $Id$
41 */
42
43/**************************************************************************
44 *                                 ftpd.c                                 *
45 **************************************************************************
46 * Description:                                                           *
47 *                                                                        *
48 *    This file contains the daemon which services requests that appear   *
49 *    on the FTP port.  This server is compatible with FTP, but it        *
50 *    also provides 'hooks' to make it usable in situations where files   *
51 *    are not used/necessary.  Once the server is started, it runs        *
52 *    forever.                                                            *
53 *                                                                        *
54 *                                                                        *
55 *    Organization:                                                       *
56 *                                                                        *
57 *       The FTP daemon is started upon boot along with a (configurable)  *
58 *       number of tasks to handle sessions. It runs all the time and     *
59 *       waits for connections on the known FTP port (21).  When          *
60 *       a connection is made, it wakeups a 'session' task.  That         *
61 *       session then interacts with the remote host.  When the session   *
62 *       is complete, the session task goes to sleep.  The daemon still   *
63 *       runs, however.                                                   *
64 *                                                                        *
65 *                                                                        *
66 * Supported commands are:                                                *
67 *                                                                        *
68 * RETR xxx     - Sends a file from the client.                           *
69 * STOR xxx     - Receives a file from the client.  xxx = filename.       *
70 * LIST xxx     - Sends a file list to the client.                        *
71 * NLST xxx     - Sends a file list to the client.                        *
72 * USER         - Does nothing.                                           *
73 * PASS         - Does nothing.                                           *
74 * SYST         - Replies with the system type (`RTEMS').                 *
75 * DELE xxx     - Delete file xxx.                                        *
76 * MKD xxx      - Create directory xxx.                                   *
77 * RMD xxx      - Remove directory xxx.                                   *
78 * PWD          - Print working directory.                                *
79 * CWD xxx      - Change working directory.                               *
80 * CDUP         - Change to upper directory.                              *
81 * SITE CHMOD xxx yyy - Change permissions on file yyy to xxx.            *
82 * PORT a,b,c,d,x,y   - Setup for a data port to IP address a.b.c.d       *
83 *                      and port (x*256 + y).                             *
84 * MDTM xxx     - Send date/time to the client. xxx = filename.           *
85 *                                                                        *
86 *                                                                        *
87 * The public routines contained in this file are:                        *
88 *                                                                        *
89 *    rtems_initialize_ftpd - Initializes and starts the server daemon,   *
90 *                            then returns to its caller.                 *
91 *                                                                        *
92 *------------------------------------------------------------------------*
93 * Jake Janovetz                                                          *
94 * University of Illinois                                                 *
95 * 1406 West Green Street                                                 *
96 * Urbana IL  61801                                                       *
97 **************************************************************************
98 * Change History:                                                        *
99 *  12/01/97   - Creation (JWJ)                                           *
100 *  2001-01-08 - Changes by OSV                                           *
101 *************************************************************************/
102
103/**************************************************************************
104 * Meanings of first and second digits of reply codes:
105 *
106 * Reply:  Description:
107 *-------- --------------
108 *  1yz    Positive preliminary reply.  The action is being started but
109 *         expect another reply before sending another command.
110 *  2yz    Positive completion reply.  A new command can be sent.
111 *  3yz    Positive intermediate reply.  The command has been accpeted
112 *         but another command must be sent.
113 *  4yz    Transient negative completion reply.  The requested action did
114 *         not take place, but the error condition is temporary so the
115 *         command can be reissued later.
116 *  5yz    Permanent negative completion reply.  The command was not
117 *         accepted and should not be retried.
118 *-------------------------------------------------------------------------
119 *  x0z    Syntax errors.
120 *  x1z    Information.
121 *  x2z    Connections.  Replies referring to the control or data
122 *         connections.
123 *  x3z    Authentication and accounting.  Replies for the login or
124 *         accounting commands.
125 *  x4z    Unspecified.
126 *  x5z    Filesystem status.
127 *************************************************************************/
128
129#include <stdio.h>
130#include <stdlib.h>
131#include <string.h>
132#include <unistd.h>
133#include <fcntl.h>
134#include <dirent.h>
135#include <errno.h>
136
137#include <rtems.h>
138#include <rtems/rtems_bsdnet.h>
139#include <rtems/error.h>
140#include <syslog.h>
141
142#include <sys/types.h>
143#include <sys/socket.h>
144#include <arpa/ftp.h>
145#include <netinet/in.h>
146
147#include "ftpd.h"
148
149
150#ifdef __GNUC__
151/* change to #if 1 to disable syslog entirely */
152#if 0
153#undef  syslog
154#define syslog(a, b, ...) while(0){}
155#endif
156#endif
157
158#define FTPD_SERVER_MESSAGE  "RTEMS FTP server (Version 1.1-JWJ) ready."
159
160#define FTPD_SYSTYPE "RTEMS"
161
162/* Seem to be unused */
163#if 0
164#define FTPD_WELCOME_MESSAGE \
165   "Welcome to the RTEMS FTP server.\n" \
166   "\n" \
167   "Login accepted.\n"
168#endif
169
170/* Various buffer sizes */
171enum
172{
173  FTPD_BUFSIZE  = 256,       /* Size for temporary buffers */
174  FTPD_DATASIZE = 1024,      /* Size for file transfer buffers */
175  FTPD_STACKSIZE = 8 * 1024, /* Tasks stack size */
176};
177
178/* Event to be used by session tasks for waiting */
179enum
180{
181  FTPD_RTEMS_EVENT = RTEMS_EVENT_1
182};
183
184/* Configuration table */
185extern struct rtems_ftpd_configuration rtems_ftpd_configuration;
186
187/* this is not prototyped in strict ansi mode */
188FILE *fdopen (int fildes, const char *mode);
189
190/*
191 * SessionInfo structure.
192 *
193 * The following structure is allocated for each session.
194 */
195typedef struct
196{
197  struct sockaddr_in  data_addr;   /* Data address for PORT commands */
198  FILE                *ctrl_fp;    /* File pointer for control connection */
199  int                 socket;      /* Socket descriptor for ctrl connection */
200  char                cwd[FTPD_BUFSIZE];  /* Current working directory */
201  /* Login -- future use -- */
202  int                 xfer_mode;   /* Transfer mode (ASCII/binary) */
203  rtems_id            tid;         /* Task id */
204} FTPD_SessionInfo_t;
205
206
207/*
208 * TaskPool structure.
209 */
210typedef struct
211{
212  FTPD_SessionInfo_t    *info;
213  FTPD_SessionInfo_t    **queue;
214  int                   count;
215  int                   head;
216  int                   tail;
217  rtems_id              mutex;
218  rtems_id              sem;
219} FTPD_TaskPool_t;
220
221/*
222 * Task pool instance.
223 */
224static FTPD_TaskPool_t task_pool;
225
226/*
227 * Root node for FTPD without trailing slash. Even '/' node is denoted as
228 * empty string here.
229 */
230static char root[FTPD_BUFSIZE];
231
232
233/*PAGE
234 *
235 * serr
236 *
237 * Return errror string corresponding to current 'errno'.
238 *
239 */
240static char const*
241serr(void)
242{
243  return strerror(errno);
244}
245
246
247/*
248 *  Utility routines to manage root directory and session local
249 *  current working directory.
250 */
251
252
253/*PAGE
254 *
255 * squeeze_path
256 *
257 * Squeezes path according to OS rules, i.e., eliminates /./, /../, and //
258 * from the path. Does nothing if the path is relative, i.e. doesn't begin
259 * with '/'. The trailing slash is always removed, even when alone, i.e. "/"
260 * will be "" after squeeze.
261 *
262 * Input parameters:
263 *   path - the path to be squeezed
264 *
265 * Output parameters:
266 *   path - squeezed path
267 *
268 */
269static void
270squeeze_path(char* path)
271{
272  if(path[0] == '/')
273  {
274    char* e = path + 1;
275    int rest = strlen(e);
276    while(rest >= 0)
277    {
278      int len;
279      char* s = e;
280      e = strchr(s, '/');
281      if(e)
282        ++e;
283      else
284        e = s + rest + 1;
285      len = e - s;
286      rest -= len;
287      if(len == 1 || (len == 2 && s[0] == '.'))
288      {
289        if(rest >= 0)
290          memmove(s, e, rest + 1);
291        else
292          *s++ = '\0';
293        e = s;
294      }
295      else if(len == 3 && s[0] == '.' && s[1] == '.')
296      {
297        char* ps = s;
298        if(ps - 1 > path) {
299          do
300            --ps;
301          while(ps[-1] != '/');
302        }
303        if(rest >= 0)
304          memmove(ps, e, rest + 1);
305        else
306          *ps++ = '\0';
307        e = ps;
308      }
309    }
310    if(e[-2] == '/')
311      e[-2] = '\0';
312  }
313}
314
315
316/*PAGE
317 *
318 * make_path
319 *
320 * Makes full path given file name, current working directory and root
321 * directory (file scope variable 'root').
322 *
323 * Input parameters:
324 *   cwd  - current working directory
325 *   name - file name
326 *   root (file scope variable) - FTPD root directory
327 *
328 * Output parameters:
329 *   buf - full path
330 *   returns pointer to non-root part of the 'buf', i.e. to first character
331 *           different from '/' after root part.
332 *
333 */
334static char const*
335make_path(char* buf, char const* cwd, char const* name)
336{
337  char* res = NULL;
338
339  int rlen = strlen(root);
340  int clen = strlen(cwd);
341  int nlen = strlen(name);
342  int len = rlen + nlen;
343
344  if (name[0] != '/')
345  {
346    ++len;
347    if (clen > 0)
348      len += clen + 1;
349  }
350
351  if (FTPD_BUFSIZE > len)
352  {
353    char* b = buf;
354    memcpy(b, root, rlen); b += rlen;
355    if (name[0] != '/')
356    {
357      *b++ = '/';
358      if (clen > 0)
359      {
360        memcpy(b, cwd, clen); b += clen;
361        *b++ = '/';
362      }
363    }
364    memcpy(b, name, nlen); b += nlen;
365    *b = '\0';
366
367    res = buf + rlen;
368    while(rlen-- > 0 && res[-1] == '/')
369      --res;
370    squeeze_path(res);
371  }
372
373  return res;
374}
375
376/*
377 * Task pool management routines
378 */
379
380
381/*PAGE
382 *
383 * task_pool_done
384 *
385 * Cleanup task pool.
386 *
387 * Input parameters:
388 *   count - number of entries in task pool to cleanup
389 *
390 * Output parameters:
391 *   NONE
392 *
393 */
394static void
395task_pool_done(int count)
396{
397  int i;
398  for(i = 0; i < count; ++i)
399    rtems_task_delete(task_pool.info[i].tid);
400  if(task_pool.info)
401    free(task_pool.info);
402  if(task_pool.queue)
403    free(task_pool.queue);
404  if(task_pool.mutex != (rtems_id)-1)
405    rtems_semaphore_delete(task_pool.mutex);
406  if(task_pool.sem != (rtems_id)-1)
407    rtems_semaphore_delete(task_pool.sem);
408  task_pool.info = 0;
409  task_pool.queue = 0;
410  task_pool.count = 0;
411  task_pool.sem = -1;
412  task_pool.mutex = -1;
413}
414
415/* Forward declare */
416static void session(rtems_task_argument arg);
417
418/*PAGE
419 *
420 * task_pool_init
421 *
422 * Initialize task pool.
423 *
424 * Input parameters:
425 *   count    - number of entries in task pool to create
426 *   priority - priority tasks are started with
427 *
428 * Output parameters:
429 *   returns 1 on success, 0 on failure.
430 *
431 */
432static int
433task_pool_init(int count, rtems_task_priority priority)
434{
435  int i;
436  rtems_status_code sc;
437  char id = 'a';
438
439  task_pool.count = 0;
440  task_pool.head = task_pool.tail = 0;
441  task_pool.mutex = (rtems_id)-1;
442  task_pool.sem   = (rtems_id)-1;
443
444  sc = rtems_semaphore_create(
445    rtems_build_name('F', 'T', 'P', 'M'),
446    1,
447    RTEMS_DEFAULT_ATTRIBUTES
448    | RTEMS_BINARY_SEMAPHORE
449    | RTEMS_INHERIT_PRIORITY
450    | RTEMS_PRIORITY,
451    RTEMS_NO_PRIORITY,
452    &task_pool.mutex);
453
454  if(sc == RTEMS_SUCCESSFUL)
455    sc = rtems_semaphore_create(
456      rtems_build_name('F', 'T', 'P', 'S'),
457      count,
458      RTEMS_DEFAULT_ATTRIBUTES,
459      RTEMS_NO_PRIORITY,
460      &task_pool.sem);
461
462  if(sc != RTEMS_SUCCESSFUL) {
463    task_pool_done(0);
464    syslog(LOG_ERR, "ftpd: Can not create semaphores");
465    return 0;
466  }
467
468  task_pool.info = (FTPD_SessionInfo_t*)
469    malloc(sizeof(FTPD_SessionInfo_t) * count);
470  task_pool.queue = (FTPD_SessionInfo_t**)
471    malloc(sizeof(FTPD_SessionInfo_t*) * count);
472  if (NULL == task_pool.info || NULL == task_pool.queue)
473  {
474    task_pool_done(0);
475    syslog(LOG_ERR, "ftpd: Not enough memory");
476    return 0;
477  }
478
479  for(i = 0; i < count; ++i)
480  {
481    FTPD_SessionInfo_t *info = &task_pool.info[i];
482    sc = rtems_task_create(rtems_build_name('F', 'T', 'P', id),
483      priority, FTPD_STACKSIZE,
484      RTEMS_PREEMPT | RTEMS_NO_TIMESLICE |
485      RTEMS_NO_ASR | RTEMS_INTERRUPT_LEVEL(0),
486      RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
487      &info->tid);
488    if (sc == RTEMS_SUCCESSFUL)
489    {
490      sc = rtems_task_start(
491        info->tid, session, (rtems_task_argument)info);
492      if (sc != RTEMS_SUCCESSFUL)
493        task_pool_done(i);
494    }
495    else
496      task_pool_done(i + 1);
497    if (sc != RTEMS_SUCCESSFUL)
498    {
499      syslog(LOG_ERR, "ftpd: Could not create/start FTPD session: %s",
500        rtems_status_text(sc));
501      return 0;
502    }
503    task_pool.queue[i] = task_pool.info + i;
504    info->ctrl_fp = NULL;
505    info->socket  = -1;
506    if (++id > 'z')
507      id = 'a';
508  }
509  task_pool.count = count;
510  return 1;
511}
512
513/*PAGE
514 *
515 * task_pool_obtain
516 *
517 * Obtain free task from task pool.
518 *
519 * Input parameters:
520 *   NONE
521 *
522 * Output parameters:
523 *   returns pointer to the corresponding SessionInfo structure on success,
524 *           NULL if there are no free tasks in the pool.
525 *
526 */
527static FTPD_SessionInfo_t*
528task_pool_obtain()
529{
530  FTPD_SessionInfo_t* info = 0;
531  rtems_status_code sc;
532  sc = rtems_semaphore_obtain(task_pool.sem, RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT);
533  if (sc == RTEMS_SUCCESSFUL)
534  {
535    rtems_semaphore_obtain(task_pool.mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
536    info = task_pool.queue[task_pool.head];
537    if(++task_pool.head >= task_pool.count)
538      task_pool.head = 0;
539    info->socket = -1;
540    info->ctrl_fp = NULL;
541    rtems_semaphore_release(task_pool.mutex);
542  }
543  return info;
544}
545
546/*PAGE
547 *
548 * task_pool_release
549 *
550 * Return task obtained by 'obtain()' back to the task pool.
551 *
552 * Input parameters:
553 *   info  - pointer to corresponding SessionInfo structure.
554 *
555 * Output parameters:
556 *   NONE
557 *
558 */
559static void
560task_pool_release(FTPD_SessionInfo_t* info)
561{
562  rtems_semaphore_obtain(task_pool.mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
563  task_pool.queue[task_pool.tail] = info;
564  if(++task_pool.tail >= task_pool.count)
565    task_pool.tail = 0;
566  rtems_semaphore_release(task_pool.mutex);
567  rtems_semaphore_release(task_pool.sem);
568}
569
570/*
571 * End of task pool routines
572 */
573
574/*PAGE
575 *
576 * close_socket
577 *
578 * Close socket.
579 *
580 * Input parameters:
581 *   s - socket descriptor to be closed.
582 *
583 * Output parameters:
584 *   returns 1 on success, 0 on failure
585 *
586 */
587static int
588close_socket(int s)
589{
590  if (0 <= s)
591  {
592    if (0 != close(s))
593    {
594      shutdown(s, 2);
595      if (0 != close(s))
596        return 0;
597    }
598  }
599  return 1;
600}
601
602/*PAGE
603 *
604 * close_stream
605 *
606 * Close data stream of session.
607 *
608 * Input parameters:
609 *   info - corresponding SessionInfo structure
610 *
611 * Output parameters:
612 *   NONE
613 *
614 */
615static void
616close_stream(FTPD_SessionInfo_t* info)
617{
618  if (NULL != info->ctrl_fp)
619  {
620    if (0 != fclose(info->ctrl_fp))
621    {
622      syslog(LOG_ERR, "ftpd: Could not close control stream: %s", serr());
623    }
624    else
625      info->socket = -1;
626  }
627
628  if (!close_socket(info->socket))
629    syslog(LOG_ERR, "ftpd: Could not close control socket: %s", serr());
630
631  info->ctrl_fp = NULL;
632  info->socket = -1;
633}
634
635
636/**************************************************************************
637 * Function: send_reply                                                   *
638 **************************************************************************
639 * Description:                                                           *
640 *                                                                        *
641 *    This procedure sends a reply to the client via the control          *
642 *    connection.                                                         *
643 *                                                                        *
644 *                                                                        *
645 * Inputs:                                                                *
646 *                                                                        *
647 *    int  code  - The 3-digit reply code.                                *
648 *    char *text - Reply text.                                            *
649 *                                                                        *
650 * Output:                                                                *
651 *                                                                        *
652 *    none                                                                *
653 *                                                                        *
654 *************************************************************************/
655static void
656send_reply(FTPD_SessionInfo_t  *info, int code, char *text)
657{
658  char const* s;
659
660
661  /***********************************************************************
662   * code must be a 3-digit number.
663   **********************************************************************/
664  if ((code < 100) || (code > 999))
665  {
666    syslog(LOG_ERR, "ftpd: Code not 3-digits.");
667    return;
668  }
669
670  /***********************************************************************
671   * If a text reply exists, add it to the reply data.
672   **********************************************************************/
673  s  = (info->xfer_mode == TYPE_A) ? "\r" : "";
674  if (text != NULL)
675    fprintf(info->ctrl_fp, "%d %.70s%s\n", code, text, s);
676  else
677    fprintf(info->ctrl_fp, "%d%s\n", code, s);
678  fflush(info->ctrl_fp);
679}
680
681
682/*PAGE
683 *
684 * send_mode_reply
685 *
686 * Sends BINARY/ASCII reply string depending on current transfer mode.
687 *
688 * Input parameters:
689 *   info - corresponding SessionInfo structure
690 *
691 * Output parameters:
692 *   NONE
693 *
694 */
695static void
696send_mode_reply(FTPD_SessionInfo_t *info)
697{
698  if(info->xfer_mode == TYPE_I)
699    send_reply(info, 150, "Opening BINARY mode data connection.");
700  else
701    send_reply(info, 150, "Opening ASCII mode data connection.");
702}
703
704/*PAGE
705 *
706 * data_socket
707 *
708 * Create data socket for session.
709 *
710 * Input parameters:
711 *   info - corresponding SessionInfo structure
712 *
713 * Output parameters:
714 *   returns socket descriptor, or -1 if failure
715 *
716 */
717static int
718data_socket(FTPD_SessionInfo_t *info)
719{
720  int s = socket(PF_INET, SOCK_STREAM, 0);
721  if(0 > s)
722    send_reply(info, 420, "Server error - could not create socket.");
723  else if(0 > connect(s, (struct sockaddr *)&info->data_addr,
724    sizeof(struct sockaddr)))
725  {
726    send_reply(info, 420, "Server error - could not connect socket.");
727    close_socket(s);
728    s = -1;
729  }
730  return s;
731}
732
733/**************************************************************************
734 * Function: command_retrieve                                             *
735 **************************************************************************
736 * Description:                                                           *
737 *                                                                        *
738 *    This performs the "RETR" command.  A data connection must already   *
739 *    be open (via the "PORT" command.)  Here, we send the data to the    *
740 *    connection.                                                         *
741 *                                                                        *
742 *                                                                        *
743 * Inputs:                                                                *
744 *                                                                        *
745 *    char *filename   - Source filename.                                 *
746 *                                                                        *
747 * Output:                                                                *
748 *                                                                        *
749 *    int  - 0 for reply sent.                                            *
750 *           1 for no reply sent.                                         *
751 *                                                                        *
752 *************************************************************************/
753static int
754command_retrieve(FTPD_SessionInfo_t  *info, char *filename)
755{
756  int                 s = -1;
757  int                 n;
758  int                 fd = -1;
759  char                buf[FTPD_DATASIZE];
760  int                 res = 0;
761
762  char const* r = make_path(buf, info->cwd, filename);
763
764  if (NULL == r || 0 > (fd = open(buf, O_RDONLY)))
765  {
766    send_reply(info, 450, "Error opening file.");
767    return(res);
768  }
769
770  send_mode_reply(info);
771
772  /***********************************************************************
773   * Connect to the data connection (PORT made in an earlier PORT call).
774   **********************************************************************/
775  s = data_socket(info);
776  if (0 <= s)
777  {
778    /***********************************************************************
779     * Send the data over the ether.
780     **********************************************************************/
781    while ((n = read(fd, buf, FTPD_DATASIZE)) > 0)
782    {
783      send(s, buf, n, 0);
784    }
785
786    if (0 == n)
787    {
788      if (0 == close(fd))
789      {
790        fd = -1;
791        res = 1;
792      }
793    }
794  }
795
796  if (0 == res)
797    send_reply(info, 450, "Retrieve failed.");
798  else
799    send_reply(info, 210, "File sent successfully.");
800
801  if (-1 != fd)
802    close(fd);
803
804  if (!close_socket(s))
805    syslog(LOG_ERR, "ftpd: Error closing data socket");
806
807  return(res);
808}
809
810
811/**************************************************************************
812 * Function: command_store                                                *
813 **************************************************************************
814 * Description:                                                           *
815 *                                                                        *
816 *    This performs the "STOR" command.  A data connection must already   *
817 *    be open (via the "PORT" command.)  Here, we get the data from the   *
818 *    connection and figure out what to do with it.                       *
819 *                                                                        *
820 *                                                                        *
821 * Inputs:                                                                *
822 *                                                                        *
823 *    char *filename   - Destination filename.                            *
824 *                                                                        *
825 * Output:                                                                *
826 *                                                                        *
827 *    int  - 0 for success.                                               *
828 *           1 for failure.                                               *
829 *                                                                        *
830 *************************************************************************/
831static int
832command_store(FTPD_SessionInfo_t *info, char *filename)
833{
834  int                    s;
835  int                    n;
836  unsigned long          size = 0;
837  struct rtems_ftpd_hook *usehook = NULL;
838  char                   buf[FTPD_DATASIZE];
839
840  int null = !strcmp("/dev/null", filename);
841
842  if(!null)
843  {
844    if (NULL == make_path(buf, info->cwd, filename))
845    {
846      send_reply(info, 450, "Error creating file.");
847      return(0);
848    }
849  }
850
851  send_mode_reply(info);
852
853  s = data_socket(info);
854  if(0 > s)
855  {
856    return(1);
857  }
858
859
860  /***********************************************************************
861   * File: "/dev/null" just throws the data away.
862   * Otherwise, search our list of hooks to see if we need to do something
863   *   special.
864   * OSV: FIXME: this is hack. Using /dev/null filesystem entry would be
865   *             better. However, it's not clear how to handle root directory
866   *             other than '/' then.
867   **********************************************************************/
868  if (null)
869  {
870    while ((n = read(s, buf, FTPD_DATASIZE)) > 0);
871  }
872  else if (rtems_ftpd_configuration.hooks != NULL)
873  {
874    struct rtems_ftpd_hook *hook;
875    int i;
876
877    i = 0;
878    hook = &rtems_ftpd_configuration.hooks[i++];
879    while (hook->filename != NULL)
880    {
881      if (!strcmp(hook->filename, buf))
882      {
883        usehook = hook;
884        break;
885      }
886      hook = &rtems_ftpd_configuration.hooks[i++];
887    }
888  }
889
890  if (usehook != NULL)
891  {
892    /*
893     * OSV: FIXME: Small buffer could be used and hook routine
894     * called multiple times instead. Alternatively, the support could be
895     * removed entirely in favor of configuring RTEMS pseudo-device with
896     * given name.
897     */
898
899    char                *bigBufr;
900    size_t filesize = rtems_ftpd_configuration.max_hook_filesize + 1;
901
902    /***********************************************************************
903     * Allocate space for our "file".
904     **********************************************************************/
905    bigBufr = (char *)malloc(filesize);
906    if (bigBufr == NULL)
907    {
908      send_reply(info, 440, "Server error - malloc fail.");
909      return(1);
910    }
911
912    /***********************************************************************
913     * Retrieve the file into our buffer space.
914     **********************************************************************/
915    size = 0;
916    while ((n = read(s, bigBufr + size, filesize - size)) > 0)
917    {
918      size += n;
919    }
920    if (size >= filesize)
921    {
922      send_reply(info, 440, "Server error - Buffer size exceeded.");
923      free(bigBufr);
924      close_socket(s);
925      return(1);
926    }
927    close_socket(s);
928
929    /***********************************************************************
930     * Call our hook.
931     **********************************************************************/
932    if ((usehook->hook_function)(bigBufr, size) == 0)
933    {
934      send_reply(info, 210, "File transferred successfully.");
935    }
936    else
937    {
938      send_reply(info, 440, "File transfer failed.");
939    }
940    free(bigBufr);
941  }
942  else
943  {
944    int fd =
945      creat(buf, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
946
947    if (0 > fd)
948    {
949      send_reply(info, 450, "Error creating file.");
950      close_socket(s);
951      return(0);
952    }
953
954    while ((n = read(s, buf, FTPD_DATASIZE)) > 0)
955    {
956      if (0 > write(fd, buf, n))
957      {
958        send_reply(info, 450, "Error during write.");
959        close(fd);
960        close_socket(s);
961        return(1);
962      }
963    }
964    if (0 > close(fd))
965    {
966      send_reply(info, 450, "Error during write.");
967      close_socket(s);
968      return(1);
969    }
970    close_socket(s);
971    send_reply(info, 226, "Transfer complete.");
972  }
973
974  return(0);
975}
976
977
978/*PAGE
979 *
980 * send_dirline
981 *
982 * Sends one line of LIST command reply corresponding to single file.
983 *
984 * Input parameters:
985 *   s - socket descriptor to send data to
986 *   wide - if 0, send only file name. If not 0, send 'stat' info as well in
987 *          "ls -l" format.
988 *   curTime - current time
989 *   path - path to be prepended to what is given by 'add'
990 *   add  - path to be appended to what is given by 'path', the resulting path
991 *          is then passed to 'stat()' routine
992 *   name - file name to be reported in output
993 *   buf  - buffer for temporary data
994 *
995 * Output parameters:
996 *   NONE
997 *
998 */
999static void
1000send_dirline(int s, int wide, time_t curTime, char const* path,
1001  char const* add, char const* fname, char* buf)
1002{
1003  if(wide)
1004  {
1005    struct stat stat_buf;
1006
1007    int plen = strlen(path);
1008    int alen = strlen(add);
1009    if(plen == 0)
1010    {
1011      buf[plen++] = '/';
1012      buf[plen] = '\0';
1013    }
1014    else
1015    {
1016      strcpy(buf, path);
1017      if(alen > 0 && buf[plen - 1] != '/')
1018      {
1019        buf[plen++] = '/';
1020        if(plen >= FTPD_BUFSIZE)
1021          return;
1022        buf[plen] = '\0';
1023      }
1024    }
1025    if(plen + alen >= FTPD_BUFSIZE)
1026      return;
1027    strcpy(buf + plen, add);
1028
1029    if (stat(buf, &stat_buf) == 0)
1030    {
1031      int len;
1032      struct tm bt;
1033      time_t tf = stat_buf.st_mtime;
1034      enum { SIZE = 80 };
1035      enum { SIX_MONTHS = 365*24*60*60/2 };
1036      char timeBuf[SIZE];
1037      gmtime_r(&tf, &bt);
1038      if(curTime > tf + SIX_MONTHS || tf > curTime + SIX_MONTHS)
1039        strftime (timeBuf, SIZE, "%b %d  %Y", &bt);
1040      else
1041        strftime (timeBuf, SIZE, "%b %d %H:%M", &bt);
1042
1043      len = snprintf(buf, FTPD_BUFSIZE,
1044        "%c%c%c%c%c%c%c%c%c%c  1 %5d %5d %11u %s %s\r\n",
1045        (S_ISLNK(stat_buf.st_mode)?('l'):
1046          (S_ISDIR(stat_buf.st_mode)?('d'):('-'))),
1047        (stat_buf.st_mode & S_IRUSR)?('r'):('-'),
1048        (stat_buf.st_mode & S_IWUSR)?('w'):('-'),
1049        (stat_buf.st_mode & S_IXUSR)?('x'):('-'),
1050        (stat_buf.st_mode & S_IRGRP)?('r'):('-'),
1051        (stat_buf.st_mode & S_IWGRP)?('w'):('-'),
1052        (stat_buf.st_mode & S_IXGRP)?('x'):('-'),
1053        (stat_buf.st_mode & S_IROTH)?('r'):('-'),
1054        (stat_buf.st_mode & S_IWOTH)?('w'):('-'),
1055        (stat_buf.st_mode & S_IXOTH)?('x'):('-'),
1056        (int)stat_buf.st_uid,
1057        (int)stat_buf.st_gid,
1058        (int)stat_buf.st_size,
1059        timeBuf,
1060        fname
1061      );
1062
1063      send(s, buf, len, 0);
1064    }
1065  }
1066  else
1067  {
1068    int len = snprintf(buf, FTPD_BUFSIZE, "%s\r\n", fname);
1069    send(s, buf, len, 0);
1070  }
1071}
1072
1073/**************************************************************************
1074 * Function: command_list                                      *
1075 **************************************************************************
1076 * Description:                                                           *
1077 *                                                                        *
1078 *    Sends a file list through a data connection.  The data              *
1079 *    connection must have already been opened with the "PORT" command.   *
1080 *                                                                        *
1081 *                                                                        *
1082 * Inputs:                                                                *
1083 *                                                                        *
1084 *    char *fname  - File (or directory) to list.                         *
1085 *                                                                        *
1086 * Output:                                                                *
1087 *                                                                        *
1088 *    none                                                                *
1089 *                                                                        *
1090 *************************************************************************/
1091static void
1092command_list(FTPD_SessionInfo_t *info, char const *fname, int wide)
1093{
1094  int                 s;
1095  DIR                 *dirp = 0;
1096  struct dirent       *dp = 0;
1097  struct stat         stat_buf;
1098  char                buf[FTPD_BUFSIZE];
1099  char                path[FTPD_BUFSIZE];
1100  time_t curTime;
1101  char const* res;
1102  char const* cwd = info->cwd;
1103
1104  send_reply(info, 150, "Opening ASCII mode data connection for LIST.");
1105
1106  s = data_socket(info);
1107  if(0 > s)
1108  {
1109    syslog(LOG_ERR, "ftpd: Error connecting to data socket.");
1110    return;
1111  }
1112
1113  if(fname[0] == '\0')
1114    fname = ".";
1115
1116  res = make_path(path, cwd, fname);
1117
1118  if (NULL == res || 0 > stat(path, &stat_buf))
1119  {
1120    snprintf(buf, FTPD_BUFSIZE,
1121      "%s: No such file or directory.\r\n", fname);
1122    send(s, buf, strlen(buf), 0);
1123  }
1124  else if (S_ISDIR(stat_buf.st_mode) && (NULL == (dirp = opendir(path))))
1125  {
1126    snprintf(buf, FTPD_BUFSIZE,
1127      "%s: Can not open directory.\r\n", fname);
1128    send(s, buf, strlen(buf), 0);
1129  }
1130  else
1131  {
1132    time(&curTime);
1133    if(!dirp)
1134      send_dirline(s, wide, curTime, path, "", fname, buf);
1135    else {
1136      /* FIXME: need "." and ".." only when '-a' option is given */
1137      send_dirline(s, wide, curTime, path, "", ".", buf);
1138      if(!strcmp(path, root))
1139        send_dirline(s, wide, curTime, path, "", "..", buf);
1140      else
1141        send_dirline(s, wide, curTime, path, "..", "..", buf);
1142      while ((dp = readdir(dirp)) != NULL)
1143        send_dirline(s, wide, curTime, path, dp->d_name, dp->d_name, buf);
1144    }
1145  }
1146
1147  if(dirp)
1148    closedir(dirp);
1149  close_socket(s);
1150  send_reply(info, 226, "Transfer complete.");
1151}
1152
1153
1154/*PAGE
1155 *
1156 * rtems_ftpd_cwd
1157 *
1158 * Change current working directory. We use 'chdir' here only to validate the
1159 * new directory. We keep track of current working directory ourselves because
1160 * current working directory in RTEMS isn't thread local, but we need it to be
1161 * session local.
1162 *
1163 * Input parameters:
1164 *   info - corresponding SessionInfo structure
1165 *   dir  - directory name passed in CWD command
1166 *
1167 * Output parameters:
1168 *   info->cwd is set to new CWD value.
1169 *
1170 */
1171static void
1172rtems_ftpd_cwd(FTPD_SessionInfo_t  *info, char *dir)
1173{
1174  char buf[FTPD_BUFSIZE];
1175  char const* cwd = make_path(buf, info->cwd, dir);
1176  if(cwd && chdir(buf) == 0)
1177  {
1178    send_reply(info, 250, "CWD command successful.");
1179    strcpy(info->cwd, cwd);
1180  }
1181  else
1182  {
1183    send_reply(info, 550, "CWD command failed.");
1184  }
1185}
1186
1187
1188
1189/*PAGE
1190 *
1191 * command_mdtm
1192 *
1193 * Handle FTP MDTM command
1194 *
1195 * Input parameters:
1196 *   info - corresponding SessionInfo structure
1197 *   fname - file name passed in MDTM command
1198 *
1199 * Output parameters:
1200 *   info->cwd is set to new CWD value.
1201 *
1202 */
1203static void
1204command_mdtm(FTPD_SessionInfo_t  *info, char const* fname)
1205{
1206  struct stat stbuf;
1207  char buf[FTPD_BUFSIZE];
1208
1209  if(*fname == '\0')
1210    fname = ".";
1211
1212  if (stat(fname, &stbuf) < 0)
1213  {
1214    snprintf(buf, FTPD_BUFSIZE, "%s: %s.", fname, serr());
1215    send_reply(info, 550, buf);
1216  }
1217  else
1218  {
1219    struct tm *t = gmtime(&stbuf.st_mtime);
1220    snprintf(buf, FTPD_BUFSIZE, "%04d%02d%02d%02d%02d%02d",
1221      1900 + t->tm_year,
1222      t->tm_mon+1, t->tm_mday,
1223      t->tm_hour, t->tm_min, t->tm_sec);
1224    send_reply(info, 213, buf);
1225  }
1226}
1227
1228/**************************************************************************
1229 * Function: command_port                                                 *
1230 **************************************************************************
1231 * Description:                                                           *
1232 *                                                                        *
1233 *    This procedure opens up a data port given the IP address of the     *
1234 *    remote machine and the port on the remote machine.  This connection *
1235 *    will then be used to transfer data between the hosts.               *
1236 *                                                                        *
1237 *                                                                        *
1238 * Inputs:                                                                *
1239 *                                                                        *
1240 *    char *bufr - Arguments to the "PORT" command.                       *
1241 *                                                                        *
1242 *                                                                        *
1243 * Output:                                                                *
1244 *                                                                        *
1245 *    none                                                                *
1246 *                                                                        *
1247 *************************************************************************/
1248static void
1249command_port(FTPD_SessionInfo_t *info, char *bufr)
1250{
1251  char                *ip;
1252  char                *port;
1253  int                 ip0, ip1, ip2, ip3, port0, port1;
1254
1255  sscanf(bufr, "%d,%d,%d,%d,%d,%d", &ip0, &ip1, &ip2, &ip3, &port0, &port1);
1256  ip = (char *)&(info->data_addr.sin_addr);
1257  ip[0] = ip0 & 0xff;
1258  ip[1] = ip1 & 0xff;
1259  ip[2] = ip2 & 0xff;
1260  ip[3] = ip3 & 0xff;
1261  port = (char *)&(info->data_addr.sin_port);
1262  port[0] = port0 & 0xff;
1263  port[1] = port1 & 0xff;
1264  info->data_addr.sin_family = AF_INET;
1265}
1266
1267
1268/*PAGE
1269 *
1270 * skip_options
1271 *
1272 * Utility routine to skip options (if any) from input command.
1273 *
1274 * Input parameters:
1275 *   p  - pointer to pointer to command
1276 *
1277 * Output parameters:
1278 *   p  - is changed to point to first non-option argument
1279 *
1280 */
1281static void
1282skip_options(char **p)
1283{
1284  char* buf = *p;
1285  while(1) {
1286    while(*buf == ' ')
1287      ++buf;
1288    if(*buf == '-') {
1289      if(*++buf == '-') { /* `--' should terminate options */
1290        ++buf;
1291        while(*buf == ' ')
1292          ++buf;
1293        break;
1294      }
1295      while(*buf != ' ' && *buf != '\0')
1296        ++buf;
1297    }
1298    else
1299      break;
1300  }
1301  *p = buf;
1302}
1303
1304/**************************************************************************
1305 * Function: parse_command                                     *
1306 **************************************************************************
1307 * Description:                                                           *
1308 *                                                                        *
1309 *    Here, we parse the commands that have come through the control      *
1310 *    connection.                                                         *
1311 *                                                                        *
1312 * FIXME: This section is somewhat of a hack.  We should have a better    *
1313 *        way to parse commands.                                          *
1314 *                                                                        *
1315 * Inputs:                                                                *
1316 *                                                                        *
1317 *    char *bufr     - Pointer to the buffer which contains the command   *
1318 *                     text.                                              *
1319 *                                                                        *
1320 * Output:                                                                *
1321 *                                                                        *
1322 *    none                                                                *
1323 *                                                                        *
1324 *************************************************************************/
1325static void
1326parse_command(FTPD_SessionInfo_t *info, char *bufr)
1327{
1328  char fname[FTPD_BUFSIZE];
1329
1330  if (!strncmp("PORT", bufr, 4))
1331  {
1332    send_reply(info, 200, "PORT command successful.");
1333    command_port(info, &bufr[5]);
1334  }
1335  else if (!strncmp("RETR", bufr, 4))
1336  {
1337    sscanf(&bufr[5], "%254s", fname);
1338    command_retrieve(info, fname);
1339  }
1340  else if (!strncmp("STOR", bufr, 4))
1341  {
1342    sscanf(&bufr[5], "%254s", fname);
1343    command_store(info, fname);
1344  }
1345  else if (!strncmp("LIST", bufr, 4))
1346  {
1347    bufr += 4;
1348    skip_options(&bufr);
1349    sscanf(bufr, "%254s", fname);
1350    command_list(info, fname, 1);
1351  }
1352  else if (!strncmp("NLST", bufr, 4))
1353  {
1354    bufr += 4;
1355    skip_options(&bufr);
1356    sscanf(bufr, "%254s", fname);
1357    command_list(info, fname, 0);
1358  }
1359  else if (!strncmp("MDTM", bufr, 4))
1360  {
1361    bufr += 4;
1362    skip_options(&bufr);
1363    sscanf(bufr, "%254s", fname);
1364    command_mdtm(info, fname);
1365  }
1366  else if (!strncmp("USER", bufr, 4))
1367  {
1368    send_reply(info, 230, "User logged in.");
1369  }
1370  else if (!strncmp("SYST", bufr, 4))
1371  {
1372    send_reply(info, 240, FTPD_SYSTYPE);
1373  }
1374  else if (!strncmp("TYPE", bufr, 4))
1375  {
1376    if (bufr[5] == 'I')
1377    {
1378      info->xfer_mode = TYPE_I;
1379      send_reply(info, 200, "Type set to I.");
1380    }
1381    else if (bufr[5] == 'A')
1382    {
1383      /* FIXME: ASCII mode isn't actually supported yet. */
1384      info->xfer_mode = TYPE_A;
1385      send_reply(info, 200, "Type set to A.");
1386    }
1387    else
1388    {
1389      info->xfer_mode = TYPE_I;
1390      send_reply(info, 504, "Type not implemented.  Set to I.");
1391    }
1392  }
1393  else if (!strncmp("PASS", bufr, 4))
1394  {
1395    send_reply(info, 230, "User logged in.");
1396  }
1397  else if (!strncmp("DELE", bufr, 4))
1398  {
1399    sscanf(&bufr[4], "%254s", fname);
1400    if (unlink(fname) == 0)
1401    {
1402      send_reply(info, 257, "DELE successful.");
1403    }
1404    else
1405    {
1406      send_reply(info, 550, "DELE failed.");
1407    }
1408  }
1409  else if (!strncmp("SITE CHMOD", bufr, 10))
1410  {
1411    int mask;
1412
1413    sscanf(&bufr[11], "%o %254s", &mask, fname);
1414    if (chmod(fname, (mode_t)mask) == 0)
1415    {
1416      send_reply(info, 257, "CHMOD successful.");
1417    }
1418    else
1419    {
1420      send_reply(info, 550, "CHMOD failed.");
1421    }
1422  }
1423  else if (!strncmp("RMD", bufr, 3))
1424  {
1425    sscanf(&bufr[4], "%254s", fname);
1426    if (rmdir(fname) == 0)
1427    {
1428      send_reply(info, 257, "RMD successful.");
1429    }
1430    else
1431    {
1432      send_reply(info, 550, "RMD failed.");
1433    }
1434  }
1435  else if (!strncmp("MKD", bufr, 3))
1436  {
1437    sscanf(&bufr[4], "%254s", fname);
1438    if (mkdir(fname, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
1439    {
1440      send_reply(info, 257, "MKD successful.");
1441    }
1442    else
1443    {
1444      send_reply(info, 550, "MKD failed.");
1445    }
1446  }
1447  else if (!strncmp("CWD", bufr, 3))
1448  {
1449    sscanf(&bufr[4], "%254s", fname);
1450    rtems_ftpd_cwd(info, fname);
1451  }
1452  else if (!strncmp("CDUP", bufr, 4))
1453  {
1454    rtems_ftpd_cwd(info, "..");
1455  }
1456  else if (!strncmp("PWD", bufr, 3))
1457  {
1458    char const* cwd = "/";
1459    if(info->cwd[0])
1460      cwd = info->cwd;
1461    snprintf(bufr, FTPD_BUFSIZE,
1462      "\"%s\" is the current directory.", cwd);
1463    send_reply(info, 250, bufr);
1464  }
1465  else
1466  {
1467    send_reply(info, 500, "Unrecognized/unsupported command.");
1468  }
1469}
1470
1471
1472/**************************************************************************
1473 * Function: session                                           *
1474 **************************************************************************
1475 * Description:                                                           *
1476 *                                                                        *
1477 *    This task is started when the FTP daemon gets a service request     *
1478 *    from a remote machine.  Here, we watch for commands that will       *
1479 *    come through the "control" connection.  These commands are then     *
1480 *    parsed and executed until the connection is closed, either          *
1481 *    unintentionally or intentionally with the "QUIT" command.           *
1482 *                                                                        *
1483 *                                                                        *
1484 * Inputs:                                                                *
1485 *                                                                        *
1486 *    rtems_task_argument arg - The daemon task passes the socket         *
1487 *                              which serves as the control connection.   *
1488 *                                                                        *
1489 * Output:                                                                *
1490 *                                                                        *
1491 *    none                                                                *
1492 *                                                                        *
1493 *************************************************************************/
1494static void
1495session(rtems_task_argument arg)
1496{
1497  char cmd[FTPD_BUFSIZE];
1498  FTPD_SessionInfo_t  *info = (FTPD_SessionInfo_t  *)arg;
1499  rtems_event_set set;
1500
1501  while(1) {
1502    rtems_event_receive(FTPD_RTEMS_EVENT, RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT,
1503      &set);
1504
1505    send_reply(info, 220, FTPD_SERVER_MESSAGE);
1506
1507    info->cwd[0] = 0;
1508    info->xfer_mode = TYPE_I;
1509
1510    while (1)
1511    {
1512      if (fgets(cmd, FTPD_BUFSIZE, info->ctrl_fp) == NULL)
1513      {
1514        syslog(LOG_INFO, "ftpd: Connection aborted.");
1515        break;
1516      }
1517
1518      if (!strncmp("QUIT", cmd, 4))
1519      {
1520        send_reply(info, 221, "Goodbye.");
1521        break;
1522      }
1523      else
1524      {
1525        parse_command(info, cmd);
1526      }
1527    }
1528
1529    /* Close connection and put ourselves back into the task pool. */
1530    close_stream(info);
1531    task_pool_release(info);
1532  }
1533}
1534
1535
1536/**************************************************************************
1537 * Function: daemon                                            *
1538 **************************************************************************
1539 * Description:                                                           *
1540 *                                                                        *
1541 *    This task runs in the background forever.  It waits for service     *
1542 *    requests on the FTP port (port 21).  When a request is received,    *
1543 *    it opens a new session to handle those requests until the           *
1544 *    connection is closed.                                               *
1545 *                                                                        *
1546 *                                                                        *
1547 * Inputs:                                                                *
1548 *                                                                        *
1549 *    none                                                                *
1550 *                                                                        *
1551 * Output:                                                                *
1552 *                                                                        *
1553 *    none                                                                *
1554 *                                                                        *
1555 *************************************************************************/
1556static void
1557daemon()
1558{
1559  int                 s;
1560  int                 addrLen;
1561  struct sockaddr_in  remoteAddr;
1562  struct sockaddr_in  localAddr;
1563  char                sessionID;
1564  FTPD_SessionInfo_t  *info = NULL;
1565
1566
1567  sessionID = 'a';
1568
1569  s = socket(PF_INET, SOCK_STREAM, 0);
1570  if (s < 0)
1571    syslog(LOG_ERR, "ftpd: Error creating socket: %s", serr());
1572
1573  localAddr.sin_family      = AF_INET;
1574  localAddr.sin_port        = htons(rtems_ftpd_configuration.port);
1575  localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
1576  memset(localAddr.sin_zero, '\0', sizeof(localAddr.sin_zero));
1577
1578  if (0 > bind(s, (struct sockaddr *)&localAddr, sizeof(localAddr)))
1579    syslog(LOG_ERR, "ftpd: Error binding control socket: %s", serr());
1580
1581  if (0 > listen(s, 1))
1582    syslog(LOG_ERR, "ftpd: Error listening on control socket: %s", serr());
1583
1584  while (1)
1585  {
1586    int ss;
1587    addrLen = sizeof(remoteAddr);
1588    ss = accept(s, (struct sockaddr *)&remoteAddr, &addrLen);
1589    if (0 > ss)
1590    {
1591      syslog(LOG_ERR, "ftpd: Error accepting control connection: %s", serr());
1592    }
1593    else
1594    {
1595      info = task_pool_obtain();
1596      if (NULL == info)
1597      {
1598        close_socket(ss);
1599      }
1600      else
1601      {
1602        info->socket = ss;
1603        if ((info->ctrl_fp = fdopen(info->socket, "r+")) == NULL)
1604        {
1605          syslog(LOG_ERR, "ftpd: fdopen() on socket failed: %s", serr());
1606          close_stream(info);
1607          task_pool_release(info);
1608        }
1609        else
1610        {
1611          /* Wakeup the session task. The task will call task_pool_release
1612             after it closes connection. */
1613          rtems_event_send(info->tid, FTPD_RTEMS_EVENT);
1614        }
1615      }
1616    }
1617  }
1618}
1619
1620
1621/**************************************************************************
1622 * Function: rtems_ftpd_start                                             *
1623 **************************************************************************
1624 * Description:                                                           *
1625 *                                                                        *
1626 *    Here, we start the FTPD task which waits for FTP requests and       *
1627 *    services them.  This procedure returns to its caller once the       *
1628 *    task is started.                                                    *
1629 *                                                                        *
1630 *                                                                        *
1631 * Inputs:                                                                *
1632 *                                                                        *
1633 * Output:                                                                *
1634 *                                                                        *
1635 *    int - RTEMS_SUCCESSFUL on successful start of the daemon.           *
1636 *                                                                        *
1637 *************************************************************************/
1638int
1639rtems_initialize_ftpd()
1640{
1641  rtems_status_code   sc;
1642  rtems_id            tid;
1643  rtems_task_priority priority;
1644  int count;
1645
1646  if (rtems_ftpd_configuration.port == 0)
1647  {
1648    rtems_ftpd_configuration.port = FTPD_CONTROL_PORT;
1649  }
1650
1651  if (rtems_ftpd_configuration.priority == 0)
1652  {
1653    rtems_ftpd_configuration.priority = 40;
1654  }
1655  priority = rtems_ftpd_configuration.priority;
1656
1657  if (rtems_ftpd_configuration.tasks_count <= 0)
1658    rtems_ftpd_configuration.tasks_count = 1;
1659  count = rtems_ftpd_configuration.tasks_count;
1660
1661  if (!task_pool_init(count, priority))
1662  {
1663    syslog(LOG_ERR, "ftpd: Could not initialize task pool.");
1664    return RTEMS_UNSATISFIED;
1665  }
1666
1667  sc = rtems_task_create(rtems_build_name('F', 'T', 'P', 'D'),
1668    priority, FTPD_STACKSIZE,
1669    RTEMS_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR |
1670    RTEMS_INTERRUPT_LEVEL(0),
1671    RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL,
1672    &tid);
1673
1674  if (sc == RTEMS_SUCCESSFUL)
1675  {
1676    sc = rtems_task_start(tid, daemon, 0);
1677    if (sc != RTEMS_SUCCESSFUL)
1678      rtems_task_delete(tid);
1679  }
1680
1681  if (sc != RTEMS_SUCCESSFUL)
1682  {
1683    task_pool_done(count);
1684    syslog(LOG_ERR, "ftpd: Could not create/start FTP daemon: %s",
1685      rtems_status_text(sc));
1686    return RTEMS_UNSATISFIED;
1687  }
1688
1689  root[0] = '\0';
1690  if (
1691    rtems_ftpd_configuration.root &&
1692    strlen(rtems_ftpd_configuration.root) < FTPD_BUFSIZE &&
1693    rtems_ftpd_configuration.root[0] == '/')
1694  {
1695    strcpy(root, rtems_ftpd_configuration.root);
1696    squeeze_path(root);
1697    rtems_ftpd_configuration.root = root;
1698  }
1699
1700  syslog(LOG_INFO, "ftpd: FTP daemon started (%d session%s max)",
1701    count, ((count > 1) ? "s" : ""));
1702  return RTEMS_SUCCESSFUL;
1703}
Note: See TracBrowser for help on using the repository browser.