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