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