1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22
23/*
24 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25 * Use is subject to license terms.
26 */
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <netdb.h>
31#include <sys/param.h>
32#include <sys/stat.h>
33#include <sys/time.h>
34#include <sys/socket.h>
35#include <netinet/in.h>
36#include <rpc/rpc.h>
37#include <netdir.h>
38#include <rpcsvc/rstat.h>
39#include <rpc/pmap_clnt.h>
40
41
42#define	MACHINELEN	15	/* length of machine name printed out */
43#define	MACHINELENMAX	128	/* maximum machine name length */
44#define	AVENSIZE	(3 * sizeof (long))
45#define	SLOTS	256
46
47int machinecmp();
48int loadcmp();
49int uptimecmp();
50static int collectnames();
51int singlehost();		/* returns 1 if rup of given host fails */
52void printsinglehosts();
53void printnames();
54static void putline();
55int netbufeq(struct netbuf *ap, struct netbuf *bp);
56void usage(void);
57
58struct entry {
59	struct netconfig *nconf;
60	struct netbuf *addr;
61	char *machine;
62	struct timeval boottime;
63	time_t curtime;
64	long avenrun[3];
65};
66
67int total_entries;
68int curentry;
69struct entry *entry;
70int vers;			/* which version did the broadcasting */
71int lflag;			/* load: sort by load average */
72int tflag;			/* time: sort by uptime average */
73int hflag;			/* host: sort by machine name */
74int dflag;			/* debug: list only first n machines */
75int debug;
76
77int
78main(int argc, char *argv[])
79{
80	statsvar sv;
81	statstime st;
82	int single, nfailed;
83
84	/*
85	 * set number of slots to be 256 to begin with,
86	 * this is large enough for most subnets but not all
87	 */
88
89	curentry = 0;
90	total_entries = SLOTS;
91	entry = malloc(sizeof (struct entry) * total_entries);
92	single = nfailed = 0;
93	while (argc > 1) {
94		if (argv[1][0] != '-') {
95			single++;
96			nfailed += singlehost(argv[1]);
97		} else {
98			switch (argv[1][1]) {
99
100			case 'l':
101				lflag++;
102				break;
103			case 't':
104				tflag++;
105				break;
106			case 'h':
107				hflag++;
108				break;
109			case 'd':
110				dflag++;
111				if (argc < 3)
112					usage();
113				debug = atoi(argv[2]);
114				argc--;
115				argv++;
116				break;
117			default:
118				usage();
119			}
120		}
121		argv++;
122		argc--;
123	}
124	if (single > 0) {
125		if (hflag || tflag || lflag)
126			printsinglehosts();
127		if (nfailed == single) {
128			free(entry);
129			exit(1);	/* all hosts we tried failed */
130		} else {
131			free(entry);
132			exit(0);
133		}
134
135	}
136	if (hflag || tflag || lflag) {
137		printf("collecting responses... ");
138		fflush(stdout);
139	}
140
141	sv.cp_time.cp_time_val = (int *)NULL;
142	sv.dk_xfer.dk_xfer_val = (int *)NULL;
143
144	/*
145	 * Null out pointers in the statsvar struct
146	 * so that we don't follow a random pointer
147	 * somewhere when we get our results back.
148	 * Set lengths to zero so we don't allocate
149	 * some random amount of space we don't need
150	 * (in the case where the reply was program
151	 *  not registered).
152	 */
153	sv.cp_time.cp_time_len = 0;
154	sv.cp_time.cp_time_val = (int *)NULL;
155	sv.dk_xfer.dk_xfer_len = 0;
156	sv.dk_xfer.dk_xfer_val = (int *)NULL;
157
158	vers = RSTATVERS_VAR;
159	(void) rpc_broadcast(RSTATPROG, RSTATVERS_VAR, RSTATPROC_STATS,
160			xdr_void, NULL, xdr_statsvar, (caddr_t)&sv,
161			(resultproc_t)collectnames, (char *)0);
162	vers = RSTATVERS_TIME;
163	(void) rpc_broadcast(RSTATPROG, RSTATVERS_TIME, RSTATPROC_STATS,
164			xdr_void, NULL, xdr_statstime, (caddr_t)&st,
165			(resultproc_t)collectnames, (char *)0);
166	if (hflag || tflag || lflag)
167		printnames();
168
169
170
171	free(entry);
172	return (0);
173}
174
175int
176singlehost(host)
177	char *host;
178{
179	static int debugcnt;
180	enum clnt_stat err;
181	statstime st;
182	statsvar sw_var;
183	bool_t is_var_vers = FALSE;
184
185
186	if (curentry >= total_entries) {
187		struct entry *tmp;
188
189		total_entries += SLOTS;
190		tmp = realloc((struct entry *)entry, sizeof (struct entry)
191						* total_entries);
192		if (tmp == NULL) {
193			return (1);
194		}
195		entry = tmp;
196	}
197
198	sw_var.cp_time.cp_time_val = (int *)NULL;
199	sw_var.dk_xfer.dk_xfer_val = (int *)NULL;
200	err = (enum clnt_stat)callrpc(host, RSTATPROG, RSTATVERS_VAR,
201			RSTATPROC_STATS, xdr_void, 0, xdr_statsvar, &sw_var);
202	if (err == RPC_SUCCESS) {
203		is_var_vers = TRUE;
204	} else if (err == RPC_PROGVERSMISMATCH) {
205		err = (enum clnt_stat)callrpc(host, RSTATPROG, RSTATVERS_TIME,
206			RSTATPROC_STATS, xdr_void, 0, xdr_statstime, &st);
207		if (err != RPC_SUCCESS)
208			goto error;
209	} else
210		goto error;
211
212	debugcnt++;
213	if (!hflag && !lflag && !tflag) {
214		printf("%*.*s  ", MACHINELEN, MACHINELEN, host);
215		if (is_var_vers == TRUE)
216			putline(sw_var.curtime.tv_sec, sw_var.boottime,
217				sw_var.avenrun);
218		else
219			putline(st.curtime.tv_sec, st.boottime, st.avenrun);
220		return (0);		/* success */
221	} else {
222		entry[curentry].machine = host;
223		if (is_var_vers == FALSE) { /* RSTATVERS_TIME */
224			entry[curentry].boottime.tv_sec = st.boottime.tv_sec;
225			entry[curentry].boottime.tv_usec =
226				st.boottime.tv_usec;
227			entry[curentry].curtime = st.curtime.tv_sec;
228			memcpy(entry[curentry].avenrun, st.avenrun, AVENSIZE);
229		} else { /* RSTATVERS_VAR */
230			entry[curentry].boottime.tv_sec =
231				sw_var.boottime.tv_sec;
232			entry[curentry].boottime.tv_usec =
233				sw_var.boottime.tv_usec;
234			entry[curentry].curtime = sw_var.curtime.tv_sec;
235			memcpy(entry[curentry].avenrun, sw_var.avenrun,
236							AVENSIZE);
237		}
238	}
239	curentry++;
240	if (dflag && debugcnt >= debug)
241		return (1);
242	return (0);
243
244error:
245	fprintf(stderr, "%*.*s: ", MACHINELEN, MACHINELEN, host);
246	clnt_perrno(err);
247	/*
248	 * clnt_perrno now prints a newline
249	 */
250	/* fprintf(stderr, "\n"); */
251	return (1);		/* a failure */
252}
253
254static void
255putline(now, boottime, avenrun)
256	time_t now;
257	struct timeval boottime;
258	long avenrun[];
259{
260	int uptime, days, hrs, mins, i;
261
262	uptime = now - boottime.tv_sec;
263	uptime += 30;
264	if (uptime < 0)		/* unsynchronized clocks */
265		uptime = 0;
266	days = uptime / (60*60*24);
267	uptime %= (60*60*24);
268	hrs = uptime / (60*60);
269	uptime %= (60*60);
270	mins = uptime / 60;
271
272	printf("  up");
273	if (days > 0)
274		printf(" %2d day%s", days, days > 1 ? "s," : ", ");
275	else
276		printf("         ");
277	if (hrs > 0)
278		printf(" %2d:%02d,  ", hrs, mins);
279	else
280		printf(" %2d min%s", mins, mins > 1 ? "s," : ", ");
281
282	/*
283	 * Print 1, 5, and 15 minute load averages.
284	 * (Found by looking in kernel for avenrun).
285	 */
286	printf("  load average:");
287	for (i = 0; i < (AVENSIZE / sizeof (avenrun[0])); i++) {
288		if (i > 0)
289			printf(",");
290		printf(" %.2f", (double)avenrun[i]/FSCALE);
291	}
292	printf("\n");
293}
294
295static int
296collectnames(resultsp, taddr, nconf)
297	char *resultsp;
298	struct t_bind *taddr;
299	struct netconfig *nconf;
300{
301	static int debugcnt;
302	register struct entry *entryp, *lim;
303	statstime *st;
304	statsvar *sv;
305	struct nd_hostservlist *hs;
306	extern struct netbuf *netbufdup();
307	extern struct netconfig *netconfigdup();
308	extern int netbufeq();
309
310	/*
311	 * need to realloc more space if we have more than 256 machines
312	 * that responded to the broadcast
313	 */
314
315	if (curentry >= total_entries) {
316		struct entry *tmp;
317
318		total_entries += SLOTS;
319		tmp = realloc((struct entry *)entry, sizeof (struct entry)
320						* total_entries);
321		if (tmp == NULL) {
322			return (1);
323		}
324		entry = tmp;
325	}
326	/*
327	 * weed out duplicates
328	 */
329	lim = entry + curentry;
330	for (entryp = entry; entryp < lim; entryp++)
331		if (netbufeq(&taddr->addr, entryp->addr))
332			return (0);
333
334	if (vers == RSTATVERS_TIME) {
335		st = (statstime *)resultsp;
336	} else if (vers == RSTATVERS_VAR) {
337		sv = (statsvar *)resultsp;
338	} else {
339		return (0);	/* we don't handle this version */
340	}
341	debugcnt++;
342	entry[curentry].nconf = netconfigdup(nconf);
343	entry[curentry].addr = netbufdup(&taddr->addr);
344
345	/*
346	 * if raw, print this entry out immediately
347	 * otherwise store for later sorting
348	 */
349	if (!hflag && !lflag && !tflag) {
350		if (netdir_getbyaddr(nconf, &hs, &taddr->addr) == ND_OK)
351			printf("%*.*s  ", MACHINELEN, MACHINELEN,
352				hs->h_hostservs->h_host);
353		else {
354			char *uaddr = taddr2uaddr(nconf, &taddr->addr);
355
356			if (uaddr) {
357				printf("  %*.*s", MACHINELEN, MACHINELEN,
358					uaddr);
359				(void) free(uaddr);
360			} else
361				printf("  %*.*s", MACHINELEN, MACHINELEN,
362					"unknown");
363		}
364		if (vers == RSTATVERS_TIME) {
365			putline(st->curtime.tv_sec, st->boottime, st->avenrun);
366		} else if (vers == RSTATVERS_VAR) {
367			putline(sv->curtime.tv_sec, sv->boottime, sv->avenrun);
368		}
369	} else {
370		if (vers == RSTATVERS_TIME) {
371			entry[curentry].boottime.tv_sec = st->boottime.tv_sec;
372			entry[curentry].boottime.tv_usec =
373				st->boottime.tv_usec;
374			entry[curentry].curtime = st->curtime.tv_sec;
375			memcpy(entry[curentry].avenrun, st->avenrun, AVENSIZE);
376		} else if (vers == RSTATVERS_VAR) {
377			entry[curentry].boottime.tv_sec = sv->boottime.tv_sec;
378			entry[curentry].boottime.tv_usec =
379				sv->boottime.tv_usec;
380			entry[curentry].curtime = sv->curtime.tv_sec;
381			memcpy(entry[curentry].avenrun, sv->avenrun, AVENSIZE);
382		}
383	}
384	curentry++;
385	if (dflag && debugcnt >= debug)
386		return (1);
387	return (0);
388}
389
390void
391printsinglehosts()
392{
393	register int i;
394	register struct entry *ep;
395
396
397	if (hflag)
398		qsort(entry, curentry, sizeof (struct entry), machinecmp);
399	else if (lflag)
400		qsort(entry, curentry, sizeof (struct entry), loadcmp);
401	else
402		qsort(entry, curentry, sizeof (struct entry), uptimecmp);
403	for (i = 0; i < curentry; i++) {
404		ep = &entry[i];
405		printf("%*.*s  ", MACHINELEN, MACHINELEN, ep->machine);
406		putline(ep->curtime, ep->boottime, ep->avenrun);
407
408	}
409}
410
411void
412printnames()
413{
414	char buf[MACHINELENMAX+1];
415	struct nd_hostservlist *hs;
416	register int i;
417	register struct entry *ep;
418
419
420	for (i = 0; i < curentry; i++) {
421		ep = &entry[i];
422		if (netdir_getbyaddr(ep->nconf, &hs, ep->addr) == ND_OK)
423			sprintf(buf, "%s", hs->h_hostservs->h_host);
424		else {
425			char *uaddr = taddr2uaddr(ep->nconf, ep->addr);
426
427			if (uaddr) {
428				sprintf(buf, "%s", uaddr);
429				(void) free(uaddr);
430			} else
431				sprintf(buf, "%s", "unknown");
432		}
433		if (ep->machine = (char *)malloc(MACHINELENMAX + 1))
434			strcpy(ep->machine, buf);
435	}
436	printf("\n");
437	printsinglehosts();
438}
439
440int
441machinecmp(struct entry *a, struct entry *b)
442{
443	return (strcmp(a->machine, b->machine));
444}
445
446int
447uptimecmp(struct entry *a, struct entry *b)
448{
449	if (a->boottime.tv_sec != b->boottime.tv_sec)
450		return (a->boottime.tv_sec - b->boottime.tv_sec);
451	else
452		return (a->boottime.tv_usec - b->boottime.tv_usec);
453}
454
455int
456loadcmp(struct entry *a, struct entry *b)
457{
458	register int i;
459
460	for (i = 0; i < AVENSIZE / sizeof (a->avenrun[0]); i++)
461		if (a->avenrun[i] != b->avenrun[i])
462			return (a->avenrun[i] - b->avenrun[i]);
463
464	return (0);
465}
466
467struct netbuf *
468netbufdup(ap)
469	register struct netbuf	*ap;
470{
471	register struct netbuf	*np;
472
473	np = (struct netbuf *) malloc(sizeof (struct netbuf) + ap->len);
474	if (np) {
475		np->maxlen = np->len = ap->len;
476		np->buf = ((char *)np) + sizeof (struct netbuf);
477		(void) memcpy(np->buf, ap->buf, ap->len);
478	}
479	return (np);
480}
481
482struct netconfig *
483netconfigdup(onp)
484	register struct netconfig *onp;
485{
486	register int nlookupdirs;
487	register struct netconfig *nnp;
488	extern char *strdup();
489
490	nnp = (struct netconfig *)malloc(sizeof (struct netconfig));
491	if (nnp) {
492		nnp->nc_netid = strdup(onp->nc_netid);
493		nnp->nc_semantics = onp->nc_semantics;
494		nnp->nc_flag = onp->nc_flag;
495		nnp->nc_protofmly = strdup(onp->nc_protofmly);
496		nnp->nc_proto = strdup(onp->nc_proto);
497		nnp->nc_device = strdup(onp->nc_device);
498		nnp->nc_nlookups = onp->nc_nlookups;
499		if (onp->nc_nlookups == 0)
500			nnp->nc_lookups = (char **)0;
501		else {
502			register int i;
503
504			nnp->nc_lookups = (char **)malloc(onp->nc_nlookups *
505			    sizeof (char *));
506			if (nnp->nc_lookups)
507				for (i = 0; i < onp->nc_nlookups; i++)
508					nnp->nc_lookups[i] =
509						strdup(onp->nc_lookups[i]);
510		}
511	}
512
513	return (nnp);
514}
515
516int
517netbufeq(struct netbuf *ap, struct netbuf *bp)
518{
519	return (ap->len == bp->len && !memcmp(ap->buf, bp->buf, ap->len));
520}
521
522void
523usage(void)
524{
525	fprintf(stderr, "Usage: rup [-h] [-l] [-t] [host ...]\n");
526	free(entry);
527	exit(1);
528}
529