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
main(int argc,char * argv[])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
singlehost(host)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
putline(now,boottime,avenrun)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
collectnames(resultsp,taddr,nconf)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
printsinglehosts()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
printnames()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
machinecmp(struct entry * a,struct entry * b)454 machinecmp(struct entry *a, struct entry *b)
455 {
456 return (strcmp(a->machine, b->machine));
457 }
458
459 int
uptimecmp(struct entry * a,struct entry * b)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
loadcmp(struct entry * a,struct entry * b)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 *
netbufdup(ap)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 *
netconfigdup(onp)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
netbufeq(struct netbuf * ap,struct netbuf * bp)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
usage(void)536 usage(void)
537 {
538 fprintf(stderr, "Usage: rup [-h] [-l] [-t] [host ...]\n");
539 free(entry);
540 exit(1);
541 }
542