xref: /original-bsd/usr.sbin/rwhod/rwhod.c (revision 264c46cb)
1 #ifndef lint
2 static char sccsid[] = "@(#)rwhod.c	4.18 (Berkeley) 83/06/30";
3 #endif
4 
5 #include <sys/types.h>
6 #include <sys/socket.h>
7 #include <sys/stat.h>
8 #include <sys/ioctl.h>
9 #include <sys/file.h>
10 
11 #include <net/if.h>
12 #include <netinet/in.h>
13 
14 #include <nlist.h>
15 #include <stdio.h>
16 #include <signal.h>
17 #include <errno.h>
18 #include <utmp.h>
19 #include <ctype.h>
20 #include <netdb.h>
21 #include <rwhod.h>
22 
23 struct	sockaddr_in sin = { AF_INET };
24 
25 extern	errno;
26 
27 char	myname[32];
28 
29 struct	nlist nl[] = {
30 #define	NL_AVENRUN	0
31 	{ "_avenrun" },
32 #define	NL_BOOTTIME	1
33 	{ "_boottime" },
34 	0
35 };
36 
37 /*
38  * We communicate with each neighbor in
39  * a list constructed at the time we're
40  * started up.  Neighbors are currently
41  * directly connected via a hardware interface.
42  */
43 struct	neighbor {
44 	struct	neighbor *n_next;
45 	char	*n_name;		/* interface name */
46 	char	*n_addr;		/* who to send to */
47 	int	n_addrlen;		/* size of address */
48 	int	n_flags;		/* should forward?, interface flags */
49 };
50 
51 struct	neighbor *neighbors;
52 struct	whod mywd;
53 struct	servent *sp;
54 int	s, utmpf, kmemf = -1;
55 
56 #define	WHDRSIZE	(sizeof (mywd) - sizeof (mywd.wd_we))
57 #define	RWHODIR		"/usr/spool/rwho"
58 
59 int	onalrm();
60 char	*strcpy(), *sprintf(), *malloc();
61 long	lseek();
62 int	getkmem();
63 struct	in_addr inet_makeaddr();
64 
65 main()
66 {
67 	struct sockaddr_in from;
68 	char path[64];
69 	int addr;
70 	struct hostent *hp;
71 
72 	sp = getservbyname("who", "udp");
73 	if (sp == 0) {
74 		fprintf(stderr, "rwhod: udp/who: unknown service\n");
75 		exit(1);
76 	}
77 #ifndef DEBUG
78 	if (fork())
79 		exit(0);
80 	{ int s;
81 	  for (s = 0; s < 10; s++)
82 		(void) close(s);
83 	  (void) open("/", 0);
84 	  (void) dup2(0, 1);
85 	  (void) dup2(0, 2);
86 	  s = open("/dev/tty", 2);
87 	  if (s >= 0) {
88 		ioctl(s, TIOCNOTTY, 0);
89 		(void) close(s);
90 	  }
91 	}
92 #endif
93 	(void) chdir("/dev");
94 	(void) signal(SIGHUP, getkmem);
95 	if (getuid()) {
96 		fprintf(stderr, "rwhod: not super user\n");
97 		exit(1);
98 	}
99 	/*
100 	 * Establish host name as returned by system.
101 	 */
102 	if (gethostname(myname, sizeof (myname) - 1) < 0) {
103 		perror("gethostname");
104 		exit(1);
105 	}
106 	strncpy(mywd.wd_hostname, myname, sizeof (myname) - 1);
107 	utmpf = open("/etc/utmp", O_RDONLY);
108 	if (utmpf < 0) {
109 		(void) close(creat("/etc/utmp", 0644));
110 		utmpf = open("/etc/utmp", O_RDONLY);
111 	}
112 	if (utmpf < 0) {
113 		perror("rwhod: /etc/utmp");
114 		exit(1);
115 	}
116 	getkmem();
117 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
118 		perror("rwhod: socket");
119 		exit(1);
120 	}
121 	hp = gethostbyname(myname);
122 	if (hp == NULL) {
123 		fprintf(stderr, "%s: don't know my own name\n", myname);
124 		exit(1);
125 	}
126 	sin.sin_family = hp->h_addrtype;
127 	sin.sin_port = sp->s_port;
128 	if (bind(s, &sin, sizeof (sin)) < 0) {
129 		perror("rwhod: bind");
130 		exit(1);
131 	}
132 	if (!configure(s))
133 		exit(1);
134 	signal(SIGALRM, onalrm);
135 	onalrm();
136 	for (;;) {
137 		struct whod wd;
138 		int cc, whod, len = sizeof (from);
139 
140 		cc = recvfrom(s, (char *)&wd, sizeof (struct whod), 0,
141 			&from, &len);
142 		if (cc <= 0) {
143 			if (cc < 0 && errno != EINTR)
144 				perror("rwhod: recv");
145 			continue;
146 		}
147 		if (from.sin_port != sp->s_port) {
148 			fprintf(stderr, "rwhod: %d: bad from port\n",
149 				ntohs(from.sin_port));
150 			continue;
151 		}
152 #ifdef notdef
153 		if (gethostbyname(wd.wd_hostname) == 0) {
154 			fprintf(stderr, "rwhod: %s: unknown host\n",
155 				wd.wd_hostname);
156 			continue;
157 		}
158 #endif
159 		if (wd.wd_vers != WHODVERSION)
160 			continue;
161 		if (wd.wd_type != WHODTYPE_STATUS)
162 			continue;
163 		if (!verify(wd.wd_hostname)) {
164 			fprintf(stderr, "rwhod: malformed host name from %x\n",
165 				from.sin_addr);
166 			continue;
167 		}
168 		(void) sprintf(path, "%s/whod.%s", RWHODIR, wd.wd_hostname);
169 		whod = creat(path, 0666);
170 		if (whod < 0) {
171 			fprintf(stderr, "rwhod: ");
172 			perror(path);
173 			continue;
174 		}
175 #if vax || pdp11
176 		{
177 			int i, n = (cc - WHDRSIZE)/sizeof(struct whoent);
178 			struct whoent *we;
179 
180 			/* undo header byte swapping before writing to file */
181 			wd.wd_sendtime = ntohl(wd.wd_sendtime);
182 			for (i = 0; i < 3; i++)
183 				wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]);
184 			wd.wd_boottime = ntohl(wd.wd_boottime);
185 			we = wd.wd_we;
186 			for (i = 0; i < n; i++) {
187 				we->we_idle = ntohl(we->we_idle);
188 				we->we_utmp.out_time =
189 				    ntohl(we->we_utmp.out_time);
190 				we++;
191 			}
192 		}
193 #endif
194 		(void) time(&wd.wd_recvtime);
195 		(void) write(whod, (char *)&wd, cc);
196 		(void) close(whod);
197 	}
198 }
199 
200 /*
201  * Check out host name for unprintables
202  * and other funnies before allowing a file
203  * to be created.  Sorry, but blanks aren't allowed.
204  */
205 verify(name)
206 	register char *name;
207 {
208 	register int size = 0;
209 
210 	while (*name) {
211 		if (!isascii(*name) || !isalnum(*name))
212 			return (0);
213 		name++, size++;
214 	}
215 	return (size > 0);
216 }
217 
218 int	utmptime;
219 int	utmpent;
220 struct	utmp utmp[100];
221 int	alarmcount;
222 
223 onalrm()
224 {
225 	register int i;
226 	struct stat stb;
227 	register struct whoent *we = mywd.wd_we, *wlast;
228 	int cc;
229 	double avenrun[3];
230 	time_t now = time(0);
231 	register struct neighbor *np;
232 
233 	if (alarmcount % 10 == 0)
234 		getkmem();
235 	alarmcount++;
236 	(void) fstat(utmpf, &stb);
237 	if (stb.st_mtime != utmptime) {
238 		(void) lseek(utmpf, (long)0, L_SET);
239 		cc = read(utmpf, (char *)utmp, sizeof (utmp));
240 		if (cc < 0) {
241 			perror("/etc/utmp");
242 			goto done;
243 		}
244 		wlast = &mywd.wd_we[1024 / sizeof (struct whoent) - 1];
245 		utmpent = cc / sizeof (struct utmp);
246 		for (i = 0; i < utmpent; i++)
247 			if (utmp[i].ut_name[0]) {
248 				bcopy(utmp[i].ut_line, we->we_utmp.out_line,
249 				   sizeof (utmp[i].ut_line));
250 				bcopy(utmp[i].ut_name, we->we_utmp.out_name,
251 				   sizeof (utmp[i].ut_name));
252 				we->we_utmp.out_time = htonl(utmp[i].ut_time);
253 				if (we >= wlast)
254 					break;
255 				we++;
256 			}
257 		utmpent = we - mywd.wd_we;
258 	}
259 	we = mywd.wd_we;
260 	for (i = 0; i < utmpent; i++) {
261 		if (stat(we->we_utmp.out_line, &stb) >= 0)
262 			we->we_idle = htonl(now - stb.st_atime);
263 		we++;
264 	}
265 	(void) lseek(kmemf, (long)nl[NL_AVENRUN].n_value, L_SET);
266 	(void) read(kmemf, (char *)avenrun, sizeof (avenrun));
267 	for (i = 0; i < 3; i++)
268 		mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100));
269 	cc = (char *)we - (char *)&mywd;
270 	mywd.wd_sendtime = htonl(time(0));
271 	mywd.wd_vers = WHODVERSION;
272 	mywd.wd_type = WHODTYPE_STATUS;
273 	for (np = neighbors; np != NULL; np = np->n_next)
274 		(void) sendto(s, (char *)&mywd, cc, 0,
275 			np->n_addr, np->n_addrlen);
276 done:
277 	(void) alarm(60);
278 }
279 
280 getkmem()
281 {
282 	struct nlist *nlp;
283 
284 	if (kmemf >= 0)
285 		(void) close(kmemf);
286 loop:
287 	for (nlp = &nl[sizeof (nl) / sizeof (nl[0])]; --nlp >= nl; ) {
288 		nlp->n_value = 0;
289 		nlp->n_type = 0;
290 	}
291 	nlist("/vmunix", nl);
292 	if (nl[0].n_value == 0) {
293 		fprintf(stderr, "/vmunix namelist botch\n");
294 		sleep(300);
295 		goto loop;
296 	}
297 	kmemf = open("/dev/kmem", O_RDONLY);
298 	if (kmemf < 0) {
299 		perror("/dev/kmem");
300 		sleep(300);
301 		goto loop;
302 	}
303 	(void) lseek(kmemf, (long)nl[NL_BOOTTIME].n_value, L_SET);
304 	(void) read(kmemf, (char *)&mywd.wd_boottime,
305 	    sizeof (mywd.wd_boottime));
306 	mywd.wd_boottime = htonl(mywd.wd_boottime);
307 }
308 
309 /*
310  * Figure out device configuration and select
311  * networks which deserve status information.
312  */
313 configure(s)
314 	int s;
315 {
316 	char buf[BUFSIZ];
317 	struct ifconf ifc;
318 	struct ifreq ifreq, *ifr;
319 	struct sockaddr_in *sin;
320 	register struct neighbor *np;
321 	int n;
322 
323 	ifc.ifc_len = sizeof (buf);
324 	ifc.ifc_buf = buf;
325 	if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
326 		perror("rwhod: ioctl (get interface configuration)");
327 		return (0);
328 	}
329 	ifr = ifc.ifc_req;
330 	for (n = ifc.ifc_len / sizeof (struct ifreq); n > 0; n--, ifr++) {
331 		for (np = neighbors; np != NULL; np = np->n_next)
332 			if (np->n_name &&
333 			    strcmp(ifr->ifr_name, np->n_name) == 0)
334 				break;
335 		if (np != NULL)
336 			continue;
337 		ifreq = *ifr;
338 		np = (struct neighbor *)malloc(sizeof (*np));
339 		if (np == NULL)
340 			continue;
341 		np->n_name = malloc(strlen(ifr->ifr_name) + 1);
342 		if (np->n_name == NULL) {
343 			free((char *)np);
344 			continue;
345 		}
346 		strcpy(np->n_name, ifr->ifr_name);
347 		np->n_addrlen = sizeof (ifr->ifr_addr);
348 		np->n_addr = malloc(np->n_addrlen);
349 		if (np->n_addr == NULL) {
350 			free(np->n_name);
351 			free((char *)np);
352 			continue;
353 		}
354 		bcopy((char *)&ifr->ifr_addr, np->n_addr, np->n_addrlen);
355 		if (ioctl(s, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
356 			perror("rwhod: ioctl (get interface flags)");
357 			free((char *)np);
358 			continue;
359 		}
360 		if ((ifreq.ifr_flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0) {
361 			free((char *)np);
362 			continue;
363 		}
364 		np->n_flags = ifreq.ifr_flags;
365 		if (np->n_flags & IFF_POINTOPOINT) {
366 			if (ioctl(s, SIOCGIFDSTADDR, (char *)&ifreq) < 0) {
367 				perror("rwhod: ioctl (get dstaddr)");
368 				free((char *)np);
369 				continue;
370 			}
371 			/* we assume addresses are all the same size */
372 			bcopy((char *)&ifreq.ifr_dstaddr,
373 			  np->n_addr, np->n_addrlen);
374 		}
375 		if (np->n_flags & IFF_BROADCAST) {
376 			/* we assume addresses are all the same size */
377 			sin = (struct sockaddr_in *)np->n_addr;
378 			sin->sin_addr =
379 			  inet_makeaddr(inet_netof(sin->sin_addr), INADDR_ANY);
380 		}
381 		/* gag, wish we could get rid of Internet dependencies */
382 		sin = (struct sockaddr_in *)np->n_addr;
383 		sin->sin_port = sp->s_port;
384 		np->n_next = neighbors;
385 		neighbors = np;
386 	}
387 	return (1);
388 }
389 
390 #ifdef DEBUG
391 sendto(s, buf, cc, flags, to, tolen)
392 	int s;
393 	char *buf;
394 	int cc, flags;
395 	char *to;
396 	int tolen;
397 {
398 	register struct whod *w = (struct whod *)buf;
399 	register struct whoent *we;
400 	struct sockaddr_in *sin = (struct sockaddr_in *)to;
401 	char *interval();
402 
403 	printf("sendto %x.%d\n", ntohl(sin->sin_addr), ntohs(sin->sin_port));
404 	printf("hostname %s %s\n", w->wd_hostname,
405 	   interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), "  up"));
406 	printf("load %4.2f, %4.2f, %4.2f\n",
407 	    ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0,
408 	    ntohl(w->wd_loadav[2]) / 100.0);
409 	cc -= WHDRSIZE;
410 	for (we = w->wd_we, cc /= sizeof (struct whoent); cc > 0; cc--, we++) {
411 		time_t t = ntohl(we->we_utmp.out_time);
412 		printf("%-8.8s %s:%s %.12s",
413 			we->we_utmp.out_name,
414 			w->wd_hostname, we->we_utmp.out_line,
415 			ctime(&t)+4);
416 		we->we_idle = ntohl(we->we_idle) / 60;
417 		if (we->we_idle) {
418 			if (we->we_idle >= 100*60)
419 				we->we_idle = 100*60 - 1;
420 			if (we->we_idle >= 60)
421 				printf(" %2d", we->we_idle / 60);
422 			else
423 				printf("   ");
424 			printf(":%02d", we->we_idle % 60);
425 		}
426 		printf("\n");
427 	}
428 }
429 
430 char *
431 interval(time, updown)
432 	int time;
433 	char *updown;
434 {
435 	static char resbuf[32];
436 	int days, hours, minutes;
437 
438 	if (time < 0 || time > 3*30*24*60*60) {
439 		(void) sprintf(resbuf, "   %s ??:??", updown);
440 		return (resbuf);
441 	}
442 	minutes = (time + 59) / 60;		/* round to minutes */
443 	hours = minutes / 60; minutes %= 60;
444 	days = hours / 24; hours %= 24;
445 	if (days)
446 		(void) sprintf(resbuf, "%s %2d+%02d:%02d",
447 		    updown, days, hours, minutes);
448 	else
449 		(void) sprintf(resbuf, "%s    %2d:%02d",
450 		    updown, hours, minutes);
451 	return (resbuf);
452 }
453 #endif
454