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