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