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