16460Swnj #ifndef lint 2*15541Sralph static char sccsid[] = "@(#)rwhod.c 4.21 (Berkeley) 83/11/14"; 36460Swnj #endif 46460Swnj 59215Ssam #include <sys/types.h> 69215Ssam #include <sys/socket.h> 79215Ssam #include <sys/stat.h> 89215Ssam #include <sys/ioctl.h> 913187Ssam #include <sys/file.h> 109215Ssam 1112236Ssam #include <net/if.h> 129215Ssam #include <netinet/in.h> 139215Ssam 149215Ssam #include <nlist.h> 156460Swnj #include <stdio.h> 166460Swnj #include <signal.h> 176460Swnj #include <errno.h> 186460Swnj #include <utmp.h> 198486Ssam #include <ctype.h> 208486Ssam #include <netdb.h> 2113567Ssam #include "rwhod.h" 226460Swnj 23*15541Sralph /* 24*15541Sralph * Alarm interval. Don't forget to change the down time check in ruptime 25*15541Sralph * if this is changed. 26*15541Sralph */ 27*15541Sralph #define AL_INTERVAL (5 * 60) 28*15541Sralph 298486Ssam struct sockaddr_in sin = { AF_INET }; 306460Swnj 316460Swnj extern errno; 326460Swnj 3312236Ssam char myname[32]; 346460Swnj 356460Swnj struct nlist nl[] = { 366460Swnj #define NL_AVENRUN 0 376460Swnj { "_avenrun" }, 389215Ssam #define NL_BOOTTIME 1 399215Ssam { "_boottime" }, 406460Swnj 0 416460Swnj }; 426460Swnj 4312236Ssam /* 4412236Ssam * We communicate with each neighbor in 4512236Ssam * a list constructed at the time we're 4612236Ssam * started up. Neighbors are currently 4712236Ssam * directly connected via a hardware interface. 4812236Ssam */ 4912236Ssam struct neighbor { 5012236Ssam struct neighbor *n_next; 5112236Ssam char *n_name; /* interface name */ 5212236Ssam char *n_addr; /* who to send to */ 5312236Ssam int n_addrlen; /* size of address */ 5412236Ssam int n_flags; /* should forward?, interface flags */ 5512236Ssam }; 5612236Ssam 5712236Ssam struct neighbor *neighbors; 586460Swnj struct whod mywd; 5912236Ssam struct servent *sp; 606460Swnj int s, utmpf, kmemf = -1; 616460Swnj 6212236Ssam #define WHDRSIZE (sizeof (mywd) - sizeof (mywd.wd_we)) 6312236Ssam #define RWHODIR "/usr/spool/rwho" 6411834Ssam 656460Swnj int onalrm(); 6612236Ssam char *strcpy(), *sprintf(), *malloc(); 676460Swnj long lseek(); 686460Swnj int getkmem(); 6912236Ssam struct in_addr inet_makeaddr(); 706460Swnj 716460Swnj main() 726460Swnj { 736460Swnj struct sockaddr_in from; 746460Swnj char path[64]; 756460Swnj int addr; 7612236Ssam struct hostent *hp; 776460Swnj 788486Ssam sp = getservbyname("who", "udp"); 798486Ssam if (sp == 0) { 808486Ssam fprintf(stderr, "rwhod: udp/who: unknown service\n"); 818486Ssam exit(1); 828486Ssam } 836460Swnj #ifndef DEBUG 846460Swnj if (fork()) 856460Swnj exit(0); 866460Swnj { int s; 876460Swnj for (s = 0; s < 10; s++) 886460Swnj (void) close(s); 896460Swnj (void) open("/", 0); 906460Swnj (void) dup2(0, 1); 916460Swnj (void) dup2(0, 2); 926460Swnj s = open("/dev/tty", 2); 936460Swnj if (s >= 0) { 946460Swnj ioctl(s, TIOCNOTTY, 0); 956460Swnj (void) close(s); 966460Swnj } 976460Swnj } 986460Swnj #endif 996460Swnj (void) chdir("/dev"); 1006460Swnj (void) signal(SIGHUP, getkmem); 1016460Swnj if (getuid()) { 1028486Ssam fprintf(stderr, "rwhod: not super user\n"); 1036460Swnj exit(1); 1046460Swnj } 10512236Ssam /* 10612236Ssam * Establish host name as returned by system. 10712236Ssam */ 10812236Ssam if (gethostname(myname, sizeof (myname) - 1) < 0) { 10912236Ssam perror("gethostname"); 1106460Swnj exit(1); 1116460Swnj } 11212236Ssam strncpy(mywd.wd_hostname, myname, sizeof (myname) - 1); 11313187Ssam utmpf = open("/etc/utmp", O_RDONLY); 1146460Swnj if (utmpf < 0) { 1156460Swnj (void) close(creat("/etc/utmp", 0644)); 11613187Ssam utmpf = open("/etc/utmp", O_RDONLY); 1176460Swnj } 1186460Swnj if (utmpf < 0) { 1198486Ssam perror("rwhod: /etc/utmp"); 1206460Swnj exit(1); 1216460Swnj } 1226460Swnj getkmem(); 12313187Ssam if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 1248486Ssam perror("rwhod: socket"); 1259215Ssam exit(1); 1266460Swnj } 12712236Ssam hp = gethostbyname(myname); 12812236Ssam if (hp == NULL) { 12912236Ssam fprintf(stderr, "%s: don't know my own name\n", myname); 13012236Ssam exit(1); 13112236Ssam } 13212236Ssam sin.sin_family = hp->h_addrtype; 13312236Ssam sin.sin_port = sp->s_port; 13413187Ssam if (bind(s, &sin, sizeof (sin)) < 0) { 1359215Ssam perror("rwhod: bind"); 1369215Ssam exit(1); 1379215Ssam } 13812236Ssam if (!configure(s)) 13912236Ssam exit(1); 14013027Ssam signal(SIGALRM, onalrm); 1416460Swnj onalrm(); 1426460Swnj for (;;) { 1436460Swnj struct whod wd; 14412236Ssam int cc, whod, len = sizeof (from); 1456460Swnj 14612236Ssam cc = recvfrom(s, (char *)&wd, sizeof (struct whod), 0, 14712236Ssam &from, &len); 1486460Swnj if (cc <= 0) { 1496460Swnj if (cc < 0 && errno != EINTR) 1509215Ssam perror("rwhod: recv"); 1516460Swnj continue; 1526460Swnj } 1538486Ssam if (from.sin_port != sp->s_port) { 1548486Ssam fprintf(stderr, "rwhod: %d: bad from port\n", 1558486Ssam ntohs(from.sin_port)); 1566460Swnj continue; 1576460Swnj } 1588486Ssam #ifdef notdef 1598486Ssam if (gethostbyname(wd.wd_hostname) == 0) { 1608486Ssam fprintf(stderr, "rwhod: %s: unknown host\n", 1618486Ssam wd.wd_hostname); 1626460Swnj continue; 1636460Swnj } 1648486Ssam #endif 16512347Ssam if (wd.wd_vers != WHODVERSION) 16612347Ssam continue; 16712236Ssam if (wd.wd_type != WHODTYPE_STATUS) 16812236Ssam continue; 1698486Ssam if (!verify(wd.wd_hostname)) { 1708486Ssam fprintf(stderr, "rwhod: malformed host name from %x\n", 1718486Ssam from.sin_addr); 1728486Ssam continue; 1738486Ssam } 1749914Ssam (void) sprintf(path, "%s/whod.%s", RWHODIR, wd.wd_hostname); 1756460Swnj whod = creat(path, 0666); 1766460Swnj if (whod < 0) { 1778486Ssam fprintf(stderr, "rwhod: "); 1788486Ssam perror(path); 1796460Swnj continue; 1806460Swnj } 18111834Ssam #if vax || pdp11 18211834Ssam { 18313187Ssam int i, n = (cc - WHDRSIZE)/sizeof(struct whoent); 18411834Ssam struct whoent *we; 18511834Ssam 18611834Ssam /* undo header byte swapping before writing to file */ 18711834Ssam wd.wd_sendtime = ntohl(wd.wd_sendtime); 18811834Ssam for (i = 0; i < 3; i++) 18911834Ssam wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]); 19011834Ssam wd.wd_boottime = ntohl(wd.wd_boottime); 19111834Ssam we = wd.wd_we; 19211834Ssam for (i = 0; i < n; i++) { 19311834Ssam we->we_idle = ntohl(we->we_idle); 19412872Ssam we->we_utmp.out_time = 19512872Ssam ntohl(we->we_utmp.out_time); 19611834Ssam we++; 19711834Ssam } 19811834Ssam } 19911834Ssam #endif 2006460Swnj (void) time(&wd.wd_recvtime); 2016460Swnj (void) write(whod, (char *)&wd, cc); 2026460Swnj (void) close(whod); 2036460Swnj } 2046460Swnj } 2056460Swnj 2068486Ssam /* 2078486Ssam * Check out host name for unprintables 2088486Ssam * and other funnies before allowing a file 2098486Ssam * to be created. Sorry, but blanks aren't allowed. 2108486Ssam */ 2118486Ssam verify(name) 2128486Ssam register char *name; 2138486Ssam { 2148486Ssam register int size = 0; 2158486Ssam 2168486Ssam while (*name) { 21715214Sleres if (!isascii(*name) || !(isalnum(*name) || ispunct(*name))) 2188486Ssam return (0); 2198486Ssam name++, size++; 2208486Ssam } 2218486Ssam return (size > 0); 2228486Ssam } 2238486Ssam 2246460Swnj int utmptime; 2256460Swnj int utmpent; 2266577Ssam struct utmp utmp[100]; 2276460Swnj int alarmcount; 2286460Swnj 2296460Swnj onalrm() 2306460Swnj { 2316460Swnj register int i; 2326460Swnj struct stat stb; 2336577Ssam register struct whoent *we = mywd.wd_we, *wlast; 2346460Swnj int cc; 2356460Swnj double avenrun[3]; 2366460Swnj time_t now = time(0); 23712236Ssam register struct neighbor *np; 2386460Swnj 2396460Swnj if (alarmcount % 10 == 0) 2406460Swnj getkmem(); 2416460Swnj alarmcount++; 2426460Swnj (void) fstat(utmpf, &stb); 2436460Swnj if (stb.st_mtime != utmptime) { 24413187Ssam (void) lseek(utmpf, (long)0, L_SET); 2456460Swnj cc = read(utmpf, (char *)utmp, sizeof (utmp)); 2466460Swnj if (cc < 0) { 2476460Swnj perror("/etc/utmp"); 24813469Ssam goto done; 2496460Swnj } 25012236Ssam wlast = &mywd.wd_we[1024 / sizeof (struct whoent) - 1]; 2516460Swnj utmpent = cc / sizeof (struct utmp); 2526460Swnj for (i = 0; i < utmpent; i++) 2536460Swnj if (utmp[i].ut_name[0]) { 25412807Ssam bcopy(utmp[i].ut_line, we->we_utmp.out_line, 25512807Ssam sizeof (utmp[i].ut_line)); 25612807Ssam bcopy(utmp[i].ut_name, we->we_utmp.out_name, 25712807Ssam sizeof (utmp[i].ut_name)); 25812807Ssam we->we_utmp.out_time = htonl(utmp[i].ut_time); 2596577Ssam if (we >= wlast) 2606577Ssam break; 2616460Swnj we++; 2626460Swnj } 2636460Swnj utmpent = we - mywd.wd_we; 2646460Swnj } 2656460Swnj we = mywd.wd_we; 2666460Swnj for (i = 0; i < utmpent; i++) { 26712807Ssam if (stat(we->we_utmp.out_line, &stb) >= 0) 26812807Ssam we->we_idle = htonl(now - stb.st_atime); 2696460Swnj we++; 2706460Swnj } 27113187Ssam (void) lseek(kmemf, (long)nl[NL_AVENRUN].n_value, L_SET); 2726460Swnj (void) read(kmemf, (char *)avenrun, sizeof (avenrun)); 2736460Swnj for (i = 0; i < 3; i++) 27412873Ssam mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100)); 2756460Swnj cc = (char *)we - (char *)&mywd; 27612807Ssam mywd.wd_sendtime = htonl(time(0)); 27712236Ssam mywd.wd_vers = WHODVERSION; 27812236Ssam mywd.wd_type = WHODTYPE_STATUS; 27912236Ssam for (np = neighbors; np != NULL; np = np->n_next) 28012236Ssam (void) sendto(s, (char *)&mywd, cc, 0, 28112236Ssam np->n_addr, np->n_addrlen); 28213469Ssam done: 283*15541Sralph (void) alarm(AL_INTERVAL); 2846460Swnj } 2856460Swnj 2866460Swnj getkmem() 2876460Swnj { 2886460Swnj struct nlist *nlp; 2896460Swnj 2906460Swnj if (kmemf >= 0) 2916460Swnj (void) close(kmemf); 2926460Swnj loop: 2936460Swnj for (nlp = &nl[sizeof (nl) / sizeof (nl[0])]; --nlp >= nl; ) { 2946460Swnj nlp->n_value = 0; 2956460Swnj nlp->n_type = 0; 2966460Swnj } 2976460Swnj nlist("/vmunix", nl); 2986460Swnj if (nl[0].n_value == 0) { 2996460Swnj fprintf(stderr, "/vmunix namelist botch\n"); 3006460Swnj sleep(300); 3016460Swnj goto loop; 3026460Swnj } 30313187Ssam kmemf = open("/dev/kmem", O_RDONLY); 3046460Swnj if (kmemf < 0) { 3056460Swnj perror("/dev/kmem"); 3066460Swnj sleep(300); 3076460Swnj goto loop; 3086460Swnj } 30913187Ssam (void) lseek(kmemf, (long)nl[NL_BOOTTIME].n_value, L_SET); 31013187Ssam (void) read(kmemf, (char *)&mywd.wd_boottime, 31113187Ssam sizeof (mywd.wd_boottime)); 31212807Ssam mywd.wd_boottime = htonl(mywd.wd_boottime); 3136460Swnj } 31412236Ssam 31512236Ssam /* 31612236Ssam * Figure out device configuration and select 31712236Ssam * networks which deserve status information. 31812236Ssam */ 31912236Ssam configure(s) 32012236Ssam int s; 32112236Ssam { 32212236Ssam char buf[BUFSIZ]; 32312236Ssam struct ifconf ifc; 32412236Ssam struct ifreq ifreq, *ifr; 32512236Ssam struct sockaddr_in *sin; 32612236Ssam register struct neighbor *np; 32713187Ssam int n; 32812236Ssam 32912236Ssam ifc.ifc_len = sizeof (buf); 33012236Ssam ifc.ifc_buf = buf; 33112236Ssam if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) { 33212236Ssam perror("rwhod: ioctl (get interface configuration)"); 33312236Ssam return (0); 33412236Ssam } 33512236Ssam ifr = ifc.ifc_req; 33612236Ssam for (n = ifc.ifc_len / sizeof (struct ifreq); n > 0; n--, ifr++) { 33712236Ssam for (np = neighbors; np != NULL; np = np->n_next) 33812236Ssam if (np->n_name && 33912236Ssam strcmp(ifr->ifr_name, np->n_name) == 0) 34012236Ssam break; 34112236Ssam if (np != NULL) 34212236Ssam continue; 34312236Ssam ifreq = *ifr; 34412236Ssam np = (struct neighbor *)malloc(sizeof (*np)); 34512236Ssam if (np == NULL) 34612236Ssam continue; 34712236Ssam np->n_name = malloc(strlen(ifr->ifr_name) + 1); 34812236Ssam if (np->n_name == NULL) { 34912236Ssam free((char *)np); 35012236Ssam continue; 35112236Ssam } 35212236Ssam strcpy(np->n_name, ifr->ifr_name); 35312236Ssam np->n_addrlen = sizeof (ifr->ifr_addr); 35412236Ssam np->n_addr = malloc(np->n_addrlen); 35512236Ssam if (np->n_addr == NULL) { 35612236Ssam free(np->n_name); 35712236Ssam free((char *)np); 35812236Ssam continue; 35912236Ssam } 36012236Ssam bcopy((char *)&ifr->ifr_addr, np->n_addr, np->n_addrlen); 36112236Ssam if (ioctl(s, SIOCGIFFLAGS, (char *)&ifreq) < 0) { 36212236Ssam perror("rwhod: ioctl (get interface flags)"); 36312236Ssam free((char *)np); 36412236Ssam continue; 36512236Ssam } 36612236Ssam if ((ifreq.ifr_flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0) { 36712236Ssam free((char *)np); 36812236Ssam continue; 36912236Ssam } 37012236Ssam np->n_flags = ifreq.ifr_flags; 37112236Ssam if (np->n_flags & IFF_POINTOPOINT) { 37212236Ssam if (ioctl(s, SIOCGIFDSTADDR, (char *)&ifreq) < 0) { 37312236Ssam perror("rwhod: ioctl (get dstaddr)"); 37412236Ssam free((char *)np); 37512236Ssam continue; 37612236Ssam } 37712236Ssam /* we assume addresses are all the same size */ 37812236Ssam bcopy((char *)&ifreq.ifr_dstaddr, 37912236Ssam np->n_addr, np->n_addrlen); 38012236Ssam } 38112236Ssam if (np->n_flags & IFF_BROADCAST) { 38212236Ssam /* we assume addresses are all the same size */ 38312236Ssam sin = (struct sockaddr_in *)np->n_addr; 38412236Ssam sin->sin_addr = 38512236Ssam inet_makeaddr(inet_netof(sin->sin_addr), INADDR_ANY); 38612236Ssam } 38712236Ssam /* gag, wish we could get rid of Internet dependencies */ 38812236Ssam sin = (struct sockaddr_in *)np->n_addr; 38912236Ssam sin->sin_port = sp->s_port; 39012236Ssam np->n_next = neighbors; 39112236Ssam neighbors = np; 39212236Ssam } 39312236Ssam return (1); 39412236Ssam } 39512807Ssam 39612807Ssam #ifdef DEBUG 39712807Ssam sendto(s, buf, cc, flags, to, tolen) 39812807Ssam int s; 39912807Ssam char *buf; 40012807Ssam int cc, flags; 40112807Ssam char *to; 40212807Ssam int tolen; 40312807Ssam { 40412807Ssam register struct whod *w = (struct whod *)buf; 40512807Ssam register struct whoent *we; 40612807Ssam struct sockaddr_in *sin = (struct sockaddr_in *)to; 40712807Ssam char *interval(); 40812807Ssam 40912807Ssam printf("sendto %x.%d\n", ntohl(sin->sin_addr), ntohs(sin->sin_port)); 41012807Ssam printf("hostname %s %s\n", w->wd_hostname, 41113187Ssam interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up")); 41212807Ssam printf("load %4.2f, %4.2f, %4.2f\n", 41313187Ssam ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0, 41413187Ssam ntohl(w->wd_loadav[2]) / 100.0); 41512807Ssam cc -= WHDRSIZE; 41612807Ssam for (we = w->wd_we, cc /= sizeof (struct whoent); cc > 0; cc--, we++) { 41713187Ssam time_t t = ntohl(we->we_utmp.out_time); 41812807Ssam printf("%-8.8s %s:%s %.12s", 41913187Ssam we->we_utmp.out_name, 42013187Ssam w->wd_hostname, we->we_utmp.out_line, 42113187Ssam ctime(&t)+4); 42213187Ssam we->we_idle = ntohl(we->we_idle) / 60; 42312807Ssam if (we->we_idle) { 42412807Ssam if (we->we_idle >= 100*60) 42512807Ssam we->we_idle = 100*60 - 1; 42612807Ssam if (we->we_idle >= 60) 42712807Ssam printf(" %2d", we->we_idle / 60); 42812807Ssam else 42912807Ssam printf(" "); 43012807Ssam printf(":%02d", we->we_idle % 60); 43112807Ssam } 43212807Ssam printf("\n"); 43312807Ssam } 43412807Ssam } 43512807Ssam 43612807Ssam char * 43712807Ssam interval(time, updown) 43812807Ssam int time; 43912807Ssam char *updown; 44012807Ssam { 44112807Ssam static char resbuf[32]; 44212807Ssam int days, hours, minutes; 44312807Ssam 44412807Ssam if (time < 0 || time > 3*30*24*60*60) { 44512807Ssam (void) sprintf(resbuf, " %s ??:??", updown); 44612807Ssam return (resbuf); 44712807Ssam } 44812807Ssam minutes = (time + 59) / 60; /* round to minutes */ 44912807Ssam hours = minutes / 60; minutes %= 60; 45012807Ssam days = hours / 24; hours %= 24; 45112807Ssam if (days) 45212807Ssam (void) sprintf(resbuf, "%s %2d+%02d:%02d", 45312807Ssam updown, days, hours, minutes); 45412807Ssam else 45512807Ssam (void) sprintf(resbuf, "%s %2d:%02d", 45612807Ssam updown, hours, minutes); 45712807Ssam return (resbuf); 45812807Ssam } 45912807Ssam #endif 460