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