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 }