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