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 <stdio.h> 28 #include <fcntl.h> 29 #include <unistd.h> 30 #include <sys/stat.h> 31 #include <sys/mkdev.h> 32 #include <strings.h> 33 #include <stdarg.h> 34 #include <stdlib.h> 35 #include <locale.h> 36 #include <errno.h> 37 38 #include <sys/nsctl/cfg.h> 39 40 #include <sys/unistat/spcs_s.h> 41 #include <sys/unistat/spcs_s_u.h> 42 #include <sys/unistat/spcs_errors.h> 43 #include <sys/unistat/spcs_s_impl.h> 44 45 #include <sys/nsctl/sv.h> 46 #include <sys/nsctl/nsc_hash.h> 47 48 #define DEV_EXPAND 32 49 50 #define DO_DISABLE 0 51 #define DO_ENABLE 1 52 53 /* 54 * Utility functions for iiadm and rdcadm/sndradm. 55 */ 56 57 typedef struct hash_data_s { 58 union { 59 char *users; 60 char *mode; 61 } u; 62 char *path; 63 char *node; 64 int setno; 65 } hash_data_t; 66 67 typedef struct { 68 dev_t rdev; 69 mode_t mode; 70 char *path; 71 } device_t; 72 73 static hash_data_t *make_svol_data(char *, char *, char *, int); 74 static hash_data_t *make_dsvol_data(char *, char *, char *, int); 75 static void delete_svol_data(void *); 76 static void delete_dsvol_data(void *); 77 static int sv_action(char *, CFGFILE *, char *, int); 78 79 static int add_dev_entry(const char *); 80 static int compare(const void *, const void *); 81 static char *find_devid(const char *); 82 static void free_dev_entries(); 83 static void rebuild_devhash(); 84 85 static hash_node_t **dsvol; 86 static int dsvol_loaded = 0; 87 88 static hash_node_t **svol; 89 static int svol_loaded = 0; 90 91 static hash_node_t **shadowvol; 92 93 static hash_node_t **devhash; 94 static device_t *devlist; 95 static int devcount = 0; 96 static int devalloc = 0; 97 98 /* 99 * cfg_add_user 100 * 101 * Description: 102 * Adds the calling tool as a user of the volume. 103 * 104 * Inputs: 105 * char *path: The pathname of the volume to be enabled. 106 * char *cnode: The device group name, or NULL if -C local or not cluster 107 * CFGFILE *cfg: A pointer to the current config file, or NULL if this 108 * function is to open/write/commit/close the change itself. 109 * 110 * Return values: 111 * CFG_USER_FIRST: Indicates that this is the first user of this 112 * particular volume. 113 * CFG_USER_OK: Indicates that the volume has already been entered into 114 * the config file. 115 * CFG_USER_ERR: Indicates that some failure has occurred and no changes 116 * to the config file have been made. 117 * CFG_USER_REPEAT: Indicates that this user has already registered for 118 * the volume. 119 */ 120 int 121 cfg_add_user(CFGFILE* cfg, char *path, char *cnode, char *user) 122 { 123 int self_open, self_loaded, change_made; 124 char *ctag, search_key[ CFG_MAX_KEY ], buf[ CFG_MAX_BUF ]; 125 int retval, rc; 126 hash_data_t *data; 127 128 self_open = (cfg == NULL); 129 self_loaded = 0; 130 change_made = 0; 131 132 if (self_open) { 133 cfg = cfg_open(NULL); 134 if (cfg == NULL) { 135 return (CFG_USER_ERR); 136 } 137 138 if (!cfg_lock(cfg, CFG_WRLOCK)) { 139 /* oops */ 140 cfg_close(cfg); 141 return (CFG_USER_ERR); 142 } 143 } 144 145 /* Check cnode */ 146 ctag = cfg_get_resource(cfg); 147 if (cnode) { 148 if (ctag) { 149 if (strcmp(cnode, ctag)) 150 return (CFG_USER_ERR); 151 } else 152 cfg_resource(cfg, cnode); 153 } else 154 cnode = ctag; 155 156 if (!dsvol_loaded) { 157 if (cfg_load_dsvols(cfg) < 0) { 158 if (self_open) { 159 cfg_close(cfg); 160 } 161 return (CFG_USER_ERR); 162 } 163 self_loaded = 1; 164 } 165 166 /* find the volume */ 167 (void) snprintf(search_key, CFG_MAX_KEY, "%s:%s", path, cnode); 168 data = nsc_lookup(dsvol, search_key); 169 170 if (!data) { 171 /* whoops, not found. Add as new user */ 172 cfg_rewind(cfg, CFG_SEC_CONF); 173 (void) snprintf(buf, CFG_MAX_BUF, "%s %s %s", path, cnode, 174 user); 175 rc = cfg_put_cstring(cfg, "dsvol", buf, strlen(buf)); 176 if (rc < 0) { 177 if (self_loaded) { 178 cfg_unload_dsvols(); 179 } 180 if (self_open) { 181 cfg_close(cfg); 182 } 183 return (CFG_USER_ERR); 184 } 185 /* reload hash, if we need to */ 186 if (!self_loaded) { 187 cfg_unload_dsvols(); 188 if (cfg_load_dsvols(cfg) < 0) { 189 if (self_open) { 190 cfg_close(cfg); 191 } 192 return (CFG_USER_ERR); 193 } 194 } 195 retval = CFG_USER_FIRST; 196 change_made = 1; 197 } else { 198 /* Check to ensure we're not already listed */ 199 char *p = strdup(data->u.users); 200 char *q = strtok(p, ","); 201 while (q && (strcmp(q, user) != 0)) { 202 q = strtok(0, ","); 203 } 204 free(p); /* not using data; only testing 'q' ptr */ 205 206 if (!q) { 207 /* not listed as a user */ 208 cfg_rewind(cfg, CFG_SEC_CONF); 209 (void) snprintf(buf, CFG_MAX_BUF, "%s %s %s,%s", 210 data->path, data->node, data->u.users, user); 211 (void) snprintf(search_key, CFG_MAX_KEY, "dsvol.set%d", 212 data->setno); 213 if (cfg_put_cstring(cfg, search_key, buf, 214 strlen(buf)) < 0) { 215 if (self_loaded) { 216 cfg_unload_dsvols(); 217 } 218 if (self_open) { 219 cfg_close(cfg); 220 } 221 return (CFG_USER_ERR); 222 } 223 224 /* 225 * Since we deleted an entry from the config 226 * file, we don't know what all the new 227 * set numbers are. We need to reload 228 * everything 229 */ 230 if (!self_loaded) { 231 cfg_unload_dsvols(); 232 if (cfg_load_dsvols(cfg) < 0) { 233 if (self_open) { 234 cfg_close(cfg); 235 } 236 return (CFG_USER_ERR); 237 } 238 } 239 change_made = 1; 240 retval = CFG_USER_OK; 241 } else { 242 retval = CFG_USER_REPEAT; 243 } 244 } 245 246 if (self_loaded) { 247 cfg_unload_dsvols(); 248 } 249 250 if (self_open) { 251 if (change_made) 252 (void) cfg_commit(cfg); 253 cfg_close(cfg); 254 } 255 256 return (retval); 257 } 258 259 /* 260 * cfg_rem_user 261 * 262 * Description: 263 * Removes a user from the config file. 264 * 265 * Inputs: 266 * char *path: The pathname of the volume to be enabled. 267 * char *cnode: The device group name, or NULL if -C local or not cluster 268 * char *user: The subsystem that is adding this tag (sv, ii, sndr) 269 * CFGFILE *cfg: A pointer to the current config file, or NULL if this 270 * function is to open/write/commit/close the change itself. 271 * Return values: 272 * CFG_USER_ERR: An error occurred during the processing of this 273 * directive. 274 * CFG_USER_OK: User successfully removed; volume in use by other(s). 275 * CFG_USER_LAST: User successfuly removed; no other users registered 276 * CFG_USER_GONE: The volume is no longer listed in the dsvol section, 277 * indicating some sort of application-level error. 278 * 279 */ 280 int 281 cfg_rem_user(CFGFILE *cfg, char *path, char *cnode, char *user) 282 { 283 int self_open, self_loaded, change_made; 284 char *ctag, search_key[ CFG_MAX_KEY ], buf[ CFG_MAX_BUF ]; 285 char cfg_key[ CFG_MAX_KEY ]; 286 hash_data_t *data; 287 int retval; 288 int force_remove; 289 290 self_open = (cfg == NULL); 291 self_loaded = 0; 292 change_made = 0; 293 force_remove = (strcmp(user, "sv") == 0); 294 295 if ('-' == *user) { 296 ++user; 297 } 298 299 /* Check cnode */ 300 ctag = cfg_get_resource(cfg); 301 if (cnode) { 302 if (ctag) { 303 if (strcmp(cnode, ctag)) 304 return (CFG_USER_ERR); 305 } else 306 cfg_resource(cfg, cnode); 307 } else 308 cnode = ctag; 309 310 if (self_open) { 311 cfg = cfg_open(NULL); 312 if (cfg == NULL) { 313 return (CFG_USER_ERR); 314 } 315 316 if (!cfg_lock(cfg, CFG_WRLOCK)) { 317 /* oops */ 318 cfg_close(cfg); 319 return (CFG_USER_ERR); 320 } 321 } 322 323 324 change_made = 0; 325 if (!dsvol_loaded) { 326 if (cfg_load_dsvols(cfg) < 0) { 327 if (self_open) { 328 cfg_close(cfg); 329 } 330 return (CFG_USER_ERR); 331 } 332 self_loaded = 1; 333 } 334 335 /* find the volume */ 336 (void) snprintf(search_key, CFG_MAX_KEY, "%s:%s", path, cnode); 337 data = nsc_lookup(dsvol, search_key); 338 339 if (!data) { 340 /* yipes */ 341 retval = CFG_USER_GONE; 342 } else if (force_remove) { 343 retval = CFG_USER_LAST; 344 cfg_rewind(cfg, CFG_SEC_CONF); 345 (void) snprintf(cfg_key, CFG_MAX_KEY, "dsvol.set%d", 346 data->setno); 347 if (cfg_put_cstring(cfg, cfg_key, NULL, 0) < 0) { 348 if (self_loaded) { 349 cfg_unload_dsvols(); 350 } 351 if (self_open) { 352 cfg_close(cfg); 353 } 354 return (CFG_USER_ERR); 355 } 356 if (!self_loaded) { 357 cfg_unload_dsvols(); 358 if (cfg_load_dsvols(cfg) < 0) { 359 if (self_open) { 360 cfg_close(cfg); 361 } 362 return (CFG_USER_ERR); 363 } 364 } 365 } else { 366 char *p = strdup(data->u.users); 367 char *q = strtok(p, ","); 368 int appended = 0; 369 370 (void) snprintf(buf, CFG_MAX_BUF, "%s %s ", data->path, 371 data->node); 372 while (q && (strcmp(q, user) != 0)) { 373 if (appended) { 374 strcat(buf, ","); 375 strcat(buf, q); 376 } else { 377 strcat(buf, q); 378 appended = 1; 379 } 380 q = strtok(0, ","); 381 } 382 383 if (!q) { 384 /* uh-oh */ 385 retval = CFG_USER_GONE; 386 } else { 387 /* old user skipped; add in remaining users */ 388 while (q = strtok(0, ", ")) { 389 if (appended) { 390 strcat(buf, ","); 391 strcat(buf, q); 392 } else { 393 strcat(buf, q); 394 appended = 1; 395 } 396 } 397 398 if (appended) { 399 retval = CFG_USER_OK; 400 cfg_rewind(cfg, CFG_SEC_CONF); 401 (void) snprintf(cfg_key, CFG_MAX_KEY, 402 "dsvol.set%d", data->setno); 403 if (cfg_put_cstring(cfg, cfg_key, buf, 404 strlen(buf)) < 0) { 405 if (self_loaded) { 406 cfg_unload_dsvols(); 407 } 408 if (self_open) { 409 cfg_close(cfg); 410 } 411 return (CFG_USER_ERR); 412 } 413 if (!self_loaded) { 414 cfg_unload_dsvols(); 415 if (cfg_load_dsvols(cfg) < 0) { 416 if (self_open) { 417 cfg_close(cfg); 418 } 419 return (CFG_USER_ERR); 420 } 421 } 422 } else { 423 retval = CFG_USER_LAST; 424 cfg_rewind(cfg, CFG_SEC_CONF); 425 (void) snprintf(cfg_key, CFG_MAX_KEY, 426 "dsvol.set%d", data->setno); 427 if (cfg_put_cstring(cfg, cfg_key, NULL, 428 0) < 0) { 429 if (self_loaded) { 430 cfg_unload_dsvols(); 431 } 432 if (self_open) { 433 cfg_close(cfg); 434 } 435 return (CFG_USER_ERR); 436 } 437 /* 438 * Since we deleted an entry from the config 439 * file, we don't know what all the new 440 * set numbers are. We need to reload 441 * everything 442 */ 443 if (!self_loaded) { 444 cfg_unload_dsvols(); 445 if (cfg_load_dsvols(cfg) < 0) { 446 if (self_open) { 447 cfg_close(cfg); 448 } 449 return (CFG_USER_ERR); 450 } 451 } 452 } 453 change_made = 1; 454 } 455 } 456 457 if (self_loaded) { 458 cfg_unload_dsvols(); 459 } 460 461 if (self_open) { 462 if (change_made) 463 (void) cfg_commit(cfg); 464 cfg_close(cfg); 465 } 466 467 return (retval); 468 } 469 470 /* 471 * Enable a volume under SV control (or add this char *user to the list 472 * of users of that volume). 473 * 474 * Parameters: 475 * cfg - The config file to use. 476 * path - The pathname of the volume 477 * ctag - The cluster tag for this volume (if any) 478 * user - The user (sv, ii, sndr) of the volume. 479 */ 480 int 481 cfg_vol_enable(CFGFILE *cfg, char *path, char *ctag, char *user) 482 { 483 int rc; 484 int retval; 485 486 if (!ctag || *ctag == '\0') { 487 ctag = "-"; 488 } 489 490 retval = -1; 491 rc = cfg_add_user(cfg, path, ctag, user); 492 switch (rc) { 493 case CFG_USER_ERR: 494 spcs_log("dsvol", NULL, 495 gettext("unable to set up dsvol section of config for %s"), 496 path); 497 break; 498 case CFG_USER_OK: 499 retval = 0; 500 break; 501 case CFG_USER_FIRST: 502 /* enable sv! */ 503 retval = sv_action(path, cfg, ctag, DO_ENABLE); 504 if (retval < 0) { 505 (void) cfg_rem_user(cfg, path, ctag, user); 506 } 507 break; 508 default: 509 spcs_log("dsvol", NULL, 510 gettext("unexpected return from cfg_add_user(%d)"), rc); 511 break; 512 } 513 514 return (retval); 515 } 516 517 /* 518 * Disable a volume from SV control (or remove this char *user from the list 519 * of users of that volume). 520 * 521 * Parameters: 522 * cfg - The config file to use. 523 * path - The pathname of the volume 524 * ctag - The cluster tag for this volume (if any) 525 * user - The user (sv, ii, sndr) of the volume. 526 */ 527 int 528 cfg_vol_disable(CFGFILE *cfg, char *path, char *ctag, char *user) 529 { 530 int rc; 531 int retval; 532 533 if (!ctag || *ctag == '\0') { 534 ctag = "-"; 535 } 536 537 retval = -1; 538 rc = cfg_rem_user(cfg, path, ctag, user); 539 switch (rc) { 540 case CFG_USER_ERR: 541 spcs_log("dsvol", NULL, 542 gettext("unable to set up dsvol section of config for %s"), 543 path); 544 break; 545 case CFG_USER_OK: 546 retval = 0; 547 break; 548 case CFG_USER_GONE: 549 spcs_log("dsvol", NULL, 550 gettext("%s tried to remove non-existent tag for %s"), 551 user, path); 552 break; 553 case CFG_USER_LAST: 554 /* diable sv! */ 555 retval = sv_action(path, cfg, ctag, DO_DISABLE); 556 break; 557 default: 558 spcs_log("dsvol", NULL, 559 gettext("unexpected return from cfg_rem_user(%d)"), rc); 560 break; 561 } 562 563 return (retval); 564 } 565 566 /* 567 * cfg_load_dsvols 568 * 569 * Description: 570 * Loads the dsvol section of the config file into a giant hash, to 571 * make searching faster. The important bit to remember is to not 572 * release the write lock between calling cfg_load_dsvols() and the 573 * cfg_*_user() functions. 574 * 575 * Assumptions: 576 * 1/ cfg file is open 577 * 2/ cfg file has been write-locked 578 * 3/ user of this routine may already be using hcreate/hsearch 579 * 580 * Return value: 581 * -1 if error, or total number of sets found 582 */ 583 int 584 cfg_load_dsvols(CFGFILE *cfg) 585 { 586 int set, rc, entries; 587 char search_key[ CFG_MAX_KEY ]; 588 char *buf; 589 char **entry, *path, *cnode, *users; 590 hash_data_t *data; 591 int devs_added = 0; 592 int offset = 0; 593 char *ctag = cfg_get_resource(cfg); 594 if (!ctag || *ctag == '\0') { 595 ctag = "-"; 596 } 597 598 dsvol = nsc_create_hash(); 599 if (!dsvol) { 600 return (-1); 601 } 602 603 rc = 0; 604 cfg_rewind(cfg, CFG_SEC_CONF); 605 entries = cfg_get_section(cfg, &entry, "dsvol"); 606 for (set = 1; set <= entries; set++) { 607 buf = entry[set - 1]; 608 609 /* split up the line */ 610 if (!(path = strtok(buf, " "))) { 611 /* oops, now what? */ 612 free(buf); 613 break; 614 } 615 if (!(cnode = strtok(0, " "))) { 616 free(buf); 617 break; 618 } 619 if (ctag && (strcmp(cnode, ctag) != 0)) { 620 ++offset; 621 free(buf); 622 continue; 623 } 624 625 if (!(users = strtok(0, " "))) { 626 free(buf); 627 break; 628 } 629 630 data = make_dsvol_data(path, cnode, users, set - offset); 631 if (!data) { 632 free(buf); 633 break; 634 } 635 (void) snprintf(search_key, CFG_MAX_KEY, "%s:%s", path, cnode); 636 rc = nsc_insert_node(dsvol, data, search_key); 637 if (rc < 0) { 638 free(buf); 639 break; 640 } 641 642 /* we also need to keep track of node information */ 643 rc = add_dev_entry(path); 644 if (rc < 0) { 645 free(buf); 646 break; 647 } else if (rc) 648 ++devs_added; 649 650 free(buf); 651 rc = 0; 652 } 653 654 while (set < entries) 655 free(entry[set++]); 656 if (entries) 657 free(entry); 658 659 if (devs_added) { 660 qsort(devlist, devcount, sizeof (device_t), compare); 661 rebuild_devhash(); 662 } 663 664 dsvol_loaded = 1; 665 return (rc < 0? rc : entries); 666 } 667 668 /* 669 * cfg_unload_dsvols 670 * 671 * Description: 672 * Free all memory allocated with cfg_load_dsvols. 673 */ 674 void 675 cfg_unload_dsvols() 676 { 677 if (dsvol) { 678 nsc_remove_all(dsvol, delete_dsvol_data); 679 dsvol = 0; 680 dsvol_loaded = 0; 681 } 682 } 683 684 /* 685 * cfg_load_svols 686 * 687 * Description: 688 * Loads the sv section of the config file into a giant hash, to make 689 * searching faster. The important bit to remember is to not release 690 * the write lock between calling cfg_load_svols() and the cfg_*_user() 691 * functions. 692 * 693 * Assumptions: 694 * 1/ cfg file is open 695 * 2/ cfg file has been write-locked 696 * 3/ user of this routine may already be using builtin hcreate/hsearch 697 */ 698 int 699 cfg_load_svols(CFGFILE *cfg) 700 { 701 int set, entries, offset = 0; 702 char *buf, **entry; 703 char *path, *mode, *cnode; 704 hash_data_t *data; 705 char *ctag = cfg_get_resource(cfg); 706 if (!ctag || *ctag == '\0') { 707 ctag = "-"; 708 } 709 710 svol = nsc_create_hash(); 711 if (!svol) { 712 return (-1); 713 } 714 715 cfg_rewind(cfg, CFG_SEC_CONF); 716 entries = cfg_get_section(cfg, &entry, "sv"); 717 for (set = 1; set <= entries; set++) { 718 buf = entry[set - 1]; 719 720 /* split up the line */ 721 if (!(path = strtok(buf, " "))) { 722 free(buf); 723 break; 724 } 725 if (!(mode = strtok(0, " "))) { 726 free(buf); 727 break; 728 } 729 if (!(cnode = strtok(0, " "))) { 730 cnode = ""; 731 } 732 733 if (ctag && (strcmp(cnode, ctag) != 0)) { 734 ++offset; 735 free(buf); 736 continue; 737 } 738 739 data = make_svol_data(path, mode, cnode, set - offset); 740 if (!data) { 741 free(buf); 742 break; 743 } 744 if (nsc_insert_node(svol, data, path) < 0) { 745 free(buf); 746 break; 747 } 748 free(buf); 749 } 750 while (set < entries) 751 free(entry[set++]); 752 if (entries) 753 free(entry); 754 755 svol_loaded = 1; 756 return (0); 757 } 758 759 /* 760 * cfg_unload_svols 761 * 762 * Description: 763 * Frees all memory allocated with cfg_load_dsvols 764 */ 765 void 766 cfg_unload_svols() 767 { 768 if (svol) { 769 nsc_remove_all(svol, delete_svol_data); 770 svol = 0; 771 svol_loaded = 0; 772 } 773 } 774 775 /* 776 * cfg_get_canonical_name 777 * 778 * Description: 779 * Find out whether a device is already known by another name in 780 * the config file. 781 * 782 * Parameters: 783 * cfg - The config file to use 784 * path - The pathname of the device 785 * result - (output) The name it is otherwise known as. This parameter 786 * must be freed by the caller. 787 * 788 * Return values: 789 * -1: error 790 * 0: name is as expected, or is not known 791 * 1: Name is known by different name (stored in 'result') 792 */ 793 int 794 cfg_get_canonical_name(CFGFILE *cfg, const char *path, char **result) 795 { 796 int self_loaded; 797 char *alt_path; 798 int retval; 799 800 if (devlist) { 801 self_loaded = 0; 802 } else { 803 if (cfg_load_shadows(cfg) < 0) { 804 return (-1); 805 } 806 self_loaded = 1; 807 } 808 809 /* see if it exists under a different name */ 810 alt_path = find_devid(path); 811 if (!alt_path || strcmp(path, alt_path) == 0) { 812 *result = NULL; 813 retval = 0; 814 } else { 815 /* a-ha */ 816 *result = strdup(alt_path); 817 retval = 1; 818 } 819 820 if (self_loaded) { 821 free_dev_entries(); 822 } 823 824 return (retval); 825 } 826 827 /* 828 * cfg_load_shadows 829 * 830 * Description: 831 * Load in shadow and bitmap volumes from the II section of the 832 * config file. SNDR's volumes are handled already by cfg_load_dsvols. 833 * Not all shadow volumes are listed under dsvol: they can be exported. 834 * 835 * Parameters: 836 * cfg - The config file to use 837 * 838 * Return values: 839 * -1: error 840 * 0: success 841 */ 842 int 843 cfg_load_shadows(CFGFILE *cfg) 844 { 845 int set, self_loaded, rc, entries; 846 char *buf, **entry, *ptr; 847 int devs_added = 0; 848 849 if (dsvol_loaded) { 850 self_loaded = 0; 851 } else { 852 if (cfg_load_dsvols(cfg) < 0) { 853 return (-1); 854 } 855 self_loaded = 1; 856 } 857 858 shadowvol = nsc_create_hash(); 859 if (!shadowvol) { 860 return (-1); 861 } 862 863 rc = 0; 864 cfg_rewind(cfg, CFG_SEC_CONF); 865 entries = cfg_get_section(cfg, &entry, "ii"); 866 for (set = 1; set <= entries; set++) { 867 buf = entry[set - 1]; 868 869 /* skip the master vol */ 870 ptr = strtok(buf, " "); 871 872 /* shadow is next */ 873 ptr = strtok(NULL, " "); 874 875 rc = add_dev_entry(ptr); 876 if (rc < 0) { 877 free(buf); 878 break; 879 } else if (rc) 880 ++devs_added; 881 882 /* and next is bitmap */ 883 ptr = strtok(NULL, " "); 884 885 rc = add_dev_entry(ptr); 886 if (rc < 0) { 887 free(buf); 888 break; 889 } else if (rc) 890 ++devs_added; 891 rc = 0; 892 free(buf); 893 } 894 while (set < entries) 895 free(entry[set++]); 896 if (entries) 897 free(entry); 898 899 if (self_loaded) { 900 cfg_unload_dsvols(); 901 } 902 903 if (devs_added) { 904 /* sort it, in preparation for lookups */ 905 qsort(devlist, devcount, sizeof (device_t), compare); 906 rebuild_devhash(); 907 } 908 909 return (rc); 910 } 911 912 void 913 cfg_unload_shadows() 914 { 915 /* do nothing */ 916 } 917 918 /* ---------------------------------------------------------------------- */ 919 920 static hash_data_t * 921 make_dsvol_data(char *path, char *cnode, char *users, int set) 922 { 923 hash_data_t *data; 924 925 data = (hash_data_t *)malloc(sizeof (hash_data_t)); 926 if (!data) { 927 return (0); 928 } 929 930 data->u.users = strdup(users); 931 data->path = strdup(path); 932 data->node = strdup(cnode); 933 data->setno = set; 934 935 return (data); 936 } 937 938 static void 939 delete_dsvol_data(void *data) 940 { 941 hash_data_t *p = (hash_data_t *)data; 942 943 free(p->u.users); 944 free(p->path); 945 free(p->node); 946 free(p); 947 } 948 949 static hash_data_t * 950 make_svol_data(char *path, char *mode, char *cnode, int set) 951 { 952 hash_data_t *data; 953 954 data = (hash_data_t *)malloc(sizeof (hash_data_t)); 955 if (!data) { 956 return (0); 957 } 958 959 data->u.mode = strdup(mode); 960 data->path = strdup(path); 961 data->node = strdup(cnode); 962 data->setno = set; 963 964 return (data); 965 } 966 967 968 static void 969 delete_svol_data(void *data) 970 { 971 hash_data_t *p = (hash_data_t *)data; 972 973 free(p->u.mode); 974 free(p->path); 975 free(p->node); 976 free(p); 977 } 978 979 static int 980 sv_action(char *path, CFGFILE *caller_cfg, char *ctag, int enable) 981 { 982 struct stat stb; 983 sv_conf_t svc; 984 int fd = -1; 985 int cfg_changed = 0; 986 CFGFILE *cfg; 987 int print_log = 0; 988 int err = 0, rc; 989 int sv_ioctl, spcs_err, self_loaded; 990 char *log_str1, *log_str2; 991 char key[ CFG_MAX_KEY ]; 992 char buf[ CFG_MAX_BUF ]; 993 hash_data_t *node; 994 device_t *statinfo = 0; 995 996 if (caller_cfg == NULL) { 997 cfg = cfg_open(NULL); 998 if (cfg == NULL) 999 return (-1); 1000 1001 if (ctag) 1002 cfg_resource(cfg, ctag); 1003 } else 1004 cfg = caller_cfg; 1005 1006 1007 self_loaded = 0; 1008 sv_ioctl = (enable? SVIOC_ENABLE : SVIOC_DISABLE); 1009 log_str1 = (enable? gettext("enabled %s") : gettext("disabled %s")); 1010 log_str2 = (enable? gettext("unable to enable %s") : 1011 gettext("unable to disable %s")); 1012 spcs_err = (enable? SV_EENABLED : SV_EDISABLED); 1013 bzero(&svc, sizeof (svc)); 1014 1015 if (devhash) 1016 statinfo = nsc_lookup(devhash, path); 1017 1018 if (statinfo) { 1019 if (!S_ISCHR(statinfo->mode)) 1020 goto error; 1021 svc.svc_major = major(statinfo->rdev); 1022 svc.svc_minor = minor(statinfo->rdev); 1023 } else { 1024 if (stat(path, &stb) != 0) 1025 goto error; 1026 1027 if (!S_ISCHR(stb.st_mode)) 1028 goto error; 1029 svc.svc_major = major(stb.st_rdev); 1030 svc.svc_minor = minor(stb.st_rdev); 1031 } 1032 1033 strncpy(svc.svc_path, path, sizeof (svc.svc_path)); 1034 1035 fd = open(SV_DEVICE, O_RDONLY); 1036 if (fd < 0) 1037 goto error; 1038 1039 svc.svc_flag = (NSC_DEVICE | NSC_CACHE); 1040 svc.svc_error = spcs_s_ucreate(); 1041 1042 do { 1043 rc = ioctl(fd, sv_ioctl, &svc); 1044 } while (rc < 0 && errno == EINTR); 1045 1046 if (rc < 0) { 1047 if (errno != spcs_err) { 1048 spcs_log("sv", &svc.svc_error, log_str2, svc.svc_path); 1049 if (enable) 1050 goto error; 1051 else 1052 err = errno; 1053 } else 1054 err = spcs_err; 1055 } 1056 1057 spcs_log("sv", NULL, log_str1, svc.svc_path); 1058 1059 /* SV enable succeeded */ 1060 if (caller_cfg == NULL) /* was not previously locked */ 1061 if (!cfg_lock(cfg, CFG_WRLOCK)) 1062 goto error; 1063 1064 if (err != spcs_err) { /* already enabled, already in config */ 1065 if (enable) { 1066 cfg_rewind(cfg, CFG_SEC_CONF); 1067 (void) snprintf(buf, CFG_MAX_BUF, "%s - %s", path, 1068 ctag? ctag : "-"); 1069 if (cfg_put_cstring(cfg, "sv", buf, CFG_MAX_BUF) < 0) { 1070 /* SV config not updated, so SV disable again */ 1071 (void) ioctl(fd, SVIOC_DISABLE, &svc); 1072 print_log++; 1073 } else 1074 cfg_changed = 1; 1075 } else { 1076 /* pull it out of the config */ 1077 if (!svol_loaded) { 1078 if (cfg_load_svols(cfg) < 0) { 1079 if (NULL == caller_cfg) { 1080 cfg_close(cfg); 1081 } 1082 return (-1); 1083 } 1084 self_loaded = 1; 1085 } 1086 node = nsc_lookup(svol, svc.svc_path); 1087 if (node) { 1088 cfg_rewind(cfg, CFG_SEC_CONF); 1089 (void) snprintf(key, CFG_MAX_KEY, "sv.set%d", 1090 node->setno); 1091 if (cfg_put_cstring(cfg, key, NULL, NULL) < 0) { 1092 spcs_log("sv", NULL, 1093 gettext("failed to remove %s from " 1094 "sv config"), svc.svc_path); 1095 } 1096 /* 1097 * Since we deleted an entry from the config 1098 * file, we don't know what all the new 1099 * set numbers are. We need to reload 1100 * everything 1101 */ 1102 if (!self_loaded) { 1103 cfg_unload_svols(); 1104 if (cfg_load_svols(cfg) < 0) { 1105 if (NULL == caller_cfg) { 1106 cfg_close(cfg); 1107 } 1108 return (-1); 1109 } 1110 } 1111 cfg_changed = 1; 1112 } 1113 if (self_loaded) { 1114 cfg_unload_svols(); 1115 self_loaded = 0; 1116 } 1117 } 1118 } 1119 1120 #ifdef lint 1121 (void) printf("extra line to shut lint up %s\n", module_names[0]); 1122 #endif 1123 1124 error: 1125 if (fd >= 0) 1126 (void) close(fd); 1127 1128 if (cfg == NULL) 1129 return (-1); 1130 1131 if (cfg_changed) 1132 if (caller_cfg == NULL) /* we opened config */ 1133 (void) cfg_commit(cfg); 1134 1135 if (caller_cfg == NULL) 1136 cfg_close(cfg); 1137 if ((cfg_changed) || (err == spcs_err)) 1138 return (1); 1139 if (print_log) 1140 spcs_log("sv", NULL, 1141 gettext("unable to add to configuration, disabled %s"), 1142 svc.svc_path); 1143 spcs_s_ufree(&svc.svc_error); 1144 1145 return (-1); 1146 } 1147 1148 /* 1149 * add_dev_entry 1150 * 1151 * Add an entry into the devlist and the devhash for future lookups. 1152 * 1153 * Return values: 1154 * -1 An error occurred. 1155 * 0 Entry added 1156 * 1 Entry already exists. 1157 */ 1158 static int 1159 add_dev_entry(const char *path) 1160 { 1161 struct stat buf; 1162 device_t *newmem; 1163 hash_data_t *data; 1164 1165 if (!devhash) { 1166 devhash = nsc_create_hash(); 1167 if (!devhash) { 1168 return (-1); 1169 } 1170 } else { 1171 data = nsc_lookup(devhash, path); 1172 if (data) { 1173 return (1); 1174 } 1175 } 1176 1177 if (stat(path, &buf) < 0) { 1178 /* ignore error, we are most likely deleting entry anyway */ 1179 buf.st_rdev = 0; 1180 } 1181 1182 if (devcount >= devalloc) { 1183 /* make some room */ 1184 devalloc += DEV_EXPAND; 1185 newmem = (device_t *)realloc(devlist, devalloc * 1186 sizeof (device_t)); 1187 if (!newmem) { 1188 free_dev_entries(); 1189 return (-1); 1190 } else { 1191 devlist = newmem; 1192 } 1193 } 1194 1195 devlist[ devcount ].path = strdup(path); 1196 devlist[ devcount ].rdev = buf.st_rdev; 1197 devlist[ devcount ].mode = buf.st_mode; 1198 1199 if (nsc_insert_node(devhash, &devlist[devcount], path) < 0) { 1200 return (-1); 1201 } 1202 1203 ++devcount; 1204 return (0); 1205 } 1206 1207 static void 1208 rebuild_devhash() 1209 { 1210 int i; 1211 1212 if (!devhash) 1213 nsc_remove_all(devhash, 0); 1214 1215 devhash = nsc_create_hash(); 1216 if (!devhash) 1217 return; 1218 1219 for (i = 0; i < devcount; i++) { 1220 nsc_insert_node(devhash, &devlist[i], devlist[i].path); 1221 } 1222 } 1223 1224 static int 1225 compare(const void *va, const void *vb) 1226 { 1227 device_t *a = (device_t *)va; 1228 device_t *b = (device_t *)vb; 1229 1230 return (b->rdev - a->rdev); 1231 } 1232 1233 static char * 1234 find_devid(const char *path) 1235 { 1236 device_t key; 1237 device_t *result; 1238 struct stat buf; 1239 1240 if (!devlist || !devhash) 1241 return (NULL); 1242 1243 /* See if we already know the device id by this name */ 1244 result = (device_t *)nsc_lookup(devhash, path); 1245 if (result) { 1246 return (NULL); 1247 } 1248 1249 /* try to find it by another name */ 1250 if (stat(path, &buf) < 0) 1251 return (NULL); 1252 1253 key.rdev = buf.st_rdev; 1254 1255 /* it's storted, so we use the binary-chop method to find it */ 1256 result = bsearch(&key, devlist, devcount, sizeof (device_t), compare); 1257 1258 if (result) { 1259 return (result->path); 1260 } 1261 1262 return (NULL); 1263 } 1264 1265 static void 1266 free_dev_entries() 1267 { 1268 int i; 1269 device_t *p; 1270 1271 if (!devlist) { 1272 return; 1273 } 1274 for (i = 0, p = devlist; i < devcount; i++, p++) { 1275 free(p->path); 1276 } 1277 free(devlist); 1278 devlist = NULL; 1279 devcount = 0; 1280 devalloc = 0; 1281 1282 if (devhash) { 1283 nsc_remove_all(devhash, 0); 1284 devhash = NULL; 1285 } 1286 }