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