1/*
2 * Copyright (c) 1983, 1993, 1994
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#ifndef lint
35__attribute__((__used__))
36static const char copyright[] =
37"@(#) Copyright (c) 1983, 1993, 1994\n\
38	The Regents of the University of California.  All rights reserved.\n";
39#endif /* not lint */
40
41#ifndef lint
42__attribute__((__used__))
43static const char sccsid[] = "@(#)ruptime.c	8.2 (Berkeley) 4/5/94";
44#endif /* not lint */
45
46#include <sys/cdefs.h>
47__FBSDID("$FreeBSD: src/usr.bin/ruptime/ruptime.c,v 1.19 2008/03/17 18:31:43 antoine Exp $");
48
49#include <sys/param.h>
50
51#include <protocols/rwhod.h>
52
53#include <dirent.h>
54#include <err.h>
55#include <errno.h>
56#include <fcntl.h>
57#include <stdio.h>
58#include <stdlib.h>
59#include <string.h>
60#include <time.h>
61#include <unistd.h>
62
63struct hs {
64	struct	whod *hs_wd;
65	int	hs_nusers;
66} *hs;
67struct	whod awhod;
68#define LEFTEARTH(h)		(now - (h) > 4*24*60*60)
69#define	ISDOWN(h)		(now - (h)->hs_wd->wd_recvtime > 11 * 60)
70#define	WHDRSIZE	(sizeof (awhod) - sizeof (awhod.wd_we))
71
72size_t nhosts;
73time_t now;
74int rflg = 1;
75DIR *dirp;
76
77int	 hscmp(const void *, const void *);
78char	*interval(time_t, const char *);
79int	 lcmp(const void *, const void *);
80void	 morehosts(void);
81void	 ruptime(const char *, int, int (*)(const void *, const void *));
82int	 tcmp(const void *, const void *);
83int	 ucmp(const void *, const void *);
84void	 usage(void);
85
86int
87main(int argc, char *argv[])
88{
89	int (*cmp)(const void *, const void *);
90	int aflg, ch;
91
92	aflg = 0;
93	cmp = hscmp;
94	while ((ch = getopt(argc, argv, "alrut")) != -1)
95		switch (ch) {
96		case 'a':
97			aflg = 1;
98			break;
99		case 'l':
100			cmp = lcmp;
101			break;
102		case 'r':
103			rflg = -1;
104			break;
105		case 't':
106			cmp = tcmp;
107			break;
108		case 'u':
109			cmp = ucmp;
110			break;
111		default:
112			usage();
113		}
114	argc -= optind;
115	argv += optind;
116
117	if (chdir(_PATH_RWHODIR) || (dirp = opendir(".")) == NULL)
118		err(1, "%s", _PATH_RWHODIR);
119
120	ruptime(*argv, aflg, cmp);
121	while (*argv++ != NULL) {
122		if (*argv == NULL)
123			break;
124		ruptime(*argv, aflg, cmp);
125	}
126	exit(0);
127}
128
129char *
130interval(time_t tval, const char *updown)
131{
132	static char resbuf[32];
133	int days, hours, minutes;
134
135	if (tval < 0) {
136		(void)snprintf(resbuf, sizeof(resbuf), "   %s ??:??", updown);
137		return (resbuf);
138	}
139						/* round to minutes. */
140	minutes = (tval + (60 - 1)) / 60;
141	hours = minutes / 60;
142	minutes %= 60;
143	days = hours / 24;
144	hours %= 24;
145	if (days)
146		(void)snprintf(resbuf, sizeof(resbuf),
147		    "%s %3d+%02d:%02d", updown, days, hours, minutes);
148	else
149		(void)snprintf(resbuf, sizeof(resbuf),
150		    "%s     %2d:%02d", updown, hours, minutes);
151	return (resbuf);
152}
153
154#define	HS(a)	((const struct hs *)(a))
155
156/* Alphabetical comparison. */
157int
158hscmp(const void *a1, const void *a2)
159{
160	return (rflg *
161	    strcmp(HS(a1)->hs_wd->wd_hostname, HS(a2)->hs_wd->wd_hostname));
162}
163
164/* Load average comparison. */
165int
166lcmp(const void *a1, const void *a2)
167{
168	if (ISDOWN(HS(a1)))
169		if (ISDOWN(HS(a2)))
170			return (tcmp(a1, a2));
171		else
172			return (rflg);
173	else if (ISDOWN(HS(a2)))
174		return (-rflg);
175	else
176		return (rflg *
177		   (HS(a2)->hs_wd->wd_loadav[0] - HS(a1)->hs_wd->wd_loadav[0]));
178}
179
180void
181ruptime(const char *host, int aflg, int (*cmp)(const void *, const void *))
182{
183	struct hs *hsp;
184	struct whod *wd;
185	struct whoent *we;
186	struct dirent *dp;
187	const char *hostname;
188	char buf[sizeof(struct whod)];
189	int fd, i, maxloadav;
190	size_t hspace;
191	u_int cc;
192
193	rewinddir(dirp);
194	hsp = NULL;
195	maxloadav = -1;
196	(void)time(&now);
197	for (nhosts = hspace = 0; (dp = readdir(dirp)) != NULL;) {
198		if (dp->d_ino == 0 || strncmp(dp->d_name, "whod.", 5) != 0)
199			continue;
200		if ((fd = open(dp->d_name, O_RDONLY, 0)) < 0) {
201			warn("%s", dp->d_name);
202			continue;
203		}
204		cc = read(fd, buf, sizeof(struct whod));
205		(void)close(fd);
206		if (host != NULL) {
207			hostname = ((struct whod *)buf)->wd_hostname;
208			if (strcasecmp(hostname, host) != 0)
209				continue;
210		}
211
212		if (cc < WHDRSIZE)
213			continue;
214		if (LEFTEARTH(((struct whod *)buf)->wd_recvtime))
215			continue;
216		if (nhosts == hspace) {
217			if ((hs =
218			    realloc(hs, (hspace += 40) * sizeof(*hs))) == NULL)
219				err(1, NULL);
220			hsp = hs + nhosts;
221		}
222
223		if ((hsp->hs_wd = malloc((size_t)WHDRSIZE)) == NULL)
224			err(1, NULL);
225		memmove(hsp->hs_wd, buf, (size_t)WHDRSIZE);
226
227		for (wd = (struct whod *)buf, i = 0; i < 2; ++i)
228			if (wd->wd_loadav[i] > maxloadav)
229				maxloadav = wd->wd_loadav[i];
230
231		for (hsp->hs_nusers = 0,
232		    we = (struct whoent *)(buf + cc); --we >= wd->wd_we;)
233			if (aflg || we->we_idle < 3600)
234				++hsp->hs_nusers;
235		++hsp;
236		++nhosts;
237	}
238	if (nhosts == 0) {
239		if (host == NULL)
240			errx(1, "no hosts in %s", _PATH_RWHODIR);
241		else
242			warnx("host %s not in %s", host, _PATH_RWHODIR);
243	}
244
245	qsort(hs, nhosts, sizeof(hs[0]), cmp);
246	for (i = 0; i < (int)nhosts; i++) {
247		hsp = &hs[i];
248		if (ISDOWN(hsp)) {
249			(void)printf("%-12.12s%s\n", hsp->hs_wd->wd_hostname,
250			    interval(now - hsp->hs_wd->wd_recvtime, "down"));
251			continue;
252		}
253		(void)printf(
254		    "%-12.12s%s,  %4d user%s  load %*.2f, %*.2f, %*.2f\n",
255		    hsp->hs_wd->wd_hostname,
256		    interval((time_t)hsp->hs_wd->wd_sendtime -
257			(time_t)hsp->hs_wd->wd_boottime, "  up"),
258		    hsp->hs_nusers,
259		    hsp->hs_nusers == 1 ? ", " : "s,",
260		    maxloadav >= 1000 ? 5 : 4,
261			hsp->hs_wd->wd_loadav[0] / 100.0,
262		    maxloadav >= 1000 ? 5 : 4,
263		        hsp->hs_wd->wd_loadav[1] / 100.0,
264		    maxloadav >= 1000 ? 5 : 4,
265		        hsp->hs_wd->wd_loadav[2] / 100.0);
266		free(hsp->hs_wd);
267	}
268	free(hs);
269	hs = NULL;
270}
271
272/* Number of users comparison. */
273int
274ucmp(const void *a1, const void *a2)
275{
276	if (ISDOWN(HS(a1)))
277		if (ISDOWN(HS(a2)))
278			return (tcmp(a1, a2));
279		else
280			return (rflg);
281	else if (ISDOWN(HS(a2)))
282		return (-rflg);
283	else
284		return (rflg * (HS(a2)->hs_nusers - HS(a1)->hs_nusers));
285}
286
287/* Uptime comparison. */
288int
289tcmp(const void *a1, const void *a2)
290{
291	return (rflg * (
292		(ISDOWN(HS(a2)) ? HS(a2)->hs_wd->wd_recvtime - now
293		    : HS(a2)->hs_wd->wd_sendtime - HS(a2)->hs_wd->wd_boottime)
294		-
295		(ISDOWN(HS(a1)) ? HS(a1)->hs_wd->wd_recvtime - now
296		    : HS(a1)->hs_wd->wd_sendtime - HS(a1)->hs_wd->wd_boottime)
297	));
298}
299
300void
301usage(void)
302{
303	(void)fprintf(stderr, "usage: ruptime [-alrtu] [host ...]\n");
304	exit(1);
305}
306