1 /* 2 * Copyright (c) 1983 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 char copyright[] = 10 "@(#) Copyright (c) 1983 The Regents of the University of California.\n\ 11 All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)rwhod.c 5.25 (Berkeley) 05/18/93"; 16 #endif /* not lint */ 17 18 #include <sys/param.h> 19 #include <sys/socket.h> 20 #include <sys/stat.h> 21 #include <sys/signal.h> 22 #include <sys/ioctl.h> 23 #include <sys/sysctl.h> 24 25 #include <net/if.h> 26 #include <net/if_dl.h> 27 #include <net/route.h> 28 #include <netinet/in.h> 29 #include <protocols/rwhod.h> 30 31 #include <ctype.h> 32 #include <errno.h> 33 #include <fcntl.h> 34 #include <netdb.h> 35 #include <nlist.h> 36 #include <paths.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <syslog.h> 41 #include <unistd.h> 42 #include <utmp.h> 43 44 /* 45 * Alarm interval. Don't forget to change the down time check in ruptime 46 * if this is changed. 47 */ 48 #define AL_INTERVAL (3 * 60) 49 50 char myname[MAXHOSTNAMELEN]; 51 52 struct nlist nl[] = { 53 #define NL_BOOTTIME 0 54 { "_boottime" }, 55 { 0 } 56 }; 57 58 /* 59 * We communicate with each neighbor in a list constructed at the time we're 60 * started up. Neighbors are currently directly connected via a hardware 61 * interface. 62 */ 63 struct neighbor { 64 struct neighbor *n_next; 65 char *n_name; /* interface name */ 66 struct sockaddr *n_addr; /* who to send to */ 67 int n_addrlen; /* size of address */ 68 int n_flags; /* should forward?, interface flags */ 69 }; 70 71 struct neighbor *neighbors; 72 struct whod mywd; 73 struct servent *sp; 74 int s, utmpf, kmemf = -1; 75 76 #define WHDRSIZE (sizeof(mywd) - sizeof(mywd.wd_we)) 77 78 int configure __P((int)); 79 void getkmem __P((int)); 80 void onalrm __P((int)); 81 void quit __P((char *)); 82 void rt_xaddrs __P((caddr_t, caddr_t, struct rt_addrinfo *)); 83 int verify __P((char *)); 84 #ifdef DEBUG 85 char *interval __P((int, char *)); 86 void Sendto __P((int, char *, int, int, char *, int)); 87 #define sendto Sendto 88 #endif 89 90 int 91 main(argc, argv) 92 int argc; 93 char argv[]; 94 { 95 struct sockaddr_in from; 96 struct stat st; 97 char path[64]; 98 int on = 1; 99 char *cp; 100 struct sockaddr_in sin; 101 102 if (getuid()) { 103 fprintf(stderr, "rwhod: not super user\n"); 104 exit(1); 105 } 106 sp = getservbyname("who", "udp"); 107 if (sp == NULL) { 108 fprintf(stderr, "rwhod: udp/who: unknown service\n"); 109 exit(1); 110 } 111 #ifndef DEBUG 112 daemon(1, 0); 113 #endif 114 if (chdir(_PATH_RWHODIR) < 0) { 115 (void)fprintf(stderr, "rwhod: %s: %s\n", 116 _PATH_RWHODIR, strerror(errno)); 117 exit(1); 118 } 119 (void) signal(SIGHUP, getkmem); 120 openlog("rwhod", LOG_PID, LOG_DAEMON); 121 /* 122 * Establish host name as returned by system. 123 */ 124 if (gethostname(myname, sizeof(myname) - 1) < 0) { 125 syslog(LOG_ERR, "gethostname: %m"); 126 exit(1); 127 } 128 if ((cp = index(myname, '.')) != NULL) 129 *cp = '\0'; 130 strncpy(mywd.wd_hostname, myname, sizeof(myname) - 1); 131 utmpf = open(_PATH_UTMP, O_RDONLY|O_CREAT, 0644); 132 if (utmpf < 0) { 133 syslog(LOG_ERR, "%s: %m", _PATH_UTMP); 134 exit(1); 135 } 136 getkmem(0); 137 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 138 syslog(LOG_ERR, "socket: %m"); 139 exit(1); 140 } 141 if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { 142 syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m"); 143 exit(1); 144 } 145 memset(&sin, 0, sizeof(sin)); 146 sin.sin_family = AF_INET; 147 sin.sin_port = sp->s_port; 148 if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 149 syslog(LOG_ERR, "bind: %m"); 150 exit(1); 151 } 152 if (!configure(s)) 153 exit(1); 154 signal(SIGALRM, onalrm); 155 onalrm(0); 156 for (;;) { 157 struct whod wd; 158 int cc, whod, len = sizeof(from); 159 160 cc = recvfrom(s, (char *)&wd, sizeof(struct whod), 0, 161 (struct sockaddr *)&from, &len); 162 if (cc <= 0) { 163 if (cc < 0 && errno != EINTR) 164 syslog(LOG_WARNING, "recv: %m"); 165 continue; 166 } 167 if (from.sin_port != sp->s_port) { 168 syslog(LOG_WARNING, "%d: bad from port", 169 ntohs(from.sin_port)); 170 continue; 171 } 172 if (wd.wd_vers != WHODVERSION) 173 continue; 174 if (wd.wd_type != WHODTYPE_STATUS) 175 continue; 176 if (!verify(wd.wd_hostname)) { 177 syslog(LOG_WARNING, "malformed host name from %x", 178 from.sin_addr); 179 continue; 180 } 181 (void) sprintf(path, "whod.%s", wd.wd_hostname); 182 /* 183 * Rather than truncating and growing the file each time, 184 * use ftruncate if size is less than previous size. 185 */ 186 whod = open(path, O_WRONLY | O_CREAT, 0644); 187 if (whod < 0) { 188 syslog(LOG_WARNING, "%s: %m", path); 189 continue; 190 } 191 #if ENDIAN != BIG_ENDIAN 192 { 193 int i, n = (cc - WHDRSIZE)/sizeof(struct whoent); 194 struct whoent *we; 195 196 /* undo header byte swapping before writing to file */ 197 wd.wd_sendtime = ntohl(wd.wd_sendtime); 198 for (i = 0; i < 3; i++) 199 wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]); 200 wd.wd_boottime = ntohl(wd.wd_boottime); 201 we = wd.wd_we; 202 for (i = 0; i < n; i++) { 203 we->we_idle = ntohl(we->we_idle); 204 we->we_utmp.out_time = 205 ntohl(we->we_utmp.out_time); 206 we++; 207 } 208 } 209 #endif 210 (void) time((time_t *)&wd.wd_recvtime); 211 (void) write(whod, (char *)&wd, cc); 212 if (fstat(whod, &st) < 0 || st.st_size > cc) 213 ftruncate(whod, cc); 214 (void) close(whod); 215 } 216 } 217 218 /* 219 * Check out host name for unprintables 220 * and other funnies before allowing a file 221 * to be created. Sorry, but blanks aren't allowed. 222 */ 223 int 224 verify(name) 225 register char *name; 226 { 227 register int size = 0; 228 229 while (*name) { 230 if (!isascii(*name) || !(isalnum(*name) || ispunct(*name))) 231 return (0); 232 name++, size++; 233 } 234 return (size > 0); 235 } 236 237 int utmptime; 238 int utmpent; 239 int utmpsize = 0; 240 struct utmp *utmp; 241 int alarmcount; 242 243 void 244 onalrm(signo) 245 int signo; 246 { 247 register struct neighbor *np; 248 register struct whoent *we = mywd.wd_we, *wlast; 249 register int i; 250 struct stat stb; 251 double avenrun[3]; 252 time_t now; 253 int cc; 254 255 now = time(NULL); 256 if (alarmcount % 10 == 0) 257 getkmem(0); 258 alarmcount++; 259 (void) fstat(utmpf, &stb); 260 if ((stb.st_mtime != utmptime) || (stb.st_size > utmpsize)) { 261 utmptime = stb.st_mtime; 262 if (stb.st_size > utmpsize) { 263 utmpsize = stb.st_size + 10 * sizeof(struct utmp); 264 if (utmp) 265 utmp = (struct utmp *)realloc(utmp, utmpsize); 266 else 267 utmp = (struct utmp *)malloc(utmpsize); 268 if (! utmp) { 269 fprintf(stderr, "rwhod: malloc failed\n"); 270 utmpsize = 0; 271 goto done; 272 } 273 } 274 (void) lseek(utmpf, (off_t)0, L_SET); 275 cc = read(utmpf, (char *)utmp, stb.st_size); 276 if (cc < 0) { 277 fprintf(stderr, "rwhod: %s: %s\n", 278 _PATH_UTMP, strerror(errno)); 279 goto done; 280 } 281 wlast = &mywd.wd_we[1024 / sizeof(struct whoent) - 1]; 282 utmpent = cc / sizeof(struct utmp); 283 for (i = 0; i < utmpent; i++) 284 if (utmp[i].ut_name[0]) { 285 memcpy(we->we_utmp.out_line, utmp[i].ut_line, 286 sizeof(utmp[i].ut_line)); 287 memcpy(we->we_utmp.out_name, utmp[i].ut_name, 288 sizeof(utmp[i].ut_name)); 289 we->we_utmp.out_time = htonl(utmp[i].ut_time); 290 if (we >= wlast) 291 break; 292 we++; 293 } 294 utmpent = we - mywd.wd_we; 295 } 296 297 /* 298 * The test on utmpent looks silly---after all, if no one is 299 * logged on, why worry about efficiency?---but is useful on 300 * (e.g.) compute servers. 301 */ 302 if (utmpent && chdir(_PATH_DEV)) { 303 syslog(LOG_ERR, "chdir(%s): %m", _PATH_DEV); 304 exit(1); 305 } 306 we = mywd.wd_we; 307 for (i = 0; i < utmpent; i++) { 308 if (stat(we->we_utmp.out_line, &stb) >= 0) 309 we->we_idle = htonl(now - stb.st_atime); 310 we++; 311 } 312 (void)getloadavg(avenrun, sizeof(avenrun)/sizeof(avenrun[0])); 313 for (i = 0; i < 3; i++) 314 mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100)); 315 cc = (char *)we - (char *)&mywd; 316 mywd.wd_sendtime = htonl(time(0)); 317 mywd.wd_vers = WHODVERSION; 318 mywd.wd_type = WHODTYPE_STATUS; 319 for (np = neighbors; np != NULL; np = np->n_next) 320 (void)sendto(s, (char *)&mywd, cc, 0, 321 np->n_addr, np->n_addrlen); 322 if (utmpent && chdir(_PATH_RWHODIR)) { 323 syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR); 324 exit(1); 325 } 326 done: 327 (void) alarm(AL_INTERVAL); 328 } 329 330 void 331 getkmem(signo) 332 int signo; 333 { 334 static ino_t vmunixino; 335 static time_t vmunixctime; 336 struct stat sb; 337 338 if (stat(_PATH_UNIX, &sb) < 0) { 339 if (vmunixctime) 340 return; 341 } else { 342 if (sb.st_ctime == vmunixctime && sb.st_ino == vmunixino) 343 return; 344 vmunixctime = sb.st_ctime; 345 vmunixino = sb.st_ino; 346 } 347 if (kmemf >= 0) 348 (void)close(kmemf); 349 loop: 350 if (nlist(_PATH_UNIX, nl)) { 351 syslog(LOG_WARNING, "%s: namelist botch", _PATH_UNIX); 352 sleep(300); 353 goto loop; 354 } 355 kmemf = open(_PATH_KMEM, O_RDONLY, 0); 356 if (kmemf < 0) { 357 syslog(LOG_ERR, "%s: %m", _PATH_KMEM); 358 exit(1); 359 } 360 (void) lseek(kmemf, (off_t)nl[NL_BOOTTIME].n_value, L_SET); 361 (void) read(kmemf, (char *)&mywd.wd_boottime, 362 sizeof(mywd.wd_boottime)); 363 mywd.wd_boottime = htonl(mywd.wd_boottime); 364 } 365 366 void 367 quit(msg) 368 char *msg; 369 { 370 syslog(LOG_ERR, msg); 371 exit(1); 372 } 373 374 #define ROUNDUP(a) \ 375 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 376 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) 377 378 void 379 rt_xaddrs(cp, cplim, rtinfo) 380 register caddr_t cp, cplim; 381 register struct rt_addrinfo *rtinfo; 382 { 383 register struct sockaddr *sa; 384 register int i; 385 386 memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info)); 387 for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) { 388 if ((rtinfo->rti_addrs & (1 << i)) == 0) 389 continue; 390 rtinfo->rti_info[i] = sa = (struct sockaddr *)cp; 391 ADVANCE(cp, sa); 392 } 393 } 394 395 /* 396 * Figure out device configuration and select 397 * networks which deserve status information. 398 */ 399 int 400 configure(s) 401 int s; 402 { 403 register struct neighbor *np; 404 register struct if_msghdr *ifm; 405 register struct ifa_msghdr *ifam; 406 struct sockaddr_dl *sdl; 407 size_t needed; 408 int mib[6], flags = 0, len; 409 char *buf, *lim, *next; 410 struct rt_addrinfo info; 411 412 mib[0] = CTL_NET; 413 mib[1] = PF_ROUTE; 414 mib[2] = 0; 415 mib[3] = AF_INET; 416 mib[4] = NET_RT_IFLIST; 417 mib[5] = 0; 418 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 419 quit("route-sysctl-estimate"); 420 if ((buf = malloc(needed)) == NULL) 421 quit("malloc"); 422 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) 423 quit("actual retrieval of interface table"); 424 lim = buf + needed; 425 426 sdl = NULL; /* XXX just to keep gcc -Wall happy */ 427 for (next = buf; next < lim; next += ifm->ifm_msglen) { 428 ifm = (struct if_msghdr *)next; 429 if (ifm->ifm_type == RTM_IFINFO) { 430 sdl = (struct sockaddr_dl *)(ifm + 1); 431 flags = ifm->ifm_flags; 432 continue; 433 } 434 if ((flags & IFF_UP) == 0 || 435 (flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0) 436 continue; 437 if (ifm->ifm_type != RTM_NEWADDR) 438 quit("out of sync parsing NET_RT_IFLIST"); 439 ifam = (struct ifa_msghdr *)ifm; 440 info.rti_addrs = ifam->ifam_addrs; 441 rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam, 442 &info); 443 /* gag, wish we could get rid of Internet dependencies */ 444 #define dstaddr info.rti_info[RTAX_BRD] 445 #define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr 446 #define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port 447 if (dstaddr == 0 || dstaddr->sa_family != AF_INET) 448 continue; 449 PORT_SA(dstaddr) = sp->s_port; 450 for (np = neighbors; np != NULL; np = np->n_next) 451 if (memcmp(sdl->sdl_data, np->n_name, 452 sdl->sdl_nlen) == 0 && 453 IPADDR_SA(np->n_addr) == IPADDR_SA(dstaddr)) 454 break; 455 if (np != NULL) 456 continue; 457 len = sizeof(*np) + dstaddr->sa_len + sdl->sdl_nlen + 1; 458 np = (struct neighbor *)malloc(len); 459 if (np == NULL) 460 quit("malloc of neighbor structure"); 461 memset(np, 0, len); 462 np->n_flags = flags; 463 np->n_addr = (struct sockaddr *)(np + 1); 464 np->n_addrlen = dstaddr->sa_len; 465 np->n_name = np->n_addrlen + (char *)np->n_addr; 466 np->n_next = neighbors; 467 neighbors = np; 468 memcpy((char *)np->n_addr, (char *)dstaddr, np->n_addrlen); 469 memcpy(np->n_name, sdl->sdl_data, sdl->sdl_nlen); 470 } 471 free(buf); 472 return (1); 473 } 474 475 #ifdef DEBUG 476 void 477 Sendto(s, buf, cc, flags, to, tolen) 478 int s; 479 char *buf; 480 int cc, flags; 481 char *to; 482 int tolen; 483 { 484 register struct whod *w = (struct whod *)buf; 485 register struct whoent *we; 486 struct sockaddr_in *sin = (struct sockaddr_in *)to; 487 488 printf("sendto %x.%d\n", ntohl(sin->sin_addr), ntohs(sin->sin_port)); 489 printf("hostname %s %s\n", w->wd_hostname, 490 interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up")); 491 printf("load %4.2f, %4.2f, %4.2f\n", 492 ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0, 493 ntohl(w->wd_loadav[2]) / 100.0); 494 cc -= WHDRSIZE; 495 for (we = w->wd_we, cc /= sizeof(struct whoent); cc > 0; cc--, we++) { 496 time_t t = ntohl(we->we_utmp.out_time); 497 printf("%-8.8s %s:%s %.12s", 498 we->we_utmp.out_name, 499 w->wd_hostname, we->we_utmp.out_line, 500 ctime(&t)+4); 501 we->we_idle = ntohl(we->we_idle) / 60; 502 if (we->we_idle) { 503 if (we->we_idle >= 100*60) 504 we->we_idle = 100*60 - 1; 505 if (we->we_idle >= 60) 506 printf(" %2d", we->we_idle / 60); 507 else 508 printf(" "); 509 printf(":%02d", we->we_idle % 60); 510 } 511 printf("\n"); 512 } 513 } 514 515 char * 516 interval(time, updown) 517 int time; 518 char *updown; 519 { 520 static char resbuf[32]; 521 int days, hours, minutes; 522 523 if (time < 0 || time > 3*30*24*60*60) { 524 (void) sprintf(resbuf, " %s ??:??", updown); 525 return (resbuf); 526 } 527 minutes = (time + 59) / 60; /* round to minutes */ 528 hours = minutes / 60; minutes %= 60; 529 days = hours / 24; hours %= 24; 530 if (days) 531 (void) sprintf(resbuf, "%s %2d+%02d:%02d", 532 updown, days, hours, minutes); 533 else 534 (void) sprintf(resbuf, "%s %2d:%02d", 535 updown, hours, minutes); 536 return (resbuf); 537 } 538 #endif 539