Home | History | Annotate | Download | only in sharemgr
      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 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #include <sys/types.h>
     28 #include <sys/stat.h>
     29 #include <fcntl.h>
     30 #include <stdlib.h>
     31 #include <stdio.h>
     32 #include <string.h>
     33 #include <ctype.h>
     34 #include <unistd.h>
     35 #include <getopt.h>
     36 #include <utmpx.h>
     37 #include <pwd.h>
     38 #include <auth_attr.h>
     39 #include <secdb.h>
     40 #include <sys/param.h>
     41 #include <sys/stat.h>
     42 #include <errno.h>
     43 
     44 #include <libshare.h>
     45 #include "sharemgr.h"
     46 #include <libscf.h>
     47 #include <libxml/tree.h>
     48 #include <libintl.h>
     49 #include <assert.h>
     50 #include <iconv.h>
     51 #include <langinfo.h>
     52 #include <dirent.h>
     53 
     54 static char *sa_get_usage(sa_usage_t);
     55 
     56 /*
     57  * Implementation of the common sub-commands supported by sharemgr.
     58  * A number of helper functions are also included.
     59  */
     60 
     61 /*
     62  * has_protocol(group, proto)
     63  *	If the group has an optionset with the specified protocol,
     64  *	return true (1) otherwise false (0).
     65  */
     66 static int
     67 has_protocol(sa_group_t group, char *protocol)
     68 {
     69 	sa_optionset_t optionset;
     70 	int result = 0;
     71 
     72 	optionset = sa_get_optionset(group, protocol);
     73 	if (optionset != NULL) {
     74 		result++;
     75 	}
     76 	return (result);
     77 }
     78 
     79 /*
     80  * validresource(name)
     81  *
     82  * Check that name only has valid characters in it. The current valid
     83  * set are the printable characters but not including:
     84  *	" / \ [ ] : | < > + ; , ? * = \t
     85  * Note that space is included and there is a maximum length.
     86  */
     87 static int
     88 validresource(const char *name)
     89 {
     90 	const char *cp;
     91 	size_t len;
     92 
     93 	if (name == NULL)
     94 		return (B_FALSE);
     95 
     96 	len = strlen(name);
     97 	if (len == 0 || len > SA_MAX_RESOURCE_NAME)
     98 		return (B_FALSE);
     99 
    100 	if (strpbrk(name, "\"/\\[]:|<>+;,?*=\t") != NULL) {
    101 		return (B_FALSE);
    102 	}
    103 
    104 	for (cp = name; *cp != '\0'; cp++)
    105 		if (iscntrl(*cp))
    106 			return (B_FALSE);
    107 
    108 	return (B_TRUE);
    109 }
    110 
    111 /*
    112  * conv_to_utf8(input)
    113  *
    114  * Convert the input string to utf8 from the current locale.  If the
    115  * conversion fails, use the current locale, it is likely close
    116  * enough. For example, the "C" locale is a subset of utf-8. The
    117  * return value may be a new string or the original input string.
    118  */
    119 
    120 static char *
    121 conv_to_utf8(char *input)
    122 {
    123 	iconv_t cd;
    124 	char *inval = input;
    125 	char *output = input;
    126 	char *outleft;
    127 	char *curlocale;
    128 	size_t bytesleft;
    129 	size_t size;
    130 	size_t osize;
    131 	static int warned = 0;
    132 
    133 	curlocale = nl_langinfo(CODESET);
    134 	if (curlocale == NULL)
    135 		curlocale = "C";
    136 	cd = iconv_open("UTF-8", curlocale);
    137 	if (cd != NULL && cd != (iconv_t)-1) {
    138 		size = strlen(input);
    139 		/* Assume worst case of characters expanding to 4 bytes. */
    140 		bytesleft = size * 4;
    141 		output = calloc(bytesleft, 1);
    142 		if (output != NULL) {
    143 			outleft = output;
    144 			/* inval can be modified on return */
    145 			osize = iconv(cd, (const char **)&inval, &size,
    146 			    &outleft, &bytesleft);
    147 			if (osize == (size_t)-1 || size != 0) {
    148 				free(output);
    149 				output = input;
    150 			}
    151 		} else {
    152 			/* Need to return something. */
    153 			output = input;
    154 		}
    155 		(void) iconv_close(cd);
    156 	} else {
    157 		if (!warned)
    158 			(void) fprintf(stderr,
    159 			    gettext("Cannot convert to UTF-8 from %s\n"),
    160 			    curlocale ? curlocale : gettext("unknown"));
    161 		warned = 1;
    162 	}
    163 	return (output);
    164 }
    165 
    166 /*
    167  * conv_from(input)
    168  *
    169  * Convert the input string from utf8 to current locale.  If the
    170  * conversion isn't supported, just use as is. The return value may be
    171  * a new string or the original input string.
    172  */
    173 
    174 static char *
    175 conv_from_utf8(char *input)
    176 {
    177 	iconv_t cd;
    178 	char *output = input;
    179 	char *inval = input;
    180 	char *outleft;
    181 	char *curlocale;
    182 	size_t bytesleft;
    183 	size_t size;
    184 	size_t osize;
    185 	static int warned = 0;
    186 
    187 	curlocale = nl_langinfo(CODESET);
    188 	if (curlocale == NULL)
    189 		curlocale = "C";
    190 	cd = iconv_open(curlocale, "UTF-8");
    191 	if (cd != NULL && cd != (iconv_t)-1) {
    192 		size = strlen(input);
    193 		/* Assume worst case of characters expanding to 4 bytes. */
    194 		bytesleft = size * 4;
    195 		output = calloc(bytesleft, 1);
    196 		if (output != NULL) {
    197 			outleft = output;
    198 			osize = iconv(cd, (const char **)&inval, &size,
    199 			    &outleft, &bytesleft);
    200 			if (osize == (size_t)-1 || size != 0)
    201 				output = input;
    202 		} else {
    203 			/* Need to return something. */
    204 			output = input;
    205 		}
    206 		(void) iconv_close(cd);
    207 	} else {
    208 		if (!warned)
    209 			(void) fprintf(stderr,
    210 			    gettext("Cannot convert to %s from UTF-8\n"),
    211 			    curlocale ? curlocale : gettext("unknown"));
    212 		warned = 1;
    213 	}
    214 	return (output);
    215 }
    216 
    217 /*
    218  * print_rsrc_desc(resource, sharedesc)
    219  *
    220  * Print the resource description string after converting from UTF8 to
    221  * the current locale. If sharedesc is not NULL and there is no
    222  * description on the resource, use sharedesc. sharedesc will already
    223  * be converted to UTF8.
    224  */
    225 
    226 static void
    227 print_rsrc_desc(sa_resource_t resource, char *sharedesc)
    228 {
    229 	char *description;
    230 	char *desc;
    231 
    232 	if (resource == NULL)
    233 		return;
    234 
    235 	description = sa_get_resource_description(resource);
    236 	if (description != NULL) {
    237 		desc = conv_from_utf8(description);
    238 		if (desc != description) {
    239 			sa_free_share_description(description);
    240 			description = desc;
    241 		}
    242 	} else if (sharedesc != NULL) {
    243 		description = strdup(sharedesc);
    244 	}
    245 	if (description != NULL) {
    246 		(void) printf("\t\"%s\"", description);
    247 		sa_free_share_description(description);
    248 	}
    249 }
    250 
    251 /*
    252  * set_resource_desc(share, description)
    253  *
    254  * Set the share description value after converting the description
    255  * string to UTF8 from the current locale.
    256  */
    257 
    258 static int
    259 set_resource_desc(sa_share_t share, char *description)
    260 {
    261 	char *desc;
    262 	int ret;
    263 
    264 	desc = conv_to_utf8(description);
    265 	ret = sa_set_resource_description(share, desc);
    266 	if (description != desc)
    267 		sa_free_share_description(desc);
    268 	return (ret);
    269 }
    270 
    271 /*
    272  * set_share_desc(share, description)
    273  *
    274  * Set the resource description value after converting the description
    275  * string to UTF8 from the current locale.
    276  */
    277 
    278 static int
    279 set_share_desc(sa_share_t share, char *description)
    280 {
    281 	char *desc;
    282 	int ret;
    283 
    284 	desc = conv_to_utf8(description);
    285 	ret = sa_set_share_description(share, desc);
    286 	if (description != desc)
    287 		sa_free_share_description(desc);
    288 	return (ret);
    289 }
    290 
    291 /*
    292  * add_list(list, item, data, proto)
    293  *	Adds a new list member that points holds item in the list.
    294  *	If list is NULL, it starts a new list.  The function returns
    295  *	the first member of the list.
    296  */
    297 struct list *
    298 add_list(struct list *listp, void *item, void *data, char *proto)
    299 {
    300 	struct list *new, *tmp;
    301 
    302 	new = malloc(sizeof (struct list));
    303 	if (new != NULL) {
    304 		new->next = NULL;
    305 		new->item = item;
    306 		new->itemdata = data;
    307 		new->proto = proto;
    308 	} else {
    309 		return (listp);
    310 	}
    311 
    312 	if (listp == NULL)
    313 		return (new);
    314 
    315 	for (tmp = listp; tmp->next != NULL; tmp = tmp->next) {
    316 		/* get to end of list */
    317 	}
    318 	tmp->next = new;
    319 	return (listp);
    320 }
    321 
    322 /*
    323  * free_list(list)
    324  *	Given a list, free all the members of the list;
    325  */
    326 static void
    327 free_list(struct list *listp)
    328 {
    329 	struct list *tmp;
    330 	while (listp != NULL) {
    331 		tmp = listp;
    332 		listp = listp->next;
    333 		free(tmp);
    334 	}
    335 }
    336 
    337 /*
    338  * check_authorization(instname, which)
    339  *
    340  * Checks to see if the specific type of authorization in which is
    341  * enabled for the user in this SMF service instance.
    342  */
    343 
    344 static int
    345 check_authorization(char *instname, int which)
    346 {
    347 	scf_handle_t *handle = NULL;
    348 	scf_simple_prop_t *prop = NULL;
    349 	char svcstring[SA_MAX_NAME_LEN + sizeof (SA_SVC_FMRI_BASE) + 1];
    350 	char *authstr = NULL;
    351 	ssize_t numauths;
    352 	int ret = B_TRUE;
    353 	uid_t uid;
    354 	struct passwd *pw = NULL;
    355 
    356 	uid = getuid();
    357 	pw = getpwuid(uid);
    358 	if (pw == NULL) {
    359 		ret = B_FALSE;
    360 	} else {
    361 		/*
    362 		 * Since names are restricted to SA_MAX_NAME_LEN won't
    363 		 * overflow.
    364 		 */
    365 		(void) snprintf(svcstring, sizeof (svcstring), "%s:%s",
    366 		    SA_SVC_FMRI_BASE, instname);
    367 		handle = scf_handle_create(SCF_VERSION);
    368 		if (handle != NULL) {
    369 			if (scf_handle_bind(handle) == 0) {
    370 				switch (which) {
    371 				case SVC_SET:
    372 					prop = scf_simple_prop_get(handle,
    373 					    svcstring, "general",
    374 					    SVC_AUTH_VALUE);
    375 					break;
    376 				case SVC_ACTION:
    377 					prop = scf_simple_prop_get(handle,
    378 					    svcstring, "general",
    379 					    SVC_AUTH_ACTION);
    380 					break;
    381 				}
    382 			}
    383 		}
    384 	}
    385 	/* make sure we have an authorization string property */
    386 	if (prop != NULL) {
    387 		int i;
    388 		numauths = scf_simple_prop_numvalues(prop);
    389 		for (ret = 0, i = 0; i < numauths; i++) {
    390 			authstr = scf_simple_prop_next_astring(prop);
    391 			if (authstr != NULL) {
    392 				/* check if this user has one of the strings */
    393 				if (chkauthattr(authstr, pw->pw_name)) {
    394 					ret = 1;
    395 					break;
    396 				}
    397 			}
    398 		}
    399 		endauthattr();
    400 		scf_simple_prop_free(prop);
    401 	} else {
    402 		/* no authorization string defined */
    403 		ret = 0;
    404 	}
    405 	if (handle != NULL)
    406 		scf_handle_destroy(handle);
    407 	return (ret);
    408 }
    409 
    410 /*
    411  * check_authorizations(instname, flags)
    412  *
    413  * check all the needed authorizations for the user in this service
    414  * instance. Return value of 1(true) or 0(false) indicates whether
    415  * there are authorizations for the user or not.
    416  */
    417 
    418 static int
    419 check_authorizations(char *instname, int flags)
    420 {
    421 	int ret1 = 0;
    422 	int ret2 = 0;
    423 	int ret;
    424 
    425 	if (flags & SVC_SET)
    426 		ret1 = check_authorization(instname, SVC_SET);
    427 	if (flags & SVC_ACTION)
    428 		ret2 = check_authorization(instname, SVC_ACTION);
    429 	switch (flags) {
    430 	case SVC_ACTION:
    431 		ret = ret2;
    432 		break;
    433 	case SVC_SET:
    434 		ret = ret1;
    435 		break;
    436 	case SVC_ACTION|SVC_SET:
    437 		ret = ret1 & ret2;
    438 		break;
    439 	default:
    440 		/* if not flags set, we assume we don't need authorizations */
    441 		ret = 1;
    442 	}
    443 	return (ret);
    444 }
    445 
    446 /*
    447  * notify_or_enable_share(share, protocol)
    448  *
    449  * Since some protocols don't want an "enable" when properties change,
    450  * this function will use the protocol specific notify function
    451  * first. If that fails, it will then attempt to use the
    452  * sa_enable_share().  "protocol" is the protocol that was specified
    453  * on the command line.
    454  */
    455 static void
    456 notify_or_enable_share(sa_share_t share, char *protocol)
    457 {
    458 	sa_group_t group;
    459 	sa_optionset_t opt;
    460 	int ret = SA_OK;
    461 	char *path;
    462 	char *groupproto;
    463 	sa_share_t parent = share;
    464 
    465 	/* If really a resource, get parent share */
    466 	if (!sa_is_share(share)) {
    467 		parent = sa_get_resource_parent((sa_resource_t)share);
    468 	}
    469 
    470 	/*
    471 	 * Now that we've got a share in "parent", make sure it has a path.
    472 	 */
    473 	path = sa_get_share_attr(parent, "path");
    474 	if (path == NULL)
    475 		return;
    476 
    477 	group = sa_get_parent_group(parent);
    478 
    479 	if (group == NULL) {
    480 		sa_free_attr_string(path);
    481 		return;
    482 	}
    483 	for (opt = sa_get_optionset(group, NULL);
    484 	    opt != NULL;
    485 	    opt = sa_get_next_optionset(opt)) {
    486 		groupproto = sa_get_optionset_attr(opt, "type");
    487 		if (groupproto == NULL ||
    488 		    (protocol != NULL && strcmp(groupproto, protocol) != 0)) {
    489 			sa_free_attr_string(groupproto);
    490 			continue;
    491 		}
    492 		if (sa_is_share(share)) {
    493 			if ((ret = sa_proto_change_notify(share,
    494 			    groupproto)) != SA_OK) {
    495 				ret = sa_enable_share(share, groupproto);
    496 				if (ret != SA_OK) {
    497 					(void) printf(
    498 					    gettext("Could not reenable"
    499 					    " share %s: %s\n"),
    500 					    path, sa_errorstr(ret));
    501 				}
    502 			}
    503 		} else {
    504 			/* Must be a resource */
    505 			if ((ret = sa_proto_notify_resource(share,
    506 			    groupproto)) != SA_OK) {
    507 				ret = sa_enable_resource(share, groupproto);
    508 				if (ret != SA_OK) {
    509 					(void) printf(
    510 					    gettext("Could not "
    511 					    "reenable resource %s: "
    512 					    "%s\n"), path,
    513 					    sa_errorstr(ret));
    514 				}
    515 			}
    516 		}
    517 		sa_free_attr_string(groupproto);
    518 	}
    519 	sa_free_attr_string(path);
    520 }
    521 
    522 /*
    523  * enable_group(group, updateproto, notify, proto)
    524  *
    525  * enable all the shares in the specified group. This is a helper for
    526  * enable_all_groups in order to simplify regular and subgroup (zfs)
    527  * enabling. Group has already been checked for non-NULL. If notify
    528  * is non-zero, attempt to use the notify interface rather than
    529  * enable.
    530  */
    531 static void
    532 enable_group(sa_group_t group, char *updateproto, int notify, char *proto)
    533 {
    534 	sa_share_t share;
    535 
    536 	for (share = sa_get_share(group, NULL);
    537 	    share != NULL;
    538 	    share = sa_get_next_share(share)) {
    539 		if (updateproto != NULL)
    540 			(void) sa_update_legacy(share, updateproto);
    541 		if (notify)
    542 			notify_or_enable_share(share, proto);
    543 		else
    544 			(void) sa_enable_share(share, proto);
    545 	}
    546 }
    547 
    548 /*
    549  * isenabled(group)
    550  *
    551  * Returns B_TRUE if the group is enabled or B_FALSE if it isn't.
    552  * Moved to separate function to reduce clutter in the code.
    553  */
    554 
    555 static int
    556 isenabled(sa_group_t group)
    557 {
    558 	char *state;
    559 	int ret = B_FALSE;
    560 
    561 	if (group != NULL) {
    562 		state = sa_get_group_attr(group, "state");
    563 		if (state != NULL) {
    564 
    565 			if (strcmp(state, "enabled") == 0)
    566 				ret = B_TRUE;
    567 			sa_free_attr_string(state);
    568 		}
    569 	}
    570 	return (ret);
    571 }
    572 
    573 /*
    574  * enable_all_groups(list, setstate, online, updateproto)
    575  *
    576  * Given a list of groups, enable each one found.  If updateproto is
    577  * not NULL, then update all the shares for the protocol that was
    578  * passed in. If enable is non-zero, tell enable_group to try the
    579  * notify interface since this is a property change.
    580  */
    581 static int
    582 enable_all_groups(sa_handle_t handle, struct list *work, int setstate,
    583     int online, char *updateproto, int enable)
    584 {
    585 	int ret;
    586 	char instance[SA_MAX_NAME_LEN + sizeof (SA_SVC_FMRI_BASE) + 1];
    587 	char *state;
    588 	char *name;
    589 	char *zfs = NULL;
    590 	sa_group_t group;
    591 	sa_group_t subgroup;
    592 
    593 	for (ret = SA_OK; work != NULL;	work = work->next) {
    594 		group = (sa_group_t)work->item;
    595 
    596 		/*
    597 		 * If setstate == TRUE, then make sure to set
    598 		 * enabled. This needs to be done here in order for
    599 		 * the isenabled check to succeed on a newly enabled
    600 		 * group.
    601 		 */
    602 		if (setstate == B_TRUE) {
    603 			ret = sa_set_group_attr(group, "state",	"enabled");
    604 			if (ret != SA_OK)
    605 				break;
    606 		}
    607 
    608 		/*
    609 		 * Check to see if group is enabled. If it isn't, skip
    610 		 * the rest.  We don't want shares starting if the
    611 		 * group is disabled. The properties may have been
    612 		 * updated, but there won't be a change until the
    613 		 * group is enabled.
    614 		 */
    615 		if (!isenabled(group))
    616 			continue;
    617 
    618 		/* if itemdata != NULL then a single share */
    619 		if (work->itemdata != NULL) {
    620 			if (enable) {
    621 				if (work->itemdata != NULL)
    622 					notify_or_enable_share(work->itemdata,
    623 					    updateproto);
    624 				else
    625 					ret = SA_CONFIG_ERR;
    626 			} else {
    627 				if (sa_is_share(work->itemdata)) {
    628 					ret = sa_enable_share(
    629 					    (sa_share_t)work->itemdata,
    630 					    updateproto);
    631 				} else {
    632 					ret = sa_enable_resource(
    633 					    (sa_resource_t)work->itemdata,
    634 					    updateproto);
    635 				}
    636 			}
    637 		}
    638 		if (ret != SA_OK)
    639 			break;
    640 
    641 		/* if itemdata == NULL then the whole group */
    642 		if (work->itemdata == NULL) {
    643 			zfs = sa_get_group_attr(group, "zfs");
    644 			/*
    645 			 * If the share is managed by ZFS, don't
    646 			 * update any of the protocols since ZFS is
    647 			 * handling this.  Updateproto will contain
    648 			 * the name of the protocol that we want to
    649 			 * update legacy files for.
    650 			 */
    651 			enable_group(group, zfs == NULL ? updateproto : NULL,
    652 			    enable, work->proto);
    653 			for (subgroup = sa_get_sub_group(group);
    654 			    subgroup != NULL;
    655 			    subgroup = sa_get_next_group(subgroup)) {
    656 				/* never update legacy for ZFS subgroups */
    657 				enable_group(subgroup, NULL, enable,
    658 				    work->proto);
    659 			}
    660 		}
    661 		if (online) {
    662 			zfs = sa_get_group_attr(group, "zfs");
    663 			name = sa_get_group_attr(group, "name");
    664 			if (name != NULL) {
    665 				if (zfs == NULL) {
    666 					(void) snprintf(instance,
    667 					    sizeof (instance), "%s:%s",
    668 					    SA_SVC_FMRI_BASE, name);
    669 					state = smf_get_state(instance);
    670 					if (state == NULL ||
    671 					    strcmp(state, "online") != 0) {
    672 						(void) smf_enable_instance(
    673 						    instance, 0);
    674 						free(state);
    675 					}
    676 				} else {
    677 					sa_free_attr_string(zfs);
    678 					zfs = NULL;
    679 				}
    680 				if (name != NULL)
    681 					sa_free_attr_string(name);
    682 			}
    683 		}
    684 	}
    685 	if (ret == SA_OK) {
    686 		ret = sa_update_config(handle);
    687 	}
    688 	return (ret);
    689 }
    690 
    691 /*
    692  * chk_opt(optlistp, security, proto)
    693  *
    694  * Do a sanity check on the optlist provided for the protocol.  This
    695  * is a syntax check and verification that the property is either a
    696  * general or specific to a names optionset.
    697  */
    698 
    699 static int
    700 chk_opt(struct options *optlistp, int security, char *proto)
    701 {
    702 	struct options *optlist;
    703 	char *sep = "";
    704 	int notfirst = 0;
    705 	int ret;
    706 
    707 	for (optlist = optlistp; optlist != NULL; optlist = optlist->next) {
    708 		char *optname;
    709 
    710 		optname = optlist->optname;
    711 		ret = OPT_ADD_OK;
    712 		/* extract property/value pair */
    713 		if (sa_is_security(optname, proto)) {
    714 			if (!security)
    715 				ret = OPT_ADD_SECURITY;
    716 		} else {
    717 			if (security)
    718 				ret = OPT_ADD_PROPERTY;
    719 		}
    720 		if (ret != OPT_ADD_OK) {
    721 			if (notfirst == 0)
    722 				(void) printf(
    723 				    gettext("Property syntax error: "));
    724 			switch (ret) {
    725 			case OPT_ADD_SYNTAX:
    726 				(void) printf(gettext("%ssyntax error: %s"),
    727 				    sep, optname);
    728 				sep = ", ";
    729 				break;
    730 			case OPT_ADD_SECURITY:
    731 				(void) printf(gettext("%s%s requires -S"),
    732 				    optname, sep);
    733 				sep = ", ";
    734 				break;
    735 			case OPT_ADD_PROPERTY:
    736 				(void) printf(
    737 				    gettext("%s%s not supported with -S"),
    738 				    optname, sep);
    739 				sep = ", ";
    740 				break;
    741 			}
    742 			notfirst++;
    743 		}
    744 	}
    745 	if (notfirst) {
    746 		(void) printf("\n");
    747 		ret = SA_SYNTAX_ERR;
    748 	}
    749 	return (ret);
    750 }
    751 
    752 /*
    753  * free_opt(optlist)
    754  *	Free the specified option list.
    755  */
    756 static void
    757 free_opt(struct options *optlist)
    758 {
    759 	struct options *nextopt;
    760 	while (optlist != NULL) {
    761 		nextopt = optlist->next;
    762 		free(optlist);
    763 		optlist = nextopt;
    764 	}
    765 }
    766 
    767 /*
    768  * check property list for valid properties
    769  * A null value is a remove which is always valid.
    770  */
    771 static int
    772 valid_options(sa_handle_t handle, struct options *optlist, char *proto,
    773     void *object, char *sec)
    774 {
    775 	int ret = SA_OK;
    776 	struct options *cur;
    777 	sa_property_t prop;
    778 	sa_optionset_t parent = NULL;
    779 
    780 	if (object != NULL) {
    781 		if (sec == NULL)
    782 			parent = sa_get_optionset(object, proto);
    783 		else
    784 			parent = sa_get_security(object, sec, proto);
    785 	}
    786 
    787 	for (cur = optlist; cur != NULL; cur = cur->next) {
    788 		if (cur->optvalue == NULL)
    789 			continue;
    790 		prop = sa_create_property(cur->optname, cur->optvalue);
    791 		if (prop == NULL)
    792 			ret = SA_NO_MEMORY;
    793 		if (ret != SA_OK ||
    794 		    (ret = sa_valid_property(handle, parent, proto, prop)) !=
    795 		    SA_OK) {
    796 			(void) printf(
    797 			    gettext("Could not add property %s: %s\n"),
    798 			    cur->optname, sa_errorstr(ret));
    799 		}
    800 		(void) sa_remove_property(prop);
    801 	}
    802 	return (ret);
    803 }
    804 
    805 /*
    806  * add_optionset(group, optlist, protocol, *err)
    807  *	Add the options in optlist to an optionset and then add the optionset
    808  *	to the group.
    809  *
    810  *	The return value indicates if there was a "change" while errors are
    811  *	returned via the *err parameters.
    812  */
    813 static int
    814 add_optionset(sa_group_t group, struct options *optlist, char *proto, int *err)
    815 {
    816 	sa_optionset_t optionset;
    817 	int ret = SA_OK;
    818 	int result = B_FALSE;
    819 	sa_handle_t handle;
    820 
    821 	optionset = sa_get_optionset(group, proto);
    822 	if (optionset == NULL) {
    823 		optionset = sa_create_optionset(group, proto);
    824 		if (optionset == NULL)
    825 			ret = SA_NO_MEMORY;
    826 		result = B_TRUE; /* adding a protocol is a change */
    827 	}
    828 	if (optionset == NULL) {
    829 		ret = SA_NO_MEMORY;
    830 		goto out;
    831 	}
    832 	handle = sa_find_group_handle(group);
    833 	if (handle == NULL) {
    834 		ret = SA_CONFIG_ERR;
    835 		goto out;
    836 	}
    837 	while (optlist != NULL) {
    838 		sa_property_t prop;
    839 		prop = sa_get_property(optionset, optlist->optname);
    840 		if (prop == NULL) {
    841 			/*
    842 			 * add the property, but only if it is
    843 			 * a non-NULL or non-zero length value
    844 			 */
    845 			if (optlist->optvalue != NULL) {
    846 				prop = sa_create_property(optlist->optname,
    847 				    optlist->optvalue);
    848 				if (prop != NULL) {
    849 					ret = sa_valid_property(handle,
    850 					    optionset, proto, prop);
    851 					if (ret != SA_OK) {
    852 						(void) sa_remove_property(prop);
    853 						(void) printf(gettext("Could "
    854 						    "not add property "
    855 						    "%s: %s\n"),
    856 						    optlist->optname,
    857 						    sa_errorstr(ret));
    858 					}
    859 				}
    860 				if (ret == SA_OK) {
    861 					ret = sa_add_property(optionset, prop);
    862 					if (ret != SA_OK) {
    863 						(void) printf(gettext(
    864 						    "Could not add property "
    865 						    "%s: %s\n"),
    866 						    optlist->optname,
    867 						    sa_errorstr(ret));
    868 					} else {
    869 						/* there was a change */
    870 						result = B_TRUE;
    871 					}
    872 				}
    873 			}
    874 		} else {
    875 			ret = sa_update_property(prop, optlist->optvalue);
    876 			/* should check to see if value changed */
    877 			if (ret != SA_OK) {
    878 				(void) printf(gettext("Could not update "
    879 				    "property %s: %s\n"), optlist->optname,
    880 				    sa_errorstr(ret));
    881 			} else {
    882 				result = B_TRUE;
    883 			}
    884 		}
    885 		optlist = optlist->next;
    886 	}
    887 	ret = sa_commit_properties(optionset, 0);
    888 
    889 out:
    890 	if (err != NULL)
    891 		*err = ret;
    892 	return (result);
    893 }
    894 
    895 /*
    896  * resource_compliant(group)
    897  *
    898  * Go through all the shares in the group. Assume compliant, but if
    899  * any share doesn't have at least one resource name, it isn't
    900  * compliant.
    901  */
    902 static int
    903 resource_compliant(sa_group_t group)
    904 {
    905 	sa_share_t share;
    906 
    907 	for (share = sa_get_share(group, NULL); share != NULL;
    908 	    share = sa_get_next_share(share)) {
    909 		if (sa_get_share_resource(share, NULL) == NULL) {
    910 			return (B_FALSE);
    911 		}
    912 	}
    913 	return (B_TRUE);
    914 }
    915 
    916 /*
    917  * fix_path(path)
    918  *
    919  * change all illegal characters to something else.  For now, all get
    920  * converted to '_' and the leading '/' is stripped off. This is used
    921  * to construct an resource name (SMB share name) that is valid.
    922  * Caller must pass a valid path.
    923  */
    924 static void
    925 fix_path(char *path)
    926 {
    927 	char *cp;
    928 	size_t len;
    929 
    930 	assert(path != NULL);
    931 
    932 	/* make sure we are appropriate length */
    933 	cp = path + 1; /* skip leading slash */
    934 	while (cp != NULL && strlen(cp) > SA_MAX_RESOURCE_NAME) {
    935 		cp = strchr(cp, '/');
    936 		if (cp != NULL)
    937 			cp++;
    938 	}
    939 	/* two cases - cp == NULL and cp is substring of path */
    940 	if (cp == NULL) {
    941 		/* just take last SA_MAX_RESOURCE_NAME chars */
    942 		len = 1 + strlen(path) - SA_MAX_RESOURCE_NAME;
    943 		(void) memmove(path, path + len, SA_MAX_RESOURCE_NAME);
    944 		path[SA_MAX_RESOURCE_NAME] = '\0';
    945 	} else {
    946 		len = strlen(cp) + 1;
    947 		(void) memmove(path, cp, len);
    948 	}
    949 
    950 	/*
    951 	 * Don't want any of the characters that are not allowed
    952 	 * in and SMB share name. Replace them with '_'.
    953 	 */
    954 	while (*path) {
    955 		switch (*path) {
    956 		case '/':
    957 		case '"':
    958 		case '\\':
    959 		case '[':
    960 		case ']':
    961 		case ':':
    962 		case '|':
    963 		case '<':
    964 		case '>':
    965 		case '+':
    966 		case ';':
    967 		case ',':
    968 		case '?':
    969 		case '*':
    970 		case '=':
    971 		case '\t':
    972 			*path = '_';
    973 			break;
    974 		}
    975 		path++;
    976 	}
    977 }
    978 
    979 /*
    980  * name_adjust(path, count)
    981  *
    982  * Add a ~<count> in place of last few characters. The total number of
    983  * characters is dependent on count.
    984  */
    985 #define	MAX_MANGLE_NUMBER	10000
    986 
    987 static int
    988 name_adjust(char *path, int count)
    989 {
    990 	size_t len;
    991 
    992 	len = strlen(path) - 2;
    993 	if (count > 10)
    994 		len--;
    995 	if (count > 100)
    996 		len--;
    997 	if (count > 1000)
    998 		len--;
    999 	if (len > 0)
   1000 		(void) sprintf(path + len, "~%d", count);
   1001 	else
   1002 		return (SA_BAD_VALUE);
   1003 
   1004 	return (SA_OK);
   1005 }
   1006 
   1007 /*
   1008  * make_resources(group)
   1009  *
   1010  * Go through all the shares in the group and make them have resource
   1011  * names.
   1012  */
   1013 static void
   1014 make_resources(sa_group_t group)
   1015 {
   1016 	sa_share_t share;
   1017 	int count;
   1018 	int err = SA_OK;
   1019 
   1020 	for (share = sa_get_share(group, NULL); share != NULL;
   1021 	    share = sa_get_next_share(share)) {
   1022 		/* Skip those with resources */
   1023 		if (sa_get_share_resource(share, NULL) == NULL) {
   1024 			char *path;
   1025 			path = sa_get_share_attr(share, "path");
   1026 			if (path == NULL)
   1027 				continue;
   1028 			fix_path(path);
   1029 			count = 0;	/* reset for next resource */
   1030 			while (sa_add_resource(share, path,
   1031 			    SA_SHARE_PERMANENT, &err) == NULL &&
   1032 			    err == SA_DUPLICATE_NAME) {
   1033 				int ret;
   1034 				ret = name_adjust(path, count);
   1035 				count++;
   1036 				if (ret != SA_OK ||
   1037 				    count >= MAX_MANGLE_NUMBER) {
   1038 					(void) printf(gettext(
   1039 					    "Cannot create resource name for"
   1040 					    " path: %s\n"), path);
   1041 					break;
   1042 				}
   1043 			}
   1044 			sa_free_attr_string(path);
   1045 		}
   1046 	}
   1047 }
   1048 
   1049 /*
   1050  * check_valid_group(group, protocol)
   1051  *
   1052  * Check to see that the group should have the protocol added (if
   1053  * there is one specified).
   1054  */
   1055 
   1056 static int
   1057 check_valid_group(sa_group_t group, char *groupname, char *protocol)
   1058 {
   1059 
   1060 	if (protocol != NULL) {
   1061 		if (has_protocol(group, protocol)) {
   1062 			(void) printf(gettext(
   1063 			    "Group \"%s\" already exists"
   1064 			    " with protocol %s\n"), groupname,
   1065 			    protocol);
   1066 			return (SA_DUPLICATE_NAME);
   1067 		} else if (strcmp(groupname, "default") == 0 &&
   1068 		    strcmp(protocol, "nfs") != 0) {
   1069 			(void) printf(gettext(
   1070 			    "Group \"%s\" only allows protocol "
   1071 			    "\"%s\"\n"), groupname, "nfs");
   1072 			return (SA_INVALID_PROTOCOL);
   1073 		}
   1074 	} else {
   1075 		/* must add new protocol */
   1076 		(void) printf(gettext(
   1077 		    "Group already exists and no protocol "
   1078 		    "specified.\n"));
   1079 		return (SA_DUPLICATE_NAME);
   1080 	}
   1081 	return (SA_OK);
   1082 }
   1083 
   1084 /*
   1085  * enforce_featureset(group, protocol, dryrun, force)
   1086  *
   1087  * Check the protocol featureset against the group and enforce any
   1088  * rules that might be imposed.
   1089  */
   1090 
   1091 static int
   1092 enforce_featureset(sa_group_t group, char *protocol, boolean_t dryrun,
   1093     boolean_t force)
   1094 {
   1095 	uint64_t features;
   1096 
   1097 	if (protocol == NULL)
   1098 		return (SA_OK);
   1099 
   1100 	/*
   1101 	 * First check to see if specified protocol is one we want to
   1102 	 * allow on a group. Only server protocols are allowed here.
   1103 	 */
   1104 	features = sa_proto_get_featureset(protocol);
   1105 	if (!(features & SA_FEATURE_SERVER)) {
   1106 		(void) printf(
   1107 		    gettext("Protocol \"%s\" not supported.\n"), protocol);
   1108 		return (SA_INVALID_PROTOCOL);
   1109 	}
   1110 
   1111 	/*
   1112 	 * Check to see if the new protocol is one that requires
   1113 	 * resource names and make sure we are compliant before
   1114 	 * proceeding.
   1115 	 */
   1116 	if ((features & SA_FEATURE_RESOURCE) &&
   1117 	    !resource_compliant(group)) {
   1118 		if (force && !dryrun) {
   1119 			make_resources(group);
   1120 		} else {
   1121 			(void) printf(
   1122 			    gettext("Protocol requires resource names to be "
   1123 			    "set: %s\n"), protocol);
   1124 			return (SA_RESOURCE_REQUIRED);
   1125 		}
   1126 	}
   1127 	return (SA_OK);
   1128 }
   1129 
   1130 /*
   1131  * set_all_protocols(group)
   1132  *
   1133  * Get the list of all protocols and add all server protocols to the
   1134  * group.
   1135  */
   1136 
   1137 static int
   1138 set_all_protocols(sa_group_t group)
   1139 {
   1140 	char **protolist;
   1141 	int numprotos, i;
   1142 	uint64_t features;
   1143 	sa_optionset_t optionset;
   1144 	int ret = SA_OK;
   1145 
   1146 	/*
   1147 	 * Now make sure we really want to put this protocol on a
   1148 	 * group. Only server protocols can go here.
   1149 	 */
   1150 	numprotos = sa_get_protocols(&protolist);
   1151 	for (i = 0; i < numprotos; i++) {
   1152 		features = sa_proto_get_featureset(protolist[i]);
   1153 		if (features & SA_FEATURE_SERVER) {
   1154 			optionset = sa_create_optionset(group, protolist[i]);
   1155 			if (optionset == NULL) {
   1156 				ret = SA_NO_MEMORY;
   1157 				break;
   1158 			}
   1159 		}
   1160 	}
   1161 
   1162 	if (protolist != NULL)
   1163 		free(protolist);
   1164 
   1165 	return (ret);
   1166 }
   1167 
   1168 /*
   1169  * sa_create(flags, argc, argv)
   1170  *	create a new group
   1171  *	this may or may not have a protocol associated with it.
   1172  *	No protocol means "all" protocols in this case.
   1173  */
   1174 static int
   1175 sa_create(sa_handle_t handle, int flags, int argc, char *argv[])
   1176 {
   1177 	char *groupname;
   1178 
   1179 	sa_group_t group;
   1180 	boolean_t force = B_FALSE;
   1181 	boolean_t verbose = B_FALSE;
   1182 	boolean_t dryrun = B_FALSE;
   1183 	int c;
   1184 	char *protocol = NULL;
   1185 	int ret = SA_OK;
   1186 	struct options *optlist = NULL;
   1187 	int err = SA_OK;
   1188 	int auth;
   1189 	boolean_t created = B_FALSE;
   1190 
   1191 	while ((c = getopt(argc, argv, "?fhvnP:p:")) != EOF) {
   1192 		switch (c) {
   1193 		case 'f':
   1194 			force = B_TRUE;
   1195 			break;
   1196 		case 'v':
   1197 			verbose = B_TRUE;
   1198 			break;
   1199 		case 'n':
   1200 			dryrun = B_TRUE;
   1201 			break;
   1202 		case 'P':
   1203 			if (protocol != NULL) {
   1204 				(void) printf(gettext("Specifying "
   1205 				    "multiple protocols "
   1206 				    "not supported: %s\n"), protocol);
   1207 				return (SA_SYNTAX_ERR);
   1208 			}
   1209 			protocol = optarg;
   1210 			if (sa_valid_protocol(protocol))
   1211 				break;
   1212 			(void) printf(