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