Subject: Re: ftpd daemon mode
To: Luke Mewburn <lukem@NetBSD.org>
From: Peter Postma <peter@pointless.nl>
List: tech-userlevel
Date: 07/22/2005 15:03:27
--EeQfGwPcQSOJBaQU
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
On Sat, Jul 16, 2005 at 03:52:13PM +1000, Luke Mewburn wrote:
> On Fri, Jun 24, 2005 at 01:19:36PM +0200, Peter Postma wrote:
> | I've added daemon mode (inspired from FreeBSD) to our ftpd, patch is
> | attached. Comments are welcome.
>
> Various consistency tweaks.
> Please examine ftpd and ensure that the rest of your changes
> seem consistent with the coding practice used within.
>
Thanks, I've fixed them all.
>
> Please use sigaction() for consistency with the rest of ftpd(8).
> ("signal(3) Must Die"!)
>
> Something like:
> memset(&sa, 0, sizeof(sa));
> sa.sa_handler = SIG_IGN;
> sa.sa_flags = SA_NOCLDWAIT;
> sigemptyset(&sa.sa_mask);
> (void) sigaction(SIGCHLD, &sa, NULL);
>
> I'm not sure how this will interact with ftpd_popen() and ftpd_pclose().
> I think the subsequent change of SIGCHLD to SIG_DFL later in main()
> (i.e, in the child ftpd) will allow ftpd_pclose()'s waitpid() to DTRT
> without having the parent-daemon-ftpd reap/ignore the status.
>
> I think your patch's behaviour of setting SIGCHLD to the sigchild()
> handler that waitpid()s everything may have had adverse effects
> with ftpd_pclose().
>
> Someone with more knowledge/experience of SIGCHLD semantics in
> this situation may be able to provide enlightenment.
>
I see. But SA_NOCLDWAIT, is that portable enough? (thinking of tnftpd)
I've attached the updated patch, is it ok to commit?
Thanks,
--
Peter Postma
--EeQfGwPcQSOJBaQU
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="ftpd.diff"
Index: ftpd.8
===================================================================
RCS file: /cvsroot/src/libexec/ftpd/ftpd.8,v
retrieving revision 1.74
diff -u -r1.74 ftpd.8
--- ftpd.8 7 Aug 2003 09:46:39 -0000 1.74
+++ ftpd.8 22 Jul 2005 12:40:35 -0000
@@ -63,7 +63,7 @@
.\"
.\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94
.\"
-.Dd February 26, 2003
+.Dd June 23, 2005
.Dt FTPD 8
.Os
.Sh NAME
@@ -72,7 +72,7 @@
Internet File Transfer Protocol server
.Sh SYNOPSIS
.Nm
-.Op Fl dHlqQrsuUwWX
+.Op Fl 46DdHlqQrsuUwWX
.Op Fl a Ar anondir
.Op Fl c Ar confdir
.Op Fl C Ar user
@@ -93,6 +93,14 @@
.Pp
Available options:
.Bl -tag -width Ds
+.It Fl 4
+When
+.Fl D
+is specified, bind to IPv4 addresses only.
+.It Fl 6
+When
+.Fl D
+is specified, bind to IPv6 addresses only.
.It Fl a Ar anondir
Define
.Ar anondir
@@ -128,6 +136,16 @@
.Nm
exits with an exit code of 0 if access would be granted, or 1 otherwise.
This can be useful for testing configurations.
+.It Fl D
+Run as daemon.
+.Nm
+will listen on the default FTP port for incoming connections
+and fork a child for each connection.
+This is lower overhead than starting
+.Nm
+from
+.Xr inetd 8
+and thus might be useful on busy servers to reduce load.
.It Fl d
Debugging information is written to the syslog using a facility of
.Dv LOG_FTP .
Index: ftpd.c
===================================================================
RCS file: /cvsroot/src/libexec/ftpd/ftpd.c,v
retrieving revision 1.166
diff -u -r1.166 ftpd.c
--- ftpd.c 23 Jun 2005 04:20:41 -0000 1.166
+++ ftpd.c 22 Jul 2005 12:40:37 -0000
@@ -140,6 +140,7 @@
#include <limits.h>
#include <netdb.h>
#include <pwd.h>
+#include <poll.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
@@ -181,6 +182,7 @@
volatile sig_atomic_t urgflag;
int data;
+int Dflag;
int sflag;
int stru; /* avoid C keyword */
int mode;
@@ -292,11 +294,13 @@
const char *xferlogname = NULL;
long l;
struct sigaction sa;
+ sa_family_t af = AF_UNSPEC;
connections = 1;
debug = 0;
logging = 0;
pdata = -1;
+ Dflag = 0;
sflag = 0;
dataport = 0;
dopidfile = 1; /* default: DO use a pid file to count users */
@@ -320,9 +324,17 @@
*/
openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
- while ((ch = getopt(argc, argv, "a:c:C:de:h:HlL:P:qQrst:T:uUvV:wWX"))
- != -1) {
+ while ((ch = getopt(argc, argv,
+ "46a:c:C:Dde:h:HlL:P:qQrst:T:uUvV:wWX")) != -1) {
switch (ch) {
+ case '4':
+ af = AF_INET;
+ break;
+
+ case '6':
+ af = AF_INET6;
+ break;
+
case 'a':
anondir = optarg;
break;
@@ -336,6 +348,10 @@
exit(checkaccess(optarg) ? 0 : 1);
/* NOTREACHED */
+ case 'D':
+ Dflag = 1;
+ break;
+
case 'd':
case 'v': /* deprecated */
debug = 1;
@@ -462,6 +478,108 @@
}
curname[0] = '\0';
+ if (Dflag) {
+ int error, fd, i, n, *socks;
+ struct pollfd *fds;
+ struct addrinfo hints, *res, *res0;
+
+ if (daemon(1, 0) == -1) {
+ syslog(LOG_ERR, "failed to daemonize: %m");
+ exit(1);
+ }
+ (void)memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = SA_NOCLDWAIT;
+ sigemptyset(&sa.sa_mask);
+ (void)sigaction(SIGCHLD, &sa, NULL);
+
+ (void)memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = af;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(NULL, "ftp", &hints, &res0);
+ if (error) {
+ syslog(LOG_ERR, "getaddrinfo: %s", gai_strerror(error));
+ exit(1);
+ }
+
+ for (n = 0, res = res0; res != NULL; res = res->ai_next)
+ n++;
+ if (n == 0) {
+ syslog(LOG_ERR, "no addresses available");
+ exit(1);
+ }
+ socks = malloc(n * sizeof(int));
+ fds = malloc(n * sizeof(struct pollfd));
+ if (socks == NULL || fds == NULL) {
+ syslog(LOG_ERR, "malloc: %m");
+ exit(1);
+ }
+
+ for (n = 0, res = res0; res != NULL; res = res->ai_next) {
+ socks[n] = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol);
+ if (socks[n] == -1)
+ continue;
+ (void)setsockopt(socks[n], SOL_SOCKET, SO_REUSEADDR,
+ &on, sizeof(on));
+ if (bind(socks[n], res->ai_addr, res->ai_addrlen)
+ == -1) {
+ (void)close(socks[n]);
+ continue;
+ }
+ if (listen(socks[n], 12) == -1) {
+ (void)close(socks[n]);
+ continue;
+ }
+
+ fds[n].fd = socks[n];
+ fds[n].events = POLLIN;
+ n++;
+ }
+ if (n == 0) {
+ syslog(LOG_ERR, "%m");
+ exit(1);
+ }
+ freeaddrinfo(res0);
+
+ if (pidfile(NULL) == -1)
+ syslog(LOG_ERR, "failed to write a pid file: %m");
+
+ for (;;) {
+ if (poll(fds, n, INFTIM) == -1) {
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "poll: %m");
+ exit(1);
+ }
+ for (i = 0; i < n; i++) {
+ if (fds[i].revents & POLLIN) {
+ fd = accept(fds[i].fd, NULL, NULL);
+ if (fd == -1) {
+ syslog(LOG_ERR, "accept: %m");
+ continue;
+ }
+ switch (fork()) {
+ case -1:
+ syslog(LOG_ERR, "fork: %m");
+ break;
+ case 0:
+ goto child;
+ /* NOTREACHED */
+ }
+ (void)close(fd);
+ }
+ }
+ }
+ child:
+ (void)dup2(fd, STDIN_FILENO);
+ (void)dup2(fd, STDOUT_FILENO);
+ (void)dup2(fd, STDERR_FILENO);
+ for (i = 0; i < n; i++)
+ (void)close(socks[i]);
+ }
+
memset((char *)&his_addr, 0, sizeof(his_addr));
addrlen = sizeof(his_addr.si_su);
if (getpeername(0, (struct sockaddr *)&his_addr.si_su, &addrlen) < 0) {
--EeQfGwPcQSOJBaQU--