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.26 (Berkeley) 05/21/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], size; 328 struct timeval tm; 329 330 mib[0] = CTL_KERN; 331 mib[1] = KERN_BOOTTIME; 332 size = sizeof(tm); 333 if (sysctl(mib, 2, &tm, &size, NULL, 0) == -1) { 334 syslog(LOG_ERR, "cannot get boottime: %m"); 335 exit(1); 336 } 337 mywd.wd_boottime = htonl(tm.tv_sec); 338 } 339 340 void 341 quit(msg) 342 char *msg; 343 { 344 syslog(LOG_ERR, msg); 345 exit(1); 346 } 347 348 #define ROUNDUP(a) \ 349 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 350 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) 351 352 void 353 rt_xaddrs(cp, cplim, rtinfo) 354 register caddr_t cp, cplim; 355 register struct rt_addrinfo *rtinfo; 356 { 357 register struct sockaddr *sa; 358 register int i; 359 360 memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info)); 361 for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) { 362 if ((rtinfo->rti_addrs & (1 << i)) == 0) 363 continue; 364 rtinfo->rti_info[i] = sa = (struct sockaddr *)cp; 365 ADVANCE(cp, sa); 366 } 367 } 368 369 /* 370 * Figure out device configuration and select 371 * networks which deserve status information. 372 */ 373 int 374 configure(s) 375 int s; 376 { 377 register struct neighbor *np; 378 register struct if_msghdr *ifm; 379 register struct ifa_msghdr *ifam; 380 struct sockaddr_dl *sdl; 381 size_t needed; 382 int mib[6], flags = 0, len; 383 char *buf, *lim, *next; 384 struct rt_addrinfo info; 385 386 mib[0] = CTL_NET; 387 mib[1] = PF_ROUTE; 388 mib[2] = 0; 389 mib[3] = AF_INET; 390 mib[4] = NET_RT_IFLIST; 391 mib[5] = 0; 392 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 393 quit("route-sysctl-estimate"); 394 if ((buf = malloc(needed)) == NULL) 395 quit("malloc"); 396 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) 397 quit("actual retrieval of interface table"); 398 lim = buf + needed; 399 400 sdl = NULL; /* XXX just to keep gcc -Wall happy */ 401 for (next = buf; next < lim; next += ifm->ifm_msglen) { 402 ifm = (struct if_msghdr *)next; 403 if (ifm->ifm_type == RTM_IFINFO) { 404 sdl = (struct sockaddr_dl *)(ifm + 1); 405 flags = ifm->ifm_flags; 406 continue; 407 } 408 if ((flags & IFF_UP) == 0 || 409 (flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0) 410 continue; 411 if (ifm->ifm_type != RTM_NEWADDR) 412 quit("out of sync parsing NET_RT_IFLIST"); 413 ifam = (struct ifa_msghdr *)ifm; 414 info.rti_addrs = ifam->ifam_addrs; 415 rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam, 416 &info); 417 /* gag, wish we could get rid of Internet dependencies */ 418 #define dstaddr info.rti_info[RTAX_BRD] 419 #define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr 420 #define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port 421 if (dstaddr == 0 || dstaddr->sa_family != AF_INET) 422 continue; 423 PORT_SA(dstaddr) = sp->s_port; 424 for (np = neighbors; np != NULL; np = np->n_next) 425 if (memcmp(sdl->sdl_data, np->n_name, 426 sdl->sdl_nlen) == 0 && 427 IPADDR_SA(np->n_addr) == IPADDR_SA(dstaddr)) 428 break; 429 if (np != NULL) 430 continue; 431 len = sizeof(*np) + dstaddr->sa_len + sdl->sdl_nlen + 1; 432 np = (struct neighbor *)malloc(len); 433 if (np == NULL) 434 quit("malloc of neighbor structure"); 435 memset(np, 0, len); 436 np->n_flags = flags; 437 np->n_addr = (struct sockaddr *)(np + 1); 438 np->n_addrlen = dstaddr->sa_len; 439 np->n_name = np->n_addrlen + (char *)np->n_addr; 440 np->n_next = neighbors; 441 neighbors = np; 442 memcpy((char *)np->n_addr, (char *)dstaddr, np->n_addrlen); 443 memcpy(np->n_name, sdl->sdl_data, sdl->sdl_nlen); 444 } 445 free(buf); 446 return (1); 447 } 448 449 #ifdef DEBUG 450 void 451 Sendto(s, buf, cc, flags, to, tolen) 452 int s; 453 char *buf; 454 int cc, flags; 455 char *to; 456 int tolen; 457 { 458 register struct whod *w = (struct whod *)buf; 459 register struct whoent *we; 460 struct sockaddr_in *sin = (struct sockaddr_in *)to; 461 462 printf("sendto %x.%d\n", ntohl(sin->sin_addr), ntohs(sin->sin_port)); 463 printf("hostname %s %s\n", w->wd_hostname, 464 interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up")); 465 printf("load %4.2f, %4.2f, %4.2f\n", 466 ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0, 467 ntohl(w->wd_loadav[2]) / 100.0); 468 cc -= WHDRSIZE; 469 for (we = w->wd_we, cc /= sizeof(struct whoent); cc > 0; cc--, we++) { 470 time_t t = ntohl(we->we_utmp.out_time); 471 printf("%-8.8s %s:%s %.12s", 472 we->we_utmp.out_name, 473 w->wd_hostname, we->we_utmp.out_line, 474 ctime(&t)+4); 475 we->we_idle = ntohl(we->we_idle) / 60; 476 if (we->we_idle) { 477 if (we->we_idle >= 100*60) 478 we->we_idle = 100*60 - 1; 479 if (we->we_idle >= 60) 480 printf(" %2d", we->we_idle / 60); 481 else 482 printf(" "); 483 printf(":%02d", we->we_idle % 60); 484 } 485 printf("\n"); 486 } 487 } 488 489 char * 490 interval(time, updown) 491 int time; 492 char *updown; 493 { 494 static char resbuf[32]; 495 int days, hours, minutes; 496 497 if (time < 0 || time > 3*30*24*60*60) { 498 (void) sprintf(resbuf, " %s ??:??", updown); 499 return (resbuf); 500 } 501 minutes = (time + 59) / 60; /* round to minutes */ 502 hours = minutes / 60; minutes %= 60; 503 days = hours / 24; hours %= 24; 504 if (days) 505 (void) sprintf(resbuf, "%s %2d+%02d:%02d", 506 updown, days, hours, minutes); 507 else 508 (void) sprintf(resbuf, "%s %2d:%02d", 509 updown, hours, minutes); 510 return (resbuf); 511 } 512 #endif 513