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