xref: /freebsd/usr.bin/ruptime/ruptime.c (revision 0b8224d1)
19b50d902SRodney W. Grimes /*
28a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni  *
49b50d902SRodney W. Grimes  * Copyright (c) 1983, 1993, 1994
59b50d902SRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
69b50d902SRodney W. Grimes  *
79b50d902SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
89b50d902SRodney W. Grimes  * modification, are permitted provided that the following conditions
99b50d902SRodney W. Grimes  * are met:
109b50d902SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
119b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
129b50d902SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
139b50d902SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
149b50d902SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
15fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
169b50d902SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
179b50d902SRodney W. Grimes  *    without specific prior written permission.
189b50d902SRodney W. Grimes  *
199b50d902SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
209b50d902SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
219b50d902SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
229b50d902SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
239b50d902SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
249b50d902SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
259b50d902SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
269b50d902SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
279b50d902SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
289b50d902SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
299b50d902SRodney W. Grimes  * SUCH DAMAGE.
309b50d902SRodney W. Grimes  */
319b50d902SRodney W. Grimes 
329b50d902SRodney W. Grimes #include <sys/param.h>
339b50d902SRodney W. Grimes 
349b50d902SRodney W. Grimes #include <protocols/rwhod.h>
359b50d902SRodney W. Grimes 
369b50d902SRodney W. Grimes #include <dirent.h>
379b50d902SRodney W. Grimes #include <err.h>
38821df508SXin LI #include <errno.h>
399b50d902SRodney W. Grimes #include <fcntl.h>
409b50d902SRodney W. Grimes #include <stdio.h>
419b50d902SRodney W. Grimes #include <stdlib.h>
429b50d902SRodney W. Grimes #include <string.h>
439b50d902SRodney W. Grimes #include <time.h>
449b50d902SRodney W. Grimes #include <unistd.h>
459b50d902SRodney W. Grimes 
4659880300SEd Schouten static struct hs {
4773b018e3SEd Schouten 	struct	whod hs_wd;
489b50d902SRodney W. Grimes 	int	hs_nusers;
499b50d902SRodney W. Grimes } *hs;
5002dfd2b2SSuleiman Souhlal #define	LEFTEARTH(h)	(now - (h) > 4*24*60*60)
5173b018e3SEd Schouten #define	ISDOWN(h)	(now - (h)->hs_wd.wd_recvtime > 11 * 60)
5273b018e3SEd Schouten #define	WHDRSIZE	__offsetof(struct whod, wd_we)
539b50d902SRodney W. Grimes 
5459880300SEd Schouten static size_t nhosts;
5559880300SEd Schouten static time_t now;
5659880300SEd Schouten static int rflg = 1;
5759880300SEd Schouten static DIR *dirp;
589b50d902SRodney W. Grimes 
5959880300SEd Schouten static int	 hscmp(const void *, const void *);
6059880300SEd Schouten static char	*interval(time_t, const char *);
61e245deadSBruce Evans static int	 iwidth(int);
6259880300SEd Schouten static int	 lcmp(const void *, const void *);
6359880300SEd Schouten static void	 ruptime(const char *, int, int (*)(const void *, const void *));
6459880300SEd Schouten static int	 tcmp(const void *, const void *);
6559880300SEd Schouten static int	 ucmp(const void *, const void *);
6659880300SEd Schouten static void	 usage(void);
679b50d902SRodney W. Grimes 
689b50d902SRodney W. Grimes int
main(int argc,char * argv[])69a827060aSMark Murray main(int argc, char *argv[])
709b50d902SRodney W. Grimes {
71d3cb5dedSWarner Losh 	int (*cmp)(const void *, const void *);
72e7cb5ef7SJuli Mallett 	int aflg, ch;
739b50d902SRodney W. Grimes 
749b50d902SRodney W. Grimes 	aflg = 0;
759b50d902SRodney W. Grimes 	cmp = hscmp;
761c8af878SWarner Losh 	while ((ch = getopt(argc, argv, "alrut")) != -1)
779b50d902SRodney W. Grimes 		switch (ch) {
789b50d902SRodney W. Grimes 		case 'a':
799b50d902SRodney W. Grimes 			aflg = 1;
809b50d902SRodney W. Grimes 			break;
819b50d902SRodney W. Grimes 		case 'l':
829b50d902SRodney W. Grimes 			cmp = lcmp;
839b50d902SRodney W. Grimes 			break;
849b50d902SRodney W. Grimes 		case 'r':
859b50d902SRodney W. Grimes 			rflg = -1;
869b50d902SRodney W. Grimes 			break;
879b50d902SRodney W. Grimes 		case 't':
889b50d902SRodney W. Grimes 			cmp = tcmp;
899b50d902SRodney W. Grimes 			break;
909b50d902SRodney W. Grimes 		case 'u':
919b50d902SRodney W. Grimes 			cmp = ucmp;
929b50d902SRodney W. Grimes 			break;
939b50d902SRodney W. Grimes 		default:
949b50d902SRodney W. Grimes 			usage();
959b50d902SRodney W. Grimes 		}
969b50d902SRodney W. Grimes 	argc -= optind;
979b50d902SRodney W. Grimes 	argv += optind;
989b50d902SRodney W. Grimes 
999b50d902SRodney W. Grimes 	if (chdir(_PATH_RWHODIR) || (dirp = opendir(".")) == NULL)
1009b50d902SRodney W. Grimes 		err(1, "%s", _PATH_RWHODIR);
1019b50d902SRodney W. Grimes 
102e7cb5ef7SJuli Mallett 	ruptime(*argv, aflg, cmp);
103e7cb5ef7SJuli Mallett 	while (*argv++ != NULL) {
104e7cb5ef7SJuli Mallett 		if (*argv == NULL)
105e7cb5ef7SJuli Mallett 			break;
106e7cb5ef7SJuli Mallett 		ruptime(*argv, aflg, cmp);
1079b50d902SRodney W. Grimes 	}
1089b50d902SRodney W. Grimes 	exit(0);
1099b50d902SRodney W. Grimes }
1109b50d902SRodney W. Grimes 
11159880300SEd Schouten static char *
interval(time_t tval,const char * updown)112a827060aSMark Murray interval(time_t tval, const char *updown)
1139b50d902SRodney W. Grimes {
1149b50d902SRodney W. Grimes 	static char resbuf[32];
1159b50d902SRodney W. Grimes 	int days, hours, minutes;
1169b50d902SRodney W. Grimes 
117fdb7c7dcSJoerg Wunsch 	if (tval < 0) {
1189b50d902SRodney W. Grimes 		(void)snprintf(resbuf, sizeof(resbuf), "%s      ??:??", updown);
1199b50d902SRodney W. Grimes 		return (resbuf);
1209b50d902SRodney W. Grimes 	}
12173b018e3SEd Schouten 	/* Round to minutes. */
122656dcd43SGarrett Wollman 	minutes = (tval + (60 - 1)) / 60;
123656dcd43SGarrett Wollman 	hours = minutes / 60;
124656dcd43SGarrett Wollman 	minutes %= 60;
125656dcd43SGarrett Wollman 	days = hours / 24;
126656dcd43SGarrett Wollman 	hours %= 24;
1279b50d902SRodney W. Grimes 	if (days)
1289b50d902SRodney W. Grimes 		(void)snprintf(resbuf, sizeof(resbuf),
12973b018e3SEd Schouten 		    "%s %4d+%02d:%02d", updown, days, hours, minutes);
1309b50d902SRodney W. Grimes 	else
1319b50d902SRodney W. Grimes 		(void)snprintf(resbuf, sizeof(resbuf),
1329b50d902SRodney W. Grimes 		    "%s      %2d:%02d", updown, hours, minutes);
1339b50d902SRodney W. Grimes 	return (resbuf);
1349b50d902SRodney W. Grimes }
1359b50d902SRodney W. Grimes 
136e245deadSBruce Evans /* Width to print a small nonnegative integer. */
137e245deadSBruce Evans static int
iwidth(int w)138e245deadSBruce Evans iwidth(int w)
139e245deadSBruce Evans {
140e245deadSBruce Evans 	if (w < 10)
141e245deadSBruce Evans 		return (1);
142e245deadSBruce Evans 	if (w < 100)
143e245deadSBruce Evans 		return (2);
144e245deadSBruce Evans 	if (w < 1000)
145e245deadSBruce Evans 		return (3);
146e245deadSBruce Evans 	if (w < 10000)
147e245deadSBruce Evans 		return (4);
148e245deadSBruce Evans 	return (5);
149e245deadSBruce Evans }
150e245deadSBruce Evans 
151a827060aSMark Murray #define	HS(a)	((const struct hs *)(a))
1529b50d902SRodney W. Grimes 
1539b50d902SRodney W. Grimes /* Alphabetical comparison. */
15459880300SEd Schouten static int
hscmp(const void * a1,const void * a2)155a827060aSMark Murray hscmp(const void *a1, const void *a2)
1569b50d902SRodney W. Grimes {
1579b50d902SRodney W. Grimes 	return (rflg *
15873b018e3SEd Schouten 	    strcmp(HS(a1)->hs_wd.wd_hostname, HS(a2)->hs_wd.wd_hostname));
1599b50d902SRodney W. Grimes }
1609b50d902SRodney W. Grimes 
1619b50d902SRodney W. Grimes /* Load average comparison. */
16259880300SEd Schouten static int
lcmp(const void * a1,const void * a2)163a827060aSMark Murray lcmp(const void *a1, const void *a2)
1649b50d902SRodney W. Grimes {
1659b50d902SRodney W. Grimes 	if (ISDOWN(HS(a1)))
1669b50d902SRodney W. Grimes 		if (ISDOWN(HS(a2)))
1679b50d902SRodney W. Grimes 			return (tcmp(a1, a2));
1689b50d902SRodney W. Grimes 		else
1699b50d902SRodney W. Grimes 			return (rflg);
1709b50d902SRodney W. Grimes 	else if (ISDOWN(HS(a2)))
1719b50d902SRodney W. Grimes 		return (-rflg);
1729b50d902SRodney W. Grimes 	else
1739b50d902SRodney W. Grimes 		return (rflg *
17473b018e3SEd Schouten 		   (HS(a2)->hs_wd.wd_loadav[0] - HS(a1)->hs_wd.wd_loadav[0]));
1759b50d902SRodney W. Grimes }
1769b50d902SRodney W. Grimes 
17759880300SEd Schouten static void
ruptime(const char * host,int aflg,int (* cmp)(const void *,const void *))178e7cb5ef7SJuli Mallett ruptime(const char *host, int aflg, int (*cmp)(const void *, const void *))
179e7cb5ef7SJuli Mallett {
180e7cb5ef7SJuli Mallett 	struct hs *hsp;
181e7cb5ef7SJuli Mallett 	struct whod *wd;
182e7cb5ef7SJuli Mallett 	struct whoent *we;
183e7cb5ef7SJuli Mallett 	struct dirent *dp;
184e245deadSBruce Evans 	int fd, hostnamewidth, i, loadavwidth[3], userswidth, w;
185e7cb5ef7SJuli Mallett 	size_t hspace;
18673b018e3SEd Schouten 	ssize_t cc;
187e7cb5ef7SJuli Mallett 
188e7cb5ef7SJuli Mallett 	rewinddir(dirp);
189e7cb5ef7SJuli Mallett 	hsp = NULL;
190e245deadSBruce Evans 	hostnamewidth = 0;
191e245deadSBruce Evans 	loadavwidth[0] = 4;
192e245deadSBruce Evans 	loadavwidth[1] = 4;
193e245deadSBruce Evans 	loadavwidth[2] = 4;
194e245deadSBruce Evans 	userswidth = 1;
195887019fbSAntoine Brodin 	(void)time(&now);
196e7cb5ef7SJuli Mallett 	for (nhosts = hspace = 0; (dp = readdir(dirp)) != NULL;) {
197e7cb5ef7SJuli Mallett 		if (dp->d_ino == 0 || strncmp(dp->d_name, "whod.", 5) != 0)
198e7cb5ef7SJuli Mallett 			continue;
199e7cb5ef7SJuli Mallett 		if ((fd = open(dp->d_name, O_RDONLY, 0)) < 0) {
200e7cb5ef7SJuli Mallett 			warn("%s", dp->d_name);
201e7cb5ef7SJuli Mallett 			continue;
202e7cb5ef7SJuli Mallett 		}
203e7cb5ef7SJuli Mallett 
204e7cb5ef7SJuli Mallett 		if (nhosts == hspace) {
205e7cb5ef7SJuli Mallett 			if ((hs =
206e7cb5ef7SJuli Mallett 			    realloc(hs, (hspace += 40) * sizeof(*hs))) == NULL)
207e7cb5ef7SJuli Mallett 				err(1, NULL);
208e7cb5ef7SJuli Mallett 			hsp = hs + nhosts;
209e7cb5ef7SJuli Mallett 		}
210e7cb5ef7SJuli Mallett 
21173b018e3SEd Schouten 		wd = &hsp->hs_wd;
21273b018e3SEd Schouten 		cc = read(fd, wd, sizeof(*wd));
21373b018e3SEd Schouten 		(void)close(fd);
21473b018e3SEd Schouten 		if (cc < (ssize_t)WHDRSIZE)
21573b018e3SEd Schouten 			continue;
216e7cb5ef7SJuli Mallett 
217e245deadSBruce Evans 		if (host != NULL && strcasecmp(wd->wd_hostname, host) != 0)
21873b018e3SEd Schouten 			continue;
21973b018e3SEd Schouten 		if (LEFTEARTH(wd->wd_recvtime))
22073b018e3SEd Schouten 			continue;
22173b018e3SEd Schouten 
222e245deadSBruce Evans 		if (hostnamewidth < (int)strlen(wd->wd_hostname))
223e245deadSBruce Evans 			hostnamewidth = (int)strlen(wd->wd_hostname);
224ba2c1f62SYoshihiro Takahashi 
225ba2c1f62SYoshihiro Takahashi 		if (!ISDOWN(hsp)) {
226e245deadSBruce Evans 			for (i = 0; i < 3; i++) {
227e245deadSBruce Evans 				w = iwidth(wd->wd_loadav[i] / 100) + 3;
228e245deadSBruce Evans 				if (loadavwidth[i] < w)
229e245deadSBruce Evans 					loadavwidth[i] = w;
230e245deadSBruce Evans 			}
23173b018e3SEd Schouten 			for (hsp->hs_nusers = 0, we = &wd->wd_we[0];
23273b018e3SEd Schouten 			     (char *)(we + 1) <= (char *)wd + cc; we++)
233e7cb5ef7SJuli Mallett 				if (aflg || we->we_idle < 3600)
234e7cb5ef7SJuli Mallett 					++hsp->hs_nusers;
235e245deadSBruce Evans 			if (userswidth < iwidth(hsp->hs_nusers))
236e245deadSBruce Evans 				userswidth = iwidth(hsp->hs_nusers);
237ba2c1f62SYoshihiro Takahashi 		}
238ba2c1f62SYoshihiro Takahashi 
239e7cb5ef7SJuli Mallett 		++hsp;
240e7cb5ef7SJuli Mallett 		++nhosts;
241e7cb5ef7SJuli Mallett 	}
242e7cb5ef7SJuli Mallett 	if (nhosts == 0) {
243e7cb5ef7SJuli Mallett 		if (host == NULL)
244e7cb5ef7SJuli Mallett 			errx(1, "no hosts in %s", _PATH_RWHODIR);
245e7cb5ef7SJuli Mallett 		else
246e7cb5ef7SJuli Mallett 			warnx("host %s not in %s", host, _PATH_RWHODIR);
247e7cb5ef7SJuli Mallett 	}
248e7cb5ef7SJuli Mallett 
249e7cb5ef7SJuli Mallett 	qsort(hs, nhosts, sizeof(hs[0]), cmp);
250e245deadSBruce Evans 	w = userswidth + loadavwidth[0] + loadavwidth[1] + loadavwidth[2];
251e245deadSBruce Evans 	if (hostnamewidth + w > 41)
252e245deadSBruce Evans 		hostnamewidth = 41 - w;	/* limit to 79 cols */
253e7cb5ef7SJuli Mallett 	for (i = 0; i < (int)nhosts; i++) {
254e7cb5ef7SJuli Mallett 		hsp = &hs[i];
25573b018e3SEd Schouten 		wd = &hsp->hs_wd;
256e7cb5ef7SJuli Mallett 		if (ISDOWN(hsp)) {
257e245deadSBruce Evans 			(void)printf("%-*.*s  %s\n",
258e245deadSBruce Evans 			    hostnamewidth, hostnamewidth, wd->wd_hostname,
25973b018e3SEd Schouten 			    interval(now - hsp->hs_wd.wd_recvtime, "down"));
260e7cb5ef7SJuli Mallett 			continue;
261e7cb5ef7SJuli Mallett 		}
262e7cb5ef7SJuli Mallett 		(void)printf(
263e245deadSBruce Evans 		    "%-*.*s  %s,  %*d user%s  load %*.2f, %*.2f, %*.2f\n",
264e245deadSBruce Evans 		    hostnamewidth, hostnamewidth, wd->wd_hostname,
26573b018e3SEd Schouten 		    interval((time_t)wd->wd_sendtime -
26673b018e3SEd Schouten 		        (time_t)wd->wd_boottime, "  up"),
267e245deadSBruce Evans 		    userswidth, hsp->hs_nusers,
268e7cb5ef7SJuli Mallett 		    hsp->hs_nusers == 1 ? ", " : "s,",
269e245deadSBruce Evans 		    loadavwidth[0], wd->wd_loadav[0] / 100.0,
270e245deadSBruce Evans 		    loadavwidth[1], wd->wd_loadav[1] / 100.0,
271e245deadSBruce Evans 		    loadavwidth[2], wd->wd_loadav[2] / 100.0);
272e7cb5ef7SJuli Mallett 	}
273e7cb5ef7SJuli Mallett 	free(hs);
274e7cb5ef7SJuli Mallett 	hs = NULL;
275e7cb5ef7SJuli Mallett }
276e7cb5ef7SJuli Mallett 
2779b50d902SRodney W. Grimes /* Number of users comparison. */
27859880300SEd Schouten static int
ucmp(const void * a1,const void * a2)279a827060aSMark Murray ucmp(const void *a1, const void *a2)
2809b50d902SRodney W. Grimes {
2819b50d902SRodney W. Grimes 	if (ISDOWN(HS(a1)))
2829b50d902SRodney W. Grimes 		if (ISDOWN(HS(a2)))
2839b50d902SRodney W. Grimes 			return (tcmp(a1, a2));
2849b50d902SRodney W. Grimes 		else
2859b50d902SRodney W. Grimes 			return (rflg);
2869b50d902SRodney W. Grimes 	else if (ISDOWN(HS(a2)))
2879b50d902SRodney W. Grimes 		return (-rflg);
2889b50d902SRodney W. Grimes 	else
2899b50d902SRodney W. Grimes 		return (rflg * (HS(a2)->hs_nusers - HS(a1)->hs_nusers));
2909b50d902SRodney W. Grimes }
2919b50d902SRodney W. Grimes 
2929b50d902SRodney W. Grimes /* Uptime comparison. */
29359880300SEd Schouten static int
tcmp(const void * a1,const void * a2)294a827060aSMark Murray tcmp(const void *a1, const void *a2)
2959b50d902SRodney W. Grimes {
2969b50d902SRodney W. Grimes 	return (rflg * (
29773b018e3SEd Schouten 		(ISDOWN(HS(a2)) ? HS(a2)->hs_wd.wd_recvtime - now
29873b018e3SEd Schouten 		    : HS(a2)->hs_wd.wd_sendtime - HS(a2)->hs_wd.wd_boottime)
2999b50d902SRodney W. Grimes 		-
30073b018e3SEd Schouten 		(ISDOWN(HS(a1)) ? HS(a1)->hs_wd.wd_recvtime - now
30173b018e3SEd Schouten 		    : HS(a1)->hs_wd.wd_sendtime - HS(a1)->hs_wd.wd_boottime)
3029b50d902SRodney W. Grimes 	));
3039b50d902SRodney W. Grimes }
3049b50d902SRodney W. Grimes 
30559880300SEd Schouten static void
usage(void)306a827060aSMark Murray usage(void)
3079b50d902SRodney W. Grimes {
308f682f10cSRuslan Ermilov 	(void)fprintf(stderr, "usage: ruptime [-alrtu] [host ...]\n");
3099b50d902SRodney W. Grimes 	exit(1);
3109b50d902SRodney W. Grimes }
311