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