1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /* helper functions for using libscf with sharemgr */
  28 
  29 #include <libscf.h>
  30 #include <libxml/parser.h>
  31 #include <libxml/tree.h>
  32 #include "libshare.h"
  33 #include "libshare_impl.h"
  34 #include "scfutil.h"
  35 #include <string.h>
  36 #include <ctype.h>
  37 #include <errno.h>
  38 #include <uuid/uuid.h>
  39 #include <sys/param.h>
  40 #include <signal.h>
  41 #include <sys/time.h>
  42 #include <libintl.h>
  43 
  44 ssize_t scf_max_name_len;
  45 extern struct sa_proto_plugin *sap_proto_list;
  46 extern sa_handle_impl_t get_handle_for_root(xmlNodePtr);
  47 static void set_transaction_tstamp(sa_handle_impl_t);
  48 /*
  49  * The SMF facility uses some properties that must exist. We want to
  50  * skip over these when processing protocol options.
  51  */
  52 static char *skip_props[] = {
  53         "modify_authorization",
  54         "action_authorization",
  55         "value_authorization",
  56         NULL
  57 };
  58 
  59 /*
  60  * sa_scf_fini(handle)
  61  *
  62  * Must be called when done. Called with the handle allocated in
  63  * sa_scf_init(), it cleans up the state and frees any SCF resources
  64  * still in use. Called by sa_fini().
  65  */
  66 
  67 void
  68 sa_scf_fini(scfutilhandle_t *handle)
  69 {
  70         if (handle != NULL) {
  71                 int unbind = 0;
  72                 if (handle->scope != NULL) {
  73                         unbind = 1;
  74                         scf_scope_destroy(handle->scope);
  75                 }
  76                 if (handle->instance != NULL)
  77                         scf_instance_destroy(handle->instance);
  78                 if (handle->service != NULL)
  79                         scf_service_destroy(handle->service);
  80                 if (handle->pg != NULL)
  81                         scf_pg_destroy(handle->pg);
  82                 if (handle->handle != NULL) {
  83                         handle->scf_state = SCH_STATE_UNINIT;
  84                         if (unbind)
  85                                 (void) scf_handle_unbind(handle->handle);
  86                         scf_handle_destroy(handle->handle);
  87                 }
  88                 free(handle);
  89         }
  90 }
  91 
  92 /*
  93  * sa_scf_init()
  94  *
  95  * Must be called before using any of the SCF functions. Called by
  96  * sa_init() during the API setup.
  97  */
  98 
  99 scfutilhandle_t *
 100 sa_scf_init(sa_handle_impl_t ihandle)
 101 {
 102         scfutilhandle_t *handle;
 103 
 104         scf_max_name_len = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
 105         if (scf_max_name_len <= 0)
 106                 scf_max_name_len = SA_MAX_NAME_LEN + 1;
 107 
 108         handle = calloc(1, sizeof (scfutilhandle_t));
 109         if (handle == NULL)
 110                 return (handle);
 111 
 112         ihandle->scfhandle = handle;
 113         handle->scf_state = SCH_STATE_INITIALIZING;
 114         handle->handle = scf_handle_create(SCF_VERSION);
 115         if (handle->handle == NULL) {
 116                 free(handle);
 117                 handle = NULL;
 118                 (void) printf("libshare could not access SMF repository: %s\n",
 119                     scf_strerror(scf_error()));
 120                 return (handle);
 121         }
 122         if (scf_handle_bind(handle->handle) != 0)
 123                 goto err;
 124 
 125         handle->scope = scf_scope_create(handle->handle);
 126         handle->service = scf_service_create(handle->handle);
 127         handle->pg = scf_pg_create(handle->handle);
 128 
 129         /* Make sure we have sufficient SMF running */
 130         handle->instance = scf_instance_create(handle->handle);
 131         if (handle->scope == NULL || handle->service == NULL ||
 132             handle->pg == NULL || handle->instance == NULL)
 133                 goto err;
 134         if (scf_handle_get_scope(handle->handle,
 135             SCF_SCOPE_LOCAL, handle->scope) != 0)
 136                 goto err;
 137         if (scf_scope_get_service(handle->scope,
 138             SA_GROUP_SVC_NAME, handle->service) != 0)
 139                 goto err;
 140 
 141         handle->scf_state = SCH_STATE_INIT;
 142         if (sa_get_instance(handle, "default") != SA_OK) {
 143                 sa_group_t defgrp;
 144                 defgrp = sa_create_group((sa_handle_t)ihandle, "default", NULL);
 145                 /* Only NFS enabled for "default" group. */
 146                 if (defgrp != NULL)
 147                         (void) sa_create_optionset(defgrp, "nfs");
 148         }
 149 
 150         return (handle);
 151 
 152         /* Error handling/unwinding */
 153 err:
 154         (void) sa_scf_fini(handle);
 155         (void) printf("libshare SMF initialization problem: %s\n",
 156             scf_strerror(scf_error()));
 157         return (NULL);
 158 }
 159 
 160 /*
 161  * get_scf_limit(name)
 162  *
 163  * Since we use  scf_limit a lot and do the same  check and return the
 164  * same  value  if  it  fails,   implement  as  a  function  for  code
 165  * simplification.  Basically, if  name isn't found, return MAXPATHLEN
 166  * (1024) so we have a reasonable default buffer size.
 167  */
 168 static ssize_t
 169 get_scf_limit(uint32_t name)
 170 {
 171         ssize_t vallen;
 172 
 173         vallen = scf_limit(name);
 174         if (vallen == (ssize_t)-1)
 175                 vallen = MAXPATHLEN;
 176         return (vallen);
 177 }
 178 
 179 /*
 180  * skip_property(name)
 181  *
 182  * Internal function to check to see if a property is an SMF magic
 183  * property that needs to be skipped.
 184  */
 185 static int
 186 skip_property(char *name)
 187 {
 188         int i;
 189 
 190         for (i = 0; skip_props[i] != NULL; i++)
 191                 if (strcmp(name, skip_props[i]) == 0)
 192                 return (1);
 193         return (0);
 194 }
 195 
 196 /*
 197  * generate_unique_sharename(sharename)
 198  *
 199  * Shares are represented in SMF as property groups. Due to share
 200  * paths containing characters that are not allowed in SMF names and
 201  * the need to be unique, we use UUIDs to construct a unique name.
 202  */
 203 
 204 static void
 205 generate_unique_sharename(char *sharename)
 206 {
 207         uuid_t uuid;
 208 
 209         uuid_generate(uuid);
 210         (void) strcpy(sharename, "S-");
 211         uuid_unparse(uuid, sharename + 2);
 212 }
 213 
 214 /*
 215  * valid_protocol(proto)
 216  *
 217  * Check to see if the specified protocol is a valid one for the
 218  * general sharemgr facility. We determine this by checking which
 219  * plugin protocols were found.
 220  */
 221 
 222 static int
 223 valid_protocol(char *proto)
 224 {
 225         struct sa_proto_plugin *plugin;
 226         for (plugin = sap_proto_list; plugin != NULL;
 227             plugin = plugin->plugin_next)
 228                 if (strcmp(proto, plugin->plugin_ops->sa_protocol) == 0)
 229                         return (1);
 230         return (0);
 231 }
 232 
 233 /*
 234  * sa_extract_pgroup(root, handle, pg, nodetype, proto, sectype)
 235  *
 236  * Extract the name property group and create the specified type of
 237  * node on the provided group.  type will be optionset or security.
 238  */
 239 
 240 static int
 241 sa_extract_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
 242                         scf_propertygroup_t *pg,
 243                         char *nodetype, char *proto, char *sectype)
 244 {
 245         xmlNodePtr node;
 246         scf_iter_t *iter;
 247         scf_property_t *prop;
 248         scf_value_t *value;
 249         char *name;
 250         char *valuestr;
 251         ssize_t vallen;
 252         int ret = SA_OK;
 253 
 254         vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
 255 
 256         node = xmlNewChild(root, NULL, (xmlChar *)nodetype, NULL);
 257         if (node == NULL)
 258                 return (ret);
 259 
 260         if (proto != NULL)
 261                 (void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
 262         if (sectype != NULL)
 263                 (void) xmlSetProp(node, (xmlChar *)"sectype",
 264                     (xmlChar *)sectype);
 265         /*
 266          * Have node to work with so iterate over the properties
 267          * in the pg and create option sub nodes.
 268          */
 269         iter = scf_iter_create(handle->handle);
 270         value = scf_value_create(handle->handle);
 271         prop = scf_property_create(handle->handle);
 272         name = malloc(scf_max_name_len);
 273         valuestr = malloc(vallen);
 274         /*
 275          * Want to iterate through the properties and add them
 276          * to the base optionset.
 277          */
 278         if (iter == NULL || value == NULL || prop == NULL ||
 279             valuestr == NULL || name == NULL) {
 280                 ret = SA_NO_MEMORY;
 281                 goto out;
 282         }
 283         if (scf_iter_pg_properties(iter, pg) == 0) {
 284                 /* Now iterate the properties in the group */
 285                 while (scf_iter_next_property(iter, prop) > 0) {
 286                         /* have a property */
 287                         if (scf_property_get_name(prop, name,
 288                             scf_max_name_len) > 0) {
 289                                 sa_property_t saprop;
 290                                 /* Some properties are part of the framework */
 291                                 if (skip_property(name))
 292                                         continue;
 293                                 if (scf_property_get_value(prop, value) != 0)
 294                                         continue;
 295                                 if (scf_value_get_astring(value, valuestr,
 296                                     vallen) < 0)
 297                                         continue;
 298                                 saprop = sa_create_property(name, valuestr);
 299                                 if (saprop != NULL) {
 300                                         /*
 301                                          * Since in SMF, don't
 302                                          * recurse. Use xmlAddChild
 303                                          * directly, instead.
 304                                          */
 305                                         (void) xmlAddChild(node,
 306                                             (xmlNodePtr) saprop);
 307                                 }
 308                         }
 309                 }
 310         }
 311 out:
 312         /* cleanup to avoid memory leaks */
 313         if (value != NULL)
 314                 scf_value_destroy(value);
 315         if (iter != NULL)
 316                 scf_iter_destroy(iter);
 317         if (prop != NULL)
 318                 scf_property_destroy(prop);
 319         if (name != NULL)
 320                 free(name);
 321         if (valuestr != NULL)
 322                 free(valuestr);
 323 
 324         return (ret);
 325 }
 326 
 327 /*
 328  * sa_extract_attrs(root, handle, instance)
 329  *
 330  * Local function to extract the actual attributes/properties from the
 331  * property group of the service instance. These are the well known
 332  * attributes of "state" and "zfs". If additional attributes are
 333  * added, they should be added here.
 334  */
 335 
 336 static void
 337 sa_extract_attrs(xmlNodePtr root, scfutilhandle_t *handle,
 338                     scf_instance_t *instance)
 339 {
 340         scf_property_t *prop;
 341         scf_value_t *value;
 342         char *valuestr;
 343         ssize_t vallen;
 344 
 345         vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
 346         prop = scf_property_create(handle->handle);
 347         value = scf_value_create(handle->handle);
 348         valuestr = malloc(vallen);
 349         if (prop == NULL || value == NULL || valuestr == NULL ||
 350             scf_instance_get_pg(instance, "operation", handle->pg) != 0) {
 351                 goto out;
 352         }
 353         /*
 354          * Have a property group with desired name so now get
 355          * the known attributes.
 356          */
 357         if (scf_pg_get_property(handle->pg, "state", prop) == 0) {
 358                 /* Found the property so get the value */
 359                 if (scf_property_get_value(prop, value) == 0) {
 360                         if (scf_value_get_astring(value, valuestr,
 361                             vallen) >= 0) {
 362                                 (void) xmlSetProp(root, (xmlChar *)"state",
 363                                     (xmlChar *)valuestr);
 364                         }
 365                 }
 366         }
 367         if (scf_pg_get_property(handle->pg, "zfs", prop) == 0) {
 368                 /* Found the property so get the value */
 369                 if (scf_property_get_value(prop, value) == 0) {
 370                         if (scf_value_get_astring(value, valuestr,
 371                             vallen) > 0) {
 372                                 (void) xmlSetProp(root, (xmlChar *)"zfs",
 373                                     (xmlChar *)valuestr);
 374                         }
 375                 }
 376         }
 377 out:
 378         if (valuestr != NULL)
 379                 free(valuestr);
 380         if (value != NULL)
 381                 scf_value_destroy(value);
 382         if (prop != NULL)
 383                 scf_property_destroy(prop);
 384 }
 385 
 386 /*
 387  * List of known share attributes.
 388  */
 389 
 390 static char *share_attr[] = {
 391         "path",
 392         "id",
 393         "drive-letter",
 394         "exclude",
 395         NULL,
 396 };
 397 
 398 static int
 399 is_share_attr(char *name)
 400 {
 401         int i;
 402         for (i = 0; share_attr[i] != NULL; i++)
 403                 if (strcmp(name, share_attr[i]) == 0)
 404                         return (1);
 405         return (0);
 406 }
 407 
 408 /*
 409  * _sa_make_resource(node, valuestr)
 410  *
 411  * Make a resource node on the share node. The valusestr will either
 412  * be old format (SMF acceptable string) or new format (pretty much an
 413  * arbitrary string with "nnn:" prefixing in order to persist
 414  * mapping). The input valuestr will get modified in place. This is
 415  * only used in SMF repository parsing. A possible third field will be
 416  * a "description" string.
 417  */
 418 
 419 static void
 420 _sa_make_resource(xmlNodePtr node, char *valuestr)
 421 {
 422         char *idx;
 423         char *name;
 424         char *description = NULL;
 425 
 426         idx = valuestr;
 427         name = strchr(valuestr, ':');
 428         if (name == NULL) {
 429                 /* this is old form so give an index of "0" */
 430                 idx = "0";
 431                 name = valuestr;
 432         } else {
 433                 /* NUL the ':' and move past it */
 434                 *name++ = '\0';
 435                 /* There could also be a description string */
 436                 description = strchr(name, ':');
 437                 if (description != NULL)
 438                         *description++ = '\0';
 439         }
 440         node = xmlNewChild(node, NULL, (xmlChar *)"resource", NULL);
 441         if (node != NULL) {
 442                 (void) xmlSetProp(node, (xmlChar *)"name", (xmlChar *)name);
 443                 (void) xmlSetProp(node, (xmlChar *)"id", (xmlChar *)idx);
 444                 /* SMF values are always persistent */
 445                 (void) xmlSetProp(node, (xmlChar *)"type",
 446                     (xmlChar *)"persist");
 447                 if (description != NULL && strlen(description) > 0) {
 448                         (void) xmlNewChild(node, NULL, (xmlChar *)"description",
 449                             (xmlChar *)description);
 450                 }
 451         }
 452 }
 453 
 454 
 455 /*
 456  * sa_share_from_pgroup
 457  *
 458  * Extract the share definition from the share property group. We do
 459  * some sanity checking to avoid bad data.
 460  *
 461  * Since this is only constructing the internal data structures, we
 462  * don't use the sa_* functions most of the time.
 463  */
 464 void
 465 sa_share_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
 466                         scf_propertygroup_t *pg, char *id)
 467 {
 468         xmlNodePtr node;
 469         char *name;
 470         scf_iter_t *iter;
 471         scf_property_t *prop;
 472         scf_value_t *value;
 473         ssize_t vallen;
 474         char *valuestr;
 475         int ret = SA_OK;
 476         int have_path = 0;
 477 
 478         /*
 479          * While preliminary check (starts with 'S') passed before
 480          * getting here. Need to make sure it is in ID syntax
 481          * (Snnnnnn). Note that shares with properties have similar
 482          * pgroups.
 483          */
 484         vallen = strlen(id);
 485         if (*id == SA_SHARE_PG_PREFIX[0] && vallen == SA_SHARE_PG_LEN) {
 486                 uuid_t uuid;
 487                 if (strncmp(id, SA_SHARE_PG_PREFIX,
 488                     SA_SHARE_PG_PREFIXLEN) != 0 ||
 489                     uuid_parse(id + 2, uuid) < 0)
 490                         return;
 491         } else {
 492                 return;
 493         }
 494 
 495         vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
 496 
 497         iter = scf_iter_create(handle->handle);
 498         value = scf_value_create(handle->handle);
 499         prop = scf_property_create(handle->handle);
 500         name = malloc(scf_max_name_len);
 501         valuestr = malloc(vallen);
 502 
 503         /*
 504          * Construct the share XML node. It is similar to sa_add_share
 505          * but never changes the repository. Also, there won't be any
 506          * ZFS or transient shares.  Root will be the group it is
 507          * associated with.
 508          */
 509         node = xmlNewChild(root, NULL, (xmlChar *)"share", NULL);
 510         if (node != NULL) {
 511                 /*
 512                  * Make sure the UUID part of the property group is
 513                  * stored in the share "id" property. We use this
 514                  * later.
 515                  */
 516                 (void) xmlSetProp(node, (xmlChar *)"id", (xmlChar *)id);
 517                 (void) xmlSetProp(node, (xmlChar *)"type",
 518                     (xmlChar *)"persist");
 519         }
 520 
 521         if (iter == NULL || value == NULL || prop == NULL || name == NULL)
 522                 goto out;
 523 
 524         /* Iterate over the share pg properties */
 525         if (scf_iter_pg_properties(iter, pg) != 0)
 526                 goto out;
 527 
 528         while (scf_iter_next_property(iter, prop) > 0) {
 529                 ret = SA_SYSTEM_ERR; /* assume the worst */
 530                 if (scf_property_get_name(prop, name, scf_max_name_len) > 0) {
 531                         if (scf_property_get_value(prop, value) == 0) {
 532                                 if (scf_value_get_astring(value, valuestr,
 533                                     vallen) >= 0) {
 534                                         ret = SA_OK;
 535                                 }
 536                         } else if (strcmp(name, "resource") == 0) {
 537                                 ret = SA_OK;
 538                         }
 539                 }
 540                 if (ret != SA_OK)
 541                         continue;
 542                 /*
 543                  * Check that we have the "path" property in
 544                  * name. The string in name will always be nul
 545                  * terminated if scf_property_get_name()
 546                  * succeeded.
 547                  */
 548                 if (strcmp(name, "path") == 0)
 549                         have_path = 1;
 550                 if (is_share_attr(name)) {
 551                         /*
 552                          * If a share attr, then simple -
 553                          * usually path and id name
 554                          */
 555                         (void) xmlSetProp(node, (xmlChar *)name,
 556                             (xmlChar *)valuestr);
 557                 } else if (strcmp(name, "resource") == 0) {
 558                         /*
 559                          * Resource names handled differently since
 560                          * there can be multiple on each share. The
 561                          * "resource" id must be preserved since this
 562                          * will be used by some protocols in mapping
 563                          * "property spaces" to names and is always
 564                          * used to create SMF property groups specific
 565                          * to resources.  CIFS needs this.  The first
 566                          * value is present so add and then loop for
 567                          * any additional. Since this is new and
 568                          * previous values may exist, handle
 569                          * conversions.
 570                          */
 571                         scf_iter_t *viter;
 572                         viter = scf_iter_create(handle->handle);
 573                         if (viter != NULL &&
 574                             scf_iter_property_values(viter, prop) == 0) {
 575                                 while (scf_iter_next_value(viter, value) > 0) {
 576                                         /* Have a value so process it */
 577                                         if (scf_value_get_ustring(value,
 578                                             valuestr, vallen) >= 0) {
 579                                                 /* have a ustring */
 580                                                 _sa_make_resource(node,
 581                                                     valuestr);
 582                                         } else if (scf_value_get_astring(value,
 583                                             valuestr, vallen) >= 0) {
 584                                                 /* have an astring */
 585                                                 _sa_make_resource(node,
 586                                                     valuestr);
 587                                         }
 588                                 }
 589                                 scf_iter_destroy(viter);
 590                         }
 591                 } else {
 592                         if (strcmp(name, "description") == 0) {
 593                                 /* We have a description node */
 594                                 xmlNodePtr desc;
 595                                 desc = xmlNewChild(node, NULL,
 596                                     (xmlChar *)"description", NULL);
 597                                 if (desc != NULL)
 598                                         xmlNodeSetContent(desc,
 599                                             (xmlChar *)valuestr);
 600                         }
 601                 }
 602         }
 603 out:
 604         /*
 605          * A share without a path is broken so we want to not include
 606          * these.  They shouldn't happen but if you kill a sharemgr in
 607          * the process of creating a share, it could happen.  They
 608          * should be harmless.  It is also possible that another
 609          * sharemgr is running and in the process of creating a share.
 610          */
 611         if (have_path == 0 && node != NULL) {
 612                 xmlUnlinkNode(node);
 613                 xmlFreeNode(node);
 614         }
 615         if (name != NULL)
 616                 free(name);
 617         if (valuestr != NULL)
 618                 free(valuestr);
 619         if (value != NULL)
 620                 scf_value_destroy(value);
 621         if (iter != NULL)
 622                 scf_iter_destroy(iter);
 623         if (prop != NULL)
 624                 scf_property_destroy(prop);
 625 }
 626 
 627 /*
 628  * find_share_by_id(shareid)
 629  *
 630  * Search all shares in all groups until we find the share represented
 631  * by "id".
 632  */
 633 
 634 static sa_share_t
 635 find_share_by_id(sa_handle_t handle, char *shareid)
 636 {
 637         sa_group_t group;
 638         sa_share_t share = NULL;
 639         char *id = NULL;
 640         int done = 0;
 641 
 642         for (group = sa_get_group(handle, NULL);
 643             group != NULL && !done;
 644             group = sa_get_next_group(group)) {
 645                 for (share = sa_get_share(group, NULL);
 646                     share != NULL;
 647                     share = sa_get_next_share(share)) {
 648                         id = sa_get_share_attr(share, "id");
 649                         if (id != NULL && strcmp(id, shareid) == 0) {
 650                                 sa_free_attr_string(id);
 651                                 id = NULL;
 652                                 done++;
 653                                 break;
 654                         }
 655                         if (id != NULL) {
 656                                 sa_free_attr_string(id);
 657                                 id = NULL;
 658                         }
 659                 }
 660         }
 661         return (share);
 662 }
 663 
 664 /*
 665  * find_resource_by_index(share, index)
 666  *
 667  * Search the resource records on the share for the id index.
 668  */
 669 static sa_resource_t
 670 find_resource_by_index(sa_share_t share, char *index)
 671 {
 672         sa_resource_t resource;
 673         sa_resource_t found = NULL;
 674         char *id;
 675 
 676         for (resource = sa_get_share_resource(share, NULL);
 677             resource != NULL && found == NULL;
 678             resource = sa_get_next_resource(resource)) {
 679                 id = (char *)xmlGetProp((xmlNodePtr)resource, (xmlChar *)"id");
 680                 if (id != NULL) {
 681                         if (strcmp(id, index) == 0) {
 682                                 /* found it so save in "found" */
 683                                 found = resource;
 684                         }
 685                         sa_free_attr_string(id);
 686                 }
 687         }
 688         return (found);
 689 }
 690 
 691 /*
 692  * sa_share_props_from_pgroup(root, handle, pg, id, sahandle)
 693  *
 694  * Extract share properties from the SMF property group. More sanity
 695  * checks are done and the share object is created. We ignore some
 696  * errors that could exist in the repository and only worry about
 697  * property groups that validate in naming.
 698  */
 699 
 700 static int
 701 sa_share_props_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
 702                         scf_propertygroup_t *pg, char *id, sa_handle_t sahandle)
 703 {
 704         xmlNodePtr node;
 705         char *name = NULL;
 706         scf_iter_t *iter = NULL;
 707         scf_property_t *prop = NULL;
 708         scf_value_t *value = NULL;
 709         ssize_t vallen;
 710         char *valuestr = NULL;
 711         int ret = SA_OK;
 712         char *sectype = NULL;
 713         char *proto;
 714         sa_share_t share;
 715         uuid_t uuid;
 716 
 717         /*
 718          * While preliminary check (starts with 'S') passed before
 719          * getting here. Need to make sure it is in ID syntax
 720          * (Snnnnnn). Note that shares with properties have similar
 721          * pgroups. If the pg name is more than SA_SHARE_PG_LEN
 722          * characters, it is likely one of the protocol/security
 723          * versions.
 724          */
 725         vallen = strlen(id);
 726         if (*id != SA_SHARE_PG_PREFIX[0] || vallen <= SA_SHARE_PG_LEN) {
 727                 /*
 728                  * It is ok to not have what we thought since someone might
 729                  * have added a name via SMF.
 730                  */
 731                 return (ret);
 732         }
 733         if (strncmp(id, SA_SHARE_PG_PREFIX, SA_SHARE_PG_PREFIXLEN) == 0) {
 734                 proto = strchr(id, '_');
 735                 if (proto == NULL)
 736                         return (ret);
 737                 *proto++ = '\0';
 738                 if (uuid_parse(id + SA_SHARE_PG_PREFIXLEN, uuid) < 0)
 739                         return (ret);
 740                 /*
 741                  * probably a legal optionset so check a few more
 742                  * syntax points below.
 743                  */
 744                 if (*proto == '\0') {
 745                         /* not a valid proto (null) */
 746                         return (ret);
 747                 }
 748 
 749                 sectype = strchr(proto, '_');
 750                 if (sectype != NULL)
 751                         *sectype++ = '\0';
 752                 if (!valid_protocol(proto))
 753                         return (ret);
 754         }
 755 
 756         /*
 757          * To get here, we have a valid protocol and possibly a
 758          * security. We now have to find the share that it is really
 759          * associated with. The "id" portion of the pgroup name will
 760          * match.
 761          */
 762 
 763         share = find_share_by_id(sahandle, id);
 764         if (share == NULL)
 765                 return (SA_BAD_PATH);
 766 
 767         root = (xmlNodePtr)share;
 768 
 769         vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
 770 
 771         if (sectype == NULL)
 772                 node = xmlNewChild(root, NULL, (xmlChar *)"optionset", NULL);
 773         else {
 774                 if (isdigit((int)*sectype)) {
 775                         sa_resource_t resource;
 776                         /*
 777                          * If sectype[0] is a digit, then it is an index into
 778                          * the resource names. We need to find a resource
 779                          * record and then get the properties into an
 780                          * optionset. The optionset becomes the "node" and the
 781                          * rest is hung off of the share.
 782                          */
 783                         resource = find_resource_by_index(share, sectype);
 784                         if (resource != NULL) {
 785                                 node = xmlNewChild(resource, NULL,
 786                                     (xmlChar *)"optionset", NULL);
 787                         } else {
 788                                 /* This shouldn't happen. */
 789                                 ret = SA_SYSTEM_ERR;
 790                                 goto out;
 791                         }
 792                 } else {
 793                         /*
 794                          * If not a digit, then it is a security type
 795                          * (alternate option space). Security types start with
 796                          * an alphabetic.
 797                          */
 798                         node = xmlNewChild(root, NULL, (xmlChar *)"security",
 799                             NULL);
 800                         if (node != NULL)
 801                                 (void) xmlSetProp(node, (xmlChar *)"sectype",
 802                                     (xmlChar *)sectype);
 803                 }
 804         }
 805         if (node == NULL) {
 806                 ret = SA_NO_MEMORY;
 807                 goto out;
 808         }
 809 
 810         (void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
 811         /* now find the properties */
 812         iter = scf_iter_create(handle->handle);
 813         value = scf_value_create(handle->handle);
 814         prop = scf_property_create(handle->handle);
 815         name = malloc(scf_max_name_len);
 816         valuestr = malloc(vallen);
 817 
 818         if (iter == NULL || value == NULL || prop == NULL || name == NULL)
 819                 goto out;
 820 
 821         /* iterate over the share pg properties */
 822         if (scf_iter_pg_properties(iter, pg) == 0) {
 823                 while (scf_iter_next_property(iter, prop) > 0) {
 824                         ret = SA_SYSTEM_ERR; /* assume the worst */
 825                         if (scf_property_get_name(prop, name,
 826                             scf_max_name_len) > 0) {
 827                                 if (scf_property_get_value(prop, value) == 0) {
 828                                         if (scf_value_get_astring(value,
 829                                             valuestr, vallen) >= 0) {
 830                                                 ret = SA_OK;
 831                                         }
 832                                 }
 833                         } else {
 834                                 ret = SA_SYSTEM_ERR;
 835                         }
 836                         if (ret == SA_OK) {
 837                                 sa_property_t prop;
 838                                 prop = sa_create_property(name, valuestr);
 839                                 if (prop != NULL)
 840                                         prop = (sa_property_t)xmlAddChild(node,
 841                                             (xmlNodePtr)prop);
 842                                 else
 843                                         ret = SA_NO_MEMORY;
 844                         }
 845                 }
 846         } else {
 847                 ret = SA_SYSTEM_ERR;
 848         }
 849 out:
 850         if (iter != NULL)
 851                 scf_iter_destroy(iter);
 852         if (value != NULL)
 853                 scf_value_destroy(value);
 854         if (prop != NULL)
 855                 scf_property_destroy(prop);
 856         if (name != NULL)
 857                 free(name);
 858         if (valuestr != NULL)
 859                 free(valuestr);
 860         return (ret);
 861 }
 862 
 863 /*
 864  * sa_extract_group(root, handle, instance)
 865  *
 866  * Get the config info for this instance of a group and create the XML
 867  * subtree from it.
 868  */
 869 
 870 static int
 871 sa_extract_group(xmlNodePtr root, scfutilhandle_t *handle,
 872     scf_instance_t *instance, sa_handle_t sahandle)
 873 {
 874         char *buff;
 875         xmlNodePtr node;
 876         scf_iter_t *iter;
 877         char *proto;
 878         char *sectype;
 879         boolean_t have_shares = B_FALSE;
 880         boolean_t is_default = B_FALSE;
 881         boolean_t is_nfs = B_FALSE;
 882         int ret = SA_OK;
 883         int err;
 884 
 885         buff = malloc(scf_max_name_len);
 886         if (buff == NULL)
 887                 return (SA_NO_MEMORY);
 888 
 889         iter = scf_iter_create(handle->handle);
 890         if (iter == NULL) {
 891                 ret = SA_NO_MEMORY;
 892                 goto out;
 893         }
 894 
 895         if (scf_instance_get_name(instance, buff, scf_max_name_len) > 0) {
 896                 node = xmlNewChild(root, NULL, (xmlChar *)"group", NULL);
 897                 if (node == NULL) {
 898                         ret = SA_NO_MEMORY;
 899                         goto out;
 900                 }
 901                 (void) xmlSetProp(node, (xmlChar *)"name", (xmlChar *)buff);
 902                 if (strcmp(buff, "default") == 0)
 903                         is_default = B_TRUE;
 904 
 905                 sa_extract_attrs(node, handle, instance);
 906                 /*
 907                  * Iterate through all the property groups
 908                  * looking for those with security or
 909                  * optionset prefixes. The names of the
 910                  * matching pgroups are parsed to get the
 911                  * protocol, and for security, the sectype.
 912                  * Syntax is as follows:
 913                  *    optionset | optionset_<proto>
 914                  *    security_default | security_<proto>_<sectype>
 915                  * "operation" is handled by
 916                  * sa_extract_attrs().
 917                  */
 918                 if (scf_iter_instance_pgs(iter, instance) != 0) {
 919                         ret = SA_NO_MEMORY;
 920                         goto out;
 921                 }
 922                 while (scf_iter_next_pg(iter, handle->pg) > 0) {
 923                         /* Have a pgroup so sort it out */
 924                         ret = scf_pg_get_name(handle->pg, buff,
 925                             scf_max_name_len);
 926                         if (ret <= 0)
 927                                 continue;
 928                         is_nfs = B_FALSE;
 929 
 930                         if (buff[0] == SA_SHARE_PG_PREFIX[0]) {
 931                                 sa_share_from_pgroup(node, handle,
 932                                     handle->pg, buff);
 933                                 have_shares = B_TRUE;
 934                         } else if (strncmp(buff, "optionset", 9) == 0) {
 935                                 char *nodetype = "optionset";
 936                                 /* Have an optionset */
 937                                 sectype = NULL;
 938                                 proto = strchr(buff, '_');
 939                                 if (proto != NULL) {
 940                                         *proto++ = '\0';
 941                                         sectype = strchr(proto, '_');
 942                                         if (sectype != NULL) {
 943                                                 *sectype++ = '\0';
 944                                                 nodetype = "security";
 945                                         }
 946                                         is_nfs = strcmp(proto, "nfs") == 0;
 947                                 } else if (strlen(buff) > 9) {
 948                                         /*
 949                                          * This can only occur if
 950                                          * someone has made changes
 951                                          * via an SMF command. Since
 952                                          * this would be an unknown
 953                                          * syntax, we just ignore it.
 954                                          */
 955                                         continue;
 956                                 }
 957                                 /*
 958                                  * If the group is not "default" or is
 959                                  * "default" and is_nfs, then extract the
 960                                  * pgroup.  If it is_default and !is_nfs,
 961                                  * then we have an error and should remove
 962                                  * the extraneous protocols.  We don't care
 963                                  * about errors on scf_pg_delete since we
 964                                  * might not have permission during an
 965                                  * extract only.
 966                                  */
 967                                 if (!is_default || is_nfs) {
 968                                         ret = sa_extract_pgroup(node, handle,
 969                                             handle->pg, nodetype, proto,
 970                                             sectype);
 971                                 } else {
 972                                         err = scf_pg_delete(handle->pg);
 973                                         if (err == 0)
 974                                                 (void) fprintf(stderr,
 975                                                     dgettext(TEXT_DOMAIN,
 976                                                     "Removed protocol \"%s\" "
 977                                                     "from group \"default\"\n"),
 978                                                     proto);
 979                                 }
 980                         } else if (strncmp(buff, "security", 8) == 0) {
 981                                 /*
 982                                  * Have a security (note that
 983                                  * this should change in the
 984                                  * future)
 985                                  */
 986                                 proto = strchr(buff, '_');
 987                                 sectype = NULL;
 988                                 if (proto != NULL) {
 989                                         *proto++ = '\0';
 990                                         sectype = strchr(proto, '_');
 991                                         if (sectype != NULL)
 992                                                 *sectype++ = '\0';
 993                                         if (strcmp(proto, "default") == 0)
 994                                                 proto = NULL;
 995                                 }
 996                                 ret = sa_extract_pgroup(node, handle,
 997                                     handle->pg, "security", proto, sectype);
 998                         }
 999                         /* Ignore everything else */
1000                 }
1001                 /*
1002                  * Make sure we have a valid default group.
1003                  * On first boot, default won't have any
1004                  * protocols defined and won't be enabled (but
1005                  * should be).  "default" only has NFS enabled on it.
1006                  */
1007                 if (is_default) {
1008                         char *state = sa_get_group_attr((sa_group_t)node,
1009                             "state");
1010 
1011                         if (state == NULL) {
1012                                 /* set attribute to enabled */
1013                                 (void) sa_set_group_attr((sa_group_t)node,
1014                                     "state", "enabled");
1015                                 (void) sa_create_optionset((sa_group_t)node,
1016                                     "nfs");
1017                         } else {
1018                                 sa_free_attr_string(state);
1019                         }
1020                 }
1021                 /* Do a second pass if shares were found */
1022                 if (have_shares && scf_iter_instance_pgs(iter, instance) == 0) {
1023                         while (scf_iter_next_pg(iter, handle->pg) > 0) {
1024                                 /*
1025                                  * Have a pgroup so see if it is a
1026                                  * share optionset
1027                                  */
1028                                 err = scf_pg_get_name(handle->pg, buff,
1029                                     scf_max_name_len);
1030                                 if (err  <= 0)
1031                                         continue;
1032                                 if (buff[0] == SA_SHARE_PG_PREFIX[0]) {
1033                                         ret = sa_share_props_from_pgroup(node,
1034                                             handle, handle->pg, buff,
1035                                             sahandle);
1036                                 }
1037                         }
1038                 }
1039         }
1040 out:
1041         if (iter != NULL)
1042                 scf_iter_destroy(iter);
1043         if (buff != NULL)
1044                 free(buff);
1045         return (ret);
1046 }
1047 
1048 /*
1049  * sa_extract_defaults(root, handle, instance)
1050  *
1051  * Local function to find the default properties that live in the
1052  * default instance's "operation" property group.
1053  */
1054 
1055 static void
1056 sa_extract_defaults(xmlNodePtr root, scfutilhandle_t *handle,
1057                     scf_instance_t *instance)
1058 {
1059         xmlNodePtr node;
1060         scf_property_t *prop;
1061         scf_value_t *value;
1062         char *valuestr;
1063         ssize_t vallen;
1064 
1065         vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
1066         prop = scf_property_create(handle->handle);
1067         value = scf_value_create(handle->handle);
1068         valuestr = malloc(vallen);
1069 
1070         if (prop == NULL || value == NULL || vallen == 0 ||
1071             scf_instance_get_pg(instance, "operation", handle->pg) != 0)
1072                 goto out;
1073 
1074         if (scf_pg_get_property(handle->pg, "legacy-timestamp", prop) != 0)
1075                 goto out;
1076 
1077         /* Found the property so get the value */
1078         if (scf_property_get_value(prop, value) == 0) {
1079                 if (scf_value_get_astring(value, valuestr, vallen) > 0) {
1080                         node = xmlNewChild(root, NULL, (xmlChar *)"legacy",
1081                             NULL);
1082                         if (node != NULL) {
1083                                 (void) xmlSetProp(node, (xmlChar *)"timestamp",
1084                                     (xmlChar *)valuestr);
1085                                 (void) xmlSetProp(node, (xmlChar *)"path",
1086                                     (xmlChar *)SA_LEGACY_DFSTAB);
1087                         }
1088                 }
1089         }
1090 out:
1091         if (valuestr != NULL)
1092                 free(valuestr);
1093         if (value != NULL)
1094                 scf_value_destroy(value);
1095         if (prop != NULL)
1096                 scf_property_destroy(prop);
1097 }
1098 
1099 
1100 /*
1101  * sa_get_config(handle, root, doc, sahandle)
1102  *
1103  * Walk the SMF repository for /network/shares/group and find all the
1104  * instances. These become group names.  Then add the XML structure
1105  * below the groups based on property groups and properties.
1106  */
1107 int
1108 sa_get_config(scfutilhandle_t *handle, xmlNodePtr root, sa_handle_t sahandle)
1109 {
1110         int ret = SA_OK;
1111         scf_instance_t *instance;
1112         scf_iter_t *iter;
1113         char buff[BUFSIZ * 2];
1114 
1115         instance = scf_instance_create(handle->handle);
1116         iter = scf_iter_create(handle->handle);
1117         if (instance != NULL && iter != NULL) {
1118                 if ((ret = scf_iter_service_instances(iter,
1119                     handle->service)) == 0) {
1120                         while ((ret = scf_iter_next_instance(iter,
1121                             instance)) > 0) {
1122                                 if (scf_instance_get_name(instance, buff,
1123                                     sizeof (buff)) > 0) {
1124                                         if (strcmp(buff, "default") == 0)
1125                                                 sa_extract_defaults(root,
1126                                                     handle, instance);
1127                                         ret = sa_extract_group(root, handle,
1128                                             instance, sahandle);
1129                                 }
1130                         }
1131                 }
1132         }
1133 
1134         /* Always cleanup these */
1135         if (instance != NULL)
1136                 scf_instance_destroy(instance);
1137         if (iter != NULL)
1138                 scf_iter_destroy(iter);
1139         return (ret);
1140 }
1141 
1142 /*
1143  * sa_get_instance(handle, instance)
1144  *
1145  * Get the instance of the group service. This is actually the
1146  * specific group name. The instance is needed for all property and
1147  * control operations.
1148  */
1149 
1150 int
1151 sa_get_instance(scfutilhandle_t *handle, char *instname)
1152 {
1153         if (scf_service_get_instance(handle->service, instname,
1154             handle->instance) != 0) {
1155                 return (SA_NO_SUCH_GROUP);
1156         }
1157         return (SA_OK);
1158 }
1159 
1160 /*
1161  * sa_create_instance(handle, instname)
1162  *
1163  * Create a new SMF service instance. There can only be one with a
1164  * given name.
1165  */
1166 
1167 int
1168 sa_create_instance(scfutilhandle_t *handle, char *instname)
1169 {
1170         int ret = SA_OK;
1171         char instance[SA_GROUP_INST_LEN];
1172         if (scf_service_add_instance(handle->service, instname,
1173             handle->instance) != 0) {
1174         /* better error returns need to be added based on real error */
1175                 if (scf_error() == SCF_ERROR_PERMISSION_DENIED)
1176                         ret = SA_NO_PERMISSION;
1177                 else
1178                         ret = SA_DUPLICATE_NAME;
1179         } else {
1180                 /* have the service created, so enable it */
1181                 (void) snprintf(instance, sizeof (instance), "%s:%s",
1182                     SA_SVC_FMRI_BASE, instname);
1183                 (void) smf_enable_instance(instance, 0);
1184         }
1185         return (ret);
1186 }
1187 
1188 /*
1189  * sa_delete_instance(handle, instname)
1190  *
1191  * When a group goes away, we also remove the service instance.
1192  */
1193 
1194 int
1195 sa_delete_instance(scfutilhandle_t *handle, char *instname)
1196 {
1197         int ret;
1198 
1199         if (strcmp(instname, "default") == 0) {
1200                 ret = SA_NO_PERMISSION;
1201         } else {
1202                 if ((ret = sa_get_instance(handle, instname)) == SA_OK) {
1203                         if (scf_instance_delete(handle->instance) != 0)
1204                                 /* need better analysis */
1205                                 ret = SA_NO_PERMISSION;
1206                 }
1207         }
1208         return (ret);
1209 }
1210 
1211 /*
1212  * sa_create_pgroup(handle, pgroup)
1213  *
1214  * create a new property group
1215  */
1216 
1217 int
1218 sa_create_pgroup(scfutilhandle_t *handle, char *pgroup)
1219 {
1220         int ret = SA_OK;
1221         int persist = 0;
1222 
1223         /*
1224          * Only create a handle if it doesn't exist. It is ok to exist
1225          * since the pg handle will be set as a side effect.
1226          */
1227         if (handle->pg == NULL)
1228                 handle->pg = scf_pg_create(handle->handle);
1229 
1230         /*
1231          * Special case for a non-persistent property group. This is
1232          * internal use only.
1233          */
1234         if (*pgroup == '*') {
1235                 persist = SCF_PG_FLAG_NONPERSISTENT;
1236                 pgroup++;
1237         }
1238 
1239         /*
1240          * If the pgroup exists, we are done. If it doesn't, then we
1241          * need to actually add one to the service instance.
1242          */
1243         if (scf_instance_get_pg(handle->instance,
1244             pgroup, handle->pg) != 0) {
1245 
1246                 /* Doesn't exist so create one */
1247                 if (scf_instance_add_pg(handle->instance, pgroup,
1248                     SCF_GROUP_APPLICATION, persist, handle->pg) != 0) {
1249                         switch (scf_error()) {
1250                         case SCF_ERROR_PERMISSION_DENIED:
1251                                 ret = SA_NO_PERMISSION;
1252                                 break;
1253                         default:
1254                                 ret = SA_SYSTEM_ERR;
1255                                 break;
1256                         }
1257                 }
1258         }
1259         return (ret);
1260 }
1261 
1262 /*
1263  * sa_delete_pgroup(handle, pgroup)
1264  *
1265  * Remove the property group from the current instance of the service,
1266  * but only if it actually exists.
1267  */
1268 
1269 int
1270 sa_delete_pgroup(scfutilhandle_t *handle, char *pgroup)
1271 {
1272         int ret = SA_OK;
1273         /*
1274          * Only delete if it does exist.
1275          */
1276         if (scf_instance_get_pg(handle->instance, pgroup, handle->pg) == 0) {
1277                 /* does exist so delete it */
1278                 if (scf_pg_delete(handle->pg) != 0)
1279                         ret = SA_SYSTEM_ERR;
1280         } else {
1281                 ret = SA_SYSTEM_ERR;
1282         }
1283         if (ret == SA_SYSTEM_ERR &&
1284             scf_error() == SCF_ERROR_PERMISSION_DENIED) {
1285                 ret = SA_NO_PERMISSION;
1286         }
1287         return (ret);
1288 }
1289 
1290 /*
1291  * sa_start_transaction(handle, pgroup)
1292  *
1293  * Start an SMF transaction so we can deal with properties. it would
1294  * be nice to not have to expose this, but we have to in order to
1295  * optimize.
1296  *
1297  * Basic model is to hold the transaction in the handle and allow
1298  * property adds/deletes/updates to be added then close the
1299  * transaction (or abort).  There may eventually be a need to handle
1300  * other types of transaction mechanisms but we don't do that now.
1301  *
1302  * An sa_start_transaction must be followed by either an
1303  * sa_end_transaction or sa_abort_transaction before another
1304  * sa_start_transaction can be done.
1305  */
1306 
1307 int
1308 sa_start_transaction(scfutilhandle_t *handle, char *propgroup)
1309 {
1310         int ret = SA_OK;
1311         /*
1312          * Lookup the property group and create it if it doesn't already
1313          * exist.
1314          */
1315         if (handle == NULL)
1316                 return (SA_CONFIG_ERR);
1317 
1318         if (handle->scf_state == SCH_STATE_INIT) {
1319                 ret = sa_create_pgroup(handle, propgroup);
1320                 if (ret == SA_OK) {
1321                         handle->trans = scf_transaction_create(handle->handle);
1322                         if (handle->trans != NULL) {
1323                                 if (scf_transaction_start(handle->trans,
1324                                     handle->pg) != 0) {
1325                                         ret = SA_SYSTEM_ERR;
1326                                 }
1327                                 if (ret != SA_OK) {
1328                                         scf_transaction_destroy(handle->trans);
1329                                         handle->trans = NULL;
1330                                 }
1331                         } else {
1332                                 ret = SA_SYSTEM_ERR;
1333                         }
1334                 }
1335         }
1336         if (ret == SA_SYSTEM_ERR &&
1337             scf_error() == SCF_ERROR_PERMISSION_DENIED) {
1338                 ret = SA_NO_PERMISSION;
1339         }
1340         return (ret);
1341 }
1342 
1343 
1344 /*
1345  * sa_end_transaction(scfhandle, sahandle)
1346  *
1347  * Commit the changes that were added to the transaction in the
1348  * handle. Do all necessary cleanup.
1349  */
1350 
1351 int
1352 sa_end_transaction(scfutilhandle_t *handle, sa_handle_impl_t sahandle)
1353 {
1354         int ret = SA_OK;
1355 
1356         if (handle == NULL || handle->trans == NULL || sahandle == NULL) {
1357                 ret = SA_SYSTEM_ERR;
1358         } else {
1359                 if (scf_transaction_commit(handle->trans) < 0)
1360                         ret = SA_SYSTEM_ERR;
1361                 scf_transaction_destroy_children(handle->trans);
1362                 scf_transaction_destroy(handle->trans);
1363                 if (ret == SA_OK)
1364                         set_transaction_tstamp(sahandle);
1365                 handle->trans = NULL;
1366         }
1367         return (ret);
1368 }
1369 
1370 /*
1371  * sa_abort_transaction(handle)
1372  *
1373  * Abort the changes that were added to the transaction in the
1374  * handle. Do all necessary cleanup.
1375  */
1376 
1377 void
1378 sa_abort_transaction(scfutilhandle_t *handle)
1379 {
1380         if (handle->trans != NULL) {
1381                 scf_transaction_reset_all(handle->trans);
1382                 scf_transaction_destroy_children(handle->trans);
1383                 scf_transaction_destroy(handle->trans);
1384                 handle->trans = NULL;
1385         }
1386 }
1387 
1388 /*
1389  * set_transaction_tstamp(sahandle)
1390  *
1391  * After a successful transaction commit, update the timestamp of the
1392  * last transaction. This lets us detect changes from other processes.
1393  */
1394 static void
1395 set_transaction_tstamp(sa_handle_impl_t sahandle)
1396 {
1397         char tstring[32];
1398         struct timeval tv;
1399         scfutilhandle_t *scfhandle;
1400 
1401         if (sahandle == NULL || sahandle->scfhandle == NULL)
1402                 return;
1403 
1404         scfhandle = sahandle->scfhandle;
1405 
1406         if (sa_get_instance(scfhandle, "default") != SA_OK)
1407                 return;
1408 
1409         if (gettimeofday(&tv, NULL) != 0)
1410                 return;
1411 
1412         if (sa_start_transaction(scfhandle, "*state") != SA_OK)
1413                 return;
1414 
1415         sahandle->tstrans = TSTAMP((*(timestruc_t *)&tv));
1416         (void) snprintf(tstring, sizeof (tstring), "%lld", sahandle->tstrans);
1417         if (sa_set_property(sahandle->scfhandle, "lastupdate", tstring) ==
1418             SA_OK) {
1419                 /*
1420                  * While best if it succeeds, a failure doesn't cause
1421                  * problems and we will ignore it anyway.
1422                  */
1423                 (void) scf_transaction_commit(scfhandle->trans);
1424                 scf_transaction_destroy_children(scfhandle->trans);
1425                 scf_transaction_destroy(scfhandle->trans);
1426         } else {
1427                 sa_abort_transaction(scfhandle);
1428         }
1429 }
1430 
1431 /*
1432  * sa_set_property(handle, prop, value)
1433  *
1434  * Set a property transaction entry into the pending SMF transaction.
1435  */
1436 
1437 int
1438 sa_set_property(scfutilhandle_t *handle, char *propname, char *valstr)
1439 {
1440         int ret = SA_OK;
1441         scf_value_t *value;
1442         scf_transaction_entry_t *entry;
1443         /*
1444          * Properties must be set in transactions and don't take
1445          * effect until the transaction has been ended/committed.
1446          */
1447         value = scf_value_create(handle->handle);
1448         entry = scf_entry_create(handle->handle);
1449         if (value != NULL && entry != NULL) {
1450                 if (scf_transaction_property_change(handle->trans, entry,
1451                     propname, SCF_TYPE_ASTRING) == 0 ||
1452                     scf_transaction_property_new(handle->trans, entry,
1453                     propname, SCF_TYPE_ASTRING) == 0) {
1454                         if (scf_value_set_astring(value, valstr) == 0) {
1455                                 if (scf_entry_add_value(entry, value) != 0) {
1456                                         ret = SA_SYSTEM_ERR;
1457                                         scf_value_destroy(value);
1458                                 }
1459                                 /* The value is in the transaction */
1460                                 value = NULL;
1461                         } else {
1462                                 /* Value couldn't be constructed */
1463                                 ret = SA_SYSTEM_ERR;
1464                         }
1465                         /* The entry is in the transaction */
1466                         entry = NULL;
1467                 } else {
1468                         ret = SA_SYSTEM_ERR;
1469                 }
1470         } else {
1471                 ret = SA_SYSTEM_ERR;
1472         }
1473         if (ret == SA_SYSTEM_ERR) {
1474                 switch (scf_error()) {
1475                 case SCF_ERROR_PERMISSION_DENIED:
1476                         ret = SA_NO_PERMISSION;
1477                         break;
1478                 }
1479         }
1480         /*
1481          * Cleanup if there were any errors that didn't leave these
1482          * values where they would be cleaned up later.
1483          */
1484         if (value != NULL)
1485                 scf_value_destroy(value);
1486         if (entry != NULL)
1487                 scf_entry_destroy(entry);
1488         return (ret);
1489 }
1490 
1491 /*
1492  * check_resource(share)
1493  *
1494  * Check to see if share has any persistent resources. We don't want
1495  * to save if they are all transient.
1496  */
1497 static int
1498 check_resource(sa_share_t share)
1499 {
1500         sa_resource_t resource;
1501         int ret = B_FALSE;
1502 
1503         for (resource = sa_get_share_resource(share, NULL);
1504             resource != NULL && ret == B_FALSE;
1505             resource = sa_get_next_resource(resource)) {
1506                 char *type;
1507                 type = sa_get_resource_attr(resource, "type");
1508                 if (type != NULL) {
1509                         if (strcmp(type, "transient") != 0) {
1510                                 ret = B_TRUE;
1511                         }
1512                         sa_free_attr_string(type);
1513                 }
1514         }
1515         return (ret);
1516 }
1517 
1518 /*
1519  * sa_set_resource_property(handle, prop, value)
1520  *
1521  * set a property transaction entry into the pending SMF
1522  * transaction. We don't want to include any transient resources
1523  */
1524 
1525 static int
1526 sa_set_resource_property(scfutilhandle_t *handle, sa_share_t share)
1527 {
1528         int ret = SA_OK;
1529         scf_value_t *value;
1530         scf_transaction_entry_t *entry;
1531         sa_resource_t resource;
1532         char *valstr;
1533         char *idstr;
1534         char *description;
1535         char *propstr = NULL;
1536         size_t strsize;
1537 
1538         /* don't bother if no persistent resources */
1539         if (check_resource(share) == B_FALSE)
1540                 return (ret);
1541 
1542         /*
1543          * properties must be set in transactions and don't take
1544          * effect until the transaction has been ended/committed.
1545          */
1546         entry = scf_entry_create(handle->handle);
1547         if (entry == NULL)
1548                 return (SA_SYSTEM_ERR);
1549 
1550         if (scf_transaction_property_change(handle->trans, entry,
1551             "resource", SCF_TYPE_ASTRING) != 0 &&
1552             scf_transaction_property_new(handle->trans, entry,
1553             "resource", SCF_TYPE_ASTRING) != 0) {
1554                 scf_entry_destroy(entry);
1555                 return (SA_SYSTEM_ERR);
1556 
1557         }
1558         for (resource = sa_get_share_resource(share, NULL);
1559             resource != NULL;
1560             resource = sa_get_next_resource(resource)) {
1561                 value = scf_value_create(handle->handle);
1562                 if (value == NULL) {
1563                         ret = SA_NO_MEMORY;
1564                         break;
1565                 }
1566                         /* Get size of complete string */
1567                 valstr = sa_get_resource_attr(resource, "name");
1568                 idstr = sa_get_resource_attr(resource, "id");
1569                 description = sa_get_resource_description(resource);
1570                 strsize = (valstr != NULL) ? strlen(valstr) : 0;
1571                 strsize += (idstr != NULL) ? strlen(idstr) : 0;
1572                 strsize += (description != NULL) ? strlen(description) : 0;
1573                 if (strsize > 0) {
1574                         strsize += 3; /* add nul and ':' */
1575                         propstr = (char *)malloc(strsize);
1576                         if (propstr == NULL) {
1577                                 scf_value_destroy(value);
1578                                 ret = SA_NO_MEMORY;
1579                                 goto err;
1580                         }
1581                         if (idstr == NULL)
1582                                 (void) snprintf(propstr, strsize, "%s",
1583                                     valstr ? valstr : "");
1584                         else
1585                                 (void) snprintf(propstr, strsize, "%s:%s:%s",
1586                                     idstr, valstr ? valstr : "",
1587                                     description ? description : "");
1588                         if (scf_value_set_astring(value, propstr) != 0) {
1589                                 ret = SA_SYSTEM_ERR;
1590                                 free(propstr);
1591                                 scf_value_destroy(value);
1592                                 break;
1593                         }
1594                         if (scf_entry_add_value(entry, value) != 0) {
1595                                 ret = SA_SYSTEM_ERR;
1596                                 free(propstr);
1597                                 scf_value_destroy(value);
1598                                 break;
1599                         }
1600                         /* the value is in the transaction */
1601                         value = NULL;
1602                         free(propstr);
1603                 }
1604 err:
1605                 if (valstr != NULL) {
1606                         sa_free_attr_string(valstr);
1607                         valstr = NULL;
1608                 }
1609                 if (idstr != NULL) {
1610                         sa_free_attr_string(idstr);
1611                         idstr = NULL;
1612                 }
1613                 if (description != NULL) {
1614                         sa_free_share_description(description);
1615                         description = NULL;
1616                 }
1617         }
1618         /* the entry is in the transaction */
1619         entry = NULL;
1620 
1621         if (valstr != NULL)
1622                 sa_free_attr_string(valstr);
1623         if (idstr != NULL)
1624                 sa_free_attr_string(idstr);
1625         if (description != NULL)
1626                 sa_free_share_description(description);
1627 
1628         if (ret == SA_SYSTEM_ERR) {
1629                 switch (scf_error()) {
1630                 case SCF_ERROR_PERMISSION_DENIED:
1631                         ret = SA_NO_PERMISSION;
1632                         break;
1633                 }
1634         }
1635         /*
1636          * cleanup if there were any errors that didn't leave
1637          * these values where they would be cleaned up later.
1638          */
1639         if (entry != NULL)
1640                 scf_entry_destroy(entry);
1641 
1642         return (ret);
1643 }
1644 
1645 /*
1646  * sa_commit_share(handle, group, share)
1647  *
1648  *      Commit this share to the repository.
1649  *      properties are added if they exist but can be added later.
1650  *      Need to add to dfstab and sharetab, if appropriate.
1651  */
1652 int
1653 sa_commit_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
1654 {
1655         int ret = SA_OK;
1656         char *groupname;
1657         char *name;
1658         char *description;
1659         char *sharename;
1660         ssize_t proplen;
1661         char *propstring;
1662 
1663         /*
1664          * Don't commit in the zfs group. We do commit legacy
1665          * (default) and all other groups/shares. ZFS is handled
1666          * through the ZFS configuration rather than SMF.
1667          */
1668 
1669         groupname = sa_get_group_attr(group, "name");
1670         if (groupname != NULL) {
1671                 if (strcmp(groupname, "zfs") == 0) {
1672                         /*
1673                          * Adding to the ZFS group will result in the sharenfs
1674                          * property being set but we don't want to do anything
1675                          * SMF related at this point.
1676                          */
1677                         sa_free_attr_string(groupname);
1678                         return (ret);
1679                 }
1680         }
1681 
1682         proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
1683         propstring = malloc(proplen);
1684         if (propstring == NULL)
1685                 ret = SA_NO_MEMORY;
1686 
1687         if (groupname != NULL && ret == SA_OK) {
1688                 ret = sa_get_instance(handle, groupname);
1689                 sa_free_attr_string(groupname);
1690                 groupname = NULL;
1691                 sharename = sa_get_share_attr(share, "id");
1692                 if (sharename == NULL) {
1693                         /* slipped by */
1694                         char shname[SA_SHARE_UUID_BUFLEN];
1695                         generate_unique_sharename(shname);
1696                         (void) xmlSetProp((xmlNodePtr)share, (xmlChar *)"id",
1697                             (xmlChar *)shname);
1698                         sharename = strdup(shname);
1699                 }
1700                 if (sharename != NULL) {
1701                         sigset_t old, new;
1702                         /*
1703                          * Have a share name allocated so create a pgroup for
1704                          * it. It may already exist, but that is OK.  In order
1705                          * to avoid creating a share pgroup that doesn't have
1706                          * a path property, block signals around the critical
1707                          * region of creating the share pgroup and props.
1708                          */
1709                         (void) sigprocmask(SIG_BLOCK, NULL, &new);
1710                         (void) sigaddset(&new, SIGHUP);
1711                         (void) sigaddset(&new, SIGINT);
1712                         (void) sigaddset(&new, SIGQUIT);
1713                         (void) sigaddset(&new, SIGTSTP);
1714                         (void) sigprocmask(SIG_SETMASK, &new, &old);
1715 
1716                         ret = sa_create_pgroup(handle, sharename);
1717                         if (ret == SA_OK) {
1718                                 /*
1719                                  * Now start the transaction for the
1720                                  * properties that define this share. They may
1721                                  * exist so attempt to update before create.
1722                                  */
1723                                 ret = sa_start_transaction(handle, sharename);
1724                         }
1725                         if (ret == SA_OK) {
1726                                 name = sa_get_share_attr(share, "path");
1727                                 if (name != NULL) {
1728                                         /*
1729                                          * There needs to be a path
1730                                          * for a share to exist.
1731                                          */
1732                                         ret = sa_set_property(handle, "path",
1733                                             name);
1734                                         sa_free_attr_string(name);
1735                                 } else {
1736                                         ret = SA_NO_MEMORY;
1737                                 }
1738                         }
1739                         if (ret == SA_OK) {
1740                                 name = sa_get_share_attr(share, "drive-letter");
1741                                 if (name != NULL) {
1742                                         /* A drive letter may exist for SMB */
1743                                         ret = sa_set_property(handle,
1744                                             "drive-letter", name);
1745                                         sa_free_attr_string(name);
1746                                 }
1747                         }
1748                         if (ret == SA_OK) {
1749                                 name = sa_get_share_attr(share, "exclude");
1750                                 if (name != NULL) {
1751                                         /*
1752                                          * In special cases need to
1753                                          * exclude proto enable.
1754                                          */
1755                                         ret = sa_set_property(handle,
1756                                             "exclude", name);
1757                                         sa_free_attr_string(name);
1758                                 }
1759                         }
1760                         if (ret == SA_OK) {
1761                                 /*
1762                                  * If there are resource names, bundle them up
1763                                  * and save appropriately.
1764                                  */
1765                                 ret = sa_set_resource_property(handle, share);
1766                         }
1767 
1768                         if (ret == SA_OK) {
1769                                 description = sa_get_share_description(share);
1770                                 if (description != NULL) {
1771                                         ret = sa_set_property(handle,
1772                                             "description",
1773                                             description);
1774                                         sa_free_share_description(description);
1775                                 }
1776                         }
1777                         /* Make sure we cleanup the transaction */
1778                         if (ret == SA_OK) {
1779                                 sa_handle_impl_t sahandle;
1780                                 sahandle = (sa_handle_impl_t)
1781                                     sa_find_group_handle(group);
1782                                 if (sahandle != NULL)
1783                                         ret = sa_end_transaction(handle,
1784                                             sahandle);
1785                                 else
1786                                         ret = SA_SYSTEM_ERR;
1787                         } else {
1788                                 sa_abort_transaction(handle);
1789                         }
1790 
1791                         (void) sigprocmask(SIG_SETMASK, &old, NULL);
1792 
1793                         free(sharename);
1794                 }
1795         }
1796         if (ret == SA_SYSTEM_ERR) {
1797                 int err = scf_error();
1798                 if (err == SCF_ERROR_PERMISSION_DENIED)
1799                         ret = SA_NO_PERMISSION;
1800         }
1801         if (propstring != NULL)
1802                 free(propstring);
1803         if (groupname != NULL)
1804                 sa_free_attr_string(groupname);
1805 
1806         return (ret);
1807 }
1808 
1809 /*
1810  * remove_resources(handle, share, shareid)
1811  *
1812  * If the share has resources, remove all of them and their
1813  * optionsets.
1814  */
1815 static int
1816 remove_resources(scfutilhandle_t *handle, sa_share_t share, char *shareid)
1817 {
1818         sa_resource_t resource;
1819         sa_optionset_t opt;
1820         char *proto;
1821         char *id;
1822         ssize_t proplen;
1823         char *propstring;
1824         int ret = SA_OK;
1825 
1826         proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
1827         propstring = malloc(proplen);
1828         if (propstring == NULL)
1829                 return (SA_NO_MEMORY);
1830 
1831         for (resource = sa_get_share_resource(share, NULL);
1832             resource != NULL; resource = sa_get_next_resource(resource)) {
1833                 id = sa_get_resource_attr(resource, "id");
1834                 if (id == NULL)
1835                         continue;
1836                 for (opt = sa_get_optionset(resource, NULL);
1837                     opt != NULL; opt = sa_get_next_optionset(resource)) {
1838                         proto = sa_get_optionset_attr(opt, "type");
1839                         if (proto != NULL) {
1840                                 (void) snprintf(propstring, proplen,
1841                                     "%s_%s_%s", shareid, proto, id);
1842                                 ret = sa_delete_pgroup(handle, propstring);
1843                                 sa_free_attr_string(proto);
1844                         }
1845                 }
1846                 sa_free_attr_string(id);
1847         }
1848         free(propstring);
1849         return (ret);
1850 }
1851 
1852 /*
1853  * sa_delete_share(handle, group, share)
1854  *
1855  * Remove the specified share from the group (and service instance).
1856  */
1857 
1858 int
1859 sa_delete_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
1860 {
1861         int ret = SA_OK;
1862         char *groupname = NULL;
1863         char *shareid = NULL;
1864         sa_optionset_t opt;
1865         sa_security_t sec;
1866         ssize_t proplen;
1867         char *propstring;
1868 
1869         proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
1870         propstring = malloc(proplen);
1871         if (propstring == NULL)
1872                 ret = SA_NO_MEMORY;
1873 
1874         if (ret == SA_OK) {
1875                 groupname = sa_get_group_attr(group, "name");
1876                 shareid = sa_get_share_attr(share, "id");
1877                 if (groupname == NULL || shareid == NULL) {
1878                         ret = SA_CONFIG_ERR;
1879                         goto out;
1880                 }
1881                 ret = sa_get_instance(handle, groupname);
1882                 if (ret == SA_OK) {
1883                         /* If a share has resources, remove them */
1884                         ret = remove_resources(handle, share, shareid);
1885                         /* If a share has properties, remove them */
1886                         ret = sa_delete_pgroup(handle, shareid);
1887                         for (opt = sa_get_optionset(share, NULL);
1888                             opt != NULL;
1889                             opt = sa_get_next_optionset(opt)) {
1890                                 char *proto;
1891                                 proto = sa_get_optionset_attr(opt, "type");
1892                                 if (proto != NULL) {
1893                                         (void) snprintf(propstring,
1894                                             proplen, "%s_%s", shareid,
1895                                             proto);
1896                                         ret = sa_delete_pgroup(handle,
1897                                             propstring);
1898                                         sa_free_attr_string(proto);
1899                                 } else {
1900                                         ret = SA_NO_MEMORY;
1901                                 }
1902                         }
1903                         /*
1904                          * If a share has security/negotiable
1905                          * properties, remove them.
1906                          */
1907                         for (sec = sa_get_security(share, NULL, NULL);
1908                             sec != NULL;
1909                             sec = sa_get_next_security(sec)) {
1910                                 char *proto;
1911                                 char *sectype;
1912                                 proto = sa_get_security_attr(sec, "type");
1913                                 sectype = sa_get_security_attr(sec, "sectype");
1914                                 if (proto != NULL && sectype != NULL) {
1915                                         (void) snprintf(propstring, proplen,
1916                                             "%s_%s_%s", shareid,  proto,
1917                                             sectype);
1918                                         ret = sa_delete_pgroup(handle,
1919                                             propstring);
1920                                 } else {
1921                                         ret = SA_NO_MEMORY;
1922                                 }
1923                                 if (proto != NULL)
1924                                         sa_free_attr_string(proto);
1925                                 if (sectype != NULL)
1926                                         sa_free_attr_string(sectype);
1927                         }
1928                 }
1929         }
1930 out:
1931         if (groupname != NULL)
1932                 sa_free_attr_string(groupname);
1933         if (shareid != NULL)
1934                 sa_free_attr_string(shareid);
1935         if (propstring != NULL)
1936                 free(propstring);
1937 
1938         return (ret);
1939 }