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