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 }