From lyndon@multivac.orthanc.com  Fri Oct  6 00:13:45 1995
Received: from multivac.orthanc.com (root@multivac.orthanc.com [204.244.20.2])
          by freefall.freebsd.org (8.6.12/8.6.6) with ESMTP id AAA14791
          for <FreeBSD-gnats-submit@freebsd.org>; Fri, 6 Oct 1995 00:13:34 -0700
Received: (from lyndon@localhost) by multivac.orthanc.com (8.7/8.7) id AAA00451; Fri, 6 Oct 1995 00:13:27 -0700 (PDT)
Message-Id: <199510060713.AAA00451@multivac.orthanc.com>
Date: Fri, 6 Oct 1995 00:13:27 -0700 (PDT)
From: Lyndon Nerenberg <lyndon@orthanc.com>
Reply-To: lyndon@orthanc.com
To: FreeBSD-gnats-submit@freebsd.org
Subject: rwhod does not support multicast (+FIX)
X-Send-Pr-Version: 3.2

>Number:         768
>Category:       bin
>Synopsis:       rwhod does not support multicast (+FIX)
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          closed
>Quarter:
>Keywords:
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Fri Oct  6 00:20:03 PDT 1995
>Closed-Date:    Fri Oct 6 08:16:51 PDT 1995
>Last-Modified:  Fri Oct  6 08:17:31 PDT 1995
>Originator:     Lyndon Nerenberg
>Release:        FreeBSD 2.0.5-RELEASE i386
>Organization:
Orthanc Systems: Internet and UNIX consulting
___________________________________________________________
lyndon@orthanc.com || canada!lyndon || Fax: +1 604 561 2067
                 http://www.orthanc.com/
>Environment:

	

>Description:

	rwhod has been extended to support multicast broascasts (sic).
	This is highly useful when monitoring machines on disparate
	networks. FreeBSD doesn't support this (yet).

>How-To-Repeat:

	

>Fix:
	
	The appended diff patches /usr/src/usr.sbin/rwhod/rwhod.[c8]
	to include support for multicast. It also adds a definition
	for _PATH_KERNEL to /usr/src/include/paths.h. I'm not sure
	about the latter wrt POSIX (nee _PATH_UNIX), however I feel the
	inclusion of _PATH_KERNEL would be useful in other contexts
	(and the new rwhod.c requires it).

	This code was lifted verbatim from BSD/OS 1.1 (one line
	had to be changed). It's an older CSRG version with Stanford U
	patches. No BSDi copyrights were found anywhere in the code.


===================================================================
RCS file: include/paths.h,v
retrieving revision 1.1
diff -c -r1.1 include/paths.h
*** 1.1	1995/10/06 06:45:24
--- include/paths.h	1995/10/06 06:46:03
***************
*** 60,65 ****
--- 60,66 ----
  #define	_PATH_SHELLS	"/etc/shells"
  #define	_PATH_TTY	"/dev/tty"
  #define	_PATH_UNIX	"don't use _PATH_UNIX"
+ #define _PATH_KERNEL	"/kernel"
  #define	_PATH_VI	"/usr/bin/vi"
  
  /* Provide trailing slash, since mostly used for building pathnames. */
===================================================================
RCS file: usr.sbin/rwhod/rwhod.c,v
retrieving revision 1.1
diff -c -r1.1 usr.sbin/rwhod/rwhod.c
*** 1.1	1995/10/06 06:41:36
--- usr.sbin/rwhod/rwhod.c	1995/10/06 06:44:59
***************
*** 1,6 ****
  /*
!  * Copyright (c) 1983, 1993
!  *	The Regents of the University of California.  All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
--- 1,6 ----
  /*
!  * Copyright (c) 1983 The Regents of the University of California.
!  * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
***************
*** 32,44 ****
   */
  
  #ifndef lint
! static char copyright[] =
! "@(#) Copyright (c) 1983, 1993\n\
! 	The Regents of the University of California.  All rights reserved.\n";
  #endif /* not lint */
  
  #ifndef lint
! static char sccsid[] = "@(#)rwhod.c	8.1 (Berkeley) 6/6/93";
  #endif /* not lint */
  
  #include <sys/param.h>
--- 32,44 ----
   */
  
  #ifndef lint
! char copyright[] =
! "@(#) Copyright (c) 1983 The Regents of the University of California.\n\
!  All rights reserved.\n";
  #endif /* not lint */
  
  #ifndef lint
! static char sccsid[] = "@(#)rwhod.c	5.20 (Berkeley) 3/2/91 plus MULTICAST 1.2";
  #endif /* not lint */
  
  #include <sys/param.h>
***************
*** 46,70 ****
  #include <sys/stat.h>
  #include <sys/signal.h>
  #include <sys/ioctl.h>
! #include <sys/sysctl.h>
  
  #include <net/if.h>
- #include <net/if_dl.h>
- #include <net/route.h>
  #include <netinet/in.h>
  #include <protocols/rwhod.h>
  
  #include <ctype.h>
  #include <errno.h>
- #include <fcntl.h>
  #include <netdb.h>
! #include <paths.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <syslog.h>
- #include <unistd.h>
  #include <utmp.h>
  
  /*
   * Alarm interval. Don't forget to change the down time check in ruptime
--- 46,117 ----
  #include <sys/stat.h>
  #include <sys/signal.h>
  #include <sys/ioctl.h>
! #include <sys/file.h>
  
  #include <net/if.h>
  #include <netinet/in.h>
+ 
+ #include <arpa/inet.h>
  #include <protocols/rwhod.h>
  
  #include <ctype.h>
  #include <errno.h>
  #include <netdb.h>
! #include <nlist.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <syslog.h>
  #include <utmp.h>
+ #include <unistd.h>
+ #include <paths.h>
+ 
+ int	getloadavg __P((double *, int));
+ 
+ /*
+  * This version of Berkeley's rwhod has been modified to use IP multicast
+  * datagrams, under control of new command-line options:
+  *
+  *	rwhod -m	causes rwhod to use IP multicast (instead of
+  *			broadcast or unicast) on all interfaces that have
+  *			the IFF_MULTICAST flag set in their "ifnet" structs
+  *			(excluding the loopback interface).  The multicast
+  *			reports are sent with a time-to-live of 1, to prevent
+  *			forwarding beyond the directly-connected subnet(s).
+  *
+  *	rwhod -M <ttl>	causes rwhod to send IP multicast datagrams with a
+  *			time-to-live of <ttl>, via a SINGLE interface rather
+  *			than all interfaces.  <ttl> must be between 0 and
+  *			MAX_MULTICAST_SCOPE, defined below.  Note that "-M 1"
+  *			is different than "-m", in that "-M 1" specifies
+  *			transmission on one interface only; the two modes
+  *			are exclusive.
+  *
+  *
+  * When "-m" is used, the program accepts multicast rwhod reports from all
+  * multicast-capable interfaces.  If a <ttl> argument is given, it accepts
+  * multicast reports from only one interface, the one on which reports are
+  * sent (which may be controlled via the host's routing table).  Regardless
+  * of options, the program accepts broadcast or unicast reports from
+  * all interfaces.  Thus, this program will hear the reports of old,
+  * non-multicasting rwhods, but, if multicasting is used, those old rwhods
+  * won't hear the reports generated by this program.
+  *
+  *                  -- Steve Deering, Stanford University, February 1989
+  */
+ 
+ #define NO_MULTICAST		0	  /* multicast modes */
+ #define PER_INTERFACE_MULTICAST	1
+ #define SCOPED_MULTICAST	2
+ 
+ #define MAX_MULTICAST_SCOPE	32	  /* "site-wide", by convention */
+ 
+ #define INADDR_WHOD_GROUP (u_long)0xe0000103      /* 224.0.1.3 */
+ 					  /* (belongs in protocols/rwhod.h) */
+ 
+ int			multicast_mode  = NO_MULTICAST;
+ int			multicast_scope;
+ struct sockaddr_in	multicast_addr;
  
  /*
   * Alarm interval. Don't forget to change the down time check in ruptime
***************
*** 75,88 ****
  char	myname[MAXHOSTNAMELEN];
  
  /*
!  * We communicate with each neighbor in a list constructed at the time we're
!  * started up.  Neighbors are currently directly connected via a hardware
!  * interface.
   */
  struct	neighbor {
  	struct	neighbor *n_next;
  	char	*n_name;		/* interface name */
! 	struct	sockaddr *n_addr;		/* who to send to */
  	int	n_addrlen;		/* size of address */
  	int	n_flags;		/* should forward?, interface flags */
  };
--- 122,136 ----
  char	myname[MAXHOSTNAMELEN];
  
  /*
!  * We communicate with each neighbor in
!  * a list constructed at the time we're
!  * started up.  Neighbors are currently
!  * directly connected via a hardware interface.
   */
  struct	neighbor {
  	struct	neighbor *n_next;
  	char	*n_name;		/* interface name */
! 	char	*n_addr;		/* who to send to */
  	int	n_addrlen;		/* size of address */
  	int	n_flags;		/* should forward?, interface flags */
  };
***************
*** 92,170 ****
  struct	servent *sp;
  int	s, utmpf;
  
! #define	WHDRSIZE	(sizeof(mywd) - sizeof(mywd.wd_we))
  
- int	 configure __P((int));
- void	 getboottime __P((int));
- void	 onalrm __P((int));
- void	 quit __P((char *));
- void	 rt_xaddrs __P((caddr_t, caddr_t, struct rt_addrinfo *));
- int	 verify __P((char *));
  #ifdef DEBUG
! char	*interval __P((int, char *));
! void	 Sendto __P((int, char *, int, int, char *, int));
! #define	 sendto Sendto
  #endif
  
  int
  main(argc, argv)
  	int argc;
! 	char argv[];
  {
! 	struct sockaddr_in from;
  	struct stat st;
  	char path[64];
- 	int on = 1;
- 	char *cp;
- 	struct sockaddr_in sin;
  
  	if (getuid()) {
  		fprintf(stderr, "rwhod: not super user\n");
  		exit(1);
  	}
  	sp = getservbyname("who", "udp");
  	if (sp == NULL) {
  		fprintf(stderr, "rwhod: udp/who: unknown service\n");
  		exit(1);
  	}
! #ifndef DEBUG
! 	daemon(1, 0);
! #endif
  	if (chdir(_PATH_RWHODIR) < 0) {
! 		(void)fprintf(stderr, "rwhod: %s: %s\n",
! 		    _PATH_RWHODIR, strerror(errno));
  		exit(1);
  	}
! 	(void) signal(SIGHUP, getboottime);
! 	openlog("rwhod", LOG_PID, LOG_DAEMON);
  	/*
  	 * Establish host name as returned by system.
  	 */
! 	if (gethostname(myname, sizeof(myname) - 1) < 0) {
  		syslog(LOG_ERR, "gethostname: %m");
  		exit(1);
  	}
! 	if ((cp = index(myname, '.')) != NULL)
  		*cp = '\0';
! 	strncpy(mywd.wd_hostname, myname, sizeof(myname) - 1);
  	utmpf = open(_PATH_UTMP, O_RDONLY|O_CREAT, 0644);
  	if (utmpf < 0) {
  		syslog(LOG_ERR, "%s: %m", _PATH_UTMP);
  		exit(1);
  	}
! 	getboottime(0);
  	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
  		syslog(LOG_ERR, "socket: %m");
  		exit(1);
  	}
! 	if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
  		syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m");
  		exit(1);
  	}
! 	memset(&sin, 0, sizeof(sin));
  	sin.sin_family = AF_INET;
  	sin.sin_port = sp->s_port;
! 	if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
  		syslog(LOG_ERR, "bind: %m");
  		exit(1);
  	}
--- 140,251 ----
  struct	servent *sp;
  int	s, utmpf;
  
! #define	WHDRSIZE	(sizeof (mywd) - sizeof (mywd.wd_we))
! 
! int	configure __P((int));
! void	getkmem __P((int));
! void	onalrm __P((int));
! int	verify __P((char *));
  
  #ifdef DEBUG
! int	prsend __P((int, const void *, int, int, const struct sockaddr *, int));
! #define	sendto prsend
  #endif
  
  int
  main(argc, argv)
  	int argc;
! 	char *argv[];
  {
! 	int ch, on, debug;
! 	char *cp;
  	struct stat st;
+ 	struct sockaddr_in from, sin;
  	char path[64];
  
  	if (getuid()) {
  		fprintf(stderr, "rwhod: not super user\n");
  		exit(1);
  	}
+ 	debug = 0;
+ 	while ((ch = getopt(argc, argv, "dmM:")) != EOF) {
+ 		switch (ch) {
+ 
+ 		case 'd':
+ 			debug = 1;
+ 			break;
+ 
+ 		case 'm':
+ 			multicast_mode = PER_INTERFACE_MULTICAST;
+ 			break;
+ 
+ 		case 'M':
+ 			multicast_mode  = SCOPED_MULTICAST;
+ 			multicast_scope = strtol(optarg, &cp, 10);
+ 			if (cp == optarg || *cp ||
+ 			    (u_int)multicast_scope > MAX_MULTICAST_SCOPE) {
+ 				fprintf(stderr,
+ 				    "rwhod: ttl must not exceed %u\n",
+ 				    MAX_MULTICAST_SCOPE);
+ 				exit(1);
+ 			}
+ 			break;
+ 
+ 		default:
+ 			goto usage;
+ 		}
+ 	}
+ 	if (optind < argc) {
+ usage:
+ 		fprintf(stderr, "usage: rwhod [ -d ] [ -m | -M ttl ]\n");
+ 		exit(1);
+ 	}
+ 
+ 	/* from now on, all errors go via syslog only */
+ 	openlog("rwhod", LOG_PID, debug ? LOG_DAEMON|LOG_PERROR : LOG_DAEMON);
  	sp = getservbyname("who", "udp");
  	if (sp == NULL) {
  		fprintf(stderr, "rwhod: udp/who: unknown service\n");
+ 		syslog(LOG_ERR, "udp/who: unknown service");
  		exit(1);
  	}
! 	if (!debug)
! 		daemon(1, 0);
  	if (chdir(_PATH_RWHODIR) < 0) {
! 		syslog(LOG_ERR, "%s: %m", _PATH_RWHODIR);
  		exit(1);
  	}
! 	(void) signal(SIGHUP, getkmem);
  	/*
  	 * Establish host name as returned by system.
  	 */
! 	if (gethostname(myname, sizeof (myname) - 1) < 0) {
  		syslog(LOG_ERR, "gethostname: %m");
  		exit(1);
  	}
! 	if ((cp = strchr(myname, '.')) != NULL)
  		*cp = '\0';
! 	strncpy(mywd.wd_hostname, myname, sizeof (myname) - 1);
  	utmpf = open(_PATH_UTMP, O_RDONLY|O_CREAT, 0644);
  	if (utmpf < 0) {
  		syslog(LOG_ERR, "%s: %m", _PATH_UTMP);
  		exit(1);
  	}
! 	getkmem(0);
  	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
  		syslog(LOG_ERR, "socket: %m");
  		exit(1);
  	}
! 	on = 1;
! 	if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) {
  		syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m");
  		exit(1);
  	}
! 	bzero(&sin, sizeof(sin));
  	sin.sin_family = AF_INET;
+ 	multicast_addr.sin_family = AF_INET;
  	sin.sin_port = sp->s_port;
! 	if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
  		syslog(LOG_ERR, "bind: %m");
  		exit(1);
  	}
***************
*** 174,182 ****
  	onalrm(0);
  	for (;;) {
  		struct whod wd;
! 		int cc, whod, len = sizeof(from);
  
! 		cc = recvfrom(s, (char *)&wd, sizeof(struct whod), 0,
  			(struct sockaddr *)&from, &len);
  		if (cc <= 0) {
  			if (cc < 0 && errno != EINTR)
--- 255,263 ----
  	onalrm(0);
  	for (;;) {
  		struct whod wd;
! 		int cc, whod, len = sizeof (from);
  
! 		cc = recvfrom(s, (char *)&wd, sizeof (struct whod), 0,
  			(struct sockaddr *)&from, &len);
  		if (cc <= 0) {
  			if (cc < 0 && errno != EINTR)
***************
*** 207,213 ****
  			syslog(LOG_WARNING, "%s: %m", path);
  			continue;
  		}
! #if ENDIAN != BIG_ENDIAN
  		{
  			int i, n = (cc - WHDRSIZE)/sizeof(struct whoent);
  			struct whoent *we;
--- 288,294 ----
  			syslog(LOG_WARNING, "%s: %m", path);
  			continue;
  		}
! #if BYTE_ORDER != BIG_ENDIAN
  		{
  			int i, n = (cc - WHDRSIZE)/sizeof(struct whoent);
  			struct whoent *we;
***************
*** 259,279 ****
  struct	utmp *utmp;
  int	alarmcount;
  
  void
! onalrm(signo)
! 	int signo;
  {
  	register struct neighbor *np;
  	register struct whoent *we = mywd.wd_we, *wlast;
  	register int i;
  	struct stat stb;
- 	double avenrun[3];
- 	time_t now;
  	int cc;
  
- 	now = time(NULL);
  	if (alarmcount % 10 == 0)
! 		getboottime(0);
  	alarmcount++;
  	(void) fstat(utmpf, &stb);
  	if ((stb.st_mtime != utmptime) || (stb.st_size > utmpsize)) {
--- 340,361 ----
  struct	utmp *utmp;
  int	alarmcount;
  
+ /* ARGSUSED */
  void
! onalrm(sig)
! 	int sig;
  {
  	register struct neighbor *np;
  	register struct whoent *we = mywd.wd_we, *wlast;
  	register int i;
  	struct stat stb;
  	int cc;
+ 	double avenrun[3];
+ 	time_t now = time((time_t *)NULL);
+ 	char *strerror();
  
  	if (alarmcount % 10 == 0)
! 		getkmem(0);
  	alarmcount++;
  	(void) fstat(utmpf, &stb);
  	if ((stb.st_mtime != utmptime) || (stb.st_size > utmpsize)) {
***************
*** 284,310 ****
  				utmp = (struct utmp *)realloc(utmp, utmpsize);
  			else
  				utmp = (struct utmp *)malloc(utmpsize);
! 			if (! utmp) {
  				fprintf(stderr, "rwhod: malloc failed\n");
  				utmpsize = 0;
  				goto done;
  			}
  		}
! 		(void) lseek(utmpf, (off_t)0, L_SET);
  		cc = read(utmpf, (char *)utmp, stb.st_size);
  		if (cc < 0) {
  			fprintf(stderr, "rwhod: %s: %s\n",
  			    _PATH_UTMP, strerror(errno));
  			goto done;
  		}
! 		wlast = &mywd.wd_we[1024 / sizeof(struct whoent) - 1];
! 		utmpent = cc / sizeof(struct utmp);
  		for (i = 0; i < utmpent; i++)
  			if (utmp[i].ut_name[0]) {
! 				memcpy(we->we_utmp.out_line, utmp[i].ut_line,
! 				   sizeof(utmp[i].ut_line));
! 				memcpy(we->we_utmp.out_name, utmp[i].ut_name,
! 				   sizeof(utmp[i].ut_name));
  				we->we_utmp.out_time = htonl(utmp[i].ut_time);
  				if (we >= wlast)
  					break;
--- 366,392 ----
  				utmp = (struct utmp *)realloc(utmp, utmpsize);
  			else
  				utmp = (struct utmp *)malloc(utmpsize);
! 			if (utmp == NULL) {
  				fprintf(stderr, "rwhod: malloc failed\n");
  				utmpsize = 0;
  				goto done;
  			}
  		}
! 		(void) lseek(utmpf, (long)0, L_SET);
  		cc = read(utmpf, (char *)utmp, stb.st_size);
  		if (cc < 0) {
  			fprintf(stderr, "rwhod: %s: %s\n",
  			    _PATH_UTMP, strerror(errno));
  			goto done;
  		}
! 		wlast = &mywd.wd_we[1024 / sizeof (struct whoent) - 1];
! 		utmpent = cc / sizeof (struct utmp);
  		for (i = 0; i < utmpent; i++)
  			if (utmp[i].ut_name[0]) {
! 				bcopy(utmp[i].ut_line, we->we_utmp.out_line,
! 				   sizeof (utmp[i].ut_line));
! 				bcopy(utmp[i].ut_name, we->we_utmp.out_name,
! 				   sizeof (utmp[i].ut_name));
  				we->we_utmp.out_time = htonl(utmp[i].ut_time);
  				if (we >= wlast)
  					break;
***************
*** 328,343 ****
  			we->we_idle = htonl(now - stb.st_atime);
  		we++;
  	}
! 	(void)getloadavg(avenrun, sizeof(avenrun)/sizeof(avenrun[0]));
  	for (i = 0; i < 3; i++)
  		mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100));
  	cc = (char *)we - (char *)&mywd;
  	mywd.wd_sendtime = htonl(time(0));
  	mywd.wd_vers = WHODVERSION;
  	mywd.wd_type = WHODTYPE_STATUS;
! 	for (np = neighbors; np != NULL; np = np->n_next)
! 		(void)sendto(s, (char *)&mywd, cc, 0,
! 				np->n_addr, np->n_addrlen);
  	if (utmpent && chdir(_PATH_RWHODIR)) {
  		syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR);
  		exit(1);
--- 410,446 ----
  			we->we_idle = htonl(now - stb.st_atime);
  		we++;
  	}
! 	(void) getloadavg(avenrun, sizeof(avenrun)/sizeof(avenrun[0]));
  	for (i = 0; i < 3; i++)
  		mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100));
  	cc = (char *)we - (char *)&mywd;
  	mywd.wd_sendtime = htonl(time(0));
  	mywd.wd_vers = WHODVERSION;
  	mywd.wd_type = WHODTYPE_STATUS;
! 	if (multicast_mode == SCOPED_MULTICAST) {
! 		(void) sendto(s, (char *)&mywd, cc, 0,
! 		    (struct sockaddr *)&multicast_addr, sizeof(multicast_addr));
! 	}
! 	else for (np = neighbors; np != NULL; np = np->n_next) {
! 		if (multicast_mode == PER_INTERFACE_MULTICAST &&
! 		    np->n_flags & IFF_MULTICAST) {
! 			/*
! 			 * Select the outgoing interface for the multicast.
! 			 */
! 			if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
! 			    &(((struct sockaddr_in *)np->n_addr)->sin_addr),
! 			    sizeof(struct in_addr)) < 0) {
! 				syslog(LOG_ERR,
! 					"setsockopt IP_MULTICAST_IF: %m");
! 				exit(1);
! 			}
! 			(void) sendto(s, (char *)&mywd, cc, 0,
! 				(struct sockaddr *)&multicast_addr,
! 				sizeof(multicast_addr));
! 		} else
! 			(void) sendto(s, (char *)&mywd, cc, 0,
! 				(struct sockaddr *)np->n_addr, np->n_addrlen);
! 	}
  	if (utmpent && chdir(_PATH_RWHODIR)) {
  		syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR);
  		exit(1);
***************
*** 346,396 ****
  	(void) alarm(AL_INTERVAL);
  }
  
  void
! getboottime(signo)
! 	int signo;
! {
! 	int mib[2];
! 	size_t size;
! 	struct timeval tm;
! 
! 	mib[0] = CTL_KERN;
! 	mib[1] = KERN_BOOTTIME;
! 	size = sizeof(tm);
! 	if (sysctl(mib, 2, &tm, &size, NULL, 0) == -1) {
! 		syslog(LOG_ERR, "cannot get boottime: %m");
! 		exit(1);
! 	}
! 	mywd.wd_boottime = htonl(tm.tv_sec);
! }
! 
! void
! quit(msg)
! 	char *msg;
  {
! 	syslog(LOG_ERR, msg);
! 	exit(1);
! }
! 
! #define ROUNDUP(a) \
! 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
! #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
! 
! void
! rt_xaddrs(cp, cplim, rtinfo)
! 	register caddr_t cp, cplim;
! 	register struct rt_addrinfo *rtinfo;
! {
! 	register struct sockaddr *sa;
! 	register int i;
! 
! 	memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info));
! 	for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
! 		if ((rtinfo->rti_addrs & (1 << i)) == 0)
! 			continue;
! 		rtinfo->rti_info[i] = sa = (struct sockaddr *)cp;
! 		ADVANCE(cp, sa);
! 	}
  }
  
  /*
--- 449,492 ----
  	(void) alarm(AL_INTERVAL);
  }
  
+ /* ARGSUSED */
  void
! getkmem(sig)
! 	int sig;
  {
! 	static ino_t kernel_ino;
! 	static time_t kernel_ctime;
! 	int kmemf;
! 	struct stat sb;
! 	static struct nlist nl[] = {
! #define	NL_BOOTTIME	0
! 		{ "_boottime" },
! 		0
! 	};
! 
! 	if (stat(_PATH_KERNEL, &sb) < 0) {
! 		if (kernel_ctime)
! 			return;
! 	} else {
! 		if (sb.st_ctime == kernel_ctime && sb.st_ino == kernel_ino)
! 			return;
! 		kernel_ctime = sb.st_ctime;
! 		kernel_ino = sb.st_ino;
! 	}
! 	while (nlist(_PATH_KERNEL, nl)) {
! 		syslog(LOG_WARNING, "%s: namelist botch", _PATH_KERNEL);
! 		sleep(300);
! 	}
! 	kmemf = open(_PATH_KMEM, O_RDONLY, 0);
! 	if (kmemf < 0) {
! 		syslog(LOG_ERR, "%s: %m", _PATH_KMEM);
! 		exit(1);
! 	}
! 	(void) lseek(kmemf, (long)nl[NL_BOOTTIME].n_value, L_SET);
! 	(void) read(kmemf, (char *)&mywd.wd_boottime,
! 	    sizeof (mywd.wd_boottime));
! 	(void) close(kmemf);
! 	mywd.wd_boottime = htonl(mywd.wd_boottime);
  }
  
  /*
***************
*** 401,499 ****
  configure(s)
  	int s;
  {
  	register struct neighbor *np;
! 	register struct if_msghdr *ifm;
! 	register struct ifa_msghdr *ifam;
! 	struct sockaddr_dl *sdl;
! 	size_t needed;
! 	int mib[6], flags = 0, len;
! 	char *buf, *lim, *next;
! 	struct rt_addrinfo info;
! 
! 	mib[0] = CTL_NET;
! 	mib[1] = PF_ROUTE;
! 	mib[2] = 0;
! 	mib[3] = AF_INET;
! 	mib[4] = NET_RT_IFLIST;
! 	mib[5] = 0;
! 	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
! 		quit("route-sysctl-estimate");
! 	if ((buf = malloc(needed)) == NULL)
! 		quit("malloc");
! 	if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
! 		quit("actual retrieval of interface table");
! 	lim = buf + needed;
! 
! 	sdl = NULL;		/* XXX just to keep gcc -Wall happy */
! 	for (next = buf; next < lim; next += ifm->ifm_msglen) {
! 		ifm = (struct if_msghdr *)next;
! 		if (ifm->ifm_type == RTM_IFINFO) {
! 			sdl = (struct sockaddr_dl *)(ifm + 1);
! 			flags = ifm->ifm_flags;
! 			continue;
! 		}
! 		if ((flags & IFF_UP) == 0 ||
! 		    (flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0)
! 			continue;
! 		if (ifm->ifm_type != RTM_NEWADDR)
! 			quit("out of sync parsing NET_RT_IFLIST");
! 		ifam = (struct ifa_msghdr *)ifm;
! 		info.rti_addrs = ifam->ifam_addrs;
! 		rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam,
! 			&info);
! 		/* gag, wish we could get rid of Internet dependencies */
! #define dstaddr	info.rti_info[RTAX_BRD]
! #define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr
! #define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port
! 		if (dstaddr == 0 || dstaddr->sa_family != AF_INET)
  			continue;
- 		PORT_SA(dstaddr) = sp->s_port;
  		for (np = neighbors; np != NULL; np = np->n_next)
! 			if (memcmp(sdl->sdl_data, np->n_name,
! 				   sdl->sdl_nlen) == 0 &&
! 			    IPADDR_SA(np->n_addr) == IPADDR_SA(dstaddr))
  				break;
  		if (np != NULL)
  			continue;
! 		len = sizeof(*np) + dstaddr->sa_len + sdl->sdl_nlen + 1;
! 		np = (struct neighbor *)malloc(len);
  		if (np == NULL)
! 			quit("malloc of neighbor structure");
! 		memset(np, 0, len);
! 		np->n_flags = flags;
! 		np->n_addr = (struct sockaddr *)(np + 1);
! 		np->n_addrlen = dstaddr->sa_len;
! 		np->n_name = np->n_addrlen + (char *)np->n_addr;
  		np->n_next = neighbors;
  		neighbors = np;
- 		memcpy((char *)np->n_addr, (char *)dstaddr, np->n_addrlen);
- 		memcpy(np->n_name, sdl->sdl_data, sdl->sdl_nlen);
  	}
- 	free(buf);
  	return (1);
  }
  
  #ifdef DEBUG
! void
! Sendto(s, buf, cc, flags, to, tolen)
  	int s;
! 	char *buf;
! 	int cc, flags;
! 	char *to;
  	int tolen;
  {
  	register struct whod *w = (struct whod *)buf;
  	register struct whoent *we;
  	struct sockaddr_in *sin = (struct sockaddr_in *)to;
  
! 	printf("sendto %x.%d\n", ntohl(sin->sin_addr), ntohs(sin->sin_port));
  	printf("hostname %s %s\n", w->wd_hostname,
  	   interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), "  up"));
  	printf("load %4.2f, %4.2f, %4.2f\n",
  	    ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0,
  	    ntohl(w->wd_loadav[2]) / 100.0);
  	cc -= WHDRSIZE;
! 	for (we = w->wd_we, cc /= sizeof(struct whoent); cc > 0; cc--, we++) {
  		time_t t = ntohl(we->we_utmp.out_time);
  		printf("%-8.8s %s:%s %.12s",
  			we->we_utmp.out_name,
--- 497,666 ----
  configure(s)
  	int s;
  {
+ 	char buf[BUFSIZ], *cp, *cplim;
+ 	struct ifconf ifc;
+ 	struct ifreq ifreq, *ifr;
+ 	struct sockaddr_in *sin;
  	register struct neighbor *np;
! 
! 	if (multicast_mode == SCOPED_MULTICAST) {
! 		struct ip_mreq mreq;
! 		unsigned char ttl;
! 
! 		mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP);
! 		mreq.imr_interface.s_addr = htonl(INADDR_ANY);
! 		if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
! 					&mreq, sizeof(mreq)) < 0) {
! 			syslog(LOG_ERR,
! 				"setsockopt IP_ADD_MEMBERSHIP: %m");
! 			return (0);
! 		}
! 		ttl = multicast_scope;
! 		if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL,
! 					&ttl, sizeof(ttl)) < 0) {
! 			syslog(LOG_ERR,
! 				"setsockopt IP_MULTICAST_TTL: %m");
! 			return (0);
! 		}
! 		multicast_addr.sin_addr.s_addr = htonl(INADDR_WHOD_GROUP);
! 		multicast_addr.sin_port = sp->s_port;
! 		return (1);
! 	}
! 
! 	ifc.ifc_len = sizeof (buf);
! 	ifc.ifc_buf = buf;
! 	if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
! 		syslog(LOG_ERR, "ioctl (get interface configuration)");
! 		return (0);
! 	}
! 	ifr = ifc.ifc_req;
! #ifdef AF_LINK
! #define max(a, b) (a > b ? a : b)
! #define size(p)	max((p).sa_len, sizeof(p))
! #else
! #define size(p) (sizeof (p))
! #endif
! 	cplim = buf + ifc.ifc_len; /* skip over if's with big ifr_addr's */
! 	for (cp = buf; cp < cplim;
! 			cp += sizeof (ifr->ifr_name) + size(ifr->ifr_addr)) {
! 		ifr = (struct ifreq *)cp;
! 		if (ifr->ifr_addr.sa_family != AF_INET)
  			continue;
  		for (np = neighbors; np != NULL; np = np->n_next)
! 			if (np->n_name &&
! 			    strcmp(ifr->ifr_name, np->n_name) == 0)
  				break;
  		if (np != NULL)
  			continue;
! 		ifreq = *ifr;
! 		np = (struct neighbor *)malloc(sizeof (*np));
  		if (np == NULL)
! 			continue;
! 		np->n_name = malloc(strlen(ifr->ifr_name) + 1);
! 		if (np->n_name == NULL) {
! 			free((char *)np);
! 			continue;
! 		}
! 		strcpy(np->n_name, ifr->ifr_name);
! 		np->n_addrlen = sizeof (ifr->ifr_addr);
! 		np->n_addr = malloc(np->n_addrlen);
! 		if (np->n_addr == NULL) {
! 			free(np->n_name);
! 			free((char *)np);
! 			continue;
! 		}
! 		bcopy((char *)&ifr->ifr_addr, np->n_addr, np->n_addrlen);
! 		if (ioctl(s, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
! 			syslog(LOG_ERR, "ioctl (get interface flags)");
! 			free((char *)np);
! 			continue;
! 		}
! 		if (multicast_mode == PER_INTERFACE_MULTICAST &&
! 		    (ifreq.ifr_flags & IFF_UP) &&
! 		    (ifreq.ifr_flags & IFF_MULTICAST) &&
! 		   !(ifreq.ifr_flags & IFF_LOOPBACK)) {
! 			struct ip_mreq mreq;
! 
! 			mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP);
! 			mreq.imr_interface.s_addr =
! 			  ((struct sockaddr_in *)np->n_addr)->sin_addr.s_addr;
! 			if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
! 						&mreq, sizeof(mreq)) < 0) {
! 				syslog(LOG_ERR,
! 					"setsockopt IP_ADD_MEMBERSHIP: %m");
! 				free(np->n_addr);
! 				free(np->n_name);
! 				free((char *)np);
! 				continue;
! 			}
! 			multicast_addr.sin_addr.s_addr
! 						= htonl(INADDR_WHOD_GROUP);
! 			multicast_addr.sin_port = sp->s_port;
! 			np->n_flags = ifreq.ifr_flags;
! 			np->n_next = neighbors;
! 			neighbors = np;
! 			continue;
! 		}
! 		if ((ifreq.ifr_flags & IFF_UP) == 0 ||
! 		    (ifreq.ifr_flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0) {
! 			free((char *)np);
! 			continue;
! 		}
! 		np->n_flags = ifreq.ifr_flags;
! 		if (np->n_flags & IFF_POINTOPOINT) {
! 			if (ioctl(s, SIOCGIFDSTADDR, (char *)&ifreq) < 0) {
! 				syslog(LOG_ERR, "ioctl (get dstaddr)");
! 				free((char *)np);
! 				continue;
! 			}
! 			/* we assume addresses are all the same size */
! 			bcopy((char *)&ifreq.ifr_dstaddr,
! 			  np->n_addr, np->n_addrlen);
! 		}
! 		if (np->n_flags & IFF_BROADCAST) {
! 			if (ioctl(s, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
! 				syslog(LOG_ERR, "ioctl (get broadaddr)");
! 				free((char *)np);
! 				continue;
! 			}
! 			/* we assume addresses are all the same size */
! 			bcopy((char *)&ifreq.ifr_broadaddr,
! 			  np->n_addr, np->n_addrlen);
! 		}
! 		/* gag, wish we could get rid of Internet dependencies */
! 		sin = (struct sockaddr_in *)np->n_addr;
! 		sin->sin_port = sp->s_port;
  		np->n_next = neighbors;
  		neighbors = np;
  	}
  	return (1);
  }
  
  #ifdef DEBUG
! /* ARGSUSED */
! int
! prsend(s, buf, cc0, flags, to, tolen)
  	int s;
! 	const void *buf;
! 	int cc0, flags;
! 	const struct sockaddr *to;
  	int tolen;
  {
  	register struct whod *w = (struct whod *)buf;
  	register struct whoent *we;
+ 	register int cc = cc0;
  	struct sockaddr_in *sin = (struct sockaddr_in *)to;
+ 	char *interval __P((int, const char *));
  
! 	printf("sendto %lx.%d\n", ntohl(sin->sin_addr.s_addr),
! 	    ntohs(sin->sin_port));
  	printf("hostname %s %s\n", w->wd_hostname,
  	   interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), "  up"));
  	printf("load %4.2f, %4.2f, %4.2f\n",
  	    ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0,
  	    ntohl(w->wd_loadav[2]) / 100.0);
  	cc -= WHDRSIZE;
! 	for (we = w->wd_we, cc /= sizeof (struct whoent); cc > 0; cc--, we++) {
  		time_t t = ntohl(we->we_utmp.out_time);
  		printf("%-8.8s %s:%s %.12s",
  			we->we_utmp.out_name,
***************
*** 511,516 ****
--- 678,684 ----
  		}
  		printf("\n");
  	}
+ 	return (cc0);
  }
  
  char *
===================================================================
RCS file: usr.sbin/rwhod/rwhod.8,v
retrieving revision 1.1
diff -c -r1.1 usr.sbin/rwhod/rwhod.8
*** 1.1	1995/10/06 06:42:57
--- usr.sbin/rwhod/rwhod.8	1995/10/06 06:52:34
***************
*** 1,5 ****
! .\" Copyright (c) 1983, 1991, 1993
! .\"	The Regents of the University of California.  All rights reserved.
  .\"
  .\" Redistribution and use in source and binary forms, with or without
  .\" modification, are permitted provided that the following conditions
--- 1,5 ----
! .\" Copyright (c) 1983, 1991 The Regents of the University of California.
! .\" All rights reserved.
  .\"
  .\" Redistribution and use in source and binary forms, with or without
  .\" modification, are permitted provided that the following conditions
***************
*** 29,69 ****
  .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  .\" SUCH DAMAGE.
  .\"
! .\"     @(#)rwhod.8	8.2 (Berkeley) 12/11/93
  .\"
! .Dd December 11, 1993
  .Dt RWHOD 8
  .Os BSD 4.2
  .Sh NAME
  .Nm rwhod
  .Nd system status server
  .Sh SYNOPSIS
! .Nm rwhod
  .Sh DESCRIPTION
! .Nm Rwhod
! is the server which maintains the database used by the
  .Xr rwho 1
  and
  .Xr ruptime 1
  programs.  Its operation is predicated on the ability to
  .Em broadcast
  messages on a network.
  .Pp
! .Nm Rwhod
! operates as both a producer and consumer of status information.
  As a producer of information it periodically
  queries the state of the system and constructs
! status messages which are broadcast on a network.
  As a consumer of information, it listens for other
! .Nm rwhod
  servers' status messages, validating them, then recording
  them in a collection of files located in the directory
  .Pa /var/rwho .
  .Pp
  The server transmits and receives messages at the port indicated
  in the ``rwho'' service specification; see 
  .Xr services 5 .
! The messages sent and received, are of the form:
  .Bd -literal -offset indent
  struct	outmp {
  	char	out_line[8];		/* tty name */
--- 29,119 ----
  .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  .\" SUCH DAMAGE.
  .\"
! .\"     @(#)rwhod.8	6.5 (Berkeley) 3/16/91
  .\"
! .Dd March 16, 1991
  .Dt RWHOD 8
  .Os BSD 4.2
  .Sh NAME
  .Nm rwhod
  .Nd system status server
  .Sh SYNOPSIS
! .Nm
! .br
! .Nm
! .Fl m
! .br
! .Nm
! .Fl M Ar ttl
  .Sh DESCRIPTION
! The
! .Nm
! server maintains the database used by the
  .Xr rwho 1
  and
  .Xr ruptime 1
  programs.  Its operation is predicated on the ability to
  .Em broadcast
+ or
+ .Em multicast
  messages on a network.
  .Pp
! The
! .Nm
! program operates as both a producer and consumer of status information.
  As a producer of information it periodically
  queries the state of the system and constructs
! status messages which are broadcast or multicast on a network.
  As a consumer of information, it listens for other
! .Nm
  servers' status messages, validating them, then recording
  them in a collection of files located in the directory
  .Pa /var/rwho .
  .Pp
+ The
+ .Fl m
+ and
+ .Fl M
+ flags configure
+ .Nm
+ to use multicast rather than broadcast.
+ Under
+ .Fl m ,
+ .Nm
+ sends its multicast message to all multicast-capable interfaces
+ (those with
+ .Dv IFF_MULTICAST
+ set in their flags; see
+ .Xr ifconfig 8 ).
+ In this case the time-to-live is fixed at 1,
+ preventing the multicast from being forwarded beyond
+ any directly-connected subnets.
+ With
+ .Fl M ,
+ .Nm
+ sends its multicast message with the given
+ .Ar ttl
+ as its time to live, but to the
+ .Dq whod
+ group
+ .Pq Dv 224.0.1.3
+ rather than individually to each interface.
+ This is intended to be used with a multicast router
+ which will distribute the message to members of the multicast group.
+ .Pp
+ In any mode, broadcast or multicast,
+ .Nm
+ will accept all forms of reports.
+ This means that if broadcast and multicast
+ .Nm
+ servers are combined on one network,
+ the multicast-capable systems will see everyone,
+ while the broadcast-only machines will see only each other.
+ .Pp
  The server transmits and receives messages at the port indicated
  in the ``rwho'' service specification; see 
  .Xr services 5 .
! The messages sent and received are of the form:
  .Bd -literal -offset indent
  struct	outmp {
  	char	out_line[8];		/* tty name */
***************
*** 126,146 ****
  performs an
  .Xr nlist 3
  on
! .Pa /kernel
  every 30 minutes to guard against
  the possibility that this file is not the system
  image currently operating.
  .Sh SEE ALSO
  .Xr rwho 1 ,
  .Xr ruptime 1
  .Sh BUGS
- There should be a way to relay status information between networks. 
  Status information should be sent only upon request rather than continuously.
  People often interpret the server dying
! or network communication failures
  as a machine going down.
  .Sh HISTORY
  The
  .Nm
  command appeared in
  .Bx 4.2 .
--- 176,197 ----
  performs an
  .Xr nlist 3
  on
! .Pa /bsd
  every 30 minutes to guard against
  the possibility that this file is not the system
  image currently operating.
  .Sh SEE ALSO
+ .Xr mrouted 8 ,
  .Xr rwho 1 ,
  .Xr ruptime 1
  .Sh BUGS
  Status information should be sent only upon request rather than continuously.
  People often interpret the server dying
! or network communtication failures
  as a machine going down.
  .Sh HISTORY
  The
  .Nm
  command appeared in
  .Bx 4.2 .
+ The multicast additions are from Stanford University.
>Release-Note:
>Audit-Trail:

From: "Garrett A. Wollman" <wollman@lcs.mit.edu>
To: lyndon@orthanc.com
Cc: FreeBSD-gnats-submit@freebsd.org
Subject: bin/768: rwhod does not support multicast (+FIX)
Date: Fri, 6 Oct 1995 11:01:32 -0400

 <<On Fri, 6 Oct 1995 00:13:27 -0700 (PDT), Lyndon Nerenberg <lyndon@orthanc.com> said:
 
 > 	rwhod has been extended to support multicast broascasts (sic).
 > 	This is highly useful when monitoring machines on disparate
 > 	networks. FreeBSD doesn't support this (yet).
 
 This has been in place in -current for some time now.
 
 	
 > 	The appended diff patches /usr/src/usr.sbin/rwhod/rwhod.[c8]
 > 	to include support for multicast. It also adds a definition
 > 	for _PATH_KERNEL to /usr/src/include/paths.h. 
 
 That is not the correct way to find the name of the kernel under
 FreeBSD.  Use getbootfile(3).
 
 -GAWollman
 
 --
 Garrett A. Wollman   | Shashish is simple, it's discreet, it's brief. ... 
 wollman@lcs.mit.edu  | Shashish is the bonding of hearts in spite of distance.
 Opinions not those of| It is a bond more powerful than absence.  We like people
 MIT, LCS, ANA, or NSA| who like Shashish.  - Claude McKenzie + Florent Vollant
State-Changed-From-To: open->closed 
State-Changed-By: wollman 
State-Changed-When: Fri Oct 6 08:16:51 PDT 1995 
State-Changed-Why:  
Multicast support is in current. 
>Unformatted:
