ruptime.c revision 226419
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 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#ifndef lint
31static const char copyright[] =
32"@(#) Copyright (c) 1983, 1993, 1994\n\
33	The Regents of the University of California.  All rights reserved.\n";
34#endif /* not lint */
35
36#ifndef lint
37static const char sccsid[] = "@(#)ruptime.c	8.2 (Berkeley) 4/5/94";
38#endif /* not lint */
39
40#include <sys/cdefs.h>
41__FBSDID("$FreeBSD: head/usr.bin/ruptime/ruptime.c 226419 2011-10-16 07:36:27Z ed $");
42
43#include <sys/param.h>
44
45#include <protocols/rwhod.h>
46
47#include <dirent.h>
48#include <err.h>
49#include <errno.h>
50#include <fcntl.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <string.h>
54#include <time.h>
55#include <unistd.h>
56
57struct hs {
58	struct	whod hs_wd;
59	int	hs_nusers;
60} *hs;
61#define	LEFTEARTH(h)	(now - (h) > 4*24*60*60)
62#define	ISDOWN(h)	(now - (h)->hs_wd.wd_recvtime > 11 * 60)
63#define	WHDRSIZE	__offsetof(struct whod, wd_we)
64
65size_t nhosts;
66time_t now;
67int rflg = 1;
68DIR *dirp;
69
70int	 hscmp(const void *, const void *);
71char	*interval(time_t, const char *);
72int	 lcmp(const void *, const void *);
73void	 morehosts(void);
74void	 ruptime(const char *, int, int (*)(const void *, const void *));
75int	 tcmp(const void *, const void *);
76int	 ucmp(const void *, const void *);
77void	 usage(void);
78
79int
80main(int argc, char *argv[])
81{
82	int (*cmp)(const void *, const void *);
83	int aflg, ch;
84
85	aflg = 0;
86	cmp = hscmp;
87	while ((ch = getopt(argc, argv, "alrut")) != -1)
88		switch (ch) {
89		case 'a':
90			aflg = 1;
91			break;
92		case 'l':
93			cmp = lcmp;
94			break;
95		case 'r':
96			rflg = -1;
97			break;
98		case 't':
99			cmp = tcmp;
100			break;
101		case 'u':
102			cmp = ucmp;
103			break;
104		default:
105			usage();
106		}
107	argc -= optind;
108	argv += optind;
109
110	if (chdir(_PATH_RWHODIR) || (dirp = opendir(".")) == NULL)
111		err(1, "%s", _PATH_RWHODIR);
112
113	ruptime(*argv, aflg, cmp);
114	while (*argv++ != NULL) {
115		if (*argv == NULL)
116			break;
117		ruptime(*argv, aflg, cmp);
118	}
119	exit(0);
120}
121
122char *
123interval(time_t tval, const char *updown)
124{
125	static char resbuf[32];
126	int days, hours, minutes;
127
128	if (tval < 0) {
129		(void)snprintf(resbuf, sizeof(resbuf), "%s      ??:??", updown);
130		return (resbuf);
131	}
132	/* Round to minutes. */
133	minutes = (tval + (60 - 1)) / 60;
134	hours = minutes / 60;
135	minutes %= 60;
136	days = hours / 24;
137	hours %= 24;
138	if (days)
139		(void)snprintf(resbuf, sizeof(resbuf),
140		    "%s %4d+%02d:%02d", updown, days, hours, minutes);
141	else
142		(void)snprintf(resbuf, sizeof(resbuf),
143		    "%s      %2d:%02d", updown, hours, minutes);
144	return (resbuf);
145}
146
147#define	HS(a)	((const struct hs *)(a))
148
149/* Alphabetical comparison. */
150int
151hscmp(const void *a1, const void *a2)
152{
153	return (rflg *
154	    strcmp(HS(a1)->hs_wd.wd_hostname, HS(a2)->hs_wd.wd_hostname));
155}
156
157/* Load average comparison. */
158int
159lcmp(const void *a1, const void *a2)
160{
161	if (ISDOWN(HS(a1)))
162		if (ISDOWN(HS(a2)))
163			return (tcmp(a1, a2));
164		else
165			return (rflg);
166	else if (ISDOWN(HS(a2)))
167		return (-rflg);
168	else
169		return (rflg *
170		   (HS(a2)->hs_wd.wd_loadav[0] - HS(a1)->hs_wd.wd_loadav[0]));
171}
172
173void
174ruptime(const char *host, int aflg, int (*cmp)(const void *, const void *))
175{
176	struct hs *hsp;
177	struct whod *wd;
178	struct whoent *we;
179	struct dirent *dp;
180	const char *hostname;
181	int fd, i, maxloadav;
182	size_t hspace;
183	ssize_t cc;
184
185	rewinddir(dirp);
186	hsp = NULL;
187	maxloadav = -1;
188	(void)time(&now);
189	for (nhosts = hspace = 0; (dp = readdir(dirp)) != NULL;) {
190		if (dp->d_ino == 0 || strncmp(dp->d_name, "whod.", 5) != 0)
191			continue;
192		if ((fd = open(dp->d_name, O_RDONLY, 0)) < 0) {
193			warn("%s", dp->d_name);
194			continue;
195		}
196
197		if (nhosts == hspace) {
198			if ((hs =
199			    realloc(hs, (hspace += 40) * sizeof(*hs))) == NULL)
200				err(1, NULL);
201			hsp = hs + nhosts;
202		}
203
204		wd = &hsp->hs_wd;
205		cc = read(fd, wd, sizeof(*wd));
206		(void)close(fd);
207		if (cc < (ssize_t)WHDRSIZE)
208			continue;
209
210		if (host != NULL) {
211			hostname = wd->wd_hostname;
212			if (strcasecmp(hostname, host) != 0)
213				continue;
214		}
215		if (LEFTEARTH(wd->wd_recvtime))
216			continue;
217
218		for (i = 0; i < 2; i++)
219			if (wd->wd_loadav[i] > maxloadav)
220				maxloadav = wd->wd_loadav[i];
221
222		for (hsp->hs_nusers = 0, we = &wd->wd_we[0];
223		    (char *)(we + 1) <= (char *)wd + cc; we++)
224			if (aflg || we->we_idle < 3600)
225				++hsp->hs_nusers;
226		++hsp;
227		++nhosts;
228	}
229	if (nhosts == 0) {
230		if (host == NULL)
231			errx(1, "no hosts in %s", _PATH_RWHODIR);
232		else
233			warnx("host %s not in %s", host, _PATH_RWHODIR);
234	}
235
236	qsort(hs, nhosts, sizeof(hs[0]), cmp);
237	for (i = 0; i < (int)nhosts; i++) {
238		hsp = &hs[i];
239		wd = &hsp->hs_wd;
240		if (ISDOWN(hsp)) {
241			(void)printf("%-25.25s%s\n", wd->wd_hostname,
242			    interval(now - hsp->hs_wd.wd_recvtime, "down"));
243			continue;
244		}
245		(void)printf(
246		    "%-25.25s%s,  %4d user%s  load %*.2f, %*.2f, %*.2f\n",
247		    wd->wd_hostname,
248		    interval((time_t)wd->wd_sendtime -
249		        (time_t)wd->wd_boottime, "  up"),
250		    hsp->hs_nusers,
251		    hsp->hs_nusers == 1 ? ", " : "s,",
252		    maxloadav >= 1000 ? 5 : 4,
253		        wd->wd_loadav[0] / 100.0,
254		    maxloadav >= 1000 ? 5 : 4,
255		        wd->wd_loadav[1] / 100.0,
256		    maxloadav >= 1000 ? 5 : 4,
257		        wd->wd_loadav[2] / 100.0);
258	}
259	free(hs);
260	hs = NULL;
261}
262
263/* Number of users comparison. */
264int
265ucmp(const void *a1, const void *a2)
266{
267	if (ISDOWN(HS(a1)))
268		if (ISDOWN(HS(a2)))
269			return (tcmp(a1, a2));
270		else
271			return (rflg);
272	else if (ISDOWN(HS(a2)))
273		return (-rflg);
274	else
275		return (rflg * (HS(a2)->hs_nusers - HS(a1)->hs_nusers));
276}
277
278/* Uptime comparison. */
279int
280tcmp(const void *a1, const void *a2)
281{
282	return (rflg * (
283		(ISDOWN(HS(a2)) ? HS(a2)->hs_wd.wd_recvtime - now
284		    : HS(a2)->hs_wd.wd_sendtime - HS(a2)->hs_wd.wd_boottime)
285		-
286		(ISDOWN(HS(a1)) ? HS(a1)->hs_wd.wd_recvtime - now
287		    : HS(a1)->hs_wd.wd_sendtime - HS(a1)->hs_wd.wd_boottime)
288	));
289}
290
291void
292usage(void)
293{
294	(void)fprintf(stderr, "usage: ruptime [-alrtu] [host ...]\n");
295	exit(1);
296}
297