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