Subject: Re: ftpd daemon mode
To: None <tech-userlevel@NetBSD.org, lukem@NetBSD.org>
From: Luke Mewburn <lukem@NetBSD.org>
List: tech-userlevel
Date: 07/16/2005 15:52:13
--GYaKytDE8aa4+VVK
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

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.


  | --- ftpd.8	7 Aug 2003 09:46:39 -0000	1.74
  | +++ ftpd.8	24 Jun 2005 11:08:50 -0000
  | @@ -128,6 +136,16 @@
  |  .Nm
  |  exits with an exit code of 0 if access would be granted, or 1 otherwis=
e.
  |  This can be useful for testing configurations.
  | +.It Fl D
  | +Run as daemon,

End line with `.' not `,'.


  | --- ftpd.c	23 Jun 2005 04:20:41 -0000	1.166
  | +++ ftpd.c	24 Jun 2005 11:08:53 -0000
  | @@ -462,6 +479,103 @@
  |  	}
  |  	curname[0] =3D '\0';
  | =20
  | +	if (Dflag) {
  | +		int error, fd, i, n, *socks;
  | +		struct pollfd *fds;
  | +		struct addrinfo hints, *res, *res0;
  | +
  | +		if (daemon(1, 0) =3D=3D -1) {
  | +			syslog(LOG_ERR, "failed to daemonize: %m");
  | +			exit(1);
  | +		}
  | +		(void)signal(SIGCHLD, sigchild);

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 =3D SIG_IGN;
		sa.sa_flags =3D 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.



  | +		(void)memset(&hints, 0, sizeof(hints));
  | +		hints.ai_flags =3D AI_PASSIVE;
  | +		hints.ai_family =3D af;
  | +		hints.ai_socktype =3D SOCK_STREAM;
  | +		error =3D getaddrinfo(NULL, "ftp", &hints, &res0);
  | +		if (error) {
  | +			syslog(LOG_ERR, "getaddrinfo: %s", gai_strerror(error));
  | +			exit(1);
  | +		}
  | +
  | +		for (n =3D 0, res =3D res0; res !=3D NULL; res =3D res->ai_next)
  | +			n++;
  | +		if (n =3D=3D 0) {
  | +			syslog(LOG_ERR, "no addresses available");
  | +			exit(1);
  | +		}
  | +		socks =3D malloc(n * sizeof(int));
  | +		fds =3D malloc(n * sizeof(struct pollfd));
  | +		if (socks =3D=3D NULL || fds =3D=3D NULL) {
  | +			syslog(LOG_ERR, "malloc: %m");
  | +			exit(1);
  | +		}
  | +
  | +		for (n =3D 0, res =3D res0; res !=3D NULL; res =3D res->ai_next) {
  | +			socks[n] =3D socket(res->ai_family, res->ai_socktype,
  | +			    res->ai_protocol);
  | +			if (socks[n] < 0)
  | +				continue;
  | +			(void)setsockopt(socks[n], SOL_SOCKET, SO_REUSEADDR,
  | +			    &on, sizeof(on));
  | +			if (bind(socks[n], res->ai_addr, res->ai_addrlen) < 0) {

Check against =3D=3D -1 not < 0.
(I know ftpd isn't consistent regarding this; I'll clean that up later).


  | +				(void)close(socks[n]);
  | +				continue;
  | +			}
  | +			if (listen(socks[n], 12) < 0) {

Check against =3D=3D -1 not < 0.


  | +				(void)close(socks[n]);
  | +				continue;
  | +			}
  | +
  | +			fds[n].fd =3D socks[n];
  | +			fds[n].events =3D POLLIN;
  | +			n++;
  | +		}
  | +		if (n =3D=3D 0) {
  | +			syslog(LOG_ERR, "%m");
  | +			exit(1);
  | +		}
  | +		freeaddrinfo(res0);
  | +
  | +		if (pidfile(NULL) =3D=3D -1)
  | +			syslog(LOG_ERR, "failed to write a pid file: %m");
  | +
  | +		for (;;) {
  | +			if (poll(fds, n, INFTIM) < 0) {

Check against =3D=3D -1 not < 0.


  | +				if (errno =3D=3D EINTR)
  | +					continue;
  | +				syslog(LOG_ERR, "poll: %m");
  | +				exit(1);
  | +			}
  | +			for (i =3D 0; i < n; i++) {
  | +				if (fds[i].revents & POLLIN) {
  | +					fd =3D accept(fds[i].fd, NULL, NULL);
  | +					if (fd < 0) {

Check against =3D=3D -1 not < 0.


  | +						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:

Indent the goto label by one space.
(Helps with "cvs diff -p" :)


  | +		(void)dup2(fd, STDIN_FILENO);
  | +		(void)dup2(fd, STDOUT_FILENO);
  | +		(void)dup2(fd, STDERR_FILENO);
  | +		for (i =3D 0; i < n; i++)
  | +			(void)close(socks[i]);
  | +	}
  | +
  |  	memset((char *)&his_addr, 0, sizeof(his_addr));
  |  	addrlen =3D sizeof(his_addr.si_su);
  |  	if (getpeername(0, (struct sockaddr *)&his_addr.si_su, &addrlen) < 0)=
 {
  | @@ -670,6 +784,15 @@
  |  	urgflag =3D 1;
  |  }
  | =20
  | +static void
  | +sigchild(int signo)
  | +{
  | +	int saved_errno =3D errno;
  | +
  | +	while (waitpid(-1, NULL, WNOHANG) > 0)
  | +		continue;
  | +	errno =3D saved_errno;
  | +}

This function is unnecessary and probably incorrect -- see comments
earlier about sigaction.


Cheers,
Luke.

--GYaKytDE8aa4+VVK
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (NetBSD)

iD8DBQFC2KCNpBhtmn8zJHIRAlUSAKCIebY2+Iqpex9mbN0qPlMqh4INHgCgvnIe
WevJUeiZUm2ZQY1CfJjzvEg=
=ZUGN
-----END PGP SIGNATURE-----

--GYaKytDE8aa4+VVK--