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 /*
  28  * Utility for cache configuration
  29  */
  30 #include <unistd.h>
  31 #include <stdio.h>
  32 #include <stdlib.h>
  33 #include <strings.h>
  34 #include <locale.h>
  35 #include <langinfo.h>
  36 #include <libintl.h>
  37 #include <time.h>
  38 #include <sys/nsctl/sd_bcache.h>
  39 #include <sys/wait.h>
  40 #include <errno.h>
  41 #include <signal.h>
  42 #include <sys/types.h>
  43 #include <fcntl.h>
  44 #include <stropts.h>
  45 #include <ctype.h>
  46 #include <libgen.h>
  47 
  48 #include <sys/nsctl/sdbc_ioctl.h>
  49 #include <sys/unistat/spcs_s.h>
  50 #include <sys/unistat/spcs_s_u.h>
  51 #include <sys/unistat/spcs_errors.h>
  52 #include <nsctl.h>
  53 
  54 #include <sys/nsctl/cfg.h>
  55 #define STATS_PATH      "/usr/bin/sd_stats"
  56 
  57 #define _SD_FNAME       /* bring in function names from sd_trace.h */
  58 #include <sys/nsctl/sd_trace.h>
  59 #include <sys/syslog.h>
  60 
  61 /*
  62  * Since we no longer support nvram cards, the hints wrthru and nowrthru no
  63  * longer serve any purpose, and the system will always be in wrthru mode.
  64  * WRTHRU_HINTS, if defined still allows the setting and reporting of write
  65  * hints.  This is defined by default on DEBUG builds.
  66  */
  67 #ifdef DEBUG
  68 #define WRTHRU_HINTS
  69 #endif
  70 
  71 static int sdbc_max_devices = 0;
  72 
  73 static char alert_file[200]  = "/dev/console";
  74 
  75 /* Variables used to set up paramater block passed to kernel */
  76 static _sd_cache_param_t        user_level_conf;
  77 static int                      myid;
  78 
  79 static int              nodes_configured = 0;
  80 static int              minidsp = 0; /* Is it a sp10 */
  81 static int              forced_wrthru = -1; /* 0 clear, 1 set,-1 as is */
  82 static int              no_forced_wrthru = -1;
  83 static short            node_defined[MAX_SD_NODES];
  84 static short            nodes_conf[MAX_SD_NODES];
  85 
  86 #define USAGELEN        1024
  87 char stats_usage[USAGELEN+128];
  88 char scmadmUsage[USAGELEN];
  89 
  90 static caddr_t progname;
  91 
  92 
  93 /*
  94  * Functions exported for fwcadm.
  95  */
  96 void enable_sdbc(void);
  97 void disable_sdbc(void);
  98 void sdbc_set_maxdev();
  99 
 100 static void buildusage(char *);
 101 
 102 void print_all_options(void);
 103 void get_cd_all(void);
 104 int toggle_flush(void);
 105 static void sd_gather_alert_dumps();
 106 static int get_cd(char *);
 107 static int get_hint(char *, int *, int *);
 108 static void check_and_set_mirrors(int, int);
 109 static void print_hint(const uint_t, const int);
 110 static char *get_device_name(char *arg);
 111 static void get_version();
 112 
 113 extern struct tm *localtime_r(const time_t *, struct tm *);
 114 
 115 #define PRINT_CACHE_SZ_ERR(sz) {\
 116         (void) fprintf(stderr, gettext("\n%s: desired cache size (%d) "\
 117             "set to system max (%d)\n"), \
 118             progname, (sz), MAX_CACHE_SIZE); \
 119         spcs_log("sdbc", NULL, \
 120                 gettext("desired cache size (%d) "\
 121                     "set to system max (%d)\n"), \
 122                 (sz), MAX_CACHE_SIZE); \
 123 }
 124 
 125 void
 126 sdbc_report_error(spcs_s_info_t *ustatus)
 127 {
 128         if (*ustatus != NULL) {
 129                 spcs_s_report(*ustatus, stderr);
 130                 spcs_s_ufree(ustatus);
 131         } else
 132                 (void) fprintf(stderr, "%s\n", strerror(errno));
 133 }
 134 
 135 
 136 /*
 137  * Return the per-cd hints for a cd.
 138  *
 139  * Since the global (no)wrthru and NSC_NOCACHE hints take precedence
 140  * over the per-cd hints, get them as well and OR the whole lot
 141  * together.
 142  */
 143 static int
 144 get_cd_hint(const int cd)
 145 {
 146         spcs_s_info_t ustats;
 147         int nodehint, cdhint;
 148 
 149         nodehint = SDBC_IOCTL(SDBC_GET_NODE_HINT, 0, 0, 0, 0, 0, &ustats);
 150         if (nodehint == SPCS_S_ERROR) {
 151                 (void) fprintf(stderr,
 152                     gettext("%s: get system options failed\n"), progname);
 153                 sdbc_report_error(&ustats);
 154                 exit(1);
 155         }
 156 
 157         cdhint = SDBC_IOCTL(SDBC_GET_CD_HINT, cd, 0, 0, 0, 0, &ustats);
 158         if (cdhint == SPCS_S_ERROR) {
 159                 (void) fprintf(stderr,
 160                     gettext("%s: get cd(%d) hint failed\n"), progname, cd);
 161                 sdbc_report_error(&ustats);
 162                 exit(1);
 163         }
 164 
 165 #ifdef WRTHRU_HINTS
 166         nodehint &= (NSC_FORCED_WRTHRU | NSC_NO_FORCED_WRTHRU | NSC_NOCACHE);
 167 #else
 168         nodehint &= (NSC_NOCACHE);
 169 #endif
 170         if (nodehint) {
 171                 /* set the top bit to mark it as a system override */
 172                 nodehint |= 0x80000000;
 173         }
 174 
 175         return (cdhint | nodehint);
 176 }
 177 
 178 
 179 
 180 /*
 181  * Check for a config.
 182  *
 183  * If no suitable config can be found, install the default config.
 184  *
 185  * Calling state:
 186  *      libcfg locked (mode describes type of lock)
 187  */
 188 static void
 189 convert_config(CFGFILE *cfg, CFGLOCK mode)
 190 {
 191         char buf[CFG_MAX_BUF];
 192         char *default_cfg = "128 64";
 193 
 194 retry:
 195         if (cfg_get_cstring(cfg, "scm.set1", buf, sizeof (buf)) >= 0) {
 196                 /* config exists, return */
 197                 return;
 198         }
 199 
 200         cfg_rewind(cfg, CFG_SEC_CONF);
 201 
 202 #ifdef DEBUG
 203         (void) printf(gettext("%s: installing default config entry '%s'\n"),
 204             progname, default_cfg);
 205 #endif
 206         if (mode != CFG_WRLOCK) {
 207                 cfg_unlock(cfg);
 208                 if (!cfg_lock(cfg, CFG_WRLOCK)) {
 209                         (void) fprintf(stderr,
 210                             gettext("%s: unable to lock configuration: %s\n"),
 211                             progname, cfg_error(NULL));
 212                         exit(1);
 213                 }
 214                 mode = CFG_WRLOCK;
 215 #ifdef DEBUG
 216                 (void) printf(gettext("%s: upgraded lock, retrying\n"),
 217                     progname);
 218 #endif
 219                 goto retry;
 220         }
 221 
 222         if (cfg_put_cstring(cfg, "scm", default_cfg, strlen(default_cfg)) < 0) {
 223                 (void) fprintf(stderr,
 224                     gettext("%s: unable to write configuration: %s\n"),
 225                     progname, cfg_error(NULL));
 226                 exit(1);
 227         }
 228 
 229         if (!cfg_commit(cfg)) {
 230                 (void) fprintf(stderr,
 231                     gettext("%s: unable to write to configuration: %s\n"),
 232                     progname, cfg_error(NULL));
 233         }
 234 
 235         if (mode != CFG_WRLOCK) {
 236                 if (!cfg_lock(cfg, mode)) {
 237                         (void) fprintf(stderr,
 238                             gettext("%s: unable to relock configuration: %s\n"),
 239                             progname, cfg_error(NULL));
 240                         exit(1);
 241                 }
 242         }
 243 
 244         cfg_rewind(cfg, CFG_SEC_CONF);
 245 }
 246 
 247 
 248 static int
 249 iscluster(void)
 250 {
 251         int rc;
 252 
 253         rc = cfg_iscluster();
 254         if (rc == 0) {
 255                 return (FALSE);
 256         } else if (rc > 0) {
 257                 return (TRUE);
 258         } else {
 259                 (void) fprintf(stderr,
 260                     gettext("%s: unable to ascertain environment\n"), progname);
 261                 exit(1);
 262         }
 263 
 264         /* NOTREACHED */
 265 }
 266 
 267 
 268 static void
 269 restore_hints()
 270 {
 271         CFGFILE *cfg;
 272         char key[CFG_MAX_KEY], buf[CFG_MAX_BUF];
 273         int setnumber;
 274         spcs_s_info_t ustatus;
 275         int cd;
 276 
 277         if ((cfg = cfg_open(NULL)) == NULL) {
 278                 (void) fprintf(stderr,
 279                     gettext("%s: unable to access configuration: %s\n"),
 280                     progname, cfg_error(NULL));
 281                 exit(1);
 282         }
 283         if (!cfg_lock(cfg, CFG_RDLOCK)) {
 284                 (void) fprintf(stderr,
 285                     gettext("%s: unable to lock configuration: %s\n"),
 286                     progname, cfg_error(NULL));
 287                 exit(1);
 288         }
 289 
 290         for (setnumber = 1; /*CONSTCOND*/ TRUE; setnumber++) {
 291                 (void) snprintf(key, sizeof (key), "cache_hint.set%d.device",
 292                     setnumber);
 293                 if (cfg_get_cstring(cfg,  key,  buf, sizeof (buf)) < 0) {
 294                         /* error or not found */
 295                         break;
 296                 }
 297 
 298                 if (strcmp(buf, "system") == 0) {
 299                         cd = -1;
 300                 } else {
 301                         cd = get_cd(buf);
 302                         if (cd < 0)
 303                                 continue;
 304                 }
 305 
 306                 (void) snprintf(key, sizeof (key), "cache_hint.set%d.wrthru",
 307                     setnumber);
 308                 if (cfg_get_cstring(cfg,  key,  buf, sizeof (buf)) < 0)
 309                         continue;
 310 
 311                 if (atoi(buf) == 1) {
 312                         if (cd == -1) {
 313                                 /* Node hint */
 314                                 if (SDBC_IOCTL(SDBC_SET_NODE_HINT, NSC_WRTHRU,
 315                                     1, 0, 0, 0, &ustatus) == SPCS_S_ERROR) {
 316                                         (void) fprintf(stderr,
 317                                             gettext("%s: set system "
 318                                             "option failed\n"),
 319                                             progname);
 320                                         sdbc_report_error(&ustatus);
 321                                         exit(1);
 322                                 }
 323                         } else if (SDBC_IOCTL(SDBC_SET_CD_HINT, cd,
 324                             NSC_WRTHRU, 1, 0, 0, &ustatus) == SPCS_S_ERROR) {
 325                                 (void) fprintf(stderr,
 326                                     gettext("%s: set option failed\n"),
 327                                     progname);
 328                                 sdbc_report_error(&ustatus);
 329                                 exit(1);
 330                         }
 331                 }
 332 
 333                 (void) snprintf(key, sizeof (key), "cache_hint.set%d.nordcache",
 334                     setnumber);
 335                 if (cfg_get_cstring(cfg,  key,  buf, sizeof (buf)) < 0)
 336                         continue;
 337 
 338                 if (atoi(buf) == 1) {
 339                         if (cd == -1) {
 340                                 /* Node hint */
 341                                 if (SDBC_IOCTL(SDBC_SET_NODE_HINT, NSC_NOCACHE,
 342                                     1, 0, 0, 0, &ustatus) == SPCS_S_ERROR) {
 343                                         (void) fprintf(stderr,
 344                                             gettext("%s: set system "
 345                                             "option failed\n"),
 346                                             progname);
 347                                         sdbc_report_error(&ustatus);
 348                                         exit(1);
 349                                 }
 350                         } else if (SDBC_IOCTL(SDBC_SET_CD_HINT, cd, NSC_NOCACHE,
 351                             1, 0, 0, &ustatus) == SPCS_S_ERROR) {
 352                                 (void) fprintf(stderr,
 353                                     gettext("%s: set option failed\n"),
 354                                     progname);
 355                                 sdbc_report_error(&ustatus);
 356                                 exit(1);
 357                         }
 358                 }
 359         }
 360 
 361         cfg_close(cfg);
 362 }
 363 
 364 void
 365 sdbc_set_maxdev()
 366 {
 367         spcs_s_info_t ustats;
 368 
 369         if (SDBC_IOCTL(SDBC_MAXFILES, &sdbc_max_devices,
 370             0, 0, 0, 0, &ustats) == SPCS_S_ERROR) {
 371                 (void) fprintf(stderr, gettext("%s: get maxfiles failed\n"),
 372                     progname);
 373                 sdbc_report_error(&ustats);
 374                 exit(1);
 375         }
 376 }
 377 
 378 static void
 379 bitmapfs_print(void)
 380 {
 381         CFGFILE *cfg;
 382         char key[CFG_MAX_KEY], buf[CFG_MAX_BUF];
 383         int setnumber;
 384 
 385         cfg = cfg_open(NULL);
 386         if (cfg == NULL) {
 387                 (void) fprintf(stderr,
 388                     gettext("%s: unable to access configuration: %s\n"),
 389                     progname, cfg_error(NULL));
 390                 exit(1);
 391         }
 392 
 393         if (!cfg_lock(cfg, CFG_RDLOCK)) {
 394                 (void) fprintf(stderr,
 395                     gettext("%s: unable to lock configuration: %s\n"),
 396                     progname, cfg_error(NULL));
 397                 exit(1);
 398         }
 399 
 400         for (setnumber = 1; /*CSTYLED*/; setnumber++) {
 401                 (void) snprintf(key, sizeof (key),
 402                     "bitmaps.set%d.bitmap", setnumber);
 403                 buf[0] = 0;
 404 
 405                 if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) {
 406                         if (errno == ESRCH) {
 407                                 /* end of list */
 408                                 break;
 409                         }
 410 
 411                         (void) fprintf(stderr,
 412                             gettext("%s: error reading configuration: %s\n"),
 413                             progname, cfg_error(NULL));
 414                         exit(1);
 415                 }
 416 
 417                 (void) printf("%s\n", buf);
 418         }
 419 
 420         cfg_close(cfg);
 421 }
 422 
 423 
 424 static void
 425 bitmapfs_delete(char *bitmapfs)
 426 {
 427         CFGFILE *cfg;
 428         char key[CFG_MAX_KEY], buf[CFG_MAX_BUF];
 429         int setnumber;
 430         int commit = 0;
 431 
 432         cfg = cfg_open(NULL);
 433         if (cfg == NULL) {
 434                 (void) fprintf(stderr,
 435                     gettext("%s: unable to access configuration: %s\n"),
 436                     progname, cfg_error(NULL));
 437                 exit(1);
 438         }
 439 
 440         if (!cfg_lock(cfg, CFG_WRLOCK)) {
 441                 (void) fprintf(stderr,
 442                     gettext("%s: unable to lock configuration: %s\n"),
 443                     progname, cfg_error(NULL));
 444                 exit(1);
 445         }
 446 
 447         for (setnumber = 1; /*CSTYLED*/; setnumber++) {
 448                 (void) snprintf(key, sizeof (key),
 449                     "bitmaps.set%d.bitmap", setnumber);
 450                 buf[0] = 0;
 451 
 452                 if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) {
 453                         if (errno == ESRCH) {
 454                                 /* end of list */
 455                                 (void) fprintf(stderr,
 456                                     gettext("%s: %s not found "
 457                                     "in configuration\n"),
 458                                     progname, bitmapfs);
 459                                 break;
 460                         }
 461 
 462                         (void) fprintf(stderr,
 463                             gettext("%s: error reading configuration: %s\n"),
 464                             progname, cfg_error(NULL));
 465                         exit(1);
 466                 }
 467 
 468                 if (strcmp(bitmapfs, buf) == 0) {
 469                         (void) snprintf(key, sizeof (key),
 470                             "bitmaps.set%d", setnumber);
 471 
 472                         if (cfg_put_cstring(cfg, key, (char *)NULL, 0) < 0) {
 473                                 (void) fprintf(stderr,
 474                                     gettext("%s: unable to delete %s "
 475                                     "from configuration: %s\n"),
 476                                     progname, bitmapfs, cfg_error(NULL));
 477                         } else
 478                                 commit++;
 479 
 480                         break;
 481                 }
 482         }
 483 
 484         if (commit) {
 485                 if (!cfg_commit(cfg)) {
 486                         (void) fprintf(stderr,
 487                             gettext("%s: unable to write "
 488                             "to configuration: %s\n"),
 489                             progname, cfg_error(NULL));
 490                 }
 491                 commit = 0;
 492         }
 493 
 494         cfg_close(cfg);
 495 }
 496 
 497 
 498 /*
 499  * User visible configuration.
 500  */
 501 
 502 static const struct {
 503         const char *tag;        /* libcfg tag */
 504         const char *name;       /* user presented name */
 505         const char *help;       /* explanation string */
 506 } sdbc_cfg_options[] = {
 507         { "thread", "nthreads", "number of threads" },
 508         { "size", "cache_size", "total cache size" },
 509 #ifdef DEBUG
 510         { "write_cache", "write_cache_size", "write cache size" },
 511         { "fill_pattern", "fill_pattern", "debug fill pattern" },
 512         { "reserved1", "reserved1", "unavailable, do not use" },
 513         { "iobuf", "niobuf", "number of io buffers" },
 514         { "tdemons", "ntdeamons", "number of sd_test daemons" },
 515         { "forced_wrthru", "forced_wrthru", "override wrthru detection" },
 516         { "no_forced_wrthru", "no_forced_wrthru", "override wrthru"},
 517 #endif
 518         { NULL }
 519 };
 520 
 521 
 522 static int
 523 configure_sdbc(int argc, char *argv[], int optind)
 524 {
 525         CFGFILE *cfg;
 526         char key[CFG_MAX_KEY], buf[CFG_MAX_BUF];
 527         char *cp, option[CFG_MAX_BUF], value[CFG_MAX_BUF];
 528         const int opt_width = 20;
 529         int error, found, commit;
 530         int i;
 531 
 532         error = commit = 0;
 533 
 534         cfg = cfg_open(NULL);
 535         if (cfg == NULL) {
 536                 (void) fprintf(stderr, "%s: unable to open configuration: %s",
 537                     progname, cfg_error(NULL));
 538                 return (1);
 539         }
 540 
 541         if (argc == optind) {
 542                 /* display current user visible config */
 543 
 544                 if (!cfg_lock(cfg, CFG_RDLOCK)) {
 545                         (void) fprintf(stderr,
 546                             gettext("%s: unable to lock configuration: %s\n"),
 547                             progname, cfg_error(NULL));
 548                         error = 1;
 549                         goto out;
 550                 }
 551 
 552                 convert_config(cfg, CFG_RDLOCK);
 553 
 554                 for (i = 0; sdbc_cfg_options[i].tag != NULL; i++) {
 555                         (void) snprintf(key, sizeof (key),
 556                             "scm.set1.%s", sdbc_cfg_options[i].tag);
 557                         if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) {
 558                                 if (errno == ESRCH) {
 559                                         /* not found */
 560                                         (void) strcpy(buf, "");
 561                                 } else {
 562                                         (void) fprintf(stderr,
 563                                             gettext("%s: error reading "
 564                                             "configuration: %s\n"),
 565                                             progname, cfg_error(NULL));
 566                                         error = 1;
 567                                         goto out;
 568                                 }
 569                         }
 570 
 571                         (void) printf("%-*s: %-*s /* %s */\n",
 572                             opt_width, sdbc_cfg_options[i].name,
 573                             opt_width, buf, sdbc_cfg_options[i].help);
 574                 }
 575         } else {
 576                 if (!cfg_lock(cfg, CFG_WRLOCK)) {
 577                         (void) fprintf(stderr,
 578                             gettext("%s: unable to lock configuration: %s\n"),
 579                             progname, cfg_error(NULL));
 580                         error = 1;
 581                         goto out;
 582                 }
 583 
 584                 convert_config(cfg, CFG_WRLOCK);
 585 
 586                 for (/*CSTYLED*/; optind < argc; optind++) {
 587                         (void) strncpy(option, argv[optind], sizeof (option));
 588                         option[sizeof (option) - 1] = '\0';     /* terminate */
 589 
 590                         cp = strchr(option, '=');
 591                         if (cp != NULL) {
 592                                 *cp = '\0';     /* terminate option */
 593                                 cp++;
 594                                 (void) strncpy(value, cp, sizeof (value));
 595                                 value[sizeof (value) - 1] = '\0';
 596 
 597                                 if (*value == '\0')
 598                                         (void) strncpy(value, "-",
 599                                             sizeof (value));
 600                         }
 601 
 602                         found = 0;
 603                         for (i = 0; sdbc_cfg_options[i].tag != NULL; i++) {
 604                                 if (strcmp(option,
 605                                     sdbc_cfg_options[i].name) == 0) {
 606                                         found = 1;
 607                                         break;
 608                                 }
 609                         }
 610 
 611                         if (!found) {
 612                                 (void) fprintf(stderr,
 613                                     gettext("%s: unknown configuration "
 614                                     "parameter: %s\n"), progname, option);
 615                                 continue;
 616                         }
 617 
 618                         (void) snprintf(key, sizeof (key),
 619                             "scm.set1.%s", sdbc_cfg_options[i].tag);
 620                         if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) {
 621                                 (void) fprintf(stderr,
 622                                     gettext("%s: error reading "
 623                                     "configuration: %s\n"),
 624                                     progname, cfg_error(NULL));
 625                                 error = 1;
 626                                 goto out;
 627                         }
 628 
 629                         if (*buf == '\0')
 630                                 (void) strncpy(buf, "<default>", sizeof (buf));
 631 
 632                         if (cp != NULL) {
 633                                 char *tmp;
 634                                 long val;
 635                                 /* set to new value */
 636 
 637                                 if (strcmp(value, "-")) { /* default ? */
 638 
 639                                         val = strtol(value, &tmp, 0);
 640                                         if (strcmp(value, tmp) == 0) {
 641                                                 (void) fprintf(stderr,
 642                                                     gettext(
 643                                                     "%s: bad value (%s) "
 644                                                     "for option %s\n"),
 645                                                     progname, value, option);
 646                                                 error = 1;
 647                                                 goto out;
 648                                         }
 649 
 650                                         /* make sure cache size is valid */
 651                                         if (strcmp(key, "scm.set1.size") == 0) {
 652                                                 if (val > MAX_CACHE_SIZE) {
 653                                                         PRINT_CACHE_SZ_ERR(val);
 654 
 655                                                         /*
 656                                                          * Overwrite the
 657                                                          * cache size with
 658                                                          * the maximum cache
 659                                                          * size.
 660                                                          */
 661                                                         (void) snprintf(value,
 662                                                             sizeof (value),
 663                                                             "%ld",
 664                                                             (long)
 665                                                             MAX_CACHE_SIZE);
 666                                                 }
 667                                         }
 668                                 }
 669 
 670                                 if (cfg_put_cstring(cfg, key, value,
 671                                     strlen(value)) < 0) {
 672                                         (void) fprintf(stderr,
 673                                             gettext("\n%s: error writing "
 674                                             "configuration: %s\n"),
 675                                             progname, cfg_error(NULL));
 676                                         error = 1;
 677                                         goto out;
 678                                 }
 679 
 680                                 (void) snprintf(buf, sizeof (buf),
 681                                     "%s = %s", buf,
 682                                     (strcmp(value, "-") == 0) ?
 683                                     "<default>" : value);
 684 
 685                                 commit = 1;
 686                         }
 687 
 688                         (void) printf("%-*s: %-*s /* %s */\n",
 689                             opt_width, sdbc_cfg_options[i].name,
 690                             opt_width, buf, sdbc_cfg_options[i].help);
 691                 } /* end command line args */
 692         }
 693 
 694 out:
 695         if (commit) {
 696                 if (!cfg_commit(cfg)) {
 697                         (void) fprintf(stderr,
 698                             gettext("%s: unable to write "
 699                             "to configuration: %s\n"),
 700                             progname, cfg_error(NULL));
 701                 }
 702                 commit = 0;
 703 
 704                 (void) printf("\n%s\n",
 705                     gettext("Changed configuration parameters "
 706                     "will take effect when the cache is restarted"));
 707         }
 708 
 709         cfg_close(cfg);
 710         return (error);
 711 }
 712 
 713 
 714 static char *
 715 cd_to_device(int cd)
 716 {
 717         static _sd_stats_t *cs_cur = NULL;
 718         spcs_s_info_t ustatus;
 719 
 720         if (cs_cur == NULL) {
 721                 cs_cur = malloc(sizeof (_sd_stats_t) +
 722                     (sdbc_max_devices - 1) * sizeof (_sd_shared_t));
 723 
 724                 if (cs_cur == NULL) {
 725                         (void) fprintf(stderr, gettext("%s malloc: %s\n"),
 726                             progname, strerror(errno));
 727                         exit(1);
 728                 }
 729         }
 730 
 731         if (SDBC_IOCTL(SDBC_STATS, cs_cur, 0, 0, 0, 0,
 732             &ustatus) == SPCS_S_ERROR) {
 733                 (void) fprintf(stderr,
 734                     gettext("%s: stats ioctl failed\n"), progname);
 735                 sdbc_report_error(&ustatus);
 736                 exit(1);
 737         }
 738         if (cs_cur->st_cachesize == 0 || cd >= cs_cur->st_count)
 739                 return ("");
 740 
 741         return (cs_cur->st_shared[cd].sh_filename);
 742 }
 743 
 744 /*
 745  * takes either either a string containing the cd or the device name, and
 746  * returns the device name.
 747  */
 748 static char *
 749 get_device_name(char *arg)
 750 {
 751         long cd = 0;
 752         char *device;
 753 
 754         /* if the arg has a leading '/', assume it's a valid device name */
 755         if (!arg || *arg == '/') {
 756                 return (arg);
 757         }
 758 
 759         /* treat the "all" keyword as a valid device name */
 760         if (strcmp(arg, "all") == 0) {
 761                 return (arg);
 762         }
 763 
 764         /*
 765          * Next, assume it's a cd, and try to convert it to an integer, and
 766          * subsequently convert that cd to its corresponding device name.
 767          *
 768          * Since strtol returns 0 on failure, we need to make a special case
 769          * for a cd of "0", which is valid.
 770          */
 771         if (((cd = strtol(arg, (char **)NULL, 10)) > 0) ||
 772             strcmp(arg, "0") == 0) {
 773                 device = cd_to_device((int)cd);
 774 
 775                 /* cd_to_device returns NULL or "" on failure--check both */
 776                 if (device && (strcmp(device, ""))) {
 777                         /* it seems to be a valid device name */
 778                         return (device);
 779                 }
 780         }
 781 
 782         return (NULL);
 783 }
 784 
 785 static void
 786 remove_hint(char *device)
 787 {
 788         CFGFILE *cfg;
 789         char key[CFG_MAX_KEY], buf[CFG_MAX_BUF];
 790         int setnumber;
 791         int rc;
 792 
 793         if ((cfg = cfg_open(NULL)) == NULL) {
 794                 (void) fprintf(stderr,
 795                     gettext("%s: unable to access configuration: %s\n"),
 796                     progname, cfg_error(NULL));
 797                 exit(1);
 798         }
 799         if (!cfg_lock(cfg, CFG_WRLOCK)) {
 800                 (void) fprintf(stderr,
 801                     gettext("%s: unable to lock configuration: %s\n"),
 802                     progname, cfg_error(NULL));
 803                 exit(1);
 804         }
 805 
 806         for (setnumber = 1; /*CONSTCOND*/ TRUE; setnumber++) {
 807                 (void) snprintf(key, sizeof (key), "cache_hint.set%d.device",
 808                     setnumber);
 809                 if (cfg_get_cstring(cfg,  key,  buf, sizeof (buf)) < 0) {
 810                         /* error or not found */
 811                         break;
 812                 }
 813 
 814                 if (strcmp(device, buf) != 0)
 815                         continue;
 816 
 817                 /* remove config file entry */
 818                 (void) snprintf(key, sizeof (key),
 819                     "cache_hint.set%d", setnumber);
 820                 rc = cfg_put_cstring(cfg, key, NULL, 0);
 821                 if (rc < 0)
 822                         (void) fprintf(stderr,
 823                             gettext("%s: unable to update configuration "
 824                             "storage: %s"),
 825                             progname, cfg_error(NULL));
 826                 else if (!cfg_commit(cfg))
 827                         (void) fprintf(stderr,
 828                             gettext("%s: unable to update configuration "
 829                             "storage: %s"),
 830                             progname, cfg_error(NULL));
 831                 else
 832                         (void) fprintf(stderr,
 833                             gettext("%s: persistent hint for %s"
 834                             " removed from configuration\n"),
 835                             progname, device);
 836                 break;
 837         }
 838         cfg_close(cfg);
 839 }
 840 
 841 
 842 static void
 843 save_hint(int cd, int hint, int flag)
 844 {
 845         char device[NSC_MAXPATH];
 846         CFGFILE *cfg;
 847         char key[CFG_MAX_KEY], buf[CFG_MAX_BUF];
 848         int setnumber;
 849         int found;
 850         int rc;
 851 
 852         if (hint != NSC_WRTHRU && hint != NSC_NOCACHE)
 853                 return;
 854 
 855         if (flag != 0 && flag != 1)
 856                 return;
 857 
 858         if ((cfg = cfg_open(NULL)) == NULL) {
 859                 (void) fprintf(stderr,
 860                     gettext("%s: unable to access configuration: %s\n"),
 861                     progname, cfg_error(NULL));
 862                 exit(1);
 863         }
 864         if (!cfg_lock(cfg, CFG_WRLOCK)) {
 865                 (void) fprintf(stderr,
 866                     gettext("%s: unable to lock configuration: %s\n"),
 867                     progname, cfg_error(NULL));
 868                 exit(1);
 869         }
 870 
 871         if (cd == -1)
 872                 (void) strcpy(device, "system");
 873         else
 874                 (void) strncpy(device, cd_to_device(cd), NSC_MAXPATH);
 875 
 876         found = 0;
 877         for (setnumber = 1; /*CONSTCOND*/ TRUE; setnumber++) {
 878                 (void) snprintf(key, sizeof (key), "cache_hint.set%d.device",
 879                     setnumber);
 880                 if (cfg_get_cstring(cfg,  key,  buf, sizeof (buf)) < 0) {
 881                         /* error or not found */
 882                         break;
 883                 }
 884 
 885                 if (strcmp(device, buf) == 0) {
 886                         found = 1;
 887                         break;
 888                 }
 889         }
 890 
 891         if (found) {
 892                 if (hint == NSC_WRTHRU)
 893                         (void) snprintf(key, sizeof (key),
 894                             "cache_hint.set%d.wrthru", setnumber);
 895                 else /* NSC_NOCACHE */
 896                         (void) snprintf(key, sizeof (key),
 897                             "cache_hint.set%d.nordcache", setnumber);
 898                 if (flag == 0)
 899                         rc = cfg_put_cstring(cfg, key, "0", 1);
 900                 else
 901                         rc = cfg_put_cstring(cfg, key, "1", 1);
 902         } else {
 903                 (void) strncpy(buf, device, CFG_MAX_BUF);
 904                 if (flag == 0)
 905                         (void) strncat(buf, " 0 0", CFG_MAX_BUF);
 906                 else if (hint == NSC_WRTHRU)
 907                         (void) strncat(buf, " 1 0", CFG_MAX_BUF);
 908                 else /* NSC_NOCACHE */
 909                         (void) strncat(buf, " 0 1", CFG_MAX_BUF);
 910                 rc = cfg_put_cstring(cfg, "cache_hint", buf, sizeof (buf));
 911         }
 912 
 913         if (rc < 0)
 914                 (void) fprintf(stderr,
 915                     gettext("%s: unable to update configuration storage: %s"),
 916                     progname, cfg_error(NULL));
 917         else if (!cfg_commit(cfg))
 918                 (void) fprintf(stderr,
 919                     gettext("%s: unable to update configuration storage: %s"),
 920                     progname, cfg_error(NULL));
 921         cfg_close(cfg);
 922 }
 923 
 924 #ifdef lint
 925 int
 926 scmadm_lintmain(int argc, char *argv[])
 927 #else
 928 int
 929 main(int argc, char *argv[])
 930 #endif
 931 {
 932         int o = 0;
 933         int c;
 934         int errflg = 0;
 935         int hflag = 0;
 936         int qflag = 1;
 937         extern int optind;
 938         extern char *optarg;
 939         int cd;
 940         int hint;
 941         int flag;
 942         int optflag = 0;
 943         spcs_s_info_t ustats;
 944         int Dopt, Lopt;
 945         int Oopt = 0;
 946         char *bitmapfs = NULL;
 947         const char *exclusive = gettext(
 948             "-d, -e, -m, -o, -C, -D, -L, and -v "
 949             "are mutually exclusive\n");
 950 
 951         (void) setlocale(LC_ALL, "");
 952         (void) textdomain("scm");
 953 
 954         progname = strdup(basename(argv[0]));
 955 
 956         sdbc_set_maxdev();
 957 
 958         buildusage(progname);
 959 
 960         Dopt = Lopt = 0;
 961 
 962         while ((c = getopt(argc, argv,
 963 #ifdef DEBUG
 964             "gi:t:S"
 965 #endif
 966             "CD:LOa:devqhm:o:")) != EOF) {
 967 
 968                 switch (c) {
 969 
 970                 case 'D':
 971                         if (optflag) {
 972                                 (void) fprintf(stderr, exclusive);
 973                                 goto usage;
 974                         }
 975 
 976                         Dopt++;
 977                         optflag++;
 978                         bitmapfs = optarg;
 979                         break;
 980 
 981                 case 'L':
 982                         if (optflag) {
 983                                 (void) fprintf(stderr, exclusive);
 984                                 goto usage;
 985                         }
 986 
 987                         Lopt++;
 988                         optflag++;
 989                         break;
 990 
 991 #ifdef DEBUG
 992                 case 'S':
 993                         if (optflag) {
 994                                 (void) fprintf(stderr, exclusive);
 995                                 goto usage;
 996                         }
 997 
 998                         if (putenv(stats_usage) != 0) {
 999                                 (void) fprintf(stderr,
1000                                     gettext("%s: unable to putenv()\n"),
1001                                     progname);
1002                                 exit(1);
1003                         }
1004 
1005                         argv[1] = "scmadm";
1006                         if (execv(STATS_PATH, &argv[1]) == -1) {
1007                                 (void) fprintf(stderr,
1008                                     gettext("%s: failed to execute " STATS_PATH
1009                                         "\n"), progname);
1010                                 (void) fprintf(stderr,
1011                                     gettext("Please be sure to copy sd_stats"
1012                                         " from src/cmd/ns/sdbc in a development"
1013                                         " workspace\n"));
1014                         }
1015                         exit(0);
1016                         break;
1017 #endif
1018                 case 'a':
1019                         (void) strcpy(alert_file, optarg);
1020                         break;
1021                 case 'q':
1022                         qflag++;
1023                         break;
1024                 case 'O': /* restore hints */
1025                         Oopt++;
1026                         break;
1027                 case 'C': /* configure */
1028                 case 'e': /* enable */
1029                 case 'd': /* disable */
1030                 case 'v': /* get version */
1031                 case 'o': /* get/set options */
1032                 case 'm': /* get cd map */
1033 #ifdef DEBUG
1034                 case 't': /* trace */
1035                 case 'i': /* inject_ioerr */
1036                 case 'c': /* clear_ioerr */
1037                 case 'g': /* toggle_flush */
1038 #endif
1039                         if (optflag) {
1040                                 (void) fprintf(stderr,
1041 #ifdef DEBUG
1042                                     "%s%s", gettext("-t, -i, -c, -g, "),
1043 #endif
1044                                     exclusive);
1045 
1046                                 errflg++;
1047                         }
1048                         optflag++;
1049                         o = c;
1050                         break;
1051                 case 'h':
1052                         hflag = 1;
1053                         break;
1054                 case '?':
1055                 default:
1056                         errflg++;
1057                         break;
1058                 }
1059                 if (errflg || hflag)
1060                         goto usage;
1061         }
1062 
1063         if (Oopt) {
1064                 /* Set hints saved in persistent configuration */
1065                 restore_hints();
1066                 exit(0);
1067         }
1068         if (Dopt || Lopt) {
1069                 /* bitmapfs control */
1070 
1071                 if (iscluster()) {
1072                         (void) fprintf(stderr,
1073                             gettext("%s: bitmap filesystems are not "
1074                             "allowed in a cluster\n"), progname);
1075                         goto usage;
1076                 }
1077 
1078                 if ((Dopt + Lopt) > 1) {
1079                         (void) fprintf(stderr, gettext("-D and -L are"
1080                             "mutually exclusive\n"));
1081                         goto usage;
1082                 }
1083 
1084                 if (Lopt)
1085                         bitmapfs_print();
1086                 else /* if (Dopt) */
1087                         bitmapfs_delete(bitmapfs);
1088 
1089                 exit(0);
1090         }
1091 
1092         if (!o) {
1093                 if (argc > 1)
1094                         goto usage;
1095                 (void) printf(gettext("%s: Printing all cd's and options:\n"),
1096                     progname);
1097                 print_all_options();
1098         }
1099 
1100         /* Configure */
1101         if (o == 'C') {
1102                 exit(configure_sdbc(argc, argv, optind));
1103         }
1104         /* enable */
1105         if (o == 'e') {
1106                 enable_sdbc();
1107                 if (qflag == 0)
1108                         sd_gather_alert_dumps();
1109                 exit(0);
1110         }
1111         /* disable */
1112         if (o == 'd') {
1113                 disable_sdbc();
1114                 exit(0);
1115         }
1116         /* get version */
1117         if (o == 'v') {
1118                 get_version();
1119                 exit(0);
1120         }
1121         /* node_hint or cd_hint */
1122         if (o == 'o') {
1123                 if (!(strcoll(optarg, "system"))) {  /* node_hint */
1124                         if ((optind - 1) == (argc - 1)) {  /* get */
1125                                 if ((hint = SDBC_IOCTL(SDBC_GET_NODE_HINT, 0, 0,
1126                                     0, 0, 0, &ustats)) == SPCS_S_ERROR) {
1127                                         (void) fprintf(stderr,
1128                                             gettext("%s: get system "
1129                                             "options failed\n"),
1130                                             progname);
1131                                         sdbc_report_error(&ustats);
1132                                         exit(1);
1133                                 }
1134 #ifdef WRTHRU_HINTS
1135                                 (void) printf(gettext("System Status: "));
1136                                 print_hint(hint, 1);
1137 #endif
1138                                 (void) printf(gettext("System Options: "));
1139                                 print_hint(hint, 0);
1140                                 exit(0);
1141                         } else {  /* set, clear */
1142                                 if (get_hint(argv[optind], &hint, &flag) == -1)
1143                                         goto usage;
1144                                 if (hint == -1) {
1145                                         /* remove hint from config */
1146                                         remove_hint("system");
1147                                         exit(0);
1148                                 }
1149 
1150                                 if (SDBC_IOCTL(SDBC_SET_NODE_HINT, hint, flag,
1151                                     0, 0, 0, &ustats) == SPCS_S_ERROR) {
1152                                         (void) fprintf(stderr,
1153                                             gettext("%s: set system "
1154                                             "option failed\n"),
1155                                             progname);
1156                                         sdbc_report_error(&ustats);
1157                                         exit(1);
1158                                 }
1159                                 save_hint(-1, hint, flag);
1160                                 (void) printf(gettext("%s: System option %s"
1161                                     " now set.\n"), progname, argv[optind]);
1162                                 exit(0);
1163                         }
1164                 } else {  /* cd_hint */
1165                         cd = get_cd(optarg);
1166                         if ((optind - 1) == (argc - 1)) {  /* get */
1167                                 if (cd < 0) {
1168                                         (void) fprintf(stderr,
1169                                             gettext("%s: device %s not "
1170                                             "found\n"),
1171                                             progname, optarg);
1172                                         exit(1);
1173                                 }
1174                                 hint = get_cd_hint(cd);
1175                                 (void) printf(gettext("%s: cd(%d) Current "
1176                                     "options are: "), progname, cd);
1177                                 print_hint(hint, 0);
1178                                 exit(0);
1179                         } else { /* set, clear */
1180                                 if (get_hint(argv[optind], &hint, &flag) == -1)
1181                                         goto usage;
1182                                 if (hint == -1) {
1183                                         /* remove hint from config */
1184                                         if (cd < 0)
1185                                                 remove_hint(optarg);
1186                                         else
1187                                                 remove_hint(cd_to_device(cd));
1188                                         exit(0);
1189                                 }
1190                                 if (cd < 0) {
1191                                         (void) fprintf(stderr,
1192                                             gettext("%s: device %s not "
1193                                             "found\n"),
1194                                             progname, optarg);
1195                                         exit(1);
1196                                 }
1197 
1198                                 if (SDBC_IOCTL(SDBC_SET_CD_HINT, cd, hint,
1199                                     flag, 0, 0, &ustats) == SPCS_S_ERROR) {
1200                                         (void) fprintf(stderr,
1201                                             gettext("%s: set option "
1202                                             "failed\n"), progname);
1203                                         sdbc_report_error(&ustats);
1204                                         exit(1);
1205                                 }
1206                                 save_hint(cd, hint, flag);
1207                                 (void) printf(gettext("%s: cd %d option %s now"
1208                                     " set.\n"), progname, cd, argv[optind]);
1209                                 exit(0);
1210                         }
1211                 }
1212         }
1213 
1214         if (o == 'm') {   /* "get_cd" = map */
1215                 char *dev_name;
1216 
1217                 if (!(strcoll(optarg, "all"))) /* all */
1218                         (void) get_cd_all();
1219                 else {
1220                         cd = get_cd(optarg);
1221                         if (cd < 0) {
1222                                 (void) fprintf(stderr,
1223                                     gettext("%s: device or cd %s not found\n"),
1224                                     progname, optarg);
1225                                 exit(1);
1226                         }
1227 
1228                         if ((dev_name = get_device_name(optarg)) == NULL) {
1229                                 (void) fprintf(stderr, gettext(
1230                                     "%s: device for cd %d not found\n"),
1231                                     progname, cd);
1232                                 exit(1);
1233                         }
1234 
1235                         (void) printf(gettext("%s: diskname %s; cd %d\n"),
1236                             progname, dev_name, cd);
1237                         exit(0);
1238                 }
1239         }
1240 
1241 #ifdef DEBUG
1242         if (o == 't') { /* "trace" */
1243                 int flag, value;
1244                 _sdtr_table_t tt;
1245                 if ((optind+1) != (argc-1))
1246                         goto usage;
1247                 cd = get_cd(argv[optind]);
1248                 if (cd < 0) {
1249                         (void) fprintf(stderr,
1250                             gettext("%s: device or cd %s not found\n"),
1251                             progname, argv[optind]);
1252                         exit(1);
1253                 }
1254 
1255                 value = strtol(argv[optind+1], 0, 0);
1256                 if (!(strcoll(optarg, gettext("size")))) {
1257                         flag = SD_SET_SIZE;
1258                         tt.tt_max = value;
1259                 } else if (!(strcoll(optarg, gettext("mask")))) {
1260                         flag = SD_SET_MASK;
1261                         tt.tt_mask = value;
1262                 } else if (!(strcoll(optarg, gettext("lbolt")))) {
1263                         flag = SD_SET_LBOLT;
1264                         tt.tt_lbolt = value;
1265                 } else if (!(strcoll(optarg, gettext("good")))) {
1266                         flag = SD_SET_GOOD;
1267                         tt.tt_good = value;
1268                 } else  goto usage;
1269 
1270                 if (SDBC_IOCTL(SDBC_ADUMP, (long)cd, &tt, NULL, 0L,
1271                     (long)flag, &ustats) == SPCS_S_ERROR) {
1272                         (void) fprintf(stderr,
1273                             gettext("%s: trace %s failed\n"),
1274                             progname, optarg);
1275                         sdbc_report_error(&ustats);
1276                         exit(1);
1277                 }
1278                 (void) printf(gettext("%s: trace %s processed\n"),
1279                     progname, optarg);
1280                 if (cd != -1)
1281                         (void) printf(gettext(" cd %d; size %d; mask 0x%04x; "
1282                             "lbolt %d; good %d;\n"),
1283                             cd, tt.tt_max, tt.tt_mask,
1284                             tt.tt_lbolt, tt.tt_good);
1285                 exit(0);
1286         }
1287 
1288         if (o == 'i') { /* "inject_ioerr" */
1289                 int ioj_err = EIO;
1290                 int cd;
1291                 int ioj_cnt = 0;
1292 
1293                 /* a cd of "-1" represents all devices */
1294                 if (strcmp(optarg, "-1") == 0) {
1295                         cd = -1;
1296                 } else if ((cd = get_cd(optarg)) < 0) {
1297                         (void) fprintf(stderr,
1298                             gettext("%s: device or cd %s not found\n"),
1299                             progname, optarg);
1300                         exit(1);
1301                 }
1302                 if (argc == 4)
1303                         ioj_err = strtol(argv[optind], 0, 0);
1304                 if (argc == 5)
1305                         ioj_cnt = strtol(argv[optind+1], 0, 0);
1306 
1307                 if (SDBC_IOCTL(SDBC_INJ_IOERR, cd, ioj_err, ioj_cnt, 0, 0,
1308                     &ustats) == SPCS_S_ERROR)  {
1309                         (void) fprintf(stderr,
1310                             gettext("%s: i/o error injection for cd %s "
1311                             "failed\n"), progname, optarg);
1312                         sdbc_report_error(&ustats);
1313                         exit(1);
1314                 }
1315                 (void) printf(gettext("%s: i/o error injection cd %d errno %d "
1316                     "processed\n"), progname, cd, ioj_err);
1317                 exit(0);
1318         }
1319 
1320         if (o == 'c') { /* "clear_ioerr" */
1321                 int cd;
1322 
1323                 /* a cd of "-1" represents all devices */
1324                 if (strcmp(optarg, "-1") == 0) {
1325                         cd = -1;
1326                 } else if ((cd = get_cd(optarg)) < 0) {
1327                         (void) fprintf(stderr,
1328                             gettext("%s: device or cd %s not found\n"),
1329                             progname, optarg);
1330                         exit(1);
1331                 }
1332 
1333                 if (SDBC_IOCTL(SDBC_CLR_IOERR, cd, 0, 0, 0, 0, &ustats)
1334                     == SPCS_S_ERROR) {
1335                         (void) fprintf(stderr,
1336                             gettext("%s: i/o error clear %s failed\n"),
1337                             progname, optarg);
1338                         sdbc_report_error(&ustats);
1339                         exit(1);
1340                 }
1341                 (void) printf(gettext("%s: i/o error clear for cd %d "
1342                     "processed\n"), progname, cd);
1343                 exit(0);
1344         }
1345 
1346         if (o == 'g') { /* "toggle_flush" */
1347                 flag = toggle_flush();
1348                 (void) printf(gettext("%s: sdbc cache flush now %s\n"),
1349                     progname, flag ? "on" : "off");
1350                 exit(0);
1351         }
1352 #endif /* DEBUG */
1353 
1354         return (0);
1355 usage:
1356         (void) fprintf(stderr, "%s\n", scmadmUsage);
1357         if (hflag) {
1358                 return (0);
1359         }
1360         return (1);
1361 }
1362 
1363 
1364 #define addusage(f__)   \
1365         (void) strncat(scmadmUsage, f__, sizeof (scmadmUsage));
1366 
1367 #define addusage1(f__, a__)     \
1368         (void) snprintf(fmt, sizeof (fmt), "%s%s", scmadmUsage, f__);   \
1369         (void) snprintf(scmadmUsage, sizeof (scmadmUsage), fmt, a__);
1370 
1371 #define addusage2(f__, a__, b__)        \
1372         (void) snprintf(fmt, sizeof (fmt), "%s%s", scmadmUsage, f__);   \
1373         (void) snprintf(scmadmUsage, sizeof (scmadmUsage), fmt, a__, b__);
1374 
1375 static void
1376 buildusage(char *p)
1377 {
1378         char fmt[USAGELEN];
1379 #ifdef WRTHRU_HINTS
1380         char *hints_str = "[nordcache|rdcache|wrthru|nowrthru|forget]\n";
1381 #else
1382         char *hints_str = "[nordcache|rdcache|forget]\n";
1383 #endif
1384 
1385         bzero(scmadmUsage, sizeof (scmadmUsage));
1386         bzero(fmt, sizeof (fmt));
1387 
1388         addusage(gettext("Usage :\n"));
1389         addusage1(gettext("\t%s\n"), p);
1390         addusage1(gettext("\t%s -h\n"), p);
1391         addusage1(gettext("\t%s -e\n"), p);
1392         addusage1(gettext("\t%s -d\n"), p);
1393         addusage1(gettext("\t%s -v\n"), p);
1394         addusage1(gettext("\t%s {-L | -D bitmapfs}\n"), p);
1395         addusage1(gettext("\t%s -C [parameter[=[value]] ...]\n"), p);
1396         addusage2(gettext("\t%s -o system %s"), p, hints_str);
1397         addusage2(gettext("\t%s -o <cd> %s"), p, hints_str);
1398         addusage2(gettext("\t%s -o <diskname> %s"), p, hints_str);
1399         addusage1(gettext("\t%s -m {<cd>|<diskname>|all}\n"), p);
1400 #ifdef DEBUG
1401         addusage1(gettext(
1402             "\t%s -S [-Mz] [-d delay_time] [-l logfile] [-r range]\n"), p);
1403         addusage1(gettext(
1404             "\t%s -t {size|mask|lbolt|good} <cd|diskname> <value>\n"), p);
1405         addusage1(gettext("\t%s -g\n"), p);
1406         addusage1(gettext(
1407             "\t%s -i {cd|diskname|-1 for all} [errno [countdown]]\n"), p);
1408         addusage1(gettext("\t%s -c {cd|diskname|-1 for all}\n"), p);
1409         addusage(gettext("\nt = trace\tg = toggle_flush\ti = inject ioerr\n"
1410             "c = clear ioerr\tS = stats\n"));
1411 #endif /* DEBUG */
1412         addusage(gettext(
1413             "e = enable\td = disable\tv=version\to = get/ set options\n"));
1414         addusage(gettext(
1415             "m = get cd map\n"));
1416         addusage1(gettext(
1417             "note: cd is a cache descriptor integer in the range [0-%d]\n"),
1418             sdbc_max_devices - 1);
1419         addusage(gettext(
1420             "      bitmapfs is a block device or filesystem mount point\n"));
1421 
1422 #ifdef DEBUG
1423         (void) snprintf(stats_usage, sizeof (stats_usage),
1424             "SD_STATS_USAGE=%s", scmadmUsage);
1425 #endif
1426 }
1427 
1428 static int
1429 get_hint(char *str,  int *hint, int *flag)
1430 {
1431 #ifdef WRTHRU_HINTS
1432         if (!(strcoll(str, gettext("wrthru")))) {
1433                 *hint = NSC_WRTHRU;
1434                 *flag = 1;
1435                 return (0);
1436         } else if (!(strcoll(str, gettext("nowrthru")))) {
1437                 *hint =  NSC_WRTHRU;
1438                 *flag = 0;
1439                 return (0);
1440         } else
1441 #endif
1442         if (!(strcoll(str, gettext("nordcache")))) {
1443                 *hint = NSC_NOCACHE;
1444                 *flag = 1;
1445                 return (0);
1446         } else if (!(strcoll(str, gettext("rdcache")))) {
1447                 *hint = NSC_NOCACHE;
1448                 *flag = 0;
1449                 return (0);
1450         } else if (!(strcoll(str, gettext("forget")))) {
1451                 *hint = -1;
1452                 *flag = 0;
1453                 return (0);
1454         }
1455         return (-1);
1456 }
1457 
1458 /*ARGSUSED*/
1459 void
1460 print_hint(const uint_t type, const int status)
1461 {
1462 #ifdef WRTHRU_HINTS
1463         if (status) {
1464                 if (type & NSC_FORCED_WRTHRU) {
1465                         (void) printf(gettext("Fast Writes Overridden\n"));
1466                 } else {
1467                         /* if (type & NSC_NO_FORCED_WRTHRU) */
1468                         (void) printf(gettext("default\n"));
1469                 }
1470         } else {
1471                 (void) printf("%swrthru, %srdcache",
1472                     (type & (NSC_FORCED_WRTHRU|NSC_WRTHRU)) ? "" : "no",
1473                     (type & NSC_NOCACHE) ? "no" : "");
1474 #else
1475         {
1476                 (void) printf("%srdcache", (type & NSC_NOCACHE) ? "no" : "");
1477 #endif
1478 
1479                 if (type & 0x80000000)
1480                         (void) printf(" (overridden by system)");
1481 
1482                 (void) printf("\n");
1483         }
1484 }
1485 
1486 /*
1487  * Read the configuration via libcfg
1488  */
1489 
1490 int
1491 get_cache_config()
1492 {
1493         int i;
1494         int sysid;
1495         CFGFILE *cfg;
1496         char buf[CFG_MAX_BUF];
1497         char key[CFG_MAX_KEY];
1498 
1499 
1500         if ((cfg = cfg_open(NULL)) == NULL) {
1501                 (void) fprintf(stderr,
1502                     gettext("Cannot open configuration file\n"));
1503                 exit(1);
1504         }
1505 
1506         if (!cfg_lock(cfg, CFG_RDLOCK)) {
1507                 (void) fprintf(stderr,
1508                     gettext("Cannot lock configuration file\n"));
1509                 exit(1);
1510         }
1511 
1512         convert_config(cfg, CFG_RDLOCK);
1513         (void) memset((char *)&user_level_conf, 0, sizeof (_sd_cache_param_t));
1514 
1515         /* Get the system ID */
1516         if (nsc_getsystemid(&sysid) < 0) {
1517                 (void) fprintf(stderr,
1518                     gettext("%s Unable to obtain subsystem ID: %s\n"),
1519                     progname, strerror(errno));
1520                 exit(1);
1521         }
1522         myid = sysid;
1523 
1524         user_level_conf.blk_size = 8192;        /* DEFAULT */
1525         user_level_conf.procs = 16;     /* DEFAULT */
1526         user_level_conf.reserved1 = RESERVED1_DEFAULTS;
1527 
1528         bzero(buf, CFG_MAX_BUF);
1529         (void) snprintf(key, sizeof (key), "scm.set1.thread");
1530         if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) {
1531                 user_level_conf.threads = atoi(buf);
1532         } else
1533                 user_level_conf.threads = 128;  /* DEFAULT */
1534 
1535         (void) snprintf(key, sizeof (key), "scm.set1.tdemons");
1536         if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) {
1537                 user_level_conf.test_demons = atoi(buf);
1538         }
1539 
1540         (void) snprintf(key, sizeof (key), "scm.set1.write_cache");
1541         if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) {
1542                 user_level_conf.write_cache = atoi(buf);
1543         }
1544 
1545         (void) snprintf(key, sizeof (key), "scm.set1.size");
1546         if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) {
1547                 /*
1548                  * We need to run strtol for backwards compatibility in 3.2.
1549                  * A workaround for this bug was put in 3.2 which allowed
1550                  * customers to set the cache size up to 1024 if it was
1551                  * specified in hexadecimal. Decimal still had the limit
1552                  * of 128.  This change treats them both identically.
1553                  */
1554                 user_level_conf.cache_mem[0] = (int)strtol(buf, NULL, 0);
1555                 if (user_level_conf.cache_mem[0] > MAX_CACHE_SIZE) {
1556                         (void) fprintf(stderr, gettext(
1557                             "The cache size of %ld is larger than "
1558                             "the system maximum of %ld.\nUse \"scmadm -C "
1559                             "cache_size=<size>\" to set the size to a proper "
1560                             "value.\n"),
1561                             user_level_conf.cache_mem[0], MAX_CACHE_SIZE);
1562                         user_level_conf.cache_mem[0] = MAX_CACHE_SIZE;
1563                 }
1564         }
1565 
1566         (void) snprintf(key, sizeof (key), "scm.set1.iobuf");
1567         if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) {
1568                 user_level_conf.iobuf = atoi(buf);
1569         }
1570 
1571         (void) snprintf(key, sizeof (key), "scm.set1.fill_pattern");
1572         if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) {
1573                 user_level_conf.fill_pattern = atoi(buf);
1574                 user_level_conf.gen_pattern = 1;
1575         }
1576 
1577         (void) snprintf(key, sizeof (key), "scm.set1.no_forced_wrthru");
1578         if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) {
1579                 no_forced_wrthru = atoi(buf);
1580         }
1581 
1582         (void) snprintf(key, sizeof (key), "scm.set1.forced_wrthru");
1583         if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) {
1584                 forced_wrthru = atoi(buf);
1585         }
1586 
1587         (void) snprintf(key, sizeof (key), "scm.set1.reserved1");
1588         if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) > 0) {
1589                 user_level_conf.reserved1 = atoi(buf);
1590         }
1591 
1592         cfg_close(cfg);
1593 
1594         /*
1595          * use the default minidsp configuration if no
1596          * node/mirror/remote-mirror/cluster line is in the sd.cf file
1597          */
1598         if (nodes_configured == 0)
1599                 check_and_set_mirrors(myid, _SD_NO_HOST);
1600 
1601 
1602         /* Check if our sysid was defined */
1603         if (!node_defined[myid]) {
1604                 (void) fprintf(stderr,
1605                     gettext("This node(%d) is not defined in config.\n"), myid);
1606                 exit(1);
1607         }
1608 
1609         /*
1610          * Save off number of nodes so we can calculate the point-to-point
1611          * segements.  Code in kernel currently supports MAX_SD_NODES
1612          */
1613         if ((user_level_conf.num_nodes = nodes_configured) >
1614             MAX_SD_NODES) {
1615                 (void) fprintf(stderr,
1616                     gettext("Cache can support only %d nodes(%d).\n"),
1617                     MAX_SD_NODES, nodes_configured);
1618                 exit(1);
1619         }
1620 
1621         if ((nodes_configured % 2) && !minidsp) {
1622                 if (nodes_configured == 1)
1623                         (void) fprintf(stderr,
1624                             gettext("Only one node configured, "
1625                             "mirror node must be %d\n"), _SD_NO_HOST);
1626                 else
1627                         (void) fprintf(stderr,
1628                             gettext("Cannot configure odd number of nodes.\n"));
1629                 exit(1);
1630         }
1631 
1632 
1633         /* Pass List of Nodes Configured to Cache */
1634         for (i = 0; i < nodes_configured; i++)
1635                 user_level_conf.nodes_conf[i] = nodes_conf[i];
1636 
1637         /* Place magic number in user_level_conf.  Kernel will test for it */
1638         user_level_conf.magic = _SD_MAGIC;
1639         (void) sleep(1);
1640         return (0);
1641 }
1642 
1643 _sdtr_t hdr;
1644 
1645 /* function name string */
1646 char *
1647 _sd_fname(int f)
1648 {
1649         int fn = f & ST_FUNC;
1650         static char c[8];
1651         char *s;
1652 
1653         if (f & ST_BCACHE)
1654                 s = _bcache_fname[fn];
1655         else if (f & ST_BSUB)
1656                 s = _bsub_fname[fn];
1657         else if (f & ST_IO)
1658                 s = _io_fname[fn];
1659         else if (f & ST_STATS)
1660                 s = _stats_fname[fn];
1661         else if (f & ST_CCIO)
1662                 s = _ccio_fname[fn];
1663         else if (f & ST_FT)
1664                 s = _ft_fname[fn];
1665         else if (f & ST_INFO)
1666                 s = _info_fname[fn];
1667         if (!s)
1668                 (void) sprintf(s = c, "0x%04x", f & 0xffff);
1669         return (s);
1670 }
1671 
1672 int alerts = 0;
1673 
1674 /*
1675  * Background daemon to wait for alert (on any device)
1676  * Writes the traces to "sd_alert.CD.NUM",
1677  * and writes an information message to the alert_file.
1678  */
1679 
1680 void
1681 sd_gather_alert_dumps()
1682 {
1683         _sdtr_table_t tt;
1684         _sdtr_t *buf;
1685         int cd, count, size, flag;
1686         char filename[64];
1687         int fd;
1688         time_t tloc;
1689         struct tm tm_storage;
1690         struct tm *tm_ptr;
1691         char timebuf[80];
1692         spcs_s_info_t ustats;
1693 
1694         /* fork and detach daemon */
1695         if (fork())
1696                 exit(0);
1697         (void) close(0);
1698         fd = open(alert_file, O_WRONLY|O_APPEND|O_CREAT, 0644);
1699         if (fd == -1)
1700                 fd = open("/dev/console", O_WRONLY);
1701         if (fd != -1) {
1702                 (void) dup2(fd, 1);
1703                 (void) dup2(fd, 2);
1704                 (void) close(fd);
1705         }
1706         (void) setsid();
1707 
1708         size = 10000;
1709         if (size < user_level_conf.trace_size)
1710                 size = user_level_conf.trace_size;
1711 
1712         buf = (_sdtr_t *)malloc(size * sizeof (_sdtr_t));
1713         if (!buf) {
1714                 (void) fprintf(stderr, gettext("%s malloc: %s\n"),
1715                     progname, strerror(errno));
1716                 exit(1);
1717         }
1718         tloc = time(NULL);
1719         tm_ptr = (struct tm *)localtime_r(&tloc, &tm_storage);
1720 
1721 loop:
1722         cd = SDT_ANY_CD;                /* any device */
1723         flag = SD_ALERT_WAIT;   /* block for alert */
1724         if ((count = SDBC_IOCTL(SDBC_ADUMP, cd, &tt, buf, size,
1725             flag, &ustats)) == SPCS_S_ERROR) {
1726                 (void) fprintf(stderr, gettext("%s: sd_adump\n"), progname);
1727                 sdbc_report_error(&ustats);
1728                 if (errno == EIDRM) {
1729                         (void) strftime(timebuf, 80, "%x %X", tm_ptr);
1730                         (void) fprintf(stderr,
1731                             gettext("%s: cache deconfigured at %s\n"),
1732                             progname, timebuf);
1733                         exit(0);
1734                 }
1735                 if (errno == ENOSYS)
1736                         exit(0);
1737                 exit(errno);
1738         }
1739         if (count == 0)
1740                 goto loop;
1741         cd = tt.tt_cd;
1742         (void) sprintf(filename, "%s.%d.%d", "sd_alert", cd, alerts++);
1743         if ((fd = open(filename, O_CREAT | O_RDWR, 0444)) == -1) {
1744                 (void) fprintf(stderr, gettext("%s: open: %s\n"),
1745                     progname, strerror(errno));
1746                 exit(errno);
1747         }
1748         /*
1749          * write header to identify device, write entries
1750          */
1751         hdr.t_func = SDF_CD;
1752         hdr.t_len = count;
1753         hdr.t_ret = tt.tt_cd;
1754         if (write(fd, &hdr, sizeof (_sdtr_t)) == -1) {
1755                 (void) fprintf(stderr, gettext("%s: write: %s\n"),
1756                     progname, strerror(errno));
1757                 exit(errno);
1758         }
1759 
1760         if (write(fd, buf, sizeof (_sdtr_t)*count) == -1) {
1761                 (void) fprintf(stderr, gettext("%s: write: %s\n"),
1762                     progname, strerror(errno));
1763                 exit(errno);
1764         }
1765         (void) close(fd);
1766 
1767         (void) strftime(timebuf, 80, "%x %X", tm_ptr);
1768         (void) printf("sd alert trace dump %s at %s\n", filename, timebuf);
1769         goto loop;
1770 }
1771 
1772 
1773 
1774 /*
1775  * print list of configured cd's, diskname, options and global options
1776  */
1777 void
1778 print_all_options()
1779 {
1780         static _sd_stats_t *cs_cur;
1781         spcs_s_info_t ustats;
1782         int cd;
1783         int hint;
1784         char *s1 = "device name";
1785         char *s2 = "option";
1786         char fn[19];
1787         int len;
1788 
1789         /* No corresponding free because this function exits */
1790         cs_cur = malloc(sizeof (_sd_stats_t) +
1791             (sdbc_max_devices - 1) * sizeof (_sd_shared_t));
1792         if (cs_cur == NULL) {
1793                 (void) fprintf(stderr, gettext("%s malloc: %s\n"),
1794                     progname, strerror(errno));
1795                 exit(1);
1796         }
1797 
1798         /* node hints */
1799         if ((hint = SDBC_IOCTL(SDBC_GET_NODE_HINT, 0, 0, 0, 0, 0,
1800             &ustats)) == SPCS_S_ERROR) {
1801                 (void) fprintf(stderr,
1802                     gettext("%s: get system option failed\n"),
1803                     progname);
1804                 sdbc_report_error(&ustats);
1805                 exit(1);
1806         }
1807 #ifdef WRTHRU_HINTS
1808         (void) printf(gettext("System Status: "));
1809         print_hint(hint, 1);
1810 #endif
1811         (void) printf(gettext("System Options: "));
1812         print_hint(hint, 0);
1813 
1814         /* get cds */
1815         if (SDBC_IOCTL(SDBC_STATS, cs_cur, 0, 0, 0, 0, &ustats)
1816             == SPCS_S_ERROR) {
1817                 (void) fprintf(stderr,
1818                     gettext("%s: get_cd failed in print_all options\n"),
1819                     progname);
1820                 sdbc_report_error(&ustats);
1821                 exit(1);
1822         }
1823         if (cs_cur->st_cachesize == 0)
1824                 (void) printf(gettext("Cache is disabled\n"));
1825         else if (cs_cur->st_count == 0)
1826                 (void) printf(gettext("No devices are configured\n"));
1827         else {
1828                 (void) printf(
1829                     gettext("\nConfigured cd's, disknames and options: \n"));
1830                 (void) printf(gettext("cd\t%-28s\t%-20s\n"), s1, s2);
1831                 for (cd = 0; cd < cs_cur->st_count; cd++) {
1832                         if (cs_cur->st_shared[cd].sh_alloc) {
1833                                 hint = get_cd_hint(cd);
1834                                 if ((len =
1835                                     strlen(cs_cur->st_shared[cd].sh_filename))
1836                                     > 23) {
1837                                         (void) strcpy(fn, "...");
1838                                         (void) strcat(fn,
1839                                             cs_cur->st_shared[cd].sh_filename +
1840                                             len - 20);
1841                                 } else {
1842                                         (void) strcpy(fn,
1843                                             cs_cur->st_shared[cd].sh_filename);
1844                                 }
1845 
1846                                 (void) printf(gettext("%d\t%-28.*s\t"), cd,
1847                                     NSC_MAXPATH, fn);
1848 
1849                                 print_hint(hint, 0);
1850                         }
1851                 }
1852         }
1853         exit(0);
1854 }
1855 
1856 
1857 /*
1858  * cache device -- lookup names and cache descriptors of all configured devices
1859  */
1860 void
1861 get_cd_all()
1862 {
1863         static _sd_stats_t *cs_cur;
1864         spcs_s_info_t ustats;
1865         int cd;
1866         char fn[19];
1867         int len;
1868 
1869         /* No corresponding free because this function exits */
1870         cs_cur = malloc(sizeof (_sd_stats_t) +
1871             (sdbc_max_devices - 1) * sizeof (_sd_shared_t));
1872         if (cs_cur == NULL) {
1873                 (void) fprintf(stderr, gettext("%s malloc: %s\n"),
1874                     progname, strerror(errno));
1875                 exit(1);
1876         }
1877 
1878         if (SDBC_IOCTL(SDBC_STATS, cs_cur, 0, 0, 0, 0, &ustats)
1879             == SPCS_S_ERROR) {
1880                 (void) fprintf(stderr, gettext("%s: get_cd_all"),
1881                     progname);
1882                 sdbc_report_error(&ustats);
1883                 exit(1);
1884         }
1885         if (cs_cur->st_cachesize == 0)
1886                 (void) printf(gettext("Cache is disabled\n"));
1887         else if (cs_cur->st_count == 0)
1888                 (void) printf(gettext("No devices are configured\n"));
1889         else {
1890                 (void) printf(gettext("\tcd\tdevice name\n"));
1891                 for (cd = 0; cd < cs_cur->st_count; cd++) {
1892                         if (cs_cur->st_shared[cd].sh_alloc) {
1893                                 if ((len = strlen(
1894                                     cs_cur->st_shared[cd].sh_filename)) > 15) {
1895                                         (void) strcpy(fn, "...");
1896                                         (void) strcat(fn,
1897                                             cs_cur->st_shared[cd].sh_filename +
1898                                             len - 12);
1899                                 } else {
1900                                         (void) strcpy(fn,
1901                                             cs_cur->st_shared[cd].sh_filename);
1902                                 }
1903                                 (void) printf(gettext("\t%d\t%s\n"),
1904                                     cd, fn);
1905                         }
1906                 }
1907         }
1908         exit(0);
1909 }
1910 
1911 /*
1912  * cache device -- specified by number or lookup name
1913  */
1914 static int
1915 get_cd(char *s)
1916 {
1917         static _sd_stats_t *cs_cur = NULL;
1918         spcs_s_info_t ustats;
1919         int cd, arg_cd = -1;
1920 
1921         if (cs_cur == NULL) {
1922                 /*
1923                  * No corresponding free because the memory is reused
1924                  * every time the function is called.
1925                  */
1926                 cs_cur = malloc(sizeof (_sd_stats_t) +
1927                     (sdbc_max_devices - 1) * sizeof (_sd_shared_t));
1928                 if (cs_cur == NULL) {
1929                         (void) fprintf(stderr, gettext("%s malloc: %s\n"),
1930                             progname, strerror(errno));
1931                         exit(1);
1932                 }
1933         }
1934 
1935         if (SDBC_IOCTL(SDBC_STATS, cs_cur, 0, 0, 0, 0, &ustats)
1936             == SPCS_S_ERROR) {
1937                 (void) fprintf(stderr, gettext("%s: get_cd\n"), progname);
1938                 sdbc_report_error(&ustats);
1939                 exit(1);
1940         }
1941         if (cs_cur->st_cachesize == 0) {
1942                 (void) printf(gettext("Cache is disabled\n"));
1943                 exit(0);
1944         }
1945 
1946         if (*s != '/') {
1947                 /*
1948                  * Since strtol returns 0 on failure, we need to make a
1949                  * special case for a cd of "0", which is valid.
1950                  *
1951                  * This case also deals with the difference between
1952                  * scmadm -o system and scmadm -o 0
1953                  */
1954                 if (((int)strtol(s, (char **)NULL, 10) == 0) &&
1955                     strcmp(s, "0"))
1956                         return (-1);
1957 
1958                 /*
1959                  * Only return failure at this point, in order to allow
1960                  * checking arg_cd against st_count later on.
1961                  */
1962                 if ((arg_cd = strtol(s, 0, 0)) < 0) {
1963                         return (arg_cd);
1964                 }
1965         }
1966 
1967         /* make sure the cd passed as an argument is alloc'd and < st_count */
1968         if (arg_cd >= 0) {
1969                 return (((arg_cd < cs_cur->st_count) &&
1970                     (cs_cur->st_shared[arg_cd].sh_alloc)) ? arg_cd : -1);
1971         }
1972 
1973         for (cd = 0; cd < cs_cur->st_count; cd++) {
1974                 if (cs_cur->st_shared[cd].sh_alloc &&
1975                     strcmp(s, cs_cur->st_shared[cd].sh_filename) == 0)
1976                         return (cd);
1977         }
1978         return (-1);
1979 }
1980 
1981 void
1982 check_and_set_mirrors(int node, int mirror)
1983 {
1984 
1985         if (minidsp) {
1986                 (void) fprintf(stderr,
1987                     gettext("%s: minidsp defined. "
1988                     "Cannot define other nodes.\n"),
1989                     progname);
1990                 exit(1);
1991         }
1992 
1993         if (mirror == _SD_NO_HOST) {
1994                 minidsp++;
1995         } else if ((!(node % 2) && !(node == mirror - 1)) ||
1996             (((node % 2) && !(node == mirror + 1)))) {
1997                 (void) fprintf(stderr,
1998                     gettext("%s: Node and Mirror identification values "
1999                     "must be consecutive\n"
2000                     "starting at an even number (Node = %d Mirror = %d)\n"),
2001                     progname, node, mirror);
2002                 exit(1);
2003         }
2004 
2005         node_defined[node]++;
2006 
2007         nodes_conf[nodes_configured] = node;
2008         nodes_configured++;
2009 
2010         if (node == myid) {
2011                 user_level_conf.mirror_host  = mirror;
2012         }
2013 }
2014 
2015 char *mem_string =
2016         "%-8s Structures use approx. %8d bytes (%5d pages) of memory\n";
2017 
2018 void
2019 enable_sdbc()
2020 {
2021         spcs_s_info_t ustats;
2022 
2023         if (get_cache_config()) {
2024                 (void) fprintf(stderr,
2025                     gettext("%s: unable to read configuration file\n"),
2026                     progname);
2027                 exit(1);
2028         }
2029 
2030         if (SDBC_IOCTL(SDBC_ENABLE, &user_level_conf, 0, 0, 0, 0,
2031             &ustats) == SPCS_S_ERROR) {
2032                 (void) fprintf(stderr, gettext("%s: cache enable failed\n"),
2033                     progname);
2034                 spcs_log("scm", &ustats, gettext("%s cache enable failed"),
2035                     progname);
2036                 sdbc_report_error(&ustats);
2037                 exit(1);
2038         }
2039         spcs_log("scm", NULL, gettext("%s cache enable succeeded"),
2040             progname);
2041 #ifdef DEBUG
2042         (void) printf(gettext("%s: cache has been configured\n"), progname);
2043 #endif
2044 #ifdef WRTHRU_HINTS
2045         if (iscluster()) {
2046                 /* Must writethru on a cluster, even if nvram configured */
2047                 forced_wrthru = 1;
2048         }
2049 
2050         if (minidsp && forced_wrthru != -1) {
2051                 /* Have minidsp with forced_wrthru hint. Set / Clear hint */
2052                 if (SDBC_IOCTL(SDBC_SET_NODE_HINT, NSC_FORCED_WRTHRU,
2053                     forced_wrthru, 0, 0, 0, &ustats) == SPCS_S_ERROR) {
2054                         (void) fprintf(stderr,
2055                             gettext("%s: set/clear forced_wrthru failed\n"),
2056                             progname);
2057                         sdbc_report_error(&ustats);
2058                 } else if (forced_wrthru) {
2059                         (void) printf(gettext("%s: Node option forced_wrthru "
2060                             "now set.\n"), progname);
2061                 } else {
2062                         (void) printf(gettext("%s: Node option forced_wrthru "
2063                             "now cleared.\n"), progname);
2064                 }
2065         }
2066         if (no_forced_wrthru != -1) {
2067                 if (SDBC_IOCTL(SDBC_SET_NODE_HINT, NSC_NO_FORCED_WRTHRU,
2068                     no_forced_wrthru, 0, 0, 0, &ustats) == SPCS_S_ERROR) {
2069                         (void) fprintf(stderr,
2070                             gettext("%s: set/clear no_forced_wrthru "
2071                             "failed\n"), progname);
2072                         sdbc_report_error(&ustats);
2073                 } else if (no_forced_wrthru) {
2074                         (void) printf(gettext("%s: Node option no_forced_wrthru"
2075                             " now set.\n"), progname);
2076                 } else {
2077                         (void) printf(gettext("%s: Node option no_forced_wrthru"
2078                             " now cleared.\n"), progname);
2079                 }
2080         }
2081 #endif
2082 
2083         /* do scmadm -O to cater for manual cache disable then enable */
2084         restore_hints();
2085 }
2086 
2087 void
2088 disable_sdbc()
2089 {
2090         spcs_s_info_t ustats;
2091 
2092         if (SDBC_IOCTL(SDBC_DISABLE, 0, 0, 0, 0, 0, &ustats) != SPCS_S_OK) {
2093                 /*
2094                  * If it wasn't already enabled, don't appear to fail
2095                  * or users of this program might think the cache is
2096                  * configured, when it actually isn't.
2097                  */
2098                 if (errno != SDBC_EDISABLE) {
2099                         spcs_log("scm", &ustats,
2100                             gettext("%s cache disable failed"), progname);
2101                         sdbc_report_error(&ustats);
2102                         exit(1);
2103                 }
2104         }
2105 #ifdef DEBUG
2106         (void) printf(gettext("%s: cache has been deconfigured\n"), progname);
2107 #endif
2108         spcs_log("scm", NULL, gettext("%s cache disable succeeded"),
2109             progname);
2110 }
2111 
2112 static void
2113 get_version()
2114 {
2115         cache_version_t version;
2116         spcs_s_info_t ustats;
2117 
2118         if (SDBC_IOCTL(SDBC_VERSION, &version, 0, 0, 0, 0, &ustats) ==
2119             SPCS_S_ERROR) {
2120                 (void) fprintf(stderr,
2121                     gettext("%s: get cache version failed\n"), progname);
2122                 sdbc_report_error(&ustats);
2123                 exit(1);
2124         }
2125 #ifdef DEBUG
2126         (void) printf(gettext("Cache version %d.%d.%d.%d\n"),
2127             version.major, version.minor, version.micro, version.baseline);
2128 #else
2129         if (version.micro) {
2130                 (void) printf(gettext("Cache version %d.%d.%d\n"),
2131                     version.major, version.minor, version.micro);
2132         } else {
2133                 (void) printf(gettext("Cache version %d.%d\n"),
2134                     version.major, version.minor);
2135         }
2136 #endif
2137 }
2138 
2139 #ifdef DEBUG
2140 int
2141 toggle_flush(void)
2142 {
2143         int rc;
2144         spcs_s_info_t ustats;
2145 
2146         if ((rc = SDBC_IOCTL(SDBC_TOGGLE_FLUSH, 0, 0, 0,
2147             0, 0, &ustats)) == SPCS_S_ERROR) {
2148                 (void) fprintf(stderr,
2149                     gettext("%s: toggle sdbc cache flush failed\n"),
2150                     progname);
2151                 sdbc_report_error(&ustats);
2152                 exit(1);
2153         }
2154         return (rc);
2155 }
2156 #endif