1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * Copyright 2012 Milan Jurik. All rights reserved.
  29  * Copyright 2018 Nexenta Systems, Inc.
  30  * Copyright 2019, Joyent, Inc.
  31  */
  32 
  33 #include <sys/types.h>
  34 #include <sys/stat.h>
  35 #include <fcntl.h>
  36 #include <stdlib.h>
  37 #include <stdio.h>
  38 #include <string.h>
  39 #include <ctype.h>
  40 #include <unistd.h>
  41 #include <getopt.h>
  42 #include <utmpx.h>
  43 #include <pwd.h>
  44 #include <auth_attr.h>
  45 #include <secdb.h>
  46 #include <sys/param.h>
  47 #include <sys/stat.h>
  48 #include <errno.h>
  49 
  50 #include <libshare.h>
  51 #include "sharemgr.h"
  52 #include <libscf.h>
  53 #include <libxml/tree.h>
  54 #include <libintl.h>
  55 #include <assert.h>
  56 #include <iconv.h>
  57 #include <langinfo.h>
  58 #include <dirent.h>
  59 
  60 static char *sa_get_usage(sa_usage_t);
  61 
  62 /*
  63  * Implementation of the common sub-commands supported by sharemgr.
  64  * A number of helper functions are also included.
  65  */
  66 
  67 /*
  68  * has_protocol(group, proto)
  69  *      If the group has an optionset with the specified protocol,
  70  *      return true (1) otherwise false (0).
  71  */
  72 static int
  73 has_protocol(sa_group_t group, char *protocol)
  74 {
  75         sa_optionset_t optionset;
  76         int result = 0;
  77 
  78         optionset = sa_get_optionset(group, protocol);
  79         if (optionset != NULL) {
  80                 result++;
  81         }
  82         return (result);
  83 }
  84 
  85 /*
  86  * validresource(name)
  87  *
  88  * Check that name only has valid characters in it. The current valid
  89  * set are the printable characters but not including:
  90  *      " / \ [ ] : | < > + ; , ? * = \t
  91  * Note that space is included and there is a maximum length.
  92  */
  93 static int
  94 validresource(const char *name)
  95 {
  96         const char *cp;
  97         size_t len;
  98 
  99         if (name == NULL)
 100                 return (B_FALSE);
 101 
 102         len = strlen(name);
 103         if (len == 0 || len > SA_MAX_RESOURCE_NAME)
 104                 return (B_FALSE);
 105 
 106         if (strpbrk(name, "\"/\\[]:|<>+;,?*=\t") != NULL) {
 107                 return (B_FALSE);
 108         }
 109 
 110         for (cp = name; *cp != '\0'; cp++)
 111                 if (iscntrl(*cp))
 112                         return (B_FALSE);
 113 
 114         return (B_TRUE);
 115 }
 116 
 117 /*
 118  * conv_to_utf8(input)
 119  *
 120  * Convert the input string to utf8 from the current locale.  If the
 121  * conversion fails, use the current locale, it is likely close
 122  * enough. For example, the "C" locale is a subset of utf-8. The
 123  * return value may be a new string or the original input string.
 124  */
 125 
 126 static char *
 127 conv_to_utf8(char *input)
 128 {
 129         iconv_t cd;
 130         char *inval = input;
 131         char *output = input;
 132         char *outleft;
 133         char *curlocale;
 134         size_t bytesleft;
 135         size_t size;
 136         size_t osize;
 137         static int warned = 0;
 138 
 139         curlocale = nl_langinfo(CODESET);
 140         if (curlocale == NULL)
 141                 curlocale = "C";
 142         cd = iconv_open("UTF-8", curlocale);
 143         if (cd != NULL && cd != (iconv_t)-1) {
 144                 size = strlen(input);
 145                 /* Assume worst case of characters expanding to 4 bytes. */
 146                 bytesleft = size * 4;
 147                 output = calloc(bytesleft, 1);
 148                 if (output != NULL) {
 149                         outleft = output;
 150                         /* inval can be modified on return */
 151                         osize = iconv(cd, (const char **)&inval, &size,
 152                             &outleft, &bytesleft);
 153                         if (osize == (size_t)-1 || size != 0) {
 154                                 free(output);
 155                                 output = input;
 156                         }
 157                 } else {
 158                         /* Need to return something. */
 159                         output = input;
 160                 }
 161                 (void) iconv_close(cd);
 162         } else {
 163                 if (!warned)
 164                         (void) fprintf(stderr,
 165                             gettext("Cannot convert to UTF-8 from %s\n"),
 166                             curlocale ? curlocale : gettext("unknown"));
 167                 warned = 1;
 168         }
 169         return (output);
 170 }
 171 
 172 /*
 173  * conv_from(input)
 174  *
 175  * Convert the input string from utf8 to current locale.  If the
 176  * conversion isn't supported, just use as is. The return value may be
 177  * a new string or the original input string.
 178  */
 179 
 180 static char *
 181 conv_from_utf8(char *input)
 182 {
 183         iconv_t cd;
 184         char *output = input;
 185         char *inval = input;
 186         char *outleft;
 187         char *curlocale;
 188         size_t bytesleft;
 189         size_t size;
 190         size_t osize;
 191         static int warned = 0;
 192 
 193         curlocale = nl_langinfo(CODESET);
 194         if (curlocale == NULL)
 195                 curlocale = "C";
 196         cd = iconv_open(curlocale, "UTF-8");
 197         if (cd != NULL && cd != (iconv_t)-1) {
 198                 size = strlen(input);
 199                 /* Assume worst case of characters expanding to 4 bytes. */
 200                 bytesleft = size * 4;
 201                 output = calloc(bytesleft, 1);
 202                 if (output != NULL) {
 203                         outleft = output;
 204                         osize = iconv(cd, (const char **)&inval, &size,
 205                             &outleft, &bytesleft);
 206                         if (osize == (size_t)-1 || size != 0)
 207                                 output = input;
 208                 } else {
 209                         /* Need to return something. */
 210                         output = input;
 211                 }
 212                 (void) iconv_close(cd);
 213         } else {
 214                 if (!warned)
 215                         (void) fprintf(stderr,
 216                             gettext("Cannot convert to %s from UTF-8\n"),
 217                             curlocale ? curlocale : gettext("unknown"));
 218                 warned = 1;
 219         }
 220         return (output);
 221 }
 222 
 223 /*
 224  * print_rsrc_desc(resource, sharedesc)
 225  *
 226  * Print the resource description string after converting from UTF8 to
 227  * the current locale. If sharedesc is not NULL and there is no
 228  * description on the resource, use sharedesc. sharedesc will already
 229  * be converted to UTF8.
 230  */
 231 
 232 static void
 233 print_rsrc_desc(sa_resource_t resource, char *sharedesc)
 234 {
 235         char *description;
 236         char *desc;
 237 
 238         if (resource == NULL)
 239                 return;
 240 
 241         description = sa_get_resource_description(resource);
 242         if (description != NULL) {
 243                 desc = conv_from_utf8(description);
 244                 if (desc != description) {
 245                         sa_free_share_description(description);
 246                         description = desc;
 247                 }
 248         } else if (sharedesc != NULL) {
 249                 description = strdup(sharedesc);
 250         }
 251         if (description != NULL) {
 252                 (void) printf("\t\"%s\"", description);
 253                 sa_free_share_description(description);
 254         }
 255 }
 256 
 257 /*
 258  * set_resource_desc(share, description)
 259  *
 260  * Set the share description value after converting the description
 261  * string to UTF8 from the current locale.
 262  */
 263 
 264 static int
 265 set_resource_desc(sa_share_t share, char *description)
 266 {
 267         char *desc;
 268         int ret;
 269 
 270         desc = conv_to_utf8(description);
 271         ret = sa_set_resource_description(share, desc);
 272         if (description != desc)
 273                 sa_free_share_description(desc);
 274         return (ret);
 275 }
 276 
 277 /*
 278  * set_share_desc(share, description)
 279  *
 280  * Set the resource description value after converting the description
 281  * string to UTF8 from the current locale.
 282  */
 283 
 284 static int
 285 set_share_desc(sa_share_t share, char *description)
 286 {
 287         char *desc;
 288         int ret;
 289 
 290         desc = conv_to_utf8(description);
 291         ret = sa_set_share_description(share, desc);
 292         if (description != desc)
 293                 sa_free_share_description(desc);
 294         return (ret);
 295 }
 296 
 297 /*
 298  * add_list(list, item, data, proto)
 299  *      Adds a new list member that points holds item in the list.
 300  *      If list is NULL, it starts a new list.  The function returns
 301  *      the first member of the list.
 302  */
 303 struct list *
 304 add_list(struct list *listp, void *item, void *data, char *proto)
 305 {
 306         struct list *new, *tmp;
 307 
 308         new = malloc(sizeof (struct list));
 309         if (new != NULL) {
 310                 new->next = NULL;
 311                 new->item = item;
 312                 new->itemdata = data;
 313                 new->proto = proto;
 314         } else {
 315                 return (listp);
 316         }
 317 
 318         if (listp == NULL)
 319                 return (new);
 320 
 321         for (tmp = listp; tmp->next != NULL; tmp = tmp->next) {
 322                 /* get to end of list */
 323         }
 324         tmp->next = new;
 325         return (listp);
 326 }
 327 
 328 /*
 329  * free_list(list)
 330  *      Given a list, free all the members of the list;
 331  */
 332 static void
 333 free_list(struct list *listp)
 334 {
 335         struct list *tmp;
 336         while (listp != NULL) {
 337                 tmp = listp;
 338                 listp = listp->next;
 339                 free(tmp);
 340         }
 341 }
 342 
 343 /*
 344  * check_authorization(instname, which)
 345  *
 346  * Checks to see if the specific type of authorization in which is
 347  * enabled for the user in this SMF service instance.
 348  */
 349 
 350 static int
 351 check_authorization(char *instname, int which)
 352 {
 353         scf_handle_t *handle = NULL;
 354         scf_simple_prop_t *prop = NULL;
 355         char svcstring[SA_MAX_NAME_LEN + sizeof (SA_SVC_FMRI_BASE) + 1];
 356         char *authstr = NULL;
 357         ssize_t numauths;
 358         int ret = B_TRUE;
 359         uid_t uid;
 360         struct passwd *pw = NULL;
 361 
 362         uid = getuid();
 363         pw = getpwuid(uid);
 364         if (pw == NULL) {
 365                 ret = B_FALSE;
 366         } else {
 367                 /*
 368                  * Since names are restricted to SA_MAX_NAME_LEN won't
 369                  * overflow.
 370                  */
 371                 (void) snprintf(svcstring, sizeof (svcstring), "%s:%s",
 372                     SA_SVC_FMRI_BASE, instname);
 373                 handle = scf_handle_create(SCF_VERSION);
 374                 if (handle != NULL) {
 375                         if (scf_handle_bind(handle) == 0) {
 376                                 switch (which) {
 377                                 case SVC_SET:
 378                                         prop = scf_simple_prop_get(handle,
 379                                             svcstring, "general",
 380                                             SVC_AUTH_VALUE);
 381                                         break;
 382                                 case SVC_ACTION:
 383                                         prop = scf_simple_prop_get(handle,
 384                                             svcstring, "general",
 385                                             SVC_AUTH_ACTION);
 386                                         break;
 387                                 }
 388                         }
 389                 }
 390         }
 391         /* make sure we have an authorization string property */
 392         if (prop != NULL) {
 393                 int i;
 394                 numauths = scf_simple_prop_numvalues(prop);
 395                 for (ret = 0, i = 0; i < numauths; i++) {
 396                         authstr = scf_simple_prop_next_astring(prop);
 397                         if (authstr != NULL) {
 398                                 /* check if this user has one of the strings */
 399                                 if (chkauthattr(authstr, pw->pw_name)) {
 400                                         ret = 1;
 401                                         break;
 402                                 }
 403                         }
 404                 }
 405                 endauthattr();
 406                 scf_simple_prop_free(prop);
 407         } else {
 408                 /* no authorization string defined */
 409                 ret = 0;
 410         }
 411         if (handle != NULL)
 412                 scf_handle_destroy(handle);
 413         return (ret);
 414 }
 415 
 416 /*
 417  * check_authorizations(instname, flags)
 418  *
 419  * check all the needed authorizations for the user in this service
 420  * instance. Return value of 1(true) or 0(false) indicates whether
 421  * there are authorizations for the user or not.
 422  */
 423 
 424 static int
 425 check_authorizations(char *instname, int flags)
 426 {
 427         int ret1 = 0;
 428         int ret2 = 0;
 429         int ret;
 430 
 431         if (flags & SVC_SET)
 432                 ret1 = check_authorization(instname, SVC_SET);
 433         if (flags & SVC_ACTION)
 434                 ret2 = check_authorization(instname, SVC_ACTION);
 435         switch (flags) {
 436         case SVC_ACTION:
 437                 ret = ret2;
 438                 break;
 439         case SVC_SET:
 440                 ret = ret1;
 441                 break;
 442         case SVC_ACTION|SVC_SET:
 443                 ret = ret1 & ret2;
 444                 break;
 445         default:
 446                 /* if not flags set, we assume we don't need authorizations */
 447                 ret = 1;
 448         }
 449         return (ret);
 450 }
 451 
 452 /*
 453  * notify_or_enable_share(share, protocol)
 454  *
 455  * Since some protocols don't want an "enable" when properties change,
 456  * this function will use the protocol specific notify function
 457  * first. If that fails, it will then attempt to use the
 458  * sa_enable_share().  "protocol" is the protocol that was specified
 459  * on the command line.
 460  */
 461 static void
 462 notify_or_enable_share(sa_share_t share, char *protocol)
 463 {
 464         sa_group_t group;
 465         sa_optionset_t opt;
 466         int ret = SA_OK;
 467         char *path;
 468         char *groupproto;
 469         sa_share_t parent = share;
 470 
 471         /* If really a resource, get parent share */
 472         if (!sa_is_share(share)) {
 473                 parent = sa_get_resource_parent((sa_resource_t)share);
 474         }
 475 
 476         /*
 477          * Now that we've got a share in "parent", make sure it has a path.
 478          */
 479         path = sa_get_share_attr(parent, "path");
 480         if (path == NULL)
 481                 return;
 482 
 483         group = sa_get_parent_group(parent);
 484 
 485         if (group == NULL) {
 486                 sa_free_attr_string(path);
 487                 return;
 488         }
 489         for (opt = sa_get_optionset(group, NULL);
 490             opt != NULL;
 491             opt = sa_get_next_optionset(opt)) {
 492                 groupproto = sa_get_optionset_attr(opt, "type");
 493                 if (groupproto == NULL ||
 494                     (protocol != NULL && strcmp(groupproto, protocol) != 0)) {
 495                         if (groupproto != NULL)
 496                                 sa_free_attr_string(groupproto);
 497                         continue;
 498                 }
 499                 if (sa_is_share(share)) {
 500                         if ((ret = sa_proto_change_notify(share,
 501                             groupproto)) != SA_OK) {
 502                                 ret = sa_enable_share(share, groupproto);
 503                                 if (ret != SA_OK) {
 504                                         (void) printf(
 505                                             gettext("Could not reenable"
 506                                             " share %s: %s\n"),
 507                                             path, sa_errorstr(ret));
 508                                 }
 509                         }
 510                 } else {
 511                         /* Must be a resource */
 512                         if ((ret = sa_proto_notify_resource(share,
 513                             groupproto)) != SA_OK) {
 514                                 ret = sa_enable_resource(share, groupproto);
 515                                 if (ret != SA_OK) {
 516                                         (void) printf(
 517                                             gettext("Could not "
 518                                             "reenable resource %s: "
 519                                             "%s\n"), path,
 520                                             sa_errorstr(ret));
 521                                 }
 522                         }
 523                 }
 524                 sa_free_attr_string(groupproto);
 525         }
 526         sa_free_attr_string(path);
 527 }
 528 
 529 /*
 530  * enable_group(group, updateproto, notify, proto)
 531  *
 532  * enable all the shares in the specified group. This is a helper for
 533  * enable_all_groups in order to simplify regular and subgroup (zfs)
 534  * enabling. Group has already been checked for non-NULL. If notify
 535  * is non-zero, attempt to use the notify interface rather than
 536  * enable.
 537  */
 538 static void
 539 enable_group(sa_group_t group, char *updateproto, int notify, char *proto)
 540 {
 541         sa_share_t share;
 542 
 543         /* If the protocol isn't enabled for this group skip it */
 544         if (!has_protocol(group, proto))
 545                 return;
 546 
 547         for (share = sa_get_share(group, NULL);
 548             share != NULL;
 549             share = sa_get_next_share(share)) {
 550                 if (updateproto != NULL)
 551                         (void) sa_update_legacy(share, updateproto);
 552                 if (notify)
 553                         notify_or_enable_share(share, proto);
 554                 else
 555                         (void) sa_enable_share(share, proto);
 556         }
 557 }
 558 
 559 /*
 560  * isenabled(group)
 561  *
 562  * Returns B_TRUE if the group is enabled or B_FALSE if it isn't.
 563  * Moved to separate function to reduce clutter in the code.
 564  */
 565 
 566 static int
 567 isenabled(sa_group_t group)
 568 {
 569         char *state;
 570         int ret = B_FALSE;
 571 
 572         if (group != NULL) {
 573                 state = sa_get_group_attr(group, "state");
 574                 if (state != NULL) {
 575 
 576                         if (strcmp(state, "enabled") == 0)
 577                                 ret = B_TRUE;
 578                         sa_free_attr_string(state);
 579                 }
 580         }
 581         return (ret);
 582 }
 583 
 584 /*
 585  * enable_all_groups(list, setstate, online, updateproto)
 586  *
 587  * Given a list of groups, enable each one found.  If updateproto is
 588  * not NULL, then update all the shares for the protocol that was
 589  * passed in. If enable is non-zero, tell enable_group to try the
 590  * notify interface since this is a property change.
 591  */
 592 static int
 593 enable_all_groups(sa_handle_t handle, struct list *work, int setstate,
 594     int online, char *updateproto, int enable)
 595 {
 596         int ret;
 597         char instance[SA_MAX_NAME_LEN + sizeof (SA_SVC_FMRI_BASE) + 1];
 598         char *state;
 599         char *name;
 600         char *zfs = NULL;
 601         sa_group_t group;
 602         sa_group_t subgroup;
 603 
 604         for (ret = SA_OK; work != NULL; work = work->next) {
 605                 group = (sa_group_t)work->item;
 606 
 607                 /*
 608                  * If setstate == TRUE, then make sure to set
 609                  * enabled. This needs to be done here in order for
 610                  * the isenabled check to succeed on a newly enabled
 611                  * group.
 612                  */
 613                 if (setstate == B_TRUE) {
 614                         ret = sa_set_group_attr(group, "state", "enabled");
 615                         if (ret != SA_OK)
 616                                 break;
 617                 }
 618 
 619                 /*
 620                  * Check to see if group is enabled. If it isn't, skip
 621                  * the rest.  We don't want shares starting if the
 622                  * group is disabled. The properties may have been
 623                  * updated, but there won't be a change until the
 624                  * group is enabled.
 625                  */
 626                 if (!isenabled(group))
 627                         continue;
 628 
 629                 /* if itemdata != NULL then a single share */
 630                 if (work->itemdata != NULL) {
 631                         if (enable) {
 632                                 if (work->itemdata != NULL)
 633                                         notify_or_enable_share(work->itemdata,
 634                                             updateproto);
 635                                 else
 636                                         ret = SA_CONFIG_ERR;
 637                         } else {
 638                                 if (sa_is_share(work->itemdata)) {
 639                                         ret = sa_enable_share(
 640                                             (sa_share_t)work->itemdata,
 641                                             updateproto);
 642                                 } else {
 643                                         ret = sa_enable_resource(
 644                                             (sa_resource_t)work->itemdata,
 645                                             updateproto);
 646                                 }
 647                         }
 648                 }
 649                 if (ret != SA_OK)
 650                         break;
 651 
 652                 /* if itemdata == NULL then the whole group */
 653                 if (work->itemdata == NULL) {
 654                         zfs = sa_get_group_attr(group, "zfs");
 655                         /*
 656                          * If the share is managed by ZFS, don't
 657                          * update any of the protocols since ZFS is
 658                          * handling this.  Updateproto will contain
 659                          * the name of the protocol that we want to
 660                          * update legacy files for.
 661                          */
 662                         enable_group(group, zfs == NULL ? updateproto : NULL,
 663                             enable, work->proto);
 664                         if (zfs != NULL)
 665                                 sa_free_attr_string(zfs);
 666 
 667                         for (subgroup = sa_get_sub_group(group);
 668                             subgroup != NULL;
 669                             subgroup = sa_get_next_group(subgroup)) {
 670                                 /* never update legacy for ZFS subgroups */
 671                                 enable_group(subgroup, NULL, enable,
 672                                     work->proto);
 673                         }
 674                 }
 675                 if (online) {
 676                         zfs = sa_get_group_attr(group, "zfs");
 677                         name = sa_get_group_attr(group, "name");
 678                         if (name != NULL) {
 679                                 if (zfs == NULL) {
 680                                         (void) snprintf(instance,
 681                                             sizeof (instance), "%s:%s",
 682                                             SA_SVC_FMRI_BASE, name);
 683                                         state = smf_get_state(instance);
 684                                         if (state == NULL ||
 685                                             strcmp(state, "online") != 0) {
 686                                                 (void) smf_enable_instance(
 687                                                     instance, 0);
 688                                                 free(state);
 689                                         }
 690                                 } else {
 691                                         sa_free_attr_string(zfs);
 692                                         zfs = NULL;
 693                                 }
 694                                 if (name != NULL)
 695                                         sa_free_attr_string(name);
 696                         }
 697                 }
 698         }
 699         if (ret == SA_OK) {
 700                 ret = sa_update_config(handle);
 701         }
 702         return (ret);
 703 }
 704 
 705 /*
 706  * chk_opt(optlistp, security, proto)
 707  *
 708  * Do a sanity check on the optlist provided for the protocol.  This
 709  * is a syntax check and verification that the property is either a
 710  * general or specific to a names optionset.
 711  */
 712 
 713 static int
 714 chk_opt(struct options *optlistp, int security, char *proto)
 715 {
 716         struct options *optlist;
 717         char *sep = "";
 718         int notfirst = 0;
 719         int ret;
 720 
 721         for (optlist = optlistp; optlist != NULL; optlist = optlist->next) {
 722                 char *optname;
 723 
 724                 optname = optlist->optname;
 725                 ret = OPT_ADD_OK;
 726                 /* extract property/value pair */
 727                 if (sa_is_security(optname, proto)) {
 728                         if (!security)
 729                                 ret = OPT_ADD_SECURITY;
 730                 } else {
 731                         if (security)
 732                                 ret = OPT_ADD_PROPERTY;
 733                 }
 734                 if (ret != OPT_ADD_OK) {
 735                         if (notfirst == 0)
 736                                 (void) printf(
 737                                     gettext("Property syntax error: "));
 738                         switch (ret) {
 739                         case OPT_ADD_SYNTAX:
 740                                 (void) printf(gettext("%ssyntax error: %s"),
 741                                     sep, optname);
 742                                 sep = ", ";
 743                                 break;
 744                         case OPT_ADD_SECURITY:
 745                                 (void) printf(gettext("%s%s requires -S"),
 746                                     optname, sep);
 747                                 sep = ", ";
 748                                 break;
 749                         case OPT_ADD_PROPERTY:
 750                                 (void) printf(
 751                                     gettext("%s%s not supported with -S"),
 752                                     optname, sep);
 753                                 sep = ", ";
 754                                 break;
 755                         }
 756                         notfirst++;
 757                 }
 758         }
 759         if (notfirst) {
 760                 (void) printf("\n");
 761                 ret = SA_SYNTAX_ERR;
 762         }
 763         return (ret);
 764 }
 765 
 766 /*
 767  * free_opt(optlist)
 768  *      Free the specified option list.
 769  */
 770 static void
 771 free_opt(struct options *optlist)
 772 {
 773         struct options *nextopt;
 774         while (optlist != NULL) {
 775                 nextopt = optlist->next;
 776                 free(optlist);
 777                 optlist = nextopt;
 778         }
 779 }
 780 
 781 /*
 782  * check property list for valid properties
 783  * A null value is a remove which is always valid.
 784  */
 785 static int
 786 valid_options(sa_handle_t handle, struct options *optlist, char *proto,
 787     void *object, char *sec)
 788 {
 789         int ret = SA_OK;
 790         struct options *cur;
 791         sa_property_t prop;
 792         sa_optionset_t parent = NULL;
 793 
 794         if (object != NULL) {
 795                 if (sec == NULL)
 796                         parent = sa_get_optionset(object, proto);
 797                 else
 798                         parent = sa_get_security(object, sec, proto);
 799         }
 800 
 801         for (cur = optlist; cur != NULL; cur = cur->next) {
 802                 if (cur->optvalue == NULL)
 803                         continue;
 804                 prop = sa_create_property(cur->optname, cur->optvalue);
 805                 if (prop == NULL)
 806                         ret = SA_NO_MEMORY;
 807                 if (ret != SA_OK ||
 808                     (ret = sa_valid_property(handle, parent, proto, prop)) !=
 809                     SA_OK) {
 810                         (void) printf(
 811                             gettext("Could not add property %s: %s\n"),
 812                             cur->optname, sa_errorstr(ret));
 813                 }
 814                 (void) sa_remove_property(prop);
 815         }
 816         return (ret);
 817 }
 818 
 819 /*
 820  * add_optionset(group, optlist, protocol, *err)
 821  *      Add the options in optlist to an optionset and then add the optionset
 822  *      to the group.
 823  *
 824  *      The return value indicates if there was a "change" while errors are
 825  *      returned via the *err parameters.
 826  */
 827 static int
 828 add_optionset(sa_group_t group, struct options *optlist, char *proto, int *err)
 829 {
 830         sa_optionset_t optionset;
 831         int ret = SA_OK;
 832         int result = B_FALSE;
 833         sa_handle_t handle;
 834 
 835         optionset = sa_get_optionset(group, proto);
 836         if (optionset == NULL) {
 837                 optionset = sa_create_optionset(group, proto);
 838                 if (optionset == NULL)
 839                         ret = SA_NO_MEMORY;
 840                 result = B_TRUE; /* adding a protocol is a change */
 841         }
 842         if (optionset == NULL) {
 843                 ret = SA_NO_MEMORY;
 844                 goto out;
 845         }
 846         handle = sa_find_group_handle(group);
 847         if (handle == NULL) {
 848                 ret = SA_CONFIG_ERR;
 849                 goto out;
 850         }
 851         while (optlist != NULL) {
 852                 sa_property_t prop;
 853                 prop = sa_get_property(optionset, optlist->optname);
 854                 if (prop == NULL) {
 855                         /*
 856                          * add the property, but only if it is
 857                          * a non-NULL or non-zero length value
 858                          */
 859                         if (optlist->optvalue != NULL) {
 860                                 prop = sa_create_property(optlist->optname,
 861                                     optlist->optvalue);
 862                                 if (prop != NULL) {
 863                                         ret = sa_valid_property(handle,
 864                                             optionset, proto, prop);
 865                                         if (ret != SA_OK) {
 866                                                 (void) sa_remove_property(prop);
 867                                                 (void) printf(gettext("Could "
 868                                                     "not add property "
 869                                                     "%s: %s\n"),
 870                                                     optlist->optname,
 871                                                     sa_errorstr(ret));
 872                                         }
 873                                 }
 874                                 if (ret == SA_OK) {
 875                                         ret = sa_add_property(optionset, prop);
 876                                         if (ret != SA_OK) {
 877                                                 (void) printf(gettext(
 878                                                     "Could not add property "
 879                                                     "%s: %s\n"),
 880                                                     optlist->optname,
 881                                                     sa_errorstr(ret));
 882                                         } else {
 883                                                 /* there was a change */
 884                                                 result = B_TRUE;
 885                                         }
 886                                 }
 887                         }
 888                 } else {
 889                         ret = sa_update_property(prop, optlist->optvalue);
 890                         /* should check to see if value changed */
 891                         if (ret != SA_OK) {
 892                                 (void) printf(gettext("Could not update "
 893                                     "property %s: %s\n"), optlist->optname,
 894                                     sa_errorstr(ret));
 895                         } else {
 896                                 result = B_TRUE;
 897                         }
 898                 }
 899                 optlist = optlist->next;
 900         }
 901         ret = sa_commit_properties(optionset, 0);
 902 
 903 out:
 904         if (err != NULL)
 905                 *err = ret;
 906         return (result);
 907 }
 908 
 909 /*
 910  * resource_compliant(group)
 911  *
 912  * Go through all the shares in the group. Assume compliant, but if
 913  * any share doesn't have at least one resource name, it isn't
 914  * compliant.
 915  */
 916 static int
 917 resource_compliant(sa_group_t group)
 918 {
 919         sa_share_t share;
 920 
 921         for (share = sa_get_share(group, NULL); share != NULL;
 922             share = sa_get_next_share(share)) {
 923                 if (sa_get_share_resource(share, NULL) == NULL) {
 924                         return (B_FALSE);
 925                 }
 926         }
 927         return (B_TRUE);
 928 }
 929 
 930 /*
 931  * fix_path(path)
 932  *
 933  * change all illegal characters to something else.  For now, all get
 934  * converted to '_' and the leading '/' is stripped off. This is used
 935  * to construct an resource name (SMB share name) that is valid.
 936  * Caller must pass a valid path.
 937  */
 938 static void
 939 fix_path(char *path)
 940 {
 941         char *cp;
 942         size_t len;
 943 
 944         assert(path != NULL);
 945 
 946         /* make sure we are appropriate length */
 947         cp = path + 1; /* skip leading slash */
 948         while (cp != NULL && strlen(cp) > SA_MAX_RESOURCE_NAME) {
 949                 cp = strchr(cp, '/');
 950                 if (cp != NULL)
 951                         cp++;
 952         }
 953         /* two cases - cp == NULL and cp is substring of path */
 954         if (cp == NULL) {
 955                 /* just take last SA_MAX_RESOURCE_NAME chars */
 956                 len = 1 + strlen(path) - SA_MAX_RESOURCE_NAME;
 957                 (void) memmove(path, path + len, SA_MAX_RESOURCE_NAME);
 958                 path[SA_MAX_RESOURCE_NAME] = '\0';
 959         } else {
 960                 len = strlen(cp) + 1;
 961                 (void) memmove(path, cp, len);
 962         }
 963 
 964         /*
 965          * Don't want any of the characters that are not allowed
 966          * in and SMB share name. Replace them with '_'.
 967          */
 968         while (*path) {
 969                 switch (*path) {
 970                 case '/':
 971                 case '"':
 972                 case '\\':
 973                 case '[':
 974                 case ']':
 975                 case ':':
 976                 case '|':
 977                 case '<':
 978                 case '>':
 979                 case '+':
 980                 case ';':
 981                 case ',':
 982                 case '?':
 983                 case '*':
 984                 case '=':
 985                 case '\t':
 986                         *path = '_';
 987                         break;
 988                 }
 989                 path++;
 990         }
 991 }
 992 
 993 /*
 994  * name_adjust(path, count)
 995  *
 996  * Add a ~<count> in place of last few characters. The total number of
 997  * characters is dependent on count.
 998  */
 999 #define MAX_MANGLE_NUMBER       10000
1000 
1001 static int
1002 name_adjust(char *path, int count)
1003 {
1004         size_t len;
1005 
1006         len = strlen(path) - 2;
1007         if (count > 10)
1008                 len--;
1009         if (count > 100)
1010                 len--;
1011         if (count > 1000)
1012                 len--;
1013         if (len > 0)
1014                 (void) sprintf(path + len, "~%d", count);
1015         else
1016                 return (SA_BAD_VALUE);
1017 
1018         return (SA_OK);
1019 }
1020 
1021 /*
1022  * make_resources(group)
1023  *
1024  * Go through all the shares in the group and make them have resource
1025  * names.
1026  */
1027 static void
1028 make_resources(sa_group_t group)
1029 {
1030         sa_share_t share;
1031         int count;
1032         int err = SA_OK;
1033 
1034         for (share = sa_get_share(group, NULL); share != NULL;
1035             share = sa_get_next_share(share)) {
1036                 /* Skip those with resources */
1037                 if (sa_get_share_resource(share, NULL) == NULL) {
1038                         char *path;
1039                         path = sa_get_share_attr(share, "path");
1040                         if (path == NULL)
1041                                 continue;
1042                         fix_path(path);
1043                         count = 0;      /* reset for next resource */
1044                         while (sa_add_resource(share, path,
1045                             SA_SHARE_PERMANENT, &err) == NULL &&
1046                             err == SA_DUPLICATE_NAME) {
1047                                 int ret;
1048                                 ret = name_adjust(path, count);
1049                                 count++;
1050                                 if (ret != SA_OK ||
1051                                     count >= MAX_MANGLE_NUMBER) {
1052                                         (void) printf(gettext(
1053                                             "Cannot create resource name for"
1054                                             " path: %s\n"), path);
1055                                         break;
1056                                 }
1057                         }
1058                         sa_free_attr_string(path);
1059                 }
1060         }
1061 }
1062 
1063 /*
1064  * check_valid_group(group, protocol)
1065  *
1066  * Check to see that the group should have the protocol added (if
1067  * there is one specified).
1068  */
1069 
1070 static int
1071 check_valid_group(sa_group_t group, char *groupname, char *protocol)
1072 {
1073 
1074         if (protocol != NULL) {
1075                 if (has_protocol(group, protocol)) {
1076                         (void) printf(gettext(
1077                             "Group \"%s\" already exists"
1078                             " with protocol %s\n"), groupname,
1079                             protocol);
1080                         return (SA_DUPLICATE_NAME);
1081                 } else if (strcmp(groupname, "default") == 0 &&
1082                     strcmp(protocol, "nfs") != 0) {
1083                         (void) printf(gettext(
1084                             "Group \"%s\" only allows protocol "
1085                             "\"%s\"\n"), groupname, "nfs");
1086                         return (SA_INVALID_PROTOCOL);
1087                 }
1088         } else {
1089                 /* must add new protocol */
1090                 (void) printf(gettext(
1091                     "Group already exists and no protocol "
1092                     "specified.\n"));
1093                 return (SA_DUPLICATE_NAME);
1094         }
1095         return (SA_OK);
1096 }
1097 
1098 /*
1099  * enforce_featureset(group, protocol, dryrun, force)
1100  *
1101  * Check the protocol featureset against the group and enforce any
1102  * rules that might be imposed.
1103  */
1104 
1105 static int
1106 enforce_featureset(sa_group_t group, char *protocol, boolean_t dryrun,
1107     boolean_t force)
1108 {
1109         uint64_t features;
1110 
1111         if (protocol == NULL)
1112                 return (SA_OK);
1113 
1114         /*
1115          * First check to see if specified protocol is one we want to
1116          * allow on a group. Only server protocols are allowed here.
1117          */
1118         features = sa_proto_get_featureset(protocol);
1119         if (!(features & SA_FEATURE_SERVER)) {
1120                 (void) printf(
1121                     gettext("Protocol \"%s\" not supported.\n"), protocol);
1122                 return (SA_INVALID_PROTOCOL);
1123         }
1124 
1125         /*
1126          * Check to see if the new protocol is one that requires
1127          * resource names and make sure we are compliant before
1128          * proceeding.
1129          */
1130         if ((features & SA_FEATURE_RESOURCE) &&
1131             !resource_compliant(group)) {
1132                 if (force && !dryrun) {
1133                         make_resources(group);
1134                 } else {
1135                         (void) printf(
1136                             gettext("Protocol requires resource names to be "
1137                             "set: %s\n"), protocol);
1138                         return (SA_RESOURCE_REQUIRED);
1139                 }
1140         }
1141         return (SA_OK);
1142 }
1143 
1144 /*
1145  * set_all_protocols(group)
1146  *
1147  * Get the list of all protocols and add all server protocols to the
1148  * group.
1149  */
1150 
1151 static int
1152 set_all_protocols(sa_group_t group)
1153 {
1154         char **protolist;
1155         int numprotos, i;
1156         uint64_t features;
1157         sa_optionset_t optionset;
1158         int ret = SA_OK;
1159 
1160         /*
1161          * Now make sure we really want to put this protocol on a
1162          * group. Only server protocols can go here.
1163          */
1164         numprotos = sa_get_protocols(&protolist);
1165         for (i = 0; i < numprotos; i++) {
1166                 features = sa_proto_get_featureset(protolist[i]);
1167                 if (features & SA_FEATURE_SERVER) {
1168                         optionset = sa_create_optionset(group, protolist[i]);
1169                         if (optionset == NULL) {
1170                                 ret = SA_NO_MEMORY;
1171                                 break;
1172                         }
1173                 }
1174         }
1175 
1176         if (protolist != NULL)
1177                 free(protolist);
1178 
1179         return (ret);
1180 }
1181 
1182 /*
1183  * sa_create(flags, argc, argv)
1184  *      create a new group
1185  *      this may or may not have a protocol associated with it.
1186  *      No protocol means "all" protocols in this case.
1187  */
1188 static int
1189 sa_create(sa_handle_t handle, int flags, int argc, char *argv[])
1190 {
1191         char *groupname;
1192 
1193         sa_group_t group;
1194         boolean_t force = B_FALSE;
1195         boolean_t verbose = B_FALSE;
1196         boolean_t dryrun = B_FALSE;
1197         int c;
1198         char *protocol = NULL;
1199         int ret = SA_OK;
1200         struct options *optlist = NULL;
1201         int err = SA_OK;
1202         int auth;
1203         boolean_t created = B_FALSE;
1204 
1205         while ((c = getopt(argc, argv, "?fhvnP:p:")) != EOF) {
1206                 switch (c) {
1207                 case 'f':
1208                         force = B_TRUE;
1209                         break;
1210                 case 'v':
1211                         verbose = B_TRUE;
1212                         break;
1213                 case 'n':
1214                         dryrun = B_TRUE;
1215                         break;
1216                 case 'P':
1217                         if (protocol != NULL) {
1218                                 (void) printf(gettext("Specifying "
1219                                     "multiple protocols "
1220                                     "not supported: %s\n"), protocol);
1221                                 return (SA_SYNTAX_ERR);
1222                         }
1223                         protocol = optarg;
1224                         if (sa_valid_protocol(protocol))
1225                                 break;
1226                         (void) printf(gettext(
1227                             "Invalid protocol specified: %s\n"), protocol);
1228                         return (SA_INVALID_PROTOCOL);
1229                 case 'p':
1230                         ret = add_opt(&optlist, optarg, 0);
1231                         switch (ret) {
1232                         case OPT_ADD_SYNTAX:
1233                                 (void) printf(gettext(
1234                                     "Property syntax error for property: %s\n"),
1235                                     optarg);
1236                                 return (SA_SYNTAX_ERR);
1237                         case OPT_ADD_SECURITY:
1238                                 (void) printf(gettext(
1239                                     "Security properties need "
1240                                     "to be set with set-security: %s\n"),
1241                                     optarg);
1242                                 return (SA_SYNTAX_ERR);
1243                         default:
1244                                 break;
1245                         }
1246                         break;
1247                 case 'h':
1248                         /* optopt on valid arg isn't defined */
1249                         optopt = c;
1250                         /*FALLTHROUGH*/
1251                 case '?':
1252                 default:
1253                         /*
1254                          * Since a bad option gets to here, sort it
1255                          * out and return a syntax error return value
1256                          * if necessary.
1257                          */
1258                         switch (optopt) {
1259                         default:
1260                                 err = SA_SYNTAX_ERR;
1261                                 break;
1262                         case 'h':
1263                         case '?':
1264                                 break;
1265                         }
1266                         (void) printf(gettext("usage: %s\n"),
1267                             sa_get_usage(USAGE_CREATE));
1268                         return (err);
1269                 }
1270         }
1271 
1272         if (optind >= argc) {
1273                 (void) printf(gettext("usage: %s\n"),
1274                     sa_get_usage(USAGE_CREATE));
1275                 (void) printf(gettext("\tgroup must be specified.\n"));
1276                 return (SA_BAD_PATH);
1277         }
1278 
1279         if ((optind + 1) < argc) {
1280                 (void) printf(gettext("usage: %s\n"),
1281                     sa_get_usage(USAGE_CREATE));
1282                 (void) printf(gettext("\textraneous group(s) at end\n"));
1283                 return (SA_SYNTAX_ERR);
1284         }
1285 
1286         if (protocol == NULL && optlist != NULL) {
1287                 /* lookup default protocol */
1288                 (void) printf(gettext("usage: %s\n"),
1289                     sa_get_usage(USAGE_CREATE));
1290                 (void) printf(gettext("\tprotocol must be specified "
1291                     "with properties\n"));
1292                 return (SA_INVALID_PROTOCOL);
1293         }
1294 
1295         if (optlist != NULL)
1296                 ret = chk_opt(optlist, 0, protocol);
1297         if (ret == OPT_ADD_SECURITY) {
1298                 (void) printf(gettext("Security properties not "
1299                     "supported with create\n"));
1300                 return (SA_SYNTAX_ERR);
1301         }
1302 
1303         /*
1304          * If a group already exists, we can only add a new protocol
1305          * to it and not create a new one or add the same protocol
1306          * again.
1307          */
1308 
1309         groupname = argv[optind];
1310 
1311         auth = check_authorizations(groupname, flags);
1312 
1313         group = sa_get_group(handle, groupname);
1314         if (group != NULL) {
1315                 /* group exists so must be a protocol add */
1316                 ret = check_valid_group(group, groupname, protocol);
1317         } else {
1318                 /*
1319                  * is it a valid name? Must comply with SMF instance
1320                  * name restrictions.
1321                  */
1322                 if (!sa_valid_group_name(groupname)) {
1323                         ret = SA_INVALID_NAME;
1324                         (void) printf(gettext("Invalid group name: %s\n"),
1325                             groupname);
1326                 }
1327         }
1328         if (ret == SA_OK) {
1329                 /* check protocol vs optlist */
1330                 if (optlist != NULL) {
1331                         /* check options, if any, for validity */
1332                         ret = valid_options(handle, optlist, protocol,
1333                             group, NULL);
1334                 }
1335         }
1336         if (ret == SA_OK && !dryrun) {
1337                 if (group == NULL) {
1338                         group = sa_create_group(handle, (char *)groupname,
1339                             &err);
1340                         created = B_TRUE;
1341                 }
1342                 if (group != NULL) {
1343                         sa_optionset_t optionset;
1344 
1345                         /*
1346                          * Check group and protocol against featureset
1347                          * requirements.
1348                          */
1349                         ret = enforce_featureset(group, protocol,
1350                             dryrun, force);
1351                         if (ret != SA_OK)
1352                                 goto err;
1353 
1354                         /*
1355                          * So far so good. Now add the required
1356                          * optionset(s) to the group.
1357                          */
1358                         if (optlist != NULL) {
1359                                 (void) add_optionset(group, optlist, protocol,
1360                                     &ret);
1361                         } else if (protocol != NULL) {
1362                                 optionset = sa_create_optionset(group,
1363                                     protocol);
1364                                 if (optionset == NULL)
1365                                         ret = SA_NO_MEMORY;
1366                         } else if (protocol == NULL) {
1367                                 /* default group create so add all protocols */
1368                                 ret = set_all_protocols(group);
1369                         }
1370                         /*
1371                          * We have a group and legal additions
1372                          */
1373                         if (ret == SA_OK) {
1374                                 /*
1375                                  * Commit to configuration for protocols that
1376                                  * need to do block updates. For NFS, this
1377                                  * doesn't do anything but it will be run for
1378                                  * all protocols that implement the
1379                                  * appropriate plugin.
1380                                  */
1381                                 ret = sa_update_config(handle);
1382                         } else {
1383                                 if (group != NULL)
1384                                         (void) sa_remove_group(group);
1385                         }
1386                 } else {
1387                         ret = err;
1388                         (void) printf(gettext("Could not create group: %s\n"),
1389                             sa_errorstr(ret));
1390                 }
1391         }
1392         if (dryrun && ret == SA_OK && !auth && verbose) {
1393                 (void) printf(gettext("Command would fail: %s\n"),
1394                     sa_errorstr(SA_NO_PERMISSION));
1395                 ret = SA_NO_PERMISSION;
1396         }
1397 err:
1398         if (ret != SA_OK && created)
1399                 ret = sa_remove_group(group);
1400 
1401         free_opt(optlist);
1402         return (ret);
1403 }
1404 
1405 /*
1406  * group_status(group)
1407  *
1408  * return the current status (enabled/disabled) of the group.
1409  */
1410 
1411 static char *
1412 group_status(sa_group_t group)
1413 {
1414         char *state;
1415         int enabled = 0;
1416 
1417         state = sa_get_group_attr(group, "state");
1418         if (state != NULL) {
1419                 if (strcmp(state, "enabled") == 0) {
1420                         enabled = 1;
1421                 }
1422                 sa_free_attr_string(state);
1423         }
1424         return (enabled ? "enabled" : "disabled");
1425 }
1426 
1427 /*
1428  * sa_delete(flags, argc, argv)
1429  *
1430  *      Delete a group.
1431  */
1432 
1433 static int
1434 sa_delete(sa_handle_t handle, int flags, int argc, char *argv[])
1435 {
1436         char *groupname;
1437         sa_group_t group;
1438         sa_share_t share;
1439         int verbose = 0;
1440         int dryrun = 0;
1441         int force = 0;
1442         int c;
1443         char *protocol = NULL;
1444         char *sectype = NULL;
1445         int ret = SA_OK;
1446         int auth;
1447 
1448         while ((c = getopt(argc, argv, "?hvnP:fS:")) != EOF) {
1449                 switch (c) {
1450                 case 'v':
1451                         verbose++;
1452                         break;
1453                 case 'n':
1454                         dryrun++;
1455                         break;
1456                 case 'P':
1457                         if (protocol != NULL) {
1458                                 (void) printf(gettext("Specifying "
1459                                     "multiple protocols "
1460                                     "not supported: %s\n"), protocol);
1461                                 return (SA_SYNTAX_ERR);
1462                         }
1463                         protocol = optarg;
1464                         if (!sa_valid_protocol(protocol)) {
1465                                 (void) printf(gettext("Invalid protocol "
1466                                     "specified: %s\n"), protocol);
1467                                 return (SA_INVALID_PROTOCOL);
1468                         }
1469                         break;
1470                 case 'S':
1471                         if (sectype != NULL) {
1472                                 (void) printf(gettext("Specifying "
1473                                     "multiple property "
1474                                     "spaces not supported: %s\n"), sectype);
1475                                 return (SA_SYNTAX_ERR);
1476                         }
1477                         sectype = optarg;
1478                         break;
1479                 case 'f':
1480                         force++;
1481                         break;
1482                 case 'h':
1483                         /* optopt on valid arg isn't defined */
1484                         optopt = c;
1485                         /*FALLTHROUGH*/
1486                 case '?':
1487                 default:
1488                         /*
1489                          * Since a bad option gets to here, sort it
1490                          * out and return a syntax error return value
1491                          * if necessary.
1492                          */
1493                         switch (optopt) {
1494                         default:
1495                                 ret = SA_SYNTAX_ERR;
1496                                 break;
1497                         case 'h':
1498                         case '?':
1499                                 break;
1500                         }
1501                         (void) printf(gettext("usage: %s\n"),
1502                             sa_get_usage(USAGE_DELETE));
1503                         return (ret);
1504                 }
1505         }
1506 
1507         if (optind >= argc) {
1508                 (void) printf(gettext("usage: %s\n"),
1509                     sa_get_usage(USAGE_DELETE));
1510                 (void) printf(gettext("\tgroup must be specified.\n"));
1511                 return (SA_SYNTAX_ERR);
1512         }
1513 
1514         if ((optind + 1) < argc) {
1515                 (void) printf(gettext("usage: %s\n"),
1516                     sa_get_usage(USAGE_DELETE));
1517                 (void) printf(gettext("\textraneous group(s) at end\n"));
1518                 return (SA_SYNTAX_ERR);
1519         }
1520 
1521         if (sectype != NULL && protocol == NULL) {
1522                 (void) printf(gettext("usage: %s\n"),
1523                     sa_get_usage(USAGE_DELETE));
1524                 (void) printf(gettext("\tsecurity requires protocol to be "
1525                     "specified.\n"));
1526                 return (SA_SYNTAX_ERR);
1527         }
1528 
1529         /*
1530          * Determine if the group already exists since it must in
1531          * order to be removed.
1532          *
1533          * We can delete when:
1534          *
1535          *      - group is empty
1536          *      - force flag is set
1537          *      - if protocol specified, only delete the protocol
1538          */
1539 
1540         groupname = argv[optind];
1541         group = sa_get_group(handle, groupname);
1542         if (group == NULL) {
1543                 ret = SA_NO_SUCH_GROUP;
1544                 goto done;
1545         }
1546         auth = check_authorizations(groupname, flags);
1547         if (protocol == NULL) {
1548                 share = sa_get_share(group, NULL);
1549                 if (share != NULL)
1550                         ret = SA_BUSY;
1551                 if (share == NULL || (share != NULL && force == 1)) {
1552                         ret = SA_OK;
1553                         if (!dryrun) {
1554                                 while (share != NULL) {
1555                                         sa_share_t next_share;
1556                                         next_share = sa_get_next_share(share);
1557                                         /*
1558                                          * need to do the disable of
1559                                          * each share, but don't
1560                                          * actually do anything on a
1561                                          * dryrun.
1562                                          */
1563                                         ret = sa_disable_share(share, NULL);
1564                                         ret = sa_remove_share(share);
1565                                         share = next_share;
1566                                 }
1567                                 ret = sa_remove_group(group);
1568                         }
1569                 }
1570                 /* Commit to configuration if not a dryrun */
1571                 if (!dryrun && ret == SA_OK) {
1572                         ret = sa_update_config(handle);
1573                 }
1574         } else {
1575                 /* a protocol delete */
1576                 sa_optionset_t optionset;
1577                 sa_security_t security;
1578                 if (sectype != NULL) {
1579                         /* only delete specified security */
1580                         security = sa_get_security(group, sectype, protocol);
1581                         if (security != NULL && !dryrun)
1582                                 ret = sa_destroy_security(security);
1583                         else
1584                                 ret = SA_INVALID_PROTOCOL;
1585                 } else {
1586                         optionset = sa_get_optionset(group, protocol);
1587                         if (optionset != NULL && !dryrun) {
1588                                 /*
1589                                  * have an optionset with
1590                                  * protocol to delete
1591                                  */
1592                                 ret = sa_destroy_optionset(optionset);
1593                                 /*
1594                                  * Now find all security sets
1595                                  * for the protocol and remove
1596                                  * them. Don't remove other
1597                                  * protocols.
1598                                  */
1599                                 for (security =
1600                                     sa_get_security(group, NULL, NULL);
1601                                     ret == SA_OK && security != NULL;
1602                                     security = sa_get_next_security(security)) {
1603                                         char *secprot;
1604                                         secprot = sa_get_security_attr(security,
1605                                             "type");
1606                                         if (secprot != NULL &&
1607                                             strcmp(secprot, protocol) == 0)
1608                                                 ret = sa_destroy_security(
1609                                                     security);
1610                                         if (secprot != NULL)
1611                                                 sa_free_attr_string(secprot);
1612                                 }
1613                         } else {
1614                                 if (!dryrun)
1615                                         ret = SA_INVALID_PROTOCOL;
1616                         }
1617                 }
1618                 /*
1619                  * With the protocol items removed, make sure that all
1620                  * the shares are updated in the legacy files, if
1621                  * necessary.
1622                  */
1623                 for (share = sa_get_share(group, NULL);
1624                     share != NULL;
1625                     share = sa_get_next_share(share)) {
1626                         (void) sa_delete_legacy(share, protocol);
1627                 }
1628         }
1629 
1630 done:
1631         if (ret != SA_OK) {
1632                 (void) printf(gettext("Could not delete group: %s\n"),
1633                     sa_errorstr(ret));
1634         } else if (dryrun && !auth && verbose) {
1635                 (void) printf(gettext("Command would fail: %s\n"),
1636                     sa_errorstr(SA_NO_PERMISSION));
1637         }
1638         return (ret);
1639 }
1640 
1641 /*
1642  * strndupr(*buff, str, buffsize)
1643  *
1644  * used with small strings to duplicate and possibly increase the
1645  * buffer size of a string.
1646  */
1647 static char *
1648 strndupr(char *buff, char *str, int *buffsize)
1649 {
1650         int limit;
1651         char *orig_buff = buff;
1652 
1653         if (buff == NULL) {
1654                 buff = (char *)malloc(64);
1655                 if (buff == NULL)
1656                         return (NULL);
1657                 *buffsize = 64;
1658                 buff[0] = '\0';
1659         }
1660         limit = strlen(buff) + strlen(str) + 1;
1661         if (limit > *buffsize) {
1662                 limit = *buffsize = *buffsize + ((limit / 64) + 64);
1663                 buff = realloc(buff, limit);
1664         }
1665         if (buff != NULL) {
1666                 (void) strcat(buff, str);
1667         } else {
1668                 /* if it fails, fail it hard */
1669                 if (orig_buff != NULL)
1670                         free(orig_buff);
1671         }
1672         return (buff);
1673 }
1674 
1675 /*
1676  * group_proto(group)
1677  *
1678  * return a string of all the protocols (space separated) associated
1679  * with this group.
1680  */
1681 
1682 static char *
1683 group_proto(sa_group_t group)
1684 {
1685         sa_optionset_t optionset;
1686         char *proto;
1687         char *buff = NULL;
1688         int buffsize = 0;
1689         int addspace = 0;
1690         /*
1691          * get the protocol list by finding the optionsets on this
1692          * group and extracting the type value. The initial call to
1693          * strndupr() initailizes buff.
1694          */
1695         buff = strndupr(buff, "", &buffsize);
1696         if (buff != NULL) {
1697                 for (optionset = sa_get_optionset(group, NULL);
1698                     optionset != NULL && buff != NULL;
1699                     optionset = sa_get_next_optionset(optionset)) {
1700                         /*
1701                          * extract out the protocol type from this optionset
1702                          * and append it to the buffer "buff". strndupr() will
1703                          * reallocate space as necessay.
1704                          */
1705                         proto = sa_get_optionset_attr(optionset, "type");
1706                         if (proto != NULL) {
1707                                 if (addspace++)
1708                                         buff = strndupr(buff, " ", &buffsize);
1709                                 buff = strndupr(buff, proto, &buffsize);
1710                                 sa_free_attr_string(proto);
1711                         }
1712                 }
1713         }
1714         return (buff);
1715 }
1716 
1717 /*
1718  * sa_list(flags, argc, argv)
1719  *
1720  * implements the "list" subcommand to list groups and optionally
1721  * their state and protocols.
1722  */
1723 
1724 static int
1725 sa_list(sa_handle_t handle, int flags, int argc, char *argv[])
1726 {
1727         sa_group_t group;
1728         int verbose = 0;
1729         int c;
1730         char *protocol = NULL;
1731         int ret = SA_OK;
1732 #ifdef lint
1733         flags = flags;
1734 #endif
1735 
1736         while ((c = getopt(argc, argv, "?hvP:")) != EOF) {
1737                 switch (c) {
1738                 case 'v':
1739                         verbose++;
1740                         break;
1741                 case 'P':
1742                         if (protocol != NULL) {
1743                                 (void) printf(gettext(
1744                                     "Specifying multiple protocols "
1745                                     "not supported: %s\n"),
1746                                     protocol);
1747                                 return (SA_SYNTAX_ERR);
1748                         }
1749                         protocol = optarg;
1750                         if (!sa_valid_protocol(protocol)) {
1751                                 (void) printf(gettext(
1752                                     "Invalid protocol specified: %s\n"),
1753                                     protocol);
1754                                 return (SA_INVALID_PROTOCOL);
1755                         }
1756                         break;
1757                 case 'h':
1758                         /* optopt on valid arg isn't defined */
1759                         optopt = c;
1760                         /*FALLTHROUGH*/
1761                 case '?':
1762                 default:
1763                         /*
1764                          * Since a bad option gets to here, sort it
1765                          * out and return a syntax error return value
1766                          * if necessary.
1767                          */
1768                         switch (optopt) {
1769                         default:
1770                                 ret = SA_SYNTAX_ERR;
1771                                 break;
1772                         case 'h':
1773                         case '?':
1774                                 break;
1775                         }
1776                         (void) printf(gettext("usage: %s\n"),
1777                             sa_get_usage(USAGE_LIST));
1778                         return (ret);
1779                 }
1780         }
1781 
1782         if (optind != argc) {
1783                 (void) printf(gettext("usage: %s\n"),
1784                     sa_get_usage(USAGE_LIST));
1785                 return (SA_SYNTAX_ERR);
1786         }
1787 
1788         for (group = sa_get_group(handle, NULL);
1789             group != NULL;
1790             group = sa_get_next_group(group)) {
1791                 char *name;
1792                 char *proto;
1793                 if (protocol == NULL || has_protocol(group, protocol)) {
1794                         name = sa_get_group_attr(group, "name");
1795                         if (name != NULL && (verbose > 1 || name[0] != '#')) {
1796                                 (void) printf("%s", (char *)name);
1797                                 if (verbose) {
1798                                         /*
1799                                          * Need the list of protocols
1800                                          * and current status once
1801                                          * available. We do want to
1802                                          * translate the
1803                                          * enabled/disabled text here.
1804                                          */
1805                                         (void) printf("\t%s", isenabled(group) ?
1806                                             gettext("enabled") :
1807                                             gettext("disabled"));
1808                                         proto = group_proto(group);
1809                                         if (proto != NULL) {
1810                                                 (void) printf("\t%s",
1811                                                     (char *)proto);
1812                                                 free(proto);
1813                                         }
1814                                 }
1815                                 (void) printf("\n");
1816                         }
1817                         if (name != NULL)
1818                                 sa_free_attr_string(name);
1819                 }
1820         }
1821         return (0);
1822 }
1823 
1824 /*
1825  * out_properties(optionset, proto, sec)
1826  *
1827  * Format the properties and encode the protocol and optional named
1828  * optionset into the string.
1829  *
1830  * format is protocol[:name]=(property-list)
1831  */
1832 
1833 static void
1834 out_properties(sa_optionset_t optionset, char *proto, char *sec)
1835 {
1836         char *type;
1837         char *value;
1838         int spacer;
1839         sa_property_t prop;
1840 
1841         if (sec == NULL)
1842                 (void) printf(" %s=(", proto ? proto : gettext("all"));
1843         else
1844                 (void) printf(" %s:%s=(", proto ? proto : gettext("all"), sec);
1845 
1846         for (spacer = 0, prop = sa_get_property(optionset, NULL);
1847             prop != NULL;
1848             prop = sa_get_next_property(prop)) {
1849 
1850                 /*
1851                  * extract the property name/value and output with
1852                  * appropriate spacing. I.e. no prefixed space the
1853                  * first time through but a space on subsequent
1854                  * properties.
1855                  */
1856                 type = sa_get_property_attr(prop, "type");
1857                 value = sa_get_property_attr(prop, "value");
1858                 if (type != NULL) {
1859                         (void) printf("%s%s=", spacer ? " " : "",       type);
1860                         spacer = 1;
1861                         if (value != NULL)
1862                                 (void) printf("\"%s\"", value);
1863                         else
1864                                 (void) printf("\"\"");
1865                 }
1866                 if (type != NULL)
1867                         sa_free_attr_string(type);
1868                 if (value != NULL)
1869                         sa_free_attr_string(value);
1870         }
1871         (void) printf(")");
1872 }
1873 
1874 /*
1875  * show_properties(group, protocol, prefix)
1876  *
1877  * print the properties for a group. If protocol is NULL, do all
1878  * protocols otherwise only the specified protocol. All security
1879  * (named groups specific to the protocol) are included.
1880  *
1881  * The "prefix" is always applied. The caller knows whether it wants
1882  * some type of prefix string (white space) or not.  Once the prefix
1883  * has been output, it is reduced to the zero length string for the
1884  * remainder of the property output.
1885  */
1886 
1887 static void
1888 show_properties(sa_group_t group, char *protocol, char *prefix)
1889 {
1890         sa_optionset_t optionset;
1891         sa_security_t security;
1892         char *value;
1893         char *secvalue;
1894 
1895         if (protocol != NULL) {
1896                 optionset = sa_get_optionset(group, protocol);
1897                 if (optionset != NULL) {
1898                         (void) printf("%s", prefix);
1899                         prefix = "";
1900                         out_properties(optionset, protocol, NULL);
1901                 }
1902                 security = sa_get_security(group, protocol, NULL);
1903                 if (security != NULL) {
1904                         (void) printf("%s", prefix);
1905                         prefix = "";
1906                         out_properties(security, protocol, NULL);
1907                 }
1908         } else {
1909                 for (optionset = sa_get_optionset(group, protocol);
1910                     optionset != NULL;
1911                     optionset = sa_get_next_optionset(optionset)) {
1912 
1913                         value = sa_get_optionset_attr(optionset, "type");
1914                         (void) printf("%s", prefix);
1915                         prefix = "";
1916                         out_properties(optionset, value, 0);
1917                         if (value != NULL)
1918                                 sa_free_attr_string(value);
1919                 }
1920                 for (security = sa_get_security(group, NULL, protocol);
1921                     security != NULL;
1922                     security = sa_get_next_security(security)) {
1923 
1924                         value = sa_get_security_attr(security, "type");
1925                         secvalue = sa_get_security_attr(security, "sectype");
1926                         (void) printf("%s", prefix);
1927                         prefix = "";
1928                         out_properties(security, value, secvalue);
1929                         if (value != NULL)
1930                                 sa_free_attr_string(value);
1931                         if (secvalue != NULL)
1932                                 sa_free_attr_string(secvalue);
1933                 }
1934         }
1935 }
1936 
1937 /*
1938  * get_resource(share)
1939  *
1940  * Get the first resource name, if any, and fix string to be in
1941  * current locale and have quotes if it has embedded spaces.  Return
1942  * an attr string that must be freed.
1943  */
1944 
1945 static char *
1946 get_resource(sa_share_t share)
1947 {
1948         sa_resource_t resource;
1949         char *resstring = NULL;
1950         char *retstring;
1951 
1952         if ((resource = sa_get_share_resource(share, NULL)) != NULL) {
1953                 resstring = sa_get_resource_attr(resource, "name");
1954                 if (resstring != NULL) {
1955                         char *cp;
1956                         int len;
1957 
1958                         retstring = conv_from_utf8(resstring);
1959                         if (retstring != resstring) {
1960                                 sa_free_attr_string(resstring);
1961                                 resstring = retstring;
1962                         }
1963                         if (strpbrk(resstring, " ") != NULL) {
1964                                 /* account for quotes */
1965                                 len = strlen(resstring) + 3;
1966                                 cp = calloc(len, sizeof (char));
1967                                 if (cp != NULL) {
1968                                         (void) snprintf(cp, len,
1969                                             "\"%s\"", resstring);
1970                                         sa_free_attr_string(resstring);
1971                                         resstring = cp;
1972                                 } else {
1973                                         sa_free_attr_string(resstring);
1974                                         resstring = NULL;
1975                                 }
1976                         }
1977                 }
1978         }
1979         return (resstring);
1980 }
1981 
1982 /*
1983  * has_resource_with_opt(share)
1984  *
1985  * Check to see if the share has any resource names with optionsets
1986  * set. Also indicate if multiple resource names since the syntax
1987  * would be about the same.
1988  */
1989 static int
1990 has_resource_with_opt(sa_share_t share)
1991 {
1992         sa_resource_t resource;
1993         int ret = B_FALSE;
1994 
1995         for (resource = sa_get_share_resource(share, NULL);
1996             resource != NULL;
1997             resource = sa_get_next_resource(resource)) {
1998 
1999                 if (sa_get_optionset(resource, NULL) != NULL) {
2000                         ret = B_TRUE;
2001                         break;
2002                 }
2003         }
2004         return (ret);
2005 }
2006 
2007 /*
2008  * has_multiple_resource(share)
2009  *
2010  * Check to see if the share has multiple resource names since
2011  * the syntax would be about the same.
2012  */
2013 static boolean_t
2014 has_multiple_resource(sa_share_t share)
2015 {
2016         sa_resource_t resource;
2017         int num;
2018 
2019         for (num = 0, resource = sa_get_share_resource(share, NULL);
2020             resource != NULL;
2021             resource = sa_get_next_resource(resource)) {
2022                 num++;
2023                 if (num > 1)
2024                         return (B_TRUE);
2025         }
2026         return (B_FALSE);
2027 }
2028 
2029 /*
2030  * show_share(share, verbose, properties, proto, iszfs, sharepath)
2031  *
2032  * print out the share information. With the addition of resource as a
2033  * full object that can have multiple instances below the share, we
2034  * need to display that as well.
2035  */
2036 
2037 static void
2038 show_share(sa_share_t share, int verbose, int properties, char *proto,
2039     int iszfs, char *sharepath)
2040 {
2041         char *drive;
2042         char *exclude;
2043         sa_resource_t resource = NULL;
2044         char *description;
2045         char *rsrcname;
2046         int rsrcwithopt;
2047         boolean_t multiple;
2048         char *type;
2049 
2050         rsrcwithopt = has_resource_with_opt(share);
2051 
2052         if (verbose || (properties && rsrcwithopt)) {
2053                 /* First, indicate if transient */
2054                 type = sa_get_share_attr(share, "type");
2055                 if (type != NULL && !iszfs && verbose &&
2056                     strcmp(type, "transient") == 0)
2057                         (void) printf("\t* ");
2058                 else
2059                         (void) printf("\t  ");
2060 
2061                 if (type != NULL)
2062                         sa_free_attr_string(type);
2063 
2064                 /*
2065                  * If we came in with verbose, we want to handle the case of
2066                  * multiple resources as though they had properties set.
2067                  */
2068                 multiple = has_multiple_resource(share);
2069 
2070                 /*
2071                  * if there is a description on the share and there
2072                  * are resources, treat as multiple resources in order
2073                  * to get all descriptions displayed.
2074                  */
2075                 description = sa_get_share_description(share);
2076                 resource = sa_get_share_resource(share, NULL);
2077 
2078                 if (description != NULL && resource != NULL)
2079                         multiple = B_TRUE;
2080 
2081                 /* Next, if not multiple follow old model */
2082                 if (!multiple && !rsrcwithopt) {
2083                         rsrcname = get_resource(share);
2084                         if (rsrcname != NULL && strlen(rsrcname) > 0) {
2085                                 (void) printf("%s=%s", rsrcname, sharepath);
2086                         } else {
2087                                 (void) printf("%s", sharepath);
2088                         }
2089                         if (rsrcname != NULL)
2090                                 sa_free_attr_string(rsrcname);
2091                         /* Print the description string if there is one. */
2092                         print_rsrc_desc(resource, description);
2093                 } else {
2094                         /* Treat as simple and then resources come later */
2095                         (void) printf("%s", sharepath);
2096                 }
2097                 drive = sa_get_share_attr(share, "drive-letter");
2098                 if (drive != NULL) {
2099                         if (strlen(drive) > 0)
2100                                 (void) printf(gettext("\tdrive-letter=\"%s:\""),
2101                                     drive);
2102                         sa_free_attr_string(drive);
2103                 }
2104                 if (properties)
2105                         show_properties(share, proto, "\t");
2106                 exclude = sa_get_share_attr(share, "exclude");
2107                 if (exclude != NULL) {
2108                         (void) printf(gettext("\tnot-shared-with=[%s]"),
2109                             exclude);
2110                         sa_free_attr_string(exclude);
2111                 }
2112 
2113                 if (description != NULL) {
2114                         print_rsrc_desc((sa_resource_t)share, description);
2115                 }
2116                 /*
2117                  * If there are resource names with options, show them
2118                  * here, with one line per resource. Resource specific
2119                  * options are at the end of the line followed by
2120                  * description, if any.
2121                  */
2122                 if (rsrcwithopt || multiple) {
2123                         for (resource = sa_get_share_resource(share, NULL);
2124                             resource != NULL;
2125                             resource = sa_get_next_resource(resource)) {
2126                                 int has_space;
2127                                 char *rsrc;
2128 
2129                                 (void) printf("\n\t\t  ");
2130                                 rsrcname = sa_get_resource_attr(resource,
2131                                     "name");
2132                                 if (rsrcname == NULL)
2133                                         continue;
2134 
2135                                 rsrc = conv_from_utf8(rsrcname);
2136                                 has_space = strpbrk(rsrc, " ") != NULL;
2137 
2138                                 if (has_space)
2139                                         (void) printf("\"%s\"=%s", rsrc,
2140                                             sharepath);
2141                                 else
2142                                         (void) printf("%s=%s", rsrc,
2143                                             sharepath);
2144                                 if (rsrc != rsrcname)
2145                                         sa_free_attr_string(rsrc);
2146                                 sa_free_attr_string(rsrcname);
2147                                 if (properties || rsrcwithopt)
2148                                         show_properties(resource, proto, "\t");
2149 
2150                                 /* Get description string if any */
2151                                 print_rsrc_desc(resource, description);
2152                         }
2153                 }
2154                 if (description != NULL)
2155                         sa_free_share_description(description);
2156         } else {
2157                 (void) printf("\t  %s", sharepath);
2158                 if (properties)
2159                         show_properties(share, proto, "\t");
2160         }
2161         (void) printf("\n");
2162 }
2163 
2164 /*
2165  * show_group(group, verbose, properties, proto, subgroup)
2166  *
2167  * helper function to show the contents of a group.
2168  */
2169 
2170 static void
2171 show_group(sa_group_t group, int verbose, int properties, char *proto,
2172     char *subgroup)
2173 {
2174         char *groupname;
2175         char *zfs = NULL;
2176         int iszfs = 0;
2177         char *sharepath;
2178 
2179         groupname = sa_get_group_attr(group, "name");
2180         if (groupname != NULL) {
2181                 sa_share_t share;
2182 
2183                 if (proto != NULL && !has_protocol(group, proto)) {
2184                         sa_free_attr_string(groupname);
2185                         return;
2186                 }
2187                 /*
2188                  * check to see if the group is managed by ZFS. If
2189                  * there is an attribute, then it is. A non-NULL zfs
2190                  * variable will trigger the different way to display
2191                  * and will remove the transient property indicator
2192                  * from the output.
2193                  */
2194                 zfs = sa_get_group_attr(group, "zfs");
2195                 if (zfs != NULL) {
2196                         iszfs = 1;
2197                         sa_free_attr_string(zfs);
2198                 }
2199 
2200                 if (subgroup == NULL)
2201                         (void) printf("%s", groupname);
2202                 else
2203                         (void) printf("    %s/%s", subgroup, groupname);
2204                 if (properties)
2205                         show_properties(group, proto, "");
2206                 (void) printf("\n");
2207                 if (strcmp(groupname, "zfs") == 0) {
2208                         sa_group_t zgroup;
2209 
2210                         for (zgroup = sa_get_sub_group(group);
2211                             zgroup != NULL;
2212                             zgroup = sa_get_next_group(zgroup)) {
2213                                 show_group(zgroup, verbose, properties, proto,
2214                                     "zfs");
2215                         }
2216                         sa_free_attr_string(groupname);
2217                         return;
2218                 }
2219                 /*
2220                  * Have a group, so list the contents. Resource and
2221                  * description are only listed if verbose is set.
2222                  */
2223                 for (share = sa_get_share(group, NULL);
2224                     share != NULL;
2225                     share = sa_get_next_share(share)) {
2226                         sharepath = sa_get_share_attr(share, "path");
2227                         if (sharepath != NULL) {
2228                                 show_share(share, verbose, properties, proto,
2229                                     iszfs, sharepath);
2230                                 sa_free_attr_string(sharepath);
2231                         }
2232                 }
2233         }
2234         if (groupname != NULL) {
2235                 sa_free_attr_string(groupname);
2236         }
2237 }
2238 
2239 /*
2240  * show_group_xml_init()
2241  *
2242  * Create an XML document that will be used to display config info via
2243  * XML format.
2244  */
2245 
2246 xmlDocPtr
2247 show_group_xml_init()
2248 {
2249         xmlDocPtr doc;
2250         xmlNodePtr root;
2251 
2252         doc = xmlNewDoc((xmlChar *)"1.0");
2253         if (doc != NULL) {
2254                 root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
2255                 if (root != NULL)
2256                         (void) xmlDocSetRootElement(doc, root);
2257         }
2258         return (doc);
2259 }
2260 
2261 /*
2262  * show_group_xml(doc, group)
2263  *
2264  * Copy the group info into the XML doc.
2265  */
2266 
2267 static void
2268 show_group_xml(xmlDocPtr doc, sa_group_t group)
2269 {
2270         xmlNodePtr node;
2271         xmlNodePtr root;
2272 
2273         root = xmlDocGetRootElement(doc);
2274         node = xmlCopyNode((xmlNodePtr)group, 1);
2275         if (node != NULL && root != NULL) {
2276                 (void) xmlAddChild(root, node);
2277                 /*
2278                  * In the future, we may have interally used tags that
2279                  * should not appear in the XML output. Remove
2280                  * anything we don't want to show here.
2281                  */
2282         }
2283 }
2284 
2285 /*
2286  * sa_show(flags, argc, argv)
2287  *
2288  * Implements the show subcommand.
2289  */
2290 
2291 int
2292 sa_show(sa_handle_t handle, int flags, int argc, char *argv[])
2293 {
2294         sa_group_t group;
2295         int verbose = 0;
2296         int properties = 0;
2297         int c;
2298         int ret = SA_OK;
2299         char *protocol = NULL;
2300         int xml = 0;
2301         xmlDocPtr doc;
2302 #ifdef lint
2303         flags = flags;
2304 #endif
2305 
2306         while ((c = getopt(argc, argv, "?hvP:px")) !=   EOF) {
2307                 switch (c) {
2308                 case 'v':
2309                         verbose++;
2310                         break;
2311                 case 'p':
2312                         properties++;
2313                         break;
2314                 case 'P':
2315                         if (protocol != NULL) {
2316                                 (void) printf(gettext(
2317                                     "Specifying multiple protocols "
2318                                     "not supported: %s\n"),
2319                                     protocol);
2320                                 return (SA_SYNTAX_ERR);
2321                         }
2322                         protocol = optarg;
2323                         if (!sa_valid_protocol(protocol)) {
2324                                 (void) printf(gettext(
2325                                     "Invalid protocol specified: %s\n"),
2326                                     protocol);
2327                                 return (SA_INVALID_PROTOCOL);
2328                         }
2329                         break;
2330                 case 'x':
2331                         xml++;
2332                         break;
2333                 case 'h':
2334                         /* optopt on valid arg isn't defined */
2335                         optopt = c;
2336                         /*FALLTHROUGH*/
2337                 case '?':
2338                 default:
2339                         /*
2340                          * Since a bad option gets to here, sort it
2341                          * out and return a syntax error return value
2342                          * if necessary.
2343                          */
2344                         switch (optopt) {
2345                         default:
2346                                 ret = SA_SYNTAX_ERR;
2347                                 break;
2348                         case 'h':
2349                         case '?':
2350                                 break;
2351                         }
2352                         (void) printf(gettext("usage: %s\n"),
2353                             sa_get_usage(USAGE_SHOW));
2354                         return (ret);
2355                 }
2356         }
2357 
2358         if (xml) {
2359                 doc = show_group_xml_init();
2360                 if (doc == NULL)
2361                         ret = SA_NO_MEMORY;
2362         }
2363 
2364         if (optind == argc) {
2365                 /* No group specified so go through them all */
2366                 for (group = sa_get_group(handle, NULL);
2367                     group != NULL;
2368                     group = sa_get_next_group(group)) {
2369                         /*
2370                          * Have a group so check if one we want and then list
2371                          * contents with appropriate options.
2372                          */
2373                         if (xml)
2374                                 show_group_xml(doc, group);
2375                         else
2376                                 show_group(group, verbose, properties, protocol,
2377                                     NULL);
2378                 }
2379         } else {
2380                 /* Have a specified list of groups */
2381                 for (; optind < argc; optind++) {
2382                         group = sa_get_group(handle, argv[optind]);
2383                         if (group != NULL) {
2384                                 if (xml)
2385                                         show_group_xml(doc, group);
2386                                 else
2387                                         show_group(group, verbose, properties,
2388                                             protocol, NULL);
2389                         } else {
2390                                 (void) printf(gettext("%s: not found\n"),
2391                                     argv[optind]);
2392                                 ret = SA_NO_SUCH_GROUP;
2393                         }
2394                 }
2395         }
2396         if (xml && ret == SA_OK) {
2397                 (void) xmlDocFormatDump(stdout, doc, 1);
2398                 xmlFreeDoc(doc);
2399         }
2400         return (ret);
2401 
2402 }
2403 
2404 /*
2405  * enable_share(group, share, update_legacy)
2406  *
2407  * helper function to enable a share if the group is enabled.
2408  */
2409 
2410 static int
2411 enable_share(sa_handle_t handle, sa_group_t group, sa_share_t share,
2412     int update_legacy)
2413 {
2414         char *value;
2415         int enabled;
2416         sa_optionset_t optionset;
2417         int err;
2418         int ret = SA_OK;
2419         char *zfs = NULL;
2420         int iszfs = 0;
2421         int isshare;
2422 
2423         /*
2424          * need to enable this share if the group is enabled but not
2425          * otherwise. The enable is also done on each protocol
2426          * represented in the group.
2427          */
2428         value = sa_get_group_attr(group, "state");
2429         enabled = value != NULL && strcmp(value, "enabled") == 0;
2430         if (value != NULL)
2431                 sa_free_attr_string(value);
2432         /* remove legacy config if necessary */
2433         if (update_legacy)
2434                 ret = sa_delete_legacy(share, NULL);
2435         zfs = sa_get_group_attr(group, "zfs");
2436         if (zfs != NULL) {
2437                 iszfs++;
2438                 sa_free_attr_string(zfs);
2439         }
2440 
2441         /*
2442          * Step through each optionset at the group level and
2443          * enable the share based on the protocol type. This
2444          * works because protocols must be set on the group
2445          * for the protocol to be enabled.
2446          */
2447         isshare = sa_is_share(share);
2448         for (optionset = sa_get_optionset(group, NULL);
2449             optionset != NULL && ret == SA_OK;
2450             optionset = sa_get_next_optionset(optionset)) {
2451                 value = sa_get_optionset_attr(optionset, "type");
2452                 if (value != NULL) {
2453                         if (enabled) {
2454                                 if (isshare) {
2455                                         err = sa_enable_share(share, value);
2456                                 } else {
2457                                         err = sa_enable_resource(share, value);
2458                                         if (err == SA_NOT_SUPPORTED) {
2459                                                 sa_share_t parent;
2460                                                 parent = sa_get_resource_parent(
2461                                                     share);
2462                                                 if (parent != NULL)
2463                                                         err = sa_enable_share(
2464                                                             parent, value);
2465                                         }
2466                                 }
2467                                 if (err != SA_OK) {
2468                                         ret = err;
2469                                         (void) printf(gettext(
2470                                             "Failed to enable share for "
2471                                             "\"%s\": %s\n"),
2472                                             value, sa_errorstr(ret));
2473                                 }
2474                         }
2475                         /*
2476                          * If we want to update the legacy, use a copy of
2477                          * share so we can avoid breaking the loop we are in
2478                          * since we might also need to go up the tree to the
2479                          * parent.
2480                          */
2481                         if (update_legacy && !iszfs) {
2482                                 sa_share_t update = share;
2483                                 if (!sa_is_share(share)) {
2484                                         update = sa_get_resource_parent(share);
2485                                 }
2486                                 (void) sa_update_legacy(update, value);
2487                         }
2488                         sa_free_attr_string(value);
2489                 }
2490         }
2491         if (ret == SA_OK)
2492                 (void) sa_update_config(handle);
2493         return (ret);
2494 }
2495 
2496 /*
2497  * sa_require_resource(group)
2498  *
2499  * if any of the defined protocols on the group require resource
2500  * names, then all shares must have them.
2501  */
2502 
2503 static int
2504 sa_require_resource(sa_group_t group)
2505 {
2506         sa_optionset_t optionset;
2507 
2508         for (optionset = sa_get_optionset(group, NULL);
2509             optionset != NULL;
2510             optionset = sa_get_next_optionset(optionset)) {
2511                 char *proto;
2512 
2513                 proto = sa_get_optionset_attr(optionset, "type");
2514                 if (proto != NULL) {
2515                         uint64_t features;
2516 
2517                         features = sa_proto_get_featureset(proto);
2518                         if (features & SA_FEATURE_RESOURCE) {
2519                                 sa_free_attr_string(proto);
2520                                 return (B_TRUE);
2521                         }
2522                         sa_free_attr_string(proto);
2523                 }
2524         }
2525         return (B_FALSE);
2526 }
2527 
2528 /*
2529  * sa_addshare(flags, argc, argv)
2530  *
2531  * implements add-share subcommand.
2532  */
2533 
2534 static int
2535 sa_addshare(sa_handle_t handle, int flags, int argc, char *argv[])
2536 {
2537         int verbose = 0;
2538         int dryrun = 0;
2539         int c;
2540         int ret = SA_OK;
2541         sa_group_t group;
2542         sa_share_t share;
2543         sa_resource_t resource = NULL;
2544         char *sharepath = NULL;
2545         char *description = NULL;
2546         char *rsrcname = NULL;
2547         char *rsrc = NULL;
2548         int persist = SA_SHARE_PERMANENT; /* default to persist */
2549         int auth;
2550         char dir[MAXPATHLEN];
2551 
2552         while ((c = getopt(argc, argv, "?hvns:d:r:t")) != EOF) {
2553                 switch (c) {
2554                 case 'n':
2555                         dryrun++;
2556                         break;
2557                 case 'v':
2558                         verbose++;
2559                         break;
2560                 case 'd':
2561                         description = optarg;
2562                         break;
2563                 case 'r':
2564                         if (rsrcname != NULL) {
2565                                 (void) printf(gettext("Adding multiple "
2566                                     "resource names not"
2567                                     " supported\n"));
2568                                 return (SA_SYNTAX_ERR);
2569                         }
2570                         rsrcname = optarg;
2571                         break;
2572                 case 's':
2573                         /*
2574                          * Save share path into group. Currently limit
2575                          * to one share per command.
2576                          */
2577                         if (sharepath != NULL) {
2578                                 (void) printf(gettext(
2579                                     "Adding multiple shares not supported\n"));
2580                                 return (SA_SYNTAX_ERR);
2581                         }
2582                         sharepath = optarg;
2583                         break;
2584                 case 't':
2585                         persist = SA_SHARE_TRANSIENT;
2586                         break;
2587                 case 'h':
2588                         /* optopt on valid arg isn't defined */
2589                         optopt = c;
2590                         /*FALLTHROUGH*/
2591                 case '?':
2592                 default:
2593                         /*
2594                          * Since a bad option gets to here, sort it
2595                          * out and return a syntax error return value
2596                          * if necessary.
2597                          */
2598                         switch (optopt) {
2599                         default:
2600                                 ret = SA_SYNTAX_ERR;
2601                                 break;
2602                         case 'h':
2603                         case '?':
2604                                 break;
2605                         }
2606                         (void) printf(gettext("usage: %s\n"),
2607                             sa_get_usage(USAGE_ADD_SHARE));
2608                         return (ret);
2609                 }
2610         }
2611 
2612         if (optind >= argc) {
2613                 (void) printf(gettext("usage: %s\n"),
2614                     sa_get_usage(USAGE_ADD_SHARE));
2615                 if (dryrun || sharepath != NULL || description != NULL ||
2616                     rsrcname != NULL || verbose || persist) {
2617                         (void) printf(gettext("\tgroup must be specified\n"));
2618                         ret = SA_NO_SUCH_GROUP;
2619                 } else {
2620                         ret = SA_OK;
2621                 }
2622         } else {
2623                 if (sharepath == NULL) {
2624                         (void) printf(gettext("usage: %s\n"),
2625                             sa_get_usage(USAGE_ADD_SHARE));
2626                         (void) printf(gettext(
2627                             "\t-s sharepath must be specified\n"));
2628                         ret = SA_BAD_PATH;
2629                 }
2630                 if (ret == SA_OK) {
2631                         if (realpath(sharepath, dir) == NULL) {
2632                                 ret = SA_BAD_PATH;
2633                                 (void) printf(gettext("Path "
2634                                     "is not valid: %s\n"),
2635                                     sharepath);
2636                         } else {
2637                                 sharepath = dir;
2638                         }
2639                 }
2640                 if (ret == SA_OK && rsrcname != NULL) {
2641                         /* check for valid syntax */
2642                         if (validresource(rsrcname)) {
2643                                 rsrc = conv_to_utf8(rsrcname);
2644                                 resource = sa_find_resource(handle, rsrc);
2645                                 if (resource != NULL) {
2646                                         /*
2647                                          * Resource names must be
2648                                          * unique in the system
2649                                          */
2650                                         ret = SA_DUPLICATE_NAME;
2651                                         (void) printf(gettext("usage: %s\n"),
2652                                             sa_get_usage(USAGE_ADD_SHARE));
2653                                         (void) printf(gettext(
2654                                             "\tresource names must be unique "
2655                                             "in the system\n"));
2656                                 }
2657                         } else {
2658                                 (void) printf(gettext("usage: %s\n"),
2659                                     sa_get_usage(USAGE_ADD_SHARE));
2660                                 (void) printf(gettext(
2661                                     "\tresource names use restricted "
2662                                     "character set\n"));
2663                                 ret = SA_INVALID_NAME;
2664                         }
2665                 }
2666 
2667                 if (ret != SA_OK) {
2668                         if (rsrc != NULL && rsrcname != rsrc)
2669                                 sa_free_attr_string(rsrc);
2670                         return (ret);
2671                 }
2672 
2673                 share = sa_find_share(handle, sharepath);
2674                 if (share != NULL) {
2675                         if (rsrcname == NULL) {
2676                                 /*
2677                                  * Can only have a duplicate share if a new
2678                                  * resource name is being added.
2679                                  */
2680                                 ret = SA_DUPLICATE_NAME;
2681                                 (void) printf(gettext("Share path already "
2682                                     "shared: %s\n"), sharepath);
2683                         }
2684                 }
2685                 if (ret != SA_OK)
2686                         return (ret);
2687 
2688                 group = sa_get_group(handle, argv[optind]);
2689                 if (group != NULL) {
2690                         if (sa_require_resource(group) == B_TRUE &&
2691                             rsrcname == NULL) {
2692                                 (void) printf(gettext(
2693                                     "Resource name is required "
2694                                     "by at least one enabled protocol "
2695                                     "in group\n"));
2696                                 return (SA_RESOURCE_REQUIRED);
2697                         }
2698                         if (share == NULL && ret == SA_OK) {
2699                                 if (dryrun)
2700                                         ret = sa_check_path(group, sharepath,
2701                                             SA_CHECK_NORMAL);
2702                                 else
2703                                         share = sa_add_share(group, sharepath,
2704                                             persist, &ret);
2705                         }
2706                         /*
2707                          * Make sure this isn't an attempt to put a resourced
2708                          * share into a different group than it already is in.
2709                          */
2710                         if (share != NULL) {
2711                                 sa_group_t parent;
2712                                 parent = sa_get_parent_group(share);
2713                                 if (parent != group) {
2714                                         ret = SA_DUPLICATE_NAME;
2715                                         (void) printf(gettext(
2716                                             "Share path already "
2717                                             "shared: %s\n"), sharepath);
2718                                 }
2719                         }
2720                         if (!dryrun && share == NULL) {
2721                                 (void) printf(gettext(
2722                                     "Could not add share: %s\n"),
2723                                     sa_errorstr(ret));
2724                         } else {
2725                                 auth = check_authorizations(argv[optind],
2726                                     flags);
2727                                 if (!dryrun && ret == SA_OK) {
2728                                         if (rsrcname != NULL) {
2729                                                 resource = sa_add_resource(
2730                                                     share,
2731                                                     rsrc,
2732                                                     SA_SHARE_PERMANENT,
2733                                                     &ret);
2734                                         }
2735                                         if (ret == SA_OK &&
2736                                             description != NULL) {
2737                                                 if (resource != NULL)
2738                                                         ret =
2739                                                             set_resource_desc(
2740                                                             resource,
2741                                                             description);
2742                                                 else
2743                                                         ret =
2744                                                             set_share_desc(
2745                                                             share,
2746                                                             description);
2747                                         }
2748                                         if (ret == SA_OK) {
2749                                                 /* now enable the share(s) */
2750                                                 if (resource != NULL) {
2751                                                         ret = enable_share(
2752                                                             handle,
2753                                                             group,
2754                                                             resource,
2755                                                             1);
2756                                                 } else {
2757                                                         ret = enable_share(
2758                                                             handle,
2759                                                             group,
2760                                                             share,
2761                                                             1);
2762                                                 }
2763                                                 ret = sa_update_config(handle);
2764                                         }
2765                                         switch (ret) {
2766                                         case SA_DUPLICATE_NAME:
2767                                                 (void) printf(gettext(
2768                                                     "Resource name in"
2769                                                     "use: %s\n"),
2770                                                     rsrcname);
2771                                                 break;
2772                                         default:
2773                                                 (void) printf(gettext(
2774                                                     "Could not set "
2775                                                     "attribute: %s\n"),
2776                                                     sa_errorstr(ret));
2777                                                 break;
2778                                         case SA_OK:
2779                                                 break;
2780                                         }
2781                                 } else if (dryrun && ret == SA_OK &&
2782                                     !auth && verbose) {
2783                                         (void) printf(gettext(
2784                                             "Command would fail: %s\n"),
2785                                             sa_errorstr(SA_NO_PERMISSION));
2786                                         ret = SA_NO_PERMISSION;
2787                                 }
2788                         }
2789                 } else {
2790                         switch (ret) {
2791                         default:
2792                                 (void) printf(gettext(
2793                                     "Group \"%s\" not found\n"), argv[optind]);
2794                                 ret = SA_NO_SUCH_GROUP;
2795                                 break;
2796                         case SA_BAD_PATH:
2797                         case SA_DUPLICATE_NAME:
2798                                 break;
2799                         }
2800                 }
2801         }
2802         return (ret);
2803 }
2804 
2805 /*
2806  * sa_moveshare(flags, argc, argv)
2807  *
2808  * implements move-share subcommand.
2809  */
2810 
2811 int
2812 sa_moveshare(sa_handle_t handle, int flags, int argc, char *argv[])
2813 {
2814         int verbose = 0;
2815         int dryrun = 0;
2816         int c;
2817         int ret = SA_OK;
2818         sa_group_t group;
2819         sa_share_t share;
2820         char *rsrcname = NULL;
2821         char *sharepath = NULL;
2822         int authsrc = 0, authdst = 0;
2823         char dir[MAXPATHLEN];
2824 
2825         while ((c = getopt(argc, argv, "?hvnr:s:")) != EOF) {
2826                 switch (c) {
2827                 case 'n':
2828                         dryrun++;
2829                         break;
2830                 case 'v':
2831                         verbose++;
2832                         break;
2833                 case 'r':
2834                         if (rsrcname != NULL) {
2835                                 (void) printf(gettext(
2836                                     "Moving multiple resource names not"
2837                                     " supported\n"));
2838                                 return (SA_SYNTAX_ERR);
2839                         }
2840                         rsrcname = optarg;
2841                         break;
2842                 case 's':
2843                         /*
2844                          * Remove share path from group. Currently limit
2845                          * to one share per command.
2846                          */
2847                         if (sharepath != NULL) {
2848                                 (void) printf(gettext("Moving multiple shares"
2849                                     " not supported\n"));
2850                                 return (SA_SYNTAX_ERR);
2851                         }
2852                         sharepath = optarg;
2853                         break;
2854                 case 'h':
2855                         /* optopt on valid arg isn't defined */
2856                         optopt = c;
2857                         /*FALLTHROUGH*/
2858                 case '?':
2859                 default:
2860                         /*
2861                          * Since a bad option gets to here, sort it
2862                          * out and return a syntax error return value
2863                          * if necessary.
2864                          */
2865                         switch (optopt) {
2866                         default:
2867                                 ret = SA_SYNTAX_ERR;
2868                                 break;
2869                         case 'h':
2870                         case '?':
2871                                 break;
2872                         }
2873                         (void) printf(gettext("usage: %s\n"),
2874                             sa_get_usage(USAGE_MOVE_SHARE));
2875                         return (ret);
2876                 }
2877         }
2878 
2879         if (optind >= argc || sharepath == NULL) {
2880                 (void) printf(gettext("usage: %s\n"),
2881                     sa_get_usage(USAGE_MOVE_SHARE));
2882                 if (dryrun || verbose || sharepath != NULL) {
2883                         (void) printf(gettext("\tgroup must be specified\n"));
2884                         ret = SA_NO_SUCH_GROUP;
2885                 } else {
2886                         if (sharepath == NULL) {
2887                                 ret = SA_SYNTAX_ERR;
2888                                 (void) printf(gettext(
2889                                     "\tsharepath must be specified\n"));
2890                         } else {
2891                                 ret = SA_OK;
2892                         }
2893                 }
2894         } else {
2895                 sa_group_t parent;
2896                 char *zfsold;
2897                 char *zfsnew;
2898 
2899                 if (sharepath == NULL) {
2900                         (void) printf(gettext(
2901                             "sharepath must be specified with the -s "
2902                             "option\n"));
2903                         return (SA_BAD_PATH);
2904                 }
2905                 group = sa_get_group(handle, argv[optind]);
2906                 if (group == NULL) {
2907                         (void) printf(gettext("Group \"%s\" not found\n"),
2908                             argv[optind]);
2909                         return (SA_NO_SUCH_GROUP);
2910                 }
2911                 share = sa_find_share(handle, sharepath);
2912                 /*
2913                  * If a share wasn't found, it may have been a symlink
2914                  * or has a trailing '/'. Try again after resolving
2915                  * with realpath().
2916                  */
2917                 if (share == NULL) {
2918                         if (realpath(sharepath, dir) == NULL) {
2919                                 (void) printf(gettext("Path "
2920                                     "is not valid: %s\n"),
2921                                     sharepath);
2922                                 return (SA_BAD_PATH);
2923                         }
2924                         sharepath = dir;
2925                         share = sa_find_share(handle, sharepath);
2926                 }
2927                 if (share == NULL) {
2928                         (void) printf(gettext("Share not found: %s\n"),
2929                             sharepath);
2930                         return (SA_NO_SUCH_PATH);
2931                 }
2932                 authdst = check_authorizations(argv[optind], flags);
2933 
2934                 parent = sa_get_parent_group(share);
2935                 if (parent != NULL) {
2936                         char *pname;
2937                         pname = sa_get_group_attr(parent, "name");
2938                         if (pname != NULL) {
2939                                 authsrc = check_authorizations(pname, flags);
2940                                 sa_free_attr_string(pname);
2941                         }
2942                         zfsold = sa_get_group_attr(parent, "zfs");
2943                         zfsnew = sa_get_group_attr(group, "zfs");
2944                         if ((zfsold != NULL && zfsnew == NULL) ||
2945                             (zfsold == NULL && zfsnew != NULL)) {
2946                                 ret = SA_NOT_ALLOWED;
2947                         }
2948                         if (zfsold != NULL)
2949                                 sa_free_attr_string(zfsold);
2950                         if (zfsnew != NULL)
2951                                 sa_free_attr_string(zfsnew);
2952                 }
2953 
2954                 if (ret == SA_OK && parent != group && !dryrun) {
2955                         char *oldstate;
2956                         /*
2957                          * Note that the share may need to be
2958                          * "unshared" if the new group is disabled and
2959                          * the old was enabled or it may need to be
2960                          * share to update if the new group is
2961                          * enabled. We disable before the move and
2962                          * will have to enable after the move in order
2963                          * to cleanup entries for protocols that
2964                          * aren't in the new group.
2965                          */
2966                         oldstate = sa_get_group_attr(parent, "state");
2967                         if (oldstate != NULL) {
2968                                 /* enable_share determines what to do */
2969                                 if (strcmp(oldstate, "enabled") == 0)
2970                                         (void) sa_disable_share(share, NULL);
2971                                 sa_free_attr_string(oldstate);
2972                         }
2973                 }
2974 
2975                 if (!dryrun && ret == SA_OK)
2976                         ret = sa_move_share(group, share);
2977 
2978                 /*
2979                  * Reenable and update any config information.
2980                  */
2981                 if (ret == SA_OK && parent != group && !dryrun) {
2982                         ret = sa_update_config(handle);
2983 
2984                         (void) enable_share(handle, group, share, 1);
2985                 }
2986 
2987                 if (ret != SA_OK)
2988                         (void) printf(gettext("Could not move share: %s\n"),
2989                             sa_errorstr(ret));
2990 
2991                 if (dryrun && ret == SA_OK && !(authsrc & authdst) &&
2992                     verbose) {
2993                         (void) printf(gettext("Command would fail: %s\n"),
2994                             sa_errorstr(SA_NO_PERMISSION));
2995                 }
2996         }
2997         return (ret);
2998 }
2999 
3000 /*
3001  * sa_removeshare(flags, argc, argv)
3002  *
3003  * implements remove-share subcommand.
3004  */
3005 
3006 int
3007 sa_removeshare(sa_handle_t handle, int flags, int argc, char *argv[])
3008 {
3009         int verbose = 0;
3010         int dryrun = 0;
3011         int force = 0;
3012         int c;
3013         int ret = SA_OK;
3014         sa_group_t group;
3015         sa_resource_t resource = NULL;
3016         sa_share_t share = NULL;
3017         char *rsrcname = NULL;
3018         char *sharepath = NULL;
3019         char dir[MAXPATHLEN];
3020         int auth;
3021 
3022         while ((c = getopt(argc, argv, "?hfnr:s:v")) != EOF) {
3023                 switch (c) {
3024                 case 'n':
3025                         dryrun++;
3026                         break;
3027                 case 'v':
3028                         verbose++;
3029                         break;
3030                 case 'f':
3031                         force++;
3032                         break;
3033                 case 's':
3034                         /*
3035                          * Remove share path from group. Currently limit
3036                          * to one share per command.
3037                          */
3038                         if (sharepath != NULL) {
3039                                 (void) printf(gettext(
3040                                     "Removing multiple shares not "
3041                                     "supported\n"));
3042                                 return (SA_SYNTAX_ERR);
3043                         }
3044                         sharepath = optarg;
3045                         break;
3046                 case 'r':
3047                         /*
3048                          * Remove share from group if last resource or remove
3049                          * resource from share if multiple resources.
3050                          */
3051                         if (rsrcname != NULL) {
3052                                 (void) printf(gettext(
3053                                     "Removing multiple resource names not "
3054                                     "supported\n"));
3055                                 return (SA_SYNTAX_ERR);
3056                         }
3057                         rsrcname = optarg;
3058                         break;
3059                 case 'h':
3060                         /* optopt on valid arg isn't defined */
3061                         optopt = c;
3062                         /*FALLTHROUGH*/
3063                 case '?':
3064                 default:
3065                         /*
3066                          * Since a bad option gets to here, sort it
3067                          * out and return a syntax error return value
3068                          * if necessary.
3069                          */
3070                         switch (optopt) {
3071                         default:
3072                                 ret = SA_SYNTAX_ERR;
3073                                 break;
3074                         case 'h':
3075                         case '?':
3076                                 break;
3077                         }
3078                         (void) printf(gettext("usage: %s\n"),
3079                             sa_get_usage(USAGE_REMOVE_SHARE));
3080                         return (ret);
3081                 }
3082         }
3083 
3084         if (optind >= argc || (rsrcname == NULL && sharepath == NULL)) {
3085                 if (sharepath == NULL && rsrcname == NULL) {
3086                         (void) printf(gettext("usage: %s\n"),
3087                             sa_get_usage(USAGE_REMOVE_SHARE));
3088                         (void) printf(gettext("\t-s sharepath or -r resource"
3089                             " must be specified\n"));
3090                         ret = SA_BAD_PATH;
3091                 } else {
3092                         ret = SA_OK;
3093                 }
3094         }
3095         if (ret != SA_OK) {
3096                 return (ret);
3097         }
3098 
3099         if (optind < argc) {
3100                 if ((optind + 1) < argc) {
3101                         (void) printf(gettext("Extraneous group(s) at end of "
3102                             "command\n"));
3103                         ret = SA_SYNTAX_ERR;
3104                 } else {
3105                         group = sa_get_group(handle, argv[optind]);
3106                         if (group == NULL) {
3107                                 (void) printf(gettext(
3108                                     "Group \"%s\" not found\n"), argv[optind]);
3109                                 ret = SA_NO_SUCH_GROUP;
3110                         }
3111                 }
3112         } else {
3113                 group = NULL;
3114         }
3115 
3116         if (rsrcname != NULL) {
3117                 resource = sa_find_resource(handle, rsrcname);
3118                 if (resource == NULL) {
3119                         ret = SA_NO_SUCH_RESOURCE;
3120                         (void) printf(gettext(
3121                             "Resource name not found for share: %s\n"),
3122                             rsrcname);
3123                 }
3124         }
3125 
3126         /*
3127          * Lookup the path in the internal configuration. Care
3128          * must be taken to handle the case where the
3129          * underlying path has been removed since we need to
3130          * be able to deal with that as well.
3131          */
3132         if (ret == SA_OK) {
3133                 if (sharepath != NULL) {
3134                         if (group != NULL)
3135                                 share = sa_get_share(group, sharepath);
3136                         else
3137                                 share = sa_find_share(handle, sharepath);
3138                 }
3139 
3140                 if (resource != NULL) {
3141                         sa_share_t rsrcshare;
3142                         rsrcshare = sa_get_resource_parent(resource);
3143                         if (share == NULL)
3144                                 share = rsrcshare;
3145                         else if (share != rsrcshare) {
3146                                 ret = SA_NO_SUCH_RESOURCE;
3147                                 (void) printf(gettext(
3148                                     "Bad resource name for share: %s\n"),
3149                                     rsrcname);
3150                                 share = NULL;
3151                         }
3152                 }
3153 
3154                 /*
3155                  * If we didn't find the share with the provided path,
3156                  * it may be a symlink so attempt to resolve it using
3157                  * realpath and try again. Realpath will resolve any
3158                  * symlinks and place them in "dir". Note that
3159                  * sharepath is only used for the lookup the first
3160                  * time and later for error messages. dir will be used
3161                  * on the second attempt. Once a share is found, all
3162                  * operations are based off of the share variable.
3163                  */
3164                 if (share == NULL) {
3165                         if (realpath(sharepath, dir) == NULL) {
3166                                 ret = SA_BAD_PATH;
3167                                 (void) printf(gettext(
3168                                     "Path is not valid: %s\n"), sharepath);
3169                         } else {
3170                                 if (group != NULL)
3171                                         share = sa_get_share(group, dir);
3172                                 else
3173                                         share = sa_find_share(handle, dir);
3174                         }
3175                 }
3176         }
3177 
3178         /*
3179          * If there hasn't been an error, there was likely a
3180          * path found. If not, give the appropriate error
3181          * message and set the return error. If it was found,
3182          * then disable the share and then remove it from the
3183          * configuration.
3184          */
3185         if (ret != SA_OK) {
3186                 return (ret);
3187         }
3188         if (share == NULL) {
3189                 if (group != NULL)
3190                         (void) printf(gettext("Share not found in group %s:"
3191                             " %s\n"), argv[optind], sharepath);
3192                 else
3193                         (void) printf(gettext("Share not found: %s\n"),
3194                             sharepath);
3195                 ret = SA_NO_SUCH_PATH;
3196         } else {
3197                 if (group == NULL)
3198                         group = sa_get_parent_group(share);
3199                 if (!dryrun) {
3200                         if (ret == SA_OK) {
3201                                 if (resource != NULL)
3202                                         ret = sa_disable_resource(resource,
3203                                             NULL);
3204                                 else
3205                                         ret = sa_disable_share(share, NULL);
3206                                 /*
3207                                  * We don't care if it fails since it
3208                                  * could be disabled already. Some
3209                                  * unexpected errors could occur that
3210                                  * prevent removal, so also check for
3211                                  * force being set.
3212                                  */
3213                                 if ((ret == SA_OK || ret == SA_NO_SUCH_PATH ||
3214                                     ret == SA_NOT_SUPPORTED ||
3215                                     ret == SA_SYSTEM_ERR || force) &&
3216                                     resource == NULL)
3217                                         ret = sa_remove_share(share);
3218 
3219                                 if ((ret == SA_OK || ret == SA_NO_SUCH_PATH ||
3220                                     ret == SA_NOT_SUPPORTED ||
3221                                     ret == SA_SYSTEM_ERR || force) &&
3222                                     resource != NULL) {
3223                                         ret = sa_remove_resource(resource);
3224                                         if (ret == SA_OK) {
3225                                                 /*
3226                                                  * If this was the
3227                                                  * last one, remove
3228                                                  * the share as well.
3229                                                  */
3230                                                 resource =
3231                                                     sa_get_share_resource(
3232                                                     share, NULL);
3233                                                 if (resource == NULL)
3234                                                         ret = sa_remove_share(
3235                                                             share);
3236                                         }
3237                                 }
3238                                 if (ret == SA_OK)
3239                                         ret = sa_update_config(handle);
3240                         }
3241                         if (ret != SA_OK)
3242                                 (void) printf(gettext("Could not remove share:"
3243                                     " %s\n"), sa_errorstr(ret));
3244                 } else if (ret == SA_OK) {
3245                         char *pname;
3246                         pname = sa_get_group_attr(group, "name");
3247                         if (pname != NULL) {
3248                                 auth = check_authorizations(pname, flags);
3249                                 sa_free_attr_string(pname);
3250                         }
3251                         if (!auth && verbose) {
3252                                 (void) printf(gettext(
3253                                     "Command would fail: %s\n"),
3254                                     sa_errorstr(SA_NO_PERMISSION));
3255                         }
3256                 }
3257         }
3258         return (ret);
3259 }
3260 
3261 /*
3262  * sa_set_share(flags, argc, argv)
3263  *
3264  * implements set-share subcommand.
3265  */
3266 
3267 int
3268 sa_set_share(sa_handle_t handle, int flags, int argc, char *argv[])
3269 {
3270         int dryrun = 0;
3271         int c;
3272         int ret = SA_OK;
3273         sa_group_t group, sharegroup;
3274         sa_share_t share = NULL;
3275         sa_resource_t resource = NULL;
3276         char *sharepath = NULL;
3277         char *description = NULL;
3278         char *rsrcname = NULL;
3279         char *rsrc = NULL;
3280         char *newname = NULL;
3281         char *newrsrc;
3282         char *groupname = NULL;
3283         int auth;
3284         int verbose = 0;
3285 
3286         while ((c = getopt(argc, argv, "?hnd:r:s:")) != EOF) {
3287                 switch (c) {
3288                 case 'n':
3289                         dryrun++;
3290                         break;
3291                 case 'd':
3292                         description = optarg;
3293                         break;
3294                 case 'v':
3295                         verbose++;
3296                         break;
3297                 case 'r':
3298                         /*
3299                          * Update share by resource name
3300                          */
3301                         if (rsrcname != NULL) {
3302                                 (void) printf(gettext(
3303                                     "Updating multiple resource names not "
3304                                     "supported\n"));
3305                                 return (SA_SYNTAX_ERR);
3306                         }
3307                         rsrcname = optarg;
3308                         break;
3309                 case 's':
3310                         /*
3311                          * Save share path into group. Currently limit
3312                          * to one share per command.
3313                          */
3314                         if (sharepath != NULL) {
3315                                 (void) printf(gettext(
3316                                     "Updating multiple shares not "
3317                                     "supported\n"));
3318                                 return (SA_SYNTAX_ERR);
3319                         }
3320                         sharepath = optarg;
3321                         break;
3322                 case 'h':
3323                         /* optopt on valid arg isn't defined */
3324                         optopt = c;
3325                         /*FALLTHROUGH*/
3326                 case '?':
3327                 default:
3328                         /*
3329                          * Since a bad option gets to here, sort it
3330                          * out and return a syntax error return value
3331                          * if necessary.
3332                          */
3333                         switch (optopt) {
3334                         default:
3335                                 ret = SA_SYNTAX_ERR;
3336                                 break;
3337                         case 'h':
3338                         case '?':
3339                                 break;
3340                         }
3341                         (void) printf(gettext("usage: %s\n"),
3342                             sa_get_usage(USAGE_SET_SHARE));
3343                         return (ret);
3344                 }
3345         }
3346 
3347         if (optind >= argc && sharepath == NULL && rsrcname == NULL) {
3348                 if (sharepath == NULL) {
3349                         (void) printf(gettext("usage: %s\n"),
3350                             sa_get_usage(USAGE_SET_SHARE));
3351                         (void) printf(gettext("\tgroup must be specified\n"));
3352                         ret = SA_BAD_PATH;
3353                 } else {
3354                         ret = SA_OK;
3355                 }
3356         }
3357         if ((optind + 1) < argc) {
3358                 (void) printf(gettext("usage: %s\n"),
3359                     sa_get_usage(USAGE_SET_SHARE));
3360                 (void) printf(gettext("\tExtraneous group(s) at end\n"));
3361                 ret = SA_SYNTAX_ERR;
3362         }
3363 
3364         /*
3365          * Must have at least one of sharepath and rsrcrname.
3366          * It is a syntax error to be missing both.
3367          */
3368         if (sharepath == NULL && rsrcname == NULL) {
3369                 (void) printf(gettext("usage: %s\n"),
3370                     sa_get_usage(USAGE_SET_SHARE));
3371                 ret = SA_SYNTAX_ERR;
3372         }
3373 
3374         if (ret != SA_OK)
3375                 return (ret);
3376 
3377         if (optind < argc) {
3378                 groupname = argv[optind];
3379                 group = sa_get_group(handle, groupname);
3380         } else {
3381                 group = NULL;
3382                 groupname = NULL;
3383         }
3384         if (rsrcname != NULL) {
3385                 /*
3386                  * If rsrcname exists, split rename syntax and then
3387                  * convert to utf 8 if no errors.
3388                  */
3389                 newname = strchr(rsrcname, '=');
3390                 if (newname != NULL) {
3391                         *newname++ = '\0';
3392                 }
3393                 if (!validresource(rsrcname)) {
3394                         ret = SA_INVALID_NAME;
3395                         (void) printf(gettext("Invalid resource name: "
3396                             "\"%s\"\n"), rsrcname);
3397                 } else {
3398                         rsrc = conv_to_utf8(rsrcname);
3399                 }
3400                 if (newname != NULL) {
3401                         if (!validresource(newname)) {
3402                                 ret = SA_INVALID_NAME;
3403                                 (void) printf(gettext("Invalid resource name: "
3404                                     "%s\n"), newname);
3405                                 newname = NULL;
3406                         } else {
3407                                 newrsrc = conv_to_utf8(newname);
3408                         }
3409                 }
3410         }
3411 
3412         if (ret != SA_OK) {
3413                 if (rsrcname != NULL && rsrcname != rsrc)
3414                         sa_free_attr_string(rsrc);
3415                 if (newname != NULL && newname != newrsrc)
3416                         sa_free_attr_string(newrsrc);
3417                 return (ret);
3418         }
3419 
3420         if (sharepath != NULL) {
3421                 share = sa_find_share(handle, sharepath);
3422         } else if (rsrcname != NULL) {
3423                 resource = sa_find_resource(handle, rsrc);
3424                 if (resource != NULL)
3425                         share = sa_get_resource_parent(resource);
3426                 else
3427                         ret = SA_NO_SUCH_RESOURCE;
3428         }
3429         if (share != NULL) {
3430                 sharegroup = sa_get_parent_group(share);
3431                 if (group != NULL && group != sharegroup) {
3432                         (void) printf(gettext("Group \"%s\" does not contain "
3433                             "share %s\n"),
3434                             argv[optind], sharepath);
3435                         ret = SA_BAD_PATH;
3436                 } else {
3437                         int delgroupname = 0;
3438                         if (groupname == NULL) {
3439                                 groupname = sa_get_group_attr(sharegroup,
3440                                     "name");
3441                                 delgroupname = 1;
3442                         }
3443                         if (groupname != NULL) {
3444                                 auth = check_authorizations(groupname, flags);
3445                                 if (delgroupname) {
3446                                         sa_free_attr_string(groupname);
3447                                         groupname = NULL;
3448                                 }
3449                         } else {
3450                                 ret = SA_NO_MEMORY;
3451                         }
3452                         if (rsrcname != NULL) {
3453                                 resource = sa_find_resource(handle, rsrc);
3454                                 if (!dryrun) {
3455                                         if (newname != NULL &&
3456                                             resource != NULL)
3457                                                 ret = sa_rename_resource(
3458                                                     resource, newrsrc);
3459                                         else if (newname != NULL)
3460                                                 ret = SA_NO_SUCH_RESOURCE;
3461                                         if (newname != NULL &&
3462                                             newname != newrsrc)
3463                                                 sa_free_attr_string(newrsrc);
3464                                 }
3465                                 if (rsrc != rsrcname)
3466                                         sa_free_attr_string(rsrc);
3467                         }
3468 
3469                         /*
3470                          * If the user has set a description, it will be
3471                          * on the resource if -r was used otherwise it
3472                          * must be on the share.
3473                          */
3474                         if (!dryrun && ret == SA_OK && description != NULL) {
3475                                 char *desc;
3476                                 desc = conv_to_utf8(description);
3477                                 if (resource != NULL)
3478                                         ret = sa_set_resource_description(
3479                                             resource, desc);
3480                                 else
3481                                         ret = sa_set_share_description(share,
3482                                             desc);
3483                                 if (desc != description)
3484                                         sa_free_share_description(desc);
3485                         }
3486                 }
3487                 if (!dryrun && ret == SA_OK) {
3488                         if (resource != NULL)
3489                                 (void) sa_enable_resource(resource, NULL);
3490                         ret = sa_update_config(handle);
3491                 }
3492                 switch (ret) {
3493                 case SA_DUPLICATE_NAME:
3494                         (void) printf(gettext("Resource name in use: %s\n"),
3495                             rsrcname);
3496                         break;
3497                 default:
3498                         (void) printf(gettext("Could not set: %s\n"),
3499                             sa_errorstr(ret));
3500                         break;
3501                 case SA_OK:
3502                         if (dryrun && !auth && verbose) {
3503                                 (void) printf(gettext(
3504                                     "Command would fail: %s\n"),
3505                                     sa_errorstr(SA_NO_PERMISSION));
3506                         }
3507                         break;
3508                 }
3509         } else {
3510                 switch (ret) {
3511                 case SA_NO_SUCH_RESOURCE:
3512                         (void) printf(gettext("Resource \"%s\" not found\n"),
3513                             rsrcname);
3514                         break;
3515                 default:
3516                         if (sharepath != NULL) {
3517                                 (void) printf(
3518                                     gettext("Share path \"%s\" not found\n"),
3519                                     sharepath);
3520                                 ret = SA_NO_SUCH_PATH;
3521                         } else {
3522                                 (void) printf(gettext("Set failed: %s\n"),
3523                                     sa_errorstr(ret));
3524                         }
3525                 }
3526         }
3527 
3528         return (ret);
3529 }
3530 
3531 /*
3532  * add_security(group, sectype, optlist, proto, *err)
3533  *
3534  * Helper function to add a security option (named optionset) to the
3535  * group.
3536  */
3537 
3538 static int
3539 add_security(sa_group_t group, char *sectype,
3540     struct options *optlist, char *proto, int *err)
3541 {
3542         sa_security_t security;
3543         int ret = SA_OK;
3544         int result = 0;
3545         sa_handle_t handle;
3546 
3547         sectype = sa_proto_space_alias(proto, sectype);
3548         security = sa_get_security(group, sectype, proto);
3549         if (security == NULL)
3550                 security = sa_create_security(group, sectype, proto);
3551 
3552         if (sectype != NULL)
3553                 sa_free_attr_string(sectype);
3554 
3555         if (security == NULL)
3556                 goto done;
3557 
3558         handle = sa_find_group_handle(group);
3559         if (handle == NULL) {
3560                 ret = SA_CONFIG_ERR;
3561                 goto done;
3562         }
3563         while (optlist != NULL) {
3564                 sa_property_t prop;
3565                 prop = sa_get_property(security, optlist->optname);
3566                 if (prop == NULL) {
3567                         /*
3568                          * Add the property, but only if it is
3569                          * a non-NULL or non-zero length value
3570                          */
3571                         if (optlist->optvalue != NULL) {
3572                                 prop = sa_create_property(optlist->optname,
3573                                     optlist->optvalue);
3574                                 if (prop != NULL) {
3575                                         ret = sa_valid_property(handle,
3576                                             security, proto, prop);
3577                                         if (ret != SA_OK) {
3578                                                 (void) sa_remove_property(prop);
3579                                                 (void) printf(gettext(
3580                                                     "Could not add "
3581                                                     "property %s: %s\n"),
3582                                                     optlist->optname,
3583                                                     sa_errorstr(ret));
3584                                         }
3585                                         if (ret == SA_OK) {
3586                                                 ret = sa_add_property(security,
3587                                                     prop);
3588                                                 if (ret != SA_OK) {
3589                                                         (void) printf(gettext(
3590                                                             "Could not add "
3591                                                             "property (%s=%s):"
3592                                                             " %s\n"),
3593                                                             optlist->optname,
3594                                                             optlist->optvalue,
3595                                                             sa_errorstr(ret));
3596                                                 } else {
3597                                                         result = 1;
3598                                                 }
3599                                         }
3600                                 }
3601                         }
3602                 } else {
3603                         ret = sa_update_property(prop, optlist->optvalue);
3604                         result = 1; /* should check if really changed */
3605                 }
3606                 optlist = optlist->next;
3607         }
3608         /*
3609          * When done, properties may have all been removed but
3610          * we need to keep the security type itself until
3611          * explicitly removed.
3612          */
3613         if (result)
3614                 ret = sa_commit_properties(security, 0);
3615 done:
3616         *err = ret;
3617         return (result);
3618 }
3619 
3620 /*
3621  * zfscheck(group, share)
3622  *
3623  * For the special case where a share was provided, make sure it is a
3624  * compatible path for a ZFS property change.  The only path
3625  * acceptable is the path that defines the zfs sub-group (dataset with
3626  * the sharenfs property set) and not one of the paths that inherited
3627  * the NFS properties. Returns SA_OK if it is usable and
3628  * SA_NOT_ALLOWED if it isn't.
3629  *
3630  * If group is not a ZFS group/subgroup, we assume OK since the check
3631  * on return will catch errors for those cases.  What we are looking
3632  * for here is that the group is ZFS and the share is not the defining
3633  * share.  All else is SA_OK.
3634  */
3635 
3636 static int
3637 zfscheck(sa_group_t group, sa_share_t share)
3638 {
3639         int ret = SA_OK;
3640         char *attr;
3641 
3642         if (sa_group_is_zfs(group)) {
3643                 /*
3644                  * The group is a ZFS group.  Does the share represent
3645                  * the dataset that defined the group? It is only OK
3646                  * if the attribute "subgroup" exists on the share and
3647                  * has a value of "true".
3648                  */
3649 
3650                 ret = SA_NOT_ALLOWED;
3651                 attr = sa_get_share_attr(share, "subgroup");
3652                 if (attr != NULL) {
3653                         if (strcmp(attr, "true") == 0)
3654                                 ret = SA_OK;
3655                         sa_free_attr_string(attr);
3656                 }
3657         }
3658         return (ret);
3659 }
3660 
3661 /*
3662  * basic_set(groupname, optlist, protocol, sharepath, rsrcname, dryrun)
3663  *
3664  * This function implements "set" when a name space (-S) is not
3665  * specified. It is a basic set. Options and other CLI parsing has
3666  * already been done.
3667  *
3668  * "rsrcname" is a "resource name". If it is non-NULL, it must match
3669  * the sharepath if present or group if present, otherwise it is used
3670  * to set options.
3671  *
3672  * Resource names may take options if the protocol supports it. If the
3673  * protocol doesn't support resource level options, rsrcname is just
3674  * an alias for the share.
3675  */
3676 
3677 static int
3678 basic_set(sa_handle_t handle, char *groupname, struct options *optlist,
3679     char *protocol, char *sharepath, char *rsrcname, int dryrun)
3680 {
3681         sa_group_t group;
3682         int ret = SA_OK;
3683         int change = 0;
3684         struct list *worklist = NULL;
3685 
3686         group = sa_get_group(handle, groupname);
3687         if (group != NULL) {
3688                 sa_share_t share = NULL;
3689                 sa_resource_t resource = NULL;
3690 
3691                 /*
3692                  * If there is a sharepath, make sure it belongs to
3693                  * the group.
3694                  */
3695                 if (sharepath != NULL) {
3696                         share = sa_get_share(group, sharepath);
3697                         if (share == NULL) {
3698                                 (void) printf(gettext(
3699                                     "Share does not exist in group %s\n"),
3700                                     groupname, sharepath);
3701                                 ret = SA_NO_SUCH_PATH;
3702                         } else {
3703                                 /* if ZFS and OK, then only group */
3704                                 ret = zfscheck(group, share);
3705                                 if (ret == SA_OK &&
3706                                     sa_group_is_zfs(group))
3707                                         share = NULL;
3708                                 if (ret == SA_NOT_ALLOWED)
3709                                         (void) printf(gettext(
3710                                             "Properties on ZFS group shares "
3711                                             "not supported: %s\n"), sharepath);
3712                         }
3713                 }
3714 
3715                 /*
3716                  * If a resource name exists, make sure it belongs to
3717                  * the share if present else it belongs to the
3718                  * group. Also check the protocol to see if it
3719                  * supports resource level properties or not. If not,
3720                  * use share only.
3721                  */
3722                 if (rsrcname != NULL) {
3723                         if (share != NULL) {
3724                                 resource = sa_get_share_resource(share,
3725                                     rsrcname);
3726                                 if (resource == NULL)
3727                                         ret = SA_NO_SUCH_RESOURCE;
3728                         } else {
3729                                 resource = sa_get_resource(group, rsrcname);
3730                                 if (resource != NULL)
3731                                         share = sa_get_resource_parent(
3732                                             resource);
3733                                 else
3734                                         ret = SA_NO_SUCH_RESOURCE;
3735                         }
3736                         if (ret == SA_OK && resource != NULL) {
3737                                 uint64_t features;
3738                                 /*
3739                                  * Check to see if the resource can take
3740                                  * properties. If so, stick the resource into
3741                                  * "share" so it will all just work.
3742                                  */
3743                                 features = sa_proto_get_featureset(protocol);
3744                                 if (features & SA_FEATURE_RESOURCE)
3745                                         share = (sa_share_t)resource;
3746                         }
3747                 }
3748 
3749                 if (ret == SA_OK) {
3750                         /* group must exist */
3751                         ret = valid_options(handle, optlist, protocol,
3752                             share == NULL ? group : share, NULL);
3753                         if (ret == SA_OK && !dryrun) {
3754                                 if (share != NULL)
3755                                         change |= add_optionset(share, optlist,
3756                                             protocol, &ret);
3757                                 else
3758                                         change |= add_optionset(group, optlist,
3759                                             protocol, &ret);
3760                                 if (ret == SA_OK && change)
3761                                         worklist = add_list(worklist, group,
3762                                             share, protocol);
3763                         }
3764                 }
3765                 free_opt(optlist);
3766         } else {
3767                 (void) printf(gettext("Group \"%s\" not found\n"), groupname);
3768                 ret = SA_NO_SUCH_GROUP;
3769         }
3770         /*
3771          * we have a group and potentially legal additions
3772          */
3773 
3774         /*
3775          * Commit to configuration if not a dryrunp and properties
3776          * have changed.
3777          */
3778         if (!dryrun && ret == SA_OK && change && worklist != NULL)
3779                 /* properties changed, so update all shares */
3780                 (void) enable_all_groups(handle, worklist, 0, 0, protocol,
3781                     B_TRUE);
3782 
3783         if (worklist != NULL)
3784                 free_list(worklist);
3785         return (ret);
3786 }
3787 
3788 /*
3789  * space_set(groupname, optlist, protocol, sharepath, dryrun)
3790  *
3791  * This function implements "set" when a name space (-S) is
3792  * specified. It is a namespace set. Options and other CLI parsing has
3793  * already been done.
3794  */
3795 
3796 static int
3797 space_set(sa_handle_t handle, char *groupname, struct options *optlist,
3798     char *protocol, char *sharepath, int dryrun, char *sectype)
3799 {
3800         sa_group_t group;
3801         int ret = SA_OK;
3802         int change = 0;
3803         struct list *worklist = NULL;
3804 
3805         /*
3806          * make sure protcol and sectype are valid
3807          */
3808 
3809         if (sa_proto_valid_space(protocol, sectype) == 0) {
3810                 (void) printf(gettext("Option space \"%s\" not valid "
3811                     "for protocol.\n"), sectype);
3812                 return (SA_INVALID_SECURITY);
3813         }
3814 
3815         group = sa_get_group(handle, groupname);
3816         if (group != NULL) {
3817                 sa_share_t share = NULL;
3818                 if (sharepath != NULL) {
3819                         share = sa_get_share(group, sharepath);
3820                         if (share == NULL) {
3821                                 (void) printf(gettext(
3822                                     "Share does not exist in group %s\n"),
3823                                     groupname, sharepath);
3824                                 ret = SA_NO_SUCH_PATH;
3825                         } else {
3826                                 /* if ZFS and OK, then only group */
3827                                 ret = zfscheck(group, share);
3828                                 if (ret == SA_OK &&
3829                                     sa_group_is_zfs(group))
3830                                         share = NULL;
3831                                 if (ret == SA_NOT_ALLOWED)
3832                                         (void) printf(gettext(
3833                                             "Properties on ZFS group shares "
3834                                             "not supported: %s\n"), sharepath);
3835                         }
3836                 }
3837                 if (ret == SA_OK) {
3838                         /* group must exist */
3839                         ret = valid_options(handle, optlist, protocol,
3840                             share == NULL ? group : share, sectype);
3841                         if (ret == SA_OK && !dryrun) {
3842                                 if (share != NULL)
3843                                         change = add_security(share, sectype,
3844                                             optlist, protocol, &ret);
3845                                 else
3846                                         change = add_security(group, sectype,
3847                                             optlist, protocol, &ret);
3848                                 if (ret != SA_OK)
3849                                         (void) printf(gettext(
3850                                             "Could not set property: %s\n"),
3851                                             sa_errorstr(ret));
3852                         }
3853                         if (ret == SA_OK && change)
3854                                 worklist = add_list(worklist, group, share,
3855                                     protocol);
3856                 }
3857                 free_opt(optlist);
3858         } else {
3859                 (void) printf(gettext("Group \"%s\" not found\n"), groupname);
3860                 ret = SA_NO_SUCH_GROUP;
3861         }
3862 
3863         /*
3864          * We have a group and potentially legal additions.
3865          */
3866 
3867         /* Commit to configuration if not a dryrun */
3868         if (!dryrun && ret == 0) {
3869                 if (change && worklist != NULL) {
3870                         /* properties changed, so update all shares */
3871                         (void) enable_all_groups(handle, worklist, 0, 0,
3872                             protocol, B_TRUE);
3873                 }
3874                 ret = sa_update_config(handle);
3875         }
3876         if (worklist != NULL)
3877                 free_list(worklist);
3878         return (ret);
3879 }
3880 
3881 /*
3882  * sa_set(flags, argc, argv)
3883  *
3884  * Implements the set subcommand. It keys off of -S to determine which
3885  * set of operations to actually do.
3886  */
3887 
3888 int
3889 sa_set(sa_handle_t handle, int flags, int argc, char *argv[])
3890 {
3891         char *groupname;
3892         int verbose = 0;
3893         int dryrun = 0;
3894         int c;
3895         char *protocol = NULL;
3896         int ret = SA_OK;
3897         struct options *optlist = NULL;
3898         char *rsrcname = NULL;
3899         char *sharepath = NULL;
3900         char *optset = NULL;
3901         int auth;
3902 
3903         while ((c = getopt(argc, argv, "?hvnP:p:r:s:S:")) != EOF) {
3904                 switch (c) {
3905                 case 'v':
3906                         verbose++;
3907                         break;
3908                 case 'n':
3909                         dryrun++;
3910                         break;
3911                 case 'P':
3912                         if (protocol != NULL) {
3913                                 (void) printf(gettext(
3914                                     "Specifying multiple protocols "
3915                                     "not supported: %s\n"), protocol);
3916                                 return (SA_SYNTAX_ERR);
3917                         }
3918                         protocol = optarg;
3919                         if (!sa_valid_protocol(protocol)) {
3920                                 (void) printf(gettext(
3921                                     "Invalid protocol specified: %s\n"),
3922                                     protocol);
3923                                 return (SA_INVALID_PROTOCOL);
3924                         }
3925                         break;
3926                 case 'p':
3927                         ret = add_opt(&optlist, optarg, 0);
3928                         switch (ret) {
3929                         case OPT_ADD_SYNTAX:
3930                                 (void) printf(gettext("Property syntax error:"
3931                                     " %s\n"), optarg);
3932                                 return (SA_SYNTAX_ERR);
3933                         case OPT_ADD_MEMORY:
3934                                 (void) printf(gettext("No memory to set "
3935                                     "property: %s\n"), optarg);
3936                                 return (SA_NO_MEMORY);
3937                         default:
3938                                 break;
3939                         }
3940                         break;
3941                 case 'r':
3942                         if (rsrcname != NULL) {
3943                                 (void) printf(gettext(
3944                                     "Setting multiple resource names not"
3945                                     " supported\n"));
3946                                 return (SA_SYNTAX_ERR);
3947                         }
3948                         rsrcname = optarg;
3949                         break;
3950                 case 's':
3951                         if (sharepath != NULL) {
3952                                 (void) printf(gettext(
3953                                     "Setting multiple shares not supported\n"));
3954                                 return (SA_SYNTAX_ERR);
3955                         }
3956                         sharepath = optarg;
3957                         break;
3958                 case 'S':
3959                         if (optset != NULL) {
3960                                 (void) printf(gettext(
3961                                     "Specifying multiple property "
3962                                     "spaces not supported: %s\n"), optset);
3963                                 return (SA_SYNTAX_ERR);
3964                         }
3965                         optset = optarg;
3966                         break;
3967                 case 'h':
3968                         /* optopt on valid arg isn't defined */
3969                         optopt = c;
3970                         /*FALLTHROUGH*/
3971                 case '?':
3972                 default:
3973                         /*
3974                          * Since a bad option gets to here, sort it
3975                          * out and return a syntax error return value
3976                          * if necessary.
3977                          */
3978                         switch (optopt) {
3979                         default:
3980                                 ret = SA_SYNTAX_ERR;
3981                                 break;
3982                         case 'h':
3983                         case '?':
3984                                 break;
3985                         }
3986                         (void) printf(gettext("usage: %s\n"),
3987                             sa_get_usage(USAGE_SET));
3988                         return (ret);
3989                 }
3990         }
3991 
3992         if (optlist != NULL)
3993                 ret = chk_opt(optlist, optset != NULL, protocol);
3994 
3995         if (optind >= argc || (optlist == NULL && optset == NULL) ||
3996             protocol == NULL || ret != OPT_ADD_OK) {
3997                 char *sep = "\t";
3998 
3999                 (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_SET));
4000                 if (optind >= argc) {
4001                         (void) printf(gettext("%sgroup must be specified"),
4002                             sep);
4003                         sep = ", ";
4004                 }
4005                 if (optlist == NULL) {
4006                         (void) printf(gettext("%sat least one property must be"
4007                             " specified"), sep);
4008                         sep = ", ";
4009                 }
4010                 if (protocol == NULL) {
4011                         (void) printf(gettext("%sprotocol must be specified"),
4012                             sep);
4013                         sep = ", ";
4014                 }
4015                 (void) printf("\n");
4016                 ret = SA_SYNTAX_ERR;
4017         } else {
4018                 /*
4019                  * Group already exists so we can proceed after a few
4020                  * additional checks related to ZFS handling.
4021                  */
4022 
4023                 groupname = argv[optind];
4024                 if (strcmp(groupname, "zfs") == 0) {
4025                         (void) printf(gettext("Changing properties for group "
4026                             "\"zfs\" not allowed\n"));
4027                         return (SA_NOT_ALLOWED);
4028                 }
4029 
4030                 auth = check_authorizations(groupname, flags);
4031                 if (optset == NULL)
4032                         ret = basic_set(handle, groupname, optlist, protocol,
4033                             sharepath, rsrcname, dryrun);
4034                 else
4035                         ret = space_set(handle, groupname, optlist, protocol,
4036                             sharepath, dryrun, optset);
4037                 if (dryrun && ret == SA_OK && !auth && verbose) {
4038                         (void) printf(gettext("Command would fail: %s\n"),
4039                             sa_errorstr(SA_NO_PERMISSION));
4040                 }
4041         }
4042         return (ret);
4043 }
4044 
4045 /*
4046  * remove_options(group, optlist, proto, *err)
4047  *
4048  * Helper function to actually remove options from a group after all
4049  * preprocessing is done.
4050  */
4051 
4052 static int
4053 remove_options(sa_group_t group, struct options *optlist,
4054     char *proto, int *err)
4055 {
4056         struct options *cur;
4057         sa_optionset_t optionset;
4058         sa_property_t prop;
4059         int change = 0;
4060         int ret = SA_OK;
4061 
4062         optionset = sa_get_optionset(group, proto);
4063         if (optionset != NULL) {
4064                 for (cur = optlist; cur != NULL; cur = cur->next) {
4065                         prop = sa_get_property(optionset, cur->optname);
4066                         if (prop != NULL) {
4067                                 ret = sa_remove_property(prop);
4068                                 if (ret != SA_OK)
4069                                         break;
4070                                 change = 1;
4071                         }
4072                 }
4073         }
4074         if (ret == SA_OK && change)
4075                 ret = sa_commit_properties(optionset, 0);
4076 
4077         if (err != NULL)
4078                 *err = ret;
4079         return (change);
4080 }
4081 
4082 /*
4083  * valid_unset(group, optlist, proto)
4084  *
4085  * Sanity check the optlist to make sure they can be removed. Issue an
4086  * error if a property doesn't exist.
4087  */
4088 
4089 static int
4090 valid_unset(sa_group_t group, struct options *optlist, char *proto)
4091 {
4092         struct options *cur;
4093         sa_optionset_t optionset;
4094         sa_property_t prop;
4095         int ret = SA_OK;
4096 
4097         optionset = sa_get_optionset(group, proto);
4098         if (optionset != NULL) {
4099                 for (cur = optlist; cur != NULL; cur = cur->next) {
4100                         prop = sa_get_property(optionset, cur->optname);
4101                         if (prop == NULL) {
4102                                 (void) printf(gettext(
4103                                     "Could not unset property %s: not set\n"),
4104                                     cur->optname);
4105                                 ret = SA_NO_SUCH_PROP;
4106                         }
4107                 }
4108         }
4109         return (ret);
4110 }
4111 
4112 /*
4113  * valid_unset_security(group, optlist, proto)
4114  *
4115  * Sanity check the optlist to make sure they can be removed. Issue an
4116  * error if a property doesn't exist.
4117  */
4118 
4119 static int
4120 valid_unset_security(sa_group_t group, struct options *optlist, char *proto,
4121     char *sectype)
4122 {
4123         struct options *cur;
4124         sa_security_t security;
4125         sa_property_t prop;
4126         int ret = SA_OK;
4127         char *sec;
4128 
4129         sec = sa_proto_space_alias(proto, sectype);
4130         security = sa_get_security(group, sec, proto);
4131         if (security != NULL) {
4132                 for (cur = optlist; cur != NULL; cur = cur->next) {
4133                         prop = sa_get_property(security, cur->optname);
4134                         if (prop == NULL) {
4135                                 (void) printf(gettext(
4136                                     "Could not unset property %s: not set\n"),
4137                                     cur->optname);
4138                                 ret = SA_NO_SUCH_PROP;
4139                         }
4140                 }
4141         } else {
4142                 (void) printf(gettext(
4143                     "Could not unset %s: space not defined\n"), sectype);
4144                 ret = SA_NO_SUCH_SECURITY;
4145         }
4146         if (sec != NULL)
4147                 sa_free_attr_string(sec);
4148         return (ret);
4149 }
4150 
4151 /*
4152  * remove_security(group, optlist, proto)
4153  *
4154  * Remove the properties since they were checked as valid.
4155  */
4156 
4157 static int
4158 remove_security(sa_group_t group, char *sectype,
4159     struct options *optlist, char *proto, int *err)
4160 {
4161         sa_security_t security;
4162         int ret = SA_OK;
4163         int change = 0;
4164 
4165         sectype = sa_proto_space_alias(proto, sectype);
4166         security = sa_get_security(group, sectype, proto);
4167         if (sectype != NULL)
4168                 sa_free_attr_string(sectype);
4169 
4170         if (security != NULL) {
4171                 while (optlist != NULL) {
4172                         sa_property_t prop;
4173                         prop = sa_get_property(security, optlist->optname);
4174                         if (prop != NULL) {
4175                                 ret = sa_remove_property(prop);
4176                                 if (ret != SA_OK)
4177                                         break;
4178                                 change = 1;
4179                         }
4180                         optlist = optlist->next;
4181                 }
4182                 /*
4183                  * when done, properties may have all been removed but
4184                  * we need to keep the security type itself until
4185                  * explicitly removed.
4186                  */
4187                 if (ret == SA_OK && change)
4188                         ret = sa_commit_properties(security, 0);
4189         } else {
4190                 ret = SA_NO_SUCH_PROP;
4191         }
4192         if (err != NULL)
4193                 *err = ret;
4194         return (change);
4195 }
4196 
4197 /*
4198  * basic_unset(groupname, optlist, protocol, sharepath, rsrcname, dryrun)
4199  *
4200  * Unset non-named optionset properties.
4201  */
4202 
4203 static int
4204 basic_unset(sa_handle_t handle, char *groupname, struct options *optlist,
4205     char *protocol, char *sharepath, char *rsrcname, int dryrun)
4206 {
4207         sa_group_t group;
4208         int ret = SA_OK;
4209         int change = 0;
4210         struct list *worklist = NULL;
4211         sa_share_t share = NULL;
4212         sa_resource_t resource = NULL;
4213 
4214         group = sa_get_group(handle, groupname);
4215         if (group == NULL)
4216                 return (ret);
4217 
4218         /*
4219          * If there is a sharepath, make sure it belongs to
4220          * the group.
4221          */
4222         if (sharepath != NULL) {
4223                 share = sa_get_share(group, sharepath);
4224                 if (share == NULL) {
4225                         (void) printf(gettext(
4226                             "Share does not exist in group %s\n"),
4227                             groupname, sharepath);
4228                         ret = SA_NO_SUCH_PATH;
4229                 }
4230         }
4231         /*
4232          * If a resource name exists, make sure it belongs to
4233          * the share if present else it belongs to the
4234          * group. Also check the protocol to see if it
4235          * supports resource level properties or not. If not,
4236          * use share only.
4237          */
4238         if (rsrcname != NULL) {
4239                 if (share != NULL) {
4240                         resource = sa_get_share_resource(share, rsrcname);
4241                         if (resource == NULL)
4242                                 ret = SA_NO_SUCH_RESOURCE;
4243                 } else {
4244                         resource = sa_get_resource(group, rsrcname);
4245                         if (resource != NULL) {
4246                                 share = sa_get_resource_parent(resource);
4247                         } else {
4248                                 ret = SA_NO_SUCH_RESOURCE;
4249                         }
4250                 }
4251                 if (ret == SA_OK && resource != NULL) {
4252                         uint64_t features;
4253                         /*
4254                          * Check to see if the resource can take
4255                          * properties. If so, stick the resource into
4256                          * "share" so it will all just work.
4257                          */
4258                         features = sa_proto_get_featureset(protocol);
4259                         if (features & SA_FEATURE_RESOURCE)
4260                                 share = (sa_share_t)resource;
4261                 }
4262         }
4263 
4264         if (ret == SA_OK) {
4265                 /* group must exist */
4266                 ret = valid_unset(share != NULL ? share : group,
4267                     optlist, protocol);
4268                 if (ret == SA_OK && !dryrun) {
4269                         if (share != NULL) {
4270                                 sa_optionset_t optionset;
4271                                 sa_property_t prop;
4272                                 change |= remove_options(share, optlist,
4273                                     protocol, &ret);
4274                                 /*
4275                                  * If a share optionset is
4276                                  * empty, remove it.
4277                                  */
4278                                 optionset = sa_get_optionset((sa_share_t)share,
4279                                     protocol);
4280                                 if (optionset != NULL) {
4281                                         prop = sa_get_property(optionset, NULL);
4282                                         if (prop == NULL)
4283                                                 (void) sa_destroy_optionset(
4284                                                     optionset);
4285                                 }
4286                         } else {
4287                                 change |= remove_options(group,
4288                                     optlist, protocol, &ret);
4289                         }
4290                         if (ret == SA_OK && change)
4291                                 worklist = add_list(worklist, group, share,
4292                                     protocol);
4293                         if (ret != SA_OK)
4294                                 (void) printf(gettext(
4295                                     "Could not remove properties: "
4296                                     "%s\n"), sa_errorstr(ret));
4297                 }
4298         } else {
4299                 (void) printf(gettext("Group \"%s\" not found\n"), groupname);
4300                 ret = SA_NO_SUCH_GROUP;
4301         }
4302         free_opt(optlist);
4303 
4304         /*
4305          * We have a group and potentially legal additions
4306          *
4307          * Commit to configuration if not a dryrun
4308          */
4309         if (!dryrun && ret == SA_OK) {
4310                 if (change && worklist != NULL) {
4311                         /* properties changed, so update all shares */
4312                         (void) enable_all_groups(handle, worklist, 0, 0,
4313                             protocol, B_TRUE);
4314                 }
4315         }
4316         if (worklist != NULL)
4317                 free_list(worklist);
4318         return (ret);
4319 }
4320 
4321 /*
4322  * space_unset(groupname, optlist, protocol, sharepath, dryrun)
4323  *
4324  * Unset named optionset properties.
4325  */
4326 static int
4327 space_unset(sa_handle_t handle, char *groupname, struct options *optlist,
4328     char *protocol, char *sharepath, int dryrun, char *sectype)
4329 {
4330         sa_group_t group;
4331         int ret = SA_OK;
4332         int change = 0;
4333         struct list *worklist = NULL;
4334         sa_share_t share = NULL;
4335 
4336         group = sa_get_group(handle, groupname);
4337         if (group == NULL) {
4338                 (void) printf(gettext("Group \"%s\" not found\n"), groupname);
4339                 return (SA_NO_SUCH_GROUP);
4340         }
4341         if (sharepath != NULL) {
4342                 share = sa_get_share(group, sharepath);
4343                 if (share == NULL) {
4344                         (void) printf(gettext(
4345                             "Share does not exist in group %s\n"),
4346                             groupname, sharepath);
4347                         return (SA_NO_SUCH_PATH);
4348                 }
4349         }
4350         ret = valid_unset_security(share != NULL ? share : group,
4351             optlist, protocol, sectype);
4352 
4353         if (ret == SA_OK && !dryrun) {
4354                 if (optlist != NULL) {
4355                         if (share != NULL) {
4356                                 sa_security_t optionset;
4357                                 sa_property_t prop;
4358                                 change = remove_security(share,
4359                                     sectype, optlist, protocol, &ret);
4360 
4361                                 /* If a share security is empty, remove it */
4362                                 optionset = sa_get_security((sa_group_t)share,
4363                                     sectype, protocol);
4364                                 if (optionset != NULL) {
4365                                         prop = sa_get_property(optionset,
4366                                             NULL);
4367                                         if (prop == NULL)
4368                                                 ret = sa_destroy_security(
4369                                                     optionset);
4370                                 }
4371                         } else {
4372                                 change = remove_security(group, sectype,
4373                                     optlist, protocol, &ret);
4374                         }
4375                 } else {
4376                         sa_security_t security;
4377                         char *sec;
4378                         sec = sa_proto_space_alias(protocol, sectype);
4379                         security = sa_get_security(group, sec, protocol);
4380                         if (sec != NULL)
4381                                 sa_free_attr_string(sec);
4382                         if (security != NULL) {
4383                                 ret = sa_destroy_security(security);
4384                                 if (ret == SA_OK)
4385                                         change = 1;
4386                         } else {
4387                                 ret = SA_NO_SUCH_PROP;
4388                         }
4389                 }
4390                 if (ret != SA_OK)
4391                         (void) printf(gettext("Could not unset property: %s\n"),
4392                             sa_errorstr(ret));
4393         }
4394 
4395         if (ret == SA_OK && change)
4396                 worklist = add_list(worklist, group, 0, protocol);
4397 
4398         free_opt(optlist);
4399         /*
4400          * We have a group and potentially legal additions
4401          */
4402 
4403         /* Commit to configuration if not a dryrun */
4404         if (!dryrun && ret == 0) {
4405                 /* properties changed, so update all shares */
4406                 if (change && worklist != NULL)
4407                         (void) enable_all_groups(handle, worklist, 0, 0,
4408                             protocol, B_TRUE);
4409                 ret = sa_update_config(handle);
4410         }
4411         if (worklist != NULL)
4412                 free_list(worklist);
4413         return (ret);
4414 }
4415 
4416 /*
4417  * sa_unset(flags, argc, argv)
4418  *
4419  * Implements the unset subcommand. Parsing done here and then basic
4420  * or space versions of the real code are called.
4421  */
4422 
4423 int
4424 sa_unset(sa_handle_t handle, int flags, int argc, char *argv[])
4425 {
4426         char *groupname;
4427         int verbose = 0;
4428         int dryrun = 0;
4429         int c;
4430         char *protocol = NULL;
4431         int ret = SA_OK;
4432         struct options *optlist = NULL;
4433         char *rsrcname = NULL;
4434         char *sharepath = NULL;
4435         char *optset = NULL;
4436         int auth;
4437 
4438         while ((c = getopt(argc, argv, "?hvnP:p:r:s:S:")) != EOF) {
4439                 switch (c) {
4440                 case 'v':
4441                         verbose++;
4442                         break;
4443                 case 'n':
4444                         dryrun++;
4445                         break;
4446                 case 'P':
4447                         if (protocol != NULL) {
4448                                 (void) printf(gettext(
4449                                     "Specifying multiple protocols "
4450                                     "not supported: %s\n"), protocol);
4451                                 return (SA_SYNTAX_ERR);
4452                         }
4453                         protocol = optarg;
4454                         if (!sa_valid_protocol(protocol)) {
4455                                 (void) printf(gettext(
4456                                     "Invalid protocol specified: %s\n"),
4457                                     protocol);
4458                                 return (SA_INVALID_PROTOCOL);
4459                         }
4460                         break;
4461                 case 'p':
4462                         ret = add_opt(&optlist, optarg, 1);
4463                         switch (ret) {
4464                         case OPT_ADD_SYNTAX:
4465                                 (void) printf(gettext("Property syntax error "
4466                                     "for property %s\n"), optarg);
4467                                 return (SA_SYNTAX_ERR);
4468 
4469                         case OPT_ADD_PROPERTY:
4470                                 (void) printf(gettext("Properties need to be "
4471                                     "set with set command: %s\n"), optarg);
4472                                 return (SA_SYNTAX_ERR);
4473 
4474                         default:
4475                                 break;
4476                         }
4477                         break;
4478                 case 'r':
4479                         /*
4480                          * Unset properties on resource if applicable or on
4481                          * share if resource for this protocol doesn't use
4482                          * resources.
4483                          */
4484                         if (rsrcname != NULL) {
4485                                 (void) printf(gettext(
4486                                     "Unsetting multiple resource "
4487                                     "names not supported\n"));
4488                                 return (SA_SYNTAX_ERR);
4489                         }
4490                         rsrcname = optarg;
4491                         break;
4492                 case 's':
4493                         if (sharepath != NULL) {
4494                                 (void) printf(gettext(
4495                                     "Adding multiple shares not supported\n"));
4496                                 return (SA_SYNTAX_ERR);
4497                         }
4498                         sharepath = optarg;
4499                         break;
4500                 case 'S':
4501                         if (optset != NULL) {
4502                                 (void) printf(gettext(
4503                                     "Specifying multiple property "
4504                                     "spaces not supported: %s\n"), optset);
4505                                 return (SA_SYNTAX_ERR);
4506                         }
4507                         optset = optarg;
4508                         break;
4509                 case 'h':
4510                         /* optopt on valid arg isn't defined */
4511                         optopt = c;
4512                         /*FALLTHROUGH*/
4513                 case '?':
4514                 default:
4515                         /*
4516                          * Since a bad option gets to here, sort it
4517                          * out and return a syntax error return value
4518                          * if necessary.
4519                          */
4520                         switch (optopt) {
4521                         default:
4522                                 ret = SA_SYNTAX_ERR;
4523                                 break;
4524                         case 'h':
4525                         case '?':
4526                                 break;
4527                         }
4528                         (void) printf(gettext("usage: %s\n"),
4529                             sa_get_usage(USAGE_UNSET));
4530                         return (ret);
4531                 }
4532         }
4533 
4534         if (optlist != NULL)
4535                 ret = chk_opt(optlist, optset != NULL, protocol);
4536 
4537         if (optind >= argc || (optlist == NULL && optset == NULL) ||
4538             protocol == NULL) {
4539                 char *sep = "\t";
4540                 (void) printf(gettext("usage: %s\n"),
4541                     sa_get_usage(USAGE_UNSET));
4542                 if (optind >= argc) {
4543                         (void) printf(gettext("%sgroup must be specified"),
4544                             sep);
4545                         sep = ", ";
4546                 }
4547                 if (optlist == NULL) {
4548                         (void) printf(gettext("%sat least one property must "
4549                             "be specified"), sep);
4550                         sep = ", ";
4551                 }
4552                 if (protocol == NULL) {
4553                         (void) printf(gettext("%sprotocol must be specified"),
4554                             sep);
4555                         sep = ", ";
4556                 }
4557                 (void) printf("\n");
4558                 ret = SA_SYNTAX_ERR;
4559         } else {
4560 
4561                 /*
4562                  * If a group already exists, we can only add a new
4563                  * protocol to it and not create a new one or add the
4564                  * same protocol again.
4565                  */
4566 
4567                 groupname = argv[optind];
4568                 auth = check_authorizations(groupname, flags);
4569                 if (optset == NULL)
4570                         ret = basic_unset(handle, groupname, optlist, protocol,
4571                             sharepath, rsrcname, dryrun);
4572                 else
4573                         ret = space_unset(handle, groupname, optlist, protocol,
4574                             sharepath, dryrun, optset);
4575 
4576                 if (dryrun && ret == SA_OK && !auth && verbose)
4577                         (void) printf(gettext("Command would fail: %s\n"),
4578                             sa_errorstr(SA_NO_PERMISSION));
4579         }
4580         return (ret);
4581 }
4582 
4583 /*
4584  * sa_enable_group(flags, argc, argv)
4585  *
4586  * Implements the enable subcommand
4587  */
4588 
4589 int
4590 sa_enable_group(sa_handle_t handle, int flags, int argc, char *argv[])
4591 {
4592         int verbose = 0;
4593         int dryrun = 0;
4594         int all = 0;
4595         int c;
4596         int ret = SA_OK;
4597         char *protocol = NULL;
4598         char *state;
4599         struct list *worklist = NULL;
4600         int auth = 1;
4601         sa_group_t group;
4602 
4603         while ((c = getopt(argc, argv, "?havnP:")) != EOF) {
4604                 switch (c) {
4605                 case 'a':
4606                         all = 1;
4607                         break;
4608                 case 'n':
4609                         dryrun++;
4610                         break;
4611                 case 'P':
4612                         if (protocol != NULL) {
4613                                 (void) printf(gettext(
4614                                     "Specifying multiple protocols "
4615                                     "not supported: %s\n"), protocol);
4616                                 return (SA_SYNTAX_ERR);
4617                         }
4618                         protocol = optarg;
4619                         if (!sa_valid_protocol(protocol)) {
4620                                 (void) printf(gettext(
4621                                     "Invalid protocol specified: %s\n"),
4622                                     protocol);
4623                                 return (SA_INVALID_PROTOCOL);
4624                         }
4625                         break;
4626                 case 'v':
4627                         verbose++;
4628                         break;
4629                 case 'h':
4630                         /* optopt on valid arg isn't defined */
4631                         optopt = c;
4632                         /*FALLTHROUGH*/
4633                 case '?':
4634                 default:
4635                         /*
4636                          * Since a bad option gets to here, sort it
4637                          * out and return a syntax error return value
4638                          * if necessary.
4639                          */
4640                         switch (optopt) {
4641                         default:
4642                                 ret = SA_SYNTAX_ERR;
4643                                 break;
4644                         case 'h':
4645                         case '?':
4646                                 (void) printf(gettext("usage: %s\n"),
4647                                     sa_get_usage(USAGE_ENABLE));
4648                                 return (ret);
4649                         }
4650                 }
4651         }
4652 
4653         if (optind == argc && !all) {
4654                 (void) printf(gettext("usage: %s\n"),
4655                     sa_get_usage(USAGE_ENABLE));
4656                 (void) printf(gettext("\tmust specify group\n"));
4657                 return (SA_NO_SUCH_PATH);
4658         }
4659         if (!all) {
4660                 while (optind < argc) {
4661                         group = sa_get_group(handle, argv[optind]);
4662                         if (group != NULL) {
4663                                 auth &= check_authorizations(argv[optind],
4664                                     flags);
4665                                 state = sa_get_group_attr(group, "state");
4666                                 if (state != NULL &&
4667                                     strcmp(state, "enabled") == 0) {
4668                                         /* already enabled */
4669                                         if (verbose)
4670                                                 (void) printf(gettext(
4671                                                     "Group \"%s\" is already "
4672                                                     "enabled\n"),
4673                                                     argv[optind]);
4674                                         ret = SA_BUSY; /* already enabled */
4675                                 } else {
4676                                         worklist = add_list(worklist, group,
4677                                             0, protocol);
4678                                         if (verbose)
4679                                                 (void) printf(gettext(
4680                                                     "Enabling group \"%s\"\n"),
4681                                                     argv[optind]);
4682                                 }
4683                                 if (state != NULL)
4684                                         sa_free_attr_string(state);
4685                         } else {
4686                                 ret = SA_NO_SUCH_GROUP;
4687                         }
4688                         optind++;
4689                 }
4690         } else {
4691                 for (group = sa_get_group(handle, NULL);
4692                     group != NULL;
4693                     group = sa_get_next_group(group)) {
4694                         worklist = add_list(worklist, group, 0, protocol);
4695                 }
4696         }
4697         if (!dryrun && ret == SA_OK)
4698                 ret = enable_all_groups(handle, worklist, 1, 0, NULL, B_FALSE);
4699 
4700         if (ret != SA_OK && ret != SA_BUSY)
4701                 (void) printf(gettext("Could not enable group: %s\n"),
4702                     sa_errorstr(ret));
4703         if (ret == SA_BUSY)
4704                 ret = SA_OK;
4705 
4706         if (worklist != NULL)
4707                 free_list(worklist);
4708         if (dryrun && ret == SA_OK && !auth && verbose) {
4709                 (void) printf(gettext("Command would fail: %s\n"),
4710                     sa_errorstr(SA_NO_PERMISSION));
4711         }
4712         return (ret);
4713 }
4714 
4715 /*
4716  * disable_group(group, proto)
4717  *
4718  * Disable all the shares in the specified group.. This is a helper
4719  * for disable_all_groups in order to simplify regular and subgroup
4720  * (zfs) disabling. Group has already been checked for non-NULL.
4721  */
4722 
4723 static int
4724 disable_group(sa_group_t group, char *proto)
4725 {
4726         sa_share_t share;
4727         int ret = SA_OK;
4728 
4729         /*
4730          * If the protocol isn't enabled, skip it and treat as
4731          * successful.
4732          */
4733         if (!has_protocol(group, proto))
4734                 return (ret);
4735 
4736         for (share = sa_get_share(group, NULL);
4737             share != NULL && ret == SA_OK;
4738             share = sa_get_next_share(share)) {
4739                 ret = sa_disable_share(share, proto);
4740                 if (ret == SA_NO_SUCH_PATH) {
4741                         /*
4742                          * this is OK since the path is gone. we can't
4743                          * re-share it anyway so no error.
4744                          */
4745                         ret = SA_OK;
4746                 }
4747         }
4748         return (ret);
4749 }
4750 
4751 /*
4752  * disable_all_groups(work, setstate)
4753  *
4754  * helper function that disables the shares in the list of groups
4755  * provided. It optionally marks the group as disabled. Used by both
4756  * enable and start subcommands.
4757  */
4758 
4759 static int
4760 disable_all_groups(sa_handle_t handle, struct list *work, int setstate)
4761 {
4762         int ret = SA_OK;
4763         sa_group_t subgroup, group;
4764 
4765         while (work != NULL && ret == SA_OK) {
4766                 group = (sa_group_t)work->item;
4767                 if (setstate)
4768                         ret = sa_set_group_attr(group, "state", "disabled");
4769                 if (ret == SA_OK) {
4770                         char *name;
4771                         name = sa_get_group_attr(group, "name");
4772                         if (name != NULL && strcmp(name, "zfs") == 0) {
4773                                 /* need to get the sub-groups for stopping */
4774                                 for (subgroup = sa_get_sub_group(group);
4775                                     subgroup != NULL;
4776                                     subgroup = sa_get_next_group(subgroup)) {
4777                                         ret = disable_group(subgroup,
4778                                             work->proto);
4779                                 }
4780                         } else {
4781                                 ret = disable_group(group, work->proto);
4782                         }
4783                         if (name != NULL)
4784                                 sa_free_attr_string(name);
4785                         /*
4786                          * We don't want to "disable" since it won't come
4787                          * up after a reboot.  The SMF framework should do
4788                          * the right thing. On enable we do want to do
4789                          * something.
4790                          */
4791                 }
4792                 work = work->next;
4793         }
4794         if (ret == SA_OK)
4795                 ret = sa_update_config(handle);
4796         return (ret);
4797 }
4798 
4799 /*
4800  * sa_disable_group(flags, argc, argv)
4801  *
4802  * Implements the disable subcommand
4803  */
4804 
4805 int
4806 sa_disable_group(sa_handle_t handle, int flags, int argc, char *argv[])
4807 {
4808         int verbose = 0;
4809         int dryrun = 0;
4810         int all = 0;
4811         int c;
4812         int ret = SA_OK;
4813         char *protocol = NULL;
4814         char *state;
4815         struct list *worklist = NULL;
4816         sa_group_t group;
4817         int auth = 1;
4818 
4819         while ((c = getopt(argc, argv, "?havn")) != EOF) {
4820                 switch (c) {
4821                 case 'a':
4822                         all = 1;
4823                         break;
4824                 case 'n':
4825                         dryrun++;
4826                         break;
4827                 case 'P':
4828                         if (protocol != NULL) {
4829                                 (void) printf(gettext(
4830                                     "Specifying multiple protocols "
4831                                     "not supported: %s\n"), protocol);
4832                                 return (SA_SYNTAX_ERR);
4833                         }
4834                         protocol = optarg;
4835                         if (!sa_valid_protocol(protocol)) {
4836                                 (void) printf(gettext(
4837                                     "Invalid protocol specified: %s\n"),
4838                                     protocol);
4839                                 return (SA_INVALID_PROTOCOL);
4840                         }
4841                         break;
4842                 case 'v':
4843                         verbose++;
4844                         break;
4845                 case 'h':
4846                         /* optopt on valid arg isn't defined */
4847                         optopt = c;
4848                         /*FALLTHROUGH*/
4849                 case '?':
4850                 default:
4851                         /*
4852                          * Since a bad option gets to here, sort it
4853                          * out and return a syntax error return value
4854                          * if necessary.
4855                          */
4856                         switch (optopt) {
4857                         default:
4858                                 ret = SA_SYNTAX_ERR;
4859                                 break;
4860                         case 'h':
4861                         case '?':
4862                                 break;
4863                         }
4864                         (void) printf(gettext("usage: %s\n"),
4865                             sa_get_usage(USAGE_DISABLE));
4866                         return (ret);
4867                 }
4868         }
4869 
4870         if (optind == argc && !all) {
4871                 (void) printf(gettext("usage: %s\n"),
4872                     sa_get_usage(USAGE_DISABLE));
4873                 (void) printf(gettext("\tmust specify group\n"));
4874                 return (SA_NO_SUCH_PATH);
4875         }
4876         if (!all) {
4877                 while (optind < argc) {
4878                         group = sa_get_group(handle, argv[optind]);
4879                         if (group != NULL) {
4880                                 auth &= check_authorizations(argv[optind],
4881                                     flags);
4882                                 state = sa_get_group_attr(group, "state");
4883                                 if (state == NULL ||
4884                                     strcmp(state, "disabled") == 0) {
4885                                         /* already disabled */
4886                                         if (verbose)
4887                                                 (void) printf(gettext(
4888                                                     "Group \"%s\" is "
4889                                                     "already disabled\n"),
4890                                                     argv[optind]);
4891                                         ret = SA_BUSY; /* already disabled */
4892                                 } else {
4893                                         worklist = add_list(worklist, group, 0,
4894                                             protocol);
4895                                         if (verbose)
4896                                                 (void) printf(gettext(
4897                                                     "Disabling group "
4898                                                     "\"%s\"\n"), argv[optind]);
4899                                 }
4900                                 if (state != NULL)
4901                                         sa_free_attr_string(state);
4902                         } else {
4903                                 ret = SA_NO_SUCH_GROUP;
4904                         }
4905                         optind++;
4906                 }
4907         } else {
4908                 for (group = sa_get_group(handle, NULL);
4909                     group != NULL;
4910                     group = sa_get_next_group(group))
4911                         worklist = add_list(worklist, group, 0, protocol);
4912         }
4913 
4914         if (ret == SA_OK && !dryrun)
4915                 ret = disable_all_groups(handle, worklist, 1);
4916         if (ret != SA_OK && ret != SA_BUSY)
4917                 (void) printf(gettext("Could not disable group: %s\n"),
4918                     sa_errorstr(ret));
4919         if (ret == SA_BUSY)
4920                 ret = SA_OK;
4921         if (worklist != NULL)
4922                 free_list(worklist);
4923         if (dryrun && ret == SA_OK && !auth && verbose)
4924                 (void) printf(gettext("Command would fail: %s\n"),
4925                     sa_errorstr(SA_NO_PERMISSION));
4926         return (ret);
4927 }
4928 
4929 /*
4930  * sa_start_group(flags, argc, argv)
4931  *
4932  * Implements the start command.
4933  * This is similar to enable except it doesn't change the state
4934  * of the group(s) and only enables shares if the group is already
4935  * enabled.
4936  */
4937 
4938 int
4939 sa_start_group(sa_handle_t handle, int flags, int argc, char *argv[])
4940 {
4941         int verbose = 0;
4942         int all = 0;
4943         int c;
4944         int ret = SMF_EXIT_OK;
4945         char *protocol = NULL;
4946         char *state;
4947         struct list *worklist = NULL;
4948         sa_group_t group;
4949 #ifdef lint
4950         flags = flags;
4951 #endif
4952 
4953         while ((c = getopt(argc, argv, "?havP:")) != EOF) {
4954                 switch (c) {
4955                 case 'a':
4956                         all = 1;
4957                         break;
4958                 case 'P':
4959                         if (protocol != NULL) {
4960                                 (void) printf(gettext(
4961                                     "Specifying multiple protocols "
4962                                     "not supported: %s\n"), protocol);
4963                                 return (SA_SYNTAX_ERR);
4964                         }
4965                         protocol = optarg;
4966                         if (!sa_valid_protocol(protocol)) {
4967                                 (void) printf(gettext(
4968                                     "Invalid protocol specified: %s\n"),
4969                                     protocol);
4970                                 return (SA_INVALID_PROTOCOL);
4971                         }
4972                         break;
4973                 case 'v':
4974                         verbose++;
4975                         break;
4976                 case 'h':
4977                         /* optopt on valid arg isn't defined */
4978                         optopt = c;
4979                         /*FALLTHROUGH*/
4980                 case '?':
4981                 default:
4982                         /*
4983                          * Since a bad option gets to here, sort it
4984                          * out and return a syntax error return value
4985                          * if necessary.
4986                          */
4987                         ret = SA_OK;
4988                         switch (optopt) {
4989                         default:
4990                                 ret = SA_SYNTAX_ERR;
4991                                 break;
4992                         case 'h':
4993                         case '?':
4994                                 break;
4995                         }
4996                         (void) printf(gettext("usage: %s\n"),
4997                             sa_get_usage(USAGE_START));
4998                         return (ret);
4999                 }
5000         }
5001 
5002         if (optind == argc && !all) {
5003                 (void) printf(gettext("usage: %s\n"),
5004                     sa_get_usage(USAGE_START));
5005                 return (SMF_EXIT_ERR_FATAL);
5006         }
5007 
5008         if (!all) {
5009                 while (optind < argc) {
5010                         group = sa_get_group(handle, argv[optind]);
5011                         if (group != NULL) {
5012                                 state = sa_get_group_attr(group, "state");
5013                                 if (state == NULL ||
5014                                     strcmp(state, "enabled") == 0) {
5015                                         worklist = add_list(worklist, group, 0,
5016                                             protocol);
5017                                         if (verbose)
5018                                                 (void) printf(gettext(
5019                                                     "Starting group \"%s\"\n"),
5020                                                     argv[optind]);
5021                                 } else {
5022                                         /*
5023                                          * Determine if there are any
5024                                          * protocols.  If there aren't any,
5025                                          * then there isn't anything to do in
5026                                          * any case so no error.
5027                                          */
5028                                         if (sa_get_optionset(group,
5029                                             protocol) != NULL) {
5030                                                 ret = SMF_EXIT_OK;
5031                                         }
5032                                 }
5033                                 if (state != NULL)
5034                                         sa_free_attr_string(state);
5035                         }
5036                         optind++;
5037                 }
5038         } else {
5039                 for (group = sa_get_group(handle, NULL);
5040                     group != NULL;
5041                     group = sa_get_next_group(group)) {
5042                         state = sa_get_group_attr(group, "state");
5043                         if (state == NULL || strcmp(state, "enabled") == 0)
5044                                 worklist = add_list(worklist, group, 0,
5045                                     protocol);
5046                         if (state != NULL)
5047                                 sa_free_attr_string(state);
5048                 }
5049         }
5050 
5051         (void) enable_all_groups(handle, worklist, 0, 1, protocol, B_FALSE);
5052 
5053         if (worklist != NULL)
5054                 free_list(worklist);
5055         return (ret);
5056 }
5057 
5058 /*
5059  * sa_stop_group(flags, argc, argv)
5060  *
5061  * Implements the stop command.
5062  * This is similar to disable except it doesn't change the state
5063  * of the group(s) and only disables shares if the group is already
5064  * enabled.
5065  */
5066 int
5067 sa_stop_group(sa_handle_t handle, int flags, int argc, char *argv[])
5068 {
5069         int verbose = 0;
5070         int all = 0;
5071         int c;
5072         int ret = SMF_EXIT_OK;
5073         char *protocol = NULL;
5074         char *state;
5075         struct list *worklist = NULL;
5076         sa_group_t group;
5077 #ifdef lint
5078         flags = flags;
5079 #endif
5080 
5081         while ((c = getopt(argc, argv, "?havP:")) != EOF) {
5082                 switch (c) {
5083                 case 'a':
5084                         all = 1;
5085                         break;
5086                 case 'P':
5087                         if (protocol != NULL) {
5088                                 (void) printf(gettext(
5089                                     "Specifying multiple protocols "
5090                                     "not supported: %s\n"), protocol);
5091                                 return (SA_SYNTAX_ERR);
5092                         }
5093                         protocol = optarg;
5094                         if (!sa_valid_protocol(protocol)) {
5095                                 (void) printf(gettext(
5096                                     "Invalid protocol specified: %s\n"),
5097                                     protocol);
5098                                 return (SA_INVALID_PROTOCOL);
5099                         }
5100                         break;
5101                 case 'v':
5102                         verbose++;
5103                         break;
5104                 case 'h':
5105                         /* optopt on valid arg isn't defined */
5106                         optopt = c;
5107                         /*FALLTHROUGH*/
5108                 case '?':
5109                 default:
5110                         /*
5111                          * Since a bad option gets to here, sort it
5112                          * out and return a syntax error return value
5113                          * if necessary.
5114                          */
5115                         ret = SA_OK;
5116                         switch (optopt) {
5117                         default:
5118                                 ret = SA_SYNTAX_ERR;
5119                                 break;
5120                         case 'h':
5121                         case '?':
5122                                 break;
5123                         }
5124                         (void) printf(gettext("usage: %s\n"),
5125                             sa_get_usage(USAGE_STOP));
5126                         return (ret);
5127                 }
5128         }
5129 
5130         if (optind == argc && !all) {
5131                 (void) printf(gettext("usage: %s\n"),
5132                     sa_get_usage(USAGE_STOP));
5133                 return (SMF_EXIT_ERR_FATAL);
5134         } else if (!all) {
5135                 while (optind < argc) {
5136                         group = sa_get_group(handle, argv[optind]);
5137                         if (group != NULL) {
5138                                 state = sa_get_group_attr(group, "state");
5139                                 if (state == NULL ||
5140                                     strcmp(state, "enabled") == 0) {
5141                                         worklist = add_list(worklist, group, 0,
5142                                             protocol);
5143                                         if (verbose)
5144                                                 (void) printf(gettext(
5145                                                     "Stopping group \"%s\"\n"),
5146                                                     argv[optind]);
5147                                 } else {
5148                                         ret = SMF_EXIT_OK;
5149                                 }
5150                                 if (state != NULL)
5151                                         sa_free_attr_string(state);
5152                         }
5153                         optind++;
5154                 }
5155         } else {
5156                 for (group = sa_get_group(handle, NULL);
5157                     group != NULL;
5158                     group = sa_get_next_group(group)) {
5159                         state = sa_get_group_attr(group, "state");
5160                         if (state == NULL || strcmp(state, "enabled") == 0)
5161                                 worklist = add_list(worklist, group, 0,
5162                                     protocol);
5163                         if (state != NULL)
5164                                 sa_free_attr_string(state);
5165                 }
5166         }
5167         (void) disable_all_groups(handle, worklist, 0);
5168         ret = sa_update_config(handle);
5169 
5170         if (worklist != NULL)
5171                 free_list(worklist);
5172         return (ret);
5173 }
5174 
5175 /*
5176  * remove_all_options(share, proto)
5177  *
5178  * Removes all options on a share.
5179  */
5180 
5181 static void
5182 remove_all_options(sa_share_t share, char *proto)
5183 {
5184         sa_optionset_t optionset;
5185         sa_security_t security;
5186         sa_security_t prevsec = NULL;
5187 
5188         optionset = sa_get_optionset(share, proto);
5189         if (optionset != NULL)
5190                 (void) sa_destroy_optionset(optionset);
5191         for (security = sa_get_security(share, NULL, NULL);
5192             security != NULL;
5193             security = sa_get_next_security(security)) {
5194                 char *type;
5195                 /*
5196                  * We walk through the list.  prevsec keeps the
5197                  * previous security so we can delete it without
5198                  * destroying the list.
5199                  */
5200                 if (prevsec != NULL) {
5201                         /* remove the previously seen security */
5202                         (void) sa_destroy_security(prevsec);
5203                         /* set to NULL so we don't try multiple times */
5204                         prevsec = NULL;
5205                 }
5206                 type = sa_get_security_attr(security, "type");
5207                 if (type != NULL) {
5208                         /*
5209                          * if the security matches the specified protocol, we
5210                          * want to remove it. prevsec holds it until either
5211                          * the next pass or we fall out of the loop.
5212                          */
5213                         if (strcmp(type, proto) == 0)
5214                                 prevsec = security;
5215                         sa_free_attr_string(type);
5216                 }
5217         }
5218         /* in case there is one left */
5219         if (prevsec != NULL)
5220                 (void) sa_destroy_security(prevsec);
5221 }
5222 
5223 
5224 /*
5225  * for legacy support, we need to handle the old syntax. This is what
5226  * we get if sharemgr is called with the name "share" rather than
5227  * sharemgr.
5228  */
5229 
5230 static int
5231 format_legacy_path(char *buff, int buffsize, char *proto, char *cmd)
5232 {
5233         int err;
5234 
5235         err = snprintf(buff, buffsize, "/usr/lib/fs/%s/%s", proto, cmd);
5236         if (err > buffsize)
5237                 return (-1);
5238         return (0);
5239 }
5240 
5241 
5242 /*
5243  * check_legacy_cmd(proto, cmd)
5244  *
5245  * Check to see if the cmd exists in /usr/lib/fs/<proto>/<cmd> and is
5246  * executable.
5247  */
5248 
5249 static int
5250 check_legacy_cmd(char *path)
5251 {
5252         struct stat st;
5253         int ret = 0;
5254 
5255         if (stat(path, &st) == 0) {
5256                 if (S_ISREG(st.st_mode) &&
5257                     st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
5258                         ret = 1;
5259         }
5260         return (ret);
5261 }
5262 
5263 /*
5264  * run_legacy_command(proto, cmd, argv)
5265  *
5266  * We know the command exists, so attempt to execute it with all the
5267  * arguments. This implements full legacy share support for those
5268  * protocols that don't have plugin providers.
5269  */
5270 
5271 static int
5272 run_legacy_command(char *path, char *argv[])
5273 {
5274         int ret;
5275 
5276         ret = execv(path, argv);
5277         if (ret < 0) {
5278                 switch (errno) {
5279                 case EACCES:
5280                         ret = SA_NO_PERMISSION;
5281                         break;
5282                 default:
5283                         ret = SA_SYSTEM_ERR;
5284                         break;
5285                 }
5286         }
5287         return (ret);
5288 }
5289 
5290 /*
5291  * out_share(out, group, proto)
5292  *
5293  * Display the share information in the format that the "share"
5294  * command has traditionally used.
5295  */
5296 
5297 static void
5298 out_share(FILE *out, sa_group_t group, char *proto)
5299 {
5300         sa_share_t share;
5301         char resfmt[128];
5302         char *defprop;
5303 
5304         /*
5305          * The original share command defaulted to displaying NFS
5306          * shares or allowed a protocol to be specified. We want to
5307          * skip those shares that are not the specified protocol.
5308          */
5309         if (proto != NULL && sa_get_optionset(group, proto) == NULL)
5310                 return;
5311 
5312         if (proto == NULL)
5313                 proto = "nfs";
5314 
5315         /*
5316          * get the default property string.  NFS uses "rw" but
5317          * everything else will use "".
5318          */
5319         if (proto != NULL && strcmp(proto, "nfs") != 0)
5320                 defprop = "\"\"";
5321         else
5322                 defprop = "rw";
5323 
5324         for (share = sa_get_share(group, NULL);
5325             share != NULL;
5326             share = sa_get_next_share(share)) {
5327                 char *path;
5328                 char *type;
5329                 char *resource;
5330                 char *description;
5331                 char *groupname;
5332                 char *sharedstate;
5333                 int shared = 1;
5334                 char *soptions;
5335                 char shareopts[MAXNAMLEN];
5336 
5337                 sharedstate = sa_get_share_attr(share, "shared");
5338                 path = sa_get_share_attr(share, "path");
5339                 type = sa_get_share_attr(share, "type");
5340                 resource = get_resource(share);
5341                 groupname = sa_get_group_attr(group, "name");
5342 
5343                 if (groupname != NULL && strcmp(groupname, "default") == 0) {
5344                         sa_free_attr_string(groupname);
5345                         groupname = NULL;
5346                 }
5347                 description = sa_get_share_description(share);
5348 
5349                 /*
5350                  * Want the sharetab version if it exists, defaulting
5351                  * to NFS if no protocol specified.
5352                  */
5353                 (void) snprintf(shareopts, MAXNAMLEN, "shareopts-%s", proto);
5354                 soptions = sa_get_share_attr(share, shareopts);
5355 
5356                 if (sharedstate == NULL)
5357                         shared = 0;
5358 
5359                 if (soptions == NULL)
5360                         soptions = sa_proto_legacy_format(proto, share, 1);
5361 
5362                 if (shared) {
5363                         /* only active shares go here */
5364                         (void) snprintf(resfmt, sizeof (resfmt), "%s%s%s",
5365                             resource != NULL ? resource : "-",
5366                             groupname != NULL ? "@" : "",
5367                             groupname != NULL ? groupname : "");
5368                         (void) fprintf(out, "%-14.14s  %s   %s   \"%s\"  \n",
5369                             resfmt, (path != NULL) ? path : "",
5370                             (soptions != NULL && strlen(soptions) > 0) ?
5371                             soptions : defprop,
5372                             (description != NULL) ? description : "");
5373                 }
5374 
5375                 if (path != NULL)
5376                         sa_free_attr_string(path);
5377                 if (type != NULL)
5378                         sa_free_attr_string(type);
5379                 if (resource != NULL)
5380                         sa_free_attr_string(resource);
5381                 if (groupname != NULL)
5382                         sa_free_attr_string(groupname);
5383                 if (description != NULL)
5384                         sa_free_share_description(description);
5385                 if (sharedstate != NULL)
5386                         sa_free_attr_string(sharedstate);
5387                 if (soptions != NULL)
5388                         sa_format_free(soptions);
5389         }
5390 }
5391 
5392 /*
5393  * output_legacy_file(out, proto)
5394  *
5395  * Walk all of the groups for the specified protocol and call
5396  * out_share() to format and write in the format displayed by the
5397  * "share" command with no arguments.
5398  */
5399 
5400 static void
5401 output_legacy_file(FILE *out, char *proto, sa_handle_t handle)
5402 {
5403         sa_group_t group;
5404 
5405         for (group = sa_get_group(handle, NULL);
5406             group != NULL;
5407             group = sa_get_next_group(group)) {
5408                 char *zfs;
5409 
5410                 /*
5411                  * Go through all the groups and ZFS
5412                  * sub-groups. out_share() will format the shares in
5413                  * the group appropriately.
5414                  */
5415 
5416                 zfs = sa_get_group_attr(group, "zfs");
5417                 if (zfs != NULL) {
5418                         sa_group_t zgroup;
5419                         sa_free_attr_string(zfs);
5420                         for (zgroup = sa_get_sub_group(group);
5421                             zgroup != NULL;
5422                             zgroup = sa_get_next_group(zgroup)) {
5423 
5424                                 /* got a group, so display it */
5425                                 out_share(out, zgroup, proto);
5426                         }
5427                 } else {
5428                         out_share(out, group, proto);
5429                 }
5430         }
5431 }
5432 
5433 int
5434 sa_legacy_share(sa_handle_t handle, int flags, int argc, char *argv[])
5435 {
5436         char *protocol = "nfs";
5437         char *options = NULL;
5438         char *description = NULL;
5439         char *groupname = NULL;
5440         char *sharepath = NULL;
5441         char *resource = NULL;
5442         char *groupstatus = NULL;
5443         int persist = SA_SHARE_TRANSIENT;
5444         int argsused = 0;
5445         int c;
5446         int ret = SA_OK;
5447         int zfs = 0;
5448         int true_legacy = 0;
5449         int curtype = SA_SHARE_TRANSIENT;
5450         char cmd[MAXPATHLEN];
5451         sa_group_t group = NULL;
5452         sa_resource_t rsrc = NULL;
5453         sa_share_t share;
5454         char dir[MAXPATHLEN];
5455         uint64_t features;
5456 #ifdef lint
5457         flags = flags;
5458 #endif
5459 
5460         while ((c = getopt(argc, argv, "?hF:d:o:p")) != EOF) {
5461                 switch (c) {
5462                 case 'd':
5463                         description = optarg;
5464                         argsused++;
5465                         break;
5466                 case 'F':
5467                         protocol = optarg;
5468                         if (!sa_valid_protocol(protocol)) {
5469                                 if (format_legacy_path(cmd, MAXPATHLEN,
5470                                     protocol, "share") == 0 &&
5471                                     check_legacy_cmd(cmd)) {
5472                                         true_legacy++;
5473                                 } else {
5474                                         (void) fprintf(stderr, gettext(
5475                                             "Invalid protocol specified: "
5476                                             "%s\n"), protocol);
5477                                         return (SA_INVALID_PROTOCOL);
5478                                 }
5479                         }
5480                         break;
5481                 case 'o':
5482                         options = optarg;
5483                         argsused++;
5484                         break;
5485                 case 'p':
5486                         persist = SA_SHARE_PERMANENT;
5487                         argsused++;
5488                         break;
5489                 case 'h':
5490                         /* optopt on valid arg isn't defined */
5491                         optopt = c;
5492                         /*FALLTHROUGH*/
5493                 case '?':
5494                 default:
5495                         /*
5496                          * Since a bad option gets to here, sort it
5497                          * out and return a syntax error return value
5498                          * if necessary.
5499                          */
5500                         switch (optopt) {
5501                         default:
5502                                 ret = SA_LEGACY_ERR;
5503                                 break;
5504                         case 'h':
5505                         case '?':
5506                                 break;
5507                         }
5508                         (void) fprintf(stderr, gettext("usage: %s\n"),
5509                             sa_get_usage(USAGE_SHARE));
5510                         return (ret);
5511                 }
5512         }
5513 
5514         /* Have the info so construct what is needed */
5515         if (!argsused && optind == argc) {
5516                 /* display current info in share format */
5517                 (void) output_legacy_file(stdout, protocol, handle);
5518                 return (ret);
5519         }
5520 
5521         /* We are modifying the configuration */
5522         if (optind == argc) {
5523                 (void) fprintf(stderr, gettext("usage: %s\n"),
5524                     sa_get_usage(USAGE_SHARE));
5525                 return (SA_LEGACY_ERR);
5526         }
5527         if (true_legacy) {
5528                 /* If still using legacy share/unshare, exec it */
5529                 ret = run_legacy_command(cmd, argv);
5530                 return (ret);
5531         }
5532 
5533         sharepath = argv[optind++];
5534         if (optind < argc) {
5535                 resource = argv[optind];
5536                 groupname = strchr(resource, '@');
5537                 if (groupname != NULL)
5538                         *groupname++ = '\0';
5539         }
5540         if (realpath(sharepath, dir) == NULL)
5541                 ret = SA_BAD_PATH;
5542         else
5543                 sharepath = dir;
5544         if (ret == SA_OK)
5545                 share = sa_find_share(handle, sharepath);
5546         else
5547                 share = NULL;
5548 
5549         features = sa_proto_get_featureset(protocol);
5550 
5551         if (groupname != NULL) {
5552                 ret = SA_NOT_ALLOWED;
5553         } else if (ret == SA_OK) {
5554                 char *legacygroup;
5555                 /*
5556                  * The legacy group is always present and zfs groups
5557                  * come and go.  zfs shares may be in sub-groups and
5558                  * the zfs share will already be in that group so it
5559                  * isn't an error. If the protocol is "smb", the group
5560                  * "smb" is used when "default" would otherwise be
5561                  * used.  "default" is NFS only and "smb" is SMB only.
5562                  */
5563                 if (strcmp(protocol, "smb") == 0)
5564                         legacygroup = "smb";
5565                 else
5566                         legacygroup = "default";
5567 
5568                 /*
5569                  * If the share exists (not NULL), then make sure it
5570                  * is one we want to handle by getting the parent
5571                  * group.
5572                  */
5573                 if (share != NULL) {
5574                         group = sa_get_parent_group(share);
5575                 } else {
5576                         group = sa_get_group(handle, legacygroup);
5577                         if (group == NULL && strcmp(legacygroup, "smb") == 0) {
5578                                 /*
5579                                  * This group may not exist, so create
5580                                  * as necessary. It only contains the
5581                                  * "smb" protocol.
5582                                  */
5583                                 group = sa_create_group(handle, legacygroup,
5584                                     &ret);
5585                                 if (group != NULL)
5586                                         (void) sa_create_optionset(group,
5587                                             protocol);
5588                         }
5589                 }
5590 
5591                 if (group == NULL) {
5592                         ret = SA_SYSTEM_ERR;
5593                         goto err;
5594                 }
5595 
5596                 groupstatus = group_status(group);
5597                 if (share == NULL) {
5598                         share = sa_add_share(group, sharepath,
5599                             persist, &ret);
5600                         if (share == NULL &&
5601                             ret == SA_DUPLICATE_NAME) {
5602                                 /*
5603                                  * Could be a ZFS path being started
5604                                  */
5605                                 if (sa_zfs_is_shared(handle,
5606                                     sharepath)) {
5607                                         ret = SA_OK;
5608                                         group = sa_get_group(handle,
5609                                             "zfs");
5610                                         if (group == NULL) {
5611                                                 /*
5612                                                  * This shouldn't
5613                                                  * happen.
5614                                                  */
5615                                                 ret = SA_CONFIG_ERR;
5616                                         } else {
5617                                                 share = sa_add_share(
5618                                                     group, sharepath,
5619                                                     persist, &ret);
5620                                         }
5621                                 }
5622                         }
5623                 } else {
5624                         char *type;
5625                         /*
5626                          * May want to change persist state, but the
5627                          * important thing is to change options. We
5628                          * need to change them regardless of the
5629                          * source.
5630                          */
5631 
5632                         if (sa_zfs_is_shared(handle, sharepath)) {
5633                                 zfs = 1;
5634                         }
5635                         remove_all_options(share, protocol);
5636                         type = sa_get_share_attr(share, "type");
5637                         if (type != NULL &&
5638                             strcmp(type, "transient") != 0) {
5639                                 curtype = SA_SHARE_PERMANENT;
5640                         }
5641                         if (type != NULL)
5642                                 sa_free_attr_string(type);
5643                         if (curtype != persist) {
5644                                 (void) sa_set_share_attr(share, "type",
5645                                     persist == SA_SHARE_PERMANENT ?
5646                                     "persist" : "transient");
5647                         }
5648                 }
5649 
5650                 /*
5651                  * If there is a resource name, we may
5652                  * actually care about it if this is share for
5653                  * a protocol that uses resource level sharing
5654                  * (SMB). We need to find the resource and, if
5655                  * it exists, make sure it belongs to the
5656                  * current share. If it doesn't exist, attempt
5657                  * to create it.
5658                  */
5659 
5660                 if (ret == SA_OK && resource != NULL) {
5661                         rsrc = sa_find_resource(handle, resource);
5662                         if (rsrc != NULL) {
5663                                 if (share != sa_get_resource_parent(rsrc))
5664                                         ret = SA_DUPLICATE_NAME;
5665                         } else {
5666                                 rsrc = sa_add_resource(share, resource,
5667                                     persist, &ret);
5668                         }
5669                         if (features & SA_FEATURE_RESOURCE)
5670                                 share = rsrc;
5671                 }
5672 
5673                 /* Have a group to hold this share path */
5674                 if (ret == SA_OK && options != NULL &&
5675                     strlen(options) > 0) {
5676                         ret = sa_parse_legacy_options(share,
5677                             options,
5678                             protocol);
5679                 }
5680                 if (!zfs) {
5681                         /*
5682                          * ZFS shares never have a description
5683                          * and we can't store the values so
5684                          * don't try.
5685                          */
5686                         if (ret == SA_OK && description != NULL)
5687                                 ret = sa_set_share_description(share,
5688                                     description);
5689                 }
5690                 if (ret == SA_OK &&
5691                     strcmp(groupstatus, "enabled") == 0) {
5692                         if (rsrc != share)
5693                                 ret = sa_enable_share(share, protocol);
5694                         else
5695                                 ret = sa_enable_resource(rsrc,
5696                                     protocol);
5697                         if (ret == SA_OK &&
5698                             persist == SA_SHARE_PERMANENT) {
5699                                 (void) sa_update_legacy(share,
5700                                     protocol);
5701                         }
5702                         if (ret == SA_OK)
5703                                 ret = sa_update_config(handle);
5704                 }
5705         }
5706 err:
5707         if (ret != SA_OK) {
5708                 (void) fprintf(stderr, gettext("Could not share: %s: %s\n"),
5709                     sharepath, sa_errorstr(ret));
5710                 ret = SA_LEGACY_ERR;
5711         }
5712         return (ret);
5713 }
5714 
5715 /*
5716  * sa_legacy_unshare(flags, argc, argv)
5717  *
5718  * Implements the original unshare command.
5719  */
5720 int
5721 sa_legacy_unshare(sa_handle_t handle, int flags, int argc, char *argv[])
5722 {
5723         char *protocol = "nfs"; /* for now */
5724         char *options = NULL;
5725         char *sharepath = NULL;
5726         int persist = SA_SHARE_TRANSIENT;
5727         int argsused = 0;
5728         int c;
5729         int ret = SA_OK;
5730         int true_legacy = 0;
5731         uint64_t features = 0;
5732         sa_resource_t resource = NULL;
5733         char cmd[MAXPATHLEN];
5734 #ifdef lint
5735         flags = flags;
5736         options = options;
5737 #endif
5738 
5739         while ((c = getopt(argc, argv, "?hF:o:p")) != EOF) {
5740                 switch (c) {
5741                 case 'F':
5742                         protocol = optarg;
5743                         if (!sa_valid_protocol(protocol)) {
5744                                 if (format_legacy_path(cmd, MAXPATHLEN,
5745                                     protocol, "unshare") == 0 &&
5746                                     check_legacy_cmd(cmd)) {
5747                                         true_legacy++;
5748                                 } else {
5749                                         (void) printf(gettext(
5750                                             "Invalid file system name\n"));
5751                                         return (SA_INVALID_PROTOCOL);
5752                                 }
5753                         }
5754                         break;
5755                 case 'o':
5756                         options = optarg;
5757                         argsused++;
5758                         break;
5759                 case 'p':
5760                         persist = SA_SHARE_PERMANENT;
5761                         argsused++;
5762                         break;
5763                 case 'h':
5764                         /* optopt on valid arg isn't defined */
5765                         optopt = c;
5766                         /*FALLTHROUGH*/
5767                 case '?':
5768                 default:
5769                         /*
5770                          * Since a bad option gets to here, sort it
5771                          * out and return a syntax error return value
5772                          * if necessary.
5773                          */
5774                         switch (optopt) {
5775                         default:
5776                                 ret = SA_LEGACY_ERR;
5777                                 break;
5778                         case 'h':
5779                         case '?':
5780                                 break;
5781                         }
5782                         (void) printf(gettext("usage: %s\n"),
5783                             sa_get_usage(USAGE_UNSHARE));
5784                         return (ret);
5785                 }
5786         }
5787 
5788         /* Have the info so construct what is needed */
5789         if (optind == argc || (optind + 1) < argc || options != NULL) {
5790                 ret = SA_SYNTAX_ERR;
5791         } else {
5792                 sa_share_t share;
5793                 char dir[MAXPATHLEN];
5794                 if (true_legacy) {
5795                         /* if still using legacy share/unshare, exec it */
5796                         ret = run_legacy_command(cmd, argv);
5797                         return (ret);
5798                 }
5799                 /*
5800                  * Find the path in the internal configuration. If it
5801                  * isn't found, attempt to resolve the path via
5802                  * realpath() and try again.
5803                  */
5804                 sharepath = argv[optind++];
5805                 share = sa_find_share(handle, sharepath);
5806                 if (share == NULL) {
5807                         if (realpath(sharepath, dir) == NULL) {
5808                                 ret = SA_NO_SUCH_PATH;
5809                         } else {
5810                                 share = sa_find_share(handle, dir);
5811                         }
5812                 }
5813                 if (share == NULL) {
5814                         /* Could be a resource name so check that next */
5815                         features = sa_proto_get_featureset(protocol);
5816                         resource = sa_find_resource(handle, sharepath);
5817                         if (resource != NULL) {
5818                                 share = sa_get_resource_parent(resource);
5819                                 if (features & SA_FEATURE_RESOURCE)
5820                                         (void) sa_disable_resource(resource,
5821                                             protocol);
5822                                 if (persist == SA_SHARE_PERMANENT) {
5823                                         ret = sa_remove_resource(resource);
5824                                         if (ret == SA_OK)
5825                                                 ret = sa_update_config(handle);
5826                                 }
5827                                 /*
5828                                  * If we still have a resource on the
5829                                  * share, we don't disable the share
5830                                  * itself. IF there aren't anymore, we
5831                                  * need to remove the share. The
5832                                  * removal will be done in the next
5833                                  * section if appropriate.
5834                                  */
5835                                 resource = sa_get_share_resource(share, NULL);
5836                                 if (resource != NULL)
5837                                         share = NULL;
5838                         } else if (ret == SA_OK) {
5839                                 /* Didn't find path and no  resource */
5840                                 ret = SA_BAD_PATH;
5841                         }
5842                 }
5843                 if (share != NULL && resource == NULL) {
5844                         ret = sa_disable_share(share, protocol);
5845                         /*
5846                          * Errors are ok and removal should still occur. The
5847                          * legacy unshare is more forgiving of errors than the
5848                          * remove-share subcommand which may need the force
5849                          * flag set for some error conditions. That is, the
5850                          * "unshare" command will always unshare if it can
5851                          * while "remove-share" might require the force option.
5852                          */
5853                         if (persist == SA_SHARE_PERMANENT) {
5854                                 ret = sa_remove_share(share);
5855                                 if (ret == SA_OK)
5856                                         ret = sa_update_config(handle);
5857                         }
5858                 } else if (ret == SA_OK && share == NULL && resource == NULL) {
5859                         /*
5860                          * If both share and resource are NULL, then
5861                          * share not found. If one or the other was
5862                          * found or there was an earlier error, we
5863                          * assume it was handled earlier.
5864                          */
5865                         ret = SA_NOT_SHARED;
5866                 }
5867         }
5868         switch (ret) {
5869         default:
5870                 (void) printf("%s: %s\n", sharepath, sa_errorstr(ret));
5871                 ret = SA_LEGACY_ERR;
5872                 break;
5873         case SA_SYNTAX_ERR:
5874                 (void) printf(gettext("usage: %s\n"),
5875                     sa_get_usage(USAGE_UNSHARE));
5876                 break;
5877         case SA_OK:
5878                 break;
5879         }
5880         return (ret);
5881 }
5882 
5883 /*
5884  * Common commands that implement the sub-commands used by all
5885  * protocols. The entries are found via the lookup command
5886  */
5887 
5888 static sa_command_t commands[] = {
5889         {"add-share", 0, sa_addshare, USAGE_ADD_SHARE, SVC_SET},
5890         {"create", 0, sa_create, USAGE_CREATE, SVC_SET|SVC_ACTION},
5891         {"delete", 0, sa_delete, USAGE_DELETE, SVC_SET|SVC_ACTION},
5892         {"disable", 0, sa_disable_group, USAGE_DISABLE, SVC_SET|SVC_ACTION},
5893         {"enable", 0, sa_enable_group, USAGE_ENABLE, SVC_SET|SVC_ACTION},
5894         {"list", 0, sa_list, USAGE_LIST},
5895         {"move-share", 0, sa_moveshare, USAGE_MOVE_SHARE, SVC_SET},
5896         {"remove-share", 0, sa_removeshare, USAGE_REMOVE_SHARE, SVC_SET},
5897         {"set", 0, sa_set, USAGE_SET, SVC_SET},
5898         {"set-share", 0, sa_set_share, USAGE_SET_SHARE, SVC_SET},
5899         {"show", 0, sa_show, USAGE_SHOW},
5900         {"share", 0, sa_legacy_share, USAGE_SHARE, SVC_SET|SVC_ACTION},
5901         {"start", CMD_NODISPLAY, sa_start_group, USAGE_START,
5902             SVC_SET|SVC_ACTION},
5903         {"stop", CMD_NODISPLAY, sa_stop_group, USAGE_STOP, SVC_SET|SVC_ACTION},
5904         {"unset", 0, sa_unset, USAGE_UNSET, SVC_SET},
5905         {"unshare", 0, sa_legacy_unshare, USAGE_UNSHARE, SVC_SET|SVC_ACTION},
5906         {NULL, 0, NULL, 0}
5907 };
5908 
5909 static char *
5910 sa_get_usage(sa_usage_t index)
5911 {
5912         char *ret = NULL;
5913         switch (index) {
5914         case USAGE_ADD_SHARE:
5915                 ret = gettext("add-share [-nth] [-r resource-name] "
5916                     "[-d \"description text\"] -s sharepath group");
5917                 break;
5918         case USAGE_CREATE:
5919                 ret = gettext(
5920                     "create [-nvh] [-P proto [-p property=value]] group");
5921                 break;
5922         case USAGE_DELETE:
5923                 ret = gettext("delete [-nvh] [-P proto] [-f] group");
5924                 break;
5925         case USAGE_DISABLE:
5926                 ret = gettext("disable [-nvh] {-a | group ...}");
5927                 break;
5928         case USAGE_ENABLE:
5929                 ret = gettext("enable [-nvh] {-a | group ...}");
5930                 break;
5931         case USAGE_LIST:
5932                 ret = gettext("list [-vh] [-P proto]");
5933                 break;
5934         case USAGE_MOVE_SHARE:
5935                 ret = gettext(
5936                     "move-share [-nvh] -s sharepath destination-group");
5937                 break;
5938         case USAGE_REMOVE_SHARE:
5939                 ret = gettext(
5940                     "remove-share [-fnvh] {-s sharepath | -r resource} "
5941                     "group");
5942                 break;
5943         case USAGE_SET:
5944                 ret = gettext("set [-nvh] -P proto [-S optspace] "
5945                     "[-p property=value]* [-s sharepath] [-r resource]] "
5946                     "group");
5947                 break;
5948         case USAGE_SET_SECURITY:
5949                 ret = gettext("set-security [-nvh] -P proto -S security-type "
5950                     "[-p property=value]* group");
5951                 break;
5952         case USAGE_SET_SHARE:
5953                 ret = gettext("set-share [-nh] [-r resource] "
5954                     "[-d \"description text\"] -s sharepath group");
5955                 break;
5956         case USAGE_SHOW:
5957                 ret = gettext("show [-pvxh] [-P proto] [group ...]");
5958                 break;
5959         case USAGE_SHARE:
5960                 ret = gettext("share [-F fstype] [-p] [-o optionlist]"
5961                     "[-d description] [pathname [resourcename]]");
5962                 break;
5963         case USAGE_START:
5964                 ret = gettext("start [-vh] [-P proto] {-a | group ...}");
5965                 break;
5966         case USAGE_STOP:
5967                 ret = gettext("stop [-vh] [-P proto] {-a | group ...}");
5968                 break;
5969         case USAGE_UNSET:
5970                 ret = gettext("unset [-nvh] -P proto [-S optspace] "
5971                     "[-p property]* group");
5972                 break;
5973         case USAGE_UNSET_SECURITY:
5974                 ret = gettext("unset-security [-nvh] -P proto "
5975                     "-S security-type [-p property]* group");
5976                 break;
5977         case USAGE_UNSHARE:
5978                 ret = gettext(
5979                     "unshare [-F fstype] [-p] [-o optionlist] sharepath");
5980                 break;
5981         }
5982         return (ret);
5983 }
5984 
5985 /*
5986  * sa_lookup(cmd, proto)
5987  *
5988  * Lookup the sub-command. proto isn't currently used, but it may
5989  * eventually provide a way to provide protocol specific sub-commands.
5990  */
5991 sa_command_t *
5992 sa_lookup(char *cmd, char *proto)
5993 {
5994         int i;
5995         size_t len;
5996 #ifdef lint
5997         proto = proto;
5998 #endif
5999 
6000         len = strlen(cmd);
6001         for (i = 0; commands[i].cmdname != NULL; i++) {
6002                 if (strncmp(cmd, commands[i].cmdname, len) == 0)
6003                         return (&commands[i]);
6004         }
6005         return (NULL);
6006 }
6007 
6008 void
6009 sub_command_help(char *proto)
6010 {
6011         int i;
6012 #ifdef lint
6013         proto = proto;
6014 #endif
6015 
6016         (void) printf(gettext("\tsub-commands:\n"));
6017         for (i = 0; commands[i].cmdname != NULL; i++) {
6018                 if (!(commands[i].flags & (CMD_ALIAS|CMD_NODISPLAY)))
6019                         (void) printf("\t%s\n",
6020                             sa_get_usage((sa_usage_t)commands[i].cmdidx));
6021         }
6022 }