1 1.41 kre /* $NetBSD: rwhod.c,v 1.41 2018/10/30 21:15:09 kre Exp $ */ 2 1.24 christos 3 1.1 cgd /* 4 1.8 jtc * Copyright (c) 1983, 1993 5 1.8 jtc * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * Redistribution and use in source and binary forms, with or without 8 1.1 cgd * modification, are permitted provided that the following conditions 9 1.1 cgd * are met: 10 1.1 cgd * 1. Redistributions of source code must retain the above copyright 11 1.1 cgd * notice, this list of conditions and the following disclaimer. 12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 cgd * notice, this list of conditions and the following disclaimer in the 14 1.1 cgd * documentation and/or other materials provided with the distribution. 15 1.22 agc * 3. Neither the name of the University nor the names of its contributors 16 1.1 cgd * may be used to endorse or promote products derived from this software 17 1.1 cgd * without specific prior written permission. 18 1.1 cgd * 19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 cgd * SUCH DAMAGE. 30 1.1 cgd */ 31 1.1 cgd 32 1.11 lukem #include <sys/cdefs.h> 33 1.1 cgd #ifndef lint 34 1.37 lukem __COPYRIGHT("@(#) Copyright (c) 1983, 1993\ 35 1.37 lukem The Regents of the University of California. All rights reserved."); 36 1.1 cgd #endif /* not lint */ 37 1.1 cgd 38 1.1 cgd #ifndef lint 39 1.11 lukem #if 0 40 1.11 lukem static char sccsid[] = "@(#)rwhod.c 8.1 (Berkeley) 6/6/93"; 41 1.11 lukem #else 42 1.41 kre __RCSID("$NetBSD: rwhod.c,v 1.41 2018/10/30 21:15:09 kre Exp $"); 43 1.11 lukem #endif 44 1.1 cgd #endif /* not lint */ 45 1.1 cgd 46 1.1 cgd #include <sys/param.h> 47 1.1 cgd #include <sys/socket.h> 48 1.1 cgd #include <sys/stat.h> 49 1.1 cgd #include <sys/signal.h> 50 1.1 cgd #include <sys/ioctl.h> 51 1.8 jtc #include <sys/sysctl.h> 52 1.1 cgd 53 1.1 cgd #include <net/if.h> 54 1.8 jtc #include <net/if_dl.h> 55 1.8 jtc #include <net/route.h> 56 1.1 cgd #include <netinet/in.h> 57 1.8 jtc #include <protocols/rwhod.h> 58 1.11 lukem #include <arpa/inet.h> 59 1.1 cgd 60 1.8 jtc #include <ctype.h> 61 1.40 christos #include <pwd.h> 62 1.12 lukem #include <err.h> 63 1.1 cgd #include <errno.h> 64 1.8 jtc #include <fcntl.h> 65 1.1 cgd #include <netdb.h> 66 1.8 jtc #include <paths.h> 67 1.24 christos #include <poll.h> 68 1.8 jtc #include <stdio.h> 69 1.8 jtc #include <stdlib.h> 70 1.8 jtc #include <string.h> 71 1.1 cgd #include <syslog.h> 72 1.8 jtc #include <unistd.h> 73 1.15 thorpej #include <util.h> 74 1.1 cgd 75 1.19 christos #include "utmpentry.h" 76 1.29 christos 77 1.26 peter #define CHECK_INTERVAL (3 * 60) 78 1.1 cgd 79 1.29 christos /* Time interval limit; ruptime will think that we are down > than this */ 80 1.29 christos #define MAX_INTERVAL (11 * 60) 81 1.29 christos 82 1.29 christos 83 1.24 christos static char myname[MAXHOSTNAMELEN + 1]; 84 1.1 cgd 85 1.1 cgd /* 86 1.8 jtc * We communicate with each neighbor in a list constructed at the time we're 87 1.8 jtc * started up. Neighbors are currently directly connected via a hardware 88 1.8 jtc * interface. 89 1.1 cgd */ 90 1.26 peter struct neighbor { 91 1.1 cgd struct neighbor *n_next; 92 1.1 cgd char *n_name; /* interface name */ 93 1.14 mrg struct sockaddr *n_addr; /* who to send to */ 94 1.1 cgd int n_addrlen; /* size of address */ 95 1.1 cgd int n_flags; /* should forward?, interface flags */ 96 1.1 cgd }; 97 1.1 cgd 98 1.24 christos static struct neighbor *neighbors; 99 1.24 christos static struct whod mywd; 100 1.24 christos static struct servent *sp; 101 1.24 christos static volatile sig_atomic_t onsighup; 102 1.1 cgd 103 1.8 jtc #define WHDRSIZE (sizeof(mywd) - sizeof(mywd.wd_we)) 104 1.1 cgd 105 1.24 christos static int configure(int); 106 1.24 christos static void getboottime(void); 107 1.24 christos static void send_host_information(int); 108 1.26 peter static void sighup(int); 109 1.24 christos static void handleread(int); 110 1.39 joerg __dead static void quit(const char *); 111 1.24 christos static void rt_xaddrs(void *, void *, struct rt_addrinfo *); 112 1.31 tsarna static int drop_privs(char *); 113 1.35 perry static void usage(void) __dead; 114 1.24 christos static int verify(const char *); 115 1.8 jtc #ifdef DEBUG 116 1.24 christos static char *interval(int, const char *); 117 1.24 christos static ssize_t Sendto(int, const void *, size_t, int, 118 1.24 christos const struct sockaddr *, socklen_t); 119 1.30 christos #else 120 1.30 christos #define Sendto sendto 121 1.8 jtc #endif 122 1.1 cgd 123 1.8 jtc int 124 1.24 christos main(int argc, char *argv[]) 125 1.1 cgd { 126 1.28 christos int s, ch; 127 1.28 christos int time_interval = 180; /* Default time (180 seconds) */ 128 1.28 christos char *cp, *ep; 129 1.26 peter socklen_t on = 1; 130 1.24 christos struct sockaddr_in sasin; 131 1.24 christos struct pollfd pfd[1]; 132 1.26 peter struct timeval delta, next, now; 133 1.31 tsarna char *newuser = NULL; 134 1.1 cgd 135 1.28 christos setprogname(argv[0]); 136 1.28 christos 137 1.12 lukem if (getuid()) 138 1.26 peter errx(EXIT_FAILURE, "not super user"); 139 1.28 christos 140 1.31 tsarna while ((ch = getopt(argc, argv, "i:u:")) != -1) { 141 1.28 christos switch (ch) { 142 1.29 christos case 'i': 143 1.28 christos time_interval = (int)strtol(optarg, &ep, 10); 144 1.28 christos 145 1.28 christos switch (*ep) { 146 1.28 christos case '\0': 147 1.28 christos break; 148 1.28 christos case 'm': 149 1.28 christos case 'M': 150 1.28 christos /* Time in minutes. */ 151 1.28 christos time_interval *= 60; 152 1.28 christos if (ep[1] == '\0') 153 1.28 christos break; 154 1.28 christos /*FALLTHROUGH*/ 155 1.28 christos default: 156 1.28 christos errx(1, "Invalid argument: `%s'", optarg); 157 1.28 christos } 158 1.28 christos 159 1.28 christos if (time_interval <= 0) 160 1.29 christos errx(1, "Interval must be greater than 0"); 161 1.28 christos 162 1.29 christos if (time_interval > MAX_INTERVAL) 163 1.29 christos errx(1, "Interval cannot be greater than" 164 1.29 christos " %d minutes", MAX_INTERVAL / 60); 165 1.28 christos break; 166 1.31 tsarna 167 1.31 tsarna case 'u': 168 1.31 tsarna newuser = optarg; 169 1.31 tsarna break; 170 1.31 tsarna 171 1.28 christos default: 172 1.28 christos usage(); 173 1.28 christos } 174 1.28 christos } 175 1.28 christos 176 1.1 cgd sp = getservbyname("who", "udp"); 177 1.12 lukem if (sp == NULL) 178 1.26 peter errx(EXIT_FAILURE, "udp/who: unknown service"); 179 1.1 cgd #ifndef DEBUG 180 1.26 peter (void)daemon(1, 0); 181 1.26 peter (void)pidfile(NULL); 182 1.1 cgd #endif 183 1.12 lukem if (chdir(_PATH_RWHODIR) < 0) 184 1.26 peter err(EXIT_FAILURE, "%s", _PATH_RWHODIR); 185 1.26 peter (void)signal(SIGHUP, sighup); 186 1.30 christos openlog(getprogname(), LOG_PID, LOG_DAEMON); 187 1.1 cgd /* 188 1.1 cgd * Establish host name as returned by system. 189 1.1 cgd */ 190 1.8 jtc if (gethostname(myname, sizeof(myname) - 1) < 0) { 191 1.1 cgd syslog(LOG_ERR, "gethostname: %m"); 192 1.26 peter exit(EXIT_FAILURE); 193 1.1 cgd } 194 1.13 mrg myname[sizeof(myname) - 1] = '\0'; 195 1.12 lukem if ((cp = strchr(myname, '.')) != NULL) 196 1.1 cgd *cp = '\0'; 197 1.24 christos (void)strncpy(mywd.wd_hostname, myname, sizeof(mywd.wd_hostname) - 1); 198 1.24 christos getboottime(); 199 1.1 cgd if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 200 1.1 cgd syslog(LOG_ERR, "socket: %m"); 201 1.26 peter exit(EXIT_FAILURE); 202 1.1 cgd } 203 1.8 jtc if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { 204 1.1 cgd syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m"); 205 1.26 peter exit(EXIT_FAILURE); 206 1.1 cgd } 207 1.24 christos (void)memset(&sasin, 0, sizeof(sasin)); 208 1.24 christos sasin.sin_family = AF_INET; 209 1.24 christos sasin.sin_port = sp->s_port; 210 1.24 christos if (bind(s, (struct sockaddr *)&sasin, sizeof(sasin)) < 0) { 211 1.1 cgd syslog(LOG_ERR, "bind: %m"); 212 1.26 peter exit(EXIT_FAILURE); 213 1.1 cgd } 214 1.1 cgd if (!configure(s)) 215 1.26 peter exit(EXIT_FAILURE); 216 1.24 christos 217 1.31 tsarna if (newuser) 218 1.31 tsarna if (!drop_privs(newuser)) 219 1.31 tsarna exit(EXIT_FAILURE); 220 1.31 tsarna 221 1.24 christos send_host_information(s); 222 1.28 christos delta.tv_sec = time_interval; 223 1.26 peter delta.tv_usec = 0; 224 1.24 christos gettimeofday(&now, NULL); 225 1.26 peter timeradd(&now, &delta, &next); 226 1.24 christos 227 1.24 christos pfd[0].fd = s; 228 1.25 christos pfd[0].events = POLLIN; 229 1.24 christos 230 1.1 cgd for (;;) { 231 1.24 christos int n; 232 1.1 cgd 233 1.24 christos n = poll(pfd, 1, 1000); 234 1.24 christos 235 1.24 christos if (onsighup) { 236 1.24 christos onsighup = 0; 237 1.24 christos getboottime(); 238 1.1 cgd } 239 1.24 christos 240 1.24 christos if (n == 1) 241 1.24 christos handleread(s); 242 1.24 christos 243 1.24 christos (void)gettimeofday(&now, NULL); 244 1.26 peter if (timercmp(&now, &next, >)) { 245 1.24 christos send_host_information(s); 246 1.26 peter timeradd(&now, &delta, &next); 247 1.16 mjl } 248 1.24 christos } 249 1.26 peter 250 1.26 peter /* NOTREACHED */ 251 1.26 peter return 0; 252 1.24 christos } 253 1.24 christos 254 1.24 christos static void 255 1.26 peter sighup(int signo __unused) 256 1.24 christos { 257 1.24 christos onsighup = 1; 258 1.24 christos } 259 1.24 christos 260 1.24 christos static void 261 1.24 christos handleread(int s) 262 1.24 christos { 263 1.24 christos struct sockaddr_in from; 264 1.24 christos struct stat st; 265 1.24 christos char path[64]; 266 1.24 christos struct whod wd; 267 1.24 christos int cc, whod; 268 1.24 christos socklen_t len = sizeof(from); 269 1.24 christos 270 1.24 christos cc = recvfrom(s, (char *)&wd, sizeof(struct whod), 0, 271 1.24 christos (struct sockaddr *)&from, &len); 272 1.24 christos if (cc <= 0) { 273 1.24 christos if (cc < 0 && errno != EINTR) 274 1.24 christos syslog(LOG_WARNING, "recv: %m"); 275 1.24 christos return; 276 1.24 christos } 277 1.24 christos if (from.sin_port != sp->s_port) { 278 1.24 christos syslog(LOG_WARNING, "%d: bad from port", 279 1.24 christos ntohs(from.sin_port)); 280 1.24 christos return; 281 1.24 christos } 282 1.38 lukem if (cc < (int)WHDRSIZE) { 283 1.24 christos syslog(LOG_WARNING, "Short packet from %s", 284 1.24 christos inet_ntoa(from.sin_addr)); 285 1.24 christos return; 286 1.24 christos } 287 1.24 christos 288 1.24 christos if (wd.wd_vers != WHODVERSION) 289 1.24 christos return; 290 1.24 christos if (wd.wd_type != WHODTYPE_STATUS) 291 1.24 christos return; 292 1.27 junyoung /* 293 1.24 christos * Ensure null termination of the name within the packet. 294 1.24 christos * Otherwise we might overflow or read past the end. 295 1.24 christos */ 296 1.24 christos wd.wd_hostname[sizeof(wd.wd_hostname)-1] = 0; 297 1.24 christos if (!verify(wd.wd_hostname)) { 298 1.24 christos syslog(LOG_WARNING, "malformed host name from %s", 299 1.24 christos inet_ntoa(from.sin_addr)); 300 1.24 christos return; 301 1.24 christos } 302 1.24 christos (void)snprintf(path, sizeof(path), "whod.%s", wd.wd_hostname); 303 1.24 christos /* 304 1.24 christos * Rather than truncating and growing the file each time, 305 1.24 christos * use ftruncate if size is less than previous size. 306 1.24 christos */ 307 1.24 christos whod = open(path, O_WRONLY | O_CREAT, 0644); 308 1.24 christos if (whod < 0) { 309 1.24 christos syslog(LOG_WARNING, "%s: %m", path); 310 1.24 christos return; 311 1.24 christos } 312 1.1 cgd #if ENDIAN != BIG_ENDIAN 313 1.24 christos { 314 1.26 peter int i, n = (cc - WHDRSIZE) / sizeof(struct whoent); 315 1.24 christos struct whoent *we; 316 1.24 christos 317 1.24 christos /* undo header byte swapping before writing to file */ 318 1.24 christos wd.wd_sendtime = ntohl(wd.wd_sendtime); 319 1.24 christos for (i = 0; i < 3; i++) 320 1.24 christos wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]); 321 1.24 christos wd.wd_boottime = ntohl(wd.wd_boottime); 322 1.24 christos we = wd.wd_we; 323 1.24 christos for (i = 0; i < n; i++) { 324 1.24 christos we->we_idle = ntohl(we->we_idle); 325 1.24 christos we->we_utmp.out_time = 326 1.24 christos ntohl(we->we_utmp.out_time); 327 1.24 christos we++; 328 1.1 cgd } 329 1.24 christos } 330 1.1 cgd #endif 331 1.34 mrg wd.wd_recvtime = time(NULL); 332 1.24 christos (void)write(whod, (char *)&wd, cc); 333 1.24 christos if (fstat(whod, &st) < 0 || st.st_size > cc) 334 1.24 christos (void)ftruncate(whod, cc); 335 1.24 christos (void)close(whod); 336 1.1 cgd } 337 1.1 cgd 338 1.1 cgd /* 339 1.1 cgd * Check out host name for unprintables 340 1.1 cgd * and other funnies before allowing a file 341 1.1 cgd * to be created. Sorry, but blanks aren't allowed. 342 1.1 cgd */ 343 1.24 christos static int 344 1.24 christos verify(const char *name) 345 1.1 cgd { 346 1.12 lukem int size = 0; 347 1.1 cgd 348 1.1 cgd while (*name) { 349 1.24 christos if (!isascii((unsigned char)*name) || 350 1.24 christos !(isalnum((unsigned char)*name) || 351 1.24 christos ispunct((unsigned char)*name))) 352 1.24 christos return 0; 353 1.1 cgd name++, size++; 354 1.1 cgd } 355 1.24 christos return size > 0; 356 1.1 cgd } 357 1.1 cgd 358 1.24 christos static void 359 1.24 christos send_host_information(int s) 360 1.1 cgd { 361 1.12 lukem struct neighbor *np; 362 1.12 lukem struct whoent *we = mywd.wd_we, *wlast; 363 1.27 junyoung int i, cc, utmpent = 0; 364 1.1 cgd struct stat stb; 365 1.8 jtc double avenrun[3]; 366 1.8 jtc time_t now; 367 1.19 christos static struct utmpentry *ohead = NULL; 368 1.19 christos struct utmpentry *ep; 369 1.26 peter static int count = 0; 370 1.1 cgd 371 1.8 jtc now = time(NULL); 372 1.26 peter if (count % 10 == 0) 373 1.24 christos getboottime(); 374 1.26 peter count++; 375 1.19 christos 376 1.19 christos (void)getutentries(NULL, &ep); 377 1.36 dholland /* XXX probably should expose utmp mtime, check that instead */ 378 1.19 christos if (ep != ohead) { 379 1.19 christos wlast = &mywd.wd_we[1024 / sizeof(struct whoent) - 1]; 380 1.19 christos for (; ep; ep = ep->next) { 381 1.19 christos (void)strncpy(we->we_utmp.out_line, ep->line, 382 1.33 christos sizeof(we->we_utmp.out_line)); 383 1.19 christos (void)strncpy(we->we_utmp.out_name, ep->name, 384 1.33 christos sizeof(we->we_utmp.out_name)); 385 1.19 christos we->we_utmp.out_time = htonl(ep->tv.tv_sec); 386 1.19 christos if (we >= wlast) 387 1.19 christos break; 388 1.19 christos we++; 389 1.1 cgd } 390 1.1 cgd utmpent = we - mywd.wd_we; 391 1.1 cgd } 392 1.1 cgd 393 1.1 cgd /* 394 1.1 cgd * The test on utmpent looks silly---after all, if no one is 395 1.1 cgd * logged on, why worry about efficiency?---but is useful on 396 1.1 cgd * (e.g.) compute servers. 397 1.1 cgd */ 398 1.1 cgd if (utmpent && chdir(_PATH_DEV)) { 399 1.1 cgd syslog(LOG_ERR, "chdir(%s): %m", _PATH_DEV); 400 1.26 peter exit(EXIT_FAILURE); 401 1.1 cgd } 402 1.1 cgd we = mywd.wd_we; 403 1.1 cgd for (i = 0; i < utmpent; i++) { 404 1.1 cgd if (stat(we->we_utmp.out_line, &stb) >= 0) 405 1.1 cgd we->we_idle = htonl(now - stb.st_atime); 406 1.1 cgd we++; 407 1.1 cgd } 408 1.8 jtc (void)getloadavg(avenrun, sizeof(avenrun)/sizeof(avenrun[0])); 409 1.1 cgd for (i = 0; i < 3; i++) 410 1.1 cgd mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100)); 411 1.1 cgd cc = (char *)we - (char *)&mywd; 412 1.1 cgd mywd.wd_sendtime = htonl(time(0)); 413 1.1 cgd mywd.wd_vers = WHODVERSION; 414 1.1 cgd mywd.wd_type = WHODTYPE_STATUS; 415 1.1 cgd for (np = neighbors; np != NULL; np = np->n_next) 416 1.30 christos (void)Sendto(s, (char *)&mywd, cc, 0, 417 1.8 jtc np->n_addr, np->n_addrlen); 418 1.1 cgd if (utmpent && chdir(_PATH_RWHODIR)) { 419 1.1 cgd syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR); 420 1.26 peter exit(EXIT_FAILURE); 421 1.1 cgd } 422 1.1 cgd } 423 1.1 cgd 424 1.24 christos static void 425 1.24 christos getboottime(void) 426 1.1 cgd { 427 1.8 jtc int mib[2]; 428 1.8 jtc size_t size; 429 1.41 kre struct timespec tm; 430 1.8 jtc 431 1.8 jtc mib[0] = CTL_KERN; 432 1.8 jtc mib[1] = KERN_BOOTTIME; 433 1.8 jtc size = sizeof(tm); 434 1.8 jtc if (sysctl(mib, 2, &tm, &size, NULL, 0) == -1) { 435 1.8 jtc syslog(LOG_ERR, "cannot get boottime: %m"); 436 1.26 peter exit(EXIT_FAILURE); 437 1.8 jtc } 438 1.8 jtc mywd.wd_boottime = htonl(tm.tv_sec); 439 1.8 jtc } 440 1.8 jtc 441 1.24 christos static void 442 1.24 christos quit(const char *msg) 443 1.8 jtc { 444 1.18 is syslog(LOG_ERR, "%s", msg); 445 1.26 peter exit(EXIT_FAILURE); 446 1.8 jtc } 447 1.8 jtc 448 1.8 jtc #define ROUNDUP(a) \ 449 1.8 jtc ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 450 1.24 christos #define ADVANCE(x, n) ((char *)(x) + ROUNDUP((n)->sa_len)) 451 1.8 jtc 452 1.24 christos static void 453 1.24 christos rt_xaddrs(void *cp, void *cplim, struct rt_addrinfo *rtinfo) 454 1.8 jtc { 455 1.12 lukem struct sockaddr *sa; 456 1.12 lukem int i; 457 1.8 jtc 458 1.24 christos (void)memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info)); 459 1.8 jtc for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) { 460 1.8 jtc if ((rtinfo->rti_addrs & (1 << i)) == 0) 461 1.8 jtc continue; 462 1.8 jtc rtinfo->rti_info[i] = sa = (struct sockaddr *)cp; 463 1.24 christos cp = ADVANCE(cp, sa); 464 1.8 jtc } 465 1.1 cgd } 466 1.1 cgd 467 1.1 cgd /* 468 1.1 cgd * Figure out device configuration and select 469 1.1 cgd * networks which deserve status information. 470 1.1 cgd */ 471 1.24 christos static int 472 1.24 christos configure(int s) 473 1.1 cgd { 474 1.12 lukem struct neighbor *np; 475 1.12 lukem struct if_msghdr *ifm; 476 1.12 lukem struct ifa_msghdr *ifam; 477 1.8 jtc struct sockaddr_dl *sdl; 478 1.8 jtc size_t needed; 479 1.8 jtc int mib[6], flags = 0, len; 480 1.8 jtc char *buf, *lim, *next; 481 1.8 jtc struct rt_addrinfo info; 482 1.24 christos struct sockaddr_in dstaddr; 483 1.8 jtc 484 1.8 jtc mib[0] = CTL_NET; 485 1.8 jtc mib[1] = PF_ROUTE; 486 1.8 jtc mib[2] = 0; 487 1.8 jtc mib[3] = AF_INET; 488 1.8 jtc mib[4] = NET_RT_IFLIST; 489 1.8 jtc mib[5] = 0; 490 1.8 jtc if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 491 1.8 jtc quit("route-sysctl-estimate"); 492 1.8 jtc if ((buf = malloc(needed)) == NULL) 493 1.8 jtc quit("malloc"); 494 1.8 jtc if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) 495 1.8 jtc quit("actual retrieval of interface table"); 496 1.8 jtc lim = buf + needed; 497 1.8 jtc 498 1.8 jtc sdl = NULL; /* XXX just to keep gcc -Wall happy */ 499 1.8 jtc for (next = buf; next < lim; next += ifm->ifm_msglen) { 500 1.8 jtc ifm = (struct if_msghdr *)next; 501 1.8 jtc if (ifm->ifm_type == RTM_IFINFO) { 502 1.8 jtc sdl = (struct sockaddr_dl *)(ifm + 1); 503 1.8 jtc flags = ifm->ifm_flags; 504 1.8 jtc continue; 505 1.8 jtc } 506 1.8 jtc if ((flags & IFF_UP) == 0 || 507 1.8 jtc (flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0) 508 1.8 jtc continue; 509 1.8 jtc if (ifm->ifm_type != RTM_NEWADDR) 510 1.8 jtc quit("out of sync parsing NET_RT_IFLIST"); 511 1.8 jtc ifam = (struct ifa_msghdr *)ifm; 512 1.8 jtc info.rti_addrs = ifam->ifam_addrs; 513 1.24 christos rt_xaddrs((ifam + 1), ifam->ifam_msglen + (char *)ifam, &info); 514 1.8 jtc /* gag, wish we could get rid of Internet dependencies */ 515 1.24 christos if (info.rti_info[RTAX_BRD] == NULL || 516 1.24 christos info.rti_info[RTAX_BRD]->sa_family != AF_INET) 517 1.24 christos continue; 518 1.24 christos (void)memcpy(&dstaddr, info.rti_info[RTAX_BRD], 519 1.24 christos sizeof(dstaddr)); 520 1.8 jtc #define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr 521 1.8 jtc #define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port 522 1.24 christos PORT_SA(&dstaddr) = sp->s_port; 523 1.1 cgd for (np = neighbors; np != NULL; np = np->n_next) 524 1.8 jtc if (memcmp(sdl->sdl_data, np->n_name, 525 1.8 jtc sdl->sdl_nlen) == 0 && 526 1.24 christos IPADDR_SA(np->n_addr) == IPADDR_SA(&dstaddr)) 527 1.1 cgd break; 528 1.1 cgd if (np != NULL) 529 1.1 cgd continue; 530 1.24 christos len = sizeof(*np) + dstaddr.sin_len + sdl->sdl_nlen + 1; 531 1.8 jtc np = (struct neighbor *)malloc(len); 532 1.1 cgd if (np == NULL) 533 1.8 jtc quit("malloc of neighbor structure"); 534 1.24 christos (void)memset(np, 0, len); 535 1.8 jtc np->n_flags = flags; 536 1.8 jtc np->n_addr = (struct sockaddr *)(np + 1); 537 1.24 christos np->n_addrlen = dstaddr.sin_len; 538 1.8 jtc np->n_name = np->n_addrlen + (char *)np->n_addr; 539 1.1 cgd np->n_next = neighbors; 540 1.1 cgd neighbors = np; 541 1.24 christos (void)memcpy(np->n_addr, &dstaddr, np->n_addrlen); 542 1.24 christos (void)memcpy(np->n_name, sdl->sdl_data, sdl->sdl_nlen); 543 1.1 cgd } 544 1.8 jtc free(buf); 545 1.1 cgd return (1); 546 1.1 cgd } 547 1.1 cgd 548 1.1 cgd #ifdef DEBUG 549 1.24 christos static ssize_t 550 1.24 christos Sendto(int s, const void *buf, size_t cc, int flags, const struct sockaddr *to, 551 1.24 christos socklen_t tolen) 552 1.1 cgd { 553 1.12 lukem struct whod *w = (struct whod *)buf; 554 1.12 lukem struct whoent *we; 555 1.24 christos struct sockaddr_in *sasin = (struct sockaddr_in *)to; 556 1.30 christos ssize_t ret; 557 1.30 christos 558 1.30 christos ret = sendto(s, buf, cc, flags, to, tolen); 559 1.1 cgd 560 1.28 christos printf("sendto %s.%d\n", inet_ntoa(sasin->sin_addr), 561 1.24 christos ntohs(sasin->sin_port)); 562 1.1 cgd printf("hostname %s %s\n", w->wd_hostname, 563 1.1 cgd interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up")); 564 1.1 cgd printf("load %4.2f, %4.2f, %4.2f\n", 565 1.1 cgd ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0, 566 1.1 cgd ntohl(w->wd_loadav[2]) / 100.0); 567 1.1 cgd cc -= WHDRSIZE; 568 1.8 jtc for (we = w->wd_we, cc /= sizeof(struct whoent); cc > 0; cc--, we++) { 569 1.1 cgd time_t t = ntohl(we->we_utmp.out_time); 570 1.20 christos printf("%-8.8s %s:%s %.12s", we->we_utmp.out_name, 571 1.20 christos w->wd_hostname, we->we_utmp.out_line, ctime(&t)+4); 572 1.1 cgd we->we_idle = ntohl(we->we_idle) / 60; 573 1.1 cgd if (we->we_idle) { 574 1.1 cgd if (we->we_idle >= 100*60) 575 1.1 cgd we->we_idle = 100*60 - 1; 576 1.1 cgd if (we->we_idle >= 60) 577 1.1 cgd printf(" %2d", we->we_idle / 60); 578 1.1 cgd else 579 1.1 cgd printf(" "); 580 1.1 cgd printf(":%02d", we->we_idle % 60); 581 1.1 cgd } 582 1.1 cgd printf("\n"); 583 1.1 cgd } 584 1.30 christos return ret; 585 1.1 cgd } 586 1.1 cgd 587 1.24 christos static char * 588 1.24 christos interval(int time, const char *updown) 589 1.1 cgd { 590 1.1 cgd static char resbuf[32]; 591 1.1 cgd int days, hours, minutes; 592 1.1 cgd 593 1.1 cgd if (time < 0 || time > 3*30*24*60*60) { 594 1.21 itojun (void)snprintf(resbuf, sizeof(resbuf), " %s ??:??", updown); 595 1.1 cgd return (resbuf); 596 1.1 cgd } 597 1.1 cgd minutes = (time + 59) / 60; /* round to minutes */ 598 1.1 cgd hours = minutes / 60; minutes %= 60; 599 1.1 cgd days = hours / 24; hours %= 24; 600 1.1 cgd if (days) 601 1.21 itojun (void)snprintf(resbuf, sizeof(resbuf), "%s %2d+%02d:%02d", 602 1.1 cgd updown, days, hours, minutes); 603 1.1 cgd else 604 1.21 itojun (void)snprintf(resbuf, sizeof(resbuf), "%s %2d:%02d", 605 1.1 cgd updown, hours, minutes); 606 1.24 christos return resbuf; 607 1.1 cgd } 608 1.1 cgd #endif 609 1.28 christos 610 1.31 tsarna static int 611 1.31 tsarna drop_privs(char *newuser) 612 1.31 tsarna { 613 1.31 tsarna struct passwd *pw; 614 1.31 tsarna gid_t gidset[1]; 615 1.31 tsarna 616 1.31 tsarna pw = getpwnam(newuser); 617 1.31 tsarna if (pw == NULL) { 618 1.31 tsarna syslog(LOG_ERR, "no user %.100s", newuser); 619 1.31 tsarna return 0; 620 1.31 tsarna } 621 1.31 tsarna 622 1.31 tsarna endpwent(); 623 1.31 tsarna 624 1.31 tsarna gidset[0] = pw->pw_gid; 625 1.31 tsarna if (setgroups(1, gidset) == -1) { 626 1.31 tsarna syslog(LOG_ERR, "setgroups: %m"); 627 1.31 tsarna return 0; 628 1.31 tsarna } 629 1.31 tsarna 630 1.31 tsarna if (setgid(pw->pw_gid) == -1) { 631 1.31 tsarna syslog(LOG_ERR, "setgid: %m"); 632 1.31 tsarna return 0; 633 1.31 tsarna } 634 1.31 tsarna 635 1.31 tsarna if (setuid(pw->pw_uid) == -1) { 636 1.31 tsarna syslog(LOG_ERR, "setuid: %m"); 637 1.31 tsarna return 0; 638 1.31 tsarna } 639 1.31 tsarna 640 1.31 tsarna return 1; 641 1.31 tsarna } 642 1.31 tsarna 643 1.28 christos static void 644 1.28 christos usage(void) 645 1.28 christos { 646 1.32 wiz (void)fprintf(stderr, "Usage: %s [-i interval] [-u user]\n", getprogname()); 647 1.30 christos exit(EXIT_FAILURE); 648 1.28 christos } 649