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