* added file logging like wu-ftpd does

This commit is contained in:
interp 2002-04-19 15:50:06 +00:00
parent 48a0a95a4a
commit fb0833078b
3 changed files with 181 additions and 71 deletions

View File

@ -9,19 +9,44 @@
\subsubsection*{Entry points}
\defun {ftpd} {anonymous-home \ovar{port}} {\noreturn}
\defun {ftpd} {anonymous-home \ovar{port \ovar{logfile}}} {\noreturn}
\begin{defundescx}{ftp-inetd} {anonymous-home} {\noreturn}
\ex{ftpd} starts the server, using \semvar{anonymous-home} as the
root directory of the server. Usage of relative paths is not
encouraged. \semvar{port} specifies the port the server is
listening for connections. It defaults to 21.
listening for connections. It defaults to 21. \ex{ftpd} makes a log
entry for each file sent or retrieved. These logs are written to
\semvar{logfile}, if given.
The log format of \ex{ftpd} is the same as the one of
\ex{wuftpd}. The fields are seperated by spaces and contain
following informations:
\codex{Fri Apr 19 17:08:14 2002 4 134.2.2.171 56881 /files.lst b \_ i a nop@ssword ftp 0 *}
\begin{enumerate}
\item Current date and time. This field contains
spaces and is 24 characters long.
\item Transfer time in seconds.
\item Remote host IP (wuftpd puts the name here).
\item File size in bytes
\item Name of file
\item Transfer type: \underline{a}scii or \underline{b}inary (image type).
\item Special action flags. As \ex{ftpd} does not support any special
action, we are always `\ex{\_}' here.
\item File was sent to user (\underline{o}utgoing) or received from user
(\underline{i}ncoming)
\item \underline{A}nonymous access
\item Anonymous ftp password. We do not use anyone.
\item Service name - always \ex{ftp}.
\item Authentication mode (always none = `\ex{0}').
\item Authenticated user ID (always not available = `\ex{*}')
\end{enumerate}
As the procedure does not return, you have to do a \ex{fork} in
order to have a ``real'' daemon:
\codex{(fork (lambda () (ftpd "/data/ftp" 8080)))}
\ex{ftpd-inetd} is the version to be used with a daemon like
\ex{inetd}. If the server is started this way, it handles the
connection through the current standard output and input ports.
order to have a ``real'' daemon: \codex{(fork (lambda () (ftpd
"/data/ftp" 8080)))} \ex{ftpd-inetd} is the version to be used with
a daemon like \ex{inetd}. If the server is started this way, it
handles the connection through the current standard output and input
ports.
\end{defundescx}
\subsubsection*{Examples}

204
ftpd.scm
View File

@ -13,11 +13,12 @@
; following things should be improved:
;
; - GET-command: ftpd reports "Can't open FILENAME for reading" if
; - GET/RETR-command: ftpd reports "Can't open FILENAME for reading" if
; file actually doesn't exist. This is confusing. Reporting
; "FILENAME does not exist" is much better.
(define *logfile* #f) ; file-port to log to like wu-ftpd (analyzable with webalizer)
(define-record session
control-input-port
@ -93,7 +94,62 @@
(log level "handling ~A-command with argument ~S" ; does this ever happen?
command-name (car argument)))))
;; Extended logging like wu.ftpd:
;; Each file up/download is protocolled
; Mon Dec 3 18:52:41 1990 1 wuarchive.wustl.edu 568881 /files.lst.Z a _ o a chris@wugate.wustl.edu ftp 0 *
;
; %.24s %d %s %d %s %c %s %c %c %s %s %d %s
; 1 2 3 4 5 6 7 8 9 10 11 12 13
;
; 1 current time in the form DDD MMM dd hh:mm:ss YYYY
; 2 transfer time in seconds
; 3 remote host name
; 4 file size in bytes
; 5 name of file
; 6 transfer type (a>scii, b>inary)
; 7 special action flags (concatenated as needed):
; C file was compressed
; U file was uncompressed
; T file was tar'ed
; _ no action taken
; 8 file was sent to user (o>utgoing) or received from
; user (i>ncoming)
; 9 accessed anonymously (r>eal, a>nonymous, g>uest) -- mostly for FTP
; 10 local username or, if guest, ID string given
; (anonymous FTP password)
; 11 service name ('ftp', other)
; 12 authentication method (bitmask)
; 0 none
; 1 RFC931 Authentication
; 13 authenticated user id (if available, '*' otherwise)
;
(define file-log
(let ((file-log-lock (make-lock)))
(lambda (start-transfer-seconds info full-path direction)
(if *logfile*
(begin
(obtain-lock file-log-lock)
(format *logfile* "~A ~A ~A ~A ~A ~A _ ~A a nop@ssword ftp 0 *~%"
(format-date "~a ~b ~d ~H:~M:~S ~Y" (date)) ; current date and time
(- (current-seconds) start-transfer-seconds) ; transfer time in secs
(socket-address->string (socket-remote-address (session-data-socket)) #f) ; remote host name
(file-info:size info) ; file size in bytes
full-path ; name of file
(case (session-type)
((ascii) "a")
((image) "b")
(else "?")) ; transfer type
direction ; incoming / outgoing file
; anonymous access
; password (no password given)
; service name
; authentication mode
; authenticated user id'
)
(force-output *logfile*)
(release-lock file-log-lock))))))
;;; CONVERTERS ------------------------------------------------
(define (protocol-family->string protocol-family)
(cond ((= protocol-family protocol-family/unspecified)
@ -113,40 +169,49 @@
(socket:outport socket)))
(define (socket-address->string socket-address)
(call-with-values
(lambda () (socket-address->internet-address socket-address))
(lambda (host-address service-port)
(format #f "~A:~A"
(format-internet-host-address host-address)
(format-port service-port)))))
(define (socket-address->string socket-address . with-port?)
(let ((with-port? (optional with-port? #t)))
(receive (host-address service-port)
(socket-address->internet-address socket-address)
(if with-port?
(format #f "~A:~A"
(format-internet-host-address host-address)
(format-port service-port))
(format #f "~A"
(format-internet-host-address host-address))))))
;;; ftpd -------------------------------------------------------
(define (ftpd anonymous-home . maybe-port)
(let ((port (optional maybe-port 21)))
(with-syslog-destination
"ftpd"
#f
#f
#f
(lambda ()
(log (syslog-level notice)
"starting daemon on port ~D with ~S as anonymous home"
port (expand-file-name anonymous-home (cwd)))
(define (ftpd anonymous-home . maybe-args)
(let-optionals
maybe-args
((port 21)
(logfile #f))
(display "ping!\n")
(if logfile
(set! *logfile* (open-output-file logfile (bitwise-ior open/create open/append))))
(with-syslog-destination
"ftpd"
#f
#f
#f
(lambda ()
(log (syslog-level notice)
"starting daemon on port ~D with ~S as anonymous home and logfile ~S"
port (expand-file-name anonymous-home (cwd)) logfile)
(bind-listen-accept-loop
protocol-family/internet
(lambda (socket address)
(let ((remote-address (socket-address->string address)))
(set-ftp-socket-options! socket)
(fork-thread
(spawn-to-handle-connection socket
address
anonymous-home
port
remote-address))))
port)))))
(bind-listen-accept-loop
protocol-family/internet
(lambda (socket address)
(let ((remote-address (socket-address->string address)))
(set-ftp-socket-options! socket)
(fork-thread
(spawn-to-handle-connection socket
address
anonymous-home
port
remote-address))))
port)))))
(define (spawn-to-handle-connection socket address anonymous-home port remote-address)
(lambda ()
@ -193,7 +258,16 @@
"closing socket ~S" socket-string)
(close-socket socket))))))))))
(define (ftpd-inetd anonymous-home)
(define (ftpd-inetd anonymous-home . maybe-logfile)
(let ((logfile (optional maybe-logfile)))
(with-errno-handler
((errno packet)
(else
(format (current-error-port) "[ftpd] Warning: Unable to write logs to ~S. Logging is now made to (current-error-port).~%[ftpd] (To disable logging at all, either leave the logfile argument or give #f as logfile)~%")
(set! *logfile* (current-error-port))))
(if logfile
(set! *logfile* (open-output-file logfile (bitwise-ior open/create open/append))))))
(with-syslog-destination
"ftpd"
#f
@ -883,7 +957,8 @@
(format #f "Can't open \"~A\" for reading."
path))))
(lambda ()
(let ((info (file-info full-path)))
(let ((info (file-info full-path))
(start-transfer-seconds (current-seconds)))
(if (not (eq? 'regular (file-info:type info)))
(begin
(log (syslog-level info) "rejecting RETR-command as ~S is not a regular file (450)"
@ -911,7 +986,11 @@
(log (syslog-level debug) "sending is from port ~S" file-port)
(copy-port->port-ascii
file-port
(socket:outport (session-data-socket))))))))))))))
(socket:outport (session-data-socket)))))
(file-log start-transfer-seconds info full-path "o"))))))))))
(define (current-seconds)
(receive (time ticks) (time+ticks) time))
(define (handle-stor path)
(log-command (syslog-level info) "STOR" path)
@ -926,32 +1005,35 @@
(log (syslog-level debug) "replying error for file ~S (maybe reason: ~S)" path reason)
(signal-error! 550 (format #f "Can't open \"~A\" for writing." path))))
(lambda ()
(call-with-output-file full-path
(lambda (file-port)
(with-data-connection
(lambda ()
(let ((inport (socket:inport (session-data-socket))))
(case (session-type)
((image)
(log (syslog-level notice)
"storing data to ~S (binary mode)"
full-path)
(log (syslog-level debug)
"storing comes from socket-inport ~S (binary-mode)"
inport)
(copy-port->port-binary
(socket:inport (session-data-socket))
file-port))
((ascii)
(log (syslog-level notice)
"storing data to ~S (ascii-mode)"
full-path)
(log (syslog-level debug)
"storing comes from socket-inport ~S (ascii-mode)"
inport)
(copy-ascii-port->port
(socket:inport (session-data-socket))
file-port))))))))))))
(let ((start-transfer-seconds (current-seconds)))
(call-with-output-file full-path
(lambda (file-port)
(with-data-connection
(lambda ()
(let ((inport (socket:inport (session-data-socket))))
(case (session-type)
((image)
(log (syslog-level notice)
"storing data to ~S (binary mode)"
full-path)
(log (syslog-level debug)
"storing comes from socket-inport ~S (binary-mode)"
inport)
(copy-port->port-binary
(socket:inport (session-data-socket))
file-port))
((ascii)
(log (syslog-level notice)
"storing data to ~S (ascii-mode)"
full-path)
(log (syslog-level debug)
"storing comes from socket-inport ~S (ascii-mode)"
inport)
(copy-ascii-port->port
(socket:inport (session-data-socket))
file-port)))
(file-log start-transfer-seconds (file-info full-path) full-path "i")
))))))))))
(define (assemble-path current-directory path)
(log (syslog-level debug) "assembling path ~S"
@ -1133,7 +1215,7 @@
; Version
(define *ftpd-version* "$Revision: 1.32 $")
(define *ftpd-version* "$Revision: 1.33 $")
(define (copy-port->port-binary input-port output-port)
(let ((buffer (make-string *window-size*)))

View File

@ -537,18 +537,21 @@
ftpd-inetd))
(define-structure ftpd ftpd-interface
(open scheme
(open scsh
conditions handle signals
structure-refs
handle-fatal-error
scsh
scheme
threads threads-internal ; last one to get CURRENT-THREAD
locks
thread-fluids ; fork-thread
fluids
string-lib
big-util
defrec-package
crlf-io strings ls
let-opt
receiving ; RECEIVE
format-net) ; pretty print of internet-addresses
(access big-scheme)
(files ftpd))