rwhod.c revision 41895
11553Srgrimes/* 21553Srgrimes * Copyright (c) 1983, 1993 31553Srgrimes * The Regents of the University of California. All rights reserved. 41553Srgrimes * 51553Srgrimes * Redistribution and use in source and binary forms, with or without 61553Srgrimes * modification, are permitted provided that the following conditions 71553Srgrimes * are met: 81553Srgrimes * 1. Redistributions of source code must retain the above copyright 91553Srgrimes * notice, this list of conditions and the following disclaimer. 101553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111553Srgrimes * notice, this list of conditions and the following disclaimer in the 121553Srgrimes * documentation and/or other materials provided with the distribution. 131553Srgrimes * 3. All advertising materials mentioning features or use of this software 141553Srgrimes * must display the following acknowledgement: 151553Srgrimes * This product includes software developed by the University of 161553Srgrimes * California, Berkeley and its contributors. 171553Srgrimes * 4. Neither the name of the University nor the names of its contributors 181553Srgrimes * may be used to endorse or promote products derived from this software 191553Srgrimes * without specific prior written permission. 201553Srgrimes * 211553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311553Srgrimes * SUCH DAMAGE. 321553Srgrimes */ 331553Srgrimes 341553Srgrimes#ifndef lint 3530380Scharnierstatic const char copyright[] = 361553Srgrimes"@(#) Copyright (c) 1983, 1993\n\ 371553Srgrimes The Regents of the University of California. All rights reserved.\n"; 381553Srgrimes#endif /* not lint */ 391553Srgrimes 401553Srgrimes#ifndef lint 4130380Scharnier#if 0 421553Srgrimesstatic char sccsid[] = "@(#)rwhod.c 8.1 (Berkeley) 6/6/93"; 4330380Scharnier#endif 4430380Scharnierstatic const char rcsid[] = 4541895Sdes "$Id: rwhod.c,v 1.7 1997/10/13 11:27:55 charnier Exp $"; 461553Srgrimes#endif /* not lint */ 471553Srgrimes 481553Srgrimes#include <sys/param.h> 491553Srgrimes#include <sys/socket.h> 501553Srgrimes#include <sys/stat.h> 511553Srgrimes#include <sys/signal.h> 521553Srgrimes#include <sys/ioctl.h> 531553Srgrimes#include <sys/sysctl.h> 541553Srgrimes 551553Srgrimes#include <net/if.h> 561553Srgrimes#include <net/if_dl.h> 571553Srgrimes#include <net/route.h> 581553Srgrimes#include <netinet/in.h> 591553Srgrimes#include <protocols/rwhod.h> 601553Srgrimes 611553Srgrimes#include <ctype.h> 6230380Scharnier#include <err.h> 631553Srgrimes#include <errno.h> 641553Srgrimes#include <fcntl.h> 651553Srgrimes#include <netdb.h> 661553Srgrimes#include <paths.h> 671553Srgrimes#include <stdio.h> 681553Srgrimes#include <stdlib.h> 691553Srgrimes#include <string.h> 701553Srgrimes#include <syslog.h> 711553Srgrimes#include <unistd.h> 721553Srgrimes#include <utmp.h> 7317829Spst#include <pwd.h> 7418092Speter#include <grp.h> 751553Srgrimes 761553Srgrimes/* 7710087Sjkh * This version of Berkeley's rwhod has been modified to use IP multicast 7810087Sjkh * datagrams, under control of a new command-line option: 7910087Sjkh * 8010087Sjkh * rwhod -m causes rwhod to use IP multicast (instead of 8110087Sjkh * broadcast or unicast) on all interfaces that have 8210087Sjkh * the IFF_MULTICAST flag set in their "ifnet" structs 8310087Sjkh * (excluding the loopback interface). The multicast 8410087Sjkh * reports are sent with a time-to-live of 1, to prevent 8510087Sjkh * forwarding beyond the directly-connected subnet(s). 8610087Sjkh * 8710087Sjkh * rwhod -m <ttl> causes rwhod to send IP multicast datagrams with a 8810087Sjkh * time-to-live of <ttl>, via a SINGLE interface rather 8910087Sjkh * than all interfaces. <ttl> must be between 0 and 9010087Sjkh * MAX_MULTICAST_SCOPE, defined below. Note that "-m 1" 9110087Sjkh * is different than "-m", in that "-m 1" specifies 9210087Sjkh * transmission on one interface only. 9310087Sjkh * 9410087Sjkh * When "-m" is used without a <ttl> argument, the program accepts multicast 9510087Sjkh * rwhod reports from all multicast-capable interfaces. If a <ttl> argument 9610087Sjkh * is given, it accepts multicast reports from only one interface, the one 9710087Sjkh * on which reports are sent (which may be controlled via the host's routing 9810087Sjkh * table). Regardless of the "-m" option, the program accepts broadcast or 9910087Sjkh * unicast reports from all interfaces. Thus, this program will hear the 10010087Sjkh * reports of old, non-multicasting rwhods, but, if multicasting is used, 10110087Sjkh * those old rwhods won't hear the reports generated by this program. 10210087Sjkh * 10310087Sjkh * -- Steve Deering, Stanford University, February 1989 10410087Sjkh */ 10510087Sjkh 10617832Spst#define UNPRIV_USER "daemon" 10717829Spst#define UNPRIV_GROUP "daemon" 10817829Spst 10910087Sjkh#define NO_MULTICAST 0 /* multicast modes */ 11010087Sjkh#define PER_INTERFACE_MULTICAST 1 11110087Sjkh#define SCOPED_MULTICAST 2 11210087Sjkh 11310087Sjkh#define MAX_MULTICAST_SCOPE 32 /* "site-wide", by convention */ 11410087Sjkh 11510087Sjkh#define INADDR_WHOD_GROUP (u_long)0xe0000103 /* 224.0.1.3 */ 11610087Sjkh /* (belongs in protocols/rwhod.h) */ 11710087Sjkh 11841895Sdesint insecure_mode; 11910087Sjkhint multicast_mode = NO_MULTICAST; 12010087Sjkhint multicast_scope; 12110087Sjkhstruct sockaddr_in multicast_addr = { sizeof multicast_addr, AF_INET }; 12210087Sjkh 12310087Sjkh/* 1241553Srgrimes * Alarm interval. Don't forget to change the down time check in ruptime 1251553Srgrimes * if this is changed. 1261553Srgrimes */ 1271553Srgrimes#define AL_INTERVAL (3 * 60) 1281553Srgrimes 1291553Srgrimeschar myname[MAXHOSTNAMELEN]; 1301553Srgrimes 1311553Srgrimes/* 1321553Srgrimes * We communicate with each neighbor in a list constructed at the time we're 1331553Srgrimes * started up. Neighbors are currently directly connected via a hardware 1341553Srgrimes * interface. 1351553Srgrimes */ 1361553Srgrimesstruct neighbor { 1371553Srgrimes struct neighbor *n_next; 1381553Srgrimes char *n_name; /* interface name */ 1391553Srgrimes struct sockaddr *n_addr; /* who to send to */ 1401553Srgrimes int n_addrlen; /* size of address */ 1411553Srgrimes int n_flags; /* should forward?, interface flags */ 1421553Srgrimes}; 1431553Srgrimes 1441553Srgrimesstruct neighbor *neighbors; 1451553Srgrimesstruct whod mywd; 1461553Srgrimesstruct servent *sp; 1471553Srgrimesint s, utmpf; 1481553Srgrimes 1491553Srgrimes#define WHDRSIZE (sizeof(mywd) - sizeof(mywd.wd_we)) 1501553Srgrimes 15117829Spstvoid run_as __P((uid_t *, gid_t *)); 1521553Srgrimesint configure __P((int)); 1531553Srgrimesvoid getboottime __P((int)); 1541553Srgrimesvoid onalrm __P((int)); 1551553Srgrimesvoid quit __P((char *)); 1561553Srgrimesvoid rt_xaddrs __P((caddr_t, caddr_t, struct rt_addrinfo *)); 15717829Spstint verify __P((char *, int)); 15830380Scharnierstatic void usage __P((void)); 1591553Srgrimes#ifdef DEBUG 1601553Srgrimeschar *interval __P((int, char *)); 16117829Spstvoid Sendto __P((int, const void *, size_t, int, 16217829Spst const struct sockaddr *, int)); 1631553Srgrimes#define sendto Sendto 1641553Srgrimes#endif 1651553Srgrimes 1661553Srgrimesint 1671553Srgrimesmain(argc, argv) 1681553Srgrimes int argc; 16910087Sjkh char *argv[]; 1701553Srgrimes{ 1711553Srgrimes struct sockaddr_in from; 1721553Srgrimes struct stat st; 1731553Srgrimes char path[64]; 1741553Srgrimes int on = 1; 1751553Srgrimes char *cp; 1761553Srgrimes struct sockaddr_in sin; 17717829Spst uid_t unpriv_uid; 17817829Spst gid_t unpriv_gid; 1791553Srgrimes 18030380Scharnier if (getuid()) 18130380Scharnier errx(1, "not super user"); 18217829Spst 18317829Spst run_as(&unpriv_uid, &unpriv_gid); 18417829Spst 18510087Sjkh argv++; argc--; 18610087Sjkh while (argc > 0 && *argv[0] == '-') { 18710087Sjkh if (strcmp(*argv, "-m") == 0) { 18810087Sjkh if (argc > 1 && isdigit(*(argv + 1)[0])) { 18910087Sjkh argv++, argc--; 19010087Sjkh multicast_mode = SCOPED_MULTICAST; 19110087Sjkh multicast_scope = atoi(*argv); 19230380Scharnier if (multicast_scope > MAX_MULTICAST_SCOPE) 19330380Scharnier errx(1, "ttl must not exceed %u", 19410087Sjkh MAX_MULTICAST_SCOPE); 19510087Sjkh } 19610087Sjkh else multicast_mode = PER_INTERFACE_MULTICAST; 19710087Sjkh } 19841895Sdes else if (strcmp(*argv, "-i") == 0) 19941895Sdes insecure_mode = 1; 20030380Scharnier else 20130380Scharnier usage(); 20210087Sjkh argv++, argc--; 20310087Sjkh } 20430380Scharnier if (argc > 0) 20530380Scharnier usage(); 2061553Srgrimes#ifndef DEBUG 2071553Srgrimes daemon(1, 0); 2081553Srgrimes#endif 20910087Sjkh (void) signal(SIGHUP, getboottime); 21010087Sjkh openlog("rwhod", LOG_PID, LOG_DAEMON); 21110087Sjkh sp = getservbyname("who", "udp"); 21210087Sjkh if (sp == NULL) { 21310087Sjkh syslog(LOG_ERR, "rwhod: udp/who: unknown service\n"); 21410087Sjkh exit(1); 21510087Sjkh } 2161553Srgrimes if (chdir(_PATH_RWHODIR) < 0) { 21710087Sjkh syslog(LOG_ERR, "rwhod: %s: %m\n", _PATH_RWHODIR); 2181553Srgrimes exit(1); 2191553Srgrimes } 2201553Srgrimes /* 2211553Srgrimes * Establish host name as returned by system. 2221553Srgrimes */ 2231553Srgrimes if (gethostname(myname, sizeof(myname) - 1) < 0) { 2241553Srgrimes syslog(LOG_ERR, "gethostname: %m"); 2251553Srgrimes exit(1); 2261553Srgrimes } 2271553Srgrimes if ((cp = index(myname, '.')) != NULL) 2281553Srgrimes *cp = '\0'; 22919303Simp strncpy(mywd.wd_hostname, myname, sizeof(mywd.wd_hostname) - 1); 23019303Simp mywd.wd_hostname[sizeof(mywd.wd_hostname) - 1] = '\0'; 2311553Srgrimes utmpf = open(_PATH_UTMP, O_RDONLY|O_CREAT, 0644); 2321553Srgrimes if (utmpf < 0) { 2331553Srgrimes syslog(LOG_ERR, "%s: %m", _PATH_UTMP); 2341553Srgrimes exit(1); 2351553Srgrimes } 2361553Srgrimes getboottime(0); 2371553Srgrimes if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 2381553Srgrimes syslog(LOG_ERR, "socket: %m"); 2391553Srgrimes exit(1); 2401553Srgrimes } 2411553Srgrimes if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { 2421553Srgrimes syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m"); 2431553Srgrimes exit(1); 2441553Srgrimes } 2451553Srgrimes memset(&sin, 0, sizeof(sin)); 24610087Sjkh sin.sin_len = sizeof(sin); 2471553Srgrimes sin.sin_family = AF_INET; 2481553Srgrimes sin.sin_port = sp->s_port; 2491553Srgrimes if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 2501553Srgrimes syslog(LOG_ERR, "bind: %m"); 2511553Srgrimes exit(1); 2521553Srgrimes } 25317829Spst setgid(unpriv_gid); 25418092Speter setgroups(1, &unpriv_gid); /* XXX BOGUS groups[0] = egid */ 25517829Spst setuid(unpriv_uid); 2561553Srgrimes if (!configure(s)) 2571553Srgrimes exit(1); 2581553Srgrimes signal(SIGALRM, onalrm); 2591553Srgrimes onalrm(0); 2601553Srgrimes for (;;) { 2611553Srgrimes struct whod wd; 2621553Srgrimes int cc, whod, len = sizeof(from); 2631553Srgrimes 2641553Srgrimes cc = recvfrom(s, (char *)&wd, sizeof(struct whod), 0, 2651553Srgrimes (struct sockaddr *)&from, &len); 2661553Srgrimes if (cc <= 0) { 2671553Srgrimes if (cc < 0 && errno != EINTR) 2681553Srgrimes syslog(LOG_WARNING, "recv: %m"); 2691553Srgrimes continue; 2701553Srgrimes } 27141895Sdes if (from.sin_port != sp->s_port && !insecure_mode) { 2721553Srgrimes syslog(LOG_WARNING, "%d: bad from port", 2731553Srgrimes ntohs(from.sin_port)); 2741553Srgrimes continue; 2751553Srgrimes } 2761553Srgrimes if (wd.wd_vers != WHODVERSION) 2771553Srgrimes continue; 2781553Srgrimes if (wd.wd_type != WHODTYPE_STATUS) 2791553Srgrimes continue; 28017829Spst if (!verify(wd.wd_hostname, sizeof wd.wd_hostname)) { 2811553Srgrimes syslog(LOG_WARNING, "malformed host name from %x", 2821553Srgrimes from.sin_addr); 2831553Srgrimes continue; 2841553Srgrimes } 28530380Scharnier (void) snprintf(path, sizeof path, "whod.%s", wd.wd_hostname); 2861553Srgrimes /* 2871553Srgrimes * Rather than truncating and growing the file each time, 2881553Srgrimes * use ftruncate if size is less than previous size. 2891553Srgrimes */ 2901553Srgrimes whod = open(path, O_WRONLY | O_CREAT, 0644); 2911553Srgrimes if (whod < 0) { 2921553Srgrimes syslog(LOG_WARNING, "%s: %m", path); 2931553Srgrimes continue; 2941553Srgrimes } 2951553Srgrimes#if ENDIAN != BIG_ENDIAN 2961553Srgrimes { 2971553Srgrimes int i, n = (cc - WHDRSIZE)/sizeof(struct whoent); 2981553Srgrimes struct whoent *we; 2991553Srgrimes 3001553Srgrimes /* undo header byte swapping before writing to file */ 3011553Srgrimes wd.wd_sendtime = ntohl(wd.wd_sendtime); 3021553Srgrimes for (i = 0; i < 3; i++) 3031553Srgrimes wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]); 3041553Srgrimes wd.wd_boottime = ntohl(wd.wd_boottime); 3051553Srgrimes we = wd.wd_we; 3061553Srgrimes for (i = 0; i < n; i++) { 3071553Srgrimes we->we_idle = ntohl(we->we_idle); 3081553Srgrimes we->we_utmp.out_time = 3091553Srgrimes ntohl(we->we_utmp.out_time); 3101553Srgrimes we++; 3111553Srgrimes } 3121553Srgrimes } 3131553Srgrimes#endif 3141553Srgrimes (void) time((time_t *)&wd.wd_recvtime); 3151553Srgrimes (void) write(whod, (char *)&wd, cc); 3161553Srgrimes if (fstat(whod, &st) < 0 || st.st_size > cc) 3171553Srgrimes ftruncate(whod, cc); 3181553Srgrimes (void) close(whod); 3191553Srgrimes } 3201553Srgrimes} 3211553Srgrimes 32230380Scharnierstatic void 32330380Scharnierusage() 32430380Scharnier{ 32541895Sdes fprintf(stderr, "usage: rwhod [-i] [-m [ttl]]\n"); 32630380Scharnier exit(1); 32730380Scharnier} 32817829Spst 32917829Spstvoid 33017829Spstrun_as(uid, gid) 33117829Spst uid_t *uid; 33217829Spst gid_t *gid; 33317829Spst{ 33417829Spst struct passwd *pw; 33518092Speter struct group *gr; 33617829Spst 33717829Spst pw = getpwnam(UNPRIV_USER); 33817829Spst if (!pw) { 33917829Spst syslog(LOG_ERR, "getpwnam(%s): %m", UNPRIV_USER); 34017829Spst exit(1); 34117829Spst } 34217829Spst *uid = pw->pw_uid; 34317829Spst 34418092Speter gr = getgrnam(UNPRIV_GROUP); 34518092Speter if (!gr) { 34618092Speter syslog(LOG_ERR, "getgrnam(%s): %m", UNPRIV_GROUP); 34717829Spst exit(1); 34817829Spst } 34918092Speter *gid = gr->gr_gid; 35017829Spst} 35117829Spst 3521553Srgrimes/* 3531553Srgrimes * Check out host name for unprintables 3541553Srgrimes * and other funnies before allowing a file 3551553Srgrimes * to be created. Sorry, but blanks aren't allowed. 3561553Srgrimes */ 3571553Srgrimesint 35817829Spstverify(name, maxlen) 3591553Srgrimes register char *name; 36017829Spst register int maxlen; 3611553Srgrimes{ 3621553Srgrimes register int size = 0; 3631553Srgrimes 36419303Simp while (*name && size < maxlen - 1) { 3651553Srgrimes if (!isascii(*name) || !(isalnum(*name) || ispunct(*name))) 3661553Srgrimes return (0); 3671553Srgrimes name++, size++; 3681553Srgrimes } 36917829Spst *name = '\0'; 3701553Srgrimes return (size > 0); 3711553Srgrimes} 3721553Srgrimes 3731553Srgrimesint utmptime; 3741553Srgrimesint utmpent; 3751553Srgrimesint utmpsize = 0; 3761553Srgrimesstruct utmp *utmp; 3771553Srgrimesint alarmcount; 3781553Srgrimes 3791553Srgrimesvoid 3801553Srgrimesonalrm(signo) 3811553Srgrimes int signo; 3821553Srgrimes{ 3831553Srgrimes register struct neighbor *np; 3841553Srgrimes register struct whoent *we = mywd.wd_we, *wlast; 3851553Srgrimes register int i; 3861553Srgrimes struct stat stb; 3871553Srgrimes double avenrun[3]; 3881553Srgrimes time_t now; 3891553Srgrimes int cc; 3901553Srgrimes 3911553Srgrimes now = time(NULL); 3921553Srgrimes if (alarmcount % 10 == 0) 3931553Srgrimes getboottime(0); 3941553Srgrimes alarmcount++; 3951553Srgrimes (void) fstat(utmpf, &stb); 3961553Srgrimes if ((stb.st_mtime != utmptime) || (stb.st_size > utmpsize)) { 3971553Srgrimes utmptime = stb.st_mtime; 3981553Srgrimes if (stb.st_size > utmpsize) { 3991553Srgrimes utmpsize = stb.st_size + 10 * sizeof(struct utmp); 4001553Srgrimes if (utmp) 4011553Srgrimes utmp = (struct utmp *)realloc(utmp, utmpsize); 4021553Srgrimes else 4031553Srgrimes utmp = (struct utmp *)malloc(utmpsize); 4041553Srgrimes if (! utmp) { 40530380Scharnier syslog(LOG_WARNING, "malloc failed"); 4061553Srgrimes utmpsize = 0; 4071553Srgrimes goto done; 4081553Srgrimes } 4091553Srgrimes } 4101553Srgrimes (void) lseek(utmpf, (off_t)0, L_SET); 4111553Srgrimes cc = read(utmpf, (char *)utmp, stb.st_size); 4121553Srgrimes if (cc < 0) { 41330380Scharnier syslog(LOG_ERR, "read(%s): %m", _PATH_UTMP); 4141553Srgrimes goto done; 4151553Srgrimes } 4161553Srgrimes wlast = &mywd.wd_we[1024 / sizeof(struct whoent) - 1]; 4171553Srgrimes utmpent = cc / sizeof(struct utmp); 4181553Srgrimes for (i = 0; i < utmpent; i++) 4191553Srgrimes if (utmp[i].ut_name[0]) { 4201553Srgrimes memcpy(we->we_utmp.out_line, utmp[i].ut_line, 4211553Srgrimes sizeof(utmp[i].ut_line)); 4221553Srgrimes memcpy(we->we_utmp.out_name, utmp[i].ut_name, 4231553Srgrimes sizeof(utmp[i].ut_name)); 4241553Srgrimes we->we_utmp.out_time = htonl(utmp[i].ut_time); 4251553Srgrimes if (we >= wlast) 4261553Srgrimes break; 4271553Srgrimes we++; 4281553Srgrimes } 4291553Srgrimes utmpent = we - mywd.wd_we; 4301553Srgrimes } 4311553Srgrimes 4321553Srgrimes /* 4331553Srgrimes * The test on utmpent looks silly---after all, if no one is 4341553Srgrimes * logged on, why worry about efficiency?---but is useful on 4351553Srgrimes * (e.g.) compute servers. 4361553Srgrimes */ 4371553Srgrimes if (utmpent && chdir(_PATH_DEV)) { 4381553Srgrimes syslog(LOG_ERR, "chdir(%s): %m", _PATH_DEV); 4391553Srgrimes exit(1); 4401553Srgrimes } 4411553Srgrimes we = mywd.wd_we; 4421553Srgrimes for (i = 0; i < utmpent; i++) { 4431553Srgrimes if (stat(we->we_utmp.out_line, &stb) >= 0) 4441553Srgrimes we->we_idle = htonl(now - stb.st_atime); 4451553Srgrimes we++; 4461553Srgrimes } 4471553Srgrimes (void)getloadavg(avenrun, sizeof(avenrun)/sizeof(avenrun[0])); 4481553Srgrimes for (i = 0; i < 3; i++) 4491553Srgrimes mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100)); 4501553Srgrimes cc = (char *)we - (char *)&mywd; 4511553Srgrimes mywd.wd_sendtime = htonl(time(0)); 4521553Srgrimes mywd.wd_vers = WHODVERSION; 4531553Srgrimes mywd.wd_type = WHODTYPE_STATUS; 45410087Sjkh if (multicast_mode == SCOPED_MULTICAST) { 45510087Sjkh (void) sendto(s, (char *)&mywd, cc, 0, 45610087Sjkh (struct sockaddr *)&multicast_addr, 45710087Sjkh sizeof(multicast_addr)); 45810087Sjkh } 45910087Sjkh else for (np = neighbors; np != NULL; np = np->n_next) { 46010087Sjkh if (multicast_mode == PER_INTERFACE_MULTICAST && 46110087Sjkh np->n_flags & IFF_MULTICAST) { 46210087Sjkh /* 46310087Sjkh * Select the outgoing interface for the multicast. 46410087Sjkh */ 46510087Sjkh if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, 46610087Sjkh &(((struct sockaddr_in *)np->n_addr)->sin_addr), 46710087Sjkh sizeof(struct in_addr)) < 0) { 46810087Sjkh syslog(LOG_ERR, 46910087Sjkh "setsockopt IP_MULTICAST_IF: %m"); 47010087Sjkh exit(1); 47110087Sjkh } 47210087Sjkh (void) sendto(s, (char *)&mywd, cc, 0, 47310087Sjkh (struct sockaddr *)&multicast_addr, 47410087Sjkh sizeof(multicast_addr)); 47510087Sjkh } else (void) sendto(s, (char *)&mywd, cc, 0, 47610087Sjkh np->n_addr, np->n_addrlen); 47710087Sjkh } 4781553Srgrimes if (utmpent && chdir(_PATH_RWHODIR)) { 4791553Srgrimes syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR); 4801553Srgrimes exit(1); 4811553Srgrimes } 4821553Srgrimesdone: 4831553Srgrimes (void) alarm(AL_INTERVAL); 4841553Srgrimes} 4851553Srgrimes 4861553Srgrimesvoid 4871553Srgrimesgetboottime(signo) 4881553Srgrimes int signo; 4891553Srgrimes{ 4901553Srgrimes int mib[2]; 4911553Srgrimes size_t size; 4921553Srgrimes struct timeval tm; 4931553Srgrimes 4941553Srgrimes mib[0] = CTL_KERN; 4951553Srgrimes mib[1] = KERN_BOOTTIME; 4961553Srgrimes size = sizeof(tm); 4971553Srgrimes if (sysctl(mib, 2, &tm, &size, NULL, 0) == -1) { 4981553Srgrimes syslog(LOG_ERR, "cannot get boottime: %m"); 4991553Srgrimes exit(1); 5001553Srgrimes } 5011553Srgrimes mywd.wd_boottime = htonl(tm.tv_sec); 5021553Srgrimes} 5031553Srgrimes 5041553Srgrimesvoid 5051553Srgrimesquit(msg) 5061553Srgrimes char *msg; 5071553Srgrimes{ 5081553Srgrimes syslog(LOG_ERR, msg); 5091553Srgrimes exit(1); 5101553Srgrimes} 5111553Srgrimes 5121553Srgrimes#define ROUNDUP(a) \ 5131553Srgrimes ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 5141553Srgrimes#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) 5151553Srgrimes 5161553Srgrimesvoid 5171553Srgrimesrt_xaddrs(cp, cplim, rtinfo) 5181553Srgrimes register caddr_t cp, cplim; 5191553Srgrimes register struct rt_addrinfo *rtinfo; 5201553Srgrimes{ 5211553Srgrimes register struct sockaddr *sa; 5221553Srgrimes register int i; 5231553Srgrimes 5241553Srgrimes memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info)); 5251553Srgrimes for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) { 5261553Srgrimes if ((rtinfo->rti_addrs & (1 << i)) == 0) 5271553Srgrimes continue; 5281553Srgrimes rtinfo->rti_info[i] = sa = (struct sockaddr *)cp; 5291553Srgrimes ADVANCE(cp, sa); 5301553Srgrimes } 5311553Srgrimes} 5321553Srgrimes 5331553Srgrimes/* 5341553Srgrimes * Figure out device configuration and select 5351553Srgrimes * networks which deserve status information. 5361553Srgrimes */ 5371553Srgrimesint 5381553Srgrimesconfigure(s) 5391553Srgrimes int s; 5401553Srgrimes{ 5411553Srgrimes register struct neighbor *np; 5421553Srgrimes register struct if_msghdr *ifm; 5431553Srgrimes register struct ifa_msghdr *ifam; 5441553Srgrimes struct sockaddr_dl *sdl; 5451553Srgrimes size_t needed; 5461553Srgrimes int mib[6], flags = 0, len; 5471553Srgrimes char *buf, *lim, *next; 5481553Srgrimes struct rt_addrinfo info; 5491553Srgrimes 55010087Sjkh if (multicast_mode != NO_MULTICAST) { 55110087Sjkh multicast_addr.sin_addr.s_addr = htonl(INADDR_WHOD_GROUP); 55210087Sjkh multicast_addr.sin_port = sp->s_port; 55310087Sjkh } 55410087Sjkh 55510087Sjkh if (multicast_mode == SCOPED_MULTICAST) { 55610087Sjkh struct ip_mreq mreq; 55710087Sjkh unsigned char ttl; 55810087Sjkh 55910087Sjkh mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP); 56010087Sjkh mreq.imr_interface.s_addr = htonl(INADDR_ANY); 56110087Sjkh if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, 56210087Sjkh &mreq, sizeof(mreq)) < 0) { 56310087Sjkh syslog(LOG_ERR, 56410087Sjkh "setsockopt IP_ADD_MEMBERSHIP: %m"); 56510087Sjkh return(0); 56610087Sjkh } 56710087Sjkh ttl = multicast_scope; 56810087Sjkh if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, 56910087Sjkh &ttl, sizeof(ttl)) < 0) { 57010087Sjkh syslog(LOG_ERR, 57110087Sjkh "setsockopt IP_MULTICAST_TTL: %m"); 57210087Sjkh return(0); 57310087Sjkh } 57410087Sjkh return(1); 57510087Sjkh } 57610087Sjkh 5771553Srgrimes mib[0] = CTL_NET; 5781553Srgrimes mib[1] = PF_ROUTE; 5791553Srgrimes mib[2] = 0; 5801553Srgrimes mib[3] = AF_INET; 5811553Srgrimes mib[4] = NET_RT_IFLIST; 5821553Srgrimes mib[5] = 0; 5831553Srgrimes if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 5841553Srgrimes quit("route-sysctl-estimate"); 5851553Srgrimes if ((buf = malloc(needed)) == NULL) 5861553Srgrimes quit("malloc"); 5871553Srgrimes if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) 5881553Srgrimes quit("actual retrieval of interface table"); 5891553Srgrimes lim = buf + needed; 5901553Srgrimes 5911553Srgrimes sdl = NULL; /* XXX just to keep gcc -Wall happy */ 5921553Srgrimes for (next = buf; next < lim; next += ifm->ifm_msglen) { 5931553Srgrimes ifm = (struct if_msghdr *)next; 5941553Srgrimes if (ifm->ifm_type == RTM_IFINFO) { 5951553Srgrimes sdl = (struct sockaddr_dl *)(ifm + 1); 5961553Srgrimes flags = ifm->ifm_flags; 5971553Srgrimes continue; 5981553Srgrimes } 5991553Srgrimes if ((flags & IFF_UP) == 0 || 60010087Sjkh (flags & (((multicast_mode == PER_INTERFACE_MULTICAST) ? 60110087Sjkh IFF_MULTICAST : 0) | 60210087Sjkh IFF_BROADCAST|IFF_POINTOPOINT)) == 0) 6031553Srgrimes continue; 6041553Srgrimes if (ifm->ifm_type != RTM_NEWADDR) 6051553Srgrimes quit("out of sync parsing NET_RT_IFLIST"); 6061553Srgrimes ifam = (struct ifa_msghdr *)ifm; 6071553Srgrimes info.rti_addrs = ifam->ifam_addrs; 6081553Srgrimes rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam, 6091553Srgrimes &info); 6101553Srgrimes /* gag, wish we could get rid of Internet dependencies */ 6111553Srgrimes#define dstaddr info.rti_info[RTAX_BRD] 61210087Sjkh#define ifaddr info.rti_info[RTAX_IFA] 6131553Srgrimes#define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr 6141553Srgrimes#define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port 6151553Srgrimes if (dstaddr == 0 || dstaddr->sa_family != AF_INET) 6161553Srgrimes continue; 6171553Srgrimes PORT_SA(dstaddr) = sp->s_port; 6181553Srgrimes for (np = neighbors; np != NULL; np = np->n_next) 6191553Srgrimes if (memcmp(sdl->sdl_data, np->n_name, 6201553Srgrimes sdl->sdl_nlen) == 0 && 6211553Srgrimes IPADDR_SA(np->n_addr) == IPADDR_SA(dstaddr)) 6221553Srgrimes break; 6231553Srgrimes if (np != NULL) 6241553Srgrimes continue; 6251553Srgrimes len = sizeof(*np) + dstaddr->sa_len + sdl->sdl_nlen + 1; 6261553Srgrimes np = (struct neighbor *)malloc(len); 6271553Srgrimes if (np == NULL) 6281553Srgrimes quit("malloc of neighbor structure"); 6291553Srgrimes memset(np, 0, len); 6301553Srgrimes np->n_flags = flags; 6311553Srgrimes np->n_addr = (struct sockaddr *)(np + 1); 6321553Srgrimes np->n_addrlen = dstaddr->sa_len; 6331553Srgrimes np->n_name = np->n_addrlen + (char *)np->n_addr; 63410087Sjkh memcpy((char *)np->n_addr, (char *)dstaddr, np->n_addrlen); 63510087Sjkh memcpy(np->n_name, sdl->sdl_data, sdl->sdl_nlen); 63610087Sjkh if (multicast_mode == PER_INTERFACE_MULTICAST && 63710087Sjkh (flags & IFF_MULTICAST) && 63810087Sjkh !(flags & IFF_LOOPBACK)) { 63910087Sjkh struct ip_mreq mreq; 64010087Sjkh 64110087Sjkh memcpy((char *)np->n_addr, (char *)ifaddr, 64210087Sjkh np->n_addrlen); 64310087Sjkh mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP); 64410087Sjkh mreq.imr_interface.s_addr = 64510087Sjkh ((struct sockaddr_in *)np->n_addr)->sin_addr.s_addr; 64610087Sjkh if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, 64710087Sjkh &mreq, sizeof(mreq)) < 0) { 64810087Sjkh syslog(LOG_ERR, 64910087Sjkh "setsockopt IP_ADD_MEMBERSHIP: %m"); 65010087Sjkh#if 0 65110087Sjkh /* Fall back to broadcast on this if. */ 65210087Sjkh np->n_flags &= ~IFF_MULTICAST; 65310087Sjkh#else 65410087Sjkh free((char *)np); 65510087Sjkh continue; 65610087Sjkh#endif 65710087Sjkh } 65810087Sjkh } 6591553Srgrimes np->n_next = neighbors; 6601553Srgrimes neighbors = np; 6611553Srgrimes } 6621553Srgrimes free(buf); 6631553Srgrimes return (1); 6641553Srgrimes} 6651553Srgrimes 6661553Srgrimes#ifdef DEBUG 6671553Srgrimesvoid 6681553SrgrimesSendto(s, buf, cc, flags, to, tolen) 6691553Srgrimes int s; 67017829Spst const void *buf; 67117829Spst size_t cc; 67217829Spst int flags; 67317829Spst const struct sockaddr *to; 6741553Srgrimes int tolen; 6751553Srgrimes{ 6761553Srgrimes register struct whod *w = (struct whod *)buf; 6771553Srgrimes register struct whoent *we; 6781553Srgrimes struct sockaddr_in *sin = (struct sockaddr_in *)to; 6791553Srgrimes 68017829Spst printf("sendto %x.%d\n", ntohl(sin->sin_addr.s_addr), 68117829Spst ntohs(sin->sin_port)); 6821553Srgrimes printf("hostname %s %s\n", w->wd_hostname, 6831553Srgrimes interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up")); 6841553Srgrimes printf("load %4.2f, %4.2f, %4.2f\n", 6851553Srgrimes ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0, 6861553Srgrimes ntohl(w->wd_loadav[2]) / 100.0); 6871553Srgrimes cc -= WHDRSIZE; 6881553Srgrimes for (we = w->wd_we, cc /= sizeof(struct whoent); cc > 0; cc--, we++) { 6891553Srgrimes time_t t = ntohl(we->we_utmp.out_time); 6901553Srgrimes printf("%-8.8s %s:%s %.12s", 6911553Srgrimes we->we_utmp.out_name, 6921553Srgrimes w->wd_hostname, we->we_utmp.out_line, 6931553Srgrimes ctime(&t)+4); 6941553Srgrimes we->we_idle = ntohl(we->we_idle) / 60; 6951553Srgrimes if (we->we_idle) { 6961553Srgrimes if (we->we_idle >= 100*60) 6971553Srgrimes we->we_idle = 100*60 - 1; 6981553Srgrimes if (we->we_idle >= 60) 6991553Srgrimes printf(" %2d", we->we_idle / 60); 7001553Srgrimes else 7011553Srgrimes printf(" "); 7021553Srgrimes printf(":%02d", we->we_idle % 60); 7031553Srgrimes } 7041553Srgrimes printf("\n"); 7051553Srgrimes } 7061553Srgrimes} 7071553Srgrimes 7081553Srgrimeschar * 7091553Srgrimesinterval(time, updown) 7101553Srgrimes int time; 7111553Srgrimes char *updown; 7121553Srgrimes{ 7131553Srgrimes static char resbuf[32]; 7141553Srgrimes int days, hours, minutes; 7151553Srgrimes 7161553Srgrimes if (time < 0 || time > 3*30*24*60*60) { 7171553Srgrimes (void) sprintf(resbuf, " %s ??:??", updown); 7181553Srgrimes return (resbuf); 7191553Srgrimes } 7201553Srgrimes minutes = (time + 59) / 60; /* round to minutes */ 7211553Srgrimes hours = minutes / 60; minutes %= 60; 7221553Srgrimes days = hours / 24; hours %= 24; 7231553Srgrimes if (days) 7241553Srgrimes (void) sprintf(resbuf, "%s %2d+%02d:%02d", 7251553Srgrimes updown, days, hours, minutes); 7261553Srgrimes else 7271553Srgrimes (void) sprintf(resbuf, "%s %2d:%02d", 7281553Srgrimes updown, hours, minutes); 7291553Srgrimes return (resbuf); 7301553Srgrimes} 7311553Srgrimes#endif 732