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