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 /*
  23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #include <stdio.h>
  28 #include <stdlib.h>
  29 #include <unistd.h>
  30 #include <strings.h>
  31 #include <curses.h>
  32 #include <signal.h>
  33 #include <fcntl.h>
  34 #include <locale.h>
  35 
  36 #include <sys/types.h>
  37 #include <sys/time.h>
  38 #include <sys/nsctl/sdbc_ioctl.h>
  39 #include <sys/unistat/spcs_s_u.h>
  40 #include <sys/nsctl/sd_bcache.h>
  41 #include <sys/nsctl/sd_conf.h>
  42 
  43 extern void total_display(void);
  44 extern void display_cache(void);
  45 extern void wrefresh_file(WINDOW *, int);
  46 extern int is_dirty(void);
  47 extern int dual_stats(void);
  48 void checkbuf(int);
  49 void setup_ranges(char *);
  50 void prheading(int);
  51 extern int zero_nic(void);
  52 
  53 #ifdef m88k
  54 #define USEC_INIT()     usec_ptr = (unsigned int *)timer_init()
  55 #define USEC_READ()     (*usec_ptr)
  56 #else /* !m88k */
  57 #define USEC_INIT()     USEC_START()
  58 #include <sys/time.h>
  59 static struct timeval Usec_time;
  60 static int Usec_started = 0;
  61 
  62 extern int higher(int);
  63 extern int is_dirty();
  64 extern int dual_stats();
  65 extern void total_display();
  66 extern void display_cache();
  67 extern void wrefresh_file(WINDOW *, int);
  68 void setup_ranges(char *);
  69 
  70 void prheading(int);
  71 void checkbuf(int);
  72 void quit(int);
  73 void leave(int);
  74 #pragma does_not_return(quit, leave)
  75 
  76 int sdbc_max_devices = 0;
  77 
  78 static void
  79 USEC_START()
  80 {
  81         if (!Usec_started) {
  82                 (void) gettimeofday(&Usec_time, NULL);
  83                 Usec_started = 1;
  84         }
  85 }
  86 
  87 static unsigned int
  88 USEC_READ()
  89 {
  90         struct timeval tv;
  91         if (!Usec_started)
  92                 USEC_START();
  93 
  94         (void) gettimeofday(&tv, NULL);
  95         return (unsigned)((tv.tv_sec - Usec_time.tv_sec) * 1000000
  96             + (tv.tv_usec - Usec_time.tv_usec));
  97 }
  98 #endif /* m88k */
  99 
 100 int             rev_flag = 0;           /* Reverse video flag */
 101 int             bold_flg = 0;           /* Bold flag */
 102 int             under_flg = 0;          /* Underline flag */
 103 int             errflg = 0;             /* Error flag */
 104 int             node_sw = 0;            /* Per node switch */
 105 int             toggle_total_sw = 0;
 106 int             mirror_sw = 0;          /* Dual copy switch */
 107 
 108 int             kmemfd;
 109 int             delay = 1;                      /* Display delay (seconds) */
 110 
 111 time_t  *usec_ptr;
 112 time_t  currtime = 0;
 113 int             lasttime = 0;
 114 int             Elapsed_Time = 0;
 115 
 116 static char     *range;
 117 static int      had_r_option = 0;
 118 int             logfd = -1;             /* screen output logging */
 119 extern          int range_num;
 120 extern          int screen;
 121 extern          int dual_screen;
 122 int             *on_off;
 123 int             *dual_on_off;
 124 int             *updates_prev;
 125 double          *rate_prev;
 126 int             *samples;
 127 _sd_stats_t     *cs_cur;
 128 _sd_stats_t     *cs_prev;
 129 _sd_stats_t     *cs_persec;
 130 
 131 typedef struct {
 132         int lb, ub;
 133 } range_t;
 134 
 135 extern range_t ranges[];
 136 
 137 #ifdef lint
 138 int
 139 sd_stats_lintmain(int argc, char *argv[])
 140 #else
 141 int
 142 main(int argc, char *argv[])
 143 #endif
 144 {
 145         spcs_s_info_t ustats;
 146         struct timeval tout;
 147         fd_set readfds;
 148         char *errmessage, *ch;
 149         int c, period, prev;
 150         int count = 0, dflag = 0;
 151         int fd = fileno(stdin);
 152 
 153         errmessage = NULL;
 154 
 155         if (strcmp(argv[0], "sd_stats") != 0)
 156                 errmessage = getenv("SD_STATS_USAGE");
 157 
 158         if (errmessage == NULL)
 159                 errmessage = gettext("Usage: sd_stats [-Mz] "
 160                                 "[-d delay_time] [-l logfile] [-r range]");
 161 
 162         if (SDBC_IOCTL(SDBC_MAXFILES, &sdbc_max_devices,
 163             0, 0, 0, 0, &ustats) == SPCS_S_ERROR) {
 164                 if (ustats) {   /* if SPCS_S_ERROR */
 165                         spcs_s_report(ustats, stderr);
 166                         spcs_s_ufree(&ustats);
 167                 }
 168                 (void) fprintf(stderr, gettext("cannot get maxfiles\n"));
 169                 exit(1);
 170         }
 171         on_off = calloc(sdbc_max_devices, sizeof (int));
 172         dual_on_off = calloc(sdbc_max_devices, sizeof (int));
 173         updates_prev = calloc(sdbc_max_devices, sizeof (int));
 174         samples = calloc(sdbc_max_devices, sizeof (int));
 175         rate_prev = calloc(sdbc_max_devices, sizeof (double));
 176         cs_cur = malloc(sizeof (_sd_stats_t) +
 177             (sdbc_max_devices - 1) * sizeof (_sd_shared_t));
 178         cs_prev = malloc(sizeof (_sd_stats_t) +
 179             (sdbc_max_devices - 1) * sizeof (_sd_shared_t));
 180         cs_persec = malloc(sizeof (_sd_stats_t) +
 181             (sdbc_max_devices - 1) * sizeof (_sd_shared_t));
 182         range = malloc(100);
 183 
 184         if (!on_off || !dual_on_off || !updates_prev || !samples ||
 185             !rate_prev || !cs_cur || !cs_prev || !cs_persec || !range) {
 186                 (void) fprintf(stderr, gettext("no free memory\n"));
 187                 exit(1);
 188         }
 189 
 190         *range = '\0';
 191 
 192         while ((c = getopt(argc, argv, "DMzd:l:r:h")) != EOF) {
 193 
 194                 prev = c;
 195                 switch (c) {
 196 
 197                 case 'd':
 198                         delay = atoi(optarg);
 199                         ch = optarg;
 200                         while (*ch != '\0') {
 201                                 if (!isdigit(*ch))
 202                                         errflg++;
 203                                 ch++;
 204                         }
 205                         break;
 206 
 207                 case 'l':
 208                         logfd = open(optarg, O_CREAT|O_WRONLY|O_TRUNC, 0644);
 209                         break;
 210 
 211                 case 'r':
 212                         ch = optarg;
 213                         while (*ch != '\0') {
 214                                 if ((!isdigit(*ch)) && (*ch != ',') &&
 215                                     (*ch != ':'))
 216                                         errflg++;
 217                                 ch++;
 218                         }
 219                         if (errflg)
 220                                 break;
 221 
 222                         range = realloc((char *)range,
 223                                         (strlen(range) + strlen(optarg) + 1)
 224                                         * sizeof (char));
 225 
 226                         if (had_r_option)
 227                                 (void) strcat(range, ",");
 228                         (void) strcat(range, optarg);
 229                         had_r_option = 1;
 230                         break;
 231 
 232                 case 'z':
 233                         if (SDBC_IOCTL(SDBC_ZAP_STATS, 0, 0, 0, 0, 0,
 234                                         &ustats) == SPCS_S_ERROR) {
 235                                 if (ustats) {
 236                                         spcs_s_report(ustats, stderr);
 237                                         spcs_s_ufree(&ustats);
 238                                 }
 239                         }
 240 
 241                         break;
 242 
 243                 case 'D':
 244                         dflag = 1;
 245                         break;
 246 
 247                 case 'M':
 248                         mirror_sw = 1;
 249                         break;
 250 
 251                 case 'h':
 252                 case '?':
 253                 default :
 254                         errflg++;
 255                         break;
 256                 }
 257         }
 258 
 259         if (errflg) {
 260                 (void) fprintf(stderr, "%s\n", errmessage);
 261                 exit(1);
 262         } else if (!prev) {
 263                 if (argc > 1) {
 264                         (void) fprintf(stderr, "%s\n", errmessage);
 265                         exit(1);
 266                 }
 267         }
 268 
 269         if (dflag) {
 270                 exit(is_dirty());
 271         }
 272 
 273 
 274         /*
 275          * A few curses routines to setup screen and tty interface
 276          */
 277         (void) initscr();
 278         (void) cbreak();
 279         (void) noecho();
 280         (void) nonl();
 281         (void) erase();
 282         (void) clear();
 283         (void) refresh();
 284 
 285         setup_ranges(range);
 286 
 287         /*
 288          * Set signal handle
 289          */
 290         (void) sigset(SIGPIPE, leave);
 291         (void) sigset(SIGINT, leave);
 292         (void) sigset(SIGQUIT, leave);
 293         (void) signal(SIGFPE, leave);
 294         (void) signal(SIGSEGV, leave);
 295 
 296         USEC_INIT();
 297         currtime = USEC_READ();
 298 
 299         /*
 300          * Wait one second before reading the new values
 301          */
 302         (void) sleep(1);
 303 
 304         /*CONSTCOND*/
 305         while (1) {
 306 
 307                 lasttime = currtime;
 308                 currtime = USEC_READ();
 309 
 310                 /*
 311                  * If less that 1 second, force it to one second
 312                  */
 313                 if ((period = (currtime - lasttime) / 1000000) <= 0)
 314                         period = 1;
 315 
 316                 /*
 317                  * Calculate new per/period values for statistics
 318                  */
 319                 Elapsed_Time += period;
 320 
 321                 /*
 322                  * Display new statistics
 323                  */
 324                 prheading(++count);
 325 
 326                 if (mirror_sw) {
 327                         if (dual_stats() < 0)
 328                                 mirror_sw = 0;
 329                 } else if (toggle_total_sw)
 330                         total_display();
 331                 else
 332                         display_cache();
 333 
 334                 (void) move(0, 0);
 335                 (void) refresh();
 336                 if (logfd > -1) wrefresh_file(stdscr, logfd);
 337 
 338                 FD_ZERO(&readfds);
 339                 FD_SET(fd, &readfds);
 340                 tout.tv_sec = delay;
 341                 for (;;) {
 342                         tout.tv_usec = 0;
 343                         if (select(fd + 1, &readfds, (fd_set *)0, (fd_set *)0,
 344                                 &tout) <= 0)
 345                                 break;
 346                         if ((c = getch()) == EOF) {
 347                                 (void) sleep(delay);
 348                                 break;
 349                         }
 350                         checkbuf(c);
 351                         tout.tv_sec = 0;
 352                 }
 353                 (void) erase();
 354         }
 355 #pragma error_messages(off, E_STATEMENT_NOT_REACHED)
 356         return (0);
 357 #pragma error_messages(default, E_STATEMENT_NOT_REACHED)
 358 }
 359 
 360 void
 361 checkbuf(int c)
 362 {
 363         spcs_s_info_t ustats;
 364 
 365         switch (c) {
 366         case 'b' : /* ctrl b or b --  scroll backward */
 367         case  2  :
 368                 {
 369                 if (mirror_sw == 1) {
 370                         if (dual_screen > 0)
 371                                 dual_screen--;
 372                         break;
 373                 }
 374                 if (screen > 0)
 375                         screen--;
 376                 break;
 377                 }
 378 
 379         case 'f' : /* ctrl f or f -- scroll forward */
 380         case  6  :
 381                 {
 382                 if (mirror_sw == 1) {
 383                         dual_screen++;
 384                         break;
 385                 }
 386                 screen++;
 387                 break;
 388                 }
 389 
 390         case 't':
 391         case 'T':
 392                 if (mirror_sw == 1)
 393                         mirror_sw = 0;
 394 
 395                 toggle_total_sw ^= 1;
 396                 break;
 397 
 398         case '-':
 399         case KEY_DOWN:
 400                 if (delay > 1) {
 401                         --delay;
 402                 } else {
 403                         (void) beep();
 404                 }
 405                 break;
 406 
 407         case '+':
 408         case KEY_UP:
 409                 delay++;
 410                 break;
 411 
 412         case 'C':
 413         case 0xc:
 414                 (void) clearok(stdscr, TRUE);
 415                 break;
 416 
 417         case 'B':
 418                 if (bold_flg) {
 419                         bold_flg = 0;
 420                         (void) attroff(A_BOLD);
 421                 } else {
 422                         bold_flg = 1;
 423                         (void) attron(A_BOLD);
 424                 }
 425                 break;
 426 
 427         case 'R':
 428                 if (rev_flag) {
 429                         rev_flag = 0;
 430                         (void) attroff(A_REVERSE);
 431                 } else {
 432                         rev_flag = 1;
 433                         (void) attron(A_REVERSE);
 434                 }
 435                 break;
 436 
 437         case 'z':
 438                 if (SDBC_IOCTL(SDBC_ZAP_STATS, 0, 0, 0, 0, 0,
 439                     &ustats) == SPCS_S_ERROR) {
 440                         if (ustats) {
 441                                 spcs_s_report(ustats, stderr);
 442                                 spcs_s_ufree(&ustats);
 443                         }
 444                 }
 445                 break;
 446 
 447         case 'm':
 448         case 'M':
 449                 mirror_sw = mirror_sw ? 0 : 1;
 450                 (void) clear();
 451                 break;
 452         }
 453 }
 454 
 455 void
 456 prheading(int count)
 457 {
 458         time_t  tim;
 459 
 460         /*
 461          * Print sample count in upper left corner
 462          */
 463         (void) mvprintw(0,  0, "SAMPLE %-8d", count);
 464 
 465         /*
 466          * Get time and print it in upper right corner
 467          */
 468         tim = time((time_t *)0);
 469         (void) mvprintw(0, 79 - 10, "%-8.8s\n", &(ctime(&tim)[11]));
 470 }
 471 
 472 /*ARGSUSED*/
 473 void
 474 leave(int status)
 475 {
 476         (void) sigignore(SIGPIPE);
 477         (void) sigignore(SIGALRM);
 478         /* clear(); */
 479         (void) move(LINES, 0);
 480         (void) refresh();
 481         if (logfd > -1) wrefresh_file(stdscr, logfd);
 482         quit(0);
 483 }
 484 
 485 void
 486 quit(int status)
 487 {
 488         (void) resetterm();
 489         (void) endwin();
 490         exit(status);
 491 }
 492 
 493 void
 494 setup_ranges(char *range)
 495 {
 496         int ndx;
 497         char chr1;
 498         char prev_chr = '\0';
 499         int got_colon = 0;
 500         int after_got_colon = 0;
 501         int got_comma = 0;
 502         int after_got_comma = 0;
 503         int number = 0;
 504         int prev_num = 0;
 505 
 506         if (range == NULL || (strlen(range) == 0)) {
 507                 ranges[range_num].lb = 0;
 508                 ranges[range_num].ub = sdbc_max_devices - 1;
 509                 return;
 510         } else {
 511                 ndx = 0;
 512                 got_comma = 0;
 513                 got_colon = 0;
 514                 while ((chr1 = (range[ndx++])) != '\0') {
 515                         switch (chr1) {
 516                         case '0':
 517                         case '1':
 518                         case '2':
 519                         case '3':
 520                         case '4':
 521                         case '5':
 522                         case '6':
 523                         case '7':
 524                         case '8':
 525                         case '9':
 526                                 number = number*10 + (chr1 - '0');
 527                                 break;
 528                         case ':':
 529                                 got_colon = 1;
 530                                 break;
 531                         case ',':
 532                                 got_comma = 1;
 533                                 break;
 534                         default: /* ignore any unknown characters */
 535                                 break;
 536                         }       /* switch */
 537                         if (got_comma && after_got_colon) {
 538                                 after_got_colon = 0;
 539                                 got_comma = 0;
 540                                 if (number >= sdbc_max_devices)
 541                                         number = sdbc_max_devices - 1;
 542                                 ranges[range_num].lb = prev_num;
 543                                 ranges[range_num].ub = number;
 544                                 if (range_num == 99) break;
 545                                 range_num++;
 546                                 number = 0;
 547                         } else if (got_colon && after_got_comma) {
 548                                 got_colon = 0;
 549                                 after_got_colon = 1;
 550                                 after_got_comma = 0;
 551                                 if (number >= sdbc_max_devices)
 552                                         number = sdbc_max_devices - 1;
 553                                 prev_num = number;
 554                                 number = 0;
 555                         } else if (got_colon) {
 556                                 got_colon = 0;
 557                                 after_got_colon = 1;
 558                                 if ((prev_chr != '\0') && (prev_chr != ':')) {
 559                                         if (number >= sdbc_max_devices)
 560                                                 number = sdbc_max_devices - 1;
 561                                         prev_num = number;
 562                                         number = 0;
 563                                 }
 564                         } else if (got_comma) {
 565                                 got_comma = 0;
 566                                 after_got_comma = 1;
 567                                 after_got_colon = 0;
 568                                 if (number >= sdbc_max_devices)
 569                                         number = sdbc_max_devices -1;
 570                                 if ((prev_chr != '\0') && (prev_chr != ',')) {
 571                                         ranges[range_num].lb = number;
 572                                         ranges[range_num].ub = number;
 573                                         if (range_num == 99) break;
 574                                                 range_num++;
 575                                 }
 576                                 number = 0;
 577                         }       /* if */
 578                         prev_chr = chr1;
 579                 }               /* while */
 580                 if (number >= sdbc_max_devices)
 581                         number = sdbc_max_devices - 1;
 582                 if (after_got_colon) {
 583                         ranges[range_num].lb = prev_num;
 584                         ranges[range_num].ub = number;
 585                 } else {
 586                         if ((after_got_comma) && (prev_chr == ','))
 587                                 range_num--;
 588                         else {
 589                                 ranges[range_num].lb = number;
 590                                 ranges[range_num].ub = number;
 591                         }
 592                 }
 593         }
 594 }