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