11590Srgrimes/*
21590Srgrimes * Copyright (c) 1983, 1993, 1994
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 4. Neither the name of the University nor the names of its contributors
141590Srgrimes *    may be used to endorse or promote products derived from this software
151590Srgrimes *    without specific prior written permission.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271590Srgrimes * SUCH DAMAGE.
281590Srgrimes */
291590Srgrimes
301590Srgrimes#ifndef lint
3158620Scharnierstatic const char copyright[] =
321590Srgrimes"@(#) Copyright (c) 1983, 1993, 1994\n\
331590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
341590Srgrimes#endif /* not lint */
351590Srgrimes
361590Srgrimes#ifndef lint
3795617Smarkmstatic const char sccsid[] = "@(#)ruptime.c	8.2 (Berkeley) 4/5/94";
381590Srgrimes#endif /* not lint */
391590Srgrimes
4095617Smarkm#include <sys/cdefs.h>
4195617Smarkm__FBSDID("$FreeBSD$");
4295617Smarkm
431590Srgrimes#include <sys/param.h>
441590Srgrimes
451590Srgrimes#include <protocols/rwhod.h>
461590Srgrimes
471590Srgrimes#include <dirent.h>
481590Srgrimes#include <err.h>
49200462Sdelphij#include <errno.h>
501590Srgrimes#include <fcntl.h>
511590Srgrimes#include <stdio.h>
521590Srgrimes#include <stdlib.h>
531590Srgrimes#include <string.h>
541590Srgrimes#include <time.h>
551590Srgrimes#include <unistd.h>
561590Srgrimes
571590Srgrimesstruct hs {
581590Srgrimes	struct	whod *hs_wd;
591590Srgrimes	int	hs_nusers;
601590Srgrimes} *hs;
611590Srgrimesstruct	whod awhod;
62149719Sssouhlal#define LEFTEARTH(h)		(now - (h) > 4*24*60*60)
631590Srgrimes#define	ISDOWN(h)		(now - (h)->hs_wd->wd_recvtime > 11 * 60)
641590Srgrimes#define	WHDRSIZE	(sizeof (awhod) - sizeof (awhod.wd_we))
651590Srgrimes
661590Srgrimessize_t nhosts;
671590Srgrimestime_t now;
681590Srgrimesint rflg = 1;
69111714SjmallettDIR *dirp;
701590Srgrimes
7192921Simpint	 hscmp(const void *, const void *);
7295617Smarkmchar	*interval(time_t, const char *);
7392921Simpint	 lcmp(const void *, const void *);
7492921Simpvoid	 morehosts(void);
75111714Sjmallettvoid	 ruptime(const char *, int, int (*)(const void *, const void *));
7692921Simpint	 tcmp(const void *, const void *);
7792921Simpint	 ucmp(const void *, const void *);
7892921Simpvoid	 usage(void);
791590Srgrimes
801590Srgrimesint
8195617Smarkmmain(int argc, char *argv[])
821590Srgrimes{
8392921Simp	int (*cmp)(const void *, const void *);
84111714Sjmallett	int aflg, ch;
851590Srgrimes
861590Srgrimes	aflg = 0;
871590Srgrimes	cmp = hscmp;
8824360Simp	while ((ch = getopt(argc, argv, "alrut")) != -1)
891590Srgrimes		switch (ch) {
901590Srgrimes		case 'a':
911590Srgrimes			aflg = 1;
921590Srgrimes			break;
931590Srgrimes		case 'l':
941590Srgrimes			cmp = lcmp;
951590Srgrimes			break;
961590Srgrimes		case 'r':
971590Srgrimes			rflg = -1;
981590Srgrimes			break;
991590Srgrimes		case 't':
1001590Srgrimes			cmp = tcmp;
1011590Srgrimes			break;
1021590Srgrimes		case 'u':
1031590Srgrimes			cmp = ucmp;
1041590Srgrimes			break;
1058874Srgrimes		default:
1061590Srgrimes			usage();
1071590Srgrimes		}
1081590Srgrimes	argc -= optind;
1091590Srgrimes	argv += optind;
1101590Srgrimes
1111590Srgrimes	if (chdir(_PATH_RWHODIR) || (dirp = opendir(".")) == NULL)
1121590Srgrimes		err(1, "%s", _PATH_RWHODIR);
1131590Srgrimes
114111714Sjmallett	ruptime(*argv, aflg, cmp);
115111714Sjmallett	while (*argv++ != NULL) {
116111714Sjmallett		if (*argv == NULL)
117111714Sjmallett			break;
118111714Sjmallett		ruptime(*argv, aflg, cmp);
119111714Sjmallett	}
120111714Sjmallett	exit(0);
121111714Sjmallett}
122111714Sjmallett
123111714Sjmallettchar *
124111714Sjmallettinterval(time_t tval, const char *updown)
125111714Sjmallett{
126111714Sjmallett	static char resbuf[32];
127111714Sjmallett	int days, hours, minutes;
128111714Sjmallett
129111714Sjmallett	if (tval < 0) {
130111714Sjmallett		(void)snprintf(resbuf, sizeof(resbuf), "   %s ??:??", updown);
131111714Sjmallett		return (resbuf);
132111714Sjmallett	}
133111714Sjmallett						/* round to minutes. */
134111714Sjmallett	minutes = (tval + (60 - 1)) / 60;
135111714Sjmallett	hours = minutes / 60;
136111714Sjmallett	minutes %= 60;
137111714Sjmallett	days = hours / 24;
138111714Sjmallett	hours %= 24;
139111714Sjmallett	if (days)
140111714Sjmallett		(void)snprintf(resbuf, sizeof(resbuf),
141111714Sjmallett		    "%s %3d+%02d:%02d", updown, days, hours, minutes);
142111714Sjmallett	else
143111714Sjmallett		(void)snprintf(resbuf, sizeof(resbuf),
144111714Sjmallett		    "%s     %2d:%02d", updown, hours, minutes);
145111714Sjmallett	return (resbuf);
146111714Sjmallett}
147111714Sjmallett
148111714Sjmallett#define	HS(a)	((const struct hs *)(a))
149111714Sjmallett
150111714Sjmallett/* Alphabetical comparison. */
151111714Sjmallettint
152111714Sjmalletthscmp(const void *a1, const void *a2)
153111714Sjmallett{
154111714Sjmallett	return (rflg *
155111714Sjmallett	    strcmp(HS(a1)->hs_wd->wd_hostname, HS(a2)->hs_wd->wd_hostname));
156111714Sjmallett}
157111714Sjmallett
158111714Sjmallett/* Load average comparison. */
159111714Sjmallettint
160111714Sjmallettlcmp(const void *a1, const void *a2)
161111714Sjmallett{
162111714Sjmallett	if (ISDOWN(HS(a1)))
163111714Sjmallett		if (ISDOWN(HS(a2)))
164111714Sjmallett			return (tcmp(a1, a2));
165111714Sjmallett		else
166111714Sjmallett			return (rflg);
167111714Sjmallett	else if (ISDOWN(HS(a2)))
168111714Sjmallett		return (-rflg);
169111714Sjmallett	else
170111714Sjmallett		return (rflg *
171111714Sjmallett		   (HS(a2)->hs_wd->wd_loadav[0] - HS(a1)->hs_wd->wd_loadav[0]));
172111714Sjmallett}
173111714Sjmallett
174111714Sjmallettvoid
175111714Sjmallettruptime(const char *host, int aflg, int (*cmp)(const void *, const void *))
176111714Sjmallett{
177111714Sjmallett	struct hs *hsp;
178111714Sjmallett	struct whod *wd;
179111714Sjmallett	struct whoent *we;
180111714Sjmallett	struct dirent *dp;
181111714Sjmallett	const char *hostname;
182111714Sjmallett	char buf[sizeof(struct whod)];
183111714Sjmallett	int fd, i, maxloadav;
184111714Sjmallett	size_t hspace;
185111714Sjmallett	u_int cc;
186111714Sjmallett
187111714Sjmallett	rewinddir(dirp);
188111714Sjmallett	hsp = NULL;
1891590Srgrimes	maxloadav = -1;
190177316Santoine	(void)time(&now);
1911590Srgrimes	for (nhosts = hspace = 0; (dp = readdir(dirp)) != NULL;) {
192111714Sjmallett		if (dp->d_ino == 0 || strncmp(dp->d_name, "whod.", 5) != 0)
1931590Srgrimes			continue;
1941590Srgrimes		if ((fd = open(dp->d_name, O_RDONLY, 0)) < 0) {
1951590Srgrimes			warn("%s", dp->d_name);
1961590Srgrimes			continue;
1971590Srgrimes		}
1981590Srgrimes		cc = read(fd, buf, sizeof(struct whod));
1991590Srgrimes		(void)close(fd);
200111714Sjmallett		if (host != NULL) {
201111714Sjmallett			hostname = ((struct whod *)buf)->wd_hostname;
202111714Sjmallett			if (strcasecmp(hostname, host) != 0)
203111714Sjmallett				continue;
204111714Sjmallett		}
2051590Srgrimes
2061590Srgrimes		if (cc < WHDRSIZE)
2071590Srgrimes			continue;
208149719Sssouhlal		if (LEFTEARTH(((struct whod *)buf)->wd_recvtime))
209149719Sssouhlal			continue;
2101590Srgrimes		if (nhosts == hspace) {
2111590Srgrimes			if ((hs =
2121590Srgrimes			    realloc(hs, (hspace += 40) * sizeof(*hs))) == NULL)
2131590Srgrimes				err(1, NULL);
2141590Srgrimes			hsp = hs + nhosts;
2151590Srgrimes		}
2161590Srgrimes
2171590Srgrimes		if ((hsp->hs_wd = malloc((size_t)WHDRSIZE)) == NULL)
2181590Srgrimes			err(1, NULL);
2191590Srgrimes		memmove(hsp->hs_wd, buf, (size_t)WHDRSIZE);
2201590Srgrimes
2211590Srgrimes		for (wd = (struct whod *)buf, i = 0; i < 2; ++i)
2221590Srgrimes			if (wd->wd_loadav[i] > maxloadav)
2231590Srgrimes				maxloadav = wd->wd_loadav[i];
2241590Srgrimes
2251590Srgrimes		for (hsp->hs_nusers = 0,
2261590Srgrimes		    we = (struct whoent *)(buf + cc); --we >= wd->wd_we;)
2271590Srgrimes			if (aflg || we->we_idle < 3600)
2281590Srgrimes				++hsp->hs_nusers;
2291590Srgrimes		++hsp;
2301590Srgrimes		++nhosts;
2311590Srgrimes	}
232111714Sjmallett	if (nhosts == 0) {
233111714Sjmallett		if (host == NULL)
234111714Sjmallett			errx(1, "no hosts in %s", _PATH_RWHODIR);
235111714Sjmallett		else
236111714Sjmallett			warnx("host %s not in %s", host, _PATH_RWHODIR);
237111714Sjmallett	}
2381590Srgrimes
2391590Srgrimes	qsort(hs, nhosts, sizeof(hs[0]), cmp);
24095617Smarkm	for (i = 0; i < (int)nhosts; i++) {
2411590Srgrimes		hsp = &hs[i];
2421590Srgrimes		if (ISDOWN(hsp)) {
243212771Sobrien			(void)printf("%-25.25s%s\n", hsp->hs_wd->wd_hostname,
2441590Srgrimes			    interval(now - hsp->hs_wd->wd_recvtime, "down"));
2451590Srgrimes			continue;
2461590Srgrimes		}
2471590Srgrimes		(void)printf(
248212771Sobrien		    "%-25.25s%s,  %4d user%s  load %*.2f, %*.2f, %*.2f\n",
2491590Srgrimes		    hsp->hs_wd->wd_hostname,
2501590Srgrimes		    interval((time_t)hsp->hs_wd->wd_sendtime -
2511590Srgrimes			(time_t)hsp->hs_wd->wd_boottime, "  up"),
2521590Srgrimes		    hsp->hs_nusers,
2531590Srgrimes		    hsp->hs_nusers == 1 ? ", " : "s,",
2541590Srgrimes		    maxloadav >= 1000 ? 5 : 4,
2551590Srgrimes			hsp->hs_wd->wd_loadav[0] / 100.0,
2561590Srgrimes		    maxloadav >= 1000 ? 5 : 4,
2571590Srgrimes		        hsp->hs_wd->wd_loadav[1] / 100.0,
2581590Srgrimes		    maxloadav >= 1000 ? 5 : 4,
2591590Srgrimes		        hsp->hs_wd->wd_loadav[2] / 100.0);
260111714Sjmallett		free(hsp->hs_wd);
2611590Srgrimes	}
262111714Sjmallett	free(hs);
263111714Sjmallett	hs = NULL;
2641590Srgrimes}
2651590Srgrimes
2661590Srgrimes/* Number of users comparison. */
2671590Srgrimesint
26895617Smarkmucmp(const void *a1, const void *a2)
2691590Srgrimes{
2701590Srgrimes	if (ISDOWN(HS(a1)))
2711590Srgrimes		if (ISDOWN(HS(a2)))
2721590Srgrimes			return (tcmp(a1, a2));
2731590Srgrimes		else
2741590Srgrimes			return (rflg);
2751590Srgrimes	else if (ISDOWN(HS(a2)))
2761590Srgrimes		return (-rflg);
2771590Srgrimes	else
2781590Srgrimes		return (rflg * (HS(a2)->hs_nusers - HS(a1)->hs_nusers));
2791590Srgrimes}
2801590Srgrimes
2811590Srgrimes/* Uptime comparison. */
2821590Srgrimesint
28395617Smarkmtcmp(const void *a1, const void *a2)
2841590Srgrimes{
2851590Srgrimes	return (rflg * (
2861590Srgrimes		(ISDOWN(HS(a2)) ? HS(a2)->hs_wd->wd_recvtime - now
2871590Srgrimes		    : HS(a2)->hs_wd->wd_sendtime - HS(a2)->hs_wd->wd_boottime)
2881590Srgrimes		-
2891590Srgrimes		(ISDOWN(HS(a1)) ? HS(a1)->hs_wd->wd_recvtime - now
2901590Srgrimes		    : HS(a1)->hs_wd->wd_sendtime - HS(a1)->hs_wd->wd_boottime)
2911590Srgrimes	));
2921590Srgrimes}
2931590Srgrimes
2941590Srgrimesvoid
29595617Smarkmusage(void)
2961590Srgrimes{
297146466Sru	(void)fprintf(stderr, "usage: ruptime [-alrtu] [host ...]\n");
2981590Srgrimes	exit(1);
2991590Srgrimes}
300