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