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$"); 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> 71202206Sed#include <utmpx.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; 149201060Sedint s; 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 getboottime(0); 2351553Srgrimes if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 2361553Srgrimes syslog(LOG_ERR, "socket: %m"); 2371553Srgrimes exit(1); 2381553Srgrimes } 2391553Srgrimes if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { 2401553Srgrimes syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m"); 2411553Srgrimes exit(1); 2421553Srgrimes } 24399825Salfred memset(&soin, 0, sizeof(soin)); 24499825Salfred soin.sin_len = sizeof(soin); 24599825Salfred soin.sin_family = AF_INET; 24699825Salfred soin.sin_port = sp->s_port; 24799825Salfred if (bind(s, (struct sockaddr *)&soin, sizeof(soin)) < 0) { 2481553Srgrimes syslog(LOG_ERR, "bind: %m"); 2491553Srgrimes exit(1); 2501553Srgrimes } 251220969Ssimon if (setgid(unpriv_gid) != 0) { 252220969Ssimon syslog(LOG_ERR, "setgid: %m"); 253220969Ssimon exit(1); 254220969Ssimon } 255220969Ssimon if (setgroups(1, &unpriv_gid) != 0) { /* XXX BOGUS groups[0] = egid */ 256220969Ssimon syslog(LOG_ERR, "setgroups: %m"); 257220969Ssimon exit(1); 258220969Ssimon } 259220969Ssimon if (setuid(unpriv_uid) != 0) { 260220969Ssimon syslog(LOG_ERR, "setuid: %m"); 261220969Ssimon exit(1); 262220969Ssimon } 2631553Srgrimes if (!configure(s)) 2641553Srgrimes exit(1); 26542508Ssteve if (!quiet_mode) { 26642508Ssteve signal(SIGALRM, onalrm); 26742508Ssteve onalrm(0); 26842508Ssteve } 2691553Srgrimes for (;;) { 2701553Srgrimes struct whod wd; 271141918Sstefanf socklen_t len = sizeof(from); 272141918Sstefanf int cc, whod; 27385640Sdillon time_t t; 2741553Srgrimes 2751553Srgrimes cc = recvfrom(s, (char *)&wd, sizeof(struct whod), 0, 2761553Srgrimes (struct sockaddr *)&from, &len); 2771553Srgrimes if (cc <= 0) { 2781553Srgrimes if (cc < 0 && errno != EINTR) 2791553Srgrimes syslog(LOG_WARNING, "recv: %m"); 2801553Srgrimes continue; 2811553Srgrimes } 28241895Sdes if (from.sin_port != sp->s_port && !insecure_mode) { 28370284Siedowse syslog(LOG_WARNING, "%d: bad source port from %s", 28470284Siedowse ntohs(from.sin_port), inet_ntoa(from.sin_addr)); 2851553Srgrimes continue; 2861553Srgrimes } 28770284Siedowse if (cc < WHDRSIZE) { 28870284Siedowse syslog(LOG_WARNING, "short packet from %s", 28970284Siedowse inet_ntoa(from.sin_addr)); 29070284Siedowse continue; 29170284Siedowse } 2921553Srgrimes if (wd.wd_vers != WHODVERSION) 2931553Srgrimes continue; 2941553Srgrimes if (wd.wd_type != WHODTYPE_STATUS) 2951553Srgrimes continue; 29617829Spst if (!verify(wd.wd_hostname, sizeof wd.wd_hostname)) { 29770284Siedowse syslog(LOG_WARNING, "malformed host name from %s", 29870284Siedowse inet_ntoa(from.sin_addr)); 2991553Srgrimes continue; 3001553Srgrimes } 30130380Scharnier (void) snprintf(path, sizeof path, "whod.%s", wd.wd_hostname); 3021553Srgrimes /* 3031553Srgrimes * Rather than truncating and growing the file each time, 3041553Srgrimes * use ftruncate if size is less than previous size. 3051553Srgrimes */ 3061553Srgrimes whod = open(path, O_WRONLY | O_CREAT, 0644); 3071553Srgrimes if (whod < 0) { 3081553Srgrimes syslog(LOG_WARNING, "%s: %m", path); 3091553Srgrimes continue; 3101553Srgrimes } 3111553Srgrimes#if ENDIAN != BIG_ENDIAN 3121553Srgrimes { 3131553Srgrimes int i, n = (cc - WHDRSIZE)/sizeof(struct whoent); 3141553Srgrimes struct whoent *we; 3151553Srgrimes 3161553Srgrimes /* undo header byte swapping before writing to file */ 3171553Srgrimes wd.wd_sendtime = ntohl(wd.wd_sendtime); 3181553Srgrimes for (i = 0; i < 3; i++) 3191553Srgrimes wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]); 3201553Srgrimes wd.wd_boottime = ntohl(wd.wd_boottime); 3211553Srgrimes we = wd.wd_we; 3221553Srgrimes for (i = 0; i < n; i++) { 3231553Srgrimes we->we_idle = ntohl(we->we_idle); 3241553Srgrimes we->we_utmp.out_time = 3251553Srgrimes ntohl(we->we_utmp.out_time); 3261553Srgrimes we++; 3271553Srgrimes } 3281553Srgrimes } 3291553Srgrimes#endif 33085640Sdillon (void) time(&t); 33189572Sdillon wd.wd_recvtime = _time_to_int(t); 3321553Srgrimes (void) write(whod, (char *)&wd, cc); 3331553Srgrimes if (fstat(whod, &st) < 0 || st.st_size > cc) 3341553Srgrimes ftruncate(whod, cc); 3351553Srgrimes (void) close(whod); 3361553Srgrimes } 3371553Srgrimes} 3381553Srgrimes 33930380Scharnierstatic void 34030380Scharnierusage() 34130380Scharnier{ 34248229Sbrian fprintf(stderr, "usage: rwhod [-i] [-p] [-l] [-m [ttl]]\n"); 34330380Scharnier exit(1); 34430380Scharnier} 34517829Spst 34617829Spstvoid 34717829Spstrun_as(uid, gid) 34817829Spst uid_t *uid; 34917829Spst gid_t *gid; 35017829Spst{ 35117829Spst struct passwd *pw; 35218092Speter struct group *gr; 35317829Spst 35417829Spst pw = getpwnam(UNPRIV_USER); 35517829Spst if (!pw) { 35617829Spst syslog(LOG_ERR, "getpwnam(%s): %m", UNPRIV_USER); 35717829Spst exit(1); 35817829Spst } 35917829Spst *uid = pw->pw_uid; 36017829Spst 36118092Speter gr = getgrnam(UNPRIV_GROUP); 36218092Speter if (!gr) { 36318092Speter syslog(LOG_ERR, "getgrnam(%s): %m", UNPRIV_GROUP); 36417829Spst exit(1); 36517829Spst } 36618092Speter *gid = gr->gr_gid; 36717829Spst} 36817829Spst 3691553Srgrimes/* 3701553Srgrimes * Check out host name for unprintables 3711553Srgrimes * and other funnies before allowing a file 3721553Srgrimes * to be created. Sorry, but blanks aren't allowed. 3731553Srgrimes */ 3741553Srgrimesint 37517829Spstverify(name, maxlen) 3761553Srgrimes register char *name; 37717829Spst register int maxlen; 3781553Srgrimes{ 3791553Srgrimes register int size = 0; 3801553Srgrimes 38119303Simp while (*name && size < maxlen - 1) { 3821553Srgrimes if (!isascii(*name) || !(isalnum(*name) || ispunct(*name))) 3831553Srgrimes return (0); 3841553Srgrimes name++, size++; 3851553Srgrimes } 38617829Spst *name = '\0'; 3871553Srgrimes return (size > 0); 3881553Srgrimes} 3891553Srgrimes 3901553Srgrimesvoid 391201060Sedonalrm(int signo __unused) 3921553Srgrimes{ 393201060Sed struct neighbor *np; 394201060Sed struct whoent *we = mywd.wd_we, *wend; 3951553Srgrimes struct stat stb; 396201060Sed struct utmpx *ut; 397201060Sed static int alarmcount = 0; 3981553Srgrimes double avenrun[3]; 3991553Srgrimes time_t now; 400201060Sed int i, cc; 4011553Srgrimes 4021553Srgrimes now = time(NULL); 4031553Srgrimes if (alarmcount % 10 == 0) 4041553Srgrimes getboottime(0); 4051553Srgrimes alarmcount++; 406201060Sed wend = &mywd.wd_we[1024 / sizeof(struct whoent)]; 407201060Sed setutxent(); 408201060Sed while ((ut = getutxent()) != NULL && we < wend) { 409201060Sed if (ut->ut_type != USER_PROCESS) 410201060Sed continue; 411201060Sed strncpy(we->we_utmp.out_line, ut->ut_line, 412201060Sed sizeof(we->we_utmp.out_line)); 413201060Sed strncpy(we->we_utmp.out_name, ut->ut_user, 414201060Sed sizeof(we->we_utmp.out_name)); 415201060Sed we->we_utmp.out_time = 416201060Sed htonl(_time_to_time32(ut->ut_tv.tv_sec)); 417201060Sed we++; 4181553Srgrimes } 419201060Sed endutxent(); 4201553Srgrimes 421201060Sed if (chdir(_PATH_DEV)) { 4221553Srgrimes syslog(LOG_ERR, "chdir(%s): %m", _PATH_DEV); 4231553Srgrimes exit(1); 4241553Srgrimes } 425201060Sed wend = we; 426201060Sed for (we = mywd.wd_we; we < wend; we++) { 4271553Srgrimes if (stat(we->we_utmp.out_line, &stb) >= 0) 4281553Srgrimes we->we_idle = htonl(now - stb.st_atime); 4291553Srgrimes we++; 4301553Srgrimes } 4311553Srgrimes (void)getloadavg(avenrun, sizeof(avenrun)/sizeof(avenrun[0])); 4321553Srgrimes for (i = 0; i < 3; i++) 4331553Srgrimes mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100)); 434201060Sed cc = (char *)wend - (char *)&mywd; 43589572Sdillon mywd.wd_sendtime = htonl(_time_to_time32(time(NULL))); 4361553Srgrimes mywd.wd_vers = WHODVERSION; 4371553Srgrimes mywd.wd_type = WHODTYPE_STATUS; 43810087Sjkh if (multicast_mode == SCOPED_MULTICAST) { 43910087Sjkh (void) sendto(s, (char *)&mywd, cc, 0, 44010087Sjkh (struct sockaddr *)&multicast_addr, 44110087Sjkh sizeof(multicast_addr)); 44210087Sjkh } 44310087Sjkh else for (np = neighbors; np != NULL; np = np->n_next) { 44410087Sjkh if (multicast_mode == PER_INTERFACE_MULTICAST && 44510087Sjkh np->n_flags & IFF_MULTICAST) { 44610087Sjkh /* 44710087Sjkh * Select the outgoing interface for the multicast. 44810087Sjkh */ 44910087Sjkh if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, 45010087Sjkh &(((struct sockaddr_in *)np->n_addr)->sin_addr), 45110087Sjkh sizeof(struct in_addr)) < 0) { 45210087Sjkh syslog(LOG_ERR, 45310087Sjkh "setsockopt IP_MULTICAST_IF: %m"); 45410087Sjkh exit(1); 45510087Sjkh } 45610087Sjkh (void) sendto(s, (char *)&mywd, cc, 0, 45710087Sjkh (struct sockaddr *)&multicast_addr, 45810087Sjkh sizeof(multicast_addr)); 45910087Sjkh } else (void) sendto(s, (char *)&mywd, cc, 0, 46010087Sjkh np->n_addr, np->n_addrlen); 46110087Sjkh } 462201060Sed if (chdir(_PATH_RWHODIR)) { 4631553Srgrimes syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR); 4641553Srgrimes exit(1); 4651553Srgrimes } 4661553Srgrimes (void) alarm(AL_INTERVAL); 4671553Srgrimes} 4681553Srgrimes 4691553Srgrimesvoid 4701553Srgrimesgetboottime(signo) 47199825Salfred int signo __unused; 4721553Srgrimes{ 4731553Srgrimes int mib[2]; 4741553Srgrimes size_t size; 4751553Srgrimes struct timeval tm; 4761553Srgrimes 4771553Srgrimes mib[0] = CTL_KERN; 4781553Srgrimes mib[1] = KERN_BOOTTIME; 4791553Srgrimes size = sizeof(tm); 4801553Srgrimes if (sysctl(mib, 2, &tm, &size, NULL, 0) == -1) { 4811553Srgrimes syslog(LOG_ERR, "cannot get boottime: %m"); 4821553Srgrimes exit(1); 4831553Srgrimes } 48489572Sdillon mywd.wd_boottime = htonl(_time_to_time32(tm.tv_sec)); 4851553Srgrimes} 4861553Srgrimes 4871553Srgrimesvoid 4881553Srgrimesquit(msg) 48999825Salfred const char *msg; 4901553Srgrimes{ 49162989Skris syslog(LOG_ERR, "%s", msg); 4921553Srgrimes exit(1); 4931553Srgrimes} 4941553Srgrimes 4951553Srgrimesvoid 4961553Srgrimesrt_xaddrs(cp, cplim, rtinfo) 4971553Srgrimes register caddr_t cp, cplim; 4981553Srgrimes register struct rt_addrinfo *rtinfo; 4991553Srgrimes{ 5001553Srgrimes register struct sockaddr *sa; 5011553Srgrimes register int i; 5021553Srgrimes 5031553Srgrimes memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info)); 5041553Srgrimes for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) { 5051553Srgrimes if ((rtinfo->rti_addrs & (1 << i)) == 0) 5061553Srgrimes continue; 5071553Srgrimes rtinfo->rti_info[i] = sa = (struct sockaddr *)cp; 508128186Sluigi cp += SA_SIZE(sa); 5091553Srgrimes } 5101553Srgrimes} 5111553Srgrimes 5121553Srgrimes/* 5131553Srgrimes * Figure out device configuration and select 5141553Srgrimes * networks which deserve status information. 5151553Srgrimes */ 5161553Srgrimesint 51799825Salfredconfigure(so) 51899825Salfred int so; 5191553Srgrimes{ 5201553Srgrimes register struct neighbor *np; 5211553Srgrimes register struct if_msghdr *ifm; 5221553Srgrimes register struct ifa_msghdr *ifam; 5231553Srgrimes struct sockaddr_dl *sdl; 5241553Srgrimes size_t needed; 5251553Srgrimes int mib[6], flags = 0, len; 5261553Srgrimes char *buf, *lim, *next; 5271553Srgrimes struct rt_addrinfo info; 5281553Srgrimes 52910087Sjkh if (multicast_mode != NO_MULTICAST) { 53010087Sjkh multicast_addr.sin_addr.s_addr = htonl(INADDR_WHOD_GROUP); 53110087Sjkh multicast_addr.sin_port = sp->s_port; 53210087Sjkh } 53310087Sjkh 53410087Sjkh if (multicast_mode == SCOPED_MULTICAST) { 53510087Sjkh struct ip_mreq mreq; 53610087Sjkh unsigned char ttl; 53710087Sjkh 53810087Sjkh mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP); 53910087Sjkh mreq.imr_interface.s_addr = htonl(INADDR_ANY); 54099825Salfred if (setsockopt(so, IPPROTO_IP, IP_ADD_MEMBERSHIP, 54110087Sjkh &mreq, sizeof(mreq)) < 0) { 54210087Sjkh syslog(LOG_ERR, 54310087Sjkh "setsockopt IP_ADD_MEMBERSHIP: %m"); 54410087Sjkh return(0); 54510087Sjkh } 54610087Sjkh ttl = multicast_scope; 54799825Salfred if (setsockopt(so, IPPROTO_IP, IP_MULTICAST_TTL, 54810087Sjkh &ttl, sizeof(ttl)) < 0) { 54910087Sjkh syslog(LOG_ERR, 55010087Sjkh "setsockopt IP_MULTICAST_TTL: %m"); 55110087Sjkh return(0); 55210087Sjkh } 55310087Sjkh return(1); 55410087Sjkh } 55510087Sjkh 5561553Srgrimes mib[0] = CTL_NET; 5571553Srgrimes mib[1] = PF_ROUTE; 5581553Srgrimes mib[2] = 0; 5591553Srgrimes mib[3] = AF_INET; 5601553Srgrimes mib[4] = NET_RT_IFLIST; 5611553Srgrimes mib[5] = 0; 5621553Srgrimes if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 5631553Srgrimes quit("route-sysctl-estimate"); 5641553Srgrimes if ((buf = malloc(needed)) == NULL) 5651553Srgrimes quit("malloc"); 5661553Srgrimes if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) 5671553Srgrimes quit("actual retrieval of interface table"); 5681553Srgrimes lim = buf + needed; 5691553Srgrimes 5701553Srgrimes sdl = NULL; /* XXX just to keep gcc -Wall happy */ 5711553Srgrimes for (next = buf; next < lim; next += ifm->ifm_msglen) { 5721553Srgrimes ifm = (struct if_msghdr *)next; 5731553Srgrimes if (ifm->ifm_type == RTM_IFINFO) { 5741553Srgrimes sdl = (struct sockaddr_dl *)(ifm + 1); 5751553Srgrimes flags = ifm->ifm_flags; 5761553Srgrimes continue; 5771553Srgrimes } 5781553Srgrimes if ((flags & IFF_UP) == 0 || 57910087Sjkh (flags & (((multicast_mode == PER_INTERFACE_MULTICAST) ? 58010087Sjkh IFF_MULTICAST : 0) | 58147963Sbrian IFF_BROADCAST|iff_flag)) == 0) 5821553Srgrimes continue; 5831553Srgrimes if (ifm->ifm_type != RTM_NEWADDR) 5841553Srgrimes quit("out of sync parsing NET_RT_IFLIST"); 5851553Srgrimes ifam = (struct ifa_msghdr *)ifm; 5861553Srgrimes info.rti_addrs = ifam->ifam_addrs; 5871553Srgrimes rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam, 5881553Srgrimes &info); 5891553Srgrimes /* gag, wish we could get rid of Internet dependencies */ 5901553Srgrimes#define dstaddr info.rti_info[RTAX_BRD] 59110087Sjkh#define ifaddr info.rti_info[RTAX_IFA] 5921553Srgrimes#define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr 5931553Srgrimes#define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port 5941553Srgrimes if (dstaddr == 0 || dstaddr->sa_family != AF_INET) 5951553Srgrimes continue; 5961553Srgrimes PORT_SA(dstaddr) = sp->s_port; 5971553Srgrimes for (np = neighbors; np != NULL; np = np->n_next) 5981553Srgrimes if (memcmp(sdl->sdl_data, np->n_name, 5991553Srgrimes sdl->sdl_nlen) == 0 && 6001553Srgrimes IPADDR_SA(np->n_addr) == IPADDR_SA(dstaddr)) 6011553Srgrimes break; 6021553Srgrimes if (np != NULL) 6031553Srgrimes continue; 6041553Srgrimes len = sizeof(*np) + dstaddr->sa_len + sdl->sdl_nlen + 1; 6051553Srgrimes np = (struct neighbor *)malloc(len); 6061553Srgrimes if (np == NULL) 6071553Srgrimes quit("malloc of neighbor structure"); 6081553Srgrimes memset(np, 0, len); 6091553Srgrimes np->n_flags = flags; 6101553Srgrimes np->n_addr = (struct sockaddr *)(np + 1); 6111553Srgrimes np->n_addrlen = dstaddr->sa_len; 6121553Srgrimes np->n_name = np->n_addrlen + (char *)np->n_addr; 61310087Sjkh memcpy((char *)np->n_addr, (char *)dstaddr, np->n_addrlen); 61410087Sjkh memcpy(np->n_name, sdl->sdl_data, sdl->sdl_nlen); 61510087Sjkh if (multicast_mode == PER_INTERFACE_MULTICAST && 61610087Sjkh (flags & IFF_MULTICAST) && 61710087Sjkh !(flags & IFF_LOOPBACK)) { 61810087Sjkh struct ip_mreq mreq; 61910087Sjkh 62010087Sjkh memcpy((char *)np->n_addr, (char *)ifaddr, 62110087Sjkh np->n_addrlen); 62210087Sjkh mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP); 62310087Sjkh mreq.imr_interface.s_addr = 62410087Sjkh ((struct sockaddr_in *)np->n_addr)->sin_addr.s_addr; 62510087Sjkh if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, 62610087Sjkh &mreq, sizeof(mreq)) < 0) { 62710087Sjkh syslog(LOG_ERR, 62810087Sjkh "setsockopt IP_ADD_MEMBERSHIP: %m"); 62910087Sjkh#if 0 63010087Sjkh /* Fall back to broadcast on this if. */ 63110087Sjkh np->n_flags &= ~IFF_MULTICAST; 63210087Sjkh#else 63310087Sjkh free((char *)np); 63410087Sjkh continue; 63510087Sjkh#endif 63610087Sjkh } 63710087Sjkh } 6381553Srgrimes np->n_next = neighbors; 6391553Srgrimes neighbors = np; 6401553Srgrimes } 6411553Srgrimes free(buf); 6421553Srgrimes return (1); 6431553Srgrimes} 6441553Srgrimes 6451553Srgrimes#ifdef DEBUG 6461553Srgrimesvoid 6471553SrgrimesSendto(s, buf, cc, flags, to, tolen) 6481553Srgrimes int s; 64917829Spst const void *buf; 65017829Spst size_t cc; 65117829Spst int flags; 65217829Spst const struct sockaddr *to; 6531553Srgrimes int tolen; 6541553Srgrimes{ 6551553Srgrimes register struct whod *w = (struct whod *)buf; 6561553Srgrimes register struct whoent *we; 6571553Srgrimes struct sockaddr_in *sin = (struct sockaddr_in *)to; 6581553Srgrimes 65917829Spst printf("sendto %x.%d\n", ntohl(sin->sin_addr.s_addr), 66017829Spst ntohs(sin->sin_port)); 6611553Srgrimes printf("hostname %s %s\n", w->wd_hostname, 6621553Srgrimes interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up")); 6631553Srgrimes printf("load %4.2f, %4.2f, %4.2f\n", 6641553Srgrimes ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0, 6651553Srgrimes ntohl(w->wd_loadav[2]) / 100.0); 6661553Srgrimes cc -= WHDRSIZE; 6671553Srgrimes for (we = w->wd_we, cc /= sizeof(struct whoent); cc > 0; cc--, we++) { 66889572Sdillon time_t t = _time32_to_time(ntohl(we->we_utmp.out_time)); 6691553Srgrimes printf("%-8.8s %s:%s %.12s", 6701553Srgrimes we->we_utmp.out_name, 6711553Srgrimes w->wd_hostname, we->we_utmp.out_line, 6721553Srgrimes ctime(&t)+4); 6731553Srgrimes we->we_idle = ntohl(we->we_idle) / 60; 6741553Srgrimes if (we->we_idle) { 6751553Srgrimes if (we->we_idle >= 100*60) 6761553Srgrimes we->we_idle = 100*60 - 1; 6771553Srgrimes if (we->we_idle >= 60) 6781553Srgrimes printf(" %2d", we->we_idle / 60); 6791553Srgrimes else 6801553Srgrimes printf(" "); 6811553Srgrimes printf(":%02d", we->we_idle % 60); 6821553Srgrimes } 6831553Srgrimes printf("\n"); 6841553Srgrimes } 6851553Srgrimes} 6861553Srgrimes 6871553Srgrimeschar * 6881553Srgrimesinterval(time, updown) 6891553Srgrimes int time; 6901553Srgrimes char *updown; 6911553Srgrimes{ 6921553Srgrimes static char resbuf[32]; 6931553Srgrimes int days, hours, minutes; 6941553Srgrimes 6951553Srgrimes if (time < 0 || time > 3*30*24*60*60) { 6961553Srgrimes (void) sprintf(resbuf, " %s ??:??", updown); 6971553Srgrimes return (resbuf); 6981553Srgrimes } 6991553Srgrimes minutes = (time + 59) / 60; /* round to minutes */ 7001553Srgrimes hours = minutes / 60; minutes %= 60; 7011553Srgrimes days = hours / 24; hours %= 24; 7021553Srgrimes if (days) 7031553Srgrimes (void) sprintf(resbuf, "%s %2d+%02d:%02d", 7041553Srgrimes updown, days, hours, minutes); 7051553Srgrimes else 7061553Srgrimes (void) sprintf(resbuf, "%s %2d:%02d", 7071553Srgrimes updown, hours, minutes); 7081553Srgrimes return (resbuf); 7091553Srgrimes} 7101553Srgrimes#endif 711