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 (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <stdio.h>
  27 #include <stdlib.h>
  28 #include <string.h>
  29 #include <unistd.h>
  30 #include <errno.h>
  31 #include <inttypes.h>
  32 #include <locale.h>
  33 
  34 #include <kstat.h>
  35 
  36 #include "dsstat.h"
  37 #include "multi_stats.h"
  38 
  39 /* Globals */
  40 int mode = 0;
  41 int interval = 1;
  42 int iterations = 1;
  43 int zflag = 0;
  44 int linesout = 0;
  45 
  46 short hflags = HEADERS_EXL;
  47 short dflags = 0;
  48 short rflags = 0;
  49 vslist_t *vs_top = NULL;
  50 
  51 void
  52 errout(char *msg)
  53 {
  54 
  55         (void) fprintf(stderr, msg);
  56 }
  57 
  58 void
  59 usage()
  60 {
  61         errout(gettext(
  62             "\ndsstat [-m <mode>[,<mode>]] [-f | -F] [-z] [-s <sets>] "
  63             "[-r <flags>] \\\n[-d <flags>] [<interval> [<count>]]\n\n"));
  64 }
  65 
  66 void
  67 help()
  68 {
  69         usage();
  70 
  71         errout(gettext("\t"
  72             "-d <flags> Specifies the statistics to be displayed\n\n"));
  73         errout(gettext("\t"
  74             "   For 'cache' mode\n"));
  75         errout(gettext("\t"
  76             "      Valid <flags> are 'rwfsdc', default <flags> are 'sf'\n"));
  77         errout(gettext("\t"
  78             "      r=read, w=write, f=flags, s=summary,\n"));
  79         errout(gettext("\t"
  80             "   only available for cache mode, need to combine with '-m'\n"));
  81         errout(gettext("\t"
  82             "      d=destaged, c=write cancellations\n\n"));
  83         errout(gettext("\t"
  84             "   For 'ii' mode;\n"));
  85         errout(gettext("\t"
  86             "      Valid <flags> are 'rwtfps', default <flags> are 'sf'\n"));
  87         errout(gettext("\t"
  88             "      r=read, w=write, t=timing, f=flags, p=percentages,\n"));
  89         errout(gettext("\t"
  90             "      s=summary\n\n"));
  91         errout(gettext("\t"
  92             "   For 'sndr' mode;\n"));
  93         errout(gettext("\t"
  94             "      Valid <flags> are'rwtfpsq', default <flags> are 'spf'\n"));
  95         errout(gettext("\t"
  96             "      r=read, w=write, t=timing, f=flags, p=percentages,\n"));
  97         errout(gettext("\t"
  98             "      s=summary\n"));
  99         errout(gettext("\t"
 100             "   only available for sndr mode, need to combine with '-m'\n"));
 101         errout(gettext("\t"
 102             "      q=queue\n\n"));
 103         errout(gettext("\t"
 104             "-f  prints field headers once for each iteration\n\n"));
 105         errout(gettext("\t"
 106             "-F  prints field headers once, at the start of reporting\n\n"));
 107         errout(gettext("\t"
 108             "-h  prints detailed usage message\n\n"));
 109         errout(gettext("\t"
 110             "-m <mode>[,<mode>] where mode is, 'cache', 'ii', or 'sndr'\n\n"));
 111         errout(gettext("\t"
 112             "   Multiple modes may be specified as a comma separated list,\n"));
 113         errout(gettext("\t"
 114             "   or multiple -m switches may be used.\n\n"));
 115         errout(gettext("\t"
 116             "-r <flags> specifies components to be reported\n\n"));
 117         errout(gettext("\t"
 118             "   For 'cache' mode, this option is not used.\n\n"));
 119         errout(gettext("\t"
 120             "   For 'ii' mode;\n"));
 121         errout(gettext("\t"
 122             "      Valid <flags> are 'msbo', default <flags> are 'msbo'\n"));
 123         errout(gettext("\t"
 124             "      m=master, s=shadow, b=bitmap, o=overflow\n\n"));
 125         errout(gettext("\t"
 126             "   For 'sndr' mode;\n"));
 127         errout(gettext("\t"
 128             "      Valid <flags> are 'nb', default <flags> are 'nb'\n"));
 129         errout(gettext("\t"
 130             "      n=network, b=bitmap\n\n"));
 131         errout(gettext("\t"
 132             "-s <sets> outputs specified sets\n"));
 133         errout(gettext("\t"
 134             "    Where <sets> is a comma delimited list of set names\n\n"));
 135         errout(gettext("\t"
 136             "-z  suppress reports with zero value (no activity)\n\n"));
 137         errout(gettext("\t"
 138             "<interval> is the number of seconds between reports\n\n"));
 139         errout(gettext("\t"
 140             "<count> is the number of reports to be generated\n\n"));
 141 }
 142 
 143 void
 144 fail(int err, char *msg)
 145 {
 146         errout(gettext("\ndsstat: "));
 147         errout(msg);
 148 
 149         usage();
 150 
 151         errout(gettext("For detailed usage run \"dsstat -h\"\n"));
 152 
 153         exit(err);
 154 }
 155 
 156 int
 157 set_mode(char *user_modes)
 158 {
 159         char *m;
 160         int local_mode = 0;
 161 
 162         for (m = strtok(user_modes, ","); m != NULL; m = strtok(NULL, ",")) {
 163                 if (local_mode != 0) {
 164                         local_mode |= MULTI;
 165                 }
 166 
 167                 if (strncasecmp("sndr", m, strlen(m)) == 0) {
 168                         local_mode |= SNDR;
 169                         continue;
 170                 }
 171 
 172                 if (strncasecmp("ii", m, strlen(m)) == 0) {
 173                         local_mode |= IIMG;
 174                         continue;
 175                 }
 176 
 177                 if (strncasecmp("cache", m, strlen(m)) == 0) {
 178                         local_mode |= SDBC;
 179                         continue;
 180                 }
 181 
 182                 fail(DSSTAT_EINVAL, gettext("Invalid mode specified"));
 183         }
 184 
 185         return (local_mode);
 186 }
 187 
 188 short
 189 set_dflags(char *flags)
 190 {
 191         int index;
 192         short user_dflags = 0;
 193 
 194         for (index = 0; index < strlen(flags); index++) {
 195                 switch (flags[index]) {
 196                         case 'r':
 197                                 user_dflags |= READ;
 198                                 break;
 199                         case 'w':
 200                                 user_dflags |= WRITE;
 201                                 break;
 202                         case 't':
 203                                 user_dflags |= TIMING;
 204                                 break;
 205                         case 'f':
 206                                 user_dflags |= FLAGS;
 207                                 break;
 208                         case 'p':
 209                                 user_dflags |= PCTS;
 210                                 break;
 211                         case 's':
 212                                 user_dflags |= SUMMARY;
 213                                 break;
 214                         case 'd':
 215                                 user_dflags |= DESTAGED;
 216                                 break;
 217                         case 'c':
 218                                 user_dflags |= WRCANCEL;
 219                                 break;
 220                         case 'h':
 221                                 user_dflags |= RATIO;
 222                                 break;
 223                         case 'q':
 224                                 user_dflags |= ASYNC_QUEUE;
 225                                 break;
 226                         default:
 227                                 fail(DSSTAT_EINVAL,
 228                                     gettext("Invalid display-flags set\n"));
 229                 }
 230         }
 231 
 232         return (user_dflags);
 233 }
 234 
 235 short
 236 set_rflags(char *flags)
 237 {
 238         int index;
 239         short user_rflags = 0;
 240 
 241         for (index = 0; index < strlen(flags); index++) {
 242                 switch (flags[index]) {
 243                         case 'm':
 244                                 user_rflags |= IIMG_MST;
 245                                 break;
 246                         case 's':
 247                                 user_rflags |= IIMG_SHD;
 248                                 break;
 249                         case 'b':
 250                                 user_rflags |= IIMG_BMP;
 251                                 user_rflags |= SNDR_BMP;
 252                                 break;
 253                         case 'o':
 254                                 user_rflags |= IIMG_OVR;
 255                                 break;
 256                         case 'n':
 257                                 user_rflags |= SNDR_NET;
 258                                 break;
 259                         default:
 260                                 fail(DSSTAT_EINVAL,
 261                                     gettext("Invalid report-flags set\n"));
 262                 }
 263         }
 264 
 265         return (user_rflags);
 266 }
 267 
 268 void
 269 set_vol_list(char *list)
 270 {
 271         vslist_t *pre;
 272         vslist_t *newvol;
 273         vslist_t *vslist;
 274         char *volume;
 275 
 276         for (volume = strtok(list, ","); volume != NULL;
 277             volume = strtok(NULL, ",")) {
 278                 int dup = 0;
 279                 char *vh = NULL;
 280                 char *vn = NULL;
 281 
 282                 /* get user-specified set information */
 283                 if ((vn = strchr(volume, ':')) == NULL) {
 284                         vn = volume;
 285                 } else {
 286                         *vn = '\0';
 287                         vn++;
 288                         vh = volume;
 289                 }
 290 
 291                 /* check for duplicates */
 292                 dup = 0;
 293 
 294                 for (vslist = vs_top; vslist != NULL; vslist = vslist->next) {
 295                         if (vslist->volhost && vh) {
 296                                 if (strcmp(vslist->volhost, vh) == 0 &&
 297                                     strcmp(vslist->volname, vn) == 0)
 298                                         dup = 1;
 299                         } else {
 300                                 if (strcmp(vslist->volname, vn) == 0)
 301                                         dup = 1;
 302                         }
 303 
 304                         pre = vslist;
 305                 }
 306 
 307                 if (dup)
 308                         continue;
 309 
 310                 /* initialize new vslist record */
 311                 newvol = (vslist_t *)calloc(1, sizeof (vslist_t));
 312 
 313                 newvol->volname = (char *)calloc((strlen(vn) + 1),
 314                     sizeof (char));
 315                 (void) strcpy(newvol->volname, vn);
 316 
 317                 if (vh == NULL)
 318                         goto save;
 319 
 320                 newvol->volhost = (char *)calloc((strlen(vh) + 1),
 321                     sizeof (char));
 322                 (void) strcpy(newvol->volhost, vh);
 323 
 324 save:
 325                 /* save record */
 326                 if (vs_top == NULL) {
 327                         vslist = vs_top = newvol;
 328                         vslist->next = NULL;
 329                         continue;
 330                 }
 331 
 332                 if (vslist == NULL) {
 333                         vslist = pre->next = newvol;
 334                         vslist->next = NULL;
 335                         continue;
 336                 }
 337         }
 338 }
 339 
 340 int
 341 main(int argc, char **argv)
 342 {
 343         extern char *optarg;
 344         extern int optind;
 345 
 346         int c;
 347         int error;
 348         short user_dflags = 0;
 349         short user_rflags = 0;
 350 
 351         /* Parse command line */
 352         while ((c = getopt(argc, argv, "d:fFhm:r:s:z")) != EOF) {
 353                 switch (c) {
 354                         case 'd':       /* what to display */
 355                                 user_dflags = set_dflags(optarg);
 356                                 break;
 357                         case 'f':
 358                                 hflags = HEADERS_ATT;
 359                                 break;
 360                         case 'F':
 361                                 hflags = HEADERS_BOR;
 362                                 break;
 363                         case 'h':               /* usage */
 364                                 help();
 365                                 exit(0);
 366                                 break;
 367                         case 'm':               /* Mode */
 368                                 mode |= set_mode(optarg);
 369                                 break;
 370                         case 'r':               /* what to report on */
 371                                 user_rflags = set_rflags(optarg);
 372                                 break;
 373                         case 's':
 374                                 set_vol_list(optarg);
 375                                 break;
 376                         case 'z':
 377                                 zflag = 1;
 378                                 break;
 379 
 380                         default:
 381                                 fail(DSSTAT_EINVAL,
 382                                     "Invalid argument specified\n");
 383                 }
 384         }
 385 
 386         /* Parse additional arguments */
 387         if (optind < argc) {
 388                 if ((interval = atoi(argv[optind])) <= 0) {
 389                         fail(DSSTAT_EINVAL,
 390                             gettext("Invalid interval specified.\n"));
 391                 } else {
 392                         iterations = -1;
 393                 }
 394 
 395                 optind++;
 396 
 397                 if (optind < argc) {
 398                         if ((iterations = atoi(argv[optind])) <= 0) {
 399                                 fail(DSSTAT_EINVAL,
 400                                     gettext("Invalid count specified.\n"));
 401                         }
 402                 }
 403 
 404                 optind++;
 405         }
 406 
 407         if (optind < argc) {
 408                 fail(DSSTAT_EINVAL,
 409                     gettext("Too many parameters specified.\n"));
 410         }
 411 
 412         if (mode == 0)
 413                 mode |= MULTI | IIMG | SNDR | SDBC;
 414 
 415         /* Select statistics to gather */
 416         if (mode & SNDR) {
 417                 if (! (mode & MULTI)) {
 418                         if (user_rflags & IIMG_BMP)
 419                                 user_rflags ^= IIMG_BMP;
 420 
 421                         if ((user_dflags | SNDR_DIS_MASK) != SNDR_DIS_MASK) {
 422                                 fail(DSSTAT_EINVAL, gettext("Invalid "
 423                                     "display-flags for RemoteMirror\n"));
 424                         }
 425 
 426                         if ((user_rflags | SNDR_REP_MASK) != SNDR_REP_MASK) {
 427                                 fail(DSSTAT_EINVAL,
 428                                     gettext("Invalid report-flags for "
 429                                     "Remote Mirror\n"));
 430                         }
 431                 }
 432 
 433                 if ((mode & MULTI) && (user_dflags & ASYNC_QUEUE)) {
 434                         fail(DSSTAT_EINVAL, gettext("Remote Mirror async. queue"
 435                             "statistics can not be displayed with mutiple "
 436                             "modes."));
 437                 }
 438 
 439                 if (user_dflags)
 440                         dflags = user_dflags;
 441                 else
 442                         dflags |= (SUMMARY | PCTS | FLAGS | RATIO);
 443 
 444                 if (user_rflags)
 445                         rflags = user_rflags;
 446                 else
 447                         rflags |= (SNDR_NET | SNDR_BMP);
 448         }
 449 
 450         if (mode & IIMG) {
 451                 if (! (mode & MULTI)) {
 452                         if (user_rflags & SNDR_BMP)
 453                                 user_rflags ^= SNDR_BMP;
 454 
 455                         if ((user_dflags | IIMG_DIS_MASK) != IIMG_DIS_MASK) {
 456                                 fail(DSSTAT_EINVAL,
 457                                     gettext("Invalid display-flags for "
 458                                     "Point-in-Time Copy\n"));
 459                         }
 460 
 461                         if ((user_rflags | IIMG_REP_MASK) != IIMG_REP_MASK) {
 462                                 fail(DSSTAT_EINVAL,
 463                                     gettext("Invalid report-flags for "
 464                                     "Point-in-Time Copy\n"));
 465                         }
 466                 }
 467 
 468                 if (user_dflags)
 469                         dflags = user_dflags;
 470                 else
 471                         dflags |= (SUMMARY | PCTS | FLAGS | RATIO);
 472 
 473                 if (user_rflags)
 474                         rflags = user_rflags;
 475                 else
 476                         rflags |= (IIMG_MST | IIMG_SHD | IIMG_BMP | IIMG_OVR);
 477         }
 478 
 479         if (mode & SDBC) {
 480                 if (! (mode & MULTI)) {
 481                         if ((user_dflags | CACHE_DIS_MASK) != CACHE_DIS_MASK) {
 482                                 fail(DSSTAT_EINVAL, gettext("Invalid "
 483                                     "display-flags for CACHE\n"));
 484                         }
 485 
 486                         if ((user_rflags | CACHE_REP_MASK) != CACHE_REP_MASK) {
 487                                 fail(DSSTAT_EINVAL, gettext("Invalid "
 488                                     "report-flags for CACHE\n"));
 489                         }
 490                 } else {
 491                     if ((user_dflags & DESTAGED) || (user_dflags & WRCANCEL)) {
 492                         if (user_dflags & DESTAGED)
 493                             fail(DSSTAT_EINVAL, gettext("Cache, destaged "
 494                                 "statistics can not be displayed with mutiple "
 495                                 "modes."));
 496                         else
 497                             fail(DSSTAT_EINVAL, gettext("Cache, write "
 498                                 "cancellations "
 499                                 "statistics can not be displayed with mutiple "
 500                                 "modes."));
 501                     }
 502                 }
 503 
 504                 if (user_dflags)
 505                         dflags = user_dflags;
 506                 else
 507                         if (mode & MULTI)
 508                                 dflags |= (SUMMARY);
 509                         else
 510                                 dflags |= (SUMMARY | FLAGS);
 511 
 512                 if (user_rflags)
 513                         rflags = user_rflags;
 514                 else
 515                         rflags |= user_rflags;
 516         }
 517 
 518         error = do_stats();
 519 
 520         if (error == EAGAIN) {
 521                 fail(DSSTAT_NOSTAT, gettext("No statistics available for the "
 522                     "specified mode(s).\n"));
 523         }
 524 
 525         if (error == EINVAL) {
 526                 fail(DSSTAT_EINVAL,
 527                     gettext("Invalid kstat format detected.\n"));
 528         }
 529 
 530         if (error == ENOMEM) {
 531                 fail(DSSTAT_ENOMEM,
 532                     gettext("Unable to open kstat device for reading.\n"));
 533         }
 534 
 535         if (error == -1) {
 536                 if (execv("/usr/sbin/dsstat", argv) != 0) {
 537                         fail(DSSTAT_EMAP, gettext("Kstat is invalid.\n"));
 538                 }
 539         }
 540 
 541         if (error) {
 542                 fail(DSSTAT_EUNKNWN, gettext("An unknown error occured.\n"));
 543         }
 544 
 545         return (0);
 546 }