13034Sdougm /* 23034Sdougm * CDDL HEADER START 33034Sdougm * 43034Sdougm * The contents of this file are subject to the terms of the 53034Sdougm * Common Development and Distribution License (the "License"). 63034Sdougm * You may not use this file except in compliance with the License. 73034Sdougm * 83034Sdougm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 93034Sdougm * or http://www.opensolaris.org/os/licensing. 103034Sdougm * See the License for the specific language governing permissions 113034Sdougm * and limitations under the License. 123034Sdougm * 133034Sdougm * When distributing Covered Code, include this CDDL HEADER in each 143034Sdougm * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 153034Sdougm * If applicable, add the following below this CDDL HEADER, with the 163034Sdougm * fields enclosed by brackets "[]" replaced with your own identifying 173034Sdougm * information: Portions Copyright [yyyy] [name of copyright owner] 183034Sdougm * 193034Sdougm * CDDL HEADER END 203034Sdougm */ 213034Sdougm 223034Sdougm /* 233348Sdougm * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 243034Sdougm * Use is subject to license terms. 253034Sdougm */ 263034Sdougm 273034Sdougm #pragma ident "%Z%%M% %I% %E% SMI" 283034Sdougm 293034Sdougm /* 303034Sdougm * Share control API 313034Sdougm */ 323034Sdougm #include <stdio.h> 333034Sdougm #include <string.h> 343034Sdougm #include <ctype.h> 353034Sdougm #include <sys/types.h> 363034Sdougm #include <sys/stat.h> 37*3663Sdougm #include <fcntl.h> 383034Sdougm #include <unistd.h> 393034Sdougm #include <libxml/parser.h> 403034Sdougm #include <libxml/tree.h> 413034Sdougm #include "libshare.h" 423034Sdougm #include "libshare_impl.h" 433034Sdougm #include <libscf.h> 443034Sdougm #include "scfutil.h" 453034Sdougm #include <ctype.h> 463034Sdougm #include <libintl.h> 473034Sdougm 483034Sdougm #if _NOT_SMF 493034Sdougm #define CONFIG_FILE "/var/tmp/share.cfg" 503034Sdougm #define CONFIG_FILE_TMP "/var/tmp/share.cfg.tmp" 513034Sdougm #endif 523034Sdougm #define TSTAMP(tm) (uint64_t)(((uint64_t)tm.tv_sec << 32) | \ 533034Sdougm (tm.tv_nsec & 0xffffffff)) 543034Sdougm 55*3663Sdougm #define DFS_LOCK_FILE "/etc/dfs/fstypes" 56*3663Sdougm 573034Sdougm /* 583034Sdougm * internal data structures 593034Sdougm */ 603034Sdougm 613034Sdougm static xmlNodePtr sa_config_tree; /* the current config */ 623034Sdougm static xmlDocPtr sa_config_doc = NULL; /* current config document */ 633034Sdougm extern struct sa_proto_plugin *sap_proto_list; 643034Sdougm 653034Sdougm /* current SMF/SVC repository handle */ 663034Sdougm static scfutilhandle_t *scf_handle = NULL; 673034Sdougm extern void getlegacyconfig(char *, xmlNodePtr *); 683034Sdougm extern int gettransients(xmlNodePtr *); 693034Sdougm extern int sa_valid_property(void *, char *, sa_property_t); 703034Sdougm extern char *sa_fstype(char *); 713034Sdougm extern int sa_is_share(void *); 723034Sdougm extern ssize_t scf_max_name_len; /* defined in scfutil during initialization */ 733034Sdougm extern int sa_group_is_zfs(sa_group_t); 743034Sdougm extern int sa_path_is_zfs(char *); 753034Sdougm extern int sa_zfs_set_sharenfs(sa_group_t, char *, int); 763034Sdougm extern void update_legacy_config(void); 773034Sdougm extern int issubdir(char *, char *); 783218Sdougm extern void sa_zfs_init(void); 793218Sdougm extern void sa_zfs_fini(void); 80*3663Sdougm extern void sablocksigs(sigset_t *); 81*3663Sdougm extern void saunblocksigs(sigset_t *); 823034Sdougm 833034Sdougm static int sa_initialized = 0; 843034Sdougm 853034Sdougm /* helper functions */ 863034Sdougm 873034Sdougm char * 883034Sdougm sa_errorstr(int err) 893034Sdougm { 903034Sdougm static char errstr[32]; 913034Sdougm char *ret = NULL; 923034Sdougm 933034Sdougm switch (err) { 943034Sdougm case SA_OK: 953407Sdougm ret = dgettext(TEXT_DOMAIN, "ok"); 963034Sdougm break; 973034Sdougm case SA_NO_SUCH_PATH: 983407Sdougm ret = dgettext(TEXT_DOMAIN, "path doesn't exist"); 993034Sdougm break; 1003034Sdougm case SA_NO_MEMORY: 1013407Sdougm ret = dgettext(TEXT_DOMAIN, "no memory"); 1023034Sdougm break; 1033034Sdougm case SA_DUPLICATE_NAME: 1043407Sdougm ret = dgettext(TEXT_DOMAIN, "name in use"); 1053034Sdougm break; 1063034Sdougm case SA_BAD_PATH: 1073407Sdougm ret = dgettext(TEXT_DOMAIN, "bad path"); 1083034Sdougm break; 1093034Sdougm case SA_NO_SUCH_GROUP: 1103407Sdougm ret = dgettext(TEXT_DOMAIN, "no such group"); 1113034Sdougm break; 1123034Sdougm case SA_CONFIG_ERR: 1133407Sdougm ret = dgettext(TEXT_DOMAIN, "configuration error"); 1143034Sdougm break; 1153034Sdougm case SA_SYSTEM_ERR: 1163407Sdougm ret = dgettext(TEXT_DOMAIN, "system error"); 1173034Sdougm break; 1183034Sdougm case SA_SYNTAX_ERR: 1193407Sdougm ret = dgettext(TEXT_DOMAIN, "syntax error"); 1203034Sdougm break; 1213034Sdougm case SA_NO_PERMISSION: 1223407Sdougm ret = dgettext(TEXT_DOMAIN, "no permission"); 1233034Sdougm break; 1243034Sdougm case SA_BUSY: 1253407Sdougm ret = dgettext(TEXT_DOMAIN, "busy"); 1263034Sdougm break; 1273034Sdougm case SA_NO_SUCH_PROP: 1283407Sdougm ret = dgettext(TEXT_DOMAIN, "no such property"); 1293034Sdougm break; 1303034Sdougm case SA_INVALID_NAME: 1313407Sdougm ret = dgettext(TEXT_DOMAIN, "invalid name"); 1323034Sdougm break; 1333034Sdougm case SA_INVALID_PROTOCOL: 1343407Sdougm ret = dgettext(TEXT_DOMAIN, "invalid protocol"); 1353034Sdougm break; 1363034Sdougm case SA_NOT_ALLOWED: 1373407Sdougm ret = dgettext(TEXT_DOMAIN, "operation not allowed"); 1383034Sdougm break; 1393034Sdougm case SA_BAD_VALUE: 1403407Sdougm ret = dgettext(TEXT_DOMAIN, "bad property value"); 1413034Sdougm break; 1423034Sdougm case SA_INVALID_SECURITY: 1433407Sdougm ret = dgettext(TEXT_DOMAIN, "invalid security type"); 1443034Sdougm break; 1453034Sdougm case SA_NO_SUCH_SECURITY: 1463407Sdougm ret = dgettext(TEXT_DOMAIN, "security type not found"); 1473034Sdougm break; 1483034Sdougm case SA_VALUE_CONFLICT: 1493407Sdougm ret = dgettext(TEXT_DOMAIN, "property value conflict"); 1503034Sdougm break; 1513034Sdougm case SA_NOT_IMPLEMENTED: 1523407Sdougm ret = dgettext(TEXT_DOMAIN, "not implemented"); 1533034Sdougm break; 1543034Sdougm case SA_INVALID_PATH: 1553407Sdougm ret = dgettext(TEXT_DOMAIN, "invalid path"); 1563034Sdougm break; 1573034Sdougm case SA_NOT_SUPPORTED: 1583407Sdougm ret = dgettext(TEXT_DOMAIN, "operation not supported"); 1593034Sdougm break; 1603034Sdougm case SA_PROP_SHARE_ONLY: 1613407Sdougm ret = dgettext(TEXT_DOMAIN, "property not valid for group"); 1623034Sdougm break; 1633034Sdougm case SA_NOT_SHARED: 1643407Sdougm ret = dgettext(TEXT_DOMAIN, "not shared"); 1653034Sdougm break; 1663034Sdougm default: 1673034Sdougm (void) snprintf(errstr, sizeof (errstr), 1683407Sdougm dgettext(TEXT_DOMAIN, "unknown %d"), err); 1693034Sdougm ret = errstr; 1703034Sdougm } 1713034Sdougm return (ret); 1723034Sdougm } 1733034Sdougm 1743034Sdougm /* 1753034Sdougm * set_legacy_timestamp(root, path, timevalue) 1763034Sdougm * 1773034Sdougm * add the current timestamp value to the configuration for use in 1783034Sdougm * determining when to update the legacy files. For SMF, this 1793034Sdougm * property is kept in default/operation/legacy_timestamp 1803034Sdougm */ 1813034Sdougm 1823034Sdougm static void 1833034Sdougm set_legacy_timestamp(xmlNodePtr root, char *path, uint64_t tval) 1843034Sdougm { 1853034Sdougm xmlNodePtr node; 1863034Sdougm xmlChar *lpath = NULL; 1873034Sdougm 1883034Sdougm for (node = root->xmlChildrenNode; node != NULL; 1893034Sdougm node = node->next) { 1903034Sdougm if (xmlStrcmp(node->name, (xmlChar *)"legacy") == 0) { 1913034Sdougm /* a possible legacy node for this path */ 1923034Sdougm lpath = xmlGetProp(node, (xmlChar *)"path"); 1933034Sdougm if (lpath != NULL && xmlStrcmp(lpath, (xmlChar *)path) == 0) { 1943034Sdougm xmlFree(lpath); 1953034Sdougm break; 1963034Sdougm } 1973034Sdougm if (lpath != NULL) 1983034Sdougm xmlFree(lpath); 1993034Sdougm } 2003034Sdougm } 2013034Sdougm if (node == NULL) { 2023034Sdougm /* need to create the first legacy timestamp node */ 2033034Sdougm node = xmlNewChild(root, NULL, (xmlChar *)"legacy", NULL); 2043034Sdougm } 2053034Sdougm if (node != NULL) { 2063034Sdougm char tstring[32]; 2073034Sdougm int ret; 2083034Sdougm 2093034Sdougm (void) snprintf(tstring, sizeof (tstring), "%lld", tval); 2103034Sdougm xmlSetProp(node, (xmlChar *)"timestamp", (xmlChar *)tstring); 2113034Sdougm xmlSetProp(node, (xmlChar *)"path", (xmlChar *)path); 2123034Sdougm /* now commit to SMF */ 2133034Sdougm ret = sa_get_instance(scf_handle, "default"); 2143034Sdougm if (ret == SA_OK) { 2153034Sdougm ret = sa_start_transaction(scf_handle, "operation"); 2163034Sdougm if (ret == SA_OK) { 2173034Sdougm ret = sa_set_property(scf_handle, "legacy-timestamp", 2183034Sdougm tstring); 2193034Sdougm if (ret == SA_OK) { 2203034Sdougm (void) sa_end_transaction(scf_handle); 2213034Sdougm } else { 2223034Sdougm sa_abort_transaction(scf_handle); 2233034Sdougm } 2243034Sdougm } 2253034Sdougm } 2263034Sdougm } 2273034Sdougm } 2283034Sdougm 2293034Sdougm /* 2303034Sdougm * is_shared(share) 2313034Sdougm * 2323034Sdougm * determine if the specified share is currently shared or not. 2333034Sdougm */ 2343034Sdougm static int 2353034Sdougm is_shared(sa_share_t share) 2363034Sdougm { 2373034Sdougm char *shared; 2383034Sdougm int result = 0; /* assume not */ 2393034Sdougm 2403034Sdougm shared = sa_get_share_attr(share, "shared"); 2413034Sdougm if (shared != NULL) { 2423034Sdougm if (strcmp(shared, "true") == 0) 2433034Sdougm result = 1; 2443034Sdougm sa_free_attr_string(shared); 2453034Sdougm } 2463034Sdougm return (result); 2473034Sdougm } 2483034Sdougm 2493034Sdougm /* 250*3663Sdougm * checksubdirgroup(group, newpath, strictness) 2513348Sdougm * 252*3663Sdougm * check all the specified newpath against all the paths in the 253*3663Sdougm * group. This is a helper function for checksubdir to make it easier 254*3663Sdougm * to also check ZFS subgroups. 255*3663Sdougm * The strictness values mean: 2563348Sdougm * SA_CHECK_NORMAL == only check newpath against shares that are active 2573348Sdougm * SA_CHECK_STRICT == check newpath against both active shares and those 2583348Sdougm * stored in the repository 2593034Sdougm */ 2603034Sdougm static int 261*3663Sdougm checksubdirgroup(sa_group_t group, char *newpath, int strictness) 2623034Sdougm { 2633034Sdougm sa_share_t share; 264*3663Sdougm char *path; 265*3663Sdougm int issub = SA_OK; 2663034Sdougm 267*3663Sdougm for (share = sa_get_share(group, NULL); share != NULL; 268*3663Sdougm share = sa_get_next_share(share)) { 2693034Sdougm /* 2703034Sdougm * The original behavior of share never checked 2713034Sdougm * against the permanent configuration 2723034Sdougm * (/etc/dfs/dfstab). PIT has a number of cases where 2733034Sdougm * it depends on this older behavior even though it 2743034Sdougm * could be considered incorrect. We may tighten this 2753034Sdougm * up in the future. 2763034Sdougm */ 277*3663Sdougm if (strictness == SA_CHECK_NORMAL && !is_shared(share)) 278*3663Sdougm continue; 2793034Sdougm 280*3663Sdougm path = sa_get_share_attr(share, "path"); 2813348Sdougm /* 2823348Sdougm * If path is NULL, then a share is in the process of 2833348Sdougm * construction or someone has modified the property 284*3663Sdougm * group inappropriately. It should be 285*3663Sdougm * ignored. issubdir() comes from the original share 286*3663Sdougm * implementation and does the difficult part of 287*3663Sdougm * checking subdirectories. 2883348Sdougm */ 289*3663Sdougm if (path == NULL) 290*3663Sdougm continue; 291*3663Sdougm if (newpath != NULL && 292*3663Sdougm (strcmp(path, newpath) == 0 || issubdir(newpath, path) || 293*3663Sdougm issubdir(path, newpath))) { 2943034Sdougm sa_free_attr_string(path); 2953034Sdougm path = NULL; 296*3663Sdougm issub = SA_INVALID_PATH; 297*3663Sdougm break; 298*3663Sdougm } 299*3663Sdougm sa_free_attr_string(path); 300*3663Sdougm path = NULL; 301*3663Sdougm } 302*3663Sdougm return (issub); 303*3663Sdougm } 304*3663Sdougm 305*3663Sdougm /* 306*3663Sdougm * checksubdir(newpath, strictness) 307*3663Sdougm * 308*3663Sdougm * checksubdir determines if the specified path (newpath) is a 309*3663Sdougm * subdirectory of another share. It calls checksubdirgroup() to do 310*3663Sdougm * the complicated work. The strictness parameter determines how 311*3663Sdougm * strict a check to make against the path. The strictness values 312*3663Sdougm * mean: SA_CHECK_NORMAL == only check newpath against shares that are 313*3663Sdougm * active SA_CHECK_STRICT == check newpath against both active shares 314*3663Sdougm * and those * stored in the repository 315*3663Sdougm */ 316*3663Sdougm static int 317*3663Sdougm checksubdir(char *newpath, int strictness) 318*3663Sdougm { 319*3663Sdougm sa_group_t group; 320*3663Sdougm int issub; 321*3663Sdougm char *path = NULL; 322*3663Sdougm 323*3663Sdougm for (issub = 0, group = sa_get_group(NULL); 324*3663Sdougm group != NULL && !issub; 325*3663Sdougm group = sa_get_next_group(group)) { 326*3663Sdougm if (sa_group_is_zfs(group)) { 327*3663Sdougm sa_group_t subgroup; 328*3663Sdougm for (subgroup = sa_get_sub_group(group); 329*3663Sdougm subgroup != NULL && !issub; 330*3663Sdougm subgroup = sa_get_next_group(subgroup)) 331*3663Sdougm issub = checksubdirgroup(subgroup, newpath, strictness); 332*3663Sdougm } else { 333*3663Sdougm issub = checksubdirgroup(group, newpath, strictness); 3343034Sdougm } 3353034Sdougm } 3363034Sdougm if (path != NULL) 3373034Sdougm sa_free_attr_string(path); 3383034Sdougm return (issub); 3393034Sdougm } 3403034Sdougm 3413034Sdougm /* 3423348Sdougm * validpath(path, strictness) 3433034Sdougm * determine if the provided path is valid for a share. It shouldn't 3443034Sdougm * be a sub-dir of an already shared path or the parent directory of a 3453034Sdougm * share path. 3463034Sdougm */ 3473034Sdougm static int 3483348Sdougm validpath(char *path, int strictness) 3493034Sdougm { 3503034Sdougm int error = SA_OK; 3513034Sdougm struct stat st; 3523034Sdougm sa_share_t share; 3533034Sdougm char *fstype; 3543034Sdougm 3553034Sdougm if (*path != '/') { 3563034Sdougm return (SA_BAD_PATH); 3573034Sdougm } 3583034Sdougm if (stat(path, &st) < 0) { 3593034Sdougm error = SA_NO_SUCH_PATH; 3603034Sdougm } else { 3613034Sdougm share = sa_find_share(path); 3623034Sdougm if (share != NULL) { 3633034Sdougm error = SA_DUPLICATE_NAME; 3643034Sdougm } 3653034Sdougm if (error == SA_OK) { 3663034Sdougm /* 3673034Sdougm * check for special case with file system that might 3683034Sdougm * have restrictions. For now, ZFS is the only case 3693034Sdougm * since it has its own idea of how to configure 3703034Sdougm * shares. We do this before subdir checking since 3713034Sdougm * things like ZFS will do that for us. This should 3723034Sdougm * also be done via plugin interface. 3733034Sdougm */ 3743034Sdougm fstype = sa_fstype(path); 3753034Sdougm if (fstype != NULL && strcmp(fstype, "zfs") == 0) { 3763034Sdougm if (sa_zfs_is_shared(path)) 377*3663Sdougm error = SA_INVALID_NAME; 3783034Sdougm } 3793034Sdougm if (fstype != NULL) 3803034Sdougm sa_free_fstype(fstype); 3813034Sdougm } 3823034Sdougm if (error == SA_OK) { 3833348Sdougm error = checksubdir(path, strictness); 3843034Sdougm } 3853034Sdougm } 3863034Sdougm return (error); 3873034Sdougm } 3883034Sdougm 3893034Sdougm /* 3903034Sdougm * check to see if group/share is persistent. 3913034Sdougm */ 3923034Sdougm static int 3933034Sdougm is_persistent(sa_group_t group) 3943034Sdougm { 3953034Sdougm char *type; 3963034Sdougm int persist = 1; 3973034Sdougm 3983034Sdougm type = sa_get_group_attr(group, "type"); 3993034Sdougm if (type != NULL && strcmp(type, "transient") == 0) 4003034Sdougm persist = 0; 4013034Sdougm if (type != NULL) 4023034Sdougm sa_free_attr_string(type); 4033034Sdougm return (persist); 4043034Sdougm } 4053034Sdougm 4063034Sdougm /* 4073034Sdougm * sa_valid_group_name(name) 4083034Sdougm * 4093034Sdougm * check that the "name" contains only valid characters and otherwise 4103034Sdougm * fits the required naming conventions. Valid names must start with 4113034Sdougm * an alphabetic and the remainder may consist of only alphanumeric 4123034Sdougm * plus the '-' and '_' characters. This name limitation comes from 4133034Sdougm * inherent limitations in SMF. 4143034Sdougm */ 4153034Sdougm 4163034Sdougm int 4173034Sdougm sa_valid_group_name(char *name) 4183034Sdougm { 4193034Sdougm int ret = 1; 4203034Sdougm ssize_t len; 4213034Sdougm 4223034Sdougm if (name != NULL && isalpha(*name)) { 4233034Sdougm char c; 4243034Sdougm len = strlen(name); 4253034Sdougm if (len < (scf_max_name_len - sizeof ("group:"))) { 4263034Sdougm for (c = *name++; c != '\0' && ret != 0; c = *name++) { 4273034Sdougm if (!isalnum(c) && c != '-' && c != '_') 4283034Sdougm ret = 0; 4293034Sdougm } 4303034Sdougm } else { 4313034Sdougm ret = 0; 4323034Sdougm } 4333034Sdougm } else { 4343034Sdougm ret = 0; 4353034Sdougm } 4363034Sdougm return (ret); 4373034Sdougm } 4383034Sdougm 4393034Sdougm 4403034Sdougm /* 4413034Sdougm * is_zfs_group(group) 4423034Sdougm * Determine if the specified group is a ZFS sharenfs group 4433034Sdougm */ 4443034Sdougm static int 4453034Sdougm is_zfs_group(sa_group_t group) 4463034Sdougm { 4473034Sdougm int ret = 0; 4483034Sdougm xmlNodePtr parent; 4493034Sdougm xmlChar *zfs; 4503034Sdougm 4513034Sdougm if (strcmp((char *)((xmlNodePtr)group)->name, "share") == 0) { 4523034Sdougm parent = (xmlNodePtr)sa_get_parent_group(group); 4533034Sdougm } else { 4543034Sdougm parent = (xmlNodePtr)group; 4553034Sdougm } 4563034Sdougm zfs = xmlGetProp(parent, (xmlChar *)"zfs"); 4573034Sdougm if (zfs != NULL) { 4583034Sdougm xmlFree(zfs); 4593034Sdougm ret = 1; 4603034Sdougm } 4613034Sdougm return (ret); 4623034Sdougm } 4633034Sdougm 4643034Sdougm /* 4653034Sdougm * sa_optionset_name(optionset, oname, len, id) 4663034Sdougm * return the SMF name for the optionset. If id is not NULL, it 4673034Sdougm * will have the GUID value for a share and should be used 4683034Sdougm * instead of the keyword "optionset" which is used for 4693034Sdougm * groups. If the optionset doesn't have a protocol type 4703034Sdougm * associated with it, "default" is used. This shouldn't happen 4713034Sdougm * at this point but may be desirable in the future if there are 4723034Sdougm * protocol independent properties added. The name is returned in 4733034Sdougm * oname. 4743034Sdougm */ 4753034Sdougm 4763034Sdougm static int 4773034Sdougm sa_optionset_name(sa_optionset_t optionset, char *oname, size_t len, char *id) 4783034Sdougm { 4793034Sdougm char *proto; 4803034Sdougm 4813034Sdougm if (id == NULL) 4823034Sdougm id = "optionset"; 4833034Sdougm 4843034Sdougm proto = sa_get_optionset_attr(optionset, "type"); 4853034Sdougm len = snprintf(oname, len, "%s_%s", id, proto ? proto : "default"); 4863034Sdougm 4873034Sdougm if (proto != NULL) 4883034Sdougm sa_free_attr_string(proto); 4893034Sdougm return (len); 4903034Sdougm } 4913034Sdougm 4923034Sdougm /* 4933034Sdougm * sa_security_name(optionset, oname, len, id) 4943034Sdougm * 4953034Sdougm * return the SMF name for the security. If id is not NULL, it will 4963034Sdougm * have the GUID value for a share and should be used instead of the 4973034Sdougm * keyword "optionset" which is used for groups. If the optionset 4983034Sdougm * doesn't have a protocol type associated with it, "default" is 4993034Sdougm * used. This shouldn't happen at this point but may be desirable in 5003034Sdougm * the future if there are protocol independent properties added. The 5013034Sdougm * name is returned in oname. The security type is also encoded into 5023034Sdougm * the name. In the future, this wil *be handled a bit differently. 5033034Sdougm */ 5043034Sdougm 5053034Sdougm static int 5063034Sdougm sa_security_name(sa_security_t security, char *oname, size_t len, char *id) 5073034Sdougm { 5083034Sdougm char *proto; 5093034Sdougm char *sectype; 5103034Sdougm 5113034Sdougm if (id == NULL) 5123034Sdougm id = "optionset"; 5133034Sdougm 5143034Sdougm proto = sa_get_security_attr(security, "type"); 5153034Sdougm sectype = sa_get_security_attr(security, "sectype"); 5163034Sdougm len = snprintf(oname, len, "%s_%s_%s", id, 5173034Sdougm proto ? proto : "default", 5183034Sdougm sectype ? sectype : "default"); 5193034Sdougm if (proto != NULL) 5203034Sdougm sa_free_attr_string(proto); 5213034Sdougm if (sectype != NULL) 5223034Sdougm sa_free_attr_string(sectype); 5233034Sdougm return (len); 5243034Sdougm } 5253034Sdougm 5263034Sdougm /* 5273348Sdougm * sa_init(init_service) 5283034Sdougm * Initialize the API 5293034Sdougm * find all the shared objects 5303034Sdougm * init the tables with all objects 5313034Sdougm * read in the current configuration 5323034Sdougm */ 5333034Sdougm 5343034Sdougm void 5353034Sdougm sa_init(int init_service) 5363034Sdougm { 5373034Sdougm struct stat st; 5383034Sdougm int legacy = 0; 5393034Sdougm uint64_t tval = 0; 540*3663Sdougm int lockfd; 541*3663Sdougm sigset_t old; 542*3663Sdougm int updatelegacy = B_FALSE; 543*3663Sdougm scf_simple_prop_t *prop; 5443034Sdougm 5453034Sdougm if (!sa_initialized) { 5463034Sdougm /* get protocol specific structures */ 5473034Sdougm (void) proto_plugin_init(); 5483034Sdougm if (init_service & SA_INIT_SHARE_API) { 5493034Sdougm /* 5503218Sdougm * initialize access into libzfs. We use this when 5513218Sdougm * collecting info about ZFS datasets and shares. 5523218Sdougm */ 5533218Sdougm sa_zfs_init(); 5543218Sdougm /* 5553034Sdougm * since we want to use SMF, initialize an svc handle 5563034Sdougm * and find out what is there. 5573034Sdougm */ 5583034Sdougm scf_handle = sa_scf_init(); 5593034Sdougm if (scf_handle != NULL) { 560*3663Sdougm /* 561*3663Sdougm * Need to lock the extraction of the 562*3663Sdougm * configuration if the dfstab file has 563*3663Sdougm * changed. Lock everything now and release if 564*3663Sdougm * not needed. Use a file that isn't being 565*3663Sdougm * manipulated by other parts of the system in 566*3663Sdougm * order to not interfere with locking. Using 567*3663Sdougm * dfstab doesn't work. 568*3663Sdougm */ 569*3663Sdougm sablocksigs(&old); 570*3663Sdougm lockfd = open(DFS_LOCK_FILE, O_RDWR); 571*3663Sdougm if (lockfd >= 0) { 572*3663Sdougm extern int errno; 573*3663Sdougm errno = 0; 574*3663Sdougm (void) lockf(lockfd, F_LOCK, 0); 575*3663Sdougm /* 576*3663Sdougm * Check whether we are going to need to merge 577*3663Sdougm * any dfstab changes. This is done by 578*3663Sdougm * comparing the value of legacy-timestamp 579*3663Sdougm * with the current st_ctim of the file. If 580*3663Sdougm * they are different, an update is needed and 581*3663Sdougm * the file must remain locked until the merge 582*3663Sdougm * is done in order to prevent multiple 583*3663Sdougm * startups from changing the SMF repository 584*3663Sdougm * at the same time. The first to get the 585*3663Sdougm * lock will make any changes before the 586*3663Sdougm * others can read the repository. 587*3663Sdougm */ 588*3663Sdougm prop = scf_simple_prop_get(scf_handle->handle, 589*3663Sdougm (const char *) 590*3663Sdougm SA_SVC_FMRI_BASE ":default", 591*3663Sdougm "operation", 592*3663Sdougm "legacy-timestamp"); 593*3663Sdougm if (prop != NULL) { 594*3663Sdougm char *i64; 595*3663Sdougm i64 = scf_simple_prop_next_astring(prop); 596*3663Sdougm if (i64 != NULL) { 597*3663Sdougm tval = strtoull(i64, NULL, 0); 598*3663Sdougm } 599*3663Sdougm if (stat(SA_LEGACY_DFSTAB, &st) >= 0 && 600*3663Sdougm tval != TSTAMP(st.st_ctim)) { 601*3663Sdougm updatelegacy = B_TRUE; 602*3663Sdougm } 603*3663Sdougm } else { 604*3663Sdougm /* We haven't set the timestamp before so do it. */ 605*3663Sdougm updatelegacy = B_TRUE; 606*3663Sdougm } 607*3663Sdougm } 608*3663Sdougm if (updatelegacy == B_FALSE) { 609*3663Sdougm /* Don't need the lock anymore */ 610*3663Sdougm (void) lockf(lockfd, F_ULOCK, 0); 611*3663Sdougm (void) close(lockfd); 612*3663Sdougm } 6133034Sdougm (void) sa_get_config(scf_handle, &sa_config_tree, 6143034Sdougm &sa_config_doc); 615*3663Sdougm saunblocksigs(&old); 6163034Sdougm if (tval == 0) { 6173034Sdougm /* first time so make sure default is setup */ 6183034Sdougm sa_group_t defgrp; 6193034Sdougm sa_optionset_t opt; 6203034Sdougm defgrp = sa_get_group("default"); 6213034Sdougm if (defgrp != NULL) { 6223034Sdougm opt = sa_get_optionset(defgrp, NULL); 6233034Sdougm if (opt == NULL) 6243034Sdougm /* NFS is the default for default */ 6253034Sdougm opt = sa_create_optionset(defgrp, "nfs"); 6263034Sdougm } 6273034Sdougm } 628*3663Sdougm if (updatelegacy == B_TRUE) { 629*3663Sdougm sablocksigs(&old); 6303034Sdougm getlegacyconfig(SA_LEGACY_DFSTAB, &sa_config_tree); 6313034Sdougm if (stat(SA_LEGACY_DFSTAB, &st) >= 0) 6323034Sdougm set_legacy_timestamp(sa_config_tree, 6333034Sdougm SA_LEGACY_DFSTAB, 6343034Sdougm TSTAMP(st.st_ctim)); 635*3663Sdougm saunblocksigs(&old); 636*3663Sdougm /* Safe to unlock now to allow others to run */ 637*3663Sdougm (void) lockf(lockfd, F_ULOCK, 0); 638*3663Sdougm (void) close(lockfd); 6393034Sdougm } 6403034Sdougm legacy |= sa_get_zfs_shares("zfs"); 6413034Sdougm legacy |= gettransients(&sa_config_tree); 6423034Sdougm } 6433034Sdougm } 6443034Sdougm } 6453034Sdougm } 6463034Sdougm 6473034Sdougm /* 6483034Sdougm * sa_fini() 6493034Sdougm * Uninitialize the API structures including the configuration 6503218Sdougm * data structures and ZFS related data. 6513034Sdougm */ 6523034Sdougm 6533034Sdougm void 6543034Sdougm sa_fini() 6553034Sdougm { 6563034Sdougm if (sa_initialized) { 6573034Sdougm /* free the config trees */ 6583034Sdougm sa_initialized = 0; 6593034Sdougm if (sa_config_doc != NULL) 6603034Sdougm xmlFreeDoc(sa_config_doc); 6613034Sdougm sa_config_tree = NULL; 6623034Sdougm sa_config_doc = NULL; 6633034Sdougm sa_scf_fini(scf_handle); 6643218Sdougm sa_zfs_fini(); 6653034Sdougm (void) proto_plugin_init(); 6663034Sdougm } 6673034Sdougm } 6683034Sdougm 6693034Sdougm /* 6703034Sdougm * sa_get_protocols(char **protocol) 6713034Sdougm * Get array of protocols that are supported 6723034Sdougm * Returns pointer to an allocated and NULL terminated 6733034Sdougm * array of strings. Caller must free. 6743034Sdougm * This really should be determined dynamically. 6753034Sdougm * If there aren't any defined, return -1. 6763034Sdougm * Use free() to return memory. 6773034Sdougm */ 6783034Sdougm 6793034Sdougm int 6803034Sdougm sa_get_protocols(char ***protocols) 6813034Sdougm { 6823034Sdougm int numproto = -1; 6833034Sdougm 6843034Sdougm if (protocols != NULL) { 6853034Sdougm struct sa_proto_plugin *plug; 6863034Sdougm for (numproto = 0, plug = sap_proto_list; plug != NULL; 6873034Sdougm plug = plug->plugin_next) { 6883034Sdougm numproto++; 6893034Sdougm } 6903034Sdougm 6913034Sdougm *protocols = calloc(numproto + 1, sizeof (char *)); 6923034Sdougm if (*protocols != NULL) { 6933034Sdougm int ret = 0; 6943034Sdougm for (plug = sap_proto_list; plug != NULL; 6953034Sdougm plug = plug->plugin_next) { 6963034Sdougm /* faking for now */ 6973034Sdougm (*protocols)[ret++] = plug->plugin_ops->sa_protocol; 6983034Sdougm } 6993034Sdougm } else { 7003034Sdougm numproto = -1; 7013034Sdougm } 7023034Sdougm } 7033034Sdougm return (numproto); 7043034Sdougm } 7053034Sdougm 7063034Sdougm /* 7073034Sdougm * find_group_by_name(node, group) 7083034Sdougm * 7093034Sdougm * search the XML document subtree specified by node to find the group 7103034Sdougm * specified by group. Searching subtree allows subgroups to be 7113034Sdougm * searched for. 7123034Sdougm */ 7133034Sdougm 7143034Sdougm static xmlNodePtr 7153034Sdougm find_group_by_name(xmlNodePtr node, xmlChar *group) 7163034Sdougm { 7173034Sdougm xmlChar *name = NULL; 7183034Sdougm 7193034Sdougm for (node = node->xmlChildrenNode; node != NULL; 7203034Sdougm node = node->next) { 7213034Sdougm if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) { 7223034Sdougm /* if no groupname, return the first found */ 7233034Sdougm if (group == NULL) 7243034Sdougm break; 7253034Sdougm name = xmlGetProp(node, (xmlChar *)"name"); 7263034Sdougm if (name != NULL && 7273034Sdougm xmlStrcmp(name, group) == 0) { 7283034Sdougm break; 7293034Sdougm } 7303034Sdougm if (name != NULL) { 7313034Sdougm xmlFree(name); 7323034Sdougm name = NULL; 7333034Sdougm } 7343034Sdougm } 7353034Sdougm } 7363034Sdougm if (name != NULL) 7373034Sdougm xmlFree(name); 7383034Sdougm return (node); 7393034Sdougm } 7403034Sdougm 7413034Sdougm /* 7423034Sdougm * sa_get_group(groupname) 7433034Sdougm * Return the "group" specified. If groupname is NULL, 7443034Sdougm * return the first group of the list of groups. 7453034Sdougm */ 7463034Sdougm sa_group_t 7473034Sdougm sa_get_group(char *groupname) 7483034Sdougm { 7493034Sdougm xmlNodePtr node = NULL; 7503034Sdougm char *subgroup = NULL; 7513034Sdougm char *group = NULL; 7523034Sdougm 7533034Sdougm if (sa_config_tree != NULL) { 7543034Sdougm if (groupname != NULL) { 7553034Sdougm group = strdup(groupname); 7563034Sdougm subgroup = strchr(group, '/'); 7573034Sdougm if (subgroup != NULL) 7583034Sdougm *subgroup++ = '\0'; 7593034Sdougm } 7603034Sdougm node = find_group_by_name(sa_config_tree, (xmlChar *)group); 7613034Sdougm /* if a subgroup, find it before returning */ 7623034Sdougm if (subgroup != NULL && node != NULL) { 7633034Sdougm node = find_group_by_name(node, (xmlChar *)subgroup); 7643034Sdougm } 7653034Sdougm } 7663034Sdougm if (node != NULL && (char *)group != NULL) 7673034Sdougm (void) sa_get_instance(scf_handle, (char *)group); 7683034Sdougm if (group != NULL) 7693034Sdougm free(group); 7703034Sdougm return ((sa_group_t)(node)); 7713034Sdougm } 7723034Sdougm 7733034Sdougm /* 7743034Sdougm * sa_get_next_group(group) 7753034Sdougm * Return the "next" group after the specified group from 7763034Sdougm * the internal group list. NULL if there are no more. 7773034Sdougm */ 7783034Sdougm sa_group_t 7793034Sdougm sa_get_next_group(sa_group_t group) 7803034Sdougm { 7813034Sdougm xmlNodePtr ngroup = NULL; 7823034Sdougm if (group != NULL) { 7833034Sdougm for (ngroup = ((xmlNodePtr)group)->next; ngroup != NULL; 7843034Sdougm ngroup = ngroup->next) { 7853034Sdougm if (xmlStrcmp(ngroup->name, (xmlChar *)"group") == 0) 7863034Sdougm break; 7873034Sdougm } 7883034Sdougm } 7893034Sdougm return ((sa_group_t)ngroup); 7903034Sdougm } 7913034Sdougm 7923034Sdougm /* 7933034Sdougm * sa_get_share(group, sharepath) 7943034Sdougm * Return the share object for the share specified. The share 7953034Sdougm * must be in the specified group. Return NULL if not found. 7963034Sdougm */ 7973034Sdougm sa_share_t 7983034Sdougm sa_get_share(sa_group_t group, char *sharepath) 7993034Sdougm { 8003034Sdougm xmlNodePtr node = NULL; 8013034Sdougm xmlChar *path; 8023034Sdougm 8033034Sdougm /* 8043034Sdougm * For future scalability, this should end up building a cache 8053034Sdougm * since it will get called regularly by the mountd and info 8063034Sdougm * services. 8073034Sdougm */ 8083034Sdougm if (group != NULL) { 8093034Sdougm for (node = ((xmlNodePtr)group)->children; node != NULL; 8103034Sdougm node = node->next) { 8113034Sdougm if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) { 8123034Sdougm if (sharepath == NULL) { 8133034Sdougm break; 8143034Sdougm } else { 8153034Sdougm /* is it the correct share? */ 8163034Sdougm path = xmlGetProp(node, (xmlChar *)"path"); 8173034Sdougm if (path != NULL && 8183034Sdougm xmlStrcmp(path, (xmlChar *)sharepath) == 0) { 8193034Sdougm xmlFree(path); 8203034Sdougm break; 8213034Sdougm } 8223034Sdougm xmlFree(path); 8233034Sdougm } 8243034Sdougm } 8253034Sdougm } 8263034Sdougm } 8273034Sdougm return ((sa_share_t)node); 8283034Sdougm } 8293034Sdougm 8303034Sdougm /* 8313034Sdougm * sa_get_next_share(share) 8323034Sdougm * Return the next share following the specified share 8333034Sdougm * from the internal list of shares. Returns NULL if there 8343034Sdougm * are no more shares. The list is relative to the same 8353034Sdougm * group. 8363034Sdougm */ 8373034Sdougm sa_share_t 8383034Sdougm sa_get_next_share(sa_share_t share) 8393034Sdougm { 8403034Sdougm xmlNodePtr node = NULL; 8413034Sdougm 8423034Sdougm if (share != NULL) { 8433034Sdougm for (node = ((xmlNodePtr)share)->next; node != NULL; 8443034Sdougm node = node->next) { 8453034Sdougm if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) { 8463034Sdougm break; 8473034Sdougm } 8483034Sdougm } 8493034Sdougm } 8503034Sdougm return ((sa_share_t)node); 8513034Sdougm } 8523034Sdougm 8533034Sdougm /* 8543034Sdougm * _sa_get_child_node(node, type) 8553034Sdougm * 8563034Sdougm * find the child node of the specified node that has "type". This is 8573034Sdougm * used to implement several internal functions. 8583034Sdougm */ 8593034Sdougm 8603034Sdougm static xmlNodePtr 8613034Sdougm _sa_get_child_node(xmlNodePtr node, xmlChar *type) 8623034Sdougm { 8633034Sdougm xmlNodePtr child; 8643034Sdougm for (child = node->xmlChildrenNode; child != NULL; 8653034Sdougm child = child->next) 8663034Sdougm if (xmlStrcmp(child->name, type) == 0) 8673034Sdougm return (child); 8683034Sdougm return ((xmlNodePtr)NULL); 8693034Sdougm } 8703034Sdougm 8713034Sdougm /* 8723034Sdougm * find_share(group, path) 8733034Sdougm * 8743034Sdougm * Search all the shares in the specified group for one that has the 8753034Sdougm * specified path. 8763034Sdougm */ 8773034Sdougm 8783034Sdougm static sa_share_t 8793034Sdougm find_share(sa_group_t group, char *sharepath) 8803034Sdougm { 8813034Sdougm sa_share_t share; 8823034Sdougm char *path; 8833034Sdougm 8843034Sdougm for (share = sa_get_share(group, NULL); share != NULL; 8853034Sdougm share = sa_get_next_share(share)) { 8863034Sdougm path = sa_get_share_attr(share, "path"); 8873034Sdougm if (path != NULL && strcmp(path, sharepath) == 0) { 8883034Sdougm sa_free_attr_string(path); 8893034Sdougm break; 8903034Sdougm } 8913034Sdougm if (path != NULL) 8923034Sdougm sa_free_attr_string(path); 8933034Sdougm } 8943034Sdougm return (share); 8953034Sdougm } 8963034Sdougm 8973034Sdougm /* 8983034Sdougm * sa_get_sub_group(group) 8993034Sdougm * 9003034Sdougm * Get the first sub-group of group. The sa_get_next_group() function 9013034Sdougm * can be used to get the rest. This is currently only used for ZFS 9023034Sdougm * sub-groups but could be used to implement a more general mechanism. 9033034Sdougm */ 9043034Sdougm 9053034Sdougm sa_group_t 9063034Sdougm sa_get_sub_group(sa_group_t group) 9073034Sdougm { 9083034Sdougm return ((sa_group_t)_sa_get_child_node((xmlNodePtr)group, 9093034Sdougm (xmlChar *)"group")); 9103034Sdougm } 9113034Sdougm 9123034Sdougm /* 9133034Sdougm * sa_find_share(sharepath) 9143034Sdougm * Finds a share regardless of group. In the future, this 9153034Sdougm * function should utilize a cache and hash table of some kind. 9163034Sdougm * The current assumption is that a path will only be shared 9173034Sdougm * once. In the future, this may change as implementation of 9183034Sdougm * resource names comes into being. 9193034Sdougm */ 9203034Sdougm sa_share_t 9213034Sdougm sa_find_share(char *sharepath) 9223034Sdougm { 9233034Sdougm sa_group_t group; 9243034Sdougm sa_group_t zgroup; 9253034Sdougm sa_share_t share = NULL; 9263034Sdougm int done = 0; 9273034Sdougm 9283034Sdougm for (group = sa_get_group(NULL); group != NULL && !done; 9293034Sdougm group = sa_get_next_group(group)) { 9303034Sdougm if (is_zfs_group(group)) { 9313034Sdougm for (zgroup = (sa_group_t)_sa_get_child_node((xmlNodePtr)group, 9323034Sdougm (xmlChar *)"group"); 9333034Sdougm zgroup != NULL; zgroup = sa_get_next_group(zgroup)) { 9343034Sdougm share = find_share(zgroup, sharepath); 9353034Sdougm if (share != NULL) 9363034Sdougm break; 9373034Sdougm } 9383034Sdougm } else { 9393034Sdougm share = find_share(group, sharepath); 9403034Sdougm } 9413034Sdougm if (share != NULL) 9423034Sdougm break; 9433034Sdougm } 9443034Sdougm return (share); 9453034Sdougm } 9463034Sdougm 9473034Sdougm /* 9483348Sdougm * sa_check_path(group, path, strictness) 9493034Sdougm * 9503034Sdougm * check that path is a valid path relative to the group. Currently, 9513034Sdougm * we are ignoring the group and checking only the NFS rules. Later, 9523034Sdougm * we may want to use the group to then check against the protocols 9533348Sdougm * enabled on the group. The strictness values mean: 9543348Sdougm * SA_CHECK_NORMAL == only check newpath against shares that are active 9553348Sdougm * SA_CHECK_STRICT == check newpath against both active shares and those 9563348Sdougm * stored in the repository 9573034Sdougm */ 9583034Sdougm 9593034Sdougm int 9603348Sdougm sa_check_path(sa_group_t group, char *path, int strictness) 9613034Sdougm { 9623034Sdougm #ifdef lint 9633034Sdougm group = group; 9643034Sdougm #endif 9653348Sdougm return (validpath(path, strictness)); 9663034Sdougm } 9673034Sdougm 9683034Sdougm /* 9693034Sdougm * _sa_add_share(group, sharepath, persist, *error) 9703034Sdougm * 9713034Sdougm * common code for all types of add_share. sa_add_share() is the 9723034Sdougm * public API, we also need to be able to do this when parsing legacy 9733034Sdougm * files and construction of the internal configuration while 9743034Sdougm * extracting config info from SMF. 9753034Sdougm */ 9763034Sdougm 9773034Sdougm sa_share_t 9783034Sdougm _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) 9793034Sdougm { 9803034Sdougm xmlNodePtr node = NULL; 9813034Sdougm int err; 9823034Sdougm 9833034Sdougm err = SA_OK; /* assume success */ 9843034Sdougm 9853034Sdougm node = xmlNewChild((xmlNodePtr)group, NULL, 9863034Sdougm (xmlChar *)"share", NULL); 9873034Sdougm if (node != NULL) { 9883034Sdougm xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath); 9893034Sdougm xmlSetProp(node, (xmlChar *)"type", persist ? 9903034Sdougm (xmlChar *)"persist" : (xmlChar *)"transient"); 9913034Sdougm if (persist != SA_SHARE_TRANSIENT) { 9923034Sdougm /* 9933034Sdougm * persistent shares come in two flavors: SMF and 9943034Sdougm * ZFS. Sort this one out based on target group and 9953034Sdougm * path type. Currently, only NFS is supported in the 9963034Sdougm * ZFS group and it is always on. 9973034Sdougm */ 9983034Sdougm if (sa_group_is_zfs(group) && sa_path_is_zfs(sharepath)) { 9993034Sdougm err = sa_zfs_set_sharenfs(group, sharepath, 1); 10003034Sdougm } else { 10013034Sdougm err = sa_commit_share(scf_handle, group, 10023034Sdougm (sa_share_t)node); 10033034Sdougm } 10043034Sdougm } 10053034Sdougm if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER) { 10063034Sdougm /* called by the dfstab parser so could be a show */ 10073034Sdougm err = SA_OK; 10083034Sdougm } 10093034Sdougm if (err != SA_OK) { 10103034Sdougm /* 10113034Sdougm * we couldn't commit to the repository so undo 10123034Sdougm * our internal state to reflect reality. 10133034Sdougm */ 10143034Sdougm xmlUnlinkNode(node); 10153034Sdougm xmlFreeNode(node); 10163034Sdougm node = NULL; 10173034Sdougm } 10183034Sdougm } else { 10193034Sdougm err = SA_NO_MEMORY; 10203034Sdougm } 10213034Sdougm if (error != NULL) 10223034Sdougm *error = err; 10233034Sdougm return (node); 10243034Sdougm } 10253034Sdougm 10263034Sdougm /* 10273034Sdougm * sa_add_share(group, sharepath, persist, *error) 10283034Sdougm * 10293034Sdougm * Add a new share object to the specified group. The share will 10303034Sdougm * have the specified sharepath and will only be constructed if 10313034Sdougm * it is a valid path to be shared. NULL is returned on error 10323034Sdougm * and a detailed error value will be returned via the error 10333034Sdougm * pointer. 10343034Sdougm */ 10353034Sdougm sa_share_t 10363034Sdougm sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) 10373034Sdougm { 10383034Sdougm xmlNodePtr node = NULL; 10393034Sdougm sa_share_t dup; 10403348Sdougm int strictness = SA_CHECK_NORMAL; 10413348Sdougm 10423348Sdougm /* 10433348Sdougm * If the share is to be permanent, use strict checking so a 10443348Sdougm * bad config doesn't get created. Transient shares only need 10453348Sdougm * to check against the currently active 10463348Sdougm * shares. SA_SHARE_PARSER is a modifier used internally to 10473348Sdougm * indicate that we are being called by the dfstab parser and 10483348Sdougm * that we need strict checking in all cases. Normally persist 10493348Sdougm * is in integer value but SA_SHARE_PARSER may be or'd into 10503348Sdougm * it as an override. 10513348Sdougm */ 10523348Sdougm if (persist & SA_SHARE_PARSER || persist == SA_SHARE_PERMANENT) 10533348Sdougm strictness = SA_CHECK_STRICT; 10543034Sdougm 10553034Sdougm if ((dup = sa_find_share(sharepath)) == NULL && 10563348Sdougm (*error = sa_check_path(group, sharepath, strictness)) == 10573348Sdougm SA_OK) { 10583034Sdougm node = _sa_add_share(group, sharepath, persist, error); 10593034Sdougm } 10603034Sdougm if (dup != NULL) 10613034Sdougm *error = SA_DUPLICATE_NAME; 10623034Sdougm 10633034Sdougm return ((sa_share_t)node); 10643034Sdougm } 10653034Sdougm 10663034Sdougm /* 10673034Sdougm * sa_enable_share(share, protocol) 10683034Sdougm * Enable the specified share to the specified protocol. 10693034Sdougm * If protocol is NULL, then all protocols. 10703034Sdougm */ 10713034Sdougm int 10723034Sdougm sa_enable_share(sa_share_t share, char *protocol) 10733034Sdougm { 10743034Sdougm char *sharepath; 10753034Sdougm struct stat st; 10763034Sdougm int err = 0; 10773034Sdougm 10783034Sdougm sharepath = sa_get_share_attr(share, "path"); 10793034Sdougm if (stat(sharepath, &st) < 0) { 10803034Sdougm err = SA_NO_SUCH_PATH; 10813034Sdougm } else { 10823034Sdougm /* tell the server about the share */ 10833034Sdougm if (protocol != NULL) { 10843034Sdougm /* lookup protocol specific handler */ 10853034Sdougm err = sa_proto_share(protocol, share); 10863034Sdougm if (err == SA_OK) 10873034Sdougm (void) sa_set_share_attr(share, "shared", "true"); 10883034Sdougm } else { 10893034Sdougm /* tell all protocols */ 10903034Sdougm err = sa_proto_share("nfs", share); /* only NFS for now */ 10913034Sdougm (void) sa_set_share_attr(share, "shared", "true"); 10923034Sdougm } 10933034Sdougm } 10943034Sdougm if (sharepath != NULL) 10953034Sdougm sa_free_attr_string(sharepath); 10963034Sdougm return (err); 10973034Sdougm } 10983034Sdougm 10993034Sdougm /* 11003034Sdougm * sa_disable_share(share, protocol) 11013034Sdougm * Disable the specified share to the specified protocol. 11023034Sdougm * If protocol is NULL, then all protocols. 11033034Sdougm */ 11043034Sdougm int 11053034Sdougm sa_disable_share(sa_share_t share, char *protocol) 11063034Sdougm { 11073034Sdougm char *path; 11083034Sdougm char *shared; 11093034Sdougm int ret = SA_OK; 11103034Sdougm 11113034Sdougm path = sa_get_share_attr(share, "path"); 11123034Sdougm shared = sa_get_share_attr(share, "shared"); 11133034Sdougm 11143034Sdougm if (protocol != NULL) { 11153034Sdougm ret = sa_proto_unshare(protocol, path); 11163034Sdougm } else { 11173034Sdougm /* need to do all protocols */ 11183034Sdougm ret = sa_proto_unshare("nfs", path); 11193034Sdougm } 11203034Sdougm if (ret == SA_OK) 11213034Sdougm (void) sa_set_share_attr(share, "shared", NULL); 11223034Sdougm if (path != NULL) 11233034Sdougm sa_free_attr_string(path); 11243034Sdougm if (shared != NULL) 11253034Sdougm sa_free_attr_string(shared); 11263034Sdougm return (ret); 11273034Sdougm } 11283034Sdougm 11293034Sdougm /* 11303034Sdougm * sa_remove_share(share) 11313034Sdougm * 11323034Sdougm * remove the specified share from its containing group. 11333034Sdougm * Remove from the SMF or ZFS configuration space. 11343034Sdougm */ 11353034Sdougm 11363034Sdougm int 11373034Sdougm sa_remove_share(sa_share_t share) 11383034Sdougm { 11393034Sdougm sa_group_t group; 11403034Sdougm int ret = SA_OK; 11413034Sdougm char *type; 11423034Sdougm int transient = 0; 11433034Sdougm char *groupname; 11443034Sdougm char *zfs; 11453034Sdougm 11463034Sdougm type = sa_get_share_attr(share, "type"); 11473034Sdougm group = sa_get_parent_group(share); 11483034Sdougm zfs = sa_get_group_attr(group, "zfs"); 11493034Sdougm groupname = sa_get_group_attr(group, "name"); 11503034Sdougm if (type != NULL && strcmp(type, "persist") != 0) 11513034Sdougm transient = 1; 11523034Sdougm if (type != NULL) 11533034Sdougm sa_free_attr_string(type); 11543034Sdougm 11553034Sdougm /* remove the node from its group then free the memory */ 11563034Sdougm 11573034Sdougm /* 11583034Sdougm * need to test if "busy" 11593034Sdougm */ 11603034Sdougm /* only do SMF action if permanent */ 11613034Sdougm if (!transient || zfs != NULL) { 11623034Sdougm /* remove from legacy dfstab as well as possible SMF */ 11633034Sdougm ret = sa_delete_legacy(share); 11643034Sdougm if (ret == SA_OK) { 11653034Sdougm if (!sa_group_is_zfs(group)) { 11663034Sdougm ret = sa_delete_share(scf_handle, group, share); 11673034Sdougm } else { 11683034Sdougm char *sharepath = sa_get_share_attr(share, "path"); 11693034Sdougm if (sharepath != NULL) { 11703034Sdougm ret = sa_zfs_set_sharenfs(group, sharepath, 0); 11713034Sdougm sa_free_attr_string(sharepath); 11723034Sdougm } 11733034Sdougm } 11743034Sdougm } 11753034Sdougm } 11763034Sdougm if (groupname != NULL) 11773034Sdougm sa_free_attr_string(groupname); 11783034Sdougm if (zfs != NULL) 11793034Sdougm sa_free_attr_string(zfs); 11803034Sdougm 11813034Sdougm xmlUnlinkNode((xmlNodePtr)share); 11823034Sdougm xmlFreeNode((xmlNodePtr)share); 11833034Sdougm return (ret); 11843034Sdougm } 11853034Sdougm 11863034Sdougm /* 11873034Sdougm * sa_move_share(group, share) 11883034Sdougm * 11893034Sdougm * move the specified share to the specified group. Update SMF 11903034Sdougm * appropriately. 11913034Sdougm */ 11923034Sdougm 11933034Sdougm int 11943034Sdougm sa_move_share(sa_group_t group, sa_share_t share) 11953034Sdougm { 11963034Sdougm sa_group_t oldgroup; 11973034Sdougm int ret = SA_OK; 11983034Sdougm 11993034Sdougm /* remove the node from its group then free the memory */ 12003034Sdougm 12013034Sdougm oldgroup = sa_get_parent_group(share); 12023034Sdougm if (oldgroup != group) { 12033034Sdougm xmlUnlinkNode((xmlNodePtr)share); 12043034Sdougm /* now that the share isn't in its old group, add to the new one */ 12053034Sdougm xmlAddChild((xmlNodePtr)group, (xmlNodePtr)share); 12063034Sdougm /* need to deal with SMF */ 12073034Sdougm if (ret == SA_OK) { 12083034Sdougm /* 12093034Sdougm * need to remove from old group first and then add to 12103034Sdougm * new group. Ideally, we would do the other order but 12113034Sdougm * need to avoid having the share in two groups at the 12123034Sdougm * same time. 12133034Sdougm */ 12143034Sdougm ret = sa_delete_share(scf_handle, oldgroup, share); 12153034Sdougm } 12163034Sdougm ret = sa_commit_share(scf_handle, group, share); 12173034Sdougm } 12183034Sdougm return (ret); 12193034Sdougm } 12203034Sdougm 12213034Sdougm /* 12223034Sdougm * sa_get_parent_group(share) 12233034Sdougm * 12243034Sdougm * Return the containg group for the share. If a group was actually 12253034Sdougm * passed in, we don't want a parent so return NULL. 12263034Sdougm */ 12273034Sdougm 12283034Sdougm sa_group_t 12293034Sdougm sa_get_parent_group(sa_share_t share) 12303034Sdougm { 12313034Sdougm xmlNodePtr node = NULL; 12323034Sdougm if (share != NULL) { 12333034Sdougm node = ((xmlNodePtr)share)->parent; 12343034Sdougm /* 12353034Sdougm * make sure parent is a group and not sharecfg since 12363034Sdougm * we may be cheating and passing in a group. 12373034Sdougm * Eventually, groups of groups might come into being. 12383034Sdougm */ 12393034Sdougm if (node == NULL || 12403034Sdougm xmlStrcmp(node->name, (xmlChar *)"sharecfg") == 0) 12413034Sdougm node = NULL; 12423034Sdougm } 12433034Sdougm return ((sa_group_t)node); 12443034Sdougm } 12453034Sdougm 12463034Sdougm /* 12473034Sdougm * _sa_create_group(groupname) 12483034Sdougm * 12493034Sdougm * Create a group in the document. The caller will need to deal with 12503034Sdougm * configuration store and activation. 12513034Sdougm */ 12523034Sdougm 12533034Sdougm sa_group_t 12543034Sdougm _sa_create_group(char *groupname) 12553034Sdougm { 12563034Sdougm xmlNodePtr node = NULL; 12573034Sdougm 12583034Sdougm if (sa_valid_group_name(groupname)) { 12593034Sdougm node = xmlNewChild(sa_config_tree, NULL, 12603034Sdougm (xmlChar *)"group", NULL); 12613034Sdougm if (node != NULL) { 12623034Sdougm xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname); 12633034Sdougm xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled"); 12643034Sdougm } 12653034Sdougm } 12663034Sdougm return ((sa_group_t)node); 12673034Sdougm } 12683034Sdougm 12693034Sdougm /* 12703034Sdougm * _sa_create_zfs_group(group, groupname) 12713034Sdougm * 12723034Sdougm * Create a ZFS subgroup under the specified group. This may 12733034Sdougm * eventually form the basis of general sub-groups, but is currently 12743034Sdougm * restricted to ZFS. 12753034Sdougm */ 12763034Sdougm sa_group_t 12773034Sdougm _sa_create_zfs_group(sa_group_t group, char *groupname) 12783034Sdougm { 12793034Sdougm xmlNodePtr node = NULL; 12803034Sdougm 12813034Sdougm node = xmlNewChild((xmlNodePtr)group, NULL, 12823034Sdougm (xmlChar *)"group", NULL); 12833034Sdougm if (node != NULL) { 12843034Sdougm xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname); 12853034Sdougm xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled"); 12863034Sdougm } 12873034Sdougm 12883034Sdougm return ((sa_group_t)node); 12893034Sdougm } 12903034Sdougm 12913034Sdougm /* 12923034Sdougm * sa_create_group(groupname, *error) 12933034Sdougm * 12943034Sdougm * Create a new group with groupname. Need to validate that it is a 12953034Sdougm * legal name for SMF and the construct the SMF service instance of 12963034Sdougm * svc:/network/shares/group to implement the group. All necessary 12973034Sdougm * operational properties must be added to the group at this point 12983034Sdougm * (via the SMF transaction model). 12993034Sdougm */ 13003034Sdougm sa_group_t 13013034Sdougm sa_create_group(char *groupname, int *error) 13023034Sdougm { 13033034Sdougm xmlNodePtr node = NULL; 13043034Sdougm sa_group_t group; 13053034Sdougm int ret; 13063034Sdougm char rbacstr[256]; 13073034Sdougm 13083034Sdougm ret = SA_OK; 13093034Sdougm 13103034Sdougm if (scf_handle == NULL) { 13113034Sdougm ret = SA_SYSTEM_ERR; 13123034Sdougm goto err; 13133034Sdougm } 13143034Sdougm 13153034Sdougm group = sa_get_group(groupname); 13163034Sdougm if (group != NULL) { 13173034Sdougm ret = SA_DUPLICATE_NAME; 13183034Sdougm } else { 13193034Sdougm if (sa_valid_group_name(groupname)) { 13203034Sdougm node = xmlNewChild(sa_config_tree, NULL, 13213034Sdougm (xmlChar *)"group", NULL); 13223034Sdougm if (node != NULL) { 13233034Sdougm xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname); 13243034Sdougm /* default to the group being enabled */ 13253034Sdougm xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled"); 13263034Sdougm ret = sa_create_instance(scf_handle, groupname); 13273034Sdougm if (ret == SA_OK) { 13283034Sdougm ret = sa_start_transaction(scf_handle, "operation"); 13293034Sdougm } 13303034Sdougm if (ret == SA_OK) { 13313034Sdougm ret = sa_set_property(scf_handle, "state", "enabled"); 13323034Sdougm if (ret == SA_OK) { 13333034Sdougm ret = sa_end_transaction(scf_handle); 13343034Sdougm } else { 13353034Sdougm sa_abort_transaction(scf_handle); 13363034Sdougm } 13373034Sdougm } 13383034Sdougm if (ret == SA_OK) { 13393034Sdougm /* initialize the RBAC strings */ 13403034Sdougm ret = sa_start_transaction(scf_handle, "general"); 13413034Sdougm if (ret == SA_OK) { 13423034Sdougm (void) snprintf(rbacstr, sizeof (rbacstr), "%s.%s", 13433034Sdougm SA_RBAC_MANAGE, groupname); 13443034Sdougm ret = sa_set_property(scf_handle, 13453034Sdougm "action_authorization", 13463034Sdougm rbacstr); 13473034Sdougm } 13483034Sdougm if (ret == SA_OK) { 13493034Sdougm (void) snprintf(rbacstr, sizeof (rbacstr), "%s.%s", 13503034Sdougm SA_RBAC_VALUE, groupname); 13513034Sdougm ret = sa_set_property(scf_handle, 13523034Sdougm "value_authorization", 13533034Sdougm rbacstr); 13543034Sdougm } 13553034Sdougm if (ret == SA_OK) { 13563034Sdougm ret = sa_end_transaction(scf_handle); 13573034Sdougm } else { 13583034Sdougm sa_abort_transaction(scf_handle); 13593034Sdougm } 13603034Sdougm } 13613034Sdougm if (ret != SA_OK) { 13623034Sdougm /* 13633034Sdougm * Couldn't commit the group so we need to 13643034Sdougm * undo internally. 13653034Sdougm */ 13663034Sdougm xmlUnlinkNode(node); 13673034Sdougm xmlFreeNode(node); 13683034Sdougm node = NULL; 13693034Sdougm } 13703034Sdougm } else { 13713034Sdougm ret = SA_NO_MEMORY; 13723034Sdougm } 13733034Sdougm } else { 13743034Sdougm ret = SA_INVALID_NAME; 13753034Sdougm } 13763034Sdougm } 13773034Sdougm err: 13783034Sdougm if (error != NULL) 13793034Sdougm *error = ret; 13803034Sdougm return ((sa_group_t)node); 13813034Sdougm } 13823034Sdougm 13833034Sdougm /* 13843034Sdougm * sa_remove_group(group) 13853034Sdougm * 13863034Sdougm * Remove the specified group. This deletes from the SMF repository. 13873034Sdougm * All property groups and properties are removed. 13883034Sdougm */ 13893034Sdougm 13903034Sdougm int 13913034Sdougm sa_remove_group(sa_group_t group) 13923034Sdougm { 13933034Sdougm char *name; 13943034Sdougm int ret = SA_OK; 13953034Sdougm 13963034Sdougm name = sa_get_group_attr(group, "name"); 13973034Sdougm if (name != NULL) { 13983034Sdougm ret = sa_delete_instance(scf_handle, name); 13993034Sdougm sa_free_attr_string(name); 14003034Sdougm } 14013034Sdougm xmlUnlinkNode((xmlNodePtr)group); /* make sure unlinked */ 14023034Sdougm xmlFreeNode((xmlNodePtr)group); /* now it is gone */ 14033034Sdougm return (ret); 14043034Sdougm } 14053034Sdougm 14063034Sdougm /* 14073034Sdougm * sa_update_config() 14083034Sdougm * 14093034Sdougm * Used to update legacy files that need to be updated in bulk 14103034Sdougm * Currently, this is a placeholder and will go away in a future 14113034Sdougm * release. 14123034Sdougm */ 14133034Sdougm 14143034Sdougm int 14153034Sdougm sa_update_config() 14163034Sdougm { 14173034Sdougm /* 14183034Sdougm * do legacy files first so we can tell when they change. 14193034Sdougm * This will go away when we start updating individual records 14203034Sdougm * rather than the whole file. 14213034Sdougm */ 14223034Sdougm update_legacy_config(); 14233034Sdougm return (SA_OK); 14243034Sdougm } 14253034Sdougm 14263034Sdougm /* 14273034Sdougm * get_node_attr(node, tag) 14283034Sdougm * 14293034Sdougm * Get the speficied tag(attribute) if it exists on the node. This is 14303034Sdougm * used internally by a number of attribute oriented functions. 14313034Sdougm */ 14323034Sdougm 14333034Sdougm static char * 14343034Sdougm get_node_attr(void *nodehdl, char *tag) 14353034Sdougm { 14363034Sdougm xmlNodePtr node = (xmlNodePtr)nodehdl; 14373034Sdougm xmlChar *name = NULL; 14383034Sdougm 14393034Sdougm if (node != NULL) { 14403034Sdougm name = xmlGetProp(node, (xmlChar *)tag); 14413034Sdougm } 14423034Sdougm return ((char *)name); 14433034Sdougm } 14443034Sdougm 14453034Sdougm /* 14463034Sdougm * get_node_attr(node, tag) 14473034Sdougm * 14483034Sdougm * Set the speficied tag(attribute) to the specified value This is 14493034Sdougm * used internally by a number of attribute oriented functions. It 14503034Sdougm * doesn't update the repository, only the internal document state. 14513034Sdougm */ 14523034Sdougm 14533034Sdougm void 14543034Sdougm set_node_attr(void *nodehdl, char *tag, char *value) 14553034Sdougm { 14563034Sdougm xmlNodePtr node = (xmlNodePtr)nodehdl; 14573034Sdougm if (node != NULL && tag != NULL) { 14583034Sdougm if (value != NULL) { 14593034Sdougm xmlSetProp(node, (xmlChar *)tag, (xmlChar *)value); 14603034Sdougm } else { 14613034Sdougm xmlUnsetProp(node, (xmlChar *)tag); 14623034Sdougm } 14633034Sdougm } 14643034Sdougm } 14653034Sdougm 14663034Sdougm /* 14673034Sdougm * sa_get_group_attr(group, tag) 14683034Sdougm * 14693034Sdougm * Get the specied attribute, if defined, for the group. 14703034Sdougm */ 14713034Sdougm 14723034Sdougm char * 14733034Sdougm sa_get_group_attr(sa_group_t group, char *tag) 14743034Sdougm { 14753034Sdougm return (get_node_attr((void *)group, tag)); 14763034Sdougm } 14773034Sdougm 14783034Sdougm /* 14793034Sdougm * sa_set_group_attr(group, tag, value) 14803034Sdougm * 14813034Sdougm * set the specified tag/attribute on the group using value as its 14823034Sdougm * value. 14833034Sdougm * 14843034Sdougm * This will result in setting the property in the SMF repository as 14853034Sdougm * well as in the internal document. 14863034Sdougm */ 14873034Sdougm 14883034Sdougm int 14893034Sdougm sa_set_group_attr(sa_group_t group, char *tag, char *value) 14903034Sdougm { 14913034Sdougm int ret; 14923034Sdougm char *groupname; 14933034Sdougm 14943034Sdougm groupname = sa_get_group_attr(group, "name"); 14953034Sdougm ret = sa_get_instance(scf_handle, groupname); 14963034Sdougm if (ret == SA_OK) { 14973034Sdougm set_node_attr((void *)group, tag, value); 14983034Sdougm ret = sa_start_transaction(scf_handle, "operation"); 14993034Sdougm if (ret == SA_OK) { 15003034Sdougm ret = sa_set_property(scf_handle, tag, value); 15013034Sdougm if (ret == SA_OK) 15023034Sdougm (void) sa_end_transaction(scf_handle); 15033034Sdougm else { 15043034Sdougm sa_abort_transaction(scf_handle); 15053034Sdougm } 15063034Sdougm } 15073034Sdougm } 15083034Sdougm if (groupname != NULL) 15093034Sdougm sa_free_attr_string(groupname); 15103034Sdougm return (ret); 15113034Sdougm } 15123034Sdougm 15133034Sdougm /* 15143034Sdougm * sa_get_share_attr(share, tag) 15153034Sdougm * 15163034Sdougm * Return the value of the tag/attribute set on the specified 15173034Sdougm * share. Returns NULL if the tag doesn't exist. 15183034Sdougm */ 15193034Sdougm 15203034Sdougm char * 15213034Sdougm sa_get_share_attr(sa_share_t share, char *tag) 15223034Sdougm { 15233034Sdougm return (get_node_attr((void *)share, tag)); 15243034Sdougm } 15253034Sdougm 15263034Sdougm /* 15273034Sdougm * sa_get_resource(group, resource) 15283034Sdougm * 15293034Sdougm * Search all the shares in the speified group for a share with a 15303034Sdougm * resource name matching the one specified. 15313034Sdougm * 15323034Sdougm * In the future, it may be advantageous to allow group to be NULL and 15333034Sdougm * search all groups but that isn't needed at present. 15343034Sdougm */ 15353034Sdougm 15363034Sdougm sa_share_t 15373034Sdougm sa_get_resource(sa_group_t group, char *resource) 15383034Sdougm { 15393034Sdougm sa_share_t share = NULL; 15403034Sdougm char *name = NULL; 15413034Sdougm 15423034Sdougm if (resource != NULL) { 15433034Sdougm for (share = sa_get_share(group, NULL); share != NULL; 15443034Sdougm share = sa_get_next_share(share)) { 15453034Sdougm name = sa_get_share_attr(share, "resource"); 15463034Sdougm if (name != NULL) { 15473034Sdougm if (strcmp(name, resource) == 0) 15483034Sdougm break; 15493034Sdougm sa_free_attr_string(name); 15503034Sdougm name = NULL; 15513034Sdougm } 15523034Sdougm } 15533034Sdougm if (name != NULL) 15543034Sdougm sa_free_attr_string(name); 15553034Sdougm } 15563034Sdougm return ((sa_share_t)share); 15573034Sdougm } 15583034Sdougm 15593034Sdougm /* 15603034Sdougm * _sa_set_share_description(share, description) 15613034Sdougm * 15623034Sdougm * Add a description tag with text contents to the specified share. 15633034Sdougm * A separate XML tag is used rather than a property. 15643034Sdougm */ 15653034Sdougm 15663034Sdougm xmlNodePtr 15673034Sdougm _sa_set_share_description(sa_share_t share, char *content) 15683034Sdougm { 15693034Sdougm xmlNodePtr node; 15703034Sdougm node = xmlNewChild((xmlNodePtr)share, 15713034Sdougm NULL, (xmlChar *)"description", NULL); 15723034Sdougm xmlNodeSetContent(node, (xmlChar *)content); 15733034Sdougm return (node); 15743034Sdougm } 15753034Sdougm 15763034Sdougm /* 15773034Sdougm * sa_set_share_attr(share, tag, value) 15783034Sdougm * 15793034Sdougm * Set the share attribute specified by tag to the specified value. In 15803034Sdougm * the case of "resource", enforce a no duplicates in a group rule. If 15813034Sdougm * the share is not transient, commit the changes to the repository 15823034Sdougm * else just update the share internally. 15833034Sdougm */ 15843034Sdougm 15853034Sdougm int 15863034Sdougm sa_set_share_attr(sa_share_t share, char *tag, char *value) 15873034Sdougm { 15883034Sdougm sa_group_t group; 15893034Sdougm sa_share_t resource; 15903034Sdougm int ret = SA_OK; 15913034Sdougm 15923034Sdougm group = sa_get_parent_group(share); 15933034Sdougm 15943034Sdougm /* 15953034Sdougm * There are some attributes that may have specific 15963034Sdougm * restrictions on them. Initially, only "resource" has 15973034Sdougm * special meaning that needs to be checked. Only one instance 15983034Sdougm * of a resource name may exist within a group. 15993034Sdougm */ 16003034Sdougm 16013034Sdougm if (strcmp(tag, "resource") == 0) { 16023034Sdougm resource = sa_get_resource(group, value); 16033034Sdougm if (resource != share && resource != NULL) 16043034Sdougm ret = SA_DUPLICATE_NAME; 16053034Sdougm } 16063034Sdougm if (ret == SA_OK) { 16073034Sdougm set_node_attr((void *)share, tag, value); 16083034Sdougm if (group != NULL) { 16093034Sdougm char *type; 16103034Sdougm /* we can probably optimize this some */ 16113034Sdougm type = sa_get_share_attr(share, "type"); 16123034Sdougm if (type == NULL || strcmp(type, "transient") != 0) 16133034Sdougm ret = sa_commit_share(scf_handle, group, share); 16143034Sdougm if (type != NULL) 16153034Sdougm sa_free_attr_string(type); 16163034Sdougm } 16173034Sdougm } 16183034Sdougm return (ret); 16193034Sdougm } 16203034Sdougm 16213034Sdougm /* 16223034Sdougm * sa_get_property_attr(prop, tag) 16233034Sdougm * 16243034Sdougm * Get the value of the specified property attribute. Standard 16253034Sdougm * attributes are "type" and "value". 16263034Sdougm */ 16273034Sdougm 16283034Sdougm char * 16293034Sdougm sa_get_property_attr(sa_property_t prop, char *tag) 16303034Sdougm { 16313034Sdougm return (get_node_attr((void *)prop, tag)); 16323034Sdougm } 16333034Sdougm 16343034Sdougm /* 16353034Sdougm * sa_get_optionset_attr(prop, tag) 16363034Sdougm * 16373034Sdougm * Get the value of the specified property attribute. Standard 16383034Sdougm * attribute is "type". 16393034Sdougm */ 16403034Sdougm 16413034Sdougm char * 16423034Sdougm sa_get_optionset_attr(sa_property_t optionset, char *tag) 16433034Sdougm { 16443034Sdougm return (get_node_attr((void *)optionset, tag)); 16453034Sdougm 16463034Sdougm } 16473034Sdougm 16483034Sdougm /* 16493034Sdougm * sa_set_optionset_attr(optionset, tag, value) 16503034Sdougm * 16513034Sdougm * Set the specified attribute(tag) to the specified value on the 16523034Sdougm * optionset. 16533034Sdougm */ 16543034Sdougm 16553034Sdougm void 16563034Sdougm sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value) 16573034Sdougm { 16583034Sdougm set_node_attr((void *)optionset, tag, value); 16593034Sdougm } 16603034Sdougm 16613034Sdougm /* 16623034Sdougm * sa_free_attr_string(string) 16633034Sdougm * 16643034Sdougm * Free the string that was returned in one of the sa_get_*_attr() 16653034Sdougm * functions. 16663034Sdougm */ 16673034Sdougm 16683034Sdougm void 16693034Sdougm sa_free_attr_string(char *string) 16703034Sdougm { 16713034Sdougm xmlFree((xmlChar *)string); 16723034Sdougm } 16733034Sdougm 16743034Sdougm /* 16753034Sdougm * sa_get_optionset(group, proto) 16763034Sdougm * 16773034Sdougm * Return the optionset, if it exists, that is associated with the 16783034Sdougm * specified protocol. 16793034Sdougm */ 16803034Sdougm 16813034Sdougm sa_optionset_t 16823034Sdougm sa_get_optionset(void *group, char *proto) 16833034Sdougm { 16843034Sdougm xmlNodePtr node; 16853034Sdougm xmlChar *value = NULL; 16863034Sdougm 16873034Sdougm for (node = ((xmlNodePtr)group)->children; node != NULL; 16883034Sdougm node = node->next) { 16893034Sdougm if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) { 16903034Sdougm value = xmlGetProp(node, (xmlChar *)"type"); 16913034Sdougm if (proto != NULL) { 16923034Sdougm if (value != NULL && 16933034Sdougm xmlStrcmp(value, (xmlChar *)proto) == 0) { 16943034Sdougm break; 16953034Sdougm } 16963034Sdougm if (value != NULL) { 16973034Sdougm xmlFree(value); 16983034Sdougm value = NULL; 16993034Sdougm } 17003034Sdougm } else { 17013034Sdougm break; 17023034Sdougm } 17033034Sdougm } 17043034Sdougm } 17053034Sdougm if (value != NULL) 17063034Sdougm xmlFree(value); 17073034Sdougm return ((sa_optionset_t)node); 17083034Sdougm } 17093034Sdougm 17103034Sdougm /* 17113034Sdougm * sa_get_next_optionset(optionset) 17123034Sdougm * 17133034Sdougm * Return the next optionset in the group. NULL if this was the last. 17143034Sdougm */ 17153034Sdougm 17163034Sdougm sa_optionset_t 17173034Sdougm sa_get_next_optionset(sa_optionset_t optionset) 17183034Sdougm { 17193034Sdougm xmlNodePtr node; 17203034Sdougm 17213034Sdougm for (node = ((xmlNodePtr)optionset)->next; node != NULL; 17223034Sdougm node = node->next) { 17233034Sdougm if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) { 17243034Sdougm break; 17253034Sdougm } 17263034Sdougm } 17273034Sdougm return ((sa_optionset_t)node); 17283034Sdougm } 17293034Sdougm 17303034Sdougm /* 17313034Sdougm * sa_get_security(group, sectype, proto) 17323034Sdougm * 17333034Sdougm * Return the security optionset. The internal name is a hold over 17343034Sdougm * from the implementation and will be changed before the API is 17353034Sdougm * finalized. This is really a named optionset that can be negotiated 17363034Sdougm * as a group of properties (like NFS security options). 17373034Sdougm */ 17383034Sdougm 17393034Sdougm sa_security_t 17403034Sdougm sa_get_security(sa_group_t group, char *sectype, char *proto) 17413034Sdougm { 17423034Sdougm xmlNodePtr node; 17433034Sdougm xmlChar *value = NULL; 17443034Sdougm 17453034Sdougm for (node = ((xmlNodePtr)group)->children; node != NULL; 17463034Sdougm node = node->next) { 17473034Sdougm if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) { 17483034Sdougm if (proto != NULL) { 17493034Sdougm value = xmlGetProp(node, (xmlChar *)"type"); 17503034Sdougm if (value == NULL || 17513034Sdougm (value != NULL && 17523034Sdougm xmlStrcmp(value, (xmlChar *)proto) != 0)) { 17533034Sdougm /* it doesn't match so continue */ 17543034Sdougm xmlFree(value); 17553034Sdougm value = NULL; 17563034Sdougm continue; 17573034Sdougm } 17583034Sdougm } 17593034Sdougm if (value != NULL) { 17603034Sdougm xmlFree(value); 17613034Sdougm value = NULL; 17623034Sdougm } 17633034Sdougm /* potential match */ 17643034Sdougm if (sectype != NULL) { 17653034Sdougm value = xmlGetProp(node, (xmlChar *)"sectype"); 17663034Sdougm if (value != NULL && 17673034Sdougm xmlStrcmp(value, (xmlChar *)sectype) == 0) { 17683034Sdougm break; 17693034Sdougm } 17703034Sdougm } else { 17713034Sdougm break; 17723034Sdougm } 17733034Sdougm } 17743034Sdougm if (value != NULL) { 17753034Sdougm xmlFree(value); 17763034Sdougm value = NULL; 17773034Sdougm } 17783034Sdougm } 17793034Sdougm if (value != NULL) 17803034Sdougm xmlFree(value); 17813034Sdougm return ((sa_security_t)node); 17823034Sdougm } 17833034Sdougm 17843034Sdougm /* 17853034Sdougm * sa_get_next_security(security) 17863034Sdougm * 17873034Sdougm * Get the next security optionset if one exists. 17883034Sdougm */ 17893034Sdougm 17903034Sdougm sa_security_t 17913034Sdougm sa_get_next_security(sa_security_t security) 17923034Sdougm { 17933034Sdougm xmlNodePtr node; 17943034Sdougm 17953034Sdougm for (node = ((xmlNodePtr)security)->next; node != NULL; 17963034Sdougm node = node->next) { 17973034Sdougm if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) { 17983034Sdougm break; 17993034Sdougm } 18003034Sdougm } 18013034Sdougm return ((sa_security_t)node); 18023034Sdougm } 18033034Sdougm 18043034Sdougm /* 18053034Sdougm * sa_get_property(optionset, prop) 18063034Sdougm * 18073034Sdougm * Get the property object with the name specified in prop from the 18083034Sdougm * optionset. 18093034Sdougm */ 18103034Sdougm 18113034Sdougm sa_property_t 18123034Sdougm sa_get_property(sa_optionset_t optionset, char *prop) 18133034Sdougm { 18143034Sdougm xmlNodePtr node = (xmlNodePtr)optionset; 18153034Sdougm xmlChar *value = NULL; 18163034Sdougm 18173034Sdougm if (optionset == NULL) 18183034Sdougm return (NULL); 18193034Sdougm 18203034Sdougm for (node = node->children; node != NULL; 18213034Sdougm node = node->next) { 18223034Sdougm if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 18233034Sdougm if (prop == NULL) 18243034Sdougm break; 18253034Sdougm value = xmlGetProp(node, (xmlChar *)"type"); 18263034Sdougm if (value != NULL && xmlStrcmp(value, (xmlChar *)prop) == 0) { 18273034Sdougm break; 18283034Sdougm } 18293034Sdougm if (value != NULL) { 18303034Sdougm xmlFree(value); 18313034Sdougm value = NULL; 18323034Sdougm } 18333034Sdougm } 18343034Sdougm } 18353034Sdougm if (value != NULL) 18363034Sdougm xmlFree(value); 18373034Sdougm if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) { 18383034Sdougm /* avoid a non option node -- it is possible to be a text node */ 18393034Sdougm node = NULL; 18403034Sdougm } 18413034Sdougm return ((sa_property_t)node); 18423034Sdougm } 18433034Sdougm 18443034Sdougm /* 18453034Sdougm * sa_get_next_property(property) 18463034Sdougm * 18473034Sdougm * Get the next property following the specified property. NULL if 18483034Sdougm * this was the last. 18493034Sdougm */ 18503034Sdougm 18513034Sdougm sa_property_t 18523034Sdougm sa_get_next_property(sa_property_t property) 18533034Sdougm { 18543034Sdougm xmlNodePtr node; 18553034Sdougm 18563034Sdougm for (node = ((xmlNodePtr)property)->next; node != NULL; 18573034Sdougm node = node->next) { 18583034Sdougm if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 18593034Sdougm break; 18603034Sdougm } 18613034Sdougm } 18623034Sdougm return ((sa_property_t)node); 18633034Sdougm } 18643034Sdougm 18653034Sdougm /* 18663034Sdougm * sa_set_share_description(share, content) 18673034Sdougm * 18683034Sdougm * Set the description of share to content. 18693034Sdougm */ 18703034Sdougm 18713034Sdougm int 18723034Sdougm sa_set_share_description(sa_share_t share, char *content) 18733034Sdougm { 18743034Sdougm xmlNodePtr node; 18753034Sdougm sa_group_t group; 18763034Sdougm int ret = SA_OK; 18773034Sdougm 18783034Sdougm for (node = ((xmlNodePtr)share)->children; node != NULL; 18793034Sdougm node = node->next) { 18803034Sdougm if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { 18813034Sdougm break; 18823034Sdougm } 18833034Sdougm } 18843034Sdougm group = sa_get_parent_group(share); 18853034Sdougm /* no existing description but want to add */ 18863034Sdougm if (node == NULL && content != NULL) { 18873034Sdougm /* add a description */ 18883034Sdougm node = _sa_set_share_description(share, content); 18893034Sdougm } else if (node != NULL && content != NULL) { 18903034Sdougm /* update a description */ 18913034Sdougm xmlNodeSetContent(node, (xmlChar *)content); 18923034Sdougm } else if (node != NULL && content == NULL) { 18933034Sdougm /* remove an existing description */ 18943034Sdougm xmlUnlinkNode(node); 18953034Sdougm xmlFreeNode(node); 18963034Sdougm } 18973034Sdougm if (group != NULL && is_persistent((sa_group_t)share)) 18983034Sdougm ret = sa_commit_share(scf_handle, group, share); 18993034Sdougm return (ret); 19003034Sdougm } 19013034Sdougm 19023034Sdougm /* 19033034Sdougm * fixproblemchars(string) 19043034Sdougm * 19053034Sdougm * don't want any newline or tab characters in the text since these 19063034Sdougm * could break display of data and legacy file formats. 19073034Sdougm */ 19083034Sdougm static void 19093034Sdougm fixproblemchars(char *str) 19103034Sdougm { 19113034Sdougm int c; 19123034Sdougm for (c = *str; c != '\0'; c = *++str) { 19133034Sdougm if (c == '\t' || c == '\n') 19143034Sdougm *str = ' '; 19153034Sdougm else if (c == '"') 19163034Sdougm *str = '\''; 19173034Sdougm } 19183034Sdougm } 19193034Sdougm 19203034Sdougm /* 19213034Sdougm * sa_get_share_description(share) 19223034Sdougm * 19233034Sdougm * Return the description text for the specified share if it 19243034Sdougm * exists. NULL if no description exists. 19253034Sdougm */ 19263034Sdougm 19273034Sdougm char * 19283034Sdougm sa_get_share_description(sa_share_t share) 19293034Sdougm { 19303034Sdougm xmlChar *description = NULL; 19313034Sdougm xmlNodePtr node; 19323034Sdougm 19333034Sdougm for (node = ((xmlNodePtr)share)->children; node != NULL; 19343034Sdougm node = node->next) { 19353034Sdougm if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { 19363034Sdougm break; 19373034Sdougm } 19383034Sdougm } 19393034Sdougm if (node != NULL) { 19403034Sdougm description = xmlNodeGetContent((xmlNodePtr)share); 19413034Sdougm fixproblemchars((char *)description); 19423034Sdougm } 19433034Sdougm return ((char *)description); 19443034Sdougm } 19453034Sdougm 19463034Sdougm /* 19473034Sdougm * sa_free(share_description(description) 19483034Sdougm * 19493034Sdougm * Free the description string. 19503034Sdougm */ 19513034Sdougm 19523034Sdougm void 19533034Sdougm sa_free_share_description(char *description) 19543034Sdougm { 19553034Sdougm xmlFree((xmlChar *)description); 19563034Sdougm } 19573034Sdougm 19583034Sdougm /* 19593034Sdougm * sa_create_optionset(group, proto) 19603034Sdougm * 19613034Sdougm * Create an optionset for the specified protocol in the specied 19623034Sdougm * group. This is manifested as a property group within SMF. 19633034Sdougm */ 19643034Sdougm 19653034Sdougm sa_optionset_t 19663034Sdougm sa_create_optionset(sa_group_t group, char *proto) 19673034Sdougm { 19683034Sdougm sa_optionset_t optionset; 19693034Sdougm sa_group_t parent = group; 19703034Sdougm 19713034Sdougm optionset = sa_get_optionset(group, proto); 19723034Sdougm if (optionset != NULL) { 19733034Sdougm /* can't have a duplicate protocol */ 19743034Sdougm optionset = NULL; 19753034Sdougm } else { 19763034Sdougm optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group, 19773034Sdougm NULL, 19783034Sdougm (xmlChar *)"optionset", 19793034Sdougm NULL); 19803034Sdougm /* 19813034Sdougm * only put to repository if on a group and we were 19823034Sdougm * able to create an optionset. 19833034Sdougm */ 19843034Sdougm if (optionset != NULL) { 19853034Sdougm char oname[256]; 19863034Sdougm char *groupname; 19873034Sdougm char *id = NULL; 19883034Sdougm 19893034Sdougm if (sa_is_share(group)) 19903034Sdougm parent = sa_get_parent_group((sa_share_t)group); 19913034Sdougm 19923034Sdougm sa_set_optionset_attr(optionset, "type", proto); 19933034Sdougm 19943034Sdougm if (sa_is_share(group)) { 19953034Sdougm id = sa_get_share_attr((sa_share_t)group, "id"); 19963034Sdougm } 19973034Sdougm (void) sa_optionset_name(optionset, oname, 19983034Sdougm sizeof (oname), id); 19993034Sdougm groupname = sa_get_group_attr(parent, "name"); 20003034Sdougm if (groupname != NULL && is_persistent(group)) { 20013034Sdougm (void) sa_get_instance(scf_handle, groupname); 20023034Sdougm sa_free_attr_string(groupname); 20033034Sdougm (void) sa_create_pgroup(scf_handle, oname); 20043034Sdougm } 20053034Sdougm if (id != NULL) 20063034Sdougm sa_free_attr_string(id); 20073034Sdougm } 20083034Sdougm } 20093034Sdougm return (optionset); 20103034Sdougm } 20113034Sdougm 20123034Sdougm /* 20133034Sdougm * sa_get_property_parent(property) 20143034Sdougm * 20153034Sdougm * Given a property, return the object it is a property of. This will 20163034Sdougm * be an optionset of some type. 20173034Sdougm */ 20183034Sdougm 20193034Sdougm static sa_optionset_t 20203034Sdougm sa_get_property_parent(sa_property_t property) 20213034Sdougm { 20223034Sdougm xmlNodePtr node = NULL; 20233034Sdougm 20243034Sdougm if (property != NULL) { 20253034Sdougm node = ((xmlNodePtr)property)->parent; 20263034Sdougm } 20273034Sdougm return ((sa_optionset_t)node); 20283034Sdougm } 20293034Sdougm 20303034Sdougm /* 20313034Sdougm * sa_get_optionset_parent(optionset) 20323034Sdougm * 20333034Sdougm * Return the parent of the specified optionset. This could be a group 20343034Sdougm * or a share. 20353034Sdougm */ 20363034Sdougm 20373034Sdougm static sa_group_t 20383034Sdougm sa_get_optionset_parent(sa_optionset_t optionset) 20393034Sdougm { 20403034Sdougm xmlNodePtr node = NULL; 20413034Sdougm 20423034Sdougm if (optionset != NULL) { 20433034Sdougm node = ((xmlNodePtr)optionset)->parent; 20443034Sdougm } 20453034Sdougm return ((sa_group_t)node); 20463034Sdougm } 20473034Sdougm 20483034Sdougm /* 20493034Sdougm * zfs_needs_update(share) 20503034Sdougm * 20513034Sdougm * In order to avoid making multiple updates to a ZFS share when 20523034Sdougm * setting properties, the share attribute "changed" will be set to 20533034Sdougm * true when a property is added or modifed. When done adding 20543034Sdougm * properties, we can then detect that an update is needed. We then 20553034Sdougm * clear the state here to detect additional changes. 20563034Sdougm */ 20573034Sdougm 20583034Sdougm static int 20593034Sdougm zfs_needs_update(sa_share_t share) 20603034Sdougm { 20613034Sdougm char *attr; 20623034Sdougm int result = 0; 20633034Sdougm 20643034Sdougm attr = sa_get_share_attr(share, "changed"); 20653034Sdougm if (attr != NULL) { 20663034Sdougm sa_free_attr_string(attr); 20673034Sdougm result = 1; 20683034Sdougm } 20693034Sdougm set_node_attr((void *)share, "changed", NULL); 20703034Sdougm return (result); 20713034Sdougm } 20723034Sdougm 20733034Sdougm /* 20743034Sdougm * zfs_set_update(share) 20753034Sdougm * 20763034Sdougm * Set the changed attribute of the share to true. 20773034Sdougm */ 20783034Sdougm 20793034Sdougm static void 20803034Sdougm zfs_set_update(sa_share_t share) 20813034Sdougm { 20823034Sdougm set_node_attr((void *)share, "changed", "true"); 20833034Sdougm } 20843034Sdougm 20853034Sdougm /* 20863034Sdougm * sa_commit_properties(optionset, clear) 20873034Sdougm * 20883034Sdougm * Check if SMF or ZFS config and either update or abort the pending 20893034Sdougm * changes. 20903034Sdougm */ 20913034Sdougm 20923034Sdougm int 20933034Sdougm sa_commit_properties(sa_optionset_t optionset, int clear) 20943034Sdougm { 20953034Sdougm sa_group_t group; 20963034Sdougm sa_group_t parent; 20973034Sdougm int zfs = 0; 20983034Sdougm int needsupdate = 0; 20993034Sdougm int ret = SA_OK; 21003034Sdougm 21013034Sdougm group = sa_get_optionset_parent(optionset); 21023034Sdougm if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) { 21033034Sdougm /* only update ZFS if on a share */ 21043034Sdougm parent = sa_get_parent_group(group); 21053034Sdougm zfs++; 21063034Sdougm if (parent != NULL && is_zfs_group(parent)) { 21073034Sdougm needsupdate = zfs_needs_update(group); 21083034Sdougm } else { 21093034Sdougm zfs = 0; 21103034Sdougm } 21113034Sdougm } 21123034Sdougm if (zfs) { 21133034Sdougm if (!clear && needsupdate) 21143034Sdougm ret = sa_zfs_update((sa_share_t)group); 21153034Sdougm } else { 21163034Sdougm if (clear) 21173034Sdougm (void) sa_abort_transaction(scf_handle); 21183034Sdougm else 21193034Sdougm ret = sa_end_transaction(scf_handle); 21203034Sdougm } 21213034Sdougm return (ret); 21223034Sdougm } 21233034Sdougm 21243034Sdougm /* 21253034Sdougm * sa_destroy_optionset(optionset) 21263034Sdougm * 21273034Sdougm * Remove the optionset from its group. Update the repostory to 21283034Sdougm * reflect this change. 21293034Sdougm */ 21303034Sdougm 21313034Sdougm int 21323034Sdougm sa_destroy_optionset(sa_optionset_t optionset) 21333034Sdougm { 21343034Sdougm char name[256]; 21353034Sdougm int len; 21363034Sdougm int ret; 21373034Sdougm char *id = NULL; 21383034Sdougm sa_group_t group; 21393034Sdougm int ispersist = 1; 21403034Sdougm 21413034Sdougm /* now delete the prop group */ 21423034Sdougm group = sa_get_optionset_parent(optionset); 21433034Sdougm if (group != NULL && sa_is_share(group)) { 21443034Sdougm ispersist = is_persistent(group); 21453034Sdougm id = sa_get_share_attr((sa_share_t)group, "id"); 21463034Sdougm } 21473034Sdougm if (ispersist) { 21483034Sdougm len = sa_optionset_name(optionset, name, sizeof (name), id); 21493034Sdougm if (len > 0) { 21503034Sdougm ret = sa_delete_pgroup(scf_handle, name); 21513034Sdougm } 21523034Sdougm } 21533034Sdougm xmlUnlinkNode((xmlNodePtr)optionset); 21543034Sdougm xmlFreeNode((xmlNodePtr)optionset); 21553034Sdougm if (id != NULL) 21563034Sdougm sa_free_attr_string(id); 21573034Sdougm return (ret); 21583034Sdougm } 21593034Sdougm 21603034Sdougm /* private to the implementation */ 21613034Sdougm int 21623034Sdougm _sa_remove_optionset(sa_optionset_t optionset) 21633034Sdougm { 21643034Sdougm int ret = SA_OK; 21653034Sdougm 21663034Sdougm xmlUnlinkNode((xmlNodePtr)optionset); 21673034Sdougm xmlFreeNode((xmlNodePtr)optionset); 21683034Sdougm return (ret); 21693034Sdougm } 21703034Sdougm 21713034Sdougm /* 21723034Sdougm * sa_create_security(group, sectype, proto) 21733034Sdougm * 21743034Sdougm * Create a security optionset (one that has a type name and a 21753034Sdougm * proto). Security is left over from a pure NFS implementation. The 21763034Sdougm * naming will change in the future when the API is released. 21773034Sdougm */ 21783034Sdougm sa_security_t 21793034Sdougm sa_create_security(sa_group_t group, char *sectype, char *proto) 21803034Sdougm { 21813034Sdougm sa_security_t security; 21823034Sdougm char *id = NULL; 21833034Sdougm sa_group_t parent; 21843034Sdougm char *groupname = NULL; 21853034Sdougm 21863034Sdougm if (group != NULL && sa_is_share(group)) { 21873034Sdougm id = sa_get_share_attr((sa_share_t)group, "id"); 21883034Sdougm parent = sa_get_parent_group(group); 21893034Sdougm if (parent != NULL) 21903034Sdougm groupname = sa_get_group_attr(parent, "name"); 21913034Sdougm } else if (group != NULL) { 21923034Sdougm groupname = sa_get_group_attr(group, "name"); 21933034Sdougm } 21943034Sdougm 21953034Sdougm security = sa_get_security(group, sectype, proto); 21963034Sdougm if (security != NULL) { 21973034Sdougm /* can't have a duplicate security option */ 21983034Sdougm security = NULL; 21993034Sdougm } else { 22003034Sdougm security = (sa_security_t)xmlNewChild((xmlNodePtr)group, 22013034Sdougm NULL, 22023034Sdougm (xmlChar *)"security", 22033034Sdougm NULL); 22043034Sdougm if (security != NULL) { 22053034Sdougm char oname[256]; 22063034Sdougm sa_set_security_attr(security, "type", proto); 22073034Sdougm 22083034Sdougm sa_set_security_attr(security, "sectype", sectype); 22093034Sdougm (void) sa_security_name(security, oname, 22103034Sdougm sizeof (oname), id); 22113034Sdougm if (groupname != NULL && is_persistent(group)) { 22123034Sdougm (void) sa_get_instance(scf_handle, groupname); 22133034Sdougm (void) sa_create_pgroup(scf_handle, oname); 22143034Sdougm } 22153034Sdougm } 22163034Sdougm } 22173034Sdougm if (groupname != NULL) 22183034Sdougm sa_free_attr_string(groupname); 22193034Sdougm return (security); 22203034Sdougm } 22213034Sdougm 22223034Sdougm /* 22233034Sdougm * sa_destroy_security(security) 22243034Sdougm * 22253034Sdougm * Remove the specified optionset from the document and the 22263034Sdougm * configuration. 22273034Sdougm */ 22283034Sdougm 22293034Sdougm int 22303034Sdougm sa_destroy_security(sa_security_t security) 22313034Sdougm { 22323034Sdougm char name[256]; 22333034Sdougm int len; 22343034Sdougm int ret = SA_OK; 22353034Sdougm char *id = NULL; 22363034Sdougm sa_group_t group; 22373034Sdougm int iszfs = 0; 22383034Sdougm int ispersist = 1; 22393034Sdougm 22403034Sdougm group = sa_get_optionset_parent(security); 22413034Sdougm 22423034Sdougm if (group != NULL) 22433034Sdougm iszfs = sa_group_is_zfs(group); 22443034Sdougm 22453034Sdougm if (group != NULL && !iszfs) { 22463034Sdougm if (sa_is_share(group)) 22473034Sdougm ispersist = is_persistent(group); 22483034Sdougm id = sa_get_share_attr((sa_share_t)group, "id"); 22493034Sdougm } 22503034Sdougm if (ispersist) { 22513034Sdougm len = sa_security_name(security, name, sizeof (name), id); 22523034Sdougm if (!iszfs && len > 0) { 22533034Sdougm ret = sa_delete_pgroup(scf_handle, name); 22543034Sdougm } 22553034Sdougm } 22563034Sdougm xmlUnlinkNode((xmlNodePtr)security); 22573034Sdougm xmlFreeNode((xmlNodePtr)security); 22583034Sdougm if (iszfs) { 22593034Sdougm ret = sa_zfs_update(group); 22603034Sdougm } 22613034Sdougm if (id != NULL) 22623034Sdougm sa_free_attr_string(id); 22633034Sdougm return (ret); 22643034Sdougm } 22653034Sdougm 22663034Sdougm /* 22673034Sdougm * sa_get_security_attr(optionset, tag) 22683034Sdougm * 22693034Sdougm * Return the specified attribute value from the optionset. 22703034Sdougm */ 22713034Sdougm 22723034Sdougm char * 22733034Sdougm sa_get_security_attr(sa_property_t optionset, char *tag) 22743034Sdougm { 22753034Sdougm return (get_node_attr((void *)optionset, tag)); 22763034Sdougm 22773034Sdougm } 22783034Sdougm 22793034Sdougm /* 22803034Sdougm * sa_set_security_attr(optionset, tag, value) 22813034Sdougm * 22823034Sdougm * Set the optioset attribute specied by tag to the specified value. 22833034Sdougm */ 22843034Sdougm 22853034Sdougm void 22863034Sdougm sa_set_security_attr(sa_group_t optionset, char *tag, char *value) 22873034Sdougm { 22883034Sdougm set_node_attr((void *)optionset, tag, value); 22893034Sdougm } 22903034Sdougm 22913034Sdougm /* 22923034Sdougm * is_nodetype(node, type) 22933034Sdougm * 22943034Sdougm * Check to see if node is of the type specified. 22953034Sdougm */ 22963034Sdougm 22973034Sdougm static int 22983034Sdougm is_nodetype(void *node, char *type) 22993034Sdougm { 23003034Sdougm return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0); 23013034Sdougm } 23023034Sdougm 23033034Sdougm /* 23043034Sdougm * sa_set_prop_by_prop(optionset, group, prop, type) 23053034Sdougm * 23063034Sdougm * Add/remove/update the specified property prop into the optionset or 23073034Sdougm * share. If a share, sort out which property group based on GUID. In 23083034Sdougm * all cases, the appropriate transaction is set (or ZFS share is 23093034Sdougm * marked as needing an update) 23103034Sdougm */ 23113034Sdougm 23123034Sdougm #define SA_PROP_OP_REMOVE 1 23133034Sdougm #define SA_PROP_OP_ADD 2 23143034Sdougm #define SA_PROP_OP_UPDATE 3 23153034Sdougm static int 23163034Sdougm sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group, 23173034Sdougm sa_property_t prop, int type) 23183034Sdougm { 23193034Sdougm char *name; 23203034Sdougm char *valstr; 23213034Sdougm int ret = SA_OK; 23223034Sdougm scf_transaction_entry_t *entry; 23233034Sdougm scf_value_t *value; 23243034Sdougm int opttype; /* 1 == optionset, 0 == security */ 23253034Sdougm char *id = NULL; 23263034Sdougm int iszfs = 0; 23273034Sdougm int isshare = 0; 23283034Sdougm sa_group_t parent = NULL; 23293034Sdougm 23303034Sdougm if (!is_persistent(group)) { 23313034Sdougm /* 23323034Sdougm * if the group/share is not persistent we don't need 23333034Sdougm * to do anything here 23343034Sdougm */ 23353034Sdougm return (SA_OK); 23363034Sdougm } 23373034Sdougm name = sa_get_property_attr(prop, "type"); 23383034Sdougm valstr = sa_get_property_attr(prop, "value"); 23393034Sdougm entry = scf_entry_create(scf_handle->handle); 23403034Sdougm opttype = is_nodetype((void *)optionset, "optionset"); 23413034Sdougm 23423034Sdougm if (valstr != NULL && entry != NULL) { 23433034Sdougm if (sa_is_share(group)) { 23443034Sdougm isshare = 1; 23453034Sdougm parent = sa_get_parent_group(group); 23463034Sdougm if (parent != NULL) { 23473034Sdougm iszfs = is_zfs_group(parent); 23483034Sdougm } 23493034Sdougm } else { 23503034Sdougm iszfs = is_zfs_group(group); 23513034Sdougm } 23523034Sdougm if (!iszfs) { 23533034Sdougm if (scf_handle->trans == NULL) { 23543034Sdougm char oname[256]; 23553034Sdougm char *groupname = NULL; 23563034Sdougm if (isshare) { 23573034Sdougm if (parent != NULL) { 23583034Sdougm groupname = sa_get_group_attr(parent, "name"); 23593034Sdougm } 23603034Sdougm id = sa_get_share_attr((sa_share_t)group, "id"); 23613034Sdougm } else { 23623034Sdougm groupname = sa_get_group_attr(group, "name"); 23633034Sdougm } 23643034Sdougm if (groupname != NULL) { 23653034Sdougm ret = sa_get_instance(scf_handle, groupname); 23663034Sdougm sa_free_attr_string(groupname); 23673034Sdougm } 23683034Sdougm if (opttype) 23693034Sdougm (void) sa_optionset_name(optionset, oname, 23703034Sdougm sizeof (oname), id); 23713034Sdougm else 23723034Sdougm (void) sa_security_name(optionset, oname, 23733034Sdougm sizeof (oname), id); 23743034Sdougm ret = sa_start_transaction(scf_handle, oname); 23753034Sdougm } 23763034Sdougm if (ret == SA_OK) { 23773034Sdougm switch (type) { 23783034Sdougm case SA_PROP_OP_REMOVE: 23793034Sdougm ret = scf_transaction_property_delete(scf_handle->trans, 23803034Sdougm entry, 23813034Sdougm name); 23823034Sdougm break; 23833034Sdougm case SA_PROP_OP_ADD: 23843034Sdougm case SA_PROP_OP_UPDATE: 23853034Sdougm value = scf_value_create(scf_handle->handle); 23863034Sdougm if (value != NULL) { 23873034Sdougm if (type == SA_PROP_OP_ADD) 23883034Sdougm ret = scf_transaction_property_new( 23893034Sdougm scf_handle->trans, 23903034Sdougm entry, 23913034Sdougm name, 23923034Sdougm SCF_TYPE_ASTRING); 23933034Sdougm else 23943034Sdougm ret = scf_transaction_property_change( 23953034Sdougm scf_handle->trans, 23963034Sdougm entry, 23973034Sdougm name, 23983034Sdougm SCF_TYPE_ASTRING); 23993034Sdougm if (ret == 0) { 24003034Sdougm ret = scf_value_set_astring(value, valstr); 24013034Sdougm if (ret == 0) 24023034Sdougm ret = scf_entry_add_value(entry, value); 24033034Sdougm if (ret != 0) { 24043034Sdougm scf_value_destroy(value); 24053034Sdougm ret = SA_SYSTEM_ERR; 24063034Sdougm } 24073034Sdougm } else { 24083034Sdougm scf_entry_destroy(entry); 24093034Sdougm ret = SA_SYSTEM_ERR; 24103034Sdougm } 24113034Sdougm break; 24123034Sdougm } 24133034Sdougm } 24143034Sdougm } 24153034Sdougm } else { 24163034Sdougm /* 24173034Sdougm * ZFS update. The calling function would have updated 24183034Sdougm * the internal XML structure. Just need to flag it as 24193034Sdougm * changed for ZFS. 24203034Sdougm */ 24213034Sdougm zfs_set_update((sa_share_t)group); 24223034Sdougm } 24233034Sdougm } 24243034Sdougm 24253034Sdougm if (name != NULL) 24263034Sdougm sa_free_attr_string(name); 24273034Sdougm if (valstr != NULL) 24283034Sdougm sa_free_attr_string(valstr); 24293034Sdougm else if (entry != NULL) 24303034Sdougm scf_entry_destroy(entry); 24313034Sdougm 24323034Sdougm if (ret == -1) 24333034Sdougm ret = SA_SYSTEM_ERR; 24343034Sdougm 24353034Sdougm return (ret); 24363034Sdougm } 24373034Sdougm 24383034Sdougm /* 24393034Sdougm * sa_create_property(name, value) 24403034Sdougm * 24413034Sdougm * Create a new property with the specified name and value. 24423034Sdougm */ 24433034Sdougm 24443034Sdougm sa_property_t 24453034Sdougm sa_create_property(char *name, char *value) 24463034Sdougm { 24473034Sdougm xmlNodePtr node; 24483034Sdougm 24493034Sdougm node = xmlNewNode(NULL, (xmlChar *)"option"); 24503034Sdougm if (node != NULL) { 24513034Sdougm xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name); 24523034Sdougm xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value); 24533034Sdougm } 24543034Sdougm return ((sa_property_t)node); 24553034Sdougm } 24563034Sdougm 24573034Sdougm /* 24583034Sdougm * sa_add_property(object, property) 24593034Sdougm * 24603034Sdougm * Add the specified property to the object. Issue the appropriate 24613034Sdougm * transaction or mark a ZFS object as needing an update. 24623034Sdougm */ 24633034Sdougm 24643034Sdougm int 24653034Sdougm sa_add_property(void *object, sa_property_t property) 24663034Sdougm { 24673034Sdougm int ret = SA_OK; 24683034Sdougm sa_group_t parent; 24693034Sdougm sa_group_t group; 24703034Sdougm char *proto; 24713034Sdougm 24723034Sdougm proto = sa_get_optionset_attr(object, "type"); 24733034Sdougm if (property != NULL) { 24743034Sdougm if ((ret = sa_valid_property(object, proto, property)) == SA_OK) { 24753034Sdougm property = (sa_property_t)xmlAddChild((xmlNodePtr)object, 24763034Sdougm (xmlNodePtr)property); 24773034Sdougm } else { 24783034Sdougm if (proto != NULL) 24793034Sdougm sa_free_attr_string(proto); 24803034Sdougm return (ret); 24813034Sdougm } 24823034Sdougm } 24833034Sdougm 24843034Sdougm if (proto != NULL) 24853034Sdougm sa_free_attr_string(proto); 24863034Sdougm 24873034Sdougm parent = sa_get_parent_group(object); 24883034Sdougm if (!is_persistent(parent)) { 24893034Sdougm return (ret); 24903034Sdougm } 24913034Sdougm 24923034Sdougm if (sa_is_share(parent)) 24933034Sdougm group = sa_get_parent_group(parent); 24943034Sdougm else 24953034Sdougm group = parent; 24963034Sdougm 24973034Sdougm if (property == NULL) 24983034Sdougm ret = SA_NO_MEMORY; 24993034Sdougm else { 25003034Sdougm char oname[256]; 25013034Sdougm 25023034Sdougm if (!is_zfs_group(group)) { 25033034Sdougm char *id = NULL; 25043034Sdougm if (sa_is_share((sa_group_t)parent)) { 25053034Sdougm id = sa_get_share_attr((sa_share_t)parent, "id"); 25063034Sdougm } 25073034Sdougm if (scf_handle->trans == NULL) { 25083034Sdougm if (is_nodetype(object, "optionset")) 25093034Sdougm (void) sa_optionset_name((sa_optionset_t)object, 25103034Sdougm oname, sizeof (oname), id); 25113034Sdougm else 25123034Sdougm (void) sa_security_name((sa_optionset_t)object, 25133034Sdougm oname, sizeof (oname), id); 25143034Sdougm ret = sa_start_transaction(scf_handle, oname); 25153034Sdougm } 25163034Sdougm if (ret == SA_OK) { 25173034Sdougm char *name; 25183034Sdougm char *value; 25193034Sdougm name = sa_get_property_attr(property, "type"); 25203034Sdougm value = sa_get_property_attr(property, "value"); 25213034Sdougm if (name != NULL && value != NULL) { 25223034Sdougm if (scf_handle->scf_state == SCH_STATE_INIT) 25233034Sdougm ret = sa_set_property(scf_handle, name, value); 25243034Sdougm } else 25253034Sdougm ret = SA_CONFIG_ERR; 25263034Sdougm if (name != NULL) 25273034Sdougm sa_free_attr_string(name); 25283034Sdougm if (value != NULL) 25293034Sdougm sa_free_attr_string(value); 25303034Sdougm } 25313034Sdougm if (id != NULL) 25323034Sdougm sa_free_attr_string(id); 25333034Sdougm } else { 25343034Sdougm /* 25353034Sdougm * ZFS is a special case. We do want to allow editing 25363034Sdougm * property/security lists since we can have a better 25373034Sdougm * syntax and we also want to keep things consistent 25383034Sdougm * when possible. 25393034Sdougm * 25403034Sdougm * Right now, we defer until the sa_commit_properties 25413034Sdougm * so we can get them all at once. We do need to mark 25423034Sdougm * the share as "changed" 25433034Sdougm */ 25443034Sdougm zfs_set_update((sa_share_t)parent); 25453034Sdougm } 25463034Sdougm } 25473034Sdougm return (ret); 25483034Sdougm } 25493034Sdougm 25503034Sdougm /* 25513034Sdougm * sa_remove_property(property) 25523034Sdougm * 25533034Sdougm * Remove the specied property from its containing object. Update the 25543034Sdougm * repository as appropriate. 25553034Sdougm */ 25563034Sdougm 25573034Sdougm int 25583034Sdougm sa_remove_property(sa_property_t property) 25593034Sdougm { 25603034Sdougm int ret = SA_OK; 25613034Sdougm 25623034Sdougm if (property != NULL) { 25633034Sdougm sa_optionset_t optionset; 25643034Sdougm sa_group_t group; 25653034Sdougm optionset = sa_get_property_parent(property); 25663034Sdougm if (optionset != NULL) { 25673034Sdougm group = sa_get_optionset_parent(optionset); 25683034Sdougm if (group != NULL) { 25693034Sdougm ret = sa_set_prop_by_prop(optionset, group, property, 25703034Sdougm SA_PROP_OP_REMOVE); 25713034Sdougm } 25723034Sdougm } 25733034Sdougm xmlUnlinkNode((xmlNodePtr)property); 25743034Sdougm xmlFreeNode((xmlNodePtr)property); 25753034Sdougm } else { 25763034Sdougm ret = SA_NO_SUCH_PROP; 25773034Sdougm } 25783034Sdougm return (ret); 25793034Sdougm } 25803034Sdougm 25813034Sdougm /* 25823034Sdougm * sa_update_property(property, value) 25833034Sdougm * 25843034Sdougm * Update the specified property to the new value. If value is NULL, 25853034Sdougm * we currently treat this as a remove. 25863034Sdougm */ 25873034Sdougm 25883034Sdougm int 25893034Sdougm sa_update_property(sa_property_t property, char *value) 25903034Sdougm { 25913034Sdougm int ret = SA_OK; 25923034Sdougm if (value == NULL) { 25933034Sdougm return (sa_remove_property(property)); 25943034Sdougm } else { 25953034Sdougm sa_optionset_t optionset; 25963034Sdougm sa_group_t group; 25973034Sdougm set_node_attr((void *)property, "value", value); 25983034Sdougm optionset = sa_get_property_parent(property); 25993034Sdougm if (optionset != NULL) { 26003034Sdougm group = sa_get_optionset_parent(optionset); 26013034Sdougm if (group != NULL) { 26023034Sdougm ret = sa_set_prop_by_prop(optionset, group, property, 26033034Sdougm SA_PROP_OP_UPDATE); 26043034Sdougm } 26053034Sdougm } else { 26063034Sdougm ret = SA_NO_SUCH_PROP; 26073034Sdougm } 26083034Sdougm } 26093034Sdougm return (ret); 26103034Sdougm } 26113034Sdougm 26123034Sdougm /* 26133034Sdougm * _sa_get_next_error(node) 26143034Sdougm * 26153034Sdougm * Get the next (first if node==NULL) error node in the 26163034Sdougm * document. "error" nodes are added if there were syntax errors 26173034Sdougm * during parsing of the /etc/dfs/dfstab file. They are preserved in 26183034Sdougm * comments and recreated in the doc on the next parse. 26193034Sdougm */ 26203034Sdougm 26213034Sdougm xmlNodePtr 26223034Sdougm _sa_get_next_error(xmlNodePtr node) 26233034Sdougm { 26243034Sdougm if (node == NULL) { 26253034Sdougm for (node = sa_config_tree->xmlChildrenNode; 26263034Sdougm node != NULL; node = node->next) 26273034Sdougm if (xmlStrcmp(node->name, (xmlChar *)"error") == 0) 26283034Sdougm return (node); 26293034Sdougm } else { 26303034Sdougm for (node = node->next; node != NULL; node = node->next) 26313034Sdougm if (xmlStrcmp(node->name, (xmlChar *)"error") == 0) 26323034Sdougm return (node); 26333034Sdougm } 26343034Sdougm return (node); 26353034Sdougm } 26363034Sdougm 26373034Sdougm /* 26383034Sdougm * sa_get_protocol_property(propset, prop) 26393034Sdougm * 26403034Sdougm * Get the specified protocol specific property. These are global to 26413034Sdougm * the protocol and not specific to a group or share. 26423034Sdougm */ 26433034Sdougm 26443034Sdougm sa_property_t 26453034Sdougm sa_get_protocol_property(sa_protocol_properties_t propset, char *prop) 26463034Sdougm { 26473034Sdougm xmlNodePtr node = (xmlNodePtr)propset; 26483034Sdougm xmlChar *value = NULL; 26493034Sdougm 26503034Sdougm for (node = node->children; node != NULL; 26513034Sdougm node = node->next) { 26523034Sdougm if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 26533034Sdougm if (prop == NULL) 26543034Sdougm break; 26553034Sdougm value = xmlGetProp(node, (xmlChar *)"type"); 26563034Sdougm if (value != NULL && 26573034Sdougm xmlStrcasecmp(value, (xmlChar *)prop) == 0) { 26583034Sdougm break; 26593034Sdougm } 26603034Sdougm if (value != NULL) { 26613034Sdougm xmlFree(value); 26623034Sdougm value = NULL; 26633034Sdougm } 26643034Sdougm } 26653034Sdougm } 26663034Sdougm if (value != NULL) 26673034Sdougm xmlFree(value); 26683034Sdougm if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) { 26693034Sdougm /* avoid a non option node -- it is possible to be a text node */ 26703034Sdougm node = NULL; 26713034Sdougm } 26723034Sdougm return ((sa_property_t)node); 26733034Sdougm } 26743034Sdougm 26753034Sdougm /* 26763034Sdougm * sa_get_next_protocol_property(prop) 26773034Sdougm * 26783034Sdougm * Get the next protocol specific property in the list. 26793034Sdougm */ 26803034Sdougm 26813034Sdougm sa_property_t 26823034Sdougm sa_get_next_protocol_property(sa_property_t prop) 26833034Sdougm { 26843034Sdougm xmlNodePtr node; 26853034Sdougm 26863034Sdougm for (node = ((xmlNodePtr)prop)->next; node != NULL; 26873034Sdougm node = node->next) { 26883034Sdougm if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 26893034Sdougm break; 26903034Sdougm } 26913034Sdougm } 26923034Sdougm return ((sa_property_t)node); 26933034Sdougm } 26943034Sdougm 26953034Sdougm /* 26963034Sdougm * sa_set_protocol_property(prop, value) 26973034Sdougm * 26983034Sdougm * Set the specified property to have the new value. The protocol 26993034Sdougm * specific plugin will then be called to update the property. 27003034Sdougm */ 27013034Sdougm 27023034Sdougm int 27033034Sdougm sa_set_protocol_property(sa_property_t prop, char *value) 27043034Sdougm { 27053034Sdougm sa_protocol_properties_t propset; 27063034Sdougm char *proto; 27073034Sdougm int ret = SA_INVALID_PROTOCOL; 27083034Sdougm 27093034Sdougm propset = ((xmlNodePtr)prop)->parent; 27103034Sdougm if (propset != NULL) { 27113034Sdougm proto = sa_get_optionset_attr(propset, "type"); 27123034Sdougm if (proto != NULL) { 27133034Sdougm set_node_attr((xmlNodePtr)prop, "value", value); 27143034Sdougm ret = sa_proto_set_property(proto, prop); 27153393Sdougm sa_free_attr_string(proto); 27163034Sdougm } 27173034Sdougm } 27183034Sdougm return (ret); 27193034Sdougm } 27203034Sdougm 27213034Sdougm /* 27223034Sdougm * sa_add_protocol_property(propset, prop) 27233034Sdougm * 27243034Sdougm * Add a new property to the protocol sepcific property set. 27253034Sdougm */ 27263034Sdougm 27273034Sdougm int 27283034Sdougm sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop) 27293034Sdougm { 27303034Sdougm xmlNodePtr node; 27313034Sdougm 27323034Sdougm /* should check for legitimacy */ 27333034Sdougm node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop); 27343034Sdougm if (node != NULL) 27353034Sdougm return (SA_OK); 27363034Sdougm return (SA_NO_MEMORY); 27373034Sdougm } 27383034Sdougm 27393034Sdougm /* 27403034Sdougm * sa_create_protocol_properties(proto) 27413034Sdougm * 27423034Sdougm * Create a protocol specifity property set. 27433034Sdougm */ 27443034Sdougm 27453034Sdougm sa_protocol_properties_t 27463034Sdougm sa_create_protocol_properties(char *proto) 27473034Sdougm { 27483034Sdougm xmlNodePtr node; 27493034Sdougm node = xmlNewNode(NULL, (xmlChar *)"propertyset"); 27503034Sdougm if (node != NULL) { 27513034Sdougm xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto); 27523034Sdougm } 27533034Sdougm return (node); 27543034Sdougm } 2755