1252602Spjd/*- 2252602Spjd * Copyright (c) 1983, 1993 The Regents of the University of California. 3252603Spjd * Copyright (c) 2013 Mariusz Zaborski <oshogbo@FreeBSD.org> 4252602Spjd * All rights reserved. 51553Srgrimes * 61553Srgrimes * Redistribution and use in source and binary forms, with or without 71553Srgrimes * modification, are permitted provided that the following conditions 81553Srgrimes * are met: 91553Srgrimes * 1. Redistributions of source code must retain the above copyright 101553Srgrimes * notice, this list of conditions and the following disclaimer. 111553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 121553Srgrimes * notice, this list of conditions and the following disclaimer in the 131553Srgrimes * documentation and/or other materials provided with the distribution. 141553Srgrimes * 4. Neither the name of the University nor the names of its contributors 151553Srgrimes * may be used to endorse or promote products derived from this software 161553Srgrimes * without specific prior written permission. 171553Srgrimes * 181553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 191553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 201553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 211553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 221553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 231553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 241553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 251553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 261553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 271553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 281553Srgrimes * SUCH DAMAGE. 291553Srgrimes */ 301553Srgrimes 311553Srgrimes#ifndef lint 3230380Scharnierstatic const char copyright[] = 331553Srgrimes"@(#) Copyright (c) 1983, 1993\n\ 341553Srgrimes The Regents of the University of California. All rights reserved.\n"; 351553Srgrimes#endif /* not lint */ 361553Srgrimes 37117278Scharnier#if 0 381553Srgrimes#ifndef lint 391553Srgrimesstatic char sccsid[] = "@(#)rwhod.c 8.1 (Berkeley) 6/6/93"; 40117278Scharnier#endif /* not lint */ 4130380Scharnier#endif 421553Srgrimes 43117278Scharnier#include <sys/cdefs.h> 44117278Scharnier__FBSDID("$FreeBSD$"); 45117278Scharnier 46252605Spjd#include <sys/capability.h> 471553Srgrimes#include <sys/param.h> 481553Srgrimes#include <sys/socket.h> 491553Srgrimes#include <sys/stat.h> 501553Srgrimes#include <sys/signal.h> 511553Srgrimes#include <sys/ioctl.h> 521553Srgrimes#include <sys/sysctl.h> 53252603Spjd#include <sys/procdesc.h> 54252603Spjd#include <sys/wait.h> 551553Srgrimes 561553Srgrimes#include <net/if.h> 571553Srgrimes#include <net/if_dl.h> 581553Srgrimes#include <net/route.h> 591553Srgrimes#include <netinet/in.h> 6070284Siedowse#include <arpa/inet.h> 611553Srgrimes#include <protocols/rwhod.h> 621553Srgrimes 631553Srgrimes#include <ctype.h> 6430380Scharnier#include <err.h> 651553Srgrimes#include <errno.h> 661553Srgrimes#include <fcntl.h> 67252602Spjd#include <grp.h> 681553Srgrimes#include <netdb.h> 691553Srgrimes#include <paths.h> 70252602Spjd#include <pwd.h> 711553Srgrimes#include <stdio.h> 721553Srgrimes#include <stdlib.h> 731553Srgrimes#include <string.h> 741553Srgrimes#include <syslog.h> 7599825Salfred#include <timeconv.h> 76252602Spjd#include <utmpx.h> 771553Srgrimes#include <unistd.h> 781553Srgrimes 7917832Spst#define UNPRIV_USER "daemon" 8017829Spst#define UNPRIV_GROUP "daemon" 8117829Spst 8210087Sjkh#define NO_MULTICAST 0 /* multicast modes */ 8310087Sjkh#define PER_INTERFACE_MULTICAST 1 8410087Sjkh#define SCOPED_MULTICAST 2 8510087Sjkh 8610087Sjkh#define MAX_MULTICAST_SCOPE 32 /* "site-wide", by convention */ 8710087Sjkh 8810087Sjkh#define INADDR_WHOD_GROUP (u_long)0xe0000103 /* 224.0.1.3 */ 89252602Spjd /* (belongs in protocols/rwhod.h) */ 9010087Sjkh 91252602Spjdint insecure_mode; 92252602Spjdint quiet_mode; 93252602Spjdint iff_flag = IFF_POINTOPOINT; 94252602Spjdint multicast_mode = NO_MULTICAST; 95252602Spjdint multicast_scope; 96252602Spjdstruct sockaddr_in multicast_addr = 97252602Spjd { sizeof(multicast_addr), AF_INET, 0, { 0 }, { 0 } }; 9810087Sjkh 9910087Sjkh/* 100252603Spjd * Sleep interval. Don't forget to change the down time check in ruptime 1011553Srgrimes * if this is changed. 1021553Srgrimes */ 103252603Spjd#define SL_INTERVAL (3 * 60) 1041553Srgrimes 1051553Srgrimeschar myname[MAXHOSTNAMELEN]; 1061553Srgrimes 1071553Srgrimes/* 1081553Srgrimes * We communicate with each neighbor in a list constructed at the time we're 1091553Srgrimes * started up. Neighbors are currently directly connected via a hardware 1101553Srgrimes * interface. 1111553Srgrimes */ 1121553Srgrimesstruct neighbor { 1131553Srgrimes struct neighbor *n_next; 114252602Spjd char *n_name; /* interface name */ 1151553Srgrimes struct sockaddr *n_addr; /* who to send to */ 116252602Spjd int n_addrlen; /* size of address */ 117252602Spjd int n_flags; /* should forward?, interface flags */ 1181553Srgrimes}; 1191553Srgrimes 1201553Srgrimesstruct neighbor *neighbors; 1211553Srgrimesstruct whod mywd; 122252602Spjdstruct servent *sp; 123201060Sedint s; 124252603Spjdint fdp; 125252603Spjdpid_t pid_child_receiver; 1261553Srgrimes 12799825Salfred#define WHDRSIZE (int)(sizeof(mywd) - sizeof(mywd.wd_we)) 1281553Srgrimes 129252602Spjdint configure(int so); 130252602Spjdvoid getboottime(int signo __unused); 131252603Spjdvoid receiver_process(void); 132252602Spjdvoid rt_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo *rtinfo); 133252602Spjdvoid run_as(uid_t *uid, gid_t *gid); 134252602Spjdvoid quit(const char *msg); 135252603Spjdvoid sender_process(void); 136252602Spjdint verify(char *name, int maxlen); 13799825Salfredstatic void usage(void); 138252602Spjd 1391553Srgrimes#ifdef DEBUG 140252602Spjdchar *interval(int time, char *updown); 141252602Spjdvoid Sendto(int s, const void *buf, size_t cc, int flags, 142252602Spjd const struct sockaddr *to, int tolen); 1431553Srgrimes#define sendto Sendto 1441553Srgrimes#endif 1451553Srgrimes 146252602Spjd/* 147252602Spjd * This version of Berkeley's rwhod has been modified to use IP multicast 148252602Spjd * datagrams, under control of a new command-line option: 149252602Spjd * 150252602Spjd * rwhod -m causes rwhod to use IP multicast (instead of 151252602Spjd * broadcast or unicast) on all interfaces that have 152252602Spjd * the IFF_MULTICAST flag set in their "ifnet" structs 153252602Spjd * (excluding the loopback interface). The multicast 154252602Spjd * reports are sent with a time-to-live of 1, to prevent 155252602Spjd * forwarding beyond the directly-connected subnet(s). 156252602Spjd * 157252602Spjd * rwhod -m <ttl> causes rwhod to send IP multicast datagrams with a 158252602Spjd * time-to-live of <ttl>, via a SINGLE interface rather 159252602Spjd * than all interfaces. <ttl> must be between 0 and 160252602Spjd * MAX_MULTICAST_SCOPE, defined below. Note that "-m 1" 161252602Spjd * is different than "-m", in that "-m 1" specifies 162252602Spjd * transmission on one interface only. 163252602Spjd * 164252602Spjd * When "-m" is used without a <ttl> argument, the program accepts multicast 165252602Spjd * rwhod reports from all multicast-capable interfaces. If a <ttl> argument 166252602Spjd * is given, it accepts multicast reports from only one interface, the one 167252602Spjd * on which reports are sent (which may be controlled via the host's routing 168252602Spjd * table). Regardless of the "-m" option, the program accepts broadcast or 169252602Spjd * unicast reports from all interfaces. Thus, this program will hear the 170252602Spjd * reports of old, non-multicasting rwhods, but, if multicasting is used, 171252602Spjd * those old rwhods won't hear the reports generated by this program. 172252602Spjd * 173252602Spjd * -- Steve Deering, Stanford University, February 1989 174252602Spjd */ 1751553Srgrimesint 17699825Salfredmain(int argc, char *argv[]) 1771553Srgrimes{ 178252602Spjd int on; 1791553Srgrimes char *cp; 18099825Salfred struct sockaddr_in soin; 18117829Spst uid_t unpriv_uid; 18217829Spst gid_t unpriv_gid; 1831553Srgrimes 184252602Spjd on = 1; 18530380Scharnier if (getuid()) 18630380Scharnier errx(1, "not super user"); 18717829Spst 18817829Spst run_as(&unpriv_uid, &unpriv_gid); 18917829Spst 190252602Spjd argv++; 191252602Spjd argc--; 19210087Sjkh while (argc > 0 && *argv[0] == '-') { 19310087Sjkh if (strcmp(*argv, "-m") == 0) { 19410087Sjkh if (argc > 1 && isdigit(*(argv + 1)[0])) { 195252602Spjd argv++; 196252602Spjd argc--; 19710087Sjkh multicast_mode = SCOPED_MULTICAST; 19810087Sjkh multicast_scope = atoi(*argv); 199252602Spjd if (multicast_scope > MAX_MULTICAST_SCOPE) { 20030380Scharnier errx(1, "ttl must not exceed %u", 201252602Spjd MAX_MULTICAST_SCOPE); 202252602Spjd } 203252602Spjd } else { 204252602Spjd multicast_mode = PER_INTERFACE_MULTICAST; 20510087Sjkh } 206252602Spjd } else if (strcmp(*argv, "-i") == 0) { 20742508Ssteve insecure_mode = 1; 208252602Spjd } else if (strcmp(*argv, "-l") == 0) { 20942508Ssteve quiet_mode = 1; 210252602Spjd } else if (strcmp(*argv, "-p") == 0) { 21147963Sbrian iff_flag = 0; 212252602Spjd } else { 21330380Scharnier usage(); 214252602Spjd } 215252602Spjd argv++; 216252602Spjd argc--; 21710087Sjkh } 21830380Scharnier if (argc > 0) 21930380Scharnier usage(); 2201553Srgrimes#ifndef DEBUG 2211553Srgrimes daemon(1, 0); 2221553Srgrimes#endif 22310087Sjkh (void) signal(SIGHUP, getboottime); 224252605Spjd openlog("rwhod", LOG_PID | LOG_NDELAY, LOG_DAEMON); 22510087Sjkh sp = getservbyname("who", "udp"); 22610087Sjkh if (sp == NULL) { 227117278Scharnier syslog(LOG_ERR, "who/udp: unknown service"); 22810087Sjkh exit(1); 22910087Sjkh } 2301553Srgrimes if (chdir(_PATH_RWHODIR) < 0) { 23153770Scharnier syslog(LOG_ERR, "%s: %m", _PATH_RWHODIR); 2321553Srgrimes exit(1); 2331553Srgrimes } 2341553Srgrimes /* 2351553Srgrimes * Establish host name as returned by system. 2361553Srgrimes */ 2371553Srgrimes if (gethostname(myname, sizeof(myname) - 1) < 0) { 2381553Srgrimes syslog(LOG_ERR, "gethostname: %m"); 2391553Srgrimes exit(1); 2401553Srgrimes } 241229403Sed if ((cp = strchr(myname, '.')) != NULL) 2421553Srgrimes *cp = '\0'; 243252602Spjd strlcpy(mywd.wd_hostname, myname, sizeof(mywd.wd_hostname)); 2441553Srgrimes getboottime(0); 2451553Srgrimes if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 2461553Srgrimes syslog(LOG_ERR, "socket: %m"); 2471553Srgrimes exit(1); 2481553Srgrimes } 2491553Srgrimes if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { 2501553Srgrimes syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m"); 2511553Srgrimes exit(1); 2521553Srgrimes } 25399825Salfred memset(&soin, 0, sizeof(soin)); 25499825Salfred soin.sin_len = sizeof(soin); 25599825Salfred soin.sin_family = AF_INET; 25699825Salfred soin.sin_port = sp->s_port; 25799825Salfred if (bind(s, (struct sockaddr *)&soin, sizeof(soin)) < 0) { 2581553Srgrimes syslog(LOG_ERR, "bind: %m"); 2591553Srgrimes exit(1); 2601553Srgrimes } 261220969Ssimon if (setgid(unpriv_gid) != 0) { 262220969Ssimon syslog(LOG_ERR, "setgid: %m"); 263220969Ssimon exit(1); 264220969Ssimon } 265252602Spjd if (setgroups(1, &unpriv_gid) != 0) { /* XXX BOGUS groups[0] = egid */ 266220969Ssimon syslog(LOG_ERR, "setgroups: %m"); 267220969Ssimon exit(1); 268220969Ssimon } 269220969Ssimon if (setuid(unpriv_uid) != 0) { 270220969Ssimon syslog(LOG_ERR, "setuid: %m"); 271220969Ssimon exit(1); 272220969Ssimon } 2731553Srgrimes if (!configure(s)) 2741553Srgrimes exit(1); 27542508Ssteve if (!quiet_mode) { 276252603Spjd pid_child_receiver = pdfork(&fdp, 0); 277252603Spjd if (pid_child_receiver == 0) { 278252603Spjd receiver_process(); 279252603Spjd } else if (pid_child_receiver > 0) { 280252603Spjd sender_process(); 281252603Spjd } else if (pid_child_receiver == -1) { 282255227Spjd if (errno == ENOSYS) { 283255227Spjd syslog(LOG_ERR, 284255227Spjd "The pdfork(2) system call is not available; recompile the kernel with options PROCDESC"); 285255227Spjd } else { 286255227Spjd syslog(LOG_ERR, "pdfork: %m"); 287255227Spjd } 288252603Spjd exit(1); 289252603Spjd } 290252603Spjd } else { 291252603Spjd receiver_process(); 29242508Ssteve } 293252603Spjd} 294252603Spjd 295252603Spjdstatic void 296252603Spjdusage(void) 297252603Spjd{ 298252603Spjd 299252603Spjd fprintf(stderr, "usage: rwhod [-i] [-p] [-l] [-m [ttl]]\n"); 300252603Spjd exit(1); 301252603Spjd} 302252603Spjd 303252603Spjdvoid 304252603Spjdrun_as(uid_t *uid, gid_t *gid) 305252603Spjd{ 306252603Spjd struct passwd *pw; 307252603Spjd struct group *gr; 308252603Spjd 309252603Spjd pw = getpwnam(UNPRIV_USER); 310252603Spjd if (pw == NULL) { 311252603Spjd syslog(LOG_ERR, "getpwnam(%s): %m", UNPRIV_USER); 312252603Spjd exit(1); 313252603Spjd } 314252603Spjd *uid = pw->pw_uid; 315252603Spjd 316252603Spjd gr = getgrnam(UNPRIV_GROUP); 317252603Spjd if (gr == NULL) { 318252603Spjd syslog(LOG_ERR, "getgrnam(%s): %m", UNPRIV_GROUP); 319252603Spjd exit(1); 320252603Spjd } 321252603Spjd *gid = gr->gr_gid; 322252603Spjd} 323252603Spjd 324252603Spjd/* 325252603Spjd * Check out host name for unprintables 326252603Spjd * and other funnies before allowing a file 327252603Spjd * to be created. Sorry, but blanks aren't allowed. 328252603Spjd */ 329252603Spjdint 330252603Spjdverify(char *name, int maxlen) 331252603Spjd{ 332252603Spjd int size; 333252603Spjd 334252603Spjd size = 0; 335252603Spjd while (*name != '\0' && size < maxlen - 1) { 336254486Spjd if (!isascii((unsigned char)*name) || 337254486Spjd !(isalnum((unsigned char)*name) || 338254486Spjd ispunct((unsigned char)*name))) { 339252603Spjd return (0); 340254486Spjd } 341252603Spjd name++; 342252603Spjd size++; 343252603Spjd } 344252603Spjd *name = '\0'; 345252603Spjd return (size > 0); 346252603Spjd} 347252603Spjd 348252603Spjdvoid 349252603Spjdreceiver_process(void) 350252603Spjd{ 351252603Spjd struct sockaddr_in from; 352252603Spjd struct stat st; 353255219Spjd cap_rights_t rights; 354252603Spjd char path[64]; 355252605Spjd int dirfd; 356252603Spjd struct whod wd; 357252603Spjd socklen_t len; 358252603Spjd int cc, whod; 359252603Spjd time_t t; 360252603Spjd 361252603Spjd len = sizeof(from); 362252605Spjd dirfd = open(".", O_RDONLY | O_DIRECTORY); 363252605Spjd if (dirfd < 0) { 364252605Spjd syslog(LOG_WARNING, "%s: %m", _PATH_RWHODIR); 365252605Spjd exit(1); 366252605Spjd } 367255219Spjd cap_rights_init(&rights, CAP_CREATE, CAP_FSTAT, CAP_FTRUNCATE, 368255219Spjd CAP_LOOKUP, CAP_SEEK, CAP_WRITE); 369255219Spjd if (cap_rights_limit(dirfd, &rights) < 0 && errno != ENOSYS) { 370252605Spjd syslog(LOG_WARNING, "cap_rights_limit: %m"); 371252605Spjd exit(1); 372252605Spjd } 373252605Spjd if (cap_enter() < 0 && errno != ENOSYS) { 374252605Spjd syslog(LOG_ERR, "cap_enter: %m"); 375252605Spjd exit(1); 376252605Spjd } 3771553Srgrimes for (;;) { 378252602Spjd cc = recvfrom(s, &wd, sizeof(wd), 0, (struct sockaddr *)&from, 379252602Spjd &len); 3801553Srgrimes if (cc <= 0) { 3811553Srgrimes if (cc < 0 && errno != EINTR) 3821553Srgrimes syslog(LOG_WARNING, "recv: %m"); 3831553Srgrimes continue; 3841553Srgrimes } 38541895Sdes if (from.sin_port != sp->s_port && !insecure_mode) { 38670284Siedowse syslog(LOG_WARNING, "%d: bad source port from %s", 38770284Siedowse ntohs(from.sin_port), inet_ntoa(from.sin_addr)); 3881553Srgrimes continue; 3891553Srgrimes } 39070284Siedowse if (cc < WHDRSIZE) { 39170284Siedowse syslog(LOG_WARNING, "short packet from %s", 39270284Siedowse inet_ntoa(from.sin_addr)); 39370284Siedowse continue; 39470284Siedowse } 3951553Srgrimes if (wd.wd_vers != WHODVERSION) 3961553Srgrimes continue; 3971553Srgrimes if (wd.wd_type != WHODTYPE_STATUS) 3981553Srgrimes continue; 399252602Spjd if (!verify(wd.wd_hostname, sizeof(wd.wd_hostname))) { 40070284Siedowse syslog(LOG_WARNING, "malformed host name from %s", 40170284Siedowse inet_ntoa(from.sin_addr)); 4021553Srgrimes continue; 4031553Srgrimes } 404252602Spjd (void) snprintf(path, sizeof(path), "whod.%s", wd.wd_hostname); 4051553Srgrimes /* 4061553Srgrimes * Rather than truncating and growing the file each time, 4071553Srgrimes * use ftruncate if size is less than previous size. 4081553Srgrimes */ 409252605Spjd whod = openat(dirfd, path, O_WRONLY | O_CREAT, 0644); 4101553Srgrimes if (whod < 0) { 4111553Srgrimes syslog(LOG_WARNING, "%s: %m", path); 4121553Srgrimes continue; 4131553Srgrimes } 414255219Spjd cap_rights_init(&rights, CAP_FSTAT, CAP_FTRUNCATE, CAP_WRITE); 415255219Spjd if (cap_rights_limit(whod, &rights) < 0 && errno != ENOSYS) { 416252605Spjd syslog(LOG_WARNING, "cap_rights_limit: %m"); 417252605Spjd exit(1); 418252605Spjd } 4191553Srgrimes#if ENDIAN != BIG_ENDIAN 4201553Srgrimes { 4211553Srgrimes struct whoent *we; 422252602Spjd int i, n; 4231553Srgrimes 424252602Spjd n = (cc - WHDRSIZE) / sizeof(struct whoent); 4251553Srgrimes /* undo header byte swapping before writing to file */ 4261553Srgrimes wd.wd_sendtime = ntohl(wd.wd_sendtime); 4271553Srgrimes for (i = 0; i < 3; i++) 4281553Srgrimes wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]); 4291553Srgrimes wd.wd_boottime = ntohl(wd.wd_boottime); 4301553Srgrimes we = wd.wd_we; 4311553Srgrimes for (i = 0; i < n; i++) { 4321553Srgrimes we->we_idle = ntohl(we->we_idle); 4331553Srgrimes we->we_utmp.out_time = 4341553Srgrimes ntohl(we->we_utmp.out_time); 4351553Srgrimes we++; 4361553Srgrimes } 4371553Srgrimes } 4381553Srgrimes#endif 43985640Sdillon (void) time(&t); 44089572Sdillon wd.wd_recvtime = _time_to_int(t); 4411553Srgrimes (void) write(whod, (char *)&wd, cc); 4421553Srgrimes if (fstat(whod, &st) < 0 || st.st_size > cc) 4431553Srgrimes ftruncate(whod, cc); 4441553Srgrimes (void) close(whod); 4451553Srgrimes } 446252605Spjd (void) close(dirfd); 4471553Srgrimes} 4481553Srgrimes 44917829Spstvoid 450252603Spjdsender_process(void) 45117829Spst{ 452252603Spjd int sendcount; 4531553Srgrimes double avenrun[3]; 4541553Srgrimes time_t now; 455252603Spjd int i, cc, status; 456252602Spjd struct utmpx *ut; 457252602Spjd struct stat stb; 458252602Spjd struct neighbor *np; 459252602Spjd struct whoent *we, *wend; 4601553Srgrimes 461252603Spjd sendcount = 0; 462252603Spjd for (;;) { 463252603Spjd we = mywd.wd_we; 464252603Spjd now = time(NULL); 465252603Spjd if (sendcount % 10 == 0) 466252603Spjd getboottime(0); 467252603Spjd sendcount++; 468252603Spjd wend = &mywd.wd_we[1024 / sizeof(struct whoent)]; 469252603Spjd setutxent(); 470252603Spjd while ((ut = getutxent()) != NULL && we < wend) { 471252603Spjd if (ut->ut_type != USER_PROCESS) 472252603Spjd continue; 473252603Spjd strncpy(we->we_utmp.out_line, ut->ut_line, 474252603Spjd sizeof(we->we_utmp.out_line)); 475252603Spjd strncpy(we->we_utmp.out_name, ut->ut_user, 476252603Spjd sizeof(we->we_utmp.out_name)); 477252603Spjd we->we_utmp.out_time = 478252603Spjd htonl(_time_to_time32(ut->ut_tv.tv_sec)); 479252603Spjd we++; 480252603Spjd } 481252603Spjd endutxent(); 4821553Srgrimes 483252603Spjd if (chdir(_PATH_DEV) < 0) { 484252603Spjd syslog(LOG_ERR, "chdir(%s): %m", _PATH_DEV); 485252603Spjd exit(1); 486252603Spjd } 487252603Spjd wend = we; 488252603Spjd for (we = mywd.wd_we; we < wend; we++) { 489252603Spjd if (stat(we->we_utmp.out_line, &stb) >= 0) 490252603Spjd we->we_idle = htonl(now - stb.st_atime); 491252603Spjd we++; 492252603Spjd } 493252603Spjd (void) getloadavg(avenrun, 494252603Spjd sizeof(avenrun) / sizeof(avenrun[0])); 495252603Spjd for (i = 0; i < 3; i++) 496252603Spjd mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100)); 497252603Spjd cc = (char *)wend - (char *)&mywd; 498252603Spjd mywd.wd_sendtime = htonl(_time_to_time32(time(NULL))); 499252603Spjd mywd.wd_vers = WHODVERSION; 500252603Spjd mywd.wd_type = WHODTYPE_STATUS; 501252603Spjd if (multicast_mode == SCOPED_MULTICAST) { 502252603Spjd (void) sendto(s, (char *)&mywd, cc, 0, 503252603Spjd (struct sockaddr *)&multicast_addr, 504252603Spjd sizeof(multicast_addr)); 505252603Spjd } else { 506252603Spjd for (np = neighbors; np != NULL; np = np->n_next) { 507252603Spjd if (multicast_mode == PER_INTERFACE_MULTICAST && 508252603Spjd (np->n_flags & IFF_MULTICAST) != 0) { 509252603Spjd /* 510252603Spjd * Select the outgoing interface for the 511252603Spjd * multicast. 512252603Spjd */ 513252603Spjd if (setsockopt(s, IPPROTO_IP, 514252603Spjd IP_MULTICAST_IF, 515252603Spjd &(((struct sockaddr_in *)np->n_addr)->sin_addr), 516252603Spjd sizeof(struct in_addr)) < 0) { 517252603Spjd syslog(LOG_ERR, 518252603Spjd "setsockopt IP_MULTICAST_IF: %m"); 519252603Spjd exit(1); 520252603Spjd } 521252603Spjd (void) sendto(s, (char *)&mywd, cc, 0, 522252603Spjd (struct sockaddr *)&multicast_addr, 523252603Spjd sizeof(multicast_addr)); 524252603Spjd } else { 525252603Spjd (void) sendto(s, (char *)&mywd, cc, 0, 526252603Spjd np->n_addr, np->n_addrlen); 527252602Spjd } 52810087Sjkh } 529252602Spjd } 530252603Spjd if (chdir(_PATH_RWHODIR) < 0) { 531252603Spjd syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR); 532252603Spjd exit(1); 533252603Spjd } 534252603Spjd if (waitpid(pid_child_receiver, &status, WNOHANG) == 535252603Spjd pid_child_receiver) { 536252603Spjd break; 537252603Spjd } 538252603Spjd sleep(SL_INTERVAL); 53910087Sjkh } 5401553Srgrimes} 5411553Srgrimes 5421553Srgrimesvoid 543252602Spjdgetboottime(int signo __unused) 5441553Srgrimes{ 5451553Srgrimes int mib[2]; 5461553Srgrimes size_t size; 5471553Srgrimes struct timeval tm; 5481553Srgrimes 5491553Srgrimes mib[0] = CTL_KERN; 5501553Srgrimes mib[1] = KERN_BOOTTIME; 5511553Srgrimes size = sizeof(tm); 5521553Srgrimes if (sysctl(mib, 2, &tm, &size, NULL, 0) == -1) { 5531553Srgrimes syslog(LOG_ERR, "cannot get boottime: %m"); 5541553Srgrimes exit(1); 5551553Srgrimes } 55689572Sdillon mywd.wd_boottime = htonl(_time_to_time32(tm.tv_sec)); 5571553Srgrimes} 5581553Srgrimes 5591553Srgrimesvoid 560252602Spjdquit(const char *msg) 5611553Srgrimes{ 562252602Spjd 56362989Skris syslog(LOG_ERR, "%s", msg); 5641553Srgrimes exit(1); 5651553Srgrimes} 5661553Srgrimes 5671553Srgrimesvoid 568252602Spjdrt_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo *rtinfo) 5691553Srgrimes{ 570252602Spjd struct sockaddr *sa; 571252602Spjd int i; 5721553Srgrimes 5731553Srgrimes memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info)); 574252602Spjd for (i = 0; i < RTAX_MAX && cp < cplim; i++) { 5751553Srgrimes if ((rtinfo->rti_addrs & (1 << i)) == 0) 5761553Srgrimes continue; 577252602Spjd sa = (struct sockaddr *)cp; 578252602Spjd rtinfo->rti_info[i] = sa; 579128186Sluigi cp += SA_SIZE(sa); 5801553Srgrimes } 5811553Srgrimes} 5821553Srgrimes 5831553Srgrimes/* 5841553Srgrimes * Figure out device configuration and select 5851553Srgrimes * networks which deserve status information. 5861553Srgrimes */ 5871553Srgrimesint 588252602Spjdconfigure(int so) 5891553Srgrimes{ 590252602Spjd struct neighbor *np; 591252602Spjd struct if_msghdr *ifm; 592252602Spjd struct ifa_msghdr *ifam; 5931553Srgrimes struct sockaddr_dl *sdl; 5941553Srgrimes size_t needed; 595252602Spjd int mib[6], flags, lflags, len; 5961553Srgrimes char *buf, *lim, *next; 5971553Srgrimes struct rt_addrinfo info; 5981553Srgrimes 599252602Spjd flags = 0; 60010087Sjkh if (multicast_mode != NO_MULTICAST) { 60110087Sjkh multicast_addr.sin_addr.s_addr = htonl(INADDR_WHOD_GROUP); 60210087Sjkh multicast_addr.sin_port = sp->s_port; 60310087Sjkh } 60410087Sjkh 60510087Sjkh if (multicast_mode == SCOPED_MULTICAST) { 60610087Sjkh struct ip_mreq mreq; 60710087Sjkh unsigned char ttl; 60810087Sjkh 60910087Sjkh mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP); 61010087Sjkh mreq.imr_interface.s_addr = htonl(INADDR_ANY); 61199825Salfred if (setsockopt(so, IPPROTO_IP, IP_ADD_MEMBERSHIP, 612252602Spjd &mreq, sizeof(mreq)) < 0) { 61310087Sjkh syslog(LOG_ERR, 614252602Spjd "setsockopt IP_ADD_MEMBERSHIP: %m"); 615252602Spjd return (0); 61610087Sjkh } 61710087Sjkh ttl = multicast_scope; 618252602Spjd if (setsockopt(so, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, 619252602Spjd sizeof(ttl)) < 0) { 62010087Sjkh syslog(LOG_ERR, 621252602Spjd "setsockopt IP_MULTICAST_TTL: %m"); 622252602Spjd return (0); 62310087Sjkh } 624252602Spjd return (1); 62510087Sjkh } 62610087Sjkh 6271553Srgrimes mib[0] = CTL_NET; 6281553Srgrimes mib[1] = PF_ROUTE; 6291553Srgrimes mib[2] = 0; 6301553Srgrimes mib[3] = AF_INET; 6311553Srgrimes mib[4] = NET_RT_IFLIST; 6321553Srgrimes mib[5] = 0; 6331553Srgrimes if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 6341553Srgrimes quit("route-sysctl-estimate"); 6351553Srgrimes if ((buf = malloc(needed)) == NULL) 6361553Srgrimes quit("malloc"); 6371553Srgrimes if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) 6381553Srgrimes quit("actual retrieval of interface table"); 6391553Srgrimes lim = buf + needed; 6401553Srgrimes 6411553Srgrimes sdl = NULL; /* XXX just to keep gcc -Wall happy */ 6421553Srgrimes for (next = buf; next < lim; next += ifm->ifm_msglen) { 6431553Srgrimes ifm = (struct if_msghdr *)next; 6441553Srgrimes if (ifm->ifm_type == RTM_IFINFO) { 6451553Srgrimes sdl = (struct sockaddr_dl *)(ifm + 1); 6461553Srgrimes flags = ifm->ifm_flags; 6471553Srgrimes continue; 6481553Srgrimes } 649252602Spjd if ((flags & IFF_UP) == 0) 6501553Srgrimes continue; 651252602Spjd lflags = IFF_BROADCAST | iff_flag; 652252602Spjd if (multicast_mode == PER_INTERFACE_MULTICAST) 653252602Spjd lflags |= IFF_MULTICAST; 654252602Spjd if ((flags & lflags) == 0) 655252602Spjd continue; 6561553Srgrimes if (ifm->ifm_type != RTM_NEWADDR) 6571553Srgrimes quit("out of sync parsing NET_RT_IFLIST"); 6581553Srgrimes ifam = (struct ifa_msghdr *)ifm; 6591553Srgrimes info.rti_addrs = ifam->ifam_addrs; 6601553Srgrimes rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam, 661252602Spjd &info); 6621553Srgrimes /* gag, wish we could get rid of Internet dependencies */ 663252602Spjd#define dstaddr info.rti_info[RTAX_BRD] 664252602Spjd#define ifaddr info.rti_info[RTAX_IFA] 665252602Spjd#define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr 666252602Spjd#define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port 6671553Srgrimes if (dstaddr == 0 || dstaddr->sa_family != AF_INET) 6681553Srgrimes continue; 6691553Srgrimes PORT_SA(dstaddr) = sp->s_port; 670252602Spjd for (np = neighbors; np != NULL; np = np->n_next) { 6711553Srgrimes if (memcmp(sdl->sdl_data, np->n_name, 672252602Spjd sdl->sdl_nlen) == 0 && 673252602Spjd IPADDR_SA(np->n_addr) == IPADDR_SA(dstaddr)) { 6741553Srgrimes break; 675252602Spjd } 676252602Spjd } 6771553Srgrimes if (np != NULL) 6781553Srgrimes continue; 6791553Srgrimes len = sizeof(*np) + dstaddr->sa_len + sdl->sdl_nlen + 1; 680252602Spjd np = malloc(len); 6811553Srgrimes if (np == NULL) 6821553Srgrimes quit("malloc of neighbor structure"); 6831553Srgrimes memset(np, 0, len); 6841553Srgrimes np->n_flags = flags; 6851553Srgrimes np->n_addr = (struct sockaddr *)(np + 1); 6861553Srgrimes np->n_addrlen = dstaddr->sa_len; 6871553Srgrimes np->n_name = np->n_addrlen + (char *)np->n_addr; 68810087Sjkh memcpy((char *)np->n_addr, (char *)dstaddr, np->n_addrlen); 68910087Sjkh memcpy(np->n_name, sdl->sdl_data, sdl->sdl_nlen); 69010087Sjkh if (multicast_mode == PER_INTERFACE_MULTICAST && 691252602Spjd (flags & IFF_MULTICAST) != 0 && 692252602Spjd (flags & IFF_LOOPBACK) == 0) { 69310087Sjkh struct ip_mreq mreq; 69410087Sjkh 69510087Sjkh memcpy((char *)np->n_addr, (char *)ifaddr, 696252602Spjd np->n_addrlen); 69710087Sjkh mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP); 69810087Sjkh mreq.imr_interface.s_addr = 699252602Spjd ((struct sockaddr_in *)np->n_addr)->sin_addr.s_addr; 70010087Sjkh if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, 701252602Spjd &mreq, sizeof(mreq)) < 0) { 70210087Sjkh syslog(LOG_ERR, 70310087Sjkh "setsockopt IP_ADD_MEMBERSHIP: %m"); 70410087Sjkh#if 0 70510087Sjkh /* Fall back to broadcast on this if. */ 70610087Sjkh np->n_flags &= ~IFF_MULTICAST; 70710087Sjkh#else 708252602Spjd free(np); 70910087Sjkh continue; 71010087Sjkh#endif 71110087Sjkh } 71210087Sjkh } 7131553Srgrimes np->n_next = neighbors; 7141553Srgrimes neighbors = np; 7151553Srgrimes } 7161553Srgrimes free(buf); 7171553Srgrimes return (1); 7181553Srgrimes} 7191553Srgrimes 7201553Srgrimes#ifdef DEBUG 7211553Srgrimesvoid 722252602SpjdSendto(int s, const void *buf, size_t cc, int flags, const struct sockaddr *to, 723252602Spjd int tolen) 7241553Srgrimes{ 725252602Spjd struct whod *w; 726252602Spjd struct whoent *we; 727252602Spjd struct sockaddr_in *sin; 7281553Srgrimes 729252602Spjd w = (struct whod *)buf; 730252602Spjd sin = (struct sockaddr_in *)to; 73117829Spst printf("sendto %x.%d\n", ntohl(sin->sin_addr.s_addr), 732252602Spjd ntohs(sin->sin_port)); 7331553Srgrimes printf("hostname %s %s\n", w->wd_hostname, 734252602Spjd interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up")); 7351553Srgrimes printf("load %4.2f, %4.2f, %4.2f\n", 7361553Srgrimes ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0, 7371553Srgrimes ntohl(w->wd_loadav[2]) / 100.0); 7381553Srgrimes cc -= WHDRSIZE; 7391553Srgrimes for (we = w->wd_we, cc /= sizeof(struct whoent); cc > 0; cc--, we++) { 74089572Sdillon time_t t = _time32_to_time(ntohl(we->we_utmp.out_time)); 741252602Spjd 742252602Spjd printf("%-8.8s %s:%s %.12s", we->we_utmp.out_name, 743252602Spjd w->wd_hostname, we->we_utmp.out_line, ctime(&t) + 4); 7441553Srgrimes we->we_idle = ntohl(we->we_idle) / 60; 745252602Spjd if (we->we_idle != 0) { 746252602Spjd if (we->we_idle >= 100 * 60) 747252602Spjd we->we_idle = 100 * 60 - 1; 7481553Srgrimes if (we->we_idle >= 60) 7491553Srgrimes printf(" %2d", we->we_idle / 60); 7501553Srgrimes else 7511553Srgrimes printf(" "); 7521553Srgrimes printf(":%02d", we->we_idle % 60); 7531553Srgrimes } 7541553Srgrimes printf("\n"); 7551553Srgrimes } 7561553Srgrimes} 7571553Srgrimes 7581553Srgrimeschar * 759252602Spjdinterval(int time, char *updown) 7601553Srgrimes{ 7611553Srgrimes static char resbuf[32]; 7621553Srgrimes int days, hours, minutes; 7631553Srgrimes 764252602Spjd if (time < 0 || time > 3 * 30 * 24 * 60 * 60) { 7651553Srgrimes (void) sprintf(resbuf, " %s ??:??", updown); 7661553Srgrimes return (resbuf); 7671553Srgrimes } 7681553Srgrimes minutes = (time + 59) / 60; /* round to minutes */ 769252602Spjd hours = minutes / 60; 770252602Spjd minutes %= 60; 771252602Spjd days = hours / 24; 772252602Spjd hours %= 24; 773252602Spjd if (days > 0) { 7741553Srgrimes (void) sprintf(resbuf, "%s %2d+%02d:%02d", 7751553Srgrimes updown, days, hours, minutes); 776252602Spjd } else { 7771553Srgrimes (void) sprintf(resbuf, "%s %2d:%02d", 7781553Srgrimes updown, hours, minutes); 779252602Spjd } 7801553Srgrimes return (resbuf); 7811553Srgrimes} 7821553Srgrimes#endif 783