[Nut-upsdev] Re: [nut-commits] svn commit r731

Peter Selinger selinger at mathstat.dal.ca
Tue Jan 23 05:24:46 CET 2007


Hi Arjen,

in server/upsd.c r731, you moved the call conf_load() from after
check_perms() (~ l.1020) to before setupsignals() (~ l.989).

The problem is that conf_load() needs to open the ups driver socket,
and it assumes that STATEPATH is the current working directory. The
directory is only set in l.1016. Therefore, the first attempt to open
a socket will always fail. From the user's point of view:

Can't connect to UPS [mge] (mge): No such file or directory

The socket is later opened on the second attempt, but only after upsd
has forked into the background, so the command-line user is left with
no further information after the first error message.

Why was it necessary to move conf_load()?  In this case,
chdir(statepath) should be moved before it. But the problem is that
become_user() has to be before chdir (to check permission), and
chroot_start() has to be before become_user(), and this might perhaps
mess up listen_add() (does listen_add() require root access)?

This looks like a catch-22 to me, but I don't understand the code as
well as you do. Is there some sequence of events that will work?

Moreover, conf_load() is now called before the chroot() and
become_user(), which means that ups.conf is read by root and in the
raw directory tree. This is not how it was intended; perhaps even a
potential security problem.

-- Peter

Arjen de Korte wrote:
> 
> Author: adkorte-guest
> Date: Sat Jan 13 21:22:28 2007
> New Revision: 731
> 
> Added:
>    trunk/server/stype.h
> Modified:
>    trunk/ChangeLog
>    trunk/clients/upsclient.c
>    trunk/clients/upsclient.h
>    trunk/conf/upsd.conf.sample
>    trunk/m4/nut_check_ipv6.m4
>    trunk/server/conf.c
>    trunk/server/upsd.c
>    trunk/server/upsd.h
>    trunk/server/upstype.h
> Log:
>  * clients/upsclient.ch: 
>    - hostname can now be specified as a fully.qualified.domain.name or an [address literal]
>    - cleaned up IPv6 code
>  * m4/nut_check_ipv6.m4:
>    - removed check for AI_ADDRCONFIG, since it is no longer used (for portability reasons, wasn't really needed anyway)
>  * server/upsd.ch, server/conf.c, server/stype.h:
>    - listen on multiple TCP sockets (to be configured in upsd.conf)
>    - removed -i and -p options which are obsoleted by the above
>    - listening address can be specified as a fully.qualified.domain.name or an [address literal]
>    - cleaned up IPv6 code
>  * conf/upsd.conf.sample:
>    - added new LISTEN parameter
> 
> IPv6 code needs testing. From initial tests, it looks like the ACCESS/REJECT doesn't work in the way expected, so this might need some refining.
> 
> Modified: trunk/ChangeLog
> ==============================================================================
> --- trunk/ChangeLog	(original)
> +++ trunk/ChangeLog	Sat Jan 13 21:22:28 2007
> @@ -1,3 +1,21 @@
> +Sat Jan 13 20:12:16 UTC 2007 / Arjen de Korte <arjen at de-korte.org>
> +
> + * clients/upsclient.ch: 
> +   - hostname can now be specified as a fully.qualified.domain.name or an
> +     [address literal]
> +   - cleaned up IPv6 code
> + * m4/nut_check_ipv6.m4:
> +   - removed check for AI_ADDRCONFIG, since it is no longer used (for
> +     portability reasons, wasn't really needed anyway)
> + * server/upsd.ch, server/conf.c, server/stype.h:
> +   - listen on multiple TCP sockets (to be configured in upsd.conf)
> +   - removed -i and -p options which are obsoleted by the above
> +   - listening address can be specified as a fully.qualified.domain.name
> +     or an [address literal]
> +   - cleaned up IPv6 code
> + * conf/upsd.conf.sample:
> +   - added new LISTEN parameter
> +
>  Wed Jan 10 15:42:20 UTC 2007 / Arjen de Korte <arjen at de-korte.org>
>  
>   - server/upsd.c: set a default for the listenaddress if we're using
> 
> Modified: trunk/clients/upsclient.c
> ==============================================================================
> --- trunk/clients/upsclient.c	(original)
> +++ trunk/clients/upsclient.c	Sat Jan 13 21:22:28 2007
> @@ -27,6 +27,7 @@
>  #include <unistd.h>
>  #include <sys/socket.h>
>  #include <netinet/in.h>
> +#include <arpa/inet.h>
>  
>  #include "upsclient.h"
>  
> @@ -418,15 +419,15 @@
>  }
>  
>  #endif	/* HAVE_SSL */
> -	
> +
>  int upscli_connect(UPSCONN *ups, const char *host, int port, int flags)
>  {
>  #ifndef	HAVE_IPV6
> -	struct	sockaddr_in	local, server;
> -	struct	hostent	*serv;
> +	struct sockaddr_in	local, server;
> +	struct hostent		*serv;
>  #else
> -	struct addrinfo hints, *r, *rtmp;
> -	char *service;
> +	struct addrinfo	hints, *res, *ai;
> +	char			sport[NI_MAXSERV];
>  #endif
>  
>  	/* clear out any lingering junk */
> @@ -437,11 +438,10 @@
>  	ups->syserrno = 0;
>  	ups->upsclient_magic = UPSCLIENT_MAGIC;
>  
> -	ups->pc_ctx = malloc(sizeof(PCONF_CTX));
> -
> -	if (!ups->pc_ctx) {
> +	if ((ups->pc_ctx = malloc(sizeof(PCONF_CTX))) == NULL)
> +	{
>  		ups->upserror = UPSCLI_ERR_NOMEM;
> -		return -1;		
> +		return -1;
>  	}
>  
>  	pconf_init(ups->pc_ctx, NULL);
> @@ -449,19 +449,27 @@
>  	ups->ssl_ctx = NULL;
>  	ups->ssl = NULL;
>  
> -	if (!host) {
> +	if (!host)
> +	{
>  		ups->upserror = UPSCLI_ERR_NOSUCHHOST;
>  		return -1;
>  	}
>  
>  #ifndef	HAVE_IPV6
> -	if ((serv = gethostbyname(host)) == (struct hostent *) NULL) {
> -
> -		ups->upserror = UPSCLI_ERR_NOSUCHHOST;
> -		return -1;
> +	if ((serv = gethostbyname(host)) == (struct hostent *)NULL)
> +	{
> +		struct  in_addr	listenaddr;
> +
> +		if (!inet_aton(host, &listenaddr) || ((serv = gethostbyaddr(&listenaddr,
> +			sizeof(listenaddr), AF_INET)) == (struct hostent *)NULL))
> +		{
> +			ups->upserror = UPSCLI_ERR_NOSUCHHOST;
> +			return -1;
> +		}
>  	}
>  
> -	if ((ups->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
> +	if ((ups->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
> +	{
>  		ups->upserror = UPSCLI_ERR_SOCKFAILURE;
>  		ups->syserrno = errno;
>  		return -1;
> @@ -477,140 +485,123 @@
>  
>  	memcpy(&server.sin_addr, serv->h_addr, serv->h_length);
>  
> -	if (bind(ups->fd, (struct sockaddr *) &local, 
> -		sizeof(struct sockaddr_in)) == -1) {
> +	if (bind(ups->fd, (struct sockaddr *) &local, sizeof(struct sockaddr_in)) == -1)
> +	{
>  		ups->upserror = UPSCLI_ERR_BINDFAILURE;
>  		ups->syserrno = errno;
>  		close(ups->fd);
>  		ups->fd = -1;
> -
>  		return -1;
>  	}
>  
> -	if (connect(ups->fd, (struct sockaddr *) &server, 
> -		sizeof(struct sockaddr_in)) == -1) {
> +	if (connect(ups->fd, (struct sockaddr *) &server, sizeof(struct sockaddr_in)) == -1)
> +	{
>  		ups->upserror = UPSCLI_ERR_CONNFAILURE;
>  		ups->syserrno = errno;
>  		close(ups->fd);
>  		ups->fd = -1;
> -
>  		return -1;
>  	}
> -
> -	/* don't use xstrdup for cleaner linking (fewer dependencies) */
> -	ups->host = strdup(host);
> -
> -	if (!ups->host) {
> -		close(ups->fd);
> -		ups->fd = -1;
> -
> -		ups->upserror = UPSCLI_ERR_NOMEM;
> -		return -1;
> -	}
> -
> -	ups->port = port;
> -
> -	if (flags & UPSCLI_CONN_TRYSSL) {
> -		upscli_sslinit(ups);
> -
> -		/* see if something made us die inside sslinit */
> -		if (ups->upserror != 0)
> -			return -1;
> -	}
> -
> -	if (flags & UPSCLI_CONN_REQSSL) {
> -		if (upscli_sslinit(ups) != 1) {
> -			ups->upserror = UPSCLI_ERR_SSLFAIL;
> -			upscli_closefd(ups);
> -			return -1;
> -		}
> -	}
> -
> -	return 0;
>  #else
> -	service = malloc (sizeof (char) * 6);
> -	if (service == NULL) {
> -		ups->upserror = UPSCLI_ERR_NOMEM;
> -		return -1;
> -	}
> -
> -	if (snprintf (service, 6, "%hu", (unsigned short int)port) < 1) {
> -		return -1;
> -	}
> +	snprintf(sport, NI_MAXSERV, "%hu", (unsigned short int)port);
>  
>  	memset (&hints, 0, sizeof (struct addrinfo));
> -	hints.ai_family = flags & UPSCLI_CONN_INET ? AF_INET : (flags & UPSCLI_CONN_INET6 ? AF_INET6 : AF_UNSPEC);
> +	hints.ai_family = AF_UNSPEC;
>  	hints.ai_socktype = SOCK_STREAM;
>  	hints.ai_protocol = IPPROTO_TCP;
> -	hints.ai_flags = AI_ADDRCONFIG;
>  
> -	if (getaddrinfo (host, service, &hints, &r) != 0) {
> +	if (getaddrinfo(host, sport, &hints, &res) != 0)
> +	{
>  		ups->upserror = UPSCLI_ERR_NOSUCHHOST;
> -		free (service);
>  		return -1;
>  	}
> -	free (service);
>  
> -	for (rtmp = r; r != NULL; r = r->ai_next) {
> -		ups->fd = socket (r->ai_family, r->ai_socktype, r->ai_protocol);
> -		if (ups->fd < 0) {
> -			if (r->ai_next == NULL) {
> +	for (ai = res; ai != NULL; ai = ai->ai_next)
> +	{
> +		int	sock_fd, v;
> +
> +		if (ai->ai_socktype != hints.ai_socktype || ai->ai_protocol != hints.ai_protocol)
> +			continue;
> +
> +		if ((sock_fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0)
> +		{
> +			switch (errno)
> +			{
> +			case EAFNOSUPPORT:
> +			case EINVAL:
> +                                continue;
> +			default:
>  				ups->upserror = UPSCLI_ERR_SOCKFAILURE;
>  				ups->syserrno = errno;
> -				break;
> +				continue;
>  			}
> -			continue;
>  		}
>  
> -		if (connect (ups->fd, r->ai_addr, r->ai_addrlen) == -1) {
> -			close (ups->fd);
> -			ups->fd = -1;
> -			if (r->ai_next == NULL) {
> +		while ((v = connect(sock_fd, ai->ai_addr, ai->ai_addrlen)) < 0)
> +		{
> +			switch (errno)
> +			{
> +			case EAFNOSUPPORT:
> +				break;
> +			case EINTR:
> +			case EAGAIN:
> +				continue;
> +			default:
>  				ups->upserror = UPSCLI_ERR_CONNFAILURE;
>  				ups->syserrno = errno;
>  				break;
>  			}
> -			continue;
>  		}
> -		freeaddrinfo (rtmp);
>  
> -		/* don't use xstrdup for cleaner linking (fewer dependencies) */
> -		ups->host = strdup(host);
> +		if (v < 0)
> +		{
> +			close(sock_fd);
> +			continue;
> +		}
>  
> -		if (!ups->host) {
> -			close(ups->fd);
> -			ups->fd = -1;
> +		ups->fd = sock_fd;
> +		ups->upserror = 0;
> +		ups->syserrno = 0;
> +		break;
> +	}		
>  
> -			ups->upserror = UPSCLI_ERR_NOMEM;
> -			return -1;
> -		}
> +	freeaddrinfo(res);
>  
> -		ups->port = port;
> +	if (ups->fd < 0)
> +		return -1;
> +#endif
>  
> -		if (flags & UPSCLI_CONN_TRYSSL) {
> -			upscli_sslinit(ups);
> +	if ((ups->host = strdup(host)) == NULL)
> +	{
> +		ups->upserror = UPSCLI_ERR_NOMEM;
> +		close(ups->fd);
> +		ups->fd = -1;
> +		return -1;
> +	}
>  
> -			/* see if something made us die inside sslinit */
> -			if (ups->upserror != 0)
> -				return -1;
> -		}
> +	ups->port = port;
>  
> -		if (flags & UPSCLI_CONN_REQSSL) {
> -			if (upscli_sslinit(ups) != 1) {
> -				ups->upserror = UPSCLI_ERR_SSLFAIL;
> -				upscli_closefd(ups);
> -				return -1;
> -			}
> -		}
> +	if (flags & UPSCLI_CONN_TRYSSL)
> +	{
> +		upscli_sslinit(ups);
>  
> -		return 0;
> +		/* see if something made us die inside sslinit */
> +		if (ups->upserror != 0)
> +			return -1;
>  	}
> -	freeaddrinfo (rtmp);
>  
> -	return -1;
> -#endif
> +	if (flags & UPSCLI_CONN_REQSSL)
> +	{
> +		if (upscli_sslinit(ups) != 1)
> +		{
> +			ups->upserror = UPSCLI_ERR_SSLFAIL;
> +			upscli_closefd(ups);
> +			return -1;
> +		}
> +	}
> +		
> +	return 0;
>  }
> -
>  /* map upsd error strings back to upsclient internal numbers */
>  static struct {
>  	int	errnum;
> @@ -921,106 +912,60 @@
>  	return 0;
>  }
>  
> -/* split [upsname@]hostname[:port] into separate components */
> -int upscli_splitname(const char *buf, char **upsname, char **hostname,
> -			int *port)
> +/* split upsname at hostname[:port] into separate components */
> +int upscli_splitname(const char *buf, char **upsname, char **hostname, int *port)
>  {
> -	char	tmp[SMALLBUF], *ptr, *ap, *cp;
> +	char	tmp[SMALLBUF], *s;
>  
> +	/* paranoia */
>  	if ((!buf) || (!upsname) || (!hostname) || (!port))
>  		return -1;
>  
>  	snprintf(tmp, sizeof(tmp), "%s", buf);
>  
> -	ap = strchr(tmp, '@');
> -
> -	if (!ap) {
> +	/* split at the '@' character */
> +	if ((s = strtok(tmp, "@")) == NULL)
> +	{
>  		fprintf(stderr, "upscli_splitname: no UPS name specified (upsname at hostname)\n");
>  		return -1;
>  	}
>  
> -	*ap++ = '\0';
> -	*upsname = strdup(tmp);
> -
> -	if (!*upsname) {
> +	if ((*upsname = strdup(s)) == NULL)
> +	{
>  		fprintf(stderr, "upscli_splitname: strdup failed\n");
>  		return -1;
>  	}
>  
> -	ptr = ap;
> -
> -#ifndef	HAVE_IPV6
> -	cp = strchr(ptr, ':');
> -
> -	if (cp) {
> -		*cp++ = '\0';
> -		*hostname = strdup(ptr);
> -
> -		if (!*hostname) {
> -			fprintf(stderr, "upscli_splitname: strdup failed\n");
> -			return -1;
> -		}
> -
> -		ptr = cp;
> -
> -		*port = strtol(ptr, (char **) NULL, 10);
> -
> -	} else {
> -
> -		*hostname = strdup(ptr);
> -
> -		if (!*hostname) {
> -			fprintf(stderr, "upscli_splitname: strdup failed\n");
> -			return -1;
> -		}
> -
> -		*port = PORT;
> +	if ((s = strtok(NULL, "\0")) == NULL)
> +	{
> +		fprintf(stderr, "upscli_splitname: no hostname specified (upsname at hostname)\n");
> +		return -1;
>  	}
> -#else
> -	if (*ptr != '[') {
> -		cp = strchr(ptr, ':');
> -		if (cp) {
> -			*cp++ = '\0';
> -			*hostname = strdup(ptr);
> -			
> -			if (!*hostname) {
> -				fprintf(stderr, "upscli_splitname: strdup failed\n");
> -				return -1;
> -			}
>  
> -			ptr = cp;
> -			
> -			*port = strtol(ptr, (char **) NULL, 10);
> -
> -		} else {
> +	if (*s == '[')
> +		s = strtok(s+1, "]");	/* address literal */
> +	else
> +		s = strtok(s, ":\0");	/* hostname */
> +	
> +	if (s == NULL)
> +	{
> +		fprintf(stderr, "upscli_splitname: no hostname specified (upsname at hostname)\n");
> +		return -1;
> +	}
>  
> -			*hostname = strdup(ptr);
> +	if ((*hostname = strdup(s)) == NULL)
> +	{
> +		fprintf(stderr, "upscli_splitname: strdup failed\n");
> +		return -1;
> +	}
>  
> -			if (!*hostname) {
> -				fprintf(stderr, "upscli_splitname: strdup failed\n");
> -				return -1;
> -			}
> +	/* skip the separator between hostname and port (if any) */
> +	if (((s = strtok(NULL, "\0")) != NULL) && (*s == ':')) s++;
>  
> -			*port = PORT;
> -		}
> -	} else {
> -		ptr++;
> -		cp = strchr(ptr, ']');
> -		if (cp) {
> -			*cp = '\0';
> -			*hostname = strdup (ptr);
> -			ptr = ++cp;
> -			cp = strchr (ptr, ':');
> -			if (cp != NULL)
> -			 *port = strtol (++cp, (char **)NULL, 10);
> -			else
> -			 *port = PORT;
> -		} else {
> -			fprintf (stderr, "upscli_splitname: strchr(']') failed\n");
> -			return -1;
> -		}
> -	}
> -#endif
> +	if (s == NULL)
> +		*port = PORT;
> +	else
> +		*port = strtol(s, NULL, 10);
>  
>  	return 0;
>  }
> 
> Modified: trunk/clients/upsclient.h
> ==============================================================================
> --- trunk/clients/upsclient.h	(original)
> +++ trunk/clients/upsclient.h	Sat Jan 13 21:22:28 2007
> @@ -149,11 +149,6 @@
>  #define UPSCLI_CONN_TRYSSL	0x0001	/* try SSL, OK if not supported       */
>  #define UPSCLI_CONN_REQSSL	0x0002	/* try SSL, fail if not supported     */
>  
> -#ifdef	HAVE_IPV6
> -#define UPSCLI_CONN_INET	0x0004  /* IPv4 only */
> -#define UPSCLI_CONN_INET6	0x0008  /* IPv6 only */
> -#endif
> -
>  #ifdef __cplusplus
>  }
>  #endif
> 
> Modified: trunk/conf/upsd.conf.sample
> ==============================================================================
> --- trunk/conf/upsd.conf.sample	(original)
> +++ trunk/conf/upsd.conf.sample	Sat Jan 13 21:22:28 2007
> @@ -40,3 +40,15 @@
>  # You should only use this if your driver has difficulties keeping
>  # the data fresh within the normal 15 second interval.  Watch the syslog
>  # for notifications from upsd about staleness.
> +
> +# =======================================================================
> +# LISTEN <address> [<port>]
> +# LISTEN 0.0.0.0 3493
> +#
> +# This defaults to the global IPv4 listening address and port 3493. You
> +# may specify each interface you want upsd to listen on for connections,
> +# optionally with a port number.
> +#
> +# You may need this if you have multiple interfaces on your machine and
> +# you don't want upsd to listen to all interfaces (for instance on a
> +# firewall, you may not want to listen to the external interface).
> 
> Modified: trunk/m4/nut_check_ipv6.m4
> ==============================================================================
> --- trunk/m4/nut_check_ipv6.m4	(original)
> +++ trunk/m4/nut_check_ipv6.m4	Sat Jan 13 21:22:28 2007
> @@ -29,17 +29,17 @@
>  dnl               [nut_have_ipv6=no],
>  dnl		  [#include <netdb.h>])
>  
> -   AC_MSG_CHECKING([for AI_ADDRCONFIG])
> -   AC_COMPILE_IFELSE(
> -       [AC_LANG_PROGRAM(
> -	   [[#include <netdb.h>]],
> -	   [[int flag = AI_ADDRCONFIG]]
> -        )], 
> -       [AC_DEFINE(HAVE_AI_ADDRCONFIG, 1, [Define if `addrinfo' structure allows AI_ADDRCONFIG flag])
> -        AC_MSG_RESULT(yes)],
> -       [AC_MSG_RESULT(no)
> -        nut_have_ipv6=no]
> -   )
> +dnl AC_MSG_CHECKING([for AI_ADDRCONFIG])
> +dnl AC_COMPILE_IFELSE(
> +dnl    [AC_LANG_PROGRAM(
> +dnl	   [[#include <netdb.h>]],
> +dnl	   [[int flag = AI_ADDRCONFIG]]
> +dnl     )], 
> +dnl    [AC_DEFINE(HAVE_AI_ADDRCONFIG, 1, [Define if `addrinfo' structure allows AI_ADDRCONFIG flag])
> +dnl     AC_MSG_RESULT(yes)],
> +dnl    [AC_MSG_RESULT(no)
> +dnl     nut_have_ipv6=no]
> +dnl)
>  
>     AC_MSG_CHECKING([for IN6_IS_ADDR_V4MAPPED])
>     AC_LINK_IFELSE(
> 
> Modified: trunk/server/conf.c
> ==============================================================================
> --- trunk/server/conf.c	(original)
> +++ trunk/server/conf.c	Sat Jan 13 21:22:28 2007
> @@ -25,6 +25,8 @@
>  #include "user.h"
>  #include "access.h"
>  
> +#define	QUOTED(x)	#x
> +
>  	extern	int	maxage;
>  	extern	char	*statepath, *datapath, *certfile;
>  	extern	upstype	*firstups;
> @@ -197,6 +199,15 @@
>  		return 1;
>  	}
>  
> +	/* LISTEN <address> [<port>] */
> +	if (!strcmp(arg[0], "LISTEN")) {
> +		if (numargs < 3)
> +			listen_add(arg[1], "3493");
> +		else
> +			listen_add(arg[1], arg[2]);
> +		return 1;
> +	}
> +
>  	/* everything below here uses up through arg[2] */
>  	if (numargs < 3)
>  		return 0;
> 
> Added: trunk/server/stype.h
> ==============================================================================
> --- (empty file)
> +++ trunk/server/stype.h	Sat Jan 13 21:22:28 2007
> @@ -0,0 +1,40 @@
> +/* stype.h - server data definitions for upsd
> +
> +   Copyright (C) 2007  Arjen de Korte <arjen at de-korte.org>
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 2 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program; if not, write to the Free Software
> +   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> +*/
> +
> +#ifndef STYPE_H_SEEN
> +#define STYPE_H_SEEN 1
> +
> +#include <netdb.h>
> +
> +#ifndef NI_MAXHOST
> +#define NI_MAXHOST      1025
> +#endif
> +
> +#ifndef NI_MAXSERV
> +#define NI_MAXSERV      32
> +#endif
> +
> +typedef struct {
> +	char	*addr;
> +	char	*port;
> +	int	sock_fd;
> +	void	*next;
> +}	stype;
> +
> +#endif	/* STYPE_H_SEEN */
> 
> Modified: trunk/server/upsd.c
> ==============================================================================
> --- trunk/server/upsd.c	(original)
> +++ trunk/server/upsd.c	Sat Jan 13 21:22:28 2007
> @@ -36,11 +36,14 @@
>  #include "user.h"
>  #include "access.h"
>  #include "ctype.h"
> +#include "stype.h"
>  #include "ssl.h"
>  #include "sstate.h"
>  #include "desc.h"
>  #include "neterr.h"
>  
> +#define	QUOTED(x)	#x
> +
>  	/* externally-visible settings and pointers */
>  
>  	upstype	*firstups = NULL;
> @@ -56,20 +59,12 @@
>  
>  	/* everything else */
>  
> -static	ctype	*firstclient = NULL;
> -
> -static	int	listenfd, net_port = PORT;
> +static ctype	*firstclient = NULL;
>  
>  	/* default is to listen on all local interfaces */
> -#ifndef	HAVE_IPV6
> -static	struct	in_addr	listenaddr;
> -#else
> -static	char *listenaddr = "0.0.0.0";
> -
> -/* AF_ */
> +static stype	*firstaddr = NULL;
>  
>  static int opt_af = AF_UNSPEC;
> -#endif
>  
>  	/* signal handlers */
>  static	struct sigaction sa;
> @@ -153,99 +148,172 @@
>  		ups_data_ok(ups);
>  }
>  
> -/* create a listening socket for tcp connections */
> -static void setuptcp(void)
> +/* add another listening address */
> +void listen_add(const char *addr, const char *port)
>  {
> +	stype	*stmp, *last;
> +
> +	stmp = last = firstaddr;
> +
> +	/* find end of linked list */
> +	while (stmp != NULL) {
> +		last = stmp;
> +		stmp = stmp->next;
> +	}
> +
> +	/* grab some memory and add the info */
> +	stmp = xmalloc(sizeof(stype));
> +	stmp->addr = xstrdup(addr);
> +	stmp->port = xstrdup(port);
> +	stmp->sock_fd = -1;
> +	stmp->next = NULL;
> +
> +	if (last == NULL)
> +		firstaddr = stmp;
> +	else
> +		last->next = stmp;
> +
> +	upsdebugx(3, "listen_add: added %s:%s", stmp->addr, stmp->port);
> +}
> +
> +/* create a listening socket for tcp connections */
> +static void setuptcp(stype *serv)
>  #ifndef	HAVE_IPV6
> -	struct	sockaddr_in	server;
> +{
> +	struct hostent		*host;
> +	struct sockaddr_in	server;
>  	int	res, one = 1;
>  
> -	if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
> +	if ((host = gethostbyname(serv->addr)) == (struct hostent *)NULL) {
> +		struct  in_addr	listenaddr;
> +
> +		if (!inet_aton(serv->addr, &listenaddr) || ((host = gethostbyaddr(&listenaddr,
> +				sizeof(listenaddr), AF_INET)) == (struct hostent *)NULL))
> +			fatal_with_errno("gethost");
> +	}
> +
> +	if ((serv->sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
>  		fatal_with_errno("socket");
>  
> -	res = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (void *) &one, 
> +	res = setsockopt(serv->sock_fd, SOL_SOCKET, SO_REUSEADDR, (void *) &one, 
>  		sizeof(one));
>  
>  	if (res != 0)
>  		fatal_with_errno("setsockopt(SO_REUSEADDR)");
>  
>  	memset(&server, '\0', sizeof(server));
> -	server.sin_addr = listenaddr;
>  	server.sin_family = AF_INET;
> -	server.sin_port = htons(net_port);
> +	server.sin_port = htons(atoi(serv->port));
>  
> -	if (bind(listenfd, (struct sockaddr *) &server, sizeof(server)) == -1)
> -		fatal_with_errno("Can't bind TCP port number %d", net_port);
> +	memcpy(&server.sin_addr, host->h_addr, host->h_length);
>  
> -	if ((res = fcntl(listenfd, F_GETFL, 0)) == -1)
> +	if (bind(serv->sock_fd, (struct sockaddr *) &server, sizeof(server)) == -1)
> +		fatal_with_errno("Can't bind TCP port %s", serv->port);
> +
> +	if ((res = fcntl(serv->sock_fd, F_GETFL, 0)) == -1)
>  		fatal_with_errno("fcntl(get)");
>  
> -	if (fcntl(listenfd, F_SETFL, res | O_NDELAY) == -1)
> +	if (fcntl(serv->sock_fd, F_SETFL, res | O_NDELAY) == -1)
>  		fatal_with_errno("fcntl(set)");
>  
> -	if (listen(listenfd, 16))
> +	if (listen(serv->sock_fd, 16))
>  		fatal_with_errno("listen");
>  
>  	return;
> +}
>  #else
> -	struct addrinfo hints, *r, *rtmp;
> -	char	*service;
> -	int	res, one = 1;
> +{
> +	struct addrinfo		hints, *res, *ai;
> +	struct sockaddr_storage	sas;
> +	socklen_t			sas_len = sizeof(sas);
> +	int	v = 0, one = 1;
> +	char	saddr[NI_MAXHOST];
> +	char	sport[NI_MAXSERV];
> +
> +	upsdebugx(3, "setuptcp: try to bind to %s port %s", serv->addr, serv->port);
> +
> +	/* memset(&hints, 0, sizeof(hints)); */
> +	hints.ai_flags		= AI_PASSIVE;
> +	hints.ai_family		= PF_UNSPEC;
> +	hints.ai_socktype	= SOCK_STREAM;
> +	hints.ai_protocol	= IPPROTO_TCP;
> +	hints.ai_addrlen	= 0;
> +	hints.ai_addr		= NULL;
> +	hints.ai_canonname	= NULL;
> +	hints.ai_next		= NULL;
> +
> +        if ((v = getaddrinfo(serv->addr, serv->port, &hints, &res))) {
> +		if (v == EAI_SYSTEM)
> +                        fatal_with_errno("getaddrinfo");
> +
> +                fatalx("getaddrinfo: %s\n", gai_strerror(v));
> +        }
> +
> +        for (ai = res; ai != NULL; ai = ai->ai_next) {
> +		int sock_fd;
> +
> +		if ((ai->ai_socktype != hints.ai_socktype) || (ai->ai_protocol != hints.ai_protocol))
> +			continue;
> +
> +		if ((sock_fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
> +			upsdebug_with_errno(3, "setuptcp: socket");
> +			continue;
> +		}
> +
> +		
> +		if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)) != 0)
> +			fatal_with_errno("setuptcp: setsockopt");
> +
> +		if (bind(sock_fd, ai->ai_addr, ai->ai_addrlen) < 0) {
> +			upsdebug_with_errno(3, "setuptcp: bind");
> +			close(sock_fd);
> +			continue;
> +		}
> +
> +		if ((v = fcntl(sock_fd, F_GETFL, 0)) == -1)
> +			fatal_with_errno("setuptcp: fcntl(get)");
>  
> -	memset (&hints, 0, sizeof (struct addrinfo));
> -	hints.ai_family = opt_af;
> -	hints.ai_flags = AI_PASSIVE;
> -	hints.ai_protocol = IPPROTO_TCP;
> -	hints.ai_socktype = SOCK_STREAM;
> -
> -	service = xmalloc (sizeof (char) * 6);
> -
> -	if (snprintf (service, 6, "%hu", (unsigned short int)net_port) < 1)
> -		fatal_with_errno("snprintf");
> -
> -	if (getaddrinfo (listenaddr, service, &hints, &r) != 0) {
> -		free (service);
> -		fatal_with_errno("getaddrinfo");
> -	}
> -	free (service);
> -
> -	for (rtmp = r; r != NULL; r = r->ai_next) {
> -		listenfd = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
> -		if (listenfd < 0) {
> -			if (r->ai_next == NULL)
> -				fatal_with_errno("socket");
> +		if (fcntl(sock_fd, F_SETFL, v | O_NDELAY) == -1)
> +			fatal_with_errno("setuptcp: fcntl(set)");
> +
> +		if (listen(sock_fd, 1) < 0) {
> +			upsdebug_with_errno(3, "setuptcp: listen");
> +			close(sock_fd);
>  			continue;
>  		}
>  
> -		res = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (void *) &one, sizeof(one));
> -		if (res != 0)
> -			fatal_with_errno("setsockopt(SO_REUSEADDR)");
> -
> -		if (bind (listenfd, r->ai_addr, r->ai_addrlen) == -1) {
> -			if (r->ai_next == NULL)
> -				fatal_with_errno("Can't bind TCP port number %u", net_port);
> -			close (listenfd);
> +		if (getsockname(sock_fd, (struct sockaddr *)&sas, &sas_len) < 0) {
> +			upsdebug_with_errno(3, "setuptcp: getsockname");
> +			close(sock_fd);
>  			continue;
>  		}
>  
> -		if ((res = fcntl(listenfd, F_GETFL, 0)) == -1)
> -			fatal_with_errno("fcntl(get)");
> +		if ((v = getnameinfo((struct sockaddr *)&sas, sas_len,
> +				saddr, NI_MAXHOST, sport, NI_MAXSERV, 0))) {
> +
> +			upslogx(LOG_ERR, "setuptcp: getaddrinfo: %s\n", gai_strerror(v));
>  
> -		if (fcntl(listenfd, F_SETFL, res | O_NDELAY) == -1)
> -			fatal_with_errno("fcntl(set)");
> +			if (v == EAI_SYSTEM)
> +				upsdebug_with_errno(3, "setuptcp: getaddrinfo");
>  
> -		if (listen(listenfd, 16) == -1) {
> -			if (r->ai_next == NULL)
> -				fatal_with_errno("listen");
> -			close (listenfd);
> +			close(sock_fd);
>  			continue;
>  		}
> +
> +		serv->sock_fd = sock_fd;
>  		break;
>  	}
> -	freeaddrinfo (rtmp);
> +
> +	freeaddrinfo(res);
> +
> +	if (serv->sock_fd < 0)
> +		upslogx(LOG_WARNING, "upsd: not listening on %s:%s", serv->addr, serv->port);
> +	else
> +		upslogx(LOG_INFO, "upsd: listening on %s:%s", saddr, sport);
> +
>  	return;
> -#endif
>  }
> +#endif
>  
>  /* decrement the login counter for this ups */
>  static void declogins(const char *upsname)
> @@ -493,19 +561,20 @@
>  }
>  
>  /* answer incoming tcp connections */
> -static void answertcp(void)
> -{
> -	int	acc;
> +static void answertcp(stype *serv)
>  #ifndef	HAVE_IPV6
> +{
>  	struct	sockaddr_in csock;
>  #else
> +{
>  	struct	sockaddr_storage csock;
>  #endif
> +	int	acc;
>  	ctype	*tmp, *last;
>  	socklen_t	clen;
>  
>  	clen = sizeof(csock);
> -	acc = accept(listenfd, (struct sockaddr *) &csock, &clen);
> +	acc = accept(serv->sock_fd, (struct sockaddr *) &csock, &clen);
>  
>  	if (acc < 0)
>  		return;
> @@ -531,16 +600,14 @@
>  
>  	tmp = xmalloc(sizeof(ctype));
>  
> -#ifndef	HAVE_IPV6
> -	tmp->addr = xstrdup(inet_ntoa(csock.sin_addr));
> -#else
> -	tmp->addr = xstrdup(inet_ntopW(&csock));
> -#endif
>  	tmp->fd = acc;
>  	tmp->delete = 0;
> +
>  #ifndef	HAVE_IPV6
> +	tmp->addr = xstrdup(inet_ntoa(csock.sin_addr));
>  	memcpy(&tmp->sock, &csock, sizeof(struct sockaddr_in));
>  #else
> +	tmp->addr = xstrdup(inet_ntopW(&csock));
>  	memcpy(&tmp->sock, &csock, sizeof(struct sockaddr_storage));
>  #endif
>  
> @@ -563,11 +630,7 @@
>  	else
>  		last->next = tmp;
>  
> -#ifndef	HAVE_IPV6
> -	upslogx(LOG_INFO, "Connection from %s", inet_ntoa(csock.sin_addr));
> -#else
>  	upslogx(LOG_INFO, "Connection from %s", tmp->addr);
> -#endif
>  }
>  
>  /* read tcp messages and handle them */
> @@ -625,6 +688,24 @@
>  {
>  	ctype	*tmpcli, *tmpnext;
>  	upstype	*ups, *unext;
> +	stype	*stmp, *snext;
> +
> +	/* cleanup server fds */
> +	stmp = firstaddr;
> +
> +	while (stmp) {
> +		snext = stmp->next;
> +
> +		if (stmp->sock_fd != -1)
> +			close(stmp->sock_fd);
> +		if (stmp->addr != NULL)
> +			free(stmp->addr);
> +		if (stmp->port != NULL)
> +			free(stmp->port);
> +		free(stmp);
> +
> +		stmp = snext;
> +	}
>  
>  	/* cleanup client fds */
>  	tmpcli = firstclient;
> @@ -681,22 +762,30 @@
>  {
>  	fd_set	rfds;
>  	struct	timeval	tv;
> -	int	res, maxfd;
> +	int	res, maxfd = -1;
>  	ctype	*tmpcli, *tmpnext;
>  	upstype	*utmp, *unext;
> +	stype	*stmp, *snext;
>  
>  	if (reload_flag) {
>  		conf_reload();
>  		reload_flag = 0;
>  	}
> -
> -	FD_ZERO(&rfds);
> -	FD_SET(listenfd, &rfds);
>  	
>  	tv.tv_sec = 2;
>  	tv.tv_usec = 0;
>  
> -	maxfd = listenfd;
> +	FD_ZERO(&rfds);
> +
> +	/* scan through servers and add to FD_SET */
> +	for (stmp = firstaddr; stmp != NULL; stmp = stmp->next) {
> +		if (stmp->sock_fd != -1) {
> +			FD_SET(stmp->sock_fd, &rfds);
> +
> +			if (stmp->sock_fd > maxfd)
> +				maxfd = stmp->sock_fd;
> +		}
> +	}
>  
>  	/* scan through clients and add to FD_SET */
>  	for (tmpcli = firstclient; tmpcli != NULL; tmpcli = tmpcli->next) {
> @@ -718,11 +807,20 @@
>  	res = select(maxfd + 1, &rfds, NULL, NULL, &tv);
>  
>  	if (res > 0) {
> -		if (FD_ISSET(listenfd, &rfds))
> -			answertcp();
> +		/* scan servers for activity */
> +		stmp = firstaddr;
>  
> -		/* scan clients for activity */
> +		while (stmp) {
> +			snext = stmp->next;
> +
> +			if (stmp->sock_fd != -1)
> +				if (FD_ISSET(stmp->sock_fd, &rfds))
> +					answertcp(stmp);
>  
> +			stmp = snext;
> +		}
> +		
> +		/* scan clients for activity */
>  		tmpcli = firstclient;
>  
>  		while (tmpcli != NULL) {
> @@ -766,9 +864,6 @@
>  	printf("  -D		raise debugging level\n");
>  	printf("  -f		stay in the foreground for testing\n");
>  	printf("  -h		display this help\n");
> -	printf("  -i <address>	binds to interface <address>\n");
> -	printf("  -p <port>	sets network port (default: TCP port %d)\n", 
> -		PORT);
>  	printf("  -r <dir>	chroots to <dir>\n");
>  	printf("  -u <user>	switch to <user> (if started as root)\n");
>  	printf("  -V		display the version of this software\n");
> @@ -834,6 +929,7 @@
>  	const	char *user = NULL;
>  	char	*progname, *chroot_path = NULL;
>  	struct	passwd	*new_uid = NULL;
> +	stype	*serv;
>  
>  	progname = argv[0];
>  
> @@ -845,34 +941,20 @@
>  	datapath = xstrdup(DATADIR);
>  
>  	/* set up some things for later */
> -#ifndef	HAVE_IPV6
> -
> -	listenaddr.s_addr = INADDR_ANY;
> -#endif
>  	snprintf(pidfn, sizeof(pidfn), "%s/upsd.pid", altpidpath());
>  
>  	printf("Network UPS Tools upsd %s\n", UPS_VERSION);
>  
> -#ifndef	HAVE_IPV6
> -	while ((i = getopt(argc, argv, "+hp:r:i:fu:Vc:D")) != EOF) {
> -#else
>  	while ((i = getopt(argc, argv, "+h46p:r:i:fu:Vc:D")) != EOF) {
> -#endif
>  		switch (i) {
>  			case 'h':
>  				help(progname);
>  				break;
>  			case 'p':
> -				net_port = atoi(optarg);
> -				break;
>  			case 'i':
> -#ifndef	HAVE_IPV6
> -				if (!inet_aton(optarg, &listenaddr))
> -					fatal_with_errno("Invalid IP address");
> -#else
> -				listenaddr = xstrdup(optarg);
> -#endif
> -				break;
> +				fatalx("Specifying a listening addresses with '-i <address>' and '-p <port>'\n"
> +					"is deprecated. Use 'LISTEN <address> [<port>]' in 'upsd.conf' instead.\n"
> +					"See 'man 8 upsd.conf' for more information.");
>  			case 'r':
>  				chroot_path = optarg;
>  				break;
> @@ -930,8 +1012,22 @@
>  	if (argc != 0)
>  		help(progname);
>  
> +	/* read data from config files - upsd.conf, ups.conf, upsd.users */
> +	conf_load();
> +
>  	setupsignals();
> -	setuptcp();
> +
> +	/* default behaviour if no LISTEN addres has been specified */
> +	if (firstaddr == NULL) {
> +		firstaddr = (stype *)xmalloc(sizeof(stype));
> +		firstaddr->addr = xstrdup("0.0.0.0");
> +		firstaddr->port = xstrdup("3493");
> +		firstaddr->sock_fd = -1;
> +		firstaddr->next = NULL;
> +	}
> +
> +	for (serv = firstaddr; serv != NULL; serv = serv->next)
> +		setuptcp(serv);
>  
>  	open_syslog("upsd");
>  
> @@ -955,9 +1051,6 @@
>  	/* check statepath perms */
>  	check_perms(statepath);
>  
> -	/* read data from config files - upsd.conf, ups.conf, upsd.users */
> -	conf_load();
> -
>  	if (num_ups == 0)
>  		fatalx("Fatal error: at least one UPS must be defined in ups.conf");
>  
> 
> Modified: trunk/server/upsd.h
> ==============================================================================
> --- trunk/server/upsd.h	(original)
> +++ trunk/server/upsd.h	Sat Jan 13 21:22:28 2007
> @@ -50,6 +50,8 @@
>  upstype *get_ups_ptr(const char *upsname);
>  int ups_available(const upstype *ups, ctype *client);
>  
> +void listen_add(const char *addr, const char *port);
> +
>  void kick_login_clients(const char *upsname);
>  int sendback(ctype *client, const char *fmt, ...)
>  	__attribute__ ((__format__ (__printf__, 2, 3)));
> 
> Modified: trunk/server/upstype.h
> ==============================================================================
> --- trunk/server/upstype.h	(original)
> +++ trunk/server/upstype.h	Sat Jan 13 21:22:28 2007
> @@ -46,4 +46,6 @@
>  	void	*next;
>  }	upstype;
>  
> +extern upstype	*firstups;
> +
>  #endif	/* UPSTYPE_H_SEEN */
> 
> _______________________________________________
> nut-commits mailing list
> nut-commits at lists.alioth.debian.org
> http://lists.alioth.debian.org/mailman/listinfo/nut-commits
> 




More information about the Nut-upsdev mailing list