1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <sys/types.h>
  27 #include <sys/stat.h>
  28 #include <sys/mkdev.h>
  29 #include <sys/param.h>
  30 #include <sys/wait.h>
  31 #include <fcntl.h>
  32 #include <stdarg.h>
  33 #include <stdlib.h>
  34 #include <strings.h>
  35 #include <errno.h>
  36 #include <stdio.h>
  37 #include <locale.h>
  38 #include <unistd.h>
  39 #include <search.h>
  40 #include <libgen.h>
  41 #include <nsctl.h>
  42 
  43 #include <sys/unistat/spcs_s.h>
  44 #include <sys/unistat/spcs_s_u.h>
  45 #include <sys/unistat/spcs_errors.h>
  46 
  47 #include <sys/nsctl/sv.h>
  48 #include <sys/nsctl/sv_impl.h>
  49 
  50 #include <sys/nsctl/cfg.h>
  51 #include <sys/nsctl/nsc_hash.h>
  52 
  53 #include "../sv/svadm.h"
  54 
  55 
  56 static int sv_max_devices;
  57 
  58 
  59 /*
  60  * support for the special cluster tag "local" to be used with -C in a
  61  * cluster for local volumes.
  62  */
  63 
  64 #define SV_LOCAL_TAG    "local"
  65 
  66 static int sv_islocal;
  67 
  68 /*
  69  * libcfg access.
  70  */
  71 
  72 static CFGFILE *cfg;            /* libcfg file pointer */
  73 static int cfg_changed;         /* set to 1 if we need to commit changes */
  74 
  75 static char *cfg_cluster_tag;   /* local cluster tag */
  76 
  77 static char *implicit_tag;      /* implicit cluster tag */
  78 
  79 
  80 /*
  81  * Print width for print_sv() output.
  82  */
  83 
  84 #define STATWIDTH       (SV_MAXPATH / 2)
  85 
  86 /*
  87  * Pathnames.
  88  */
  89 
  90 static const caddr_t sv_rpath = SV_DEVICE;
  91 
  92 /*
  93  * Functions.
  94  */
  95 
  96 static int read_config_file(const caddr_t, sv_name_t []);
  97 static int enable_dev(sv_name_t *);
  98 static int disable_dev(const caddr_t);
  99 static void error(spcs_s_info_t *, caddr_t, ...);
 100 static void create_cfg_hash();
 101 static int find_in_hash(char *path);
 102 static void destroy_hashtable();
 103 static void remove_from_cfgfile(char *path, int setnumber);
 104 
 105 static caddr_t program;
 106 
 107 static void
 108 sv_cfg_open(CFGLOCK mode)
 109 {
 110         if (cfg != NULL)
 111                 return;
 112 
 113         cfg = cfg_open(NULL);
 114         if (cfg == NULL) {
 115                 error(NULL, gettext("unable to access the configuration"));
 116                 /* NOTREACHED */
 117         }
 118 
 119         if (cfg_cluster_tag && *cfg_cluster_tag) {
 120                 cfg_resource(cfg, cfg_cluster_tag);
 121         } else {
 122                 cfg_resource(cfg, NULL);
 123         }
 124         if (!cfg_lock(cfg, mode)) {
 125                 error(NULL, gettext("unable to lock the configuration"));
 126                 /* NOTREACHED */
 127         }
 128 }
 129 
 130 
 131 static void
 132 sv_cfg_close(void)
 133 {
 134         if (cfg == NULL)
 135                 return;
 136 
 137         if (cfg_changed) {
 138                 (void) cfg_commit(cfg);
 139                 cfg_changed = 0;
 140         }
 141 
 142         cfg_close(cfg);
 143         cfg = NULL;
 144 }
 145 
 146 
 147 
 148 static void
 149 usage(void)
 150 {
 151         (void) fprintf(stderr, gettext("usage:\n"));
 152 
 153         (void) fprintf(stderr, gettext(
 154             "\t%s -h                                 help\n"), program);
 155 
 156         (void) fprintf(stderr, gettext(
 157             "\t%s [-C tag]                           display status\n"),
 158             program);
 159 
 160         (void) fprintf(stderr, gettext(
 161             "\t%s [-C tag] -i                        display "
 162             "extended status\n"), program);
 163 
 164         (void) fprintf(stderr, gettext(
 165             "\t%s [-C tag] -v                        display "
 166             "version number\n"), program);
 167 
 168         (void) fprintf(stderr, gettext(
 169             "\t%s [-C tag] -e { -f file | volume }   enable\n"), program);
 170 
 171         (void) fprintf(stderr, gettext(
 172             "\t%s [-C tag] -d { -f file | volume }   disable\n"), program);
 173 
 174         (void) fprintf(stderr, gettext(
 175             "\t%s [-C tag] -r { -f file | volume }   reconfigure\n"), program);
 176 
 177         sv_cfg_close();
 178 }
 179 
 180 static void
 181 message(caddr_t prefix, spcs_s_info_t *status, caddr_t string, va_list ap)
 182 {
 183         (void) fprintf(stderr, "%s: %s: ", program, prefix);
 184         (void) vfprintf(stderr, string, ap);
 185         (void) fprintf(stderr, "\n");
 186 
 187         if (status) {
 188                 spcs_s_report(*status, stderr);
 189                 spcs_s_ufree(status);
 190         }
 191 }
 192 
 193 
 194 static void
 195 error(spcs_s_info_t *status, caddr_t string, ...)
 196 {
 197         va_list ap;
 198         va_start(ap, string);
 199 
 200         message(gettext("error"), status, string, ap);
 201 
 202         va_end(ap);
 203 
 204         sv_cfg_close();
 205         exit(1);
 206 }
 207 
 208 
 209 static void
 210 warn(spcs_s_info_t *status, caddr_t string, ...)
 211 {
 212         va_list ap;
 213         va_start(ap, string);
 214 
 215         message(gettext("warning"), status, string, ap);
 216 
 217         va_end(ap);
 218 }
 219 
 220 
 221 static void
 222 sv_get_maxdevs(void)
 223 {
 224         sv_name_t svn[1];
 225         sv_list_t svl;
 226         int fd;
 227 
 228         if (sv_max_devices > 0)
 229                 return;
 230 
 231         fd = open(sv_rpath, O_RDONLY);
 232         if (fd < 0)
 233                 error(NULL, gettext("unable to open %s: %s"),
 234                         sv_rpath, strerror(errno));
 235 
 236         bzero(&svl, sizeof (svl));
 237         bzero(&svn[0], sizeof (svn));
 238 
 239         svl.svl_names = &svn[0];
 240         svl.svl_error = spcs_s_ucreate();
 241 
 242         if (ioctl(fd, SVIOC_LIST, &svl) < 0) {
 243                 (void) close(fd);
 244                 error(&svl.svl_error, gettext("unable to get max devs"));
 245         }
 246 
 247         spcs_s_ufree(&svl.svl_error);
 248         sv_max_devices = svl.svl_maxdevs;
 249 
 250         (void) close(fd);
 251 }
 252 
 253 
 254 static sv_name_t *
 255 sv_alloc_svnames(void)
 256 {
 257         sv_name_t *svn = NULL;
 258 
 259         sv_get_maxdevs();
 260 
 261         svn = calloc(sv_max_devices, sizeof (*svn));
 262         if (svn == NULL) {
 263                 error(NULL, "unable to allocate %ld bytes of memory",
 264                     sv_max_devices * sizeof (*svn));
 265         }
 266 
 267         return (svn);
 268 }
 269 
 270 
 271 static void
 272 sv_check_dgislocal(char *dgname)
 273 {
 274         char *othernode;
 275         int rc;
 276 
 277         /*
 278          * check where this disk service is mastered
 279          */
 280 
 281         rc = cfg_dgname_islocal(dgname, &othernode);
 282         if (rc < 0) {
 283                 error(NULL, gettext("unable to find "
 284                     "disk service, %s: %s"), dgname, strerror(errno));
 285         }
 286 
 287         if (rc == 0) {
 288                 error(NULL, gettext("disk service, %s, is "
 289                     "active on node \"%s\"\nPlease re-issue "
 290                     "the command on that node"), dgname, othernode);
 291         }
 292 }
 293 
 294 
 295 /*
 296  * Carry out cluster based checks for a specified volume, or just
 297  * global options.
 298  */
 299 static void
 300 sv_check_cluster(char *path)
 301 {
 302         char dgname[CFG_MAX_BUF];
 303         static int sv_iscluster = -1;   /* set to 1 if running in a cluster */
 304 
 305         /*
 306          * Find out if we are running in a cluster
 307          */
 308         if (sv_iscluster == -1) {
 309                 if ((sv_iscluster = cfg_iscluster()) < 0) {
 310                         error(NULL, gettext("unable to ascertain environment"));
 311                 }
 312         }
 313 
 314         if (!sv_iscluster && cfg_cluster_tag != NULL) {
 315                 error(NULL, gettext("-C is not valid when not in a cluster"));
 316         }
 317 
 318         if (!sv_iscluster || sv_islocal || path == NULL) {
 319                 return;
 320         }
 321 
 322 
 323         /*
 324          * Cluster-only checks on pathname
 325          */
 326         if (cfg_dgname(path, dgname, sizeof (dgname)) == NULL) {
 327                 error(NULL, gettext("unable to determine "
 328                     "disk group name for %s"), path);
 329                 return;
 330         }
 331 
 332         if (cfg_cluster_tag != NULL) {
 333                 /*
 334                  * Do dgislocal check now in case path did not contain
 335                  * a dgname.
 336                  *
 337                  * E.g. adding a /dev/did/ device to a disk service.
 338                  */
 339 
 340                 sv_check_dgislocal(cfg_cluster_tag);
 341         }
 342 
 343         if (strcmp(dgname, "") == 0)
 344                 return;         /* NULL dgname is valid */
 345 
 346         if (cfg_cluster_tag == NULL) {
 347                 /*
 348                  * Implicitly set the cluster tag to dgname
 349                  */
 350 
 351                 sv_check_dgislocal(dgname);
 352 
 353                 if (implicit_tag) {
 354                         free(implicit_tag);
 355                         implicit_tag = NULL;
 356                 }
 357 
 358                 implicit_tag = strdup(dgname);
 359                 if (implicit_tag == NULL) {
 360                         error(NULL,
 361                             gettext("unable to allocate memory "
 362                             "for cluster tag"));
 363                 }
 364         } else {
 365                 /*
 366                  * Check dgname and cluster tag from -C are the same.
 367                  */
 368 
 369                 if (strcmp(dgname, cfg_cluster_tag) != 0) {
 370                         error(NULL,
 371                             gettext("-C (%s) does not match disk group "
 372                             "name (%s) for %s"), cfg_cluster_tag,
 373                             dgname, path);
 374                 }
 375 
 376                 /*
 377                  * sv_check_dgislocal(cfg_cluster_tag) was called above.
 378                  */
 379         }
 380 }
 381 
 382 
 383 static void
 384 print_version(void)
 385 {
 386         sv_version_t svv;
 387         int fd;
 388 
 389         bzero(&svv, sizeof (svv));
 390         svv.svv_error = spcs_s_ucreate();
 391 
 392         fd = open(sv_rpath, O_RDONLY);
 393         if (fd < 0) {
 394                 warn(NULL, gettext("unable to open %s: %s"),
 395                         sv_rpath, strerror(errno));
 396                 return;
 397         }
 398 
 399         if (ioctl(fd, SVIOC_VERSION, &svv) != 0) {
 400                 error(&svv.svv_error,
 401                     gettext("unable to read the version number"));
 402                 /* NOTREACHED */
 403         }
 404 
 405         spcs_s_ufree(&svv.svv_error);
 406 #ifdef DEBUG
 407         (void) printf(gettext("Storage Volume version %d.%d.%d.%d\n"),
 408             svv.svv_major_rev, svv.svv_minor_rev,
 409             svv.svv_micro_rev, svv.svv_baseline_rev);
 410 #else
 411         if (svv.svv_micro_rev) {
 412                 (void) printf(gettext("Storage Volume version %d.%d.%d\n"),
 413                     svv.svv_major_rev, svv.svv_minor_rev, svv.svv_micro_rev);
 414         } else {
 415                 (void) printf(gettext("Storage Volume version %d.%d\n"),
 416                     svv.svv_major_rev, svv.svv_minor_rev);
 417         }
 418 #endif
 419 
 420         (void) close(fd);
 421 }
 422 
 423 int
 424 main(int argc, char *argv[])
 425 {
 426         extern int optind;
 427         extern char *optarg;
 428         char *conf_file = NULL;
 429         int enable, disable, compare, print, version;
 430         int opt, Cflag, fflag, iflag;
 431         int rc;
 432 
 433         (void) setlocale(LC_ALL, "");
 434         (void) textdomain("svadm");
 435 
 436         program = strdup(basename(argv[0]));
 437 
 438         Cflag = fflag = iflag = 0;
 439         compare = enable = disable = version = 0;
 440 
 441         print = 1;
 442 
 443         while ((opt = getopt(argc, argv, "C:def:hirv")) != EOF) {
 444                 switch (opt) {
 445 
 446                 case 'C':
 447                         if (Cflag) {
 448                                 warn(NULL,
 449                                     gettext("-C specified multiple times"));
 450                                 usage();
 451                                 exit(2);
 452                                 /* NOTREACHED */
 453                         }
 454 
 455                         Cflag++;
 456                         cfg_cluster_tag = optarg;
 457                         break;
 458 
 459                 case 'e':
 460                         print = 0;
 461                         enable++;
 462                         break;
 463 
 464                 case 'd':
 465                         print = 0;
 466                         disable++;
 467                         break;
 468 
 469                 case 'f':
 470                         fflag++;
 471                         conf_file = optarg;
 472                         break;
 473 
 474                 case 'i':
 475                         iflag++;
 476                         break;
 477 
 478                 case 'r':
 479                         /* Compare running system with sv.cf */
 480                         print = 0;
 481                         compare++;
 482                         break;
 483 
 484                 case 'v':
 485                         print = 0;
 486                         version++;
 487                         break;
 488 
 489                 case 'h':
 490                         usage();
 491                         exit(0);
 492 
 493                 default:
 494                         usage();
 495                         exit(2);
 496                         /* NOTREACHED */
 497                 }
 498         }
 499 
 500 
 501         /*
 502          * Usage checks
 503          */
 504 
 505         if ((enable + disable + compare) > 1) {
 506                 warn(NULL, gettext("-d, -e and -r are mutually exclusive"));
 507                 usage();
 508                 exit(2);
 509         }
 510 
 511         if (fflag && (print || version)) {
 512                 warn(NULL, gettext("-f is only valid with -d, -e or -r"));
 513                 usage();
 514                 exit(2);
 515         }
 516 
 517         if (fflag && optind != argc) {
 518                 usage();
 519                 exit(2);
 520         }
 521 
 522         if (print || version) {
 523                 /* check for no more args */
 524 
 525                 if (optind != argc) {
 526                         usage();
 527                         exit(2);
 528                 }
 529         } else {
 530                 /* check for inline args */
 531 
 532                 if (!fflag && (argc - optind) != 1) {
 533                         usage();
 534                         exit(2);
 535                 }
 536         }
 537 
 538         if (!print && iflag) {
 539                 usage();
 540                 exit(2);
 541         }
 542 
 543 
 544         /*
 545          * Check for the special cluster tag and convert into the
 546          * internal representation.
 547          */
 548 
 549         if (cfg_cluster_tag != NULL &&
 550             strcmp(cfg_cluster_tag, SV_LOCAL_TAG) == 0) {
 551                 cfg_cluster_tag = "-";
 552                 sv_islocal = 1;
 553         }
 554 
 555 
 556         /*
 557          * Process commands
 558          */
 559 
 560         if (optind != argc) {
 561                 /* deal with inline volume argument */
 562 
 563                 rc = 0;
 564                 if (enable)
 565                         rc = enable_one_sv(argv[optind]);
 566                 else if (disable)
 567                         rc = disable_one_sv(argv[optind]);
 568                 else /* if (compare) */
 569                         compare_one_sv(argv[optind]);
 570 
 571                 if (rc != 0)
 572                         return (1);
 573 
 574                 return (0);
 575         }
 576 
 577         rc = 0;
 578         if (enable)
 579                 rc = enable_sv(conf_file);
 580         else if (disable)
 581                 rc = disable_sv(conf_file);
 582         else if (compare)
 583                 compare_sv(conf_file);
 584         else if (print)
 585                 print_sv(iflag);
 586         else /* if (version) */
 587                 print_version();
 588 
 589         if (rc != 0)
 590                 return (1);
 591 
 592         return (0);
 593 }
 594 
 595 
 596 
 597 /* LINT - not static as fwcadm uses it */
 598 static int
 599 enable_sv(char *conf_file)
 600 {
 601         int index;
 602         sv_name_t *svn;
 603         int cnt;
 604         int rc, ret;
 605 
 606         svn = sv_alloc_svnames();
 607 
 608         index = read_config_file(conf_file, svn);
 609 
 610         rc = ret = 0;
 611 
 612         for (cnt = 0; cnt < index; cnt++) {
 613 
 614                 /*
 615                  * Check for more data.
 616                  */
 617                 if (svn[cnt].svn_path[0] == '\0') {
 618                         /*
 619                          * This was set when reading sv.conf.  After the last
 620                          * line svn_path was set to \0, so we are finished.
 621                          * We shouldn't get here, but put this in just in
 622                          * case.
 623                          */
 624                         break;
 625                 }
 626                 rc = enable_dev(&svn[cnt]);
 627                 if (rc && !ret)
 628                         ret = rc;
 629         }
 630 
 631         sv_cfg_close();
 632 
 633         return (ret);
 634 }
 635 
 636 
 637 /* LINT - not static as fwcadm uses it */
 638 static int
 639 enable_one_sv(caddr_t path)
 640 {
 641         sv_name_t svn;
 642         int rc;
 643 
 644         sv_get_maxdevs();
 645 
 646         bzero(&svn, sizeof (svn));
 647         (void) strncpy(svn.svn_path, path, sizeof (svn.svn_path));
 648         svn.svn_mode = (NSC_DEVICE | NSC_CACHE);
 649 
 650         /* force NULL termination */
 651         svn.svn_path[sizeof (svn.svn_path) - 1] = '\0';
 652 
 653         rc = enable_dev(&svn);
 654         sv_cfg_close();
 655 
 656         return (rc);
 657 }
 658 
 659 
 660 static int
 661 enable_dev(sv_name_t *svn)
 662 {
 663         char buf[CFG_MAX_BUF];
 664         struct stat stb;
 665         sv_conf_t svc;
 666         int fd;
 667         int sev;
 668         int rc;
 669         char *lcltag;
 670         char *altname;
 671 
 672         sv_check_cluster(svn->svn_path);
 673         sv_cfg_open(CFG_WRLOCK);
 674 
 675         bzero(&svc, sizeof (svc));
 676 
 677         if (stat(svn->svn_path, &stb) != 0) {
 678                 warn(NULL, gettext("unable to access %s: %s"),
 679                         svn->svn_path, strerror(errno));
 680                 return (1);
 681         }
 682 
 683         if (!S_ISCHR(stb.st_mode)) {
 684                 warn(NULL, gettext("%s is not a character device - ignored"),
 685                     svn->svn_path);
 686                 return (1);
 687         }
 688 
 689         svc.svc_major = major(stb.st_rdev);
 690         svc.svc_minor = minor(stb.st_rdev);
 691         (void) strncpy(svc.svc_path, svn->svn_path, sizeof (svc.svc_path));
 692 
 693         fd = open(sv_rpath, O_RDONLY);
 694         if (fd < 0) {
 695                 warn(NULL, gettext("unable to open %s: %s"),
 696                         svn->svn_path, strerror(errno));
 697                 return (1);
 698         }
 699 
 700         svc.svc_flag = svn->svn_mode;
 701         svc.svc_error = spcs_s_ucreate();
 702 
 703         /* first, check for duplicates */
 704         rc = cfg_get_canonical_name(cfg, svn->svn_path, &altname);
 705         if (rc < 0) {
 706                 spcs_log("sv", NULL, gettext("Unable to parse config file"));
 707                 warn(NULL, gettext("Unable to parse config file"));
 708                 (void) close(fd);
 709                 return (1);
 710         }
 711         if (rc) {
 712                 error(NULL, gettext("'%s' has already been configured as "
 713                     "'%s'.  Re-enter command with the latter name."),
 714                     svn->svn_path, altname);
 715         }
 716 
 717         /* secondly, try to insert it into the dsvol config */
 718         if (implicit_tag && *implicit_tag) {
 719                 lcltag = implicit_tag;
 720         } else if (cfg_cluster_tag && *cfg_cluster_tag) {
 721                 lcltag = cfg_cluster_tag;
 722         } else {
 723                 lcltag = "-";
 724         }
 725         rc = cfg_add_user(cfg, svn->svn_path, lcltag, "sv");
 726         if (CFG_USER_ERR == rc) {
 727                 spcs_log("sv", NULL,
 728                     gettext("%s: unable to put %s into dsvol cfg"),
 729                     program, svn->svn_path);
 730                 warn(NULL, gettext("unable to put %s into dsvol cfg"),
 731                     svn->svn_path);
 732                 (void) close(fd);
 733                 return (1);
 734         }
 735         cfg_changed = 1;
 736 
 737         if (CFG_USER_OK == rc) {
 738                 /* success */
 739                 (void) close(fd);
 740                 return (0);
 741         }
 742 
 743         if (ioctl(fd, SVIOC_ENABLE, &svc) < 0) {
 744                 if ((CFG_USER_REPEAT == rc) && (SV_EENABLED == errno)) {
 745                         /* it's ok -- we were just double-checking */
 746                         (void) close(fd);
 747                         return (0);
 748                 }
 749 
 750                 spcs_log("sv", &svc.svc_error,
 751                     gettext("%s: unable to enable %s"),
 752                     program, svn->svn_path);
 753 
 754                 warn(&svc.svc_error, gettext("unable to enable %s"),
 755                         svn->svn_path);
 756 
 757                 /* remove it from dsvol, if we're the ones who put it in */
 758                 if (CFG_USER_FIRST == rc) {
 759                         (void) cfg_rem_user(cfg, svn->svn_path, lcltag, "sv");
 760                 }
 761                 (void) close(fd);
 762                 return (1);
 763         }
 764 
 765         spcs_log("sv", NULL, gettext("%s: enabled %s"),
 766             program, svn->svn_path);
 767 
 768         if (implicit_tag != NULL) {
 769 #ifdef DEBUG
 770                 if (cfg_cluster_tag != NULL) {
 771                         error(NULL,
 772                             gettext("enable_dev: -C %s AND implicit_tag %s!"),
 773                             cfg_cluster_tag, implicit_tag);
 774                 }
 775 #endif
 776 
 777                 (void) snprintf(buf, sizeof (buf), "%s - %s",
 778                     svc.svc_path, implicit_tag);
 779         } else {
 780                 (void) strcpy(buf, svc.svc_path);
 781         }
 782 
 783         rc = 0;
 784         if (cfg_put_cstring(cfg, "sv", buf, sizeof (buf)) < 0) {
 785                 warn(NULL,
 786                     gettext("unable to add %s to configuration storage: %s"),
 787                     svc.svc_path, cfg_error(&sev));
 788                 rc = 1;
 789         }
 790 
 791         cfg_changed = 1;
 792         spcs_s_ufree(&svc.svc_error);
 793         (void) close(fd);
 794 
 795         return (rc);
 796 }
 797 
 798 
 799 /*
 800  * This routine parses the config file passed in via conf_file and
 801  * stores the data in the svn array.  The return value is the number
 802  * of entries read from conf_file.  If an error occurs the error()
 803  * routine is called (which exits the program).
 804  */
 805 static int
 806 read_config_file(const caddr_t conf_file, sv_name_t svn[])
 807 {
 808         char line[1024], rdev[1024], junk[1024];
 809         struct stat stb;
 810         int lineno;
 811         int cnt, i;
 812         int index = 0;          /* Current location in svn array        */
 813         sv_name_t *cur_svn;     /* Pointer to svn[index]                */
 814         FILE *fp;
 815 
 816         if (access(conf_file, R_OK) != 0 ||
 817             stat(conf_file, &stb) != 0 ||
 818             !S_ISREG(stb.st_mode)) {
 819                 error(NULL, gettext("cannot read config file %s"), conf_file);
 820         }
 821 
 822         if ((fp = fopen(conf_file, "r")) == NULL) {
 823                 error(NULL, gettext("unable to open config file %s: %s"),
 824                         conf_file, strerror(errno));
 825         }
 826 
 827         lineno = 0;
 828 
 829         while (fgets(line, sizeof (line), fp) != NULL) {
 830                 lineno++;
 831 
 832                 i = strlen(line);
 833 
 834                 if (i < 1)
 835                         continue;
 836 
 837                 if (line[i-1] == '\n')
 838                         line[i-1] = '\0';
 839                 else if (i == (sizeof (line) - 1)) {
 840                         warn(NULL, gettext(
 841                 "line %d: line too long -- should be less than %d characters"),
 842                                 lineno, (sizeof (line) - 1));
 843                         warn(NULL, gettext("line %d: ignored"), lineno);
 844                 }
 845 
 846                 /*
 847                  * check for comment line.
 848                  */
 849                 if (line[0] == '#')
 850                         continue;
 851 
 852                 cnt = sscanf(line, "%s %s", rdev, junk);
 853 
 854                 if (cnt != 1 && cnt != 2) {
 855                         if (cnt > 0) {
 856                                 warn(NULL, gettext("line %d: invalid format"),
 857                                         lineno);
 858                                 warn(NULL, gettext("line %d: ignored"), lineno);
 859                         }
 860                         continue;
 861                 }
 862 
 863                 rdev[sizeof (rdev) - 1] = '\0';
 864 
 865                 cur_svn = &svn[index];  /* For easier reading below */
 866 
 867                 if (strlen(rdev) >= sizeof (cur_svn->svn_path)) {
 868                         warn(NULL, gettext(
 869                 "line %d: raw device name (%s) longer than %d characters"),
 870                                 lineno, rdev,
 871                                 (sizeof (cur_svn->svn_path) - 1));
 872                         warn(NULL, gettext("line %d: ignored"), lineno);
 873                         continue;
 874                 }
 875 
 876                 (void) strcpy(cur_svn->svn_path, rdev);
 877                 cur_svn->svn_mode = (NSC_DEVICE | NSC_CACHE);
 878 
 879                 index++;
 880         }
 881 
 882         /* Set the last path to NULL */
 883         svn[index].svn_path[0] = '\0';
 884 
 885         (void) fclose(fp);
 886 
 887         return (index);
 888 }
 889 
 890 
 891 /*
 892  * Disable the device from the kernel configuration.
 893  *
 894  * RETURN:
 895  *   0 on success
 896  *   non-zero on failure.
 897  *
 898  * Failures are reported to the user.
 899  */
 900 static int
 901 disable_dev(const caddr_t path)
 902 {
 903         struct stat stb;
 904         sv_conf_t svc;
 905         int fd;
 906 
 907         sv_check_cluster(path);
 908 
 909         if (stat(path, &stb) < 0) {
 910                 svc.svc_major = (major_t)-1;
 911                 svc.svc_minor = (minor_t)-1;
 912         } else {
 913                 svc.svc_major = major(stb.st_rdev);
 914                 svc.svc_minor = minor(stb.st_rdev);
 915         }
 916 
 917         if ((fd = open(sv_rpath, O_RDONLY)) < 0) {
 918                 warn(NULL, gettext("unable to open %s: %s"),
 919                         sv_rpath, strerror(errno));
 920                 return (-1);
 921         }
 922 
 923         (void) strcpy(svc.svc_path, path);
 924         svc.svc_error = spcs_s_ucreate();
 925 
 926         /*
 927          * Issue the ioctl to attempt to disable this device.  Note that all
 928          * the libdscfg details are handled elsewhere.
 929          */
 930         if (ioctl(fd, SVIOC_DISABLE, &svc) < 0) {
 931                 if (errno != SV_EDISABLED) {
 932                         spcs_log("sv", &svc.svc_error,
 933                                         gettext("%s: unable to disable %s"),
 934                                         program, path);
 935 
 936                         warn(&svc.svc_error,
 937                                         gettext("unable to disable %s"), path);
 938                         (void) close(fd);
 939                         return (-1);
 940                 }
 941         }
 942 
 943         spcs_log("sv", NULL, gettext("%s: disabled %s"), program, path);
 944 
 945         spcs_s_ufree(&svc.svc_error);
 946         (void) close(fd);
 947 
 948         return (0);
 949 }
 950 
 951 
 952 static void
 953 print_cluster_tag(const int setnumber)
 954 {
 955         char buf[CFG_MAX_BUF];
 956         char key[CFG_MAX_KEY];
 957 
 958         bzero(buf, sizeof (buf));
 959         (void) snprintf(key, sizeof (key), "sv.set%d.cnode", setnumber);
 960 
 961         (void) cfg_get_cstring(cfg, key, buf, sizeof (buf));
 962 
 963         if (*buf != '\0') {
 964                 if (strcmp(buf, "-") == 0) {
 965                         (void) printf(" [%s]", gettext("local to node"));
 966                 } else {
 967                         (void) printf(" [%s: %s]", gettext("cluster"), buf);
 968                 }
 969         }
 970 }
 971 
 972 
 973 /* LINT - not static as fwcadm uses it */
 974 static void
 975 print_sv(int verbose)
 976 {
 977         sv_name_t *svn, *svn_system;    /* Devices in system */
 978         sv_list_t svl_system;
 979         int fd, i;
 980         int setnumber;
 981 
 982         sv_check_cluster(NULL);
 983         sv_cfg_open(CFG_RDLOCK);
 984 
 985         svn_system = sv_alloc_svnames();
 986 
 987         if ((fd = open(sv_rpath, O_RDONLY)) < 0) {
 988                 (void) printf(gettext("unable to open %s: %s"),
 989                         sv_rpath, strerror(errno));
 990                 return;
 991         }
 992 
 993         /* Grab the system list from the driver */
 994         svl_system.svl_count = sv_max_devices;
 995         svl_system.svl_names = &svn_system[0];
 996         svl_system.svl_error = spcs_s_ucreate();
 997 
 998         if (ioctl(fd, SVIOC_LIST, &svl_system) < 0) {
 999                 error(&svl_system.svl_error, gettext("unable to get list"));
1000         }
1001 
1002         spcs_s_ufree(&svl_system.svl_error);
1003         (void) close(fd);
1004 
1005         /*
1006          * We build a hashmap out of the entries from the config file to make
1007          * searching faster. We end up taking a performance hit when the # of
1008          * volumes is small, but for larger configurations it's a
1009          * HUGE improvement.
1010          */
1011 
1012         /* build the hashtable */
1013         cfg_rewind(cfg, CFG_SEC_CONF);
1014         create_cfg_hash();
1015 
1016         /*
1017          * For each volume found from the kernel, print out
1018          * info about it from the kernel.
1019          */
1020         for (i = 0; i < svl_system.svl_count; i++) {
1021                 if (*svn_system[i].svn_path == '\0') {
1022                         break;
1023                 }
1024 
1025                 svn = &svn_system[i];
1026                 if (svn->svn_mode == 0) {
1027 #ifdef DEBUG
1028                         (void) printf(gettext("%s [kernel guard]\n"),
1029                             svn->svn_path);
1030 #endif
1031                         continue;
1032                 }
1033                 /* get sv entry from the hashtable */
1034                 if ((setnumber = find_in_hash(svn->svn_path)) != -1) {
1035                         (void) printf("%-*s", STATWIDTH, svn->svn_path);
1036 
1037                         if (verbose) {
1038                                 print_cluster_tag(setnumber);
1039                         }
1040 
1041                         (void) printf("\n");
1042 
1043                 } else {
1044                         /*
1045                          * We didn't find the entry in the hashtable.  Let
1046                          * the user know that the persistent storage is
1047                          * inconsistent with the kernel configuration.
1048                          */
1049                         if (cfg_cluster_tag == NULL)
1050                                 warn(NULL, gettext(
1051                                         "%s is configured, but not in the "
1052                                         "config storage"), svn->svn_path);
1053                 }
1054         }
1055 
1056         /* free up the hashtable */
1057         destroy_hashtable();
1058 
1059         sv_cfg_close();
1060 }
1061 
1062 
1063 /* LINT - not static as fwcadm uses it */
1064 static int
1065 disable_sv(char *conf_file)
1066 {
1067         sv_name_t *svn, *svn_system;    /* Devices in system */
1068         sv_list_t svl_system;
1069         int fd, i, setnumber;
1070         int rc, ret;
1071 
1072         svn_system = sv_alloc_svnames();
1073 
1074         rc = ret = 0;
1075 
1076         if (conf_file == NULL) {
1077                 if ((fd = open(sv_rpath, O_RDONLY)) < 0) {
1078                         (void) printf(gettext("unable to open %s: %s"),
1079                                 sv_rpath, strerror(errno));
1080                         return (1);
1081                 }
1082 
1083                 /* Grab the system list from the driver */
1084                 svl_system.svl_count = sv_max_devices;
1085                 svl_system.svl_names = &svn_system[0];
1086                 svl_system.svl_error = spcs_s_ucreate();
1087 
1088                 if (ioctl(fd, SVIOC_LIST, &svl_system) < 0) {
1089                         error(&(svl_system.svl_error),
1090                                         gettext("unable to get list"));
1091                 }
1092 
1093                 spcs_s_ufree(&(svl_system.svl_error));
1094                 (void) close(fd);
1095         } else {
1096                 svl_system.svl_count = read_config_file(conf_file, svn_system);
1097         }
1098 
1099 
1100         for (i = 0; i < svl_system.svl_count; i++) {
1101                 if (*svn_system[i].svn_path == '\0')
1102                         break;
1103 
1104                 svn = &svn_system[i];
1105 
1106                 sv_check_cluster(svn->svn_path);
1107                 sv_cfg_open(CFG_WRLOCK);
1108                 create_cfg_hash();
1109                 rc = 0;
1110                 if ((setnumber = find_in_hash(svn->svn_path)) != -1) {
1111                         if ((rc = disable_dev(svn->svn_path)) != -1) {
1112                                 remove_from_cfgfile(svn->svn_path, setnumber);
1113                         } else if (errno == SV_ENODEV) {
1114                                 remove_from_cfgfile(svn->svn_path, setnumber);
1115                         }
1116                 } else {
1117                         /* warn the user that we didn't find it in cfg file */
1118                         warn(NULL, gettext(
1119                                 "%s was not found in the config storage"),
1120                                 svn->svn_path);
1121                         /* try to disable anyway */
1122                         (void) disable_dev(svn->svn_path);
1123                         rc = 1;
1124                 }
1125 
1126                 sv_cfg_close();
1127                 destroy_hashtable();
1128 
1129                 if (rc && !ret)
1130                         ret = rc;
1131         }
1132 
1133         return (ret);
1134 }
1135 
1136 
1137 /* LINT - not static as fwcadm uses it */
1138 static int
1139 disable_one_sv(char *path)
1140 {
1141         int setnumber;
1142         int rc;
1143 
1144         sv_get_maxdevs();
1145         sv_check_cluster(path);
1146         sv_cfg_open(CFG_WRLOCK);
1147 
1148         create_cfg_hash();
1149         if ((setnumber = find_in_hash(path)) != -1) {
1150                 /* remove from kernel */
1151                 if ((rc = disable_dev(path)) == 0) {
1152                         /* remove the cfgline */
1153                         remove_from_cfgfile(path, setnumber);
1154                 } else if (errno == SV_ENODEV) {
1155                         remove_from_cfgfile(path, setnumber);
1156                 }
1157         } else {
1158                 /* warn the user that we didn't find it in cfg file */
1159                 warn(NULL,
1160                     gettext("%s was not found in the config storage"), path);
1161                 /* still attempt to remove */
1162                 (void) disable_dev(path);
1163                 rc = 1;
1164         }
1165         destroy_hashtable();
1166 
1167         sv_cfg_close();
1168         return (rc);
1169 }
1170 
1171 
1172 static void
1173 compare_tag(char *path)
1174 {
1175         char buf[CFG_MAX_BUF], vol[CFG_MAX_BUF], cnode[CFG_MAX_BUF];
1176         char key[CFG_MAX_KEY];
1177         int found, setnumber, i;
1178         char *tag;
1179 
1180         sv_check_cluster(path);
1181         cfg_resource(cfg, (char *)NULL);        /* reset */
1182         cfg_rewind(cfg, CFG_SEC_CONF);
1183 
1184 #ifdef DEBUG
1185         if (cfg_cluster_tag != NULL && implicit_tag != NULL) {
1186                 error(NULL, gettext("compare_tag: -C %s AND implicit_tag %s!"),
1187                     cfg_cluster_tag, implicit_tag);
1188         }
1189 #endif
1190 
1191         if (cfg_cluster_tag != NULL)
1192                 tag = cfg_cluster_tag;
1193         else if (implicit_tag != NULL)
1194                 tag = implicit_tag;
1195         else
1196                 tag = "-";
1197 
1198         found = 0;
1199         for (i = 0; i < sv_max_devices; i++) {
1200                 setnumber = i + 1;
1201                 (void) snprintf(key, sizeof (key), "sv.set%d", setnumber);
1202                 if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) {
1203                         break;
1204                 }
1205 
1206                 if (sscanf(buf, "%s - %s", vol, cnode) != 2) {
1207                         continue;
1208                 }
1209 
1210                 if (strcmp(path, vol) == 0) {
1211                         found = 1;
1212                         break;
1213                 }
1214         }
1215 
1216         if (!found) {
1217                 warn(NULL, gettext("unable to find %s in the configuration"),
1218                     path);
1219                 return;
1220         }
1221 
1222         /* have name match, compare cnode to new tag */
1223 
1224         if (strcmp(tag, cnode) == 0) {
1225                 /* cluster tags match */
1226                 return;
1227         }
1228 
1229         /* need to change the cluster tag */
1230 
1231         (void) snprintf(key, sizeof (key), "sv.set%d.cnode", setnumber);
1232         if (cfg_put_cstring(cfg, key, tag, strlen(tag)) < 0) {
1233                 warn(NULL,
1234                     gettext("unable to change cluster tag for %s"), path);
1235                 return;
1236         }
1237 
1238         cfg_changed = 1;
1239 
1240         /* change "-" tags to "" for display purposes */
1241 
1242         if (strcmp(tag, "-") == 0)
1243                 tag = "";
1244 
1245         if (strcmp(cnode, "-") == 0)
1246                 (void) strcpy(cnode, "");
1247 
1248         (void) printf(
1249             gettext("%s: changed cluster tag for %s from \"%s\" to \"%s\"\n"),
1250             program, path, cnode, tag);
1251 
1252         spcs_log("sv", NULL,
1253             gettext("%s: changed cluster tag for %s from \"%s\" to \"%s\""),
1254             program, path, cnode, tag);
1255 }
1256 
1257 
1258 /* LINT - not static as fwcadm uses it */
1259 static void
1260 compare_sv(char *conf_file)
1261 {
1262         sv_name_t *svn_config;          /* Devices in config file */
1263         sv_name_t *svn_system;          /* Devices in system */
1264         sv_name_t *enable;              /* Devices that need enabled */
1265         sv_list_t svl_system;
1266         int config_cnt;
1267         int sys_cnt = 0;
1268         int setnumber, i, j;
1269         int index = 0;  /* Index in enable[] */
1270         int found;
1271         int fd0;
1272 
1273         svn_config = sv_alloc_svnames();
1274         svn_system = sv_alloc_svnames();
1275         enable = sv_alloc_svnames();
1276 
1277         bzero(svn_system, sizeof (svn_system));
1278         bzero(&svl_system, sizeof (svl_system));
1279         bzero(enable, sizeof (enable));
1280 
1281         /*
1282          * Read the configuration file
1283          * The return value is the number of entries
1284          */
1285         config_cnt = read_config_file(conf_file, svn_config);
1286 
1287         if ((fd0 = open(sv_rpath, O_RDONLY)) < 0)
1288                 error(NULL, gettext("unable to open %s: %s"),
1289                         sv_rpath, strerror(errno));
1290 
1291         /* Grab the system list from the driver */
1292         svl_system.svl_count = sv_max_devices;
1293         svl_system.svl_names = &svn_system[0];
1294         svl_system.svl_error = spcs_s_ucreate();
1295 
1296         if (ioctl(fd0, SVIOC_LIST, &svl_system) < 0) {
1297                 error(&svl_system.svl_error, gettext("unable to get list"));
1298         }
1299 
1300         spcs_s_ufree(&svl_system.svl_error);
1301         (void) close(fd0);
1302 
1303         /*
1304          * Count the number of devices in the system.
1305          * The last entry in the array has '\0' for a path name.
1306          */
1307         for (j = 0; j < sv_max_devices; j++) {
1308                 if (svn_system[j].svn_path[0] != '\0') {
1309                         sys_cnt++;
1310                 } else {
1311                         break;
1312                 }
1313         }
1314         /*
1315          * Compare the configuration array with the system array.
1316          * Mark any differences and disable conflicting devices.
1317          */
1318         for (i = 0; i < config_cnt; i++) {
1319                 found = 0;
1320                 for (j = 0; j < sys_cnt; j++) {
1321                         if (svn_system[j].svn_path[0] == '\0' ||
1322                             svn_system[j].svn_mode == 0)
1323                                 continue;
1324 
1325                         /*  Check to see if path matches */
1326                         if (strcmp(svn_system[j].svn_path,
1327                             svn_config[i].svn_path) == 0) {
1328                                 /*  Found a match  */
1329                                 svn_system[j].svn_path[0] = '\0';
1330                                 found++;
1331                                 break;
1332                         }
1333                 }
1334 
1335                 if (!found) {
1336                         /* Minor number not in system  = > enable device */
1337                         enable[index].svn_mode = svn_config[i].svn_mode;
1338                         (void) strcpy(enable[index].svn_path,
1339                             svn_config[i].svn_path);
1340                         index++;
1341                 }
1342         }
1343 
1344         /* Disable any devices that weren't in the config file */
1345         for (j = 0; j < sys_cnt; j++) {
1346                 sv_check_cluster(NULL);
1347                 sv_cfg_open(CFG_WRLOCK);
1348                 create_cfg_hash();
1349                 if (svn_system[j].svn_path[0] != '\0' &&
1350                     svn_system[j].svn_mode != 0) {
1351                         (void) printf(gettext("%s: disabling sv: %s\n"),
1352                             program, svn_system[j].svn_path);
1353                         if (disable_dev(svn_system[j].svn_path) == 0) {
1354                                 setnumber =
1355                                         find_in_hash(svn_system[j].svn_path);
1356                                 if (setnumber != -1) {
1357                                         /* the volume was found in cfg store */
1358                                         remove_from_cfgfile(
1359                                         svn_system[j].svn_path, setnumber);
1360                                 }
1361                         }
1362                 }
1363                 sv_cfg_close();
1364                 destroy_hashtable();
1365         }
1366 
1367         while (index) {
1368                 /*
1369                  * Config file doesn't match system => enable the devices
1370                  * in enable[]
1371                  */
1372                 index--;
1373                 (void) printf(gettext("%s: enabling new sv: %s\n"),
1374                     program, enable[index].svn_path);
1375                 (void) enable_dev(&enable[index]);
1376         }
1377 
1378         /*
1379          * Search for entries where the cluster tag has changed.
1380          */
1381         sv_check_cluster(NULL);
1382         sv_cfg_open(CFG_WRLOCK);
1383 
1384         for (i = 0; i < sv_max_devices; i++) {
1385                 if (svn_config[i].svn_path[0] == '\0')
1386                         break;
1387 
1388                 compare_tag(svn_config[i].svn_path);
1389         }
1390 
1391         sv_cfg_close();
1392 }
1393 
1394 
1395 /*
1396  * We assume that the volume is already enabled and we can only
1397  * be changing the cluster tag.  Anything else is an error.
1398  */
1399 /* LINT - not static as fwcadm uses it */
1400 static void
1401 compare_one_sv(char *path)
1402 {
1403         sv_get_maxdevs();
1404         sv_check_cluster(NULL);
1405         sv_cfg_open(CFG_WRLOCK);
1406 
1407         compare_tag(path);
1408 
1409         sv_cfg_close();
1410 }
1411 
1412 /*
1413  * Read all sets from the libdscfg configuration file, and store everything in
1414  * the hashfile.
1415  *
1416  * We assume that the config file has been opened & rewound for us.  We store
1417  * the volume name as the key, and the setnumber where we found it as the data.
1418  *
1419  * The caller can pass in a pointer to the maximum number of volumes, or
1420  * a pointer to NULL, specifying we want 'all' the volumes.  The table is
1421  * searched using find_in_hash.
1422  */
1423 static void
1424 create_cfg_hash()
1425 {
1426         char key[CFG_MAX_KEY], buf[CFG_MAX_BUF];
1427         char vol[CFG_MAX_BUF], cnode[CFG_MAX_BUF];
1428         int setnumber;
1429         ENTRY item;
1430 
1431         if (hcreate((size_t)sv_max_devices) == 0)
1432                 error(NULL, gettext("unable to create hash table"));
1433 
1434         for (setnumber = 1; /* CSTYLED */; setnumber++) {
1435                 (void) snprintf(key, sizeof (key), "sv.set%d", setnumber);
1436                 if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0)
1437                         break;
1438 
1439                 if (sscanf(buf, "%s - %s", vol, cnode) != 2) {
1440                         continue;
1441                 }
1442 
1443                 item.key = strdup(vol);
1444                 item.data = (void *)setnumber;
1445                 if (hsearch(item, ENTER) == NULL) {
1446                         error(NULL,
1447                             gettext("unable to add entry to hash table"));
1448                 }
1449         }
1450 }
1451 
1452 /*
1453  * Function to search the hash for a specific volume.  If it is found,
1454  * we return the set number.  If it isn't found, we return -1
1455  */
1456 static int
1457 find_in_hash(char *path)
1458 {
1459         ENTRY *found_entry, item;
1460         int retval = -1;
1461 
1462         item.key = path;
1463 
1464         if ((found_entry = hsearch(item, FIND)) != NULL) {
1465                 retval = (int)found_entry->data;
1466         }
1467 
1468         return (retval);
1469 }
1470 
1471 /*
1472  * Just a wrapper to destory the hashtable.  At some point in the future we
1473  * might want to do something more....  For instance, verify that the cfg
1474  * database and the kernel configuration match (?)  Just an idea.
1475  */
1476 static void
1477 destroy_hashtable()
1478 {
1479         hdestroy();
1480 }
1481 
1482 /*
1483  * This function will remove a particular set from the config file.
1484  *
1485  * We make a whole host of assumptions:
1486  *   o the hashfile is up to date;
1487  *   o The config file has been opened with a WRLOCK for us.
1488  */
1489 static void
1490 remove_from_cfgfile(char *path, int setnumber)
1491 {
1492         char key[CFG_MAX_KEY];
1493         int sev;
1494         char *lcltag;
1495 
1496         /* attempt to remove the volume from config storage */
1497         (void) snprintf(key, sizeof (key), "sv.set%d", setnumber);
1498         if (cfg_put_cstring(cfg, key, NULL, 0) < 0) {
1499                 warn(NULL, gettext("unable to remove %s from "
1500                     "config storage: %s"), path, cfg_error(&sev));
1501         } else {
1502                 if (implicit_tag && *implicit_tag) {
1503                         lcltag = implicit_tag;
1504                 } else if (cfg_cluster_tag && *cfg_cluster_tag) {
1505                         lcltag = cfg_cluster_tag;
1506                 } else {
1507                         lcltag = "-";
1508                 }
1509                 if (cfg_rem_user(cfg, path, lcltag, "sv") != CFG_USER_LAST) {
1510                         warn(NULL, gettext("unable to remove %s from dsvol"),
1511                             path);
1512                 }
1513                 cfg_changed = 1;
1514         }
1515 }