rwhod.c revision 89572
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[] = 4550479Speter "$FreeBSD: head/usr.sbin/rwhod/rwhod.c 89572 2002-01-19 23:20:02Z dillon $"; 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> 5970284Siedowse#include <arpa/inet.h> 601553Srgrimes#include <protocols/rwhod.h> 611553Srgrimes 621553Srgrimes#include <ctype.h> 6330380Scharnier#include <err.h> 641553Srgrimes#include <errno.h> 651553Srgrimes#include <fcntl.h> 661553Srgrimes#include <netdb.h> 671553Srgrimes#include <paths.h> 681553Srgrimes#include <stdio.h> 691553Srgrimes#include <stdlib.h> 701553Srgrimes#include <string.h> 711553Srgrimes#include <syslog.h> 721553Srgrimes#include <unistd.h> 731553Srgrimes#include <utmp.h> 7417829Spst#include <pwd.h> 7518092Speter#include <grp.h> 761553Srgrimes 771553Srgrimes/* 7810087Sjkh * This version of Berkeley's rwhod has been modified to use IP multicast 7910087Sjkh * datagrams, under control of a new command-line option: 8010087Sjkh * 8110087Sjkh * rwhod -m causes rwhod to use IP multicast (instead of 8210087Sjkh * broadcast or unicast) on all interfaces that have 8310087Sjkh * the IFF_MULTICAST flag set in their "ifnet" structs 8410087Sjkh * (excluding the loopback interface). The multicast 8510087Sjkh * reports are sent with a time-to-live of 1, to prevent 8610087Sjkh * forwarding beyond the directly-connected subnet(s). 8710087Sjkh * 8810087Sjkh * rwhod -m <ttl> causes rwhod to send IP multicast datagrams with a 8910087Sjkh * time-to-live of <ttl>, via a SINGLE interface rather 9010087Sjkh * than all interfaces. <ttl> must be between 0 and 9110087Sjkh * MAX_MULTICAST_SCOPE, defined below. Note that "-m 1" 9210087Sjkh * is different than "-m", in that "-m 1" specifies 9310087Sjkh * transmission on one interface only. 9410087Sjkh * 9510087Sjkh * When "-m" is used without a <ttl> argument, the program accepts multicast 9610087Sjkh * rwhod reports from all multicast-capable interfaces. If a <ttl> argument 9710087Sjkh * is given, it accepts multicast reports from only one interface, the one 9810087Sjkh * on which reports are sent (which may be controlled via the host's routing 9910087Sjkh * table). Regardless of the "-m" option, the program accepts broadcast or 10010087Sjkh * unicast reports from all interfaces. Thus, this program will hear the 10110087Sjkh * reports of old, non-multicasting rwhods, but, if multicasting is used, 10210087Sjkh * those old rwhods won't hear the reports generated by this program. 10310087Sjkh * 10410087Sjkh * -- Steve Deering, Stanford University, February 1989 10510087Sjkh */ 10610087Sjkh 10717832Spst#define UNPRIV_USER "daemon" 10817829Spst#define UNPRIV_GROUP "daemon" 10917829Spst 11010087Sjkh#define NO_MULTICAST 0 /* multicast modes */ 11110087Sjkh#define PER_INTERFACE_MULTICAST 1 11210087Sjkh#define SCOPED_MULTICAST 2 11310087Sjkh 11410087Sjkh#define MAX_MULTICAST_SCOPE 32 /* "site-wide", by convention */ 11510087Sjkh 11610087Sjkh#define INADDR_WHOD_GROUP (u_long)0xe0000103 /* 224.0.1.3 */ 11710087Sjkh /* (belongs in protocols/rwhod.h) */ 11810087Sjkh 11941895Sdesint insecure_mode; 12042508Ssteveint quiet_mode; 12147963Sbrianint iff_flag = IFF_POINTOPOINT; 12210087Sjkhint multicast_mode = NO_MULTICAST; 12310087Sjkhint multicast_scope; 12410087Sjkhstruct sockaddr_in multicast_addr = { sizeof multicast_addr, AF_INET }; 12510087Sjkh 12610087Sjkh/* 1271553Srgrimes * Alarm interval. Don't forget to change the down time check in ruptime 1281553Srgrimes * if this is changed. 1291553Srgrimes */ 1301553Srgrimes#define AL_INTERVAL (3 * 60) 1311553Srgrimes 1321553Srgrimeschar myname[MAXHOSTNAMELEN]; 1331553Srgrimes 1341553Srgrimes/* 1351553Srgrimes * We communicate with each neighbor in a list constructed at the time we're 1361553Srgrimes * started up. Neighbors are currently directly connected via a hardware 1371553Srgrimes * interface. 1381553Srgrimes */ 1391553Srgrimesstruct neighbor { 1401553Srgrimes struct neighbor *n_next; 1411553Srgrimes char *n_name; /* interface name */ 1421553Srgrimes struct sockaddr *n_addr; /* who to send to */ 1431553Srgrimes int n_addrlen; /* size of address */ 1441553Srgrimes int n_flags; /* should forward?, interface flags */ 1451553Srgrimes}; 1461553Srgrimes 1471553Srgrimesstruct neighbor *neighbors; 1481553Srgrimesstruct whod mywd; 1491553Srgrimesstruct servent *sp; 1501553Srgrimesint s, utmpf; 1511553Srgrimes 1521553Srgrimes#define WHDRSIZE (sizeof(mywd) - sizeof(mywd.wd_we)) 1531553Srgrimes 15417829Spstvoid run_as __P((uid_t *, gid_t *)); 1551553Srgrimesint configure __P((int)); 1561553Srgrimesvoid getboottime __P((int)); 1571553Srgrimesvoid onalrm __P((int)); 1581553Srgrimesvoid quit __P((char *)); 1591553Srgrimesvoid rt_xaddrs __P((caddr_t, caddr_t, struct rt_addrinfo *)); 16017829Spstint verify __P((char *, int)); 16130380Scharnierstatic void usage __P((void)); 1621553Srgrimes#ifdef DEBUG 1631553Srgrimeschar *interval __P((int, char *)); 16417829Spstvoid Sendto __P((int, const void *, size_t, int, 16517829Spst const struct sockaddr *, int)); 1661553Srgrimes#define sendto Sendto 1671553Srgrimes#endif 1681553Srgrimes 1691553Srgrimesint 1701553Srgrimesmain(argc, argv) 1711553Srgrimes int argc; 17210087Sjkh char *argv[]; 1731553Srgrimes{ 1741553Srgrimes struct sockaddr_in from; 1751553Srgrimes struct stat st; 1761553Srgrimes char path[64]; 1771553Srgrimes int on = 1; 1781553Srgrimes char *cp; 1791553Srgrimes struct sockaddr_in sin; 18017829Spst uid_t unpriv_uid; 18117829Spst gid_t unpriv_gid; 1821553Srgrimes 18330380Scharnier if (getuid()) 18430380Scharnier errx(1, "not super user"); 18517829Spst 18617829Spst run_as(&unpriv_uid, &unpriv_gid); 18717829Spst 18810087Sjkh argv++; argc--; 18910087Sjkh while (argc > 0 && *argv[0] == '-') { 19010087Sjkh if (strcmp(*argv, "-m") == 0) { 19110087Sjkh if (argc > 1 && isdigit(*(argv + 1)[0])) { 19210087Sjkh argv++, argc--; 19310087Sjkh multicast_mode = SCOPED_MULTICAST; 19410087Sjkh multicast_scope = atoi(*argv); 19530380Scharnier if (multicast_scope > MAX_MULTICAST_SCOPE) 19630380Scharnier errx(1, "ttl must not exceed %u", 19710087Sjkh MAX_MULTICAST_SCOPE); 19810087Sjkh } 19910087Sjkh else multicast_mode = PER_INTERFACE_MULTICAST; 20010087Sjkh } 20141895Sdes else if (strcmp(*argv, "-i") == 0) 20242508Ssteve insecure_mode = 1; 20342508Ssteve else if (strcmp(*argv, "-l") == 0) 20442508Ssteve quiet_mode = 1; 20547963Sbrian else if (strcmp(*argv, "-p") == 0) 20647963Sbrian iff_flag = 0; 20730380Scharnier else 20830380Scharnier usage(); 20910087Sjkh argv++, argc--; 21010087Sjkh } 21130380Scharnier if (argc > 0) 21230380Scharnier usage(); 2131553Srgrimes#ifndef DEBUG 2141553Srgrimes daemon(1, 0); 2151553Srgrimes#endif 21610087Sjkh (void) signal(SIGHUP, getboottime); 21710087Sjkh openlog("rwhod", LOG_PID, LOG_DAEMON); 21810087Sjkh sp = getservbyname("who", "udp"); 21910087Sjkh if (sp == NULL) { 22053770Scharnier syslog(LOG_ERR, "udp/who: unknown service"); 22110087Sjkh exit(1); 22210087Sjkh } 2231553Srgrimes if (chdir(_PATH_RWHODIR) < 0) { 22453770Scharnier syslog(LOG_ERR, "%s: %m", _PATH_RWHODIR); 2251553Srgrimes exit(1); 2261553Srgrimes } 2271553Srgrimes /* 2281553Srgrimes * Establish host name as returned by system. 2291553Srgrimes */ 2301553Srgrimes if (gethostname(myname, sizeof(myname) - 1) < 0) { 2311553Srgrimes syslog(LOG_ERR, "gethostname: %m"); 2321553Srgrimes exit(1); 2331553Srgrimes } 2341553Srgrimes if ((cp = index(myname, '.')) != NULL) 2351553Srgrimes *cp = '\0'; 23619303Simp strncpy(mywd.wd_hostname, myname, sizeof(mywd.wd_hostname) - 1); 23719303Simp mywd.wd_hostname[sizeof(mywd.wd_hostname) - 1] = '\0'; 2381553Srgrimes utmpf = open(_PATH_UTMP, O_RDONLY|O_CREAT, 0644); 2391553Srgrimes if (utmpf < 0) { 2401553Srgrimes syslog(LOG_ERR, "%s: %m", _PATH_UTMP); 2411553Srgrimes exit(1); 2421553Srgrimes } 2431553Srgrimes getboottime(0); 2441553Srgrimes if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 2451553Srgrimes syslog(LOG_ERR, "socket: %m"); 2461553Srgrimes exit(1); 2471553Srgrimes } 2481553Srgrimes if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { 2491553Srgrimes syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m"); 2501553Srgrimes exit(1); 2511553Srgrimes } 2521553Srgrimes memset(&sin, 0, sizeof(sin)); 25310087Sjkh sin.sin_len = sizeof(sin); 2541553Srgrimes sin.sin_family = AF_INET; 2551553Srgrimes sin.sin_port = sp->s_port; 2561553Srgrimes if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 2571553Srgrimes syslog(LOG_ERR, "bind: %m"); 2581553Srgrimes exit(1); 2591553Srgrimes } 26017829Spst setgid(unpriv_gid); 26118092Speter setgroups(1, &unpriv_gid); /* XXX BOGUS groups[0] = egid */ 26217829Spst setuid(unpriv_uid); 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; 2711553Srgrimes int cc, whod, len = sizeof(from); 27285640Sdillon time_t t; 2731553Srgrimes 2741553Srgrimes cc = recvfrom(s, (char *)&wd, sizeof(struct whod), 0, 2751553Srgrimes (struct sockaddr *)&from, &len); 2761553Srgrimes if (cc <= 0) { 2771553Srgrimes if (cc < 0 && errno != EINTR) 2781553Srgrimes syslog(LOG_WARNING, "recv: %m"); 2791553Srgrimes continue; 2801553Srgrimes } 28141895Sdes if (from.sin_port != sp->s_port && !insecure_mode) { 28270284Siedowse syslog(LOG_WARNING, "%d: bad source port from %s", 28370284Siedowse ntohs(from.sin_port), inet_ntoa(from.sin_addr)); 2841553Srgrimes continue; 2851553Srgrimes } 28670284Siedowse if (cc < WHDRSIZE) { 28770284Siedowse syslog(LOG_WARNING, "short packet from %s", 28870284Siedowse inet_ntoa(from.sin_addr)); 28970284Siedowse continue; 29070284Siedowse } 2911553Srgrimes if (wd.wd_vers != WHODVERSION) 2921553Srgrimes continue; 2931553Srgrimes if (wd.wd_type != WHODTYPE_STATUS) 2941553Srgrimes continue; 29517829Spst if (!verify(wd.wd_hostname, sizeof wd.wd_hostname)) { 29670284Siedowse syslog(LOG_WARNING, "malformed host name from %s", 29770284Siedowse inet_ntoa(from.sin_addr)); 2981553Srgrimes continue; 2991553Srgrimes } 30030380Scharnier (void) snprintf(path, sizeof path, "whod.%s", wd.wd_hostname); 3011553Srgrimes /* 3021553Srgrimes * Rather than truncating and growing the file each time, 3031553Srgrimes * use ftruncate if size is less than previous size. 3041553Srgrimes */ 3051553Srgrimes whod = open(path, O_WRONLY | O_CREAT, 0644); 3061553Srgrimes if (whod < 0) { 3071553Srgrimes syslog(LOG_WARNING, "%s: %m", path); 3081553Srgrimes continue; 3091553Srgrimes } 3101553Srgrimes#if ENDIAN != BIG_ENDIAN 3111553Srgrimes { 3121553Srgrimes int i, n = (cc - WHDRSIZE)/sizeof(struct whoent); 3131553Srgrimes struct whoent *we; 3141553Srgrimes 3151553Srgrimes /* undo header byte swapping before writing to file */ 3161553Srgrimes wd.wd_sendtime = ntohl(wd.wd_sendtime); 3171553Srgrimes for (i = 0; i < 3; i++) 3181553Srgrimes wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]); 3191553Srgrimes wd.wd_boottime = ntohl(wd.wd_boottime); 3201553Srgrimes we = wd.wd_we; 3211553Srgrimes for (i = 0; i < n; i++) { 3221553Srgrimes we->we_idle = ntohl(we->we_idle); 3231553Srgrimes we->we_utmp.out_time = 3241553Srgrimes ntohl(we->we_utmp.out_time); 3251553Srgrimes we++; 3261553Srgrimes } 3271553Srgrimes } 3281553Srgrimes#endif 32985640Sdillon (void) time(&t); 33089572Sdillon wd.wd_recvtime = _time_to_int(t); 3311553Srgrimes (void) write(whod, (char *)&wd, cc); 3321553Srgrimes if (fstat(whod, &st) < 0 || st.st_size > cc) 3331553Srgrimes ftruncate(whod, cc); 3341553Srgrimes (void) close(whod); 3351553Srgrimes } 3361553Srgrimes} 3371553Srgrimes 33830380Scharnierstatic void 33930380Scharnierusage() 34030380Scharnier{ 34148229Sbrian fprintf(stderr, "usage: rwhod [-i] [-p] [-l] [-m [ttl]]\n"); 34230380Scharnier exit(1); 34330380Scharnier} 34417829Spst 34517829Spstvoid 34617829Spstrun_as(uid, gid) 34717829Spst uid_t *uid; 34817829Spst gid_t *gid; 34917829Spst{ 35017829Spst struct passwd *pw; 35118092Speter struct group *gr; 35217829Spst 35317829Spst pw = getpwnam(UNPRIV_USER); 35417829Spst if (!pw) { 35517829Spst syslog(LOG_ERR, "getpwnam(%s): %m", UNPRIV_USER); 35617829Spst exit(1); 35717829Spst } 35817829Spst *uid = pw->pw_uid; 35917829Spst 36018092Speter gr = getgrnam(UNPRIV_GROUP); 36118092Speter if (!gr) { 36218092Speter syslog(LOG_ERR, "getgrnam(%s): %m", UNPRIV_GROUP); 36317829Spst exit(1); 36417829Spst } 36518092Speter *gid = gr->gr_gid; 36617829Spst} 36717829Spst 3681553Srgrimes/* 3691553Srgrimes * Check out host name for unprintables 3701553Srgrimes * and other funnies before allowing a file 3711553Srgrimes * to be created. Sorry, but blanks aren't allowed. 3721553Srgrimes */ 3731553Srgrimesint 37417829Spstverify(name, maxlen) 3751553Srgrimes register char *name; 37617829Spst register int maxlen; 3771553Srgrimes{ 3781553Srgrimes register int size = 0; 3791553Srgrimes 38019303Simp while (*name && size < maxlen - 1) { 3811553Srgrimes if (!isascii(*name) || !(isalnum(*name) || ispunct(*name))) 3821553Srgrimes return (0); 3831553Srgrimes name++, size++; 3841553Srgrimes } 38517829Spst *name = '\0'; 3861553Srgrimes return (size > 0); 3871553Srgrimes} 3881553Srgrimes 3891553Srgrimesint utmptime; 3901553Srgrimesint utmpent; 3911553Srgrimesint utmpsize = 0; 3921553Srgrimesstruct utmp *utmp; 3931553Srgrimesint alarmcount; 3941553Srgrimes 3951553Srgrimesvoid 3961553Srgrimesonalrm(signo) 3971553Srgrimes int signo; 3981553Srgrimes{ 3991553Srgrimes register struct neighbor *np; 4001553Srgrimes register struct whoent *we = mywd.wd_we, *wlast; 4011553Srgrimes register int i; 4021553Srgrimes struct stat stb; 4031553Srgrimes double avenrun[3]; 4041553Srgrimes time_t now; 4051553Srgrimes int cc; 4061553Srgrimes 4071553Srgrimes now = time(NULL); 4081553Srgrimes if (alarmcount % 10 == 0) 4091553Srgrimes getboottime(0); 4101553Srgrimes alarmcount++; 4111553Srgrimes (void) fstat(utmpf, &stb); 4121553Srgrimes if ((stb.st_mtime != utmptime) || (stb.st_size > utmpsize)) { 4131553Srgrimes utmptime = stb.st_mtime; 4141553Srgrimes if (stb.st_size > utmpsize) { 4151553Srgrimes utmpsize = stb.st_size + 10 * sizeof(struct utmp); 4161553Srgrimes if (utmp) 4171553Srgrimes utmp = (struct utmp *)realloc(utmp, utmpsize); 4181553Srgrimes else 4191553Srgrimes utmp = (struct utmp *)malloc(utmpsize); 4201553Srgrimes if (! utmp) { 42130380Scharnier syslog(LOG_WARNING, "malloc failed"); 4221553Srgrimes utmpsize = 0; 4231553Srgrimes goto done; 4241553Srgrimes } 4251553Srgrimes } 4261553Srgrimes (void) lseek(utmpf, (off_t)0, L_SET); 4271553Srgrimes cc = read(utmpf, (char *)utmp, stb.st_size); 4281553Srgrimes if (cc < 0) { 42930380Scharnier syslog(LOG_ERR, "read(%s): %m", _PATH_UTMP); 4301553Srgrimes goto done; 4311553Srgrimes } 4321553Srgrimes wlast = &mywd.wd_we[1024 / sizeof(struct whoent) - 1]; 4331553Srgrimes utmpent = cc / sizeof(struct utmp); 4341553Srgrimes for (i = 0; i < utmpent; i++) 4351553Srgrimes if (utmp[i].ut_name[0]) { 4361553Srgrimes memcpy(we->we_utmp.out_line, utmp[i].ut_line, 4371553Srgrimes sizeof(utmp[i].ut_line)); 4381553Srgrimes memcpy(we->we_utmp.out_name, utmp[i].ut_name, 4391553Srgrimes sizeof(utmp[i].ut_name)); 4401553Srgrimes we->we_utmp.out_time = htonl(utmp[i].ut_time); 4411553Srgrimes if (we >= wlast) 4421553Srgrimes break; 4431553Srgrimes we++; 4441553Srgrimes } 4451553Srgrimes utmpent = we - mywd.wd_we; 4461553Srgrimes } 4471553Srgrimes 4481553Srgrimes /* 4491553Srgrimes * The test on utmpent looks silly---after all, if no one is 4501553Srgrimes * logged on, why worry about efficiency?---but is useful on 4511553Srgrimes * (e.g.) compute servers. 4521553Srgrimes */ 4531553Srgrimes if (utmpent && chdir(_PATH_DEV)) { 4541553Srgrimes syslog(LOG_ERR, "chdir(%s): %m", _PATH_DEV); 4551553Srgrimes exit(1); 4561553Srgrimes } 4571553Srgrimes we = mywd.wd_we; 4581553Srgrimes for (i = 0; i < utmpent; i++) { 4591553Srgrimes if (stat(we->we_utmp.out_line, &stb) >= 0) 4601553Srgrimes we->we_idle = htonl(now - stb.st_atime); 4611553Srgrimes we++; 4621553Srgrimes } 4631553Srgrimes (void)getloadavg(avenrun, sizeof(avenrun)/sizeof(avenrun[0])); 4641553Srgrimes for (i = 0; i < 3; i++) 4651553Srgrimes mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100)); 4661553Srgrimes cc = (char *)we - (char *)&mywd; 46789572Sdillon mywd.wd_sendtime = htonl(_time_to_time32(time(NULL))); 4681553Srgrimes mywd.wd_vers = WHODVERSION; 4691553Srgrimes mywd.wd_type = WHODTYPE_STATUS; 47010087Sjkh if (multicast_mode == SCOPED_MULTICAST) { 47110087Sjkh (void) sendto(s, (char *)&mywd, cc, 0, 47210087Sjkh (struct sockaddr *)&multicast_addr, 47310087Sjkh sizeof(multicast_addr)); 47410087Sjkh } 47510087Sjkh else for (np = neighbors; np != NULL; np = np->n_next) { 47610087Sjkh if (multicast_mode == PER_INTERFACE_MULTICAST && 47710087Sjkh np->n_flags & IFF_MULTICAST) { 47810087Sjkh /* 47910087Sjkh * Select the outgoing interface for the multicast. 48010087Sjkh */ 48110087Sjkh if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, 48210087Sjkh &(((struct sockaddr_in *)np->n_addr)->sin_addr), 48310087Sjkh sizeof(struct in_addr)) < 0) { 48410087Sjkh syslog(LOG_ERR, 48510087Sjkh "setsockopt IP_MULTICAST_IF: %m"); 48610087Sjkh exit(1); 48710087Sjkh } 48810087Sjkh (void) sendto(s, (char *)&mywd, cc, 0, 48910087Sjkh (struct sockaddr *)&multicast_addr, 49010087Sjkh sizeof(multicast_addr)); 49110087Sjkh } else (void) sendto(s, (char *)&mywd, cc, 0, 49210087Sjkh np->n_addr, np->n_addrlen); 49310087Sjkh } 4941553Srgrimes if (utmpent && chdir(_PATH_RWHODIR)) { 4951553Srgrimes syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR); 4961553Srgrimes exit(1); 4971553Srgrimes } 4981553Srgrimesdone: 4991553Srgrimes (void) alarm(AL_INTERVAL); 5001553Srgrimes} 5011553Srgrimes 5021553Srgrimesvoid 5031553Srgrimesgetboottime(signo) 5041553Srgrimes int signo; 5051553Srgrimes{ 5061553Srgrimes int mib[2]; 5071553Srgrimes size_t size; 5081553Srgrimes struct timeval tm; 5091553Srgrimes 5101553Srgrimes mib[0] = CTL_KERN; 5111553Srgrimes mib[1] = KERN_BOOTTIME; 5121553Srgrimes size = sizeof(tm); 5131553Srgrimes if (sysctl(mib, 2, &tm, &size, NULL, 0) == -1) { 5141553Srgrimes syslog(LOG_ERR, "cannot get boottime: %m"); 5151553Srgrimes exit(1); 5161553Srgrimes } 51789572Sdillon mywd.wd_boottime = htonl(_time_to_time32(tm.tv_sec)); 5181553Srgrimes} 5191553Srgrimes 5201553Srgrimesvoid 5211553Srgrimesquit(msg) 5221553Srgrimes char *msg; 5231553Srgrimes{ 52462989Skris syslog(LOG_ERR, "%s", msg); 5251553Srgrimes exit(1); 5261553Srgrimes} 5271553Srgrimes 5281553Srgrimes#define ROUNDUP(a) \ 5291553Srgrimes ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 5301553Srgrimes#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) 5311553Srgrimes 5321553Srgrimesvoid 5331553Srgrimesrt_xaddrs(cp, cplim, rtinfo) 5341553Srgrimes register caddr_t cp, cplim; 5351553Srgrimes register struct rt_addrinfo *rtinfo; 5361553Srgrimes{ 5371553Srgrimes register struct sockaddr *sa; 5381553Srgrimes register int i; 5391553Srgrimes 5401553Srgrimes memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info)); 5411553Srgrimes for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) { 5421553Srgrimes if ((rtinfo->rti_addrs & (1 << i)) == 0) 5431553Srgrimes continue; 5441553Srgrimes rtinfo->rti_info[i] = sa = (struct sockaddr *)cp; 5451553Srgrimes ADVANCE(cp, sa); 5461553Srgrimes } 5471553Srgrimes} 5481553Srgrimes 5491553Srgrimes/* 5501553Srgrimes * Figure out device configuration and select 5511553Srgrimes * networks which deserve status information. 5521553Srgrimes */ 5531553Srgrimesint 5541553Srgrimesconfigure(s) 5551553Srgrimes int s; 5561553Srgrimes{ 5571553Srgrimes register struct neighbor *np; 5581553Srgrimes register struct if_msghdr *ifm; 5591553Srgrimes register struct ifa_msghdr *ifam; 5601553Srgrimes struct sockaddr_dl *sdl; 5611553Srgrimes size_t needed; 5621553Srgrimes int mib[6], flags = 0, len; 5631553Srgrimes char *buf, *lim, *next; 5641553Srgrimes struct rt_addrinfo info; 5651553Srgrimes 56610087Sjkh if (multicast_mode != NO_MULTICAST) { 56710087Sjkh multicast_addr.sin_addr.s_addr = htonl(INADDR_WHOD_GROUP); 56810087Sjkh multicast_addr.sin_port = sp->s_port; 56910087Sjkh } 57010087Sjkh 57110087Sjkh if (multicast_mode == SCOPED_MULTICAST) { 57210087Sjkh struct ip_mreq mreq; 57310087Sjkh unsigned char ttl; 57410087Sjkh 57510087Sjkh mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP); 57610087Sjkh mreq.imr_interface.s_addr = htonl(INADDR_ANY); 57710087Sjkh if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, 57810087Sjkh &mreq, sizeof(mreq)) < 0) { 57910087Sjkh syslog(LOG_ERR, 58010087Sjkh "setsockopt IP_ADD_MEMBERSHIP: %m"); 58110087Sjkh return(0); 58210087Sjkh } 58310087Sjkh ttl = multicast_scope; 58410087Sjkh if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, 58510087Sjkh &ttl, sizeof(ttl)) < 0) { 58610087Sjkh syslog(LOG_ERR, 58710087Sjkh "setsockopt IP_MULTICAST_TTL: %m"); 58810087Sjkh return(0); 58910087Sjkh } 59010087Sjkh return(1); 59110087Sjkh } 59210087Sjkh 5931553Srgrimes mib[0] = CTL_NET; 5941553Srgrimes mib[1] = PF_ROUTE; 5951553Srgrimes mib[2] = 0; 5961553Srgrimes mib[3] = AF_INET; 5971553Srgrimes mib[4] = NET_RT_IFLIST; 5981553Srgrimes mib[5] = 0; 5991553Srgrimes if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 6001553Srgrimes quit("route-sysctl-estimate"); 6011553Srgrimes if ((buf = malloc(needed)) == NULL) 6021553Srgrimes quit("malloc"); 6031553Srgrimes if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) 6041553Srgrimes quit("actual retrieval of interface table"); 6051553Srgrimes lim = buf + needed; 6061553Srgrimes 6071553Srgrimes sdl = NULL; /* XXX just to keep gcc -Wall happy */ 6081553Srgrimes for (next = buf; next < lim; next += ifm->ifm_msglen) { 6091553Srgrimes ifm = (struct if_msghdr *)next; 6101553Srgrimes if (ifm->ifm_type == RTM_IFINFO) { 6111553Srgrimes sdl = (struct sockaddr_dl *)(ifm + 1); 6121553Srgrimes flags = ifm->ifm_flags; 6131553Srgrimes continue; 6141553Srgrimes } 6151553Srgrimes if ((flags & IFF_UP) == 0 || 61610087Sjkh (flags & (((multicast_mode == PER_INTERFACE_MULTICAST) ? 61710087Sjkh IFF_MULTICAST : 0) | 61847963Sbrian IFF_BROADCAST|iff_flag)) == 0) 6191553Srgrimes continue; 6201553Srgrimes if (ifm->ifm_type != RTM_NEWADDR) 6211553Srgrimes quit("out of sync parsing NET_RT_IFLIST"); 6221553Srgrimes ifam = (struct ifa_msghdr *)ifm; 6231553Srgrimes info.rti_addrs = ifam->ifam_addrs; 6241553Srgrimes rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam, 6251553Srgrimes &info); 6261553Srgrimes /* gag, wish we could get rid of Internet dependencies */ 6271553Srgrimes#define dstaddr info.rti_info[RTAX_BRD] 62810087Sjkh#define ifaddr info.rti_info[RTAX_IFA] 6291553Srgrimes#define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr 6301553Srgrimes#define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port 6311553Srgrimes if (dstaddr == 0 || dstaddr->sa_family != AF_INET) 6321553Srgrimes continue; 6331553Srgrimes PORT_SA(dstaddr) = sp->s_port; 6341553Srgrimes for (np = neighbors; np != NULL; np = np->n_next) 6351553Srgrimes if (memcmp(sdl->sdl_data, np->n_name, 6361553Srgrimes sdl->sdl_nlen) == 0 && 6371553Srgrimes IPADDR_SA(np->n_addr) == IPADDR_SA(dstaddr)) 6381553Srgrimes break; 6391553Srgrimes if (np != NULL) 6401553Srgrimes continue; 6411553Srgrimes len = sizeof(*np) + dstaddr->sa_len + sdl->sdl_nlen + 1; 6421553Srgrimes np = (struct neighbor *)malloc(len); 6431553Srgrimes if (np == NULL) 6441553Srgrimes quit("malloc of neighbor structure"); 6451553Srgrimes memset(np, 0, len); 6461553Srgrimes np->n_flags = flags; 6471553Srgrimes np->n_addr = (struct sockaddr *)(np + 1); 6481553Srgrimes np->n_addrlen = dstaddr->sa_len; 6491553Srgrimes np->n_name = np->n_addrlen + (char *)np->n_addr; 65010087Sjkh memcpy((char *)np->n_addr, (char *)dstaddr, np->n_addrlen); 65110087Sjkh memcpy(np->n_name, sdl->sdl_data, sdl->sdl_nlen); 65210087Sjkh if (multicast_mode == PER_INTERFACE_MULTICAST && 65310087Sjkh (flags & IFF_MULTICAST) && 65410087Sjkh !(flags & IFF_LOOPBACK)) { 65510087Sjkh struct ip_mreq mreq; 65610087Sjkh 65710087Sjkh memcpy((char *)np->n_addr, (char *)ifaddr, 65810087Sjkh np->n_addrlen); 65910087Sjkh mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP); 66010087Sjkh mreq.imr_interface.s_addr = 66110087Sjkh ((struct sockaddr_in *)np->n_addr)->sin_addr.s_addr; 66210087Sjkh if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, 66310087Sjkh &mreq, sizeof(mreq)) < 0) { 66410087Sjkh syslog(LOG_ERR, 66510087Sjkh "setsockopt IP_ADD_MEMBERSHIP: %m"); 66610087Sjkh#if 0 66710087Sjkh /* Fall back to broadcast on this if. */ 66810087Sjkh np->n_flags &= ~IFF_MULTICAST; 66910087Sjkh#else 67010087Sjkh free((char *)np); 67110087Sjkh continue; 67210087Sjkh#endif 67310087Sjkh } 67410087Sjkh } 6751553Srgrimes np->n_next = neighbors; 6761553Srgrimes neighbors = np; 6771553Srgrimes } 6781553Srgrimes free(buf); 6791553Srgrimes return (1); 6801553Srgrimes} 6811553Srgrimes 6821553Srgrimes#ifdef DEBUG 6831553Srgrimesvoid 6841553SrgrimesSendto(s, buf, cc, flags, to, tolen) 6851553Srgrimes int s; 68617829Spst const void *buf; 68717829Spst size_t cc; 68817829Spst int flags; 68917829Spst const struct sockaddr *to; 6901553Srgrimes int tolen; 6911553Srgrimes{ 6921553Srgrimes register struct whod *w = (struct whod *)buf; 6931553Srgrimes register struct whoent *we; 6941553Srgrimes struct sockaddr_in *sin = (struct sockaddr_in *)to; 6951553Srgrimes 69617829Spst printf("sendto %x.%d\n", ntohl(sin->sin_addr.s_addr), 69717829Spst ntohs(sin->sin_port)); 6981553Srgrimes printf("hostname %s %s\n", w->wd_hostname, 6991553Srgrimes interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up")); 7001553Srgrimes printf("load %4.2f, %4.2f, %4.2f\n", 7011553Srgrimes ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0, 7021553Srgrimes ntohl(w->wd_loadav[2]) / 100.0); 7031553Srgrimes cc -= WHDRSIZE; 7041553Srgrimes for (we = w->wd_we, cc /= sizeof(struct whoent); cc > 0; cc--, we++) { 70589572Sdillon time_t t = _time32_to_time(ntohl(we->we_utmp.out_time)); 7061553Srgrimes printf("%-8.8s %s:%s %.12s", 7071553Srgrimes we->we_utmp.out_name, 7081553Srgrimes w->wd_hostname, we->we_utmp.out_line, 7091553Srgrimes ctime(&t)+4); 7101553Srgrimes we->we_idle = ntohl(we->we_idle) / 60; 7111553Srgrimes if (we->we_idle) { 7121553Srgrimes if (we->we_idle >= 100*60) 7131553Srgrimes we->we_idle = 100*60 - 1; 7141553Srgrimes if (we->we_idle >= 60) 7151553Srgrimes printf(" %2d", we->we_idle / 60); 7161553Srgrimes else 7171553Srgrimes printf(" "); 7181553Srgrimes printf(":%02d", we->we_idle % 60); 7191553Srgrimes } 7201553Srgrimes printf("\n"); 7211553Srgrimes } 7221553Srgrimes} 7231553Srgrimes 7241553Srgrimeschar * 7251553Srgrimesinterval(time, updown) 7261553Srgrimes int time; 7271553Srgrimes char *updown; 7281553Srgrimes{ 7291553Srgrimes static char resbuf[32]; 7301553Srgrimes int days, hours, minutes; 7311553Srgrimes 7321553Srgrimes if (time < 0 || time > 3*30*24*60*60) { 7331553Srgrimes (void) sprintf(resbuf, " %s ??:??", updown); 7341553Srgrimes return (resbuf); 7351553Srgrimes } 7361553Srgrimes minutes = (time + 59) / 60; /* round to minutes */ 7371553Srgrimes hours = minutes / 60; minutes %= 60; 7381553Srgrimes days = hours / 24; hours %= 24; 7391553Srgrimes if (days) 7401553Srgrimes (void) sprintf(resbuf, "%s %2d+%02d:%02d", 7411553Srgrimes updown, days, hours, minutes); 7421553Srgrimes else 7431553Srgrimes (void) sprintf(resbuf, "%s %2d:%02d", 7441553Srgrimes updown, hours, minutes); 7451553Srgrimes return (resbuf); 7461553Srgrimes} 7471553Srgrimes#endif 748