xref: /illumos-gate/usr/src/cmd/rpcsvc/rup.c (revision df477c0afa111b5205c872dab36dbfde391656de)
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 
47 int machinecmp();
48 int loadcmp();
49 int uptimecmp();
50 static int collectnames();
51 int singlehost();		/* returns 1 if rup of given host fails */
52 void printsinglehosts();
53 void printnames();
54 static void putline();
55 int netbufeq(struct netbuf *ap, struct netbuf *bp);
56 void usage(void);
57 
58 struct 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 
67 int total_entries;
68 int curentry;
69 struct entry *entry;
70 int vers;			/* which version did the broadcasting */
71 int lflag;			/* load: sort by load average */
72 int tflag;			/* time: sort by uptime average */
73 int hflag;			/* host: sort by machine name */
74 int dflag;			/* debug: list only first n machines */
75 int debug;
76 
77 int
78 main(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 
175 int
176 singlehost(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 
244 error:
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 
254 static void
255 putline(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 
295 static int
296 collectnames(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 
390 void
391 printsinglehosts()
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 
411 void
412 printnames()
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 
440 int
441 machinecmp(struct entry *a, struct entry *b)
442 {
443 	return (strcmp(a->machine, b->machine));
444 }
445 
446 int
447 uptimecmp(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 
455 int
456 loadcmp(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 
467 struct netbuf *
468 netbufdup(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 
482 struct netconfig *
483 netconfigdup(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 
516 int
517 netbufeq(struct netbuf *ap, struct netbuf *bp)
518 {
519 	return (ap->len == bp->len && !memcmp(ap->buf, bp->buf, ap->len));
520 }
521 
522 void
523 usage(void)
524 {
525 	fprintf(stderr, "Usage: rup [-h] [-l] [-t] [host ...]\n");
526 	free(entry);
527 	exit(1);
528 }
529