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