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 }