rwhod.c revision 141918
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 * 4. Neither the name of the University nor the names of its contributors 141553Srgrimes * may be used to endorse or promote products derived from this software 151553Srgrimes * without specific prior written permission. 161553Srgrimes * 171553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271553Srgrimes * SUCH DAMAGE. 281553Srgrimes */ 291553Srgrimes 301553Srgrimes#ifndef lint 3130380Scharnierstatic const char copyright[] = 321553Srgrimes"@(#) Copyright (c) 1983, 1993\n\ 331553Srgrimes The Regents of the University of California. All rights reserved.\n"; 341553Srgrimes#endif /* not lint */ 351553Srgrimes 36117278Scharnier#if 0 371553Srgrimes#ifndef lint 381553Srgrimesstatic char sccsid[] = "@(#)rwhod.c 8.1 (Berkeley) 6/6/93"; 39117278Scharnier#endif /* not lint */ 4030380Scharnier#endif 411553Srgrimes 42117278Scharnier#include <sys/cdefs.h> 43117278Scharnier__FBSDID("$FreeBSD: head/usr.sbin/rwhod/rwhod.c 141918 2005-02-14 17:42:58Z stefanf $"); 44117278Scharnier 451553Srgrimes#include <sys/param.h> 461553Srgrimes#include <sys/socket.h> 471553Srgrimes#include <sys/stat.h> 481553Srgrimes#include <sys/signal.h> 491553Srgrimes#include <sys/ioctl.h> 501553Srgrimes#include <sys/sysctl.h> 511553Srgrimes 521553Srgrimes#include <net/if.h> 531553Srgrimes#include <net/if_dl.h> 541553Srgrimes#include <net/route.h> 551553Srgrimes#include <netinet/in.h> 5670284Siedowse#include <arpa/inet.h> 571553Srgrimes#include <protocols/rwhod.h> 581553Srgrimes 591553Srgrimes#include <ctype.h> 6030380Scharnier#include <err.h> 611553Srgrimes#include <errno.h> 621553Srgrimes#include <fcntl.h> 631553Srgrimes#include <netdb.h> 641553Srgrimes#include <paths.h> 651553Srgrimes#include <stdio.h> 661553Srgrimes#include <stdlib.h> 671553Srgrimes#include <string.h> 681553Srgrimes#include <syslog.h> 6999825Salfred#include <timeconv.h> 701553Srgrimes#include <unistd.h> 711553Srgrimes#include <utmp.h> 7217829Spst#include <pwd.h> 7318092Speter#include <grp.h> 741553Srgrimes 751553Srgrimes/* 7610087Sjkh * This version of Berkeley's rwhod has been modified to use IP multicast 7710087Sjkh * datagrams, under control of a new command-line option: 7810087Sjkh * 7910087Sjkh * rwhod -m causes rwhod to use IP multicast (instead of 8010087Sjkh * broadcast or unicast) on all interfaces that have 8110087Sjkh * the IFF_MULTICAST flag set in their "ifnet" structs 8210087Sjkh * (excluding the loopback interface). The multicast 8310087Sjkh * reports are sent with a time-to-live of 1, to prevent 8410087Sjkh * forwarding beyond the directly-connected subnet(s). 8510087Sjkh * 8610087Sjkh * rwhod -m <ttl> causes rwhod to send IP multicast datagrams with a 8710087Sjkh * time-to-live of <ttl>, via a SINGLE interface rather 8810087Sjkh * than all interfaces. <ttl> must be between 0 and 8910087Sjkh * MAX_MULTICAST_SCOPE, defined below. Note that "-m 1" 9010087Sjkh * is different than "-m", in that "-m 1" specifies 9110087Sjkh * transmission on one interface only. 9210087Sjkh * 9310087Sjkh * When "-m" is used without a <ttl> argument, the program accepts multicast 9410087Sjkh * rwhod reports from all multicast-capable interfaces. If a <ttl> argument 9510087Sjkh * is given, it accepts multicast reports from only one interface, the one 9610087Sjkh * on which reports are sent (which may be controlled via the host's routing 9710087Sjkh * table). Regardless of the "-m" option, the program accepts broadcast or 9810087Sjkh * unicast reports from all interfaces. Thus, this program will hear the 9910087Sjkh * reports of old, non-multicasting rwhods, but, if multicasting is used, 10010087Sjkh * those old rwhods won't hear the reports generated by this program. 10110087Sjkh * 10210087Sjkh * -- Steve Deering, Stanford University, February 1989 10310087Sjkh */ 10410087Sjkh 10517832Spst#define UNPRIV_USER "daemon" 10617829Spst#define UNPRIV_GROUP "daemon" 10717829Spst 10810087Sjkh#define NO_MULTICAST 0 /* multicast modes */ 10910087Sjkh#define PER_INTERFACE_MULTICAST 1 11010087Sjkh#define SCOPED_MULTICAST 2 11110087Sjkh 11210087Sjkh#define MAX_MULTICAST_SCOPE 32 /* "site-wide", by convention */ 11310087Sjkh 11410087Sjkh#define INADDR_WHOD_GROUP (u_long)0xe0000103 /* 224.0.1.3 */ 11510087Sjkh /* (belongs in protocols/rwhod.h) */ 11610087Sjkh 11741895Sdesint insecure_mode; 11842508Ssteveint quiet_mode; 11947963Sbrianint iff_flag = IFF_POINTOPOINT; 12010087Sjkhint multicast_mode = NO_MULTICAST; 12110087Sjkhint multicast_scope; 12299825Salfredstruct sockaddr_in multicast_addr = 12399825Salfred { sizeof multicast_addr, AF_INET, 0, { 0 }, { 0 } }; 12410087Sjkh 12510087Sjkh/* 1261553Srgrimes * Alarm interval. Don't forget to change the down time check in ruptime 1271553Srgrimes * if this is changed. 1281553Srgrimes */ 1291553Srgrimes#define AL_INTERVAL (3 * 60) 1301553Srgrimes 1311553Srgrimeschar myname[MAXHOSTNAMELEN]; 1321553Srgrimes 1331553Srgrimes/* 1341553Srgrimes * We communicate with each neighbor in a list constructed at the time we're 1351553Srgrimes * started up. Neighbors are currently directly connected via a hardware 1361553Srgrimes * interface. 1371553Srgrimes */ 1381553Srgrimesstruct neighbor { 1391553Srgrimes struct neighbor *n_next; 1401553Srgrimes char *n_name; /* interface name */ 1411553Srgrimes struct sockaddr *n_addr; /* who to send to */ 1421553Srgrimes int n_addrlen; /* size of address */ 1431553Srgrimes int n_flags; /* should forward?, interface flags */ 1441553Srgrimes}; 1451553Srgrimes 1461553Srgrimesstruct neighbor *neighbors; 1471553Srgrimesstruct whod mywd; 1481553Srgrimesstruct servent *sp; 1491553Srgrimesint s, utmpf; 1501553Srgrimes 15199825Salfred#define WHDRSIZE (int)(sizeof(mywd) - sizeof(mywd.wd_we)) 1521553Srgrimes 15399825Salfredvoid run_as(uid_t *, gid_t *); 15499825Salfredint configure(int); 15599825Salfredvoid getboottime(int); 15699825Salfredvoid onalrm(int); 15799825Salfredvoid quit(const char *); 15899825Salfredvoid rt_xaddrs(caddr_t, caddr_t, struct rt_addrinfo *); 15999825Salfredint verify(char *, int); 16099825Salfredstatic void usage(void); 1611553Srgrimes#ifdef DEBUG 16299825Salfredchar *interval(int, char *); 163117278Scharniervoid Sendto(int, const void *, size_t, int, const struct sockaddr *, int); 1641553Srgrimes#define sendto Sendto 1651553Srgrimes#endif 1661553Srgrimes 1671553Srgrimesint 16899825Salfredmain(int argc, char *argv[]) 1691553Srgrimes{ 1701553Srgrimes struct sockaddr_in from; 1711553Srgrimes struct stat st; 1721553Srgrimes char path[64]; 1731553Srgrimes int on = 1; 1741553Srgrimes char *cp; 17599825Salfred struct sockaddr_in soin; 17617829Spst uid_t unpriv_uid; 17717829Spst gid_t unpriv_gid; 1781553Srgrimes 17930380Scharnier if (getuid()) 18030380Scharnier errx(1, "not super user"); 18117829Spst 18217829Spst run_as(&unpriv_uid, &unpriv_gid); 18317829Spst 18410087Sjkh argv++; argc--; 18510087Sjkh while (argc > 0 && *argv[0] == '-') { 18610087Sjkh if (strcmp(*argv, "-m") == 0) { 18710087Sjkh if (argc > 1 && isdigit(*(argv + 1)[0])) { 18810087Sjkh argv++, argc--; 18910087Sjkh multicast_mode = SCOPED_MULTICAST; 19010087Sjkh multicast_scope = atoi(*argv); 19130380Scharnier if (multicast_scope > MAX_MULTICAST_SCOPE) 19230380Scharnier errx(1, "ttl must not exceed %u", 19310087Sjkh MAX_MULTICAST_SCOPE); 19410087Sjkh } 19510087Sjkh else multicast_mode = PER_INTERFACE_MULTICAST; 19610087Sjkh } 19741895Sdes else if (strcmp(*argv, "-i") == 0) 19842508Ssteve insecure_mode = 1; 19942508Ssteve else if (strcmp(*argv, "-l") == 0) 20042508Ssteve quiet_mode = 1; 20147963Sbrian else if (strcmp(*argv, "-p") == 0) 20247963Sbrian iff_flag = 0; 20330380Scharnier else 20430380Scharnier usage(); 20510087Sjkh argv++, argc--; 20610087Sjkh } 20730380Scharnier if (argc > 0) 20830380Scharnier usage(); 2091553Srgrimes#ifndef DEBUG 2101553Srgrimes daemon(1, 0); 2111553Srgrimes#endif 21210087Sjkh (void) signal(SIGHUP, getboottime); 21310087Sjkh openlog("rwhod", LOG_PID, LOG_DAEMON); 21410087Sjkh sp = getservbyname("who", "udp"); 21510087Sjkh if (sp == NULL) { 216117278Scharnier syslog(LOG_ERR, "who/udp: unknown service"); 21710087Sjkh exit(1); 21810087Sjkh } 2191553Srgrimes if (chdir(_PATH_RWHODIR) < 0) { 22053770Scharnier syslog(LOG_ERR, "%s: %m", _PATH_RWHODIR); 2211553Srgrimes exit(1); 2221553Srgrimes } 2231553Srgrimes /* 2241553Srgrimes * Establish host name as returned by system. 2251553Srgrimes */ 2261553Srgrimes if (gethostname(myname, sizeof(myname) - 1) < 0) { 2271553Srgrimes syslog(LOG_ERR, "gethostname: %m"); 2281553Srgrimes exit(1); 2291553Srgrimes } 2301553Srgrimes if ((cp = index(myname, '.')) != NULL) 2311553Srgrimes *cp = '\0'; 23219303Simp strncpy(mywd.wd_hostname, myname, sizeof(mywd.wd_hostname) - 1); 23319303Simp mywd.wd_hostname[sizeof(mywd.wd_hostname) - 1] = '\0'; 2341553Srgrimes utmpf = open(_PATH_UTMP, O_RDONLY|O_CREAT, 0644); 2351553Srgrimes if (utmpf < 0) { 2361553Srgrimes syslog(LOG_ERR, "%s: %m", _PATH_UTMP); 2371553Srgrimes exit(1); 2381553Srgrimes } 2391553Srgrimes getboottime(0); 2401553Srgrimes if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 2411553Srgrimes syslog(LOG_ERR, "socket: %m"); 2421553Srgrimes exit(1); 2431553Srgrimes } 2441553Srgrimes if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { 2451553Srgrimes syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m"); 2461553Srgrimes exit(1); 2471553Srgrimes } 24899825Salfred memset(&soin, 0, sizeof(soin)); 24999825Salfred soin.sin_len = sizeof(soin); 25099825Salfred soin.sin_family = AF_INET; 25199825Salfred soin.sin_port = sp->s_port; 25299825Salfred if (bind(s, (struct sockaddr *)&soin, sizeof(soin)) < 0) { 2531553Srgrimes syslog(LOG_ERR, "bind: %m"); 2541553Srgrimes exit(1); 2551553Srgrimes } 25617829Spst setgid(unpriv_gid); 25718092Speter setgroups(1, &unpriv_gid); /* XXX BOGUS groups[0] = egid */ 25817829Spst setuid(unpriv_uid); 2591553Srgrimes if (!configure(s)) 2601553Srgrimes exit(1); 26142508Ssteve if (!quiet_mode) { 26242508Ssteve signal(SIGALRM, onalrm); 26342508Ssteve onalrm(0); 26442508Ssteve } 2651553Srgrimes for (;;) { 2661553Srgrimes struct whod wd; 267141918Sstefanf socklen_t len = sizeof(from); 268141918Sstefanf int cc, whod; 26985640Sdillon time_t t; 2701553Srgrimes 2711553Srgrimes cc = recvfrom(s, (char *)&wd, sizeof(struct whod), 0, 2721553Srgrimes (struct sockaddr *)&from, &len); 2731553Srgrimes if (cc <= 0) { 2741553Srgrimes if (cc < 0 && errno != EINTR) 2751553Srgrimes syslog(LOG_WARNING, "recv: %m"); 2761553Srgrimes continue; 2771553Srgrimes } 27841895Sdes if (from.sin_port != sp->s_port && !insecure_mode) { 27970284Siedowse syslog(LOG_WARNING, "%d: bad source port from %s", 28070284Siedowse ntohs(from.sin_port), inet_ntoa(from.sin_addr)); 2811553Srgrimes continue; 2821553Srgrimes } 28370284Siedowse if (cc < WHDRSIZE) { 28470284Siedowse syslog(LOG_WARNING, "short packet from %s", 28570284Siedowse inet_ntoa(from.sin_addr)); 28670284Siedowse continue; 28770284Siedowse } 2881553Srgrimes if (wd.wd_vers != WHODVERSION) 2891553Srgrimes continue; 2901553Srgrimes if (wd.wd_type != WHODTYPE_STATUS) 2911553Srgrimes continue; 29217829Spst if (!verify(wd.wd_hostname, sizeof wd.wd_hostname)) { 29370284Siedowse syslog(LOG_WARNING, "malformed host name from %s", 29470284Siedowse inet_ntoa(from.sin_addr)); 2951553Srgrimes continue; 2961553Srgrimes } 29730380Scharnier (void) snprintf(path, sizeof path, "whod.%s", wd.wd_hostname); 2981553Srgrimes /* 2991553Srgrimes * Rather than truncating and growing the file each time, 3001553Srgrimes * use ftruncate if size is less than previous size. 3011553Srgrimes */ 3021553Srgrimes whod = open(path, O_WRONLY | O_CREAT, 0644); 3031553Srgrimes if (whod < 0) { 3041553Srgrimes syslog(LOG_WARNING, "%s: %m", path); 3051553Srgrimes continue; 3061553Srgrimes } 3071553Srgrimes#if ENDIAN != BIG_ENDIAN 3081553Srgrimes { 3091553Srgrimes int i, n = (cc - WHDRSIZE)/sizeof(struct whoent); 3101553Srgrimes struct whoent *we; 3111553Srgrimes 3121553Srgrimes /* undo header byte swapping before writing to file */ 3131553Srgrimes wd.wd_sendtime = ntohl(wd.wd_sendtime); 3141553Srgrimes for (i = 0; i < 3; i++) 3151553Srgrimes wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]); 3161553Srgrimes wd.wd_boottime = ntohl(wd.wd_boottime); 3171553Srgrimes we = wd.wd_we; 3181553Srgrimes for (i = 0; i < n; i++) { 3191553Srgrimes we->we_idle = ntohl(we->we_idle); 3201553Srgrimes we->we_utmp.out_time = 3211553Srgrimes ntohl(we->we_utmp.out_time); 3221553Srgrimes we++; 3231553Srgrimes } 3241553Srgrimes } 3251553Srgrimes#endif 32685640Sdillon (void) time(&t); 32789572Sdillon wd.wd_recvtime = _time_to_int(t); 3281553Srgrimes (void) write(whod, (char *)&wd, cc); 3291553Srgrimes if (fstat(whod, &st) < 0 || st.st_size > cc) 3301553Srgrimes ftruncate(whod, cc); 3311553Srgrimes (void) close(whod); 3321553Srgrimes } 3331553Srgrimes} 3341553Srgrimes 33530380Scharnierstatic void 33630380Scharnierusage() 33730380Scharnier{ 33848229Sbrian fprintf(stderr, "usage: rwhod [-i] [-p] [-l] [-m [ttl]]\n"); 33930380Scharnier exit(1); 34030380Scharnier} 34117829Spst 34217829Spstvoid 34317829Spstrun_as(uid, gid) 34417829Spst uid_t *uid; 34517829Spst gid_t *gid; 34617829Spst{ 34717829Spst struct passwd *pw; 34818092Speter struct group *gr; 34917829Spst 35017829Spst pw = getpwnam(UNPRIV_USER); 35117829Spst if (!pw) { 35217829Spst syslog(LOG_ERR, "getpwnam(%s): %m", UNPRIV_USER); 35317829Spst exit(1); 35417829Spst } 35517829Spst *uid = pw->pw_uid; 35617829Spst 35718092Speter gr = getgrnam(UNPRIV_GROUP); 35818092Speter if (!gr) { 35918092Speter syslog(LOG_ERR, "getgrnam(%s): %m", UNPRIV_GROUP); 36017829Spst exit(1); 36117829Spst } 36218092Speter *gid = gr->gr_gid; 36317829Spst} 36417829Spst 3651553Srgrimes/* 3661553Srgrimes * Check out host name for unprintables 3671553Srgrimes * and other funnies before allowing a file 3681553Srgrimes * to be created. Sorry, but blanks aren't allowed. 3691553Srgrimes */ 3701553Srgrimesint 37117829Spstverify(name, maxlen) 3721553Srgrimes register char *name; 37317829Spst register int maxlen; 3741553Srgrimes{ 3751553Srgrimes register int size = 0; 3761553Srgrimes 37719303Simp while (*name && size < maxlen - 1) { 3781553Srgrimes if (!isascii(*name) || !(isalnum(*name) || ispunct(*name))) 3791553Srgrimes return (0); 3801553Srgrimes name++, size++; 3811553Srgrimes } 38217829Spst *name = '\0'; 3831553Srgrimes return (size > 0); 3841553Srgrimes} 3851553Srgrimes 3861553Srgrimesint utmptime; 3871553Srgrimesint utmpent; 3881553Srgrimesint utmpsize = 0; 3891553Srgrimesstruct utmp *utmp; 3901553Srgrimesint alarmcount; 3911553Srgrimes 3921553Srgrimesvoid 3931553Srgrimesonalrm(signo) 39499825Salfred int signo __unused; 3951553Srgrimes{ 3961553Srgrimes register struct neighbor *np; 3971553Srgrimes register struct whoent *we = mywd.wd_we, *wlast; 3981553Srgrimes register int i; 3991553Srgrimes struct stat stb; 4001553Srgrimes double avenrun[3]; 4011553Srgrimes time_t now; 4021553Srgrimes int cc; 4031553Srgrimes 4041553Srgrimes now = time(NULL); 4051553Srgrimes if (alarmcount % 10 == 0) 4061553Srgrimes getboottime(0); 4071553Srgrimes alarmcount++; 4081553Srgrimes (void) fstat(utmpf, &stb); 4091553Srgrimes if ((stb.st_mtime != utmptime) || (stb.st_size > utmpsize)) { 4101553Srgrimes utmptime = stb.st_mtime; 4111553Srgrimes if (stb.st_size > utmpsize) { 4121553Srgrimes utmpsize = stb.st_size + 10 * sizeof(struct utmp); 4131553Srgrimes if (utmp) 4141553Srgrimes utmp = (struct utmp *)realloc(utmp, utmpsize); 4151553Srgrimes else 4161553Srgrimes utmp = (struct utmp *)malloc(utmpsize); 4171553Srgrimes if (! utmp) { 41830380Scharnier syslog(LOG_WARNING, "malloc failed"); 4191553Srgrimes utmpsize = 0; 4201553Srgrimes goto done; 4211553Srgrimes } 4221553Srgrimes } 4231553Srgrimes (void) lseek(utmpf, (off_t)0, L_SET); 4241553Srgrimes cc = read(utmpf, (char *)utmp, stb.st_size); 4251553Srgrimes if (cc < 0) { 42630380Scharnier syslog(LOG_ERR, "read(%s): %m", _PATH_UTMP); 4271553Srgrimes goto done; 4281553Srgrimes } 4291553Srgrimes wlast = &mywd.wd_we[1024 / sizeof(struct whoent) - 1]; 4301553Srgrimes utmpent = cc / sizeof(struct utmp); 4311553Srgrimes for (i = 0; i < utmpent; i++) 4321553Srgrimes if (utmp[i].ut_name[0]) { 4331553Srgrimes memcpy(we->we_utmp.out_line, utmp[i].ut_line, 4341553Srgrimes sizeof(utmp[i].ut_line)); 4351553Srgrimes memcpy(we->we_utmp.out_name, utmp[i].ut_name, 4361553Srgrimes sizeof(utmp[i].ut_name)); 4371553Srgrimes we->we_utmp.out_time = htonl(utmp[i].ut_time); 4381553Srgrimes if (we >= wlast) 4391553Srgrimes break; 4401553Srgrimes we++; 4411553Srgrimes } 4421553Srgrimes utmpent = we - mywd.wd_we; 4431553Srgrimes } 4441553Srgrimes 4451553Srgrimes /* 4461553Srgrimes * The test on utmpent looks silly---after all, if no one is 4471553Srgrimes * logged on, why worry about efficiency?---but is useful on 4481553Srgrimes * (e.g.) compute servers. 4491553Srgrimes */ 4501553Srgrimes if (utmpent && chdir(_PATH_DEV)) { 4511553Srgrimes syslog(LOG_ERR, "chdir(%s): %m", _PATH_DEV); 4521553Srgrimes exit(1); 4531553Srgrimes } 4541553Srgrimes we = mywd.wd_we; 4551553Srgrimes for (i = 0; i < utmpent; i++) { 4561553Srgrimes if (stat(we->we_utmp.out_line, &stb) >= 0) 4571553Srgrimes we->we_idle = htonl(now - stb.st_atime); 4581553Srgrimes we++; 4591553Srgrimes } 4601553Srgrimes (void)getloadavg(avenrun, sizeof(avenrun)/sizeof(avenrun[0])); 4611553Srgrimes for (i = 0; i < 3; i++) 4621553Srgrimes mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100)); 4631553Srgrimes cc = (char *)we - (char *)&mywd; 46489572Sdillon mywd.wd_sendtime = htonl(_time_to_time32(time(NULL))); 4651553Srgrimes mywd.wd_vers = WHODVERSION; 4661553Srgrimes mywd.wd_type = WHODTYPE_STATUS; 46710087Sjkh if (multicast_mode == SCOPED_MULTICAST) { 46810087Sjkh (void) sendto(s, (char *)&mywd, cc, 0, 46910087Sjkh (struct sockaddr *)&multicast_addr, 47010087Sjkh sizeof(multicast_addr)); 47110087Sjkh } 47210087Sjkh else for (np = neighbors; np != NULL; np = np->n_next) { 47310087Sjkh if (multicast_mode == PER_INTERFACE_MULTICAST && 47410087Sjkh np->n_flags & IFF_MULTICAST) { 47510087Sjkh /* 47610087Sjkh * Select the outgoing interface for the multicast. 47710087Sjkh */ 47810087Sjkh if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, 47910087Sjkh &(((struct sockaddr_in *)np->n_addr)->sin_addr), 48010087Sjkh sizeof(struct in_addr)) < 0) { 48110087Sjkh syslog(LOG_ERR, 48210087Sjkh "setsockopt IP_MULTICAST_IF: %m"); 48310087Sjkh exit(1); 48410087Sjkh } 48510087Sjkh (void) sendto(s, (char *)&mywd, cc, 0, 48610087Sjkh (struct sockaddr *)&multicast_addr, 48710087Sjkh sizeof(multicast_addr)); 48810087Sjkh } else (void) sendto(s, (char *)&mywd, cc, 0, 48910087Sjkh np->n_addr, np->n_addrlen); 49010087Sjkh } 4911553Srgrimes if (utmpent && chdir(_PATH_RWHODIR)) { 4921553Srgrimes syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR); 4931553Srgrimes exit(1); 4941553Srgrimes } 4951553Srgrimesdone: 4961553Srgrimes (void) alarm(AL_INTERVAL); 4971553Srgrimes} 4981553Srgrimes 4991553Srgrimesvoid 5001553Srgrimesgetboottime(signo) 50199825Salfred int signo __unused; 5021553Srgrimes{ 5031553Srgrimes int mib[2]; 5041553Srgrimes size_t size; 5051553Srgrimes struct timeval tm; 5061553Srgrimes 5071553Srgrimes mib[0] = CTL_KERN; 5081553Srgrimes mib[1] = KERN_BOOTTIME; 5091553Srgrimes size = sizeof(tm); 5101553Srgrimes if (sysctl(mib, 2, &tm, &size, NULL, 0) == -1) { 5111553Srgrimes syslog(LOG_ERR, "cannot get boottime: %m"); 5121553Srgrimes exit(1); 5131553Srgrimes } 51489572Sdillon mywd.wd_boottime = htonl(_time_to_time32(tm.tv_sec)); 5151553Srgrimes} 5161553Srgrimes 5171553Srgrimesvoid 5181553Srgrimesquit(msg) 51999825Salfred const char *msg; 5201553Srgrimes{ 52162989Skris syslog(LOG_ERR, "%s", msg); 5221553Srgrimes exit(1); 5231553Srgrimes} 5241553Srgrimes 5251553Srgrimesvoid 5261553Srgrimesrt_xaddrs(cp, cplim, rtinfo) 5271553Srgrimes register caddr_t cp, cplim; 5281553Srgrimes register struct rt_addrinfo *rtinfo; 5291553Srgrimes{ 5301553Srgrimes register struct sockaddr *sa; 5311553Srgrimes register int i; 5321553Srgrimes 5331553Srgrimes memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info)); 5341553Srgrimes for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) { 5351553Srgrimes if ((rtinfo->rti_addrs & (1 << i)) == 0) 5361553Srgrimes continue; 5371553Srgrimes rtinfo->rti_info[i] = sa = (struct sockaddr *)cp; 538128186Sluigi cp += SA_SIZE(sa); 5391553Srgrimes } 5401553Srgrimes} 5411553Srgrimes 5421553Srgrimes/* 5431553Srgrimes * Figure out device configuration and select 5441553Srgrimes * networks which deserve status information. 5451553Srgrimes */ 5461553Srgrimesint 54799825Salfredconfigure(so) 54899825Salfred int so; 5491553Srgrimes{ 5501553Srgrimes register struct neighbor *np; 5511553Srgrimes register struct if_msghdr *ifm; 5521553Srgrimes register struct ifa_msghdr *ifam; 5531553Srgrimes struct sockaddr_dl *sdl; 5541553Srgrimes size_t needed; 5551553Srgrimes int mib[6], flags = 0, len; 5561553Srgrimes char *buf, *lim, *next; 5571553Srgrimes struct rt_addrinfo info; 5581553Srgrimes 55910087Sjkh if (multicast_mode != NO_MULTICAST) { 56010087Sjkh multicast_addr.sin_addr.s_addr = htonl(INADDR_WHOD_GROUP); 56110087Sjkh multicast_addr.sin_port = sp->s_port; 56210087Sjkh } 56310087Sjkh 56410087Sjkh if (multicast_mode == SCOPED_MULTICAST) { 56510087Sjkh struct ip_mreq mreq; 56610087Sjkh unsigned char ttl; 56710087Sjkh 56810087Sjkh mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP); 56910087Sjkh mreq.imr_interface.s_addr = htonl(INADDR_ANY); 57099825Salfred if (setsockopt(so, IPPROTO_IP, IP_ADD_MEMBERSHIP, 57110087Sjkh &mreq, sizeof(mreq)) < 0) { 57210087Sjkh syslog(LOG_ERR, 57310087Sjkh "setsockopt IP_ADD_MEMBERSHIP: %m"); 57410087Sjkh return(0); 57510087Sjkh } 57610087Sjkh ttl = multicast_scope; 57799825Salfred if (setsockopt(so, IPPROTO_IP, IP_MULTICAST_TTL, 57810087Sjkh &ttl, sizeof(ttl)) < 0) { 57910087Sjkh syslog(LOG_ERR, 58010087Sjkh "setsockopt IP_MULTICAST_TTL: %m"); 58110087Sjkh return(0); 58210087Sjkh } 58310087Sjkh return(1); 58410087Sjkh } 58510087Sjkh 5861553Srgrimes mib[0] = CTL_NET; 5871553Srgrimes mib[1] = PF_ROUTE; 5881553Srgrimes mib[2] = 0; 5891553Srgrimes mib[3] = AF_INET; 5901553Srgrimes mib[4] = NET_RT_IFLIST; 5911553Srgrimes mib[5] = 0; 5921553Srgrimes if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 5931553Srgrimes quit("route-sysctl-estimate"); 5941553Srgrimes if ((buf = malloc(needed)) == NULL) 5951553Srgrimes quit("malloc"); 5961553Srgrimes if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) 5971553Srgrimes quit("actual retrieval of interface table"); 5981553Srgrimes lim = buf + needed; 5991553Srgrimes 6001553Srgrimes sdl = NULL; /* XXX just to keep gcc -Wall happy */ 6011553Srgrimes for (next = buf; next < lim; next += ifm->ifm_msglen) { 6021553Srgrimes ifm = (struct if_msghdr *)next; 6031553Srgrimes if (ifm->ifm_type == RTM_IFINFO) { 6041553Srgrimes sdl = (struct sockaddr_dl *)(ifm + 1); 6051553Srgrimes flags = ifm->ifm_flags; 6061553Srgrimes continue; 6071553Srgrimes } 6081553Srgrimes if ((flags & IFF_UP) == 0 || 60910087Sjkh (flags & (((multicast_mode == PER_INTERFACE_MULTICAST) ? 61010087Sjkh IFF_MULTICAST : 0) | 61147963Sbrian IFF_BROADCAST|iff_flag)) == 0) 6121553Srgrimes continue; 6131553Srgrimes if (ifm->ifm_type != RTM_NEWADDR) 6141553Srgrimes quit("out of sync parsing NET_RT_IFLIST"); 6151553Srgrimes ifam = (struct ifa_msghdr *)ifm; 6161553Srgrimes info.rti_addrs = ifam->ifam_addrs; 6171553Srgrimes rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam, 6181553Srgrimes &info); 6191553Srgrimes /* gag, wish we could get rid of Internet dependencies */ 6201553Srgrimes#define dstaddr info.rti_info[RTAX_BRD] 62110087Sjkh#define ifaddr info.rti_info[RTAX_IFA] 6221553Srgrimes#define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr 6231553Srgrimes#define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port 6241553Srgrimes if (dstaddr == 0 || dstaddr->sa_family != AF_INET) 6251553Srgrimes continue; 6261553Srgrimes PORT_SA(dstaddr) = sp->s_port; 6271553Srgrimes for (np = neighbors; np != NULL; np = np->n_next) 6281553Srgrimes if (memcmp(sdl->sdl_data, np->n_name, 6291553Srgrimes sdl->sdl_nlen) == 0 && 6301553Srgrimes IPADDR_SA(np->n_addr) == IPADDR_SA(dstaddr)) 6311553Srgrimes break; 6321553Srgrimes if (np != NULL) 6331553Srgrimes continue; 6341553Srgrimes len = sizeof(*np) + dstaddr->sa_len + sdl->sdl_nlen + 1; 6351553Srgrimes np = (struct neighbor *)malloc(len); 6361553Srgrimes if (np == NULL) 6371553Srgrimes quit("malloc of neighbor structure"); 6381553Srgrimes memset(np, 0, len); 6391553Srgrimes np->n_flags = flags; 6401553Srgrimes np->n_addr = (struct sockaddr *)(np + 1); 6411553Srgrimes np->n_addrlen = dstaddr->sa_len; 6421553Srgrimes np->n_name = np->n_addrlen + (char *)np->n_addr; 64310087Sjkh memcpy((char *)np->n_addr, (char *)dstaddr, np->n_addrlen); 64410087Sjkh memcpy(np->n_name, sdl->sdl_data, sdl->sdl_nlen); 64510087Sjkh if (multicast_mode == PER_INTERFACE_MULTICAST && 64610087Sjkh (flags & IFF_MULTICAST) && 64710087Sjkh !(flags & IFF_LOOPBACK)) { 64810087Sjkh struct ip_mreq mreq; 64910087Sjkh 65010087Sjkh memcpy((char *)np->n_addr, (char *)ifaddr, 65110087Sjkh np->n_addrlen); 65210087Sjkh mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP); 65310087Sjkh mreq.imr_interface.s_addr = 65410087Sjkh ((struct sockaddr_in *)np->n_addr)->sin_addr.s_addr; 65510087Sjkh if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, 65610087Sjkh &mreq, sizeof(mreq)) < 0) { 65710087Sjkh syslog(LOG_ERR, 65810087Sjkh "setsockopt IP_ADD_MEMBERSHIP: %m"); 65910087Sjkh#if 0 66010087Sjkh /* Fall back to broadcast on this if. */ 66110087Sjkh np->n_flags &= ~IFF_MULTICAST; 66210087Sjkh#else 66310087Sjkh free((char *)np); 66410087Sjkh continue; 66510087Sjkh#endif 66610087Sjkh } 66710087Sjkh } 6681553Srgrimes np->n_next = neighbors; 6691553Srgrimes neighbors = np; 6701553Srgrimes } 6711553Srgrimes free(buf); 6721553Srgrimes return (1); 6731553Srgrimes} 6741553Srgrimes 6751553Srgrimes#ifdef DEBUG 6761553Srgrimesvoid 6771553SrgrimesSendto(s, buf, cc, flags, to, tolen) 6781553Srgrimes int s; 67917829Spst const void *buf; 68017829Spst size_t cc; 68117829Spst int flags; 68217829Spst const struct sockaddr *to; 6831553Srgrimes int tolen; 6841553Srgrimes{ 6851553Srgrimes register struct whod *w = (struct whod *)buf; 6861553Srgrimes register struct whoent *we; 6871553Srgrimes struct sockaddr_in *sin = (struct sockaddr_in *)to; 6881553Srgrimes 68917829Spst printf("sendto %x.%d\n", ntohl(sin->sin_addr.s_addr), 69017829Spst ntohs(sin->sin_port)); 6911553Srgrimes printf("hostname %s %s\n", w->wd_hostname, 6921553Srgrimes interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up")); 6931553Srgrimes printf("load %4.2f, %4.2f, %4.2f\n", 6941553Srgrimes ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0, 6951553Srgrimes ntohl(w->wd_loadav[2]) / 100.0); 6961553Srgrimes cc -= WHDRSIZE; 6971553Srgrimes for (we = w->wd_we, cc /= sizeof(struct whoent); cc > 0; cc--, we++) { 69889572Sdillon time_t t = _time32_to_time(ntohl(we->we_utmp.out_time)); 6991553Srgrimes printf("%-8.8s %s:%s %.12s", 7001553Srgrimes we->we_utmp.out_name, 7011553Srgrimes w->wd_hostname, we->we_utmp.out_line, 7021553Srgrimes ctime(&t)+4); 7031553Srgrimes we->we_idle = ntohl(we->we_idle) / 60; 7041553Srgrimes if (we->we_idle) { 7051553Srgrimes if (we->we_idle >= 100*60) 7061553Srgrimes we->we_idle = 100*60 - 1; 7071553Srgrimes if (we->we_idle >= 60) 7081553Srgrimes printf(" %2d", we->we_idle / 60); 7091553Srgrimes else 7101553Srgrimes printf(" "); 7111553Srgrimes printf(":%02d", we->we_idle % 60); 7121553Srgrimes } 7131553Srgrimes printf("\n"); 7141553Srgrimes } 7151553Srgrimes} 7161553Srgrimes 7171553Srgrimeschar * 7181553Srgrimesinterval(time, updown) 7191553Srgrimes int time; 7201553Srgrimes char *updown; 7211553Srgrimes{ 7221553Srgrimes static char resbuf[32]; 7231553Srgrimes int days, hours, minutes; 7241553Srgrimes 7251553Srgrimes if (time < 0 || time > 3*30*24*60*60) { 7261553Srgrimes (void) sprintf(resbuf, " %s ??:??", updown); 7271553Srgrimes return (resbuf); 7281553Srgrimes } 7291553Srgrimes minutes = (time + 59) / 60; /* round to minutes */ 7301553Srgrimes hours = minutes / 60; minutes %= 60; 7311553Srgrimes days = hours / 24; hours %= 24; 7321553Srgrimes if (days) 7331553Srgrimes (void) sprintf(resbuf, "%s %2d+%02d:%02d", 7341553Srgrimes updown, days, hours, minutes); 7351553Srgrimes else 7361553Srgrimes (void) sprintf(resbuf, "%s %2d:%02d", 7371553Srgrimes updown, hours, minutes); 7381553Srgrimes return (resbuf); 7391553Srgrimes} 7401553Srgrimes#endif 741