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