1 #ifndef lint 2 static char sccsid[] = "@(#)rwhod.c 4.9 83/05/05"; 3 #endif 4 5 #include <sys/types.h> 6 #include <sys/socket.h> 7 #include <sys/stat.h> 8 #include <sys/ioctl.h> 9 10 #include <net/if.h> 11 #include <netinet/in.h> 12 13 #include <nlist.h> 14 #include <stdio.h> 15 #include <signal.h> 16 #include <errno.h> 17 #include <utmp.h> 18 #include <ctype.h> 19 #include <netdb.h> 20 #include <rwhod.h> 21 22 struct sockaddr_in sin = { AF_INET }; 23 24 extern errno; 25 26 char myname[32]; 27 28 struct nlist nl[] = { 29 #define NL_AVENRUN 0 30 { "_avenrun" }, 31 #define NL_BOOTTIME 1 32 { "_boottime" }, 33 0 34 }; 35 36 /* 37 * We communicate with each neighbor in 38 * a list constructed at the time we're 39 * started up. Neighbors are currently 40 * directly connected via a hardware interface. 41 */ 42 struct neighbor { 43 struct neighbor *n_next; 44 char *n_name; /* interface name */ 45 char *n_addr; /* who to send to */ 46 int n_addrlen; /* size of address */ 47 int n_flags; /* should forward?, interface flags */ 48 }; 49 50 struct neighbor *neighbors; 51 struct whod mywd; 52 struct servent *sp; 53 int s, utmpf, kmemf = -1; 54 55 #define WHDRSIZE (sizeof (mywd) - sizeof (mywd.wd_we)) 56 #define RWHODIR "/usr/spool/rwho" 57 58 int onalrm(); 59 char *strcpy(), *sprintf(), *malloc(); 60 long lseek(); 61 int getkmem(); 62 struct in_addr inet_makeaddr(); 63 64 main() 65 { 66 struct sockaddr_in from; 67 char path[64]; 68 int addr; 69 struct hostent *hp; 70 71 sp = getservbyname("who", "udp"); 72 if (sp == 0) { 73 fprintf(stderr, "rwhod: udp/who: unknown service\n"); 74 exit(1); 75 } 76 #ifndef DEBUG 77 if (fork()) 78 exit(0); 79 { int s; 80 for (s = 0; s < 10; s++) 81 (void) close(s); 82 (void) open("/", 0); 83 (void) dup2(0, 1); 84 (void) dup2(0, 2); 85 s = open("/dev/tty", 2); 86 if (s >= 0) { 87 ioctl(s, TIOCNOTTY, 0); 88 (void) close(s); 89 } 90 } 91 #endif 92 (void) chdir("/dev"); 93 (void) signal(SIGHUP, getkmem); 94 if (getuid()) { 95 fprintf(stderr, "rwhod: not super user\n"); 96 exit(1); 97 } 98 /* 99 * Establish host name as returned by system. 100 */ 101 if (gethostname(myname, sizeof (myname) - 1) < 0) { 102 perror("gethostname"); 103 exit(1); 104 } 105 strncpy(mywd.wd_hostname, myname, sizeof (myname) - 1); 106 utmpf = open("/etc/utmp", 0); 107 if (utmpf < 0) { 108 (void) close(creat("/etc/utmp", 0644)); 109 utmpf = open("/etc/utmp", 0); 110 } 111 if (utmpf < 0) { 112 perror("rwhod: /etc/utmp"); 113 exit(1); 114 } 115 getkmem(); 116 if ((s = socket(AF_INET, SOCK_DGRAM, 0, 0)) < 0) { 117 perror("rwhod: socket"); 118 exit(1); 119 } 120 hp = gethostbyname(myname); 121 if (hp == NULL) { 122 fprintf(stderr, "%s: don't know my own name\n", myname); 123 exit(1); 124 } 125 sin.sin_family = hp->h_addrtype; 126 sin.sin_port = sp->s_port; 127 if (bind(s, &sin, sizeof (sin), 0) < 0) { 128 perror("rwhod: bind"); 129 exit(1); 130 } 131 if (!configure(s)) 132 exit(1); 133 sigset(SIGALRM, onalrm); 134 onalrm(); 135 for (;;) { 136 struct whod wd; 137 int cc, whod, len = sizeof (from); 138 139 cc = recvfrom(s, (char *)&wd, sizeof (struct whod), 0, 140 &from, &len); 141 if (cc <= 0) { 142 if (cc < 0 && errno != EINTR) 143 perror("rwhod: recv"); 144 continue; 145 } 146 if (from.sin_port != sp->s_port) { 147 fprintf(stderr, "rwhod: %d: bad from port\n", 148 ntohs(from.sin_port)); 149 continue; 150 } 151 #ifdef notdef 152 if (gethostbyname(wd.wd_hostname) == 0) { 153 fprintf(stderr, "rwhod: %s: unknown host\n", 154 wd.wd_hostname); 155 continue; 156 } 157 #endif 158 if (wd.wd_type != WHODTYPE_STATUS) 159 continue; 160 if (!verify(wd.wd_hostname)) { 161 fprintf(stderr, "rwhod: malformed host name from %x\n", 162 from.sin_addr); 163 continue; 164 } 165 (void) sprintf(path, "%s/whod.%s", RWHODIR, wd.wd_hostname); 166 whod = creat(path, 0666); 167 if (whod < 0) { 168 fprintf(stderr, "rwhod: "); 169 perror(path); 170 continue; 171 } 172 #if vax || pdp11 173 { 174 int i, n = (cc - WHDRSIZE)/sizeof(struct utmp); 175 struct whoent *we; 176 177 /* undo header byte swapping before writing to file */ 178 wd.wd_sendtime = ntohl(wd.wd_sendtime); 179 for (i = 0; i < 3; i++) 180 wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]); 181 wd.wd_boottime = ntohl(wd.wd_boottime); 182 we = wd.wd_we; 183 for (i = 0; i < n; i++) { 184 we->we_idle = ntohl(we->we_idle); 185 we->we_utmp.ut_time = ntohl(we->we_utmp.ut_time); 186 we++; 187 } 188 } 189 #endif 190 (void) time(&wd.wd_recvtime); 191 (void) write(whod, (char *)&wd, cc); 192 (void) close(whod); 193 } 194 } 195 196 /* 197 * Check out host name for unprintables 198 * and other funnies before allowing a file 199 * to be created. Sorry, but blanks aren't allowed. 200 */ 201 verify(name) 202 register char *name; 203 { 204 register int size = 0; 205 206 while (*name) { 207 if (!isascii(*name) || !isalnum(*name)) 208 return (0); 209 name++, size++; 210 } 211 return (size > 0); 212 } 213 214 int utmptime; 215 int utmpent; 216 struct utmp utmp[100]; 217 int alarmcount; 218 219 onalrm() 220 { 221 register int i; 222 struct stat stb; 223 register struct whoent *we = mywd.wd_we, *wlast; 224 int cc; 225 double avenrun[3]; 226 time_t now = time(0); 227 register struct neighbor *np; 228 229 if (alarmcount % 10 == 0) 230 getkmem(); 231 alarmcount++; 232 (void) fstat(utmpf, &stb); 233 if (stb.st_mtime != utmptime) { 234 (void) lseek(utmpf, (long)0, 0); 235 cc = read(utmpf, (char *)utmp, sizeof (utmp)); 236 if (cc < 0) { 237 perror("/etc/utmp"); 238 return; 239 } 240 wlast = &mywd.wd_we[1024 / sizeof (struct whoent) - 1]; 241 utmpent = cc / sizeof (struct utmp); 242 for (i = 0; i < utmpent; i++) 243 if (utmp[i].ut_name[0]) { 244 we->we_utmp = utmp[i]; 245 if (we >= wlast) 246 break; 247 we++; 248 } 249 utmpent = we - mywd.wd_we; 250 } 251 we = mywd.wd_we; 252 for (i = 0; i < utmpent; i++) { 253 if (stat(we->we_utmp.ut_line, &stb) >= 0) 254 we->we_idle = now - stb.st_atime; 255 we++; 256 } 257 (void) lseek(kmemf, (long)nl[NL_AVENRUN].n_value, 0); 258 (void) read(kmemf, (char *)avenrun, sizeof (avenrun)); 259 for (i = 0; i < 3; i++) 260 mywd.wd_loadav[i] = avenrun[i] * 100; 261 cc = (char *)we - (char *)&mywd; 262 (void) time(&mywd.wd_sendtime); 263 #if vax || pdp11 264 mywd.wd_sendtime = htonl(mywd.wd_sendtime); 265 for (i = 0; i < 3; i++) 266 mywd.wd_loadav[i] = htonl(mywd.wd_loadav[i]); 267 mywd.wd_boottime = htonl(mywd.wd_boottime); 268 we = mywd.wd_we; 269 for (i = 0; i < utmpent; i++) { 270 we->we_idle = htonl(we->we_idle); 271 we->we_utmp.ut_time = htonl(we->we_utmp.ut_time); 272 we++; 273 } 274 #endif 275 mywd.wd_vers = WHODVERSION; 276 mywd.wd_type = WHODTYPE_STATUS; 277 for (np = neighbors; np != NULL; np = np->n_next) 278 (void) sendto(s, (char *)&mywd, cc, 0, 279 np->n_addr, np->n_addrlen); 280 (void) alarm(60); 281 } 282 283 getkmem() 284 { 285 struct nlist *nlp; 286 287 signal(SIGHUP, getkmem); 288 if (kmemf >= 0) 289 (void) close(kmemf); 290 loop: 291 for (nlp = &nl[sizeof (nl) / sizeof (nl[0])]; --nlp >= nl; ) { 292 nlp->n_value = 0; 293 nlp->n_type = 0; 294 } 295 nlist("/vmunix", nl); 296 if (nl[0].n_value == 0) { 297 fprintf(stderr, "/vmunix namelist botch\n"); 298 sleep(300); 299 goto loop; 300 } 301 kmemf = open("/dev/kmem", 0); 302 if (kmemf < 0) { 303 perror("/dev/kmem"); 304 sleep(300); 305 goto loop; 306 } 307 (void) lseek(kmemf, (long)nl[NL_BOOTTIME].n_value, 0); 308 (void) read(kmemf, (char *)&mywd.wd_boottime, sizeof (mywd.wd_boottime)); 309 } 310 311 /* 312 * Figure out device configuration and select 313 * networks which deserve status information. 314 */ 315 configure(s) 316 int s; 317 { 318 char buf[BUFSIZ]; 319 struct ifconf ifc; 320 struct ifreq ifreq, *ifr; 321 int n; 322 struct sockaddr_in *sin; 323 register struct neighbor *np; 324 325 ifc.ifc_len = sizeof (buf); 326 ifc.ifc_buf = buf; 327 if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) { 328 perror("rwhod: ioctl (get interface configuration)"); 329 return (0); 330 } 331 ifr = ifc.ifc_req; 332 for (n = ifc.ifc_len / sizeof (struct ifreq); n > 0; n--, ifr++) { 333 for (np = neighbors; np != NULL; np = np->n_next) 334 if (np->n_name && 335 strcmp(ifr->ifr_name, np->n_name) == 0) 336 break; 337 if (np != NULL) 338 continue; 339 ifreq = *ifr; 340 np = (struct neighbor *)malloc(sizeof (*np)); 341 if (np == NULL) 342 continue; 343 np->n_name = malloc(strlen(ifr->ifr_name) + 1); 344 if (np->n_name == NULL) { 345 free((char *)np); 346 continue; 347 } 348 strcpy(np->n_name, ifr->ifr_name); 349 np->n_addrlen = sizeof (ifr->ifr_addr); 350 np->n_addr = malloc(np->n_addrlen); 351 if (np->n_addr == NULL) { 352 free(np->n_name); 353 free((char *)np); 354 continue; 355 } 356 bcopy((char *)&ifr->ifr_addr, np->n_addr, np->n_addrlen); 357 if (ioctl(s, SIOCGIFFLAGS, (char *)&ifreq) < 0) { 358 perror("rwhod: ioctl (get interface flags)"); 359 free((char *)np); 360 continue; 361 } 362 if ((ifreq.ifr_flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0) { 363 free((char *)np); 364 continue; 365 } 366 np->n_flags = ifreq.ifr_flags; 367 if (np->n_flags & IFF_POINTOPOINT) { 368 if (ioctl(s, SIOCGIFDSTADDR, (char *)&ifreq) < 0) { 369 perror("rwhod: ioctl (get dstaddr)"); 370 free((char *)np); 371 continue; 372 } 373 /* we assume addresses are all the same size */ 374 bcopy((char *)&ifreq.ifr_dstaddr, 375 np->n_addr, np->n_addrlen); 376 } 377 if (np->n_flags & IFF_BROADCAST) { 378 /* we assume addresses are all the same size */ 379 sin = (struct sockaddr_in *)np->n_addr; 380 sin->sin_addr = 381 inet_makeaddr(inet_netof(sin->sin_addr), INADDR_ANY); 382 } 383 /* gag, wish we could get rid of Internet dependencies */ 384 sin = (struct sockaddr_in *)np->n_addr; 385 sin->sin_port = sp->s_port; 386 np->n_next = neighbors; 387 neighbors = np; 388 } 389 return (1); 390 } 391