1/*	$OpenBSD: rup.c,v 1.31 2017/03/20 16:10:10 deraadt Exp $	*/
2
3/*-
4 * Copyright (c) 1993, John Brezak
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *  3. The name of the author may not be used to endorse or promote products
16 *     derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <time.h>
35#include <sys/socket.h>
36#include <netdb.h>
37#include <rpc/rpc.h>
38#include <rpc/pmap_clnt.h>
39#include <arpa/inet.h>
40#include <err.h>
41#include <unistd.h>
42
43#undef FSHIFT			/* Use protocol's shift and scale values */
44#undef FSCALE
45#include <rpcsvc/rstat.h>
46
47#define HOST_WIDTH 27
48
49int printtime;			/* print the remote host(s)'s time */
50
51struct host_list {
52	struct host_list *next;
53	struct in_addr addr;
54} *hosts;
55
56void usage(void);
57int print_rup_data(char *, statstime *host_stat);
58
59static int
60search_host(struct in_addr addr)
61{
62	struct host_list *hp;
63
64	if (!hosts)
65		return(0);
66
67	for (hp = hosts; hp != NULL; hp = hp->next) {
68		if (hp->addr.s_addr == addr.s_addr)
69			return(1);
70	}
71	return(0);
72}
73
74static void
75remember_host(struct in_addr addr)
76{
77	struct host_list *hp;
78
79	if (!(hp = malloc(sizeof(struct host_list)))) {
80		err(1, NULL);
81		/* NOTREACHED */
82	}
83	hp->addr.s_addr = addr.s_addr;
84	hp->next = hosts;
85	hosts = hp;
86}
87
88
89struct rup_data {
90	char *host;
91	struct statstime statstime;
92};
93struct rup_data *rup_data;
94int rup_data_idx = 0;
95int rup_data_max = 0;
96
97enum sort_type {
98	SORT_NONE,
99	SORT_HOST,
100	SORT_LDAV,
101	SORT_UPTIME
102};
103enum sort_type sort_type;
104
105static int
106compare(const void *v1, const void *v2)
107{
108	const struct rup_data *d1 = v1;
109	const struct rup_data *d2 = v2;
110
111	switch(sort_type) {
112	case SORT_HOST:
113		return strcmp(d1->host, d2->host);
114	case SORT_LDAV:
115		return d1->statstime.avenrun[0]
116			- d2->statstime.avenrun[0];
117	case SORT_UPTIME:
118		return d1->statstime.boottime.tv_sec
119			- d2->statstime.boottime.tv_sec;
120	default:
121		/* something's really wrong here */
122		abort();
123	}
124}
125
126static void
127remember_rup_data(char *host, struct statstime *st)
128{
129	if (rup_data_idx >= rup_data_max) {
130		int newsize;
131		struct rup_data *newrup;
132
133		newsize = rup_data_max + 16;
134		newrup = recallocarray(rup_data, rup_data_max, newsize,
135		    sizeof(struct rup_data));
136		if (newrup == NULL) {
137			err(1, NULL);
138			/* NOTREACHED */
139		}
140		rup_data = newrup;
141		rup_data_max = newsize;
142	}
143
144	if ((rup_data[rup_data_idx].host = strdup(host)) == NULL)
145		err(1, NULL);
146	rup_data[rup_data_idx].statstime = *st;
147	rup_data_idx++;
148}
149
150
151static int
152rstat_reply(char *replyp, struct sockaddr_in *raddrp)
153{
154	struct hostent *hp;
155	char *host;
156	statstime *host_stat = (statstime *)replyp;
157
158	if (!search_host(raddrp->sin_addr)) {
159		hp = gethostbyaddr((char *)&raddrp->sin_addr.s_addr,
160		    sizeof(struct in_addr), AF_INET);
161		if (hp)
162			host = hp->h_name;
163		else
164			host = inet_ntoa(raddrp->sin_addr);
165
166		remember_host(raddrp->sin_addr);
167
168		if (sort_type != SORT_NONE)
169			remember_rup_data(host, host_stat);
170		else
171			print_rup_data(host, host_stat);
172	}
173
174	return (0);
175}
176
177
178int
179print_rup_data(char *host, statstime *host_stat)
180{
181	unsigned int ups = 0, upm = 0, uph = 0, upd = 0;
182	struct tm *tmp_time, host_time;
183	char days_buf[16], hours_buf[16];
184	time_t tim;
185
186	if (printtime)
187		printf("%-*.*s", HOST_WIDTH-8, HOST_WIDTH-8, host);
188	else
189		printf("%-*.*s", HOST_WIDTH, HOST_WIDTH, host);
190
191	tim = host_stat->curtime.tv_sec;
192	tmp_time = localtime(&tim);
193	host_time = *tmp_time;
194
195	host_stat->curtime.tv_sec -= host_stat->boottime.tv_sec;
196
197	if (host_stat->curtime.tv_sec > 0)
198		ups = host_stat->curtime.tv_sec;
199	upd = ups / (3600 * 24);
200	ups -= upd * 3600 * 24;
201	uph = ups / 3600;
202	ups -= uph * 3600;
203	upm = ups / 60;
204
205	if (upd != 0)
206		snprintf(days_buf, sizeof days_buf, "%3u day%s, ", upd,
207		    (upd > 1) ? "s" : "");
208	else
209		days_buf[0] = '\0';
210
211	if (uph != 0)
212		snprintf(hours_buf, sizeof hours_buf, "%2u:%02u, ",
213		    uph, upm);
214	else
215		if (upm != 0)
216			snprintf(hours_buf, sizeof hours_buf, "%2u min%s ",
217			    upm, (upm == 1) ? ", " : "s,");
218		else
219			hours_buf[0] = '\0';
220
221	if (printtime)
222		printf(" %2d:%02d%cm",
223		    (host_time.tm_hour % 12) ? (host_time.tm_hour % 12) : 12,
224		    host_time.tm_min,
225		    (host_time.tm_hour >= 12) ? 'p' : 'a');
226
227	printf(" up %9.9s%9.9s load average: %.2f %.2f %.2f\n",
228	    days_buf, hours_buf,
229	    (double)host_stat->avenrun[0] / FSCALE,
230	    (double)host_stat->avenrun[1] / FSCALE,
231	    (double)host_stat->avenrun[2] / FSCALE);
232
233	return(0);
234}
235
236
237static void
238onehost(char *host)
239{
240	CLIENT *rstat_clnt;
241	statstime host_stat;
242	static struct timeval timeout = {25, 0};
243	extern char *__progname;
244
245	rstat_clnt = clnt_create(host, RSTATPROG, RSTATVERS_TIME, "udp");
246	if (rstat_clnt == NULL) {
247		fprintf(stderr, "%s: %s", __progname,
248		    clnt_spcreateerror(host));
249		return;
250	}
251
252	bzero((char *)&host_stat, sizeof(host_stat));
253	if (clnt_call(rstat_clnt, RSTATPROC_STATS, xdr_void, NULL,
254	    xdr_statstime, &host_stat, timeout) != RPC_SUCCESS) {
255		fprintf(stderr, "%s: %s", __progname,
256		    clnt_sperror(rstat_clnt, host));
257		clnt_destroy(rstat_clnt);
258		return;
259	}
260
261	if (sort_type != SORT_NONE)
262		remember_rup_data(host, &host_stat);
263	else
264		print_rup_data(host, &host_stat);
265
266	clnt_destroy(rstat_clnt);
267}
268
269static void
270allhosts(void)
271{
272	statstime host_stat;
273	enum clnt_stat clnt_stat;
274	extern char *__progname;
275
276	if (sort_type != SORT_NONE) {
277		printf("collecting responses...\n");
278		fflush(stdout);
279	}
280
281	clnt_stat = clnt_broadcast(RSTATPROG, RSTATVERS_TIME, RSTATPROC_STATS,
282	    xdr_void, NULL, xdr_statstime, (char *)&host_stat, rstat_reply);
283	if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_TIMEDOUT) {
284		fprintf(stderr, "%s: %s\n", __progname, clnt_sperrno(clnt_stat));
285		exit(1);
286	}
287}
288
289int
290main(int argc, char *argv[])
291{
292	int ch;
293	size_t i;
294	extern int optind;
295
296	sort_type = SORT_NONE;
297	while ((ch = getopt(argc, argv, "dhlt")) != -1)
298		switch (ch) {
299		case 'd':
300			printtime = 1;
301			break;
302		case 'h':
303			sort_type = SORT_HOST;
304			break;
305		case 'l':
306			sort_type = SORT_LDAV;
307			break;
308		case 't':
309			sort_type = SORT_UPTIME;
310			break;
311		default:
312			usage();
313			/*NOTREACHED*/
314		}
315
316	setvbuf(stdout, NULL, _IOLBF, 0);
317
318	if (argc == optind)
319		allhosts();
320	else {
321		for (; optind < argc; optind++)
322			onehost(argv[optind]);
323	}
324
325	if (sort_type != SORT_NONE) {
326		qsort(rup_data, rup_data_idx, sizeof(struct rup_data),
327		    compare);
328
329		for (i = 0; i < rup_data_idx; i++) {
330			print_rup_data(rup_data[i].host,
331			    &rup_data[i].statstime);
332		}
333	}
334
335	exit(0);
336}
337
338
339void
340usage(void)
341{
342	fprintf(stderr, "usage: rup [-dhlt] [host ...]\n");
343	exit(1);
344}
345