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