1*04414a37Stsarna /* $NetBSD: rwhod.c,v 1.31 2005/09/12 16:13:13 tsarna Exp $ */ 2f17eeccaSchristos 3e541169cScgd /* 49afcf17bSjtc * Copyright (c) 1983, 1993 59afcf17bSjtc * The Regents of the University of California. All rights reserved. 6e541169cScgd * 7e541169cScgd * Redistribution and use in source and binary forms, with or without 8e541169cScgd * modification, are permitted provided that the following conditions 9e541169cScgd * are met: 10e541169cScgd * 1. Redistributions of source code must retain the above copyright 11e541169cScgd * notice, this list of conditions and the following disclaimer. 12e541169cScgd * 2. Redistributions in binary form must reproduce the above copyright 13e541169cScgd * notice, this list of conditions and the following disclaimer in the 14e541169cScgd * documentation and/or other materials provided with the distribution. 15326b2259Sagc * 3. Neither the name of the University nor the names of its contributors 16e541169cScgd * may be used to endorse or promote products derived from this software 17e541169cScgd * without specific prior written permission. 18e541169cScgd * 19e541169cScgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20e541169cScgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21e541169cScgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22e541169cScgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23e541169cScgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24e541169cScgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25e541169cScgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26e541169cScgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27e541169cScgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28e541169cScgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29e541169cScgd * SUCH DAMAGE. 30e541169cScgd */ 31e541169cScgd 328d79db10Slukem #include <sys/cdefs.h> 33e541169cScgd #ifndef lint 348d79db10Slukem __COPYRIGHT("@(#) Copyright (c) 1983, 1993\n\ 358d79db10Slukem The Regents of the University of California. All rights reserved.\n"); 36e541169cScgd #endif /* not lint */ 37e541169cScgd 38e541169cScgd #ifndef lint 398d79db10Slukem #if 0 408d79db10Slukem static char sccsid[] = "@(#)rwhod.c 8.1 (Berkeley) 6/6/93"; 418d79db10Slukem #else 42*04414a37Stsarna __RCSID("$NetBSD: rwhod.c,v 1.31 2005/09/12 16:13:13 tsarna Exp $"); 438d79db10Slukem #endif 44e541169cScgd #endif /* not lint */ 45e541169cScgd 46e541169cScgd #include <sys/param.h> 47e541169cScgd #include <sys/socket.h> 48e541169cScgd #include <sys/stat.h> 49e541169cScgd #include <sys/signal.h> 50e541169cScgd #include <sys/ioctl.h> 519afcf17bSjtc #include <sys/sysctl.h> 52e541169cScgd 53e541169cScgd #include <net/if.h> 549afcf17bSjtc #include <net/if_dl.h> 559afcf17bSjtc #include <net/route.h> 56e541169cScgd #include <netinet/in.h> 57e541169cScgd #include <protocols/rwhod.h> 588d79db10Slukem #include <arpa/inet.h> 599afcf17bSjtc 609afcf17bSjtc #include <ctype.h> 61e5d6d67cSlukem #include <err.h> 629afcf17bSjtc #include <errno.h> 639afcf17bSjtc #include <fcntl.h> 649afcf17bSjtc #include <netdb.h> 65e541169cScgd #include <paths.h> 66f17eeccaSchristos #include <poll.h> 679afcf17bSjtc #include <stdio.h> 689afcf17bSjtc #include <stdlib.h> 699afcf17bSjtc #include <string.h> 709afcf17bSjtc #include <syslog.h> 719afcf17bSjtc #include <unistd.h> 72bcd46591Sthorpej #include <util.h> 73e541169cScgd 74b166b5b4Schristos #include "utmpentry.h" 756b7013d1Schristos 76c14c7a6bSpeter #define CHECK_INTERVAL (3 * 60) 77e541169cScgd 786b7013d1Schristos /* Time interval limit; ruptime will think that we are down > than this */ 796b7013d1Schristos #define MAX_INTERVAL (11 * 60) 806b7013d1Schristos 816b7013d1Schristos 82f17eeccaSchristos static char myname[MAXHOSTNAMELEN + 1]; 83e541169cScgd 84e541169cScgd /* 859afcf17bSjtc * We communicate with each neighbor in a list constructed at the time we're 869afcf17bSjtc * started up. Neighbors are currently directly connected via a hardware 879afcf17bSjtc * interface. 88e541169cScgd */ 89e541169cScgd struct neighbor { 90e541169cScgd struct neighbor *n_next; 91e541169cScgd char *n_name; /* interface name */ 929afcf17bSjtc struct sockaddr *n_addr; /* who to send to */ 93e541169cScgd int n_addrlen; /* size of address */ 94e541169cScgd int n_flags; /* should forward?, interface flags */ 95e541169cScgd }; 96e541169cScgd 97f17eeccaSchristos static struct neighbor *neighbors; 98f17eeccaSchristos static struct whod mywd; 99f17eeccaSchristos static struct servent *sp; 100f17eeccaSchristos static volatile sig_atomic_t onsighup; 101e541169cScgd 102e541169cScgd #define WHDRSIZE (sizeof(mywd) - sizeof(mywd.wd_we)) 103e541169cScgd 104f17eeccaSchristos static int configure(int); 105f17eeccaSchristos static void getboottime(void); 106f17eeccaSchristos static void send_host_information(int); 107c14c7a6bSpeter static void sighup(int); 108f17eeccaSchristos static void handleread(int); 109f17eeccaSchristos static void quit(const char *); 110f17eeccaSchristos static void rt_xaddrs(void *, void *, struct rt_addrinfo *); 111*04414a37Stsarna static int drop_privs(char *); 112c36c99efSchristos static void usage(void) __attribute__((__noreturn__)); 113f17eeccaSchristos static int verify(const char *); 1149afcf17bSjtc #ifdef DEBUG 115f17eeccaSchristos static char *interval(int, const char *); 116f17eeccaSchristos static ssize_t Sendto(int, const void *, size_t, int, 117f17eeccaSchristos const struct sockaddr *, socklen_t); 118cb1d6897Schristos #else 119cb1d6897Schristos #define Sendto sendto 1209afcf17bSjtc #endif 121e541169cScgd 1229afcf17bSjtc int 123f17eeccaSchristos main(int argc, char *argv[]) 124e541169cScgd { 125c36c99efSchristos int s, ch; 126c36c99efSchristos int time_interval = 180; /* Default time (180 seconds) */ 127c36c99efSchristos char *cp, *ep; 128c14c7a6bSpeter socklen_t on = 1; 129f17eeccaSchristos struct sockaddr_in sasin; 130f17eeccaSchristos struct pollfd pfd[1]; 131c14c7a6bSpeter struct timeval delta, next, now; 132*04414a37Stsarna char *newuser = NULL; 133e541169cScgd 134c36c99efSchristos setprogname(argv[0]); 135c36c99efSchristos 136e5d6d67cSlukem if (getuid()) 137c14c7a6bSpeter errx(EXIT_FAILURE, "not super user"); 138c36c99efSchristos 139*04414a37Stsarna while ((ch = getopt(argc, argv, "i:u:")) != -1) { 140c36c99efSchristos switch (ch) { 1416b7013d1Schristos case 'i': 142c36c99efSchristos time_interval = (int)strtol(optarg, &ep, 10); 143c36c99efSchristos 144c36c99efSchristos switch (*ep) { 145c36c99efSchristos case '\0': 146c36c99efSchristos break; 147c36c99efSchristos case 'm': 148c36c99efSchristos case 'M': 149c36c99efSchristos /* Time in minutes. */ 150c36c99efSchristos time_interval *= 60; 151c36c99efSchristos if (ep[1] == '\0') 152c36c99efSchristos break; 153c36c99efSchristos /*FALLTHROUGH*/ 154c36c99efSchristos default: 155c36c99efSchristos errx(1, "Invalid argument: `%s'", optarg); 156c36c99efSchristos } 157c36c99efSchristos 158c36c99efSchristos if (time_interval <= 0) 1596b7013d1Schristos errx(1, "Interval must be greater than 0"); 160c36c99efSchristos 1616b7013d1Schristos if (time_interval > MAX_INTERVAL) 1626b7013d1Schristos errx(1, "Interval cannot be greater than" 1636b7013d1Schristos " %d minutes", MAX_INTERVAL / 60); 164c36c99efSchristos break; 165*04414a37Stsarna 166*04414a37Stsarna case 'u': 167*04414a37Stsarna newuser = optarg; 168*04414a37Stsarna break; 169*04414a37Stsarna 170c36c99efSchristos default: 171c36c99efSchristos usage(); 172c36c99efSchristos } 173c36c99efSchristos } 174c36c99efSchristos 175e541169cScgd sp = getservbyname("who", "udp"); 176e5d6d67cSlukem if (sp == NULL) 177c14c7a6bSpeter errx(EXIT_FAILURE, "udp/who: unknown service"); 178e541169cScgd #ifndef DEBUG 179c14c7a6bSpeter (void)daemon(1, 0); 180c14c7a6bSpeter (void)pidfile(NULL); 181e541169cScgd #endif 182e5d6d67cSlukem if (chdir(_PATH_RWHODIR) < 0) 183c14c7a6bSpeter err(EXIT_FAILURE, "%s", _PATH_RWHODIR); 184c14c7a6bSpeter (void)signal(SIGHUP, sighup); 185cb1d6897Schristos openlog(getprogname(), LOG_PID, LOG_DAEMON); 186e541169cScgd /* 187e541169cScgd * Establish host name as returned by system. 188e541169cScgd */ 189e541169cScgd if (gethostname(myname, sizeof(myname) - 1) < 0) { 190e541169cScgd syslog(LOG_ERR, "gethostname: %m"); 191c14c7a6bSpeter exit(EXIT_FAILURE); 192e541169cScgd } 19332f51971Smrg myname[sizeof(myname) - 1] = '\0'; 194e5d6d67cSlukem if ((cp = strchr(myname, '.')) != NULL) 195e541169cScgd *cp = '\0'; 196f17eeccaSchristos (void)strncpy(mywd.wd_hostname, myname, sizeof(mywd.wd_hostname) - 1); 197f17eeccaSchristos getboottime(); 198e541169cScgd if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 199e541169cScgd syslog(LOG_ERR, "socket: %m"); 200c14c7a6bSpeter exit(EXIT_FAILURE); 201e541169cScgd } 202e541169cScgd if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { 203e541169cScgd syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m"); 204c14c7a6bSpeter exit(EXIT_FAILURE); 205e541169cScgd } 206f17eeccaSchristos (void)memset(&sasin, 0, sizeof(sasin)); 207f17eeccaSchristos sasin.sin_family = AF_INET; 208f17eeccaSchristos sasin.sin_port = sp->s_port; 209f17eeccaSchristos if (bind(s, (struct sockaddr *)&sasin, sizeof(sasin)) < 0) { 210e541169cScgd syslog(LOG_ERR, "bind: %m"); 211c14c7a6bSpeter exit(EXIT_FAILURE); 212e541169cScgd } 213e541169cScgd if (!configure(s)) 214c14c7a6bSpeter exit(EXIT_FAILURE); 215f17eeccaSchristos 216*04414a37Stsarna if (newuser) 217*04414a37Stsarna if (!drop_privs(newuser)) 218*04414a37Stsarna exit(EXIT_FAILURE); 219*04414a37Stsarna 220f17eeccaSchristos send_host_information(s); 221c36c99efSchristos delta.tv_sec = time_interval; 222c14c7a6bSpeter delta.tv_usec = 0; 223f17eeccaSchristos gettimeofday(&now, NULL); 224c14c7a6bSpeter timeradd(&now, &delta, &next); 225f17eeccaSchristos 226f17eeccaSchristos pfd[0].fd = s; 227a8b658e3Schristos pfd[0].events = POLLIN; 228f17eeccaSchristos 229e541169cScgd for (;;) { 230f17eeccaSchristos int n; 231f17eeccaSchristos 232f17eeccaSchristos n = poll(pfd, 1, 1000); 233f17eeccaSchristos 234f17eeccaSchristos if (onsighup) { 235f17eeccaSchristos onsighup = 0; 236f17eeccaSchristos getboottime(); 237f17eeccaSchristos } 238f17eeccaSchristos 239f17eeccaSchristos if (n == 1) 240f17eeccaSchristos handleread(s); 241f17eeccaSchristos 242f17eeccaSchristos (void)gettimeofday(&now, NULL); 243c14c7a6bSpeter if (timercmp(&now, &next, >)) { 244f17eeccaSchristos send_host_information(s); 245c14c7a6bSpeter timeradd(&now, &delta, &next); 246f17eeccaSchristos } 247f17eeccaSchristos } 248f17eeccaSchristos 249c14c7a6bSpeter /* NOTREACHED */ 250c14c7a6bSpeter return 0; 251c14c7a6bSpeter } 252c14c7a6bSpeter 253f17eeccaSchristos static void 254c14c7a6bSpeter sighup(int signo __unused) 255f17eeccaSchristos { 256f17eeccaSchristos onsighup = 1; 257f17eeccaSchristos } 258f17eeccaSchristos 259f17eeccaSchristos static void 260f17eeccaSchristos handleread(int s) 261f17eeccaSchristos { 262f17eeccaSchristos struct sockaddr_in from; 263f17eeccaSchristos struct stat st; 264f17eeccaSchristos char path[64]; 265e541169cScgd struct whod wd; 266f17eeccaSchristos int cc, whod; 267f17eeccaSchristos socklen_t len = sizeof(from); 268e541169cScgd 269e541169cScgd cc = recvfrom(s, (char *)&wd, sizeof(struct whod), 0, 270e541169cScgd (struct sockaddr *)&from, &len); 271e541169cScgd if (cc <= 0) { 272e541169cScgd if (cc < 0 && errno != EINTR) 273e541169cScgd syslog(LOG_WARNING, "recv: %m"); 274f17eeccaSchristos return; 275e541169cScgd } 276e541169cScgd if (from.sin_port != sp->s_port) { 277e541169cScgd syslog(LOG_WARNING, "%d: bad from port", 278e541169cScgd ntohs(from.sin_port)); 279f17eeccaSchristos return; 280e541169cScgd } 281f214fa32Smjl if (cc < WHDRSIZE) { 282f214fa32Smjl syslog(LOG_WARNING, "Short packet from %s", 283f214fa32Smjl inet_ntoa(from.sin_addr)); 284f17eeccaSchristos return; 285f214fa32Smjl } 286f214fa32Smjl 287e541169cScgd if (wd.wd_vers != WHODVERSION) 288f17eeccaSchristos return; 289e541169cScgd if (wd.wd_type != WHODTYPE_STATUS) 290f17eeccaSchristos return; 291d3a4eeb1Sexplorer /* 292d3a4eeb1Sexplorer * Ensure null termination of the name within the packet. 293d3a4eeb1Sexplorer * Otherwise we might overflow or read past the end. 294d3a4eeb1Sexplorer */ 295d3a4eeb1Sexplorer wd.wd_hostname[sizeof(wd.wd_hostname)-1] = 0; 296e541169cScgd if (!verify(wd.wd_hostname)) { 2978d79db10Slukem syslog(LOG_WARNING, "malformed host name from %s", 2988d79db10Slukem inet_ntoa(from.sin_addr)); 299f17eeccaSchristos return; 300e541169cScgd } 301f17eeccaSchristos (void)snprintf(path, sizeof(path), "whod.%s", wd.wd_hostname); 302e541169cScgd /* 303e541169cScgd * Rather than truncating and growing the file each time, 304e541169cScgd * use ftruncate if size is less than previous size. 305e541169cScgd */ 306e541169cScgd whod = open(path, O_WRONLY | O_CREAT, 0644); 307e541169cScgd if (whod < 0) { 308e541169cScgd syslog(LOG_WARNING, "%s: %m", path); 309f17eeccaSchristos return; 310e541169cScgd } 311e541169cScgd #if ENDIAN != BIG_ENDIAN 312e541169cScgd { 313e541169cScgd int i, n = (cc - WHDRSIZE) / sizeof(struct whoent); 314e541169cScgd struct whoent *we; 315e541169cScgd 316e541169cScgd /* undo header byte swapping before writing to file */ 317e541169cScgd wd.wd_sendtime = ntohl(wd.wd_sendtime); 318e541169cScgd for (i = 0; i < 3; i++) 319e541169cScgd wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]); 320e541169cScgd wd.wd_boottime = ntohl(wd.wd_boottime); 321e541169cScgd we = wd.wd_we; 322e541169cScgd for (i = 0; i < n; i++) { 323e541169cScgd we->we_idle = ntohl(we->we_idle); 324e541169cScgd we->we_utmp.out_time = 325e541169cScgd ntohl(we->we_utmp.out_time); 326e541169cScgd we++; 327e541169cScgd } 328e541169cScgd } 329e541169cScgd #endif 330e541169cScgd (void)time((time_t *)&wd.wd_recvtime); 331e541169cScgd (void)write(whod, (char *)&wd, cc); 332e541169cScgd if (fstat(whod, &st) < 0 || st.st_size > cc) 333f17eeccaSchristos (void)ftruncate(whod, cc); 334e541169cScgd (void)close(whod); 335e541169cScgd } 336e541169cScgd 337e541169cScgd /* 338e541169cScgd * Check out host name for unprintables 339e541169cScgd * and other funnies before allowing a file 340e541169cScgd * to be created. Sorry, but blanks aren't allowed. 341e541169cScgd */ 342f17eeccaSchristos static int 343f17eeccaSchristos verify(const char *name) 344e541169cScgd { 345e5d6d67cSlukem int size = 0; 346e541169cScgd 347e541169cScgd while (*name) { 348f17eeccaSchristos if (!isascii((unsigned char)*name) || 349f17eeccaSchristos !(isalnum((unsigned char)*name) || 3509122339bSdsl ispunct((unsigned char)*name))) 351f17eeccaSchristos return 0; 352e541169cScgd name++, size++; 353e541169cScgd } 354f17eeccaSchristos return size > 0; 355e541169cScgd } 356e541169cScgd 357f17eeccaSchristos static void 358f17eeccaSchristos send_host_information(int s) 359e541169cScgd { 360e5d6d67cSlukem struct neighbor *np; 361e5d6d67cSlukem struct whoent *we = mywd.wd_we, *wlast; 362632e7ed5Sjunyoung int i, cc, utmpent = 0; 363e541169cScgd struct stat stb; 364e541169cScgd double avenrun[3]; 3659afcf17bSjtc time_t now; 366b166b5b4Schristos static struct utmpentry *ohead = NULL; 367b166b5b4Schristos struct utmpentry *ep; 368c14c7a6bSpeter static int count = 0; 369e541169cScgd 3709afcf17bSjtc now = time(NULL); 371c14c7a6bSpeter if (count % 10 == 0) 372f17eeccaSchristos getboottime(); 373c14c7a6bSpeter count++; 374b166b5b4Schristos 375b166b5b4Schristos (void)getutentries(NULL, &ep); 376b166b5b4Schristos if (ep != ohead) { 377b166b5b4Schristos freeutentries(ep); 378e541169cScgd wlast = &mywd.wd_we[1024 / sizeof(struct whoent) - 1]; 379b166b5b4Schristos for (; ep; ep = ep->next) { 380b166b5b4Schristos (void)strncpy(we->we_utmp.out_line, ep->line, 381b166b5b4Schristos sizeof(we->we_utmp.out_line) - 1); 382b166b5b4Schristos (void)strncpy(we->we_utmp.out_name, ep->name, 383b166b5b4Schristos sizeof(we->we_utmp.out_name) - 1); 384b166b5b4Schristos we->we_utmp.out_time = htonl(ep->tv.tv_sec); 385e541169cScgd if (we >= wlast) 386e541169cScgd break; 387e541169cScgd we++; 388e541169cScgd } 389e541169cScgd utmpent = we - mywd.wd_we; 390e541169cScgd } 391e541169cScgd 392e541169cScgd /* 393e541169cScgd * The test on utmpent looks silly---after all, if no one is 394e541169cScgd * logged on, why worry about efficiency?---but is useful on 395e541169cScgd * (e.g.) compute servers. 396e541169cScgd */ 397e541169cScgd if (utmpent && chdir(_PATH_DEV)) { 398e541169cScgd syslog(LOG_ERR, "chdir(%s): %m", _PATH_DEV); 399c14c7a6bSpeter exit(EXIT_FAILURE); 400e541169cScgd } 401e541169cScgd we = mywd.wd_we; 402e541169cScgd for (i = 0; i < utmpent; i++) { 403e541169cScgd if (stat(we->we_utmp.out_line, &stb) >= 0) 404e541169cScgd we->we_idle = htonl(now - stb.st_atime); 405e541169cScgd we++; 406e541169cScgd } 407e541169cScgd (void)getloadavg(avenrun, sizeof(avenrun)/sizeof(avenrun[0])); 408e541169cScgd for (i = 0; i < 3; i++) 409e541169cScgd mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100)); 410e541169cScgd cc = (char *)we - (char *)&mywd; 411e541169cScgd mywd.wd_sendtime = htonl(time(0)); 412e541169cScgd mywd.wd_vers = WHODVERSION; 413e541169cScgd mywd.wd_type = WHODTYPE_STATUS; 414e541169cScgd for (np = neighbors; np != NULL; np = np->n_next) 415cb1d6897Schristos (void)Sendto(s, (char *)&mywd, cc, 0, 4169afcf17bSjtc np->n_addr, np->n_addrlen); 417e541169cScgd if (utmpent && chdir(_PATH_RWHODIR)) { 418e541169cScgd syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR); 419c14c7a6bSpeter exit(EXIT_FAILURE); 420e541169cScgd } 421e541169cScgd } 422e541169cScgd 423f17eeccaSchristos static void 424f17eeccaSchristos getboottime(void) 425e541169cScgd { 4269afcf17bSjtc int mib[2]; 4279afcf17bSjtc size_t size; 4289afcf17bSjtc struct timeval tm; 429e541169cScgd 4309afcf17bSjtc mib[0] = CTL_KERN; 4319afcf17bSjtc mib[1] = KERN_BOOTTIME; 4329afcf17bSjtc size = sizeof(tm); 4339afcf17bSjtc if (sysctl(mib, 2, &tm, &size, NULL, 0) == -1) { 4349afcf17bSjtc syslog(LOG_ERR, "cannot get boottime: %m"); 435c14c7a6bSpeter exit(EXIT_FAILURE); 4369afcf17bSjtc } 4379afcf17bSjtc mywd.wd_boottime = htonl(tm.tv_sec); 438e541169cScgd } 439b55bc39bSandrew 440f17eeccaSchristos static void 441f17eeccaSchristos quit(const char *msg) 4429afcf17bSjtc { 443d8302e2dSis syslog(LOG_ERR, "%s", msg); 444c14c7a6bSpeter exit(EXIT_FAILURE); 445e541169cScgd } 4469afcf17bSjtc 4479afcf17bSjtc #define ROUNDUP(a) \ 4489afcf17bSjtc ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 449f17eeccaSchristos #define ADVANCE(x, n) ((char *)(x) + ROUNDUP((n)->sa_len)) 4509afcf17bSjtc 451f17eeccaSchristos static void 452f17eeccaSchristos rt_xaddrs(void *cp, void *cplim, struct rt_addrinfo *rtinfo) 4539afcf17bSjtc { 454e5d6d67cSlukem struct sockaddr *sa; 455e5d6d67cSlukem int i; 4569afcf17bSjtc 457f17eeccaSchristos (void)memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info)); 4589afcf17bSjtc for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) { 4599afcf17bSjtc if ((rtinfo->rti_addrs & (1 << i)) == 0) 4609afcf17bSjtc continue; 4619afcf17bSjtc rtinfo->rti_info[i] = sa = (struct sockaddr *)cp; 462f17eeccaSchristos cp = ADVANCE(cp, sa); 4639afcf17bSjtc } 464e541169cScgd } 465e541169cScgd 466e541169cScgd /* 467e541169cScgd * Figure out device configuration and select 468e541169cScgd * networks which deserve status information. 469e541169cScgd */ 470f17eeccaSchristos static int 471f17eeccaSchristos configure(int s) 472e541169cScgd { 473e5d6d67cSlukem struct neighbor *np; 474e5d6d67cSlukem struct if_msghdr *ifm; 475e5d6d67cSlukem struct ifa_msghdr *ifam; 4769afcf17bSjtc struct sockaddr_dl *sdl; 4779afcf17bSjtc size_t needed; 4789afcf17bSjtc int mib[6], flags = 0, len; 4799afcf17bSjtc char *buf, *lim, *next; 4809afcf17bSjtc struct rt_addrinfo info; 481f17eeccaSchristos struct sockaddr_in dstaddr; 482e541169cScgd 4839afcf17bSjtc mib[0] = CTL_NET; 4849afcf17bSjtc mib[1] = PF_ROUTE; 4859afcf17bSjtc mib[2] = 0; 4869afcf17bSjtc mib[3] = AF_INET; 4879afcf17bSjtc mib[4] = NET_RT_IFLIST; 4889afcf17bSjtc mib[5] = 0; 4899afcf17bSjtc if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 4909afcf17bSjtc quit("route-sysctl-estimate"); 4919afcf17bSjtc if ((buf = malloc(needed)) == NULL) 4929afcf17bSjtc quit("malloc"); 4939afcf17bSjtc if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) 4949afcf17bSjtc quit("actual retrieval of interface table"); 4959afcf17bSjtc lim = buf + needed; 4969afcf17bSjtc 4979afcf17bSjtc sdl = NULL; /* XXX just to keep gcc -Wall happy */ 4989afcf17bSjtc for (next = buf; next < lim; next += ifm->ifm_msglen) { 4999afcf17bSjtc ifm = (struct if_msghdr *)next; 5009afcf17bSjtc if (ifm->ifm_type == RTM_IFINFO) { 5019afcf17bSjtc sdl = (struct sockaddr_dl *)(ifm + 1); 5029afcf17bSjtc flags = ifm->ifm_flags; 5039afcf17bSjtc continue; 504e541169cScgd } 5059afcf17bSjtc if ((flags & IFF_UP) == 0 || 5069afcf17bSjtc (flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0) 5079afcf17bSjtc continue; 5089afcf17bSjtc if (ifm->ifm_type != RTM_NEWADDR) 5099afcf17bSjtc quit("out of sync parsing NET_RT_IFLIST"); 5109afcf17bSjtc ifam = (struct ifa_msghdr *)ifm; 5119afcf17bSjtc info.rti_addrs = ifam->ifam_addrs; 512f17eeccaSchristos rt_xaddrs((ifam + 1), ifam->ifam_msglen + (char *)ifam, &info); 5139afcf17bSjtc /* gag, wish we could get rid of Internet dependencies */ 514f17eeccaSchristos if (info.rti_info[RTAX_BRD] == NULL || 515f17eeccaSchristos info.rti_info[RTAX_BRD]->sa_family != AF_INET) 516f17eeccaSchristos continue; 517f17eeccaSchristos (void)memcpy(&dstaddr, info.rti_info[RTAX_BRD], 518f17eeccaSchristos sizeof(dstaddr)); 5199afcf17bSjtc #define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr 5209afcf17bSjtc #define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port 521f17eeccaSchristos PORT_SA(&dstaddr) = sp->s_port; 522e541169cScgd for (np = neighbors; np != NULL; np = np->n_next) 5239afcf17bSjtc if (memcmp(sdl->sdl_data, np->n_name, 5249afcf17bSjtc sdl->sdl_nlen) == 0 && 525f17eeccaSchristos IPADDR_SA(np->n_addr) == IPADDR_SA(&dstaddr)) 526e541169cScgd break; 527e541169cScgd if (np != NULL) 528e541169cScgd continue; 529f17eeccaSchristos len = sizeof(*np) + dstaddr.sin_len + sdl->sdl_nlen + 1; 5309afcf17bSjtc np = (struct neighbor *)malloc(len); 531e541169cScgd if (np == NULL) 5329afcf17bSjtc quit("malloc of neighbor structure"); 533f17eeccaSchristos (void)memset(np, 0, len); 5349afcf17bSjtc np->n_flags = flags; 5359afcf17bSjtc np->n_addr = (struct sockaddr *)(np + 1); 536f17eeccaSchristos np->n_addrlen = dstaddr.sin_len; 5379afcf17bSjtc np->n_name = np->n_addrlen + (char *)np->n_addr; 538e541169cScgd np->n_next = neighbors; 539e541169cScgd neighbors = np; 540f17eeccaSchristos (void)memcpy(np->n_addr, &dstaddr, np->n_addrlen); 541f17eeccaSchristos (void)memcpy(np->n_name, sdl->sdl_data, sdl->sdl_nlen); 542e541169cScgd } 5439afcf17bSjtc free(buf); 544e541169cScgd return (1); 545e541169cScgd } 546e541169cScgd 547e541169cScgd #ifdef DEBUG 548f17eeccaSchristos static ssize_t 549f17eeccaSchristos Sendto(int s, const void *buf, size_t cc, int flags, const struct sockaddr *to, 550f17eeccaSchristos socklen_t tolen) 551e541169cScgd { 552e5d6d67cSlukem struct whod *w = (struct whod *)buf; 553e5d6d67cSlukem struct whoent *we; 554f17eeccaSchristos struct sockaddr_in *sasin = (struct sockaddr_in *)to; 555cb1d6897Schristos ssize_t ret; 556cb1d6897Schristos 557cb1d6897Schristos ret = sendto(s, buf, cc, flags, to, tolen); 558e541169cScgd 559c36c99efSchristos printf("sendto %s.%d\n", inet_ntoa(sasin->sin_addr), 560f17eeccaSchristos ntohs(sasin->sin_port)); 561e541169cScgd printf("hostname %s %s\n", w->wd_hostname, 562e541169cScgd interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up")); 563e541169cScgd printf("load %4.2f, %4.2f, %4.2f\n", 564e541169cScgd ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0, 565e541169cScgd ntohl(w->wd_loadav[2]) / 100.0); 566e541169cScgd cc -= WHDRSIZE; 567e541169cScgd for (we = w->wd_we, cc /= sizeof(struct whoent); cc > 0; cc--, we++) { 568e541169cScgd time_t t = ntohl(we->we_utmp.out_time); 56950455a91Schristos printf("%-8.8s %s:%s %.12s", we->we_utmp.out_name, 57050455a91Schristos w->wd_hostname, we->we_utmp.out_line, ctime(&t)+4); 571e541169cScgd we->we_idle = ntohl(we->we_idle) / 60; 572e541169cScgd if (we->we_idle) { 573e541169cScgd if (we->we_idle >= 100*60) 574e541169cScgd we->we_idle = 100*60 - 1; 575e541169cScgd if (we->we_idle >= 60) 576e541169cScgd printf(" %2d", we->we_idle / 60); 577e541169cScgd else 578e541169cScgd printf(" "); 579e541169cScgd printf(":%02d", we->we_idle % 60); 580e541169cScgd } 581e541169cScgd printf("\n"); 582e541169cScgd } 583cb1d6897Schristos return ret; 584e541169cScgd } 585e541169cScgd 586f17eeccaSchristos static char * 587f17eeccaSchristos interval(int time, const char *updown) 588e541169cScgd { 589e541169cScgd static char resbuf[32]; 590e541169cScgd int days, hours, minutes; 591e541169cScgd 592e541169cScgd if (time < 0 || time > 3*30*24*60*60) { 593d9f2774cSitojun (void)snprintf(resbuf, sizeof(resbuf), " %s ??:??", updown); 594e541169cScgd return (resbuf); 595e541169cScgd } 596e541169cScgd minutes = (time + 59) / 60; /* round to minutes */ 597e541169cScgd hours = minutes / 60; minutes %= 60; 598e541169cScgd days = hours / 24; hours %= 24; 599e541169cScgd if (days) 600d9f2774cSitojun (void)snprintf(resbuf, sizeof(resbuf), "%s %2d+%02d:%02d", 601e541169cScgd updown, days, hours, minutes); 602e541169cScgd else 603d9f2774cSitojun (void)snprintf(resbuf, sizeof(resbuf), "%s %2d:%02d", 604e541169cScgd updown, hours, minutes); 605f17eeccaSchristos return resbuf; 606e541169cScgd } 607e541169cScgd #endif 608c36c99efSchristos 609*04414a37Stsarna static int 610*04414a37Stsarna drop_privs(char *newuser) 611*04414a37Stsarna { 612*04414a37Stsarna struct passwd *pw; 613*04414a37Stsarna gid_t gidset[1]; 614*04414a37Stsarna 615*04414a37Stsarna pw = getpwnam(newuser); 616*04414a37Stsarna if (pw == NULL) { 617*04414a37Stsarna syslog(LOG_ERR, "no user %.100s", newuser); 618*04414a37Stsarna return 0; 619*04414a37Stsarna } 620*04414a37Stsarna 621*04414a37Stsarna endpwent(); 622*04414a37Stsarna 623*04414a37Stsarna gidset[0] = pw->pw_gid; 624*04414a37Stsarna if (setgroups(1, gidset) == -1) { 625*04414a37Stsarna syslog(LOG_ERR, "setgroups: %m"); 626*04414a37Stsarna return 0; 627*04414a37Stsarna } 628*04414a37Stsarna 629*04414a37Stsarna if (setgid(pw->pw_gid) == -1) { 630*04414a37Stsarna syslog(LOG_ERR, "setgid: %m"); 631*04414a37Stsarna return 0; 632*04414a37Stsarna } 633*04414a37Stsarna 634*04414a37Stsarna if (setuid(pw->pw_uid) == -1) { 635*04414a37Stsarna syslog(LOG_ERR, "setuid: %m"); 636*04414a37Stsarna return 0; 637*04414a37Stsarna } 638*04414a37Stsarna 639*04414a37Stsarna return 1; 640*04414a37Stsarna } 641*04414a37Stsarna 642c36c99efSchristos static void 643c36c99efSchristos usage(void) 644c36c99efSchristos { 645cb1d6897Schristos (void)fprintf(stderr, "Usage: %s [-i <time>]\n", getprogname()); 646cb1d6897Schristos exit(EXIT_FAILURE); 647c36c99efSchristos } 648