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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2013, Joyent, Inc. All rights reserved.
  25  */
  26 
  27 /*
  28  * This is the main implementation file for the low-level repository
  29  * interface.
  30  */
  31 
  32 #include "lowlevel_impl.h"
  33 
  34 #include "repcache_protocol.h"
  35 #include "scf_type.h"
  36 
  37 #include <assert.h>
  38 #include <alloca.h>
  39 #include <door.h>
  40 #include <errno.h>
  41 #include <fcntl.h>
  42 #include <fnmatch.h>
  43 #include <libuutil.h>
  44 #include <poll.h>
  45 #include <pthread.h>
  46 #include <synch.h>
  47 #include <stddef.h>
  48 #include <stdio.h>
  49 #include <stdlib.h>
  50 #include <string.h>
  51 #include <sys/mman.h>
  52 #include <sys/sysmacros.h>
  53 #include <libzonecfg.h>
  54 #include <unistd.h>
  55 #include <dlfcn.h>
  56 
  57 #define ENV_SCF_DEBUG           "LIBSCF_DEBUG"
  58 #define ENV_SCF_DOORPATH        "LIBSCF_DOORPATH"
  59 
  60 static uint32_t default_debug = 0;
  61 static const char *default_door_path = REPOSITORY_DOOR_NAME;
  62 
  63 #define CALL_FAILED             -1
  64 #define RESULT_TOO_BIG          -2
  65 #define NOT_BOUND               -3
  66 
  67 static pthread_mutex_t  lowlevel_init_lock;
  68 static int32_t          lowlevel_inited;
  69 
  70 static uu_list_pool_t   *tran_entry_pool;
  71 static uu_list_pool_t   *datael_pool;
  72 static uu_list_pool_t   *iter_pool;
  73 
  74 /*
  75  * base32[] index32[] are used in base32 encoding and decoding.
  76  */
  77 static char base32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
  78 static char index32[128] = {
  79         -1, -1, -1, -1, -1, -1, -1, -1, /* 0-7 */
  80         -1, -1, -1, -1, -1, -1, -1, -1, /* 8-15 */
  81         -1, -1, -1, -1, -1, -1, -1, -1, /* 16-23 */
  82         -1, -1, -1, -1, -1, -1, -1, -1, /* 24-31 */
  83         -1, -1, -1, -1, -1, -1, -1, -1, /* 32-39 */
  84         -1, -1, -1, -1, -1, -1, -1, -1, /* 40-47 */
  85         -1, -1, 26, 27, 28, 29, 30, 31, /* 48-55 */
  86         -1, -1, -1, -1, -1, -1, -1, -1, /* 56-63 */
  87         -1, 0, 1, 2, 3, 4, 5, 6,        /* 64-71 */
  88         7, 8, 9, 10, 11, 12, 13, 14,    /* 72-79 */
  89         15, 16, 17, 18, 19, 20, 21, 22, /* 80-87 */
  90         23, 24, 25, -1, -1, -1, -1, -1, /* 88-95 */
  91         -1, -1, -1, -1, -1, -1, -1, -1, /* 96-103 */
  92         -1, -1, -1, -1, -1, -1, -1, -1, /* 104-111 */
  93         -1, -1, -1, -1, -1, -1, -1, -1, /* 112-119 */
  94         -1, -1, -1, -1, -1, -1, -1, -1  /* 120-127 */
  95 };
  96 
  97 #define DECODE32_GS     (8)     /* scf_decode32 group size */
  98 
  99 #ifdef lint
 100 #define assert_nolint(x) (void)0
 101 #else
 102 #define assert_nolint(x) assert(x)
 103 #endif
 104 
 105 static void scf_iter_reset_locked(scf_iter_t *iter);
 106 static void scf_value_reset_locked(scf_value_t *val, int and_destroy);
 107 
 108 #define TYPE_VALUE      (-100)
 109 
 110 /*
 111  * Hold and release subhandles.  We only allow one thread access to the
 112  * subhandles at a time, and he can use any subset, grabbing and releasing
 113  * them in any order.  The only restrictions are that you cannot hold an
 114  * already-held subhandle, and all subhandles must be released before
 115  * returning to the original caller.
 116  */
 117 static void
 118 handle_hold_subhandles(scf_handle_t *h, int mask)
 119 {
 120         assert(mask != 0 && (mask & ~RH_HOLD_ALL) == 0);
 121 
 122         (void) pthread_mutex_lock(&h->rh_lock);
 123         while (h->rh_hold_flags != 0 && h->rh_holder != pthread_self()) {
 124                 int cancel_state;
 125 
 126                 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
 127                     &cancel_state);
 128                 (void) pthread_cond_wait(&h->rh_cv, &h->rh_lock);
 129                 (void) pthread_setcancelstate(cancel_state, NULL);
 130         }
 131         if (h->rh_hold_flags == 0)
 132                 h->rh_holder = pthread_self();
 133         assert(!(h->rh_hold_flags & mask));
 134         h->rh_hold_flags |= mask;
 135         (void) pthread_mutex_unlock(&h->rh_lock);
 136 }
 137 
 138 static void
 139 handle_rele_subhandles(scf_handle_t *h, int mask)
 140 {
 141         assert(mask != 0 && (mask & ~RH_HOLD_ALL) == 0);
 142 
 143         (void) pthread_mutex_lock(&h->rh_lock);
 144         assert(h->rh_holder == pthread_self());
 145         assert((h->rh_hold_flags & mask));
 146 
 147         h->rh_hold_flags &= ~mask;
 148         if (h->rh_hold_flags == 0)
 149                 (void) pthread_cond_signal(&h->rh_cv);
 150         (void) pthread_mutex_unlock(&h->rh_lock);
 151 }
 152 
 153 #define HOLD_HANDLE(h, flag, field) \
 154         (handle_hold_subhandles((h), (flag)), (h)->field)
 155 
 156 #define RELE_HANDLE(h, flag) \
 157         (handle_rele_subhandles((h), (flag)))
 158 
 159 /*
 160  * convenience macros, for functions that only need a one or two handles at
 161  * any given time
 162  */
 163 #define HANDLE_HOLD_ITER(h)     HOLD_HANDLE((h), RH_HOLD_ITER, rh_iter)
 164 #define HANDLE_HOLD_SCOPE(h)    HOLD_HANDLE((h), RH_HOLD_SCOPE, rh_scope)
 165 #define HANDLE_HOLD_SERVICE(h)  HOLD_HANDLE((h), RH_HOLD_SERVICE, rh_service)
 166 #define HANDLE_HOLD_INSTANCE(h) HOLD_HANDLE((h), RH_HOLD_INSTANCE, rh_instance)
 167 #define HANDLE_HOLD_SNAPSHOT(h) HOLD_HANDLE((h), RH_HOLD_SNAPSHOT, rh_snapshot)
 168 #define HANDLE_HOLD_SNAPLVL(h)  HOLD_HANDLE((h), RH_HOLD_SNAPLVL, rh_snaplvl)
 169 #define HANDLE_HOLD_PG(h)       HOLD_HANDLE((h), RH_HOLD_PG, rh_pg)
 170 #define HANDLE_HOLD_PROPERTY(h) HOLD_HANDLE((h), RH_HOLD_PROPERTY, rh_property)
 171 #define HANDLE_HOLD_VALUE(h)    HOLD_HANDLE((h), RH_HOLD_VALUE, rh_value)
 172 
 173 #define HANDLE_RELE_ITER(h)     RELE_HANDLE((h), RH_HOLD_ITER)
 174 #define HANDLE_RELE_SCOPE(h)    RELE_HANDLE((h), RH_HOLD_SCOPE)
 175 #define HANDLE_RELE_SERVICE(h)  RELE_HANDLE((h), RH_HOLD_SERVICE)
 176 #define HANDLE_RELE_INSTANCE(h) RELE_HANDLE((h), RH_HOLD_INSTANCE)
 177 #define HANDLE_RELE_SNAPSHOT(h) RELE_HANDLE((h), RH_HOLD_SNAPSHOT)
 178 #define HANDLE_RELE_SNAPLVL(h)  RELE_HANDLE((h), RH_HOLD_SNAPLVL)
 179 #define HANDLE_RELE_PG(h)       RELE_HANDLE((h), RH_HOLD_PG)
 180 #define HANDLE_RELE_PROPERTY(h) RELE_HANDLE((h), RH_HOLD_PROPERTY)
 181 #define HANDLE_RELE_VALUE(h)    RELE_HANDLE((h), RH_HOLD_VALUE)
 182 
 183 /*ARGSUSED*/
 184 static int
 185 transaction_entry_compare(const void *l_arg, const void *r_arg, void *private)
 186 {
 187         const char *l_prop =
 188             ((scf_transaction_entry_t *)l_arg)->entry_property;
 189         const char *r_prop =
 190             ((scf_transaction_entry_t *)r_arg)->entry_property;
 191 
 192         int ret;
 193 
 194         ret = strcmp(l_prop, r_prop);
 195         if (ret > 0)
 196                 return (1);
 197         if (ret < 0)
 198                 return (-1);
 199         return (0);
 200 }
 201 
 202 static int
 203 datael_compare(const void *l_arg, const void *r_arg, void *private)
 204 {
 205         uint32_t l_id = ((scf_datael_t *)l_arg)->rd_entity;
 206         uint32_t r_id = (r_arg != NULL) ? ((scf_datael_t *)r_arg)->rd_entity :
 207             *(uint32_t *)private;
 208 
 209         if (l_id > r_id)
 210                 return (1);
 211         if (l_id < r_id)
 212                 return (-1);
 213         return (0);
 214 }
 215 
 216 static int
 217 iter_compare(const void *l_arg, const void *r_arg, void *private)
 218 {
 219         uint32_t l_id = ((scf_iter_t *)l_arg)->iter_id;
 220         uint32_t r_id = (r_arg != NULL) ? ((scf_iter_t *)r_arg)->iter_id :
 221             *(uint32_t *)private;
 222 
 223         if (l_id > r_id)
 224                 return (1);
 225         if (l_id < r_id)
 226                 return (-1);
 227         return (0);
 228 }
 229 
 230 static int
 231 lowlevel_init(void)
 232 {
 233         const char *debug;
 234         const char *door_path;
 235 
 236         (void) pthread_mutex_lock(&lowlevel_init_lock);
 237         if (lowlevel_inited == 0) {
 238                 if (!issetugid() &&
 239                     (debug = getenv(ENV_SCF_DEBUG)) != NULL && debug[0] != 0 &&
 240                     uu_strtoint(debug, &default_debug, sizeof (default_debug),
 241                     0, 0, 0) == -1) {
 242                         (void) fprintf(stderr, "LIBSCF: $%s (%s): %s",
 243                             ENV_SCF_DEBUG, debug,
 244                             uu_strerror(uu_error()));
 245                 }
 246 
 247                 if (!issetugid() &&
 248                     (door_path = getenv(ENV_SCF_DOORPATH)) != NULL &&
 249                     door_path[0] != 0) {
 250                         default_door_path = strdup(door_path);
 251                         if (default_door_path == NULL)
 252                                 default_door_path = door_path;
 253                 }
 254 
 255                 datael_pool = uu_list_pool_create("SUNW,libscf_datael",
 256                     sizeof (scf_datael_t), offsetof(scf_datael_t, rd_node),
 257                     datael_compare, UU_LIST_POOL_DEBUG);
 258 
 259                 iter_pool = uu_list_pool_create("SUNW,libscf_iter",
 260                     sizeof (scf_iter_t), offsetof(scf_iter_t, iter_node),
 261                     iter_compare, UU_LIST_POOL_DEBUG);
 262 
 263                 assert_nolint(offsetof(scf_transaction_entry_t,
 264                     entry_property) == 0);
 265                 tran_entry_pool = uu_list_pool_create(
 266                     "SUNW,libscf_transaction_entity",
 267                     sizeof (scf_transaction_entry_t),
 268                     offsetof(scf_transaction_entry_t, entry_link),
 269                     transaction_entry_compare, UU_LIST_POOL_DEBUG);
 270 
 271                 if (datael_pool == NULL || iter_pool == NULL ||
 272                     tran_entry_pool == NULL) {
 273                         lowlevel_inited = -1;
 274                         goto end;
 275                 }
 276 
 277                 if (!scf_setup_error()) {
 278                         lowlevel_inited = -1;
 279                         goto end;
 280                 }
 281                 lowlevel_inited = 1;
 282         }
 283 end:
 284         (void) pthread_mutex_unlock(&lowlevel_init_lock);
 285         if (lowlevel_inited > 0)
 286                 return (1);
 287         return (0);
 288 }
 289 
 290 static const struct {
 291         scf_type_t ti_type;
 292         rep_protocol_value_type_t ti_proto_type;
 293         const char *ti_name;
 294 } scf_type_info[] = {
 295         {SCF_TYPE_BOOLEAN,      REP_PROTOCOL_TYPE_BOOLEAN,      "boolean"},
 296         {SCF_TYPE_COUNT,        REP_PROTOCOL_TYPE_COUNT,        "count"},
 297         {SCF_TYPE_INTEGER,      REP_PROTOCOL_TYPE_INTEGER,      "integer"},
 298         {SCF_TYPE_TIME,         REP_PROTOCOL_TYPE_TIME,         "time"},
 299         {SCF_TYPE_ASTRING,      REP_PROTOCOL_TYPE_STRING,       "astring"},
 300         {SCF_TYPE_OPAQUE,       REP_PROTOCOL_TYPE_OPAQUE,       "opaque"},
 301         {SCF_TYPE_USTRING,      REP_PROTOCOL_SUBTYPE_USTRING,   "ustring"},
 302         {SCF_TYPE_URI,          REP_PROTOCOL_SUBTYPE_URI,       "uri"},
 303         {SCF_TYPE_FMRI,         REP_PROTOCOL_SUBTYPE_FMRI,      "fmri"},
 304         {SCF_TYPE_HOST,         REP_PROTOCOL_SUBTYPE_HOST,      "host"},
 305         {SCF_TYPE_HOSTNAME,     REP_PROTOCOL_SUBTYPE_HOSTNAME,  "hostname"},
 306         {SCF_TYPE_NET_ADDR,     REP_PROTOCOL_SUBTYPE_NETADDR,   "net_address"},
 307         {SCF_TYPE_NET_ADDR_V4,  REP_PROTOCOL_SUBTYPE_NETADDR_V4,
 308             "net_address_v4"},
 309         {SCF_TYPE_NET_ADDR_V6,  REP_PROTOCOL_SUBTYPE_NETADDR_V6,
 310             "net_address_v6"}
 311 };
 312 
 313 #define SCF_TYPE_INFO_COUNT (sizeof (scf_type_info) / sizeof (*scf_type_info))
 314 static rep_protocol_value_type_t
 315 scf_type_to_protocol_type(scf_type_t t)
 316 {
 317         int i;
 318 
 319         for (i = 0; i < SCF_TYPE_INFO_COUNT; i++)
 320                 if (scf_type_info[i].ti_type == t)
 321                         return (scf_type_info[i].ti_proto_type);
 322 
 323         return (REP_PROTOCOL_TYPE_INVALID);
 324 }
 325 
 326 static scf_type_t
 327 scf_protocol_type_to_type(rep_protocol_value_type_t t)
 328 {
 329         int i;
 330 
 331         for (i = 0; i < SCF_TYPE_INFO_COUNT; i++)
 332                 if (scf_type_info[i].ti_proto_type == t)
 333                         return (scf_type_info[i].ti_type);
 334 
 335         return (SCF_TYPE_INVALID);
 336 }
 337 
 338 const char *
 339 scf_type_to_string(scf_type_t ty)
 340 {
 341         int i;
 342 
 343         for (i = 0; i < SCF_TYPE_INFO_COUNT; i++)
 344                 if (scf_type_info[i].ti_type == ty)
 345                         return (scf_type_info[i].ti_name);
 346 
 347         return ("unknown");
 348 }
 349 
 350 scf_type_t
 351 scf_string_to_type(const char *name)
 352 {
 353         int i;
 354 
 355         for (i = 0; i < sizeof (scf_type_info) / sizeof (*scf_type_info); i++)
 356                 if (strcmp(scf_type_info[i].ti_name, name) == 0)
 357                         return (scf_type_info[i].ti_type);
 358 
 359         return (SCF_TYPE_INVALID);
 360 }
 361 
 362 int
 363 scf_type_base_type(scf_type_t type, scf_type_t *out)
 364 {
 365         rep_protocol_value_type_t t = scf_type_to_protocol_type(type);
 366         if (t == REP_PROTOCOL_TYPE_INVALID)
 367                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
 368 
 369         *out = scf_protocol_type_to_type(scf_proto_underlying_type(t));
 370         return (SCF_SUCCESS);
 371 }
 372 
 373 /*
 374  * Convert a protocol error code into an SCF_ERROR_* code.
 375  */
 376 static scf_error_t
 377 proto_error(rep_protocol_responseid_t e)
 378 {
 379         switch (e) {
 380         case REP_PROTOCOL_FAIL_MISORDERED:
 381         case REP_PROTOCOL_FAIL_UNKNOWN_ID:
 382         case REP_PROTOCOL_FAIL_INVALID_TYPE:
 383         case REP_PROTOCOL_FAIL_TRUNCATED:
 384         case REP_PROTOCOL_FAIL_TYPE_MISMATCH:
 385         case REP_PROTOCOL_FAIL_NOT_APPLICABLE:
 386         case REP_PROTOCOL_FAIL_UNKNOWN:
 387                 return (SCF_ERROR_INTERNAL);
 388 
 389         case REP_PROTOCOL_FAIL_BAD_TX:
 390                 return (SCF_ERROR_INVALID_ARGUMENT);
 391         case REP_PROTOCOL_FAIL_BAD_REQUEST:
 392                 return (SCF_ERROR_INVALID_ARGUMENT);
 393         case REP_PROTOCOL_FAIL_NO_RESOURCES:
 394                 return (SCF_ERROR_NO_RESOURCES);
 395         case REP_PROTOCOL_FAIL_NOT_FOUND:
 396                 return (SCF_ERROR_NOT_FOUND);
 397         case REP_PROTOCOL_FAIL_DELETED:
 398                 return (SCF_ERROR_DELETED);
 399         case REP_PROTOCOL_FAIL_NOT_SET:
 400                 return (SCF_ERROR_NOT_SET);
 401         case REP_PROTOCOL_FAIL_EXISTS:
 402                 return (SCF_ERROR_EXISTS);
 403         case REP_PROTOCOL_FAIL_DUPLICATE_ID:
 404                 return (SCF_ERROR_EXISTS);
 405         case REP_PROTOCOL_FAIL_PERMISSION_DENIED:
 406                 return (SCF_ERROR_PERMISSION_DENIED);
 407         case REP_PROTOCOL_FAIL_BACKEND_ACCESS:
 408                 return (SCF_ERROR_BACKEND_ACCESS);
 409         case REP_PROTOCOL_FAIL_BACKEND_READONLY:
 410                 return (SCF_ERROR_BACKEND_READONLY);
 411 
 412         case REP_PROTOCOL_SUCCESS:
 413         case REP_PROTOCOL_DONE:
 414         case REP_PROTOCOL_FAIL_NOT_LATEST:      /* TX code should handle this */
 415         default:
 416 #ifndef NDEBUG
 417                 uu_warn("%s:%d: Bad error code %d passed to proto_error().\n",
 418                     __FILE__, __LINE__, e);
 419 #endif
 420                 abort();
 421                 /*NOTREACHED*/
 422         }
 423 }
 424 
 425 ssize_t
 426 scf_limit(uint32_t limit)
 427 {
 428         switch (limit) {
 429         case SCF_LIMIT_MAX_NAME_LENGTH:
 430         case SCF_LIMIT_MAX_PG_TYPE_LENGTH:
 431                 return (REP_PROTOCOL_NAME_LEN - 1);
 432         case SCF_LIMIT_MAX_VALUE_LENGTH:
 433                 return (REP_PROTOCOL_VALUE_LEN - 1);
 434         case SCF_LIMIT_MAX_FMRI_LENGTH:
 435                 return (SCF_FMRI_PREFIX_MAX_LEN +
 436                     sizeof (SCF_FMRI_SCOPE_PREFIX) - 1 +
 437                     sizeof (SCF_FMRI_SCOPE_SUFFIX) - 1 +
 438                     sizeof (SCF_FMRI_SERVICE_PREFIX) - 1 +
 439                     sizeof (SCF_FMRI_INSTANCE_PREFIX) - 1 +
 440                     sizeof (SCF_FMRI_PROPERTYGRP_PREFIX) - 1 +
 441                     sizeof (SCF_FMRI_PROPERTY_PREFIX) - 1 +
 442                     5 * (REP_PROTOCOL_NAME_LEN - 1));
 443         default:
 444                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
 445         }
 446 }
 447 
 448 static size_t
 449 scf_opaque_decode(char *out_arg, const char *in, size_t max_out)
 450 {
 451         char a, b;
 452         char *out = out_arg;
 453 
 454         while (max_out > 0 && (a = in[0]) != 0 && (b = in[1]) != 0) {
 455                 in += 2;
 456 
 457                 if (a >= '0' && a <= '9')
 458                         a -= '0';
 459                 else if (a >= 'a' && a <= 'f')
 460                         a = a - 'a' + 10;
 461                 else if (a >= 'A' && a <= 'F')
 462                         a = a - 'A' + 10;
 463                 else
 464                         break;
 465 
 466                 if (b >= '0' && b <= '9')
 467                         b -= '0';
 468                 else if (b >= 'a' && b <= 'f')
 469                         b = b - 'a' + 10;
 470                 else if (b >= 'A' && b <= 'F')
 471                         b = b - 'A' + 10;
 472                 else
 473                         break;
 474 
 475                 *out++ = (a << 4) | b;
 476                 max_out--;
 477         }
 478 
 479         return (out - out_arg);
 480 }
 481 
 482 static size_t
 483 scf_opaque_encode(char *out_arg, const char *in_arg, size_t in_sz)
 484 {
 485         uint8_t *in = (uint8_t *)in_arg;
 486         uint8_t *end = in + in_sz;
 487         char *out = out_arg;
 488 
 489         if (out == NULL)
 490                 return (2 * in_sz);
 491 
 492         while (in < end) {
 493                 uint8_t c = *in++;
 494 
 495                 uint8_t a = (c & 0xf0) >> 4;
 496                 uint8_t b = (c & 0x0f);
 497 
 498                 if (a <= 9)
 499                         *out++ = a + '0';
 500                 else
 501                         *out++ = a + 'a' - 10;
 502 
 503                 if (b <= 9)
 504                         *out++ = b + '0';
 505                 else
 506                         *out++ = b + 'a' - 10;
 507         }
 508 
 509         *out = 0;
 510 
 511         return (out - out_arg);
 512 }
 513 
 514 static void
 515 handle_do_close(scf_handle_t *h)
 516 {
 517         assert(MUTEX_HELD(&h->rh_lock));
 518         assert(h->rh_doorfd != -1);
 519 
 520         /*
 521          * if there are any active FD users, we just move the FD over
 522          * to rh_doorfd_old -- they'll close it when they finish.
 523          */
 524         if (h->rh_fd_users > 0) {
 525                 h->rh_doorfd_old = h->rh_doorfd;
 526                 h->rh_doorfd = -1;
 527         } else {
 528                 assert(h->rh_doorfd_old == -1);
 529                 (void) close(h->rh_doorfd);
 530                 h->rh_doorfd = -1;
 531         }
 532 }
 533 
 534 /*
 535  * Check if a handle is currently bound.  fork()ing implicitly unbinds
 536  * the handle in the child.
 537  */
 538 static int
 539 handle_is_bound(scf_handle_t *h)
 540 {
 541         assert(MUTEX_HELD(&h->rh_lock));
 542 
 543         if (h->rh_doorfd == -1)
 544                 return (0);
 545 
 546         if (getpid() == h->rh_doorpid)
 547                 return (1);
 548 
 549         /* forked since our last bind -- initiate handle close */
 550         handle_do_close(h);
 551         return (0);
 552 }
 553 
 554 static int
 555 handle_has_server_locked(scf_handle_t *h)
 556 {
 557         door_info_t i;
 558         assert(MUTEX_HELD(&h->rh_lock));
 559 
 560         return (handle_is_bound(h) && door_info(h->rh_doorfd, &i) != -1 &&
 561             i.di_target != -1);
 562 }
 563 
 564 static int
 565 handle_has_server(scf_handle_t *h)
 566 {
 567         int ret;
 568 
 569         (void) pthread_mutex_lock(&h->rh_lock);
 570         ret = handle_has_server_locked(h);
 571         (void) pthread_mutex_unlock(&h->rh_lock);
 572 
 573         return (ret);
 574 }
 575 
 576 /*
 577  * This makes a door request on the client door associated with handle h.
 578  * It will automatically retry calls which fail on EINTR.  If h is not bound,
 579  * returns NOT_BOUND.  If the door call fails or the server response is too
 580  * small, returns CALL_FAILED.  If the server response is too big, truncates the
 581  * response and returns RESULT_TOO_BIG.  Otherwise, the size of the result is
 582  * returned.
 583  */
 584 static ssize_t
 585 make_door_call(scf_handle_t *h, const void *req, size_t req_sz,
 586     void *res, size_t res_sz)
 587 {
 588         door_arg_t arg;
 589         int r;
 590 
 591         assert(MUTEX_HELD(&h->rh_lock));
 592 
 593         if (!handle_is_bound(h)) {
 594                 return (NOT_BOUND);
 595         }
 596 
 597         arg.data_ptr = (void *)req;
 598         arg.data_size = req_sz;
 599         arg.desc_ptr = NULL;
 600         arg.desc_num = 0;
 601         arg.rbuf = res;
 602         arg.rsize = res_sz;
 603 
 604         while ((r = door_call(h->rh_doorfd, &arg)) < 0) {
 605                 if (errno != EINTR)
 606                         break;
 607         }
 608 
 609         if (r < 0) {
 610                 return (CALL_FAILED);
 611         }
 612 
 613         if (arg.desc_num > 0) {
 614                 while (arg.desc_num > 0) {
 615                         if (arg.desc_ptr->d_attributes & DOOR_DESCRIPTOR) {
 616                                 int cfd = arg.desc_ptr->d_data.d_desc.d_id;
 617                                 (void) close(cfd);
 618                         }
 619                         arg.desc_ptr++;
 620                         arg.desc_num--;
 621                 }
 622         }
 623         if (arg.data_ptr != res && arg.data_size > 0)
 624                 (void) memmove(res, arg.data_ptr, MIN(arg.data_size, res_sz));
 625 
 626         if (arg.rbuf != res)
 627                 (void) munmap(arg.rbuf, arg.rsize);
 628 
 629         if (arg.data_size > res_sz)
 630                 return (RESULT_TOO_BIG);
 631 
 632         if (arg.data_size < sizeof (uint32_t))
 633                 return (CALL_FAILED);
 634 
 635         return (arg.data_size);
 636 }
 637 
 638 /*
 639  * Should only be used when r < 0.
 640  */
 641 #define DOOR_ERRORS_BLOCK(r)    {                                       \
 642         switch (r) {                                                    \
 643         case NOT_BOUND:                                                 \
 644                 return (scf_set_error(SCF_ERROR_NOT_BOUND));            \
 645                                                                         \
 646         case CALL_FAILED:                                               \
 647                 return (scf_set_error(SCF_ERROR_CONNECTION_BROKEN));    \
 648                                                                         \
 649         case RESULT_TOO_BIG:                                            \
 650                 return (scf_set_error(SCF_ERROR_INTERNAL));             \
 651                                                                         \
 652         default:                                                        \
 653                 assert(r == NOT_BOUND || r == CALL_FAILED ||            \
 654                     r == RESULT_TOO_BIG);                               \
 655                 abort();                                                \
 656         }                                                               \
 657 }
 658 
 659 /*
 660  * Like make_door_call(), but takes an fd instead of a handle, and expects
 661  * a single file descriptor, returned via res_fd.
 662  *
 663  * If no file descriptor is returned, *res_fd == -1.
 664  */
 665 static int
 666 make_door_call_retfd(int fd, const void *req, size_t req_sz, void *res,
 667     size_t res_sz, int *res_fd)
 668 {
 669         door_arg_t arg;
 670         int r;
 671         char rbuf[256];
 672 
 673         *res_fd = -1;
 674 
 675         if (fd == -1)
 676                 return (NOT_BOUND);
 677 
 678         arg.data_ptr = (void *)req;
 679         arg.data_size = req_sz;
 680         arg.desc_ptr = NULL;
 681         arg.desc_num = 0;
 682         arg.rbuf = rbuf;
 683         arg.rsize = sizeof (rbuf);
 684 
 685         while ((r = door_call(fd, &arg)) < 0) {
 686                 if (errno != EINTR)
 687                         break;
 688         }
 689 
 690         if (r < 0)
 691                 return (CALL_FAILED);
 692 
 693         if (arg.desc_num > 1) {
 694                 while (arg.desc_num > 0) {
 695                         if (arg.desc_ptr->d_attributes & DOOR_DESCRIPTOR) {
 696                                 int cfd =
 697                                     arg.desc_ptr->d_data.d_desc.d_descriptor;
 698                                 (void) close(cfd);
 699                         }
 700                         arg.desc_ptr++;
 701                         arg.desc_num--;
 702                 }
 703         }
 704         if (arg.desc_num == 1 && arg.desc_ptr->d_attributes & DOOR_DESCRIPTOR)
 705                 *res_fd = arg.desc_ptr->d_data.d_desc.d_descriptor;
 706 
 707         if (arg.data_size > 0)
 708                 (void) memmove(res, arg.data_ptr, MIN(arg.data_size, res_sz));
 709 
 710         if (arg.rbuf != rbuf)
 711                 (void) munmap(arg.rbuf, arg.rsize);
 712 
 713         if (arg.data_size > res_sz)
 714                 return (RESULT_TOO_BIG);
 715 
 716         if (arg.data_size < sizeof (uint32_t))
 717                 return (CALL_FAILED);
 718 
 719         return (arg.data_size);
 720 }
 721 
 722 /*
 723  * Fails with
 724  *   _VERSION_MISMATCH
 725  *   _NO_MEMORY
 726  */
 727 scf_handle_t *
 728 scf_handle_create(scf_version_t v)
 729 {
 730         scf_handle_t *ret;
 731         int failed;
 732 
 733         /*
 734          * This will need to be revisited when we bump SCF_VERSION
 735          */
 736         if (v != SCF_VERSION) {
 737                 (void) scf_set_error(SCF_ERROR_VERSION_MISMATCH);
 738                 return (NULL);
 739         }
 740 
 741         if (!lowlevel_init()) {
 742                 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
 743                 return (NULL);
 744         }
 745 
 746         ret = uu_zalloc(sizeof (*ret));
 747         if (ret == NULL) {
 748                 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
 749                 return (NULL);
 750         }
 751 
 752         ret->rh_dataels = uu_list_create(datael_pool, ret, 0);
 753         ret->rh_iters = uu_list_create(iter_pool, ret, 0);
 754         if (ret->rh_dataels == NULL || ret->rh_iters == NULL) {
 755                 if (ret->rh_dataels != NULL)
 756                         uu_list_destroy(ret->rh_dataels);
 757                 if (ret->rh_iters != NULL)
 758                         uu_list_destroy(ret->rh_iters);
 759                 uu_free(ret);
 760                 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
 761                 return (NULL);
 762         }
 763 
 764         ret->rh_doorfd = -1;
 765         ret->rh_doorfd_old = -1;
 766         (void) pthread_mutex_init(&ret->rh_lock, NULL);
 767 
 768         handle_hold_subhandles(ret, RH_HOLD_ALL);
 769 
 770         failed = ((ret->rh_iter = scf_iter_create(ret)) == NULL ||
 771             (ret->rh_scope = scf_scope_create(ret)) == NULL ||
 772             (ret->rh_service = scf_service_create(ret)) == NULL ||
 773             (ret->rh_instance = scf_instance_create(ret)) == NULL ||
 774             (ret->rh_snapshot = scf_snapshot_create(ret)) == NULL ||
 775             (ret->rh_snaplvl = scf_snaplevel_create(ret)) == NULL ||
 776             (ret->rh_pg = scf_pg_create(ret)) == NULL ||
 777             (ret->rh_property = scf_property_create(ret)) == NULL ||
 778             (ret->rh_value = scf_value_create(ret)) == NULL);
 779 
 780         /*
 781          * these subhandles count as internal references, not external ones.
 782          */
 783         ret->rh_intrefs = ret->rh_extrefs;
 784         ret->rh_extrefs = 0;
 785         handle_rele_subhandles(ret, RH_HOLD_ALL);
 786 
 787         if (failed) {
 788                 scf_handle_destroy(ret);
 789                 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
 790                 return (NULL);
 791         }
 792 
 793         scf_value_set_count(ret->rh_value, default_debug);
 794         (void) scf_handle_decorate(ret, "debug", ret->rh_value);
 795 
 796         return (ret);
 797 }
 798 
 799 /*
 800  * Fails with
 801  *   _NO_MEMORY
 802  *   _NO_SERVER - server door could not be open()ed
 803  *                door call failed
 804  *                door_info() failed
 805  *   _VERSION_MISMATCH - server returned bad file descriptor
 806  *                       server claimed bad request
 807  *                       server reported version mismatch
 808  *                       server refused with unknown reason
 809  *   _INVALID_ARGUMENT
 810  *   _NO_RESOURCES - server is out of memory
 811  *   _PERMISSION_DENIED
 812  *   _INTERNAL - could not set up entities or iters
 813  *               server response too big
 814  */
 815 scf_handle_t *
 816 _scf_handle_create_and_bind(scf_version_t ver)
 817 {
 818         scf_handle_t *h;
 819 
 820         h = scf_handle_create(ver);
 821         if (h == NULL)
 822                 return (NULL);
 823 
 824         if (scf_handle_bind(h) == -1) {
 825                 scf_handle_destroy(h);
 826                 return (NULL);
 827         }
 828         return (h);
 829 }
 830 
 831 int
 832 scf_handle_decorate(scf_handle_t *handle, const char *name, scf_value_t *v)
 833 {
 834         if (v != SCF_DECORATE_CLEAR && handle != v->value_handle)
 835                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
 836 
 837         (void) pthread_mutex_lock(&handle->rh_lock);
 838         if (handle_is_bound(handle)) {
 839                 (void) pthread_mutex_unlock(&handle->rh_lock);
 840                 return (scf_set_error(SCF_ERROR_IN_USE));
 841         }
 842         (void) pthread_mutex_unlock(&handle->rh_lock);
 843 
 844         if (strcmp(name, "debug") == 0) {
 845                 if (v == SCF_DECORATE_CLEAR) {
 846                         (void) pthread_mutex_lock(&handle->rh_lock);
 847                         handle->rh_debug = 0;
 848                         (void) pthread_mutex_unlock(&handle->rh_lock);
 849                 } else {
 850                         uint64_t val;
 851                         if (scf_value_get_count(v, &val) < 0)
 852                                 return (-1);            /* error already set */
 853 
 854                         (void) pthread_mutex_lock(&handle->rh_lock);
 855                         handle->rh_debug = (uid_t)val;
 856                         (void) pthread_mutex_unlock(&handle->rh_lock);
 857                 }
 858                 return (0);
 859         }
 860         if (strcmp(name, "door_path") == 0) {
 861                 char name[sizeof (handle->rh_doorpath)];
 862 
 863                 if (v == SCF_DECORATE_CLEAR) {
 864                         (void) pthread_mutex_lock(&handle->rh_lock);
 865                         handle->rh_doorpath[0] = 0;
 866                         (void) pthread_mutex_unlock(&handle->rh_lock);
 867                 } else {
 868                         ssize_t len;
 869 
 870                         if ((len = scf_value_get_astring(v, name,
 871                             sizeof (name))) < 0) {
 872                                 return (-1);            /* error already set */
 873                         }
 874                         if (len == 0 || len >= sizeof (name)) {
 875                                 return (scf_set_error(
 876                                     SCF_ERROR_INVALID_ARGUMENT));
 877                         }
 878                         (void) pthread_mutex_lock(&handle->rh_lock);
 879                         (void) strlcpy(handle->rh_doorpath, name,
 880                             sizeof (handle->rh_doorpath));
 881                         (void) pthread_mutex_unlock(&handle->rh_lock);
 882                 }
 883                 return (0);
 884         }
 885 
 886         if (strcmp(name, "zone") == 0) {
 887                 char zone[MAXPATHLEN], root[MAXPATHLEN], door[MAXPATHLEN];
 888                 static int (*zone_get_rootpath)(char *, char *, size_t);
 889                 ssize_t len;
 890 
 891                 /*
 892                  * In order to be able to set the zone on a handle, we want
 893                  * to determine the zone's path, which requires us to call into
 894                  * libzonecfg -- but libzonecfg.so links against libscf.so so
 895                  * we must not explicitly link to it.  To circumvent the
 896                  * circular dependency, we will pull it in here via dlopen().
 897                  */
 898                 if (zone_get_rootpath == NULL) {
 899                         void *dl = dlopen("libzonecfg.so.1", RTLD_LAZY), *sym;
 900 
 901                         if (dl == NULL)
 902                                 return (scf_set_error(SCF_ERROR_NOT_FOUND));
 903 
 904                         if ((sym = dlsym(dl, "zone_get_rootpath")) == NULL) {
 905                                 (void) dlclose(dl);
 906                                 return (scf_set_error(SCF_ERROR_INTERNAL));
 907                         }
 908 
 909                         zone_get_rootpath = (int(*)(char *, char *, size_t))sym;
 910                 }
 911 
 912                 if (v == SCF_DECORATE_CLEAR) {
 913                         (void) pthread_mutex_lock(&handle->rh_lock);
 914                         handle->rh_doorpath[0] = 0;
 915                         (void) pthread_mutex_unlock(&handle->rh_lock);
 916 
 917                         return (0);
 918                 }
 919 
 920                 if ((len = scf_value_get_astring(v, zone, sizeof (zone))) < 0)
 921                         return (-1);
 922 
 923                 if (len == 0 || len >= sizeof (zone))
 924                         return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
 925 
 926                 if (zone_get_rootpath(zone, root, sizeof (root)) != Z_OK) {
 927                         if (strcmp(zone, GLOBAL_ZONENAME) == 0) {
 928                                 root[0] = '\0';
 929                         } else {
 930                                 return (scf_set_error(SCF_ERROR_NOT_FOUND));
 931                         }
 932                 }
 933 
 934                 if (snprintf(door, sizeof (door), "%s/%s", root,
 935                     default_door_path) >= sizeof (door))
 936                         return (scf_set_error(SCF_ERROR_INTERNAL));
 937 
 938                 (void) pthread_mutex_lock(&handle->rh_lock);
 939                 (void) strlcpy(handle->rh_doorpath, door,
 940                     sizeof (handle->rh_doorpath));
 941                 (void) pthread_mutex_unlock(&handle->rh_lock);
 942 
 943                 return (0);
 944         }
 945 
 946         return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
 947 }
 948 
 949 /*
 950  * fails with INVALID_ARGUMENT and HANDLE_MISMATCH.
 951  */
 952 int
 953 _scf_handle_decorations(scf_handle_t *handle, scf_decoration_func *f,
 954     scf_value_t *v, void *data)
 955 {
 956         scf_decoration_info_t i;
 957         char name[sizeof (handle->rh_doorpath)];
 958         uint64_t debug;
 959 
 960         if (f == NULL || v == NULL)
 961                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
 962 
 963         if (v->value_handle != handle)
 964                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
 965 
 966         i.sdi_name = (const char *)"debug";
 967         i.sdi_type = SCF_TYPE_COUNT;
 968         (void) pthread_mutex_lock(&handle->rh_lock);
 969         debug = handle->rh_debug;
 970         (void) pthread_mutex_unlock(&handle->rh_lock);
 971         if (debug != 0) {
 972                 scf_value_set_count(v, debug);
 973                 i.sdi_value = v;
 974         } else {
 975                 i.sdi_value = SCF_DECORATE_CLEAR;
 976         }
 977 
 978         if ((*f)(&i, data) == 0)
 979                 return (0);
 980 
 981         i.sdi_name = (const char *)"door_path";
 982         i.sdi_type = SCF_TYPE_ASTRING;
 983         (void) pthread_mutex_lock(&handle->rh_lock);
 984         (void) strlcpy(name, handle->rh_doorpath, sizeof (name));
 985         (void) pthread_mutex_unlock(&handle->rh_lock);
 986         if (name[0] != 0) {
 987                 (void) scf_value_set_astring(v, name);
 988                 i.sdi_value = v;
 989         } else {
 990                 i.sdi_value = SCF_DECORATE_CLEAR;
 991         }
 992 
 993         if ((*f)(&i, data) == 0)
 994                 return (0);
 995 
 996         return (1);
 997 }
 998 
 999 /*
1000  * Fails if handle is not bound.
1001  */
1002 static int
1003 handle_unbind_unlocked(scf_handle_t *handle)
1004 {
1005         rep_protocol_request_t request;
1006         rep_protocol_response_t response;
1007 
1008         if (!handle_is_bound(handle))
1009                 return (-1);
1010 
1011         request.rpr_request = REP_PROTOCOL_CLOSE;
1012 
1013         (void) make_door_call(handle, &request, sizeof (request),
1014             &response, sizeof (response));
1015 
1016         handle_do_close(handle);
1017 
1018         return (SCF_SUCCESS);
1019 }
1020 
1021 /*
1022  * Fails with
1023  *   _HANDLE_DESTROYED - dp's handle has been destroyed
1024  *   _INTERNAL - server response too big
1025  *               entity already set up with different type
1026  *   _NO_RESOURCES - server out of memory
1027  */
1028 static int
1029 datael_attach(scf_datael_t *dp)
1030 {
1031         scf_handle_t *h = dp->rd_handle;
1032 
1033         struct rep_protocol_entity_setup request;
1034         rep_protocol_response_t response;
1035         ssize_t r;
1036 
1037         assert(MUTEX_HELD(&h->rh_lock));
1038 
1039         dp->rd_reset = 0;            /* setup implicitly resets */
1040 
1041         if (h->rh_flags & HANDLE_DEAD)
1042                 return (scf_set_error(SCF_ERROR_HANDLE_DESTROYED));
1043 
1044         if (!handle_is_bound(h))
1045                 return (SCF_SUCCESS);           /* nothing to do */
1046 
1047         request.rpr_request = REP_PROTOCOL_ENTITY_SETUP;
1048         request.rpr_entityid = dp->rd_entity;
1049         request.rpr_entitytype = dp->rd_type;
1050 
1051         r = make_door_call(h, &request, sizeof (request),
1052             &response, sizeof (response));
1053 
1054         if (r == NOT_BOUND || r == CALL_FAILED)
1055                 return (SCF_SUCCESS);
1056         if (r == RESULT_TOO_BIG)
1057                 return (scf_set_error(SCF_ERROR_INTERNAL));
1058 
1059         if (response.rpr_response != REP_PROTOCOL_SUCCESS)
1060                 return (scf_set_error(proto_error(response.rpr_response)));
1061 
1062         return (SCF_SUCCESS);
1063 }
1064 
1065 /*
1066  * Fails with
1067  *   _HANDLE_DESTROYED - iter's handle has been destroyed
1068  *   _INTERNAL - server response too big
1069  *               iter already existed
1070  *   _NO_RESOURCES
1071  */
1072 static int
1073 iter_attach(scf_iter_t *iter)
1074 {
1075         scf_handle_t *h = iter->iter_handle;
1076         struct rep_protocol_iter_request request;
1077         struct rep_protocol_response response;
1078         int r;
1079 
1080         assert(MUTEX_HELD(&h->rh_lock));
1081 
1082         if (h->rh_flags & HANDLE_DEAD)
1083                 return (scf_set_error(SCF_ERROR_HANDLE_DESTROYED));
1084 
1085         if (!handle_is_bound(h))
1086                 return (SCF_SUCCESS);           /* nothing to do */
1087 
1088         request.rpr_request = REP_PROTOCOL_ITER_SETUP;
1089         request.rpr_iterid = iter->iter_id;
1090 
1091         r = make_door_call(h, &request, sizeof (request),
1092             &response, sizeof (response));
1093 
1094         if (r == NOT_BOUND || r == CALL_FAILED)
1095                 return (SCF_SUCCESS);
1096         if (r == RESULT_TOO_BIG)
1097                 return (scf_set_error(SCF_ERROR_INTERNAL));
1098 
1099         if (response.rpr_response != REP_PROTOCOL_SUCCESS)
1100                 return (scf_set_error(proto_error(response.rpr_response)));
1101 
1102         return (SCF_SUCCESS);
1103 }
1104 
1105 /*
1106  * Fails with
1107  *   _IN_USE - handle already bound
1108  *   _NO_SERVER - server door could not be open()ed
1109  *                door call failed
1110  *                door_info() failed
1111  *   _VERSION_MISMATCH - server returned bad file descriptor
1112  *                       server claimed bad request
1113  *                       server reported version mismatch
1114  *                       server refused with unknown reason
1115  *   _INVALID_ARGUMENT
1116  *   _NO_RESOURCES - server is out of memory
1117  *   _PERMISSION_DENIED
1118  *   _INTERNAL - could not set up entities or iters
1119  *               server response too big
1120  *
1121  * perhaps this should try multiple times.
1122  */
1123 int
1124 scf_handle_bind(scf_handle_t *handle)
1125 {
1126         scf_datael_t *el;
1127         scf_iter_t *iter;
1128 
1129         pid_t pid;
1130         int fd;
1131         int res;
1132         door_info_t info;
1133         repository_door_request_t request;
1134         repository_door_response_t response;
1135         const char *door_name = default_door_path;
1136 
1137         (void) pthread_mutex_lock(&handle->rh_lock);
1138         if (handle_is_bound(handle)) {
1139                 (void) pthread_mutex_unlock(&handle->rh_lock);
1140                 return (scf_set_error(SCF_ERROR_IN_USE));
1141         }
1142 
1143         /* wait until any active fd users have cleared out */
1144         while (handle->rh_fd_users > 0) {
1145                 int cancel_state;
1146 
1147                 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
1148                     &cancel_state);
1149                 (void) pthread_cond_wait(&handle->rh_cv, &handle->rh_lock);
1150                 (void) pthread_setcancelstate(cancel_state, NULL);
1151         }
1152 
1153         /* check again, since we had to drop the lock */
1154         if (handle_is_bound(handle)) {
1155                 (void) pthread_mutex_unlock(&handle->rh_lock);
1156                 return (scf_set_error(SCF_ERROR_IN_USE));
1157         }
1158 
1159         assert(handle->rh_doorfd == -1 && handle->rh_doorfd_old == -1);
1160 
1161         if (handle->rh_doorpath[0] != 0)
1162                 door_name = handle->rh_doorpath;
1163 
1164         fd = open(door_name, O_RDONLY, 0);
1165         if (fd == -1) {
1166                 (void) pthread_mutex_unlock(&handle->rh_lock);
1167                 return (scf_set_error(SCF_ERROR_NO_SERVER));
1168         }
1169 
1170         request.rdr_version = REPOSITORY_DOOR_VERSION;
1171         request.rdr_request = REPOSITORY_DOOR_REQUEST_CONNECT;
1172         request.rdr_flags = handle->rh_flags;
1173         request.rdr_debug = handle->rh_debug;
1174 
1175         pid = getpid();
1176 
1177         res = make_door_call_retfd(fd, &request, sizeof (request),
1178             &response, sizeof (response), &handle->rh_doorfd);
1179 
1180         (void) close(fd);
1181 
1182         if (res < 0) {
1183                 (void) pthread_mutex_unlock(&handle->rh_lock);
1184 
1185                 assert(res != NOT_BOUND);
1186                 if (res == CALL_FAILED)
1187                         return (scf_set_error(SCF_ERROR_NO_SERVER));
1188                 assert(res == RESULT_TOO_BIG);
1189                 return (scf_set_error(SCF_ERROR_INTERNAL));
1190         }
1191 
1192         if (handle->rh_doorfd < 0) {
1193                 (void) pthread_mutex_unlock(&handle->rh_lock);
1194 
1195                 switch (response.rdr_status) {
1196                 case REPOSITORY_DOOR_SUCCESS:
1197                         return (scf_set_error(SCF_ERROR_VERSION_MISMATCH));
1198 
1199                 case REPOSITORY_DOOR_FAIL_BAD_REQUEST:
1200                         return (scf_set_error(SCF_ERROR_VERSION_MISMATCH));
1201 
1202                 case REPOSITORY_DOOR_FAIL_VERSION_MISMATCH:
1203                         return (scf_set_error(SCF_ERROR_VERSION_MISMATCH));
1204 
1205                 case REPOSITORY_DOOR_FAIL_BAD_FLAG:
1206                         return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
1207 
1208                 case REPOSITORY_DOOR_FAIL_NO_RESOURCES:
1209                         return (scf_set_error(SCF_ERROR_NO_RESOURCES));
1210 
1211                 case REPOSITORY_DOOR_FAIL_PERMISSION_DENIED:
1212                         return (scf_set_error(SCF_ERROR_PERMISSION_DENIED));
1213 
1214                 default:
1215                         return (scf_set_error(SCF_ERROR_VERSION_MISMATCH));
1216                 }
1217         }
1218 
1219         (void) fcntl(handle->rh_doorfd, F_SETFD, FD_CLOEXEC);
1220 
1221         if (door_info(handle->rh_doorfd, &info) < 0) {
1222                 (void) close(handle->rh_doorfd);
1223                 handle->rh_doorfd = -1;
1224 
1225                 (void) pthread_mutex_unlock(&handle->rh_lock);
1226                 return (scf_set_error(SCF_ERROR_NO_SERVER));
1227         }
1228 
1229         handle->rh_doorpid = pid;
1230         handle->rh_doorid = info.di_uniquifier;
1231 
1232         /*
1233          * Now, re-attach everything
1234          */
1235         for (el = uu_list_first(handle->rh_dataels); el != NULL;
1236             el = uu_list_next(handle->rh_dataels, el)) {
1237                 if (datael_attach(el) == -1) {
1238                         assert(scf_error() != SCF_ERROR_HANDLE_DESTROYED);
1239                         (void) handle_unbind_unlocked(handle);
1240                         (void) pthread_mutex_unlock(&handle->rh_lock);
1241                         return (-1);
1242                 }
1243         }
1244 
1245         for (iter = uu_list_first(handle->rh_iters); iter != NULL;
1246             iter = uu_list_next(handle->rh_iters, iter)) {
1247                 if (iter_attach(iter) == -1) {
1248                         assert(scf_error() != SCF_ERROR_HANDLE_DESTROYED);
1249                         (void) handle_unbind_unlocked(handle);
1250                         (void) pthread_mutex_unlock(&handle->rh_lock);
1251                         return (-1);
1252                 }
1253         }
1254         (void) pthread_mutex_unlock(&handle->rh_lock);
1255         return (SCF_SUCCESS);
1256 }
1257 
1258 int
1259 scf_handle_unbind(scf_handle_t *handle)
1260 {
1261         int ret;
1262         (void) pthread_mutex_lock(&handle->rh_lock);
1263         ret = handle_unbind_unlocked(handle);
1264         (void) pthread_mutex_unlock(&handle->rh_lock);
1265         return (ret == SCF_SUCCESS ? ret : scf_set_error(SCF_ERROR_NOT_BOUND));
1266 }
1267 
1268 static scf_handle_t *
1269 handle_get(scf_handle_t *h)
1270 {
1271         (void) pthread_mutex_lock(&h->rh_lock);
1272         if (h->rh_flags & HANDLE_DEAD) {
1273                 (void) pthread_mutex_unlock(&h->rh_lock);
1274                 (void) scf_set_error(SCF_ERROR_HANDLE_DESTROYED);
1275                 return (NULL);
1276         }
1277         (void) pthread_mutex_unlock(&h->rh_lock);
1278         return (h);
1279 }
1280 
1281 /*
1282  * Called when an object is removed from the handle.  On the last remove,
1283  * cleans up and frees the handle.
1284  */
1285 static void
1286 handle_unrefed(scf_handle_t *handle)
1287 {
1288         scf_iter_t *iter;
1289         scf_value_t *v;
1290         scf_scope_t *sc;
1291         scf_service_t *svc;
1292         scf_instance_t *inst;
1293         scf_snapshot_t *snap;
1294         scf_snaplevel_t *snaplvl;
1295         scf_propertygroup_t *pg;
1296         scf_property_t *prop;
1297 
1298         assert(MUTEX_HELD(&handle->rh_lock));
1299 
1300         /*
1301          * Don't do anything if the handle has not yet been destroyed, there
1302          * are still external references, or we're already doing unrefed
1303          * handling.
1304          */
1305         if (!(handle->rh_flags & HANDLE_DEAD) ||
1306             handle->rh_extrefs > 0 ||
1307             handle->rh_fd_users > 0 ||
1308             (handle->rh_flags & HANDLE_UNREFED)) {
1309                 (void) pthread_mutex_unlock(&handle->rh_lock);
1310                 return;
1311         }
1312 
1313         handle->rh_flags |= HANDLE_UNREFED;
1314 
1315         /*
1316          * Now that we know that there are no external references, and the
1317          * HANDLE_DEAD flag keeps new ones from appearing, we can clean up
1318          * our subhandles and destroy the handle completely.
1319          */
1320         assert(handle->rh_intrefs >= 0);
1321         handle->rh_extrefs = handle->rh_intrefs;
1322         handle->rh_intrefs = 0;
1323         (void) pthread_mutex_unlock(&handle->rh_lock);
1324 
1325         handle_hold_subhandles(handle, RH_HOLD_ALL);
1326 
1327         iter = handle->rh_iter;
1328         sc = handle->rh_scope;
1329         svc = handle->rh_service;
1330         inst = handle->rh_instance;
1331         snap = handle->rh_snapshot;
1332         snaplvl = handle->rh_snaplvl;
1333         pg = handle->rh_pg;
1334         prop = handle->rh_property;
1335         v = handle->rh_value;
1336 
1337         handle->rh_iter = NULL;
1338         handle->rh_scope = NULL;
1339         handle->rh_service = NULL;
1340         handle->rh_instance = NULL;
1341         handle->rh_snapshot = NULL;
1342         handle->rh_snaplvl = NULL;
1343         handle->rh_pg = NULL;
1344         handle->rh_property = NULL;
1345         handle->rh_value = NULL;
1346 
1347         if (iter != NULL)
1348                 scf_iter_destroy(iter);
1349         if (sc != NULL)
1350                 scf_scope_destroy(sc);
1351         if (svc != NULL)
1352                 scf_service_destroy(svc);
1353         if (inst != NULL)
1354                 scf_instance_destroy(inst);
1355         if (snap != NULL)
1356                 scf_snapshot_destroy(snap);
1357         if (snaplvl != NULL)
1358                 scf_snaplevel_destroy(snaplvl);
1359         if (pg != NULL)
1360                 scf_pg_destroy(pg);
1361         if (prop != NULL)
1362                 scf_property_destroy(prop);
1363         if (v != NULL)
1364                 scf_value_destroy(v);
1365 
1366         (void) pthread_mutex_lock(&handle->rh_lock);
1367 
1368         /* there should be no outstanding children at this point */
1369         assert(handle->rh_extrefs == 0);
1370         assert(handle->rh_intrefs == 0);
1371         assert(handle->rh_values == 0);
1372         assert(handle->rh_entries == 0);
1373         assert(uu_list_numnodes(handle->rh_dataels) == 0);
1374         assert(uu_list_numnodes(handle->rh_iters) == 0);
1375 
1376         uu_list_destroy(handle->rh_dataels);
1377         uu_list_destroy(handle->rh_iters);
1378         handle->rh_dataels = NULL;
1379         handle->rh_iters = NULL;
1380         (void) pthread_mutex_unlock(&handle->rh_lock);
1381 
1382         (void) pthread_mutex_destroy(&handle->rh_lock);
1383 
1384         uu_free(handle);
1385 }
1386 
1387 void
1388 scf_handle_destroy(scf_handle_t *handle)
1389 {
1390         if (handle == NULL)
1391                 return;
1392 
1393         (void) pthread_mutex_lock(&handle->rh_lock);
1394         if (handle->rh_flags & HANDLE_DEAD) {
1395                 /*
1396                  * This is an error (you are not allowed to reference the
1397                  * handle after it is destroyed), but we can't report it.
1398                  */
1399                 (void) pthread_mutex_unlock(&handle->rh_lock);
1400                 return;
1401         }
1402         handle->rh_flags |= HANDLE_DEAD;
1403         (void) handle_unbind_unlocked(handle);
1404         handle_unrefed(handle);
1405 }
1406 
1407 ssize_t
1408 scf_myname(scf_handle_t *h, char *out, size_t len)
1409 {
1410         char *cp;
1411 
1412         if (!handle_has_server(h))
1413                 return (scf_set_error(SCF_ERROR_CONNECTION_BROKEN));
1414 
1415         cp = getenv("SMF_FMRI");
1416         if (cp == NULL)
1417                 return (scf_set_error(SCF_ERROR_NOT_SET));
1418 
1419         return (strlcpy(out, cp, len));
1420 }
1421 
1422 static uint32_t
1423 handle_alloc_entityid(scf_handle_t *h)
1424 {
1425         uint32_t nextid;
1426 
1427         assert(MUTEX_HELD(&h->rh_lock));
1428 
1429         if (uu_list_numnodes(h->rh_dataels) == UINT32_MAX)
1430                 return (0);             /* no ids available */
1431 
1432         /*
1433          * The following loop assumes that there are not a huge number of
1434          * outstanding entities when we've wrapped.  If that ends up not
1435          * being the case, the O(N^2) nature of this search will hurt a lot,
1436          * and the data structure should be switched to an AVL tree.
1437          */
1438         nextid = h->rh_nextentity + 1;
1439         for (;;) {
1440                 scf_datael_t *cur;
1441 
1442                 if (nextid == 0) {
1443                         nextid++;
1444                         h->rh_flags |= HANDLE_WRAPPED_ENTITY;
1445                 }
1446                 if (!(h->rh_flags & HANDLE_WRAPPED_ENTITY))
1447                         break;
1448 
1449                 cur = uu_list_find(h->rh_dataels, NULL, &nextid, NULL);
1450                 if (cur == NULL)
1451                         break;          /* not in use */
1452 
1453                 if (nextid == h->rh_nextentity)
1454                         return (0);     /* wrapped around; no ids available */
1455                 nextid++;
1456         }
1457 
1458         h->rh_nextentity = nextid;
1459         return (nextid);
1460 }
1461 
1462 static uint32_t
1463 handle_alloc_iterid(scf_handle_t *h)
1464 {
1465         uint32_t nextid;
1466 
1467         assert(MUTEX_HELD(&h->rh_lock));
1468 
1469         if (uu_list_numnodes(h->rh_iters) == UINT32_MAX)
1470                 return (0);             /* no ids available */
1471 
1472         /* see the comment in handle_alloc_entityid */
1473         nextid = h->rh_nextiter + 1;
1474         for (;;) {
1475                 scf_iter_t *cur;
1476 
1477                 if (nextid == 0) {
1478                         nextid++;
1479                         h->rh_flags |= HANDLE_WRAPPED_ITER;
1480                 }
1481                 if (!(h->rh_flags & HANDLE_WRAPPED_ITER))
1482                         break;                  /* not yet wrapped */
1483 
1484                 cur = uu_list_find(h->rh_iters, NULL, &nextid, NULL);
1485                 if (cur == NULL)
1486                         break;          /* not in use */
1487 
1488                 if (nextid == h->rh_nextiter)
1489                         return (0);     /* wrapped around; no ids available */
1490                 nextid++;
1491         }
1492 
1493         h->rh_nextiter = nextid;
1494         return (nextid);
1495 }
1496 
1497 static uint32_t
1498 handle_next_changeid(scf_handle_t *handle)
1499 {
1500         uint32_t nextid;
1501 
1502         assert(MUTEX_HELD(&handle->rh_lock));
1503 
1504         nextid = ++handle->rh_nextchangeid;
1505         if (nextid == 0)
1506                 nextid = ++handle->rh_nextchangeid;
1507         return (nextid);
1508 }
1509 
1510 /*
1511  * Fails with
1512  *   _INVALID_ARGUMENT - h is NULL
1513  *   _HANDLE_DESTROYED
1514  *   _INTERNAL - server response too big
1515  *               entity already set up with different type
1516  *   _NO_RESOURCES
1517  */
1518 static int
1519 datael_init(scf_datael_t *dp, scf_handle_t *h, uint32_t type)
1520 {
1521         int ret;
1522 
1523         if (h == NULL)
1524                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
1525 
1526         uu_list_node_init(dp, &dp->rd_node, datael_pool);
1527 
1528         dp->rd_handle = h;
1529         dp->rd_type = type;
1530         dp->rd_reset = 0;
1531 
1532         (void) pthread_mutex_lock(&h->rh_lock);
1533         if (h->rh_flags & HANDLE_DEAD) {
1534                 /*
1535                  * we're in undefined territory (the user cannot use a handle
1536                  * directly after it has been destroyed), but we don't want
1537                  * to allow any new references to happen, so we fail here.
1538                  */
1539                 (void) pthread_mutex_unlock(&h->rh_lock);
1540                 return (scf_set_error(SCF_ERROR_HANDLE_DESTROYED));
1541         }
1542         dp->rd_entity = handle_alloc_entityid(h);
1543         if (dp->rd_entity == 0) {
1544                 (void) pthread_mutex_unlock(&h->rh_lock);
1545                 uu_list_node_fini(dp, &dp->rd_node, datael_pool);
1546                 return (scf_set_error(SCF_ERROR_NO_MEMORY));
1547         }
1548 
1549         ret = datael_attach(dp);
1550         if (ret == 0) {
1551                 (void) uu_list_insert_before(h->rh_dataels, NULL, dp);
1552                 h->rh_extrefs++;
1553         } else {
1554                 uu_list_node_fini(dp, &dp->rd_node, datael_pool);
1555         }
1556         (void) pthread_mutex_unlock(&h->rh_lock);
1557 
1558         return (ret);
1559 }
1560 
1561 static void
1562 datael_destroy(scf_datael_t *dp)
1563 {
1564         scf_handle_t *h = dp->rd_handle;
1565 
1566         struct rep_protocol_entity_teardown request;
1567         rep_protocol_response_t response;
1568 
1569         (void) pthread_mutex_lock(&h->rh_lock);
1570         uu_list_remove(h->rh_dataels, dp);
1571         --h->rh_extrefs;
1572 
1573         if (handle_is_bound(h)) {
1574                 request.rpr_request = REP_PROTOCOL_ENTITY_TEARDOWN;
1575                 request.rpr_entityid = dp->rd_entity;
1576 
1577                 (void) make_door_call(h, &request, sizeof (request),
1578                     &response, sizeof (response));
1579         }
1580         handle_unrefed(h);                      /* drops h->rh_lock */
1581 
1582         dp->rd_handle = NULL;
1583 }
1584 
1585 static scf_handle_t *
1586 datael_handle(const scf_datael_t *dp)
1587 {
1588         return (handle_get(dp->rd_handle));
1589 }
1590 
1591 /*
1592  * We delay ENTITY_RESETs until right before the entity is used.  By doing
1593  * them lazily, we remove quite a few unnecessary calls.
1594  */
1595 static void
1596 datael_do_reset_locked(scf_datael_t *dp)
1597 {
1598         scf_handle_t *h = dp->rd_handle;
1599 
1600         struct rep_protocol_entity_reset request;
1601         rep_protocol_response_t response;
1602 
1603         assert(MUTEX_HELD(&h->rh_lock));
1604 
1605         request.rpr_request = REP_PROTOCOL_ENTITY_RESET;
1606         request.rpr_entityid = dp->rd_entity;
1607 
1608         (void) make_door_call(h, &request, sizeof (request),
1609             &response, sizeof (response));
1610 
1611         dp->rd_reset = 0;
1612 }
1613 
1614 static void
1615 datael_reset_locked(scf_datael_t *dp)
1616 {
1617         assert(MUTEX_HELD(&dp->rd_handle->rh_lock));
1618         dp->rd_reset = 1;
1619 }
1620 
1621 static void
1622 datael_reset(scf_datael_t *dp)
1623 {
1624         scf_handle_t *h = dp->rd_handle;
1625 
1626         (void) pthread_mutex_lock(&h->rh_lock);
1627         dp->rd_reset = 1;
1628         (void) pthread_mutex_unlock(&h->rh_lock);
1629 }
1630 
1631 static void
1632 datael_finish_reset(const scf_datael_t *dp_arg)
1633 {
1634         scf_datael_t *dp = (scf_datael_t *)dp_arg;
1635 
1636         if (dp->rd_reset)
1637                 datael_do_reset_locked(dp);
1638 }
1639 
1640 /*
1641  * Fails with _NOT_BOUND, _CONNECTION_BROKEN, _INTERNAL (server response too
1642  * big, bad entity id, request not applicable to entity, name too long for
1643  * buffer), _NOT_SET, _DELETED, or _CONSTRAINT_VIOLATED (snaplevel is not of an
1644  * instance).
1645  */
1646 static ssize_t
1647 datael_get_name(const scf_datael_t *dp, char *buf, size_t size, uint32_t type)
1648 {
1649         scf_handle_t *h = dp->rd_handle;
1650 
1651         struct rep_protocol_entity_name request;
1652         struct rep_protocol_name_response response;
1653         ssize_t r;
1654 
1655         (void) pthread_mutex_lock(&h->rh_lock);
1656         request.rpr_request = REP_PROTOCOL_ENTITY_NAME;
1657         request.rpr_entityid = dp->rd_entity;
1658         request.rpr_answertype = type;
1659 
1660         datael_finish_reset(dp);
1661         r = make_door_call(h, &request, sizeof (request),
1662             &response, sizeof (response));
1663         (void) pthread_mutex_unlock(&h->rh_lock);
1664 
1665         if (r < 0)
1666                 DOOR_ERRORS_BLOCK(r);
1667 
1668         if (response.rpr_response != REP_PROTOCOL_SUCCESS) {
1669                 assert(response.rpr_response != REP_PROTOCOL_FAIL_BAD_REQUEST);
1670                 if (response.rpr_response == REP_PROTOCOL_FAIL_NOT_FOUND)
1671                         return (scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED));
1672                 return (scf_set_error(proto_error(response.rpr_response)));
1673         }
1674         return (strlcpy(buf, response.rpr_name, size));
1675 }
1676 
1677 /*
1678  * Fails with _HANDLE_MISMATCH, _NOT_BOUND, _CONNECTION_BROKEN, _INTERNAL
1679  * (server response too big, bad element id), _EXISTS (elements have same id),
1680  * _NOT_SET, _DELETED, _CONSTRAINT_VIOLATED, _NOT_FOUND (scope has no parent),
1681  * or _SUCCESS.
1682  */
1683 static int
1684 datael_get_parent(const scf_datael_t *dp, scf_datael_t *pp)
1685 {
1686         scf_handle_t *h = dp->rd_handle;
1687 
1688         struct rep_protocol_entity_parent request;
1689         struct rep_protocol_response response;
1690 
1691         ssize_t r;
1692 
1693         if (h != pp->rd_handle)
1694                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
1695 
1696         (void) pthread_mutex_lock(&h->rh_lock);
1697         request.rpr_request = REP_PROTOCOL_ENTITY_GET_PARENT;
1698         request.rpr_entityid = dp->rd_entity;
1699         request.rpr_outid = pp->rd_entity;
1700 
1701         datael_finish_reset(dp);
1702         datael_finish_reset(pp);
1703         r = make_door_call(h, &request, sizeof (request),
1704             &response, sizeof (response));
1705         (void) pthread_mutex_unlock(&h->rh_lock);
1706 
1707         if (r < 0)
1708                 DOOR_ERRORS_BLOCK(r);
1709 
1710         if (response.rpr_response != REP_PROTOCOL_SUCCESS) {
1711                 if (response.rpr_response == REP_PROTOCOL_FAIL_TYPE_MISMATCH)
1712                         return (scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED));
1713                 return (scf_set_error(proto_error(response.rpr_response)));
1714         }
1715 
1716         return (SCF_SUCCESS);
1717 }
1718 
1719 /*
1720  * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT (out does not have type type,
1721  * name is invalid), _NOT_BOUND, _CONNECTION_BROKEN, _INTERNAL (server response
1722  * too big, bad id, iter already exists, element cannot have children of type,
1723  * type is invalid, iter was reset, sequence was bad, iter walks values, iter
1724  * does not walk type entities), _NOT_SET, _DELETED, _NO_RESOURCES,
1725  * _BACKEND_ACCESS, _NOT_FOUND.
1726  */
1727 static int
1728 datael_get_child_composed_locked(const scf_datael_t *dp, const char *name,
1729     uint32_t type, scf_datael_t *out, scf_iter_t *iter)
1730 {
1731         struct rep_protocol_iter_start request;
1732         struct rep_protocol_iter_read read_request;
1733         struct rep_protocol_response response;
1734 
1735         scf_handle_t *h = dp->rd_handle;
1736         ssize_t r;
1737 
1738         if (h != out->rd_handle)
1739                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
1740 
1741         if (out->rd_type != type)
1742                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
1743 
1744         assert(MUTEX_HELD(&h->rh_lock));
1745         assert(iter != NULL);
1746 
1747         scf_iter_reset_locked(iter);
1748         iter->iter_type = type;
1749 
1750         request.rpr_request = REP_PROTOCOL_ITER_START;
1751         request.rpr_iterid = iter->iter_id;
1752         request.rpr_entity = dp->rd_entity;
1753         request.rpr_itertype = type;
1754         request.rpr_flags = RP_ITER_START_EXACT | RP_ITER_START_COMPOSED;
1755 
1756         if (name == NULL || strlcpy(request.rpr_pattern, name,
1757             sizeof (request.rpr_pattern)) >= sizeof (request.rpr_pattern)) {
1758                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
1759         }
1760 
1761         datael_finish_reset(dp);
1762         datael_finish_reset(out);
1763 
1764         /*
1765          * We hold the handle lock across both door calls, so that they
1766          * appear atomic.
1767          */
1768         r = make_door_call(h, &request, sizeof (request),
1769             &response, sizeof (response));
1770 
1771         if (r < 0)
1772                 DOOR_ERRORS_BLOCK(r);
1773 
1774         if (response.rpr_response != REP_PROTOCOL_SUCCESS)
1775                 return (scf_set_error(proto_error(response.rpr_response)));
1776 
1777         iter->iter_sequence++;
1778 
1779         read_request.rpr_request = REP_PROTOCOL_ITER_READ;
1780         read_request.rpr_iterid = iter->iter_id;
1781         read_request.rpr_sequence = iter->iter_sequence;
1782         read_request.rpr_entityid = out->rd_entity;
1783 
1784         r = make_door_call(h, &read_request, sizeof (read_request),
1785             &response, sizeof (response));
1786 
1787         scf_iter_reset_locked(iter);
1788 
1789         if (r < 0)
1790                 DOOR_ERRORS_BLOCK(r);
1791 
1792         if (response.rpr_response == REP_PROTOCOL_DONE) {
1793                 return (scf_set_error(SCF_ERROR_NOT_FOUND));
1794         }
1795 
1796         if (response.rpr_response != REP_PROTOCOL_SUCCESS) {
1797                 if (response.rpr_response == REP_PROTOCOL_FAIL_NOT_SET ||
1798                     response.rpr_response == REP_PROTOCOL_FAIL_BAD_REQUEST)
1799                         return (scf_set_error(SCF_ERROR_INTERNAL));
1800                 return (scf_set_error(proto_error(response.rpr_response)));
1801         }
1802 
1803         return (0);
1804 }
1805 
1806 /*
1807  * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT (out does not have type type,
1808  * name is invalid), _NOT_BOUND, _CONNECTION_BROKEN, _INTERNAL (server response
1809  * too big, bad id, element cannot have children of type, type is invalid),
1810  * _NOT_SET, _DELETED, _NO_RESOURCES, _BACKEND_ACCESS.
1811  */
1812 static int
1813 datael_get_child_locked(const scf_datael_t *dp, const char *name,
1814     uint32_t type, scf_datael_t *out)
1815 {
1816         struct rep_protocol_entity_get_child request;
1817         struct rep_protocol_response response;
1818 
1819         scf_handle_t *h = dp->rd_handle;
1820         ssize_t r;
1821 
1822         if (h != out->rd_handle)
1823                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
1824 
1825         if (out->rd_type != type)
1826                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
1827 
1828         assert(MUTEX_HELD(&h->rh_lock));
1829 
1830         request.rpr_request = REP_PROTOCOL_ENTITY_GET_CHILD;
1831         request.rpr_entityid = dp->rd_entity;
1832         request.rpr_childid = out->rd_entity;
1833 
1834         if (name == NULL || strlcpy(request.rpr_name, name,
1835             sizeof (request.rpr_name)) >= sizeof (request.rpr_name)) {
1836                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
1837         }
1838 
1839         datael_finish_reset(dp);
1840         datael_finish_reset(out);
1841 
1842         r = make_door_call(h, &request, sizeof (request),
1843             &response, sizeof (response));
1844 
1845         if (r < 0)
1846                 DOOR_ERRORS_BLOCK(r);
1847 
1848         if (response.rpr_response != REP_PROTOCOL_SUCCESS)
1849                 return (scf_set_error(proto_error(response.rpr_response)));
1850         return (0);
1851 }
1852 
1853 /*
1854  * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT (out does not have type type,
1855  * name is invalid), _NOT_BOUND, _CONNECTION_BROKEN, _INTERNAL (server response
1856  * too big, bad id, iter already exists, element cannot have children of type,
1857  * type is invalid, iter was reset, sequence was bad, iter walks values, iter
1858  * does not walk type entities), _NOT_SET, _DELETED, _NO_RESOURCES,
1859  * _BACKEND_ACCESS, _NOT_FOUND.
1860  */
1861 static int
1862 datael_get_child(const scf_datael_t *dp, const char *name, uint32_t type,
1863     scf_datael_t *out, boolean_t composed)
1864 {
1865         scf_handle_t *h = dp->rd_handle;
1866         uint32_t held = 0;
1867         int ret;
1868 
1869         scf_iter_t *iter = NULL;
1870 
1871         if (composed)
1872                 iter = HANDLE_HOLD_ITER(h);
1873 
1874         if (out == NULL) {
1875                 switch (type) {
1876                 case REP_PROTOCOL_ENTITY_SERVICE:
1877                         out = &HANDLE_HOLD_SERVICE(h)->rd_d;
1878                         held = RH_HOLD_SERVICE;
1879                         break;
1880 
1881                 case REP_PROTOCOL_ENTITY_INSTANCE:
1882                         out = &HANDLE_HOLD_INSTANCE(h)->rd_d;
1883                         held = RH_HOLD_INSTANCE;
1884                         break;
1885 
1886                 case REP_PROTOCOL_ENTITY_SNAPSHOT:
1887                         out = &HANDLE_HOLD_SNAPSHOT(h)->rd_d;
1888                         held = RH_HOLD_SNAPSHOT;
1889                         break;
1890 
1891                 case REP_PROTOCOL_ENTITY_SNAPLEVEL:
1892                         out = &HANDLE_HOLD_SNAPLVL(h)->rd_d;
1893                         held = RH_HOLD_SNAPLVL;
1894                         break;
1895 
1896                 case REP_PROTOCOL_ENTITY_PROPERTYGRP:
1897                         out = &HANDLE_HOLD_PG(h)->rd_d;
1898                         held = RH_HOLD_PG;
1899                         break;
1900 
1901                 case REP_PROTOCOL_ENTITY_PROPERTY:
1902                         out = &HANDLE_HOLD_PROPERTY(h)->rd_d;
1903                         held = RH_HOLD_PROPERTY;
1904                         break;
1905 
1906                 default:
1907                         assert(0);
1908                         abort();
1909                 }
1910         }
1911 
1912         (void) pthread_mutex_lock(&h->rh_lock);
1913         if (composed)
1914                 ret = datael_get_child_composed_locked(dp, name, type, out,
1915                     iter);
1916         else
1917                 ret = datael_get_child_locked(dp, name, type, out);
1918         (void) pthread_mutex_unlock(&h->rh_lock);
1919 
1920         if (composed)
1921                 HANDLE_RELE_ITER(h);
1922 
1923         if (held)
1924                 handle_rele_subhandles(h, held);
1925 
1926         return (ret);
1927 }
1928 
1929 /*
1930  * Fails with
1931  *   _HANDLE_MISMATCH
1932  *   _INVALID_ARGUMENT - name is too long
1933  *                       invalid changeid
1934  *                       name is invalid
1935  *                       cannot create children for dp's type of node
1936  *   _NOT_BOUND - handle is not bound
1937  *   _CONNECTION_BROKEN - server is not reachable
1938  *   _INTERNAL - server response too big
1939  *               dp or cp has unknown id
1940  *               type is _PROPERTYGRP
1941  *               type is invalid
1942  *               dp cannot have children of type type
1943  *               database is corrupt
1944  *   _EXISTS - dp & cp have the same id
1945  *   _EXISTS - child already exists
1946  *   _DELETED - dp has been deleted
1947  *   _NOT_SET - dp is reset
1948  *   _NO_RESOURCES
1949  *   _PERMISSION_DENIED
1950  *   _BACKEND_ACCESS
1951  *   _BACKEND_READONLY
1952  */
1953 static int
1954 datael_add_child(const scf_datael_t *dp, const char *name, uint32_t type,
1955     scf_datael_t *cp)
1956 {
1957         scf_handle_t *h = dp->rd_handle;
1958 
1959         struct rep_protocol_entity_create_child request;
1960         struct rep_protocol_response response;
1961         ssize_t r;
1962         uint32_t held = 0;
1963 
1964         if (cp == NULL) {
1965                 switch (type) {
1966                 case REP_PROTOCOL_ENTITY_SCOPE:
1967                         cp = &HANDLE_HOLD_SCOPE(h)->rd_d;
1968                         held = RH_HOLD_SCOPE;
1969                         break;
1970                 case REP_PROTOCOL_ENTITY_SERVICE:
1971                         cp = &HANDLE_HOLD_SERVICE(h)->rd_d;
1972                         held = RH_HOLD_SERVICE;
1973                         break;
1974                 case REP_PROTOCOL_ENTITY_INSTANCE:
1975                         cp = &HANDLE_HOLD_INSTANCE(h)->rd_d;
1976                         held = RH_HOLD_INSTANCE;
1977                         break;
1978                 case REP_PROTOCOL_ENTITY_SNAPSHOT:
1979                 default:
1980                         assert(0);
1981                         abort();
1982                 }
1983                 assert(h == cp->rd_handle);
1984 
1985         } else if (h != cp->rd_handle) {
1986                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
1987         }
1988 
1989         if (strlcpy(request.rpr_name, name, sizeof (request.rpr_name)) >=
1990             sizeof (request.rpr_name)) {
1991                 r = scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
1992                 goto err;
1993         }
1994 
1995         (void) pthread_mutex_lock(&h->rh_lock);
1996         request.rpr_request = REP_PROTOCOL_ENTITY_CREATE_CHILD;
1997         request.rpr_entityid = dp->rd_entity;
1998         request.rpr_childtype = type;
1999         request.rpr_childid = cp->rd_entity;
2000 
2001         datael_finish_reset(dp);
2002         request.rpr_changeid = handle_next_changeid(h);
2003         r = make_door_call(h, &request, sizeof (request),
2004             &response, sizeof (response));
2005         (void) pthread_mutex_unlock(&h->rh_lock);
2006 
2007         if (held)
2008                 handle_rele_subhandles(h, held);
2009 
2010         if (r < 0)
2011                 DOOR_ERRORS_BLOCK(r);
2012 
2013         if (response.rpr_response != REP_PROTOCOL_SUCCESS)
2014                 return (scf_set_error(proto_error(response.rpr_response)));
2015 
2016         return (SCF_SUCCESS);
2017 
2018 err:
2019         if (held)
2020                 handle_rele_subhandles(h, held);
2021         return (r);
2022 }
2023 
2024 static int
2025 datael_add_pg(const scf_datael_t *dp, const char *name, const char *type,
2026     uint32_t flags, scf_datael_t *cp)
2027 {
2028         scf_handle_t *h = dp->rd_handle;
2029 
2030         struct rep_protocol_entity_create_pg request;
2031         struct rep_protocol_response response;
2032         ssize_t r;
2033 
2034         int holding_els = 0;
2035 
2036         if (cp == NULL) {
2037                 holding_els = 1;
2038                 cp = &HANDLE_HOLD_PG(h)->rd_d;
2039                 assert(h == cp->rd_handle);
2040 
2041         } else if (h != cp->rd_handle) {
2042                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2043         }
2044 
2045         request.rpr_request = REP_PROTOCOL_ENTITY_CREATE_PG;
2046 
2047         if (name == NULL || strlcpy(request.rpr_name, name,
2048             sizeof (request.rpr_name)) > sizeof (request.rpr_name)) {
2049                 r = scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2050                 goto err;
2051         }
2052 
2053         if (type == NULL || strlcpy(request.rpr_type, type,
2054             sizeof (request.rpr_type)) > sizeof (request.rpr_type)) {
2055                 r = scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2056                 goto err;
2057         }
2058 
2059         (void) pthread_mutex_lock(&h->rh_lock);
2060         request.rpr_entityid = dp->rd_entity;
2061         request.rpr_childid = cp->rd_entity;
2062         request.rpr_flags = flags;
2063 
2064         datael_finish_reset(dp);
2065         datael_finish_reset(cp);
2066         request.rpr_changeid = handle_next_changeid(h);
2067         r = make_door_call(h, &request, sizeof (request),
2068             &response, sizeof (response));
2069         (void) pthread_mutex_unlock(&h->rh_lock);
2070 
2071         if (holding_els)
2072                 HANDLE_RELE_PG(h);
2073 
2074         if (r < 0)
2075                 DOOR_ERRORS_BLOCK(r);
2076 
2077         if (response.rpr_response != REP_PROTOCOL_SUCCESS)
2078                 return (scf_set_error(proto_error(response.rpr_response)));
2079 
2080         return (SCF_SUCCESS);
2081 
2082 err:
2083         if (holding_els)
2084                 HANDLE_RELE_PG(h);
2085         return (r);
2086 }
2087 
2088 static int
2089 datael_delete(const scf_datael_t *dp)
2090 {
2091         scf_handle_t *h = dp->rd_handle;
2092 
2093         struct rep_protocol_entity_delete request;
2094         struct rep_protocol_response response;
2095         ssize_t r;
2096 
2097         (void) pthread_mutex_lock(&h->rh_lock);
2098         request.rpr_request = REP_PROTOCOL_ENTITY_DELETE;
2099         request.rpr_entityid = dp->rd_entity;
2100 
2101         datael_finish_reset(dp);
2102         request.rpr_changeid = handle_next_changeid(h);
2103         r = make_door_call(h, &request, sizeof (request),
2104             &response, sizeof (response));
2105         (void) pthread_mutex_unlock(&h->rh_lock);
2106 
2107         if (r < 0)
2108                 DOOR_ERRORS_BLOCK(r);
2109 
2110         if (response.rpr_response != REP_PROTOCOL_SUCCESS)
2111                 return (scf_set_error(proto_error(response.rpr_response)));
2112 
2113         return (SCF_SUCCESS);
2114 }
2115 
2116 /*
2117  * Fails with
2118  *   _INVALID_ARGUMENT - h is NULL
2119  *   _NO_MEMORY
2120  *   _HANDLE_DESTROYED - h has been destroyed
2121  *   _INTERNAL - server response too big
2122  *               iter already exists
2123  *   _NO_RESOURCES
2124  */
2125 scf_iter_t *
2126 scf_iter_create(scf_handle_t *h)
2127 {
2128         scf_iter_t *iter;
2129 
2130         if (h == NULL) {
2131                 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2132                 return (NULL);
2133         }
2134 
2135         iter = uu_zalloc(sizeof (*iter));
2136         if (iter == NULL) {
2137                 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2138                 return (NULL);
2139         }
2140 
2141         uu_list_node_init(iter, &iter->iter_node, iter_pool);
2142         iter->iter_handle = h;
2143         iter->iter_sequence = 1;
2144         iter->iter_type = REP_PROTOCOL_ENTITY_NONE;
2145 
2146         (void) pthread_mutex_lock(&h->rh_lock);
2147         iter->iter_id = handle_alloc_iterid(h);
2148         if (iter->iter_id == 0) {
2149                 (void) pthread_mutex_unlock(&h->rh_lock);
2150                 uu_list_node_fini(iter, &iter->iter_node, iter_pool);
2151                 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2152                 uu_free(iter);
2153                 return (NULL);
2154         }
2155         if (iter_attach(iter) == -1) {
2156                 uu_list_node_fini(iter, &iter->iter_node, iter_pool);
2157                 (void) pthread_mutex_unlock(&h->rh_lock);
2158                 uu_free(iter);
2159                 return (NULL);
2160         }
2161         (void) uu_list_insert_before(h->rh_iters, NULL, iter);
2162         h->rh_extrefs++;
2163         (void) pthread_mutex_unlock(&h->rh_lock);
2164         return (iter);
2165 }
2166 
2167 scf_handle_t *
2168 scf_iter_handle(const scf_iter_t *iter)
2169 {
2170         return (handle_get(iter->iter_handle));
2171 }
2172 
2173 static void
2174 scf_iter_reset_locked(scf_iter_t *iter)
2175 {
2176         struct rep_protocol_iter_request request;
2177         struct rep_protocol_response response;
2178 
2179         request.rpr_request = REP_PROTOCOL_ITER_RESET;
2180         request.rpr_iterid = iter->iter_id;
2181 
2182         assert(MUTEX_HELD(&iter->iter_handle->rh_lock));
2183 
2184         (void) make_door_call(iter->iter_handle,
2185             &request, sizeof (request), &response, sizeof (response));
2186 
2187         iter->iter_type = REP_PROTOCOL_ENTITY_NONE;
2188         iter->iter_sequence = 1;
2189 }
2190 
2191 void
2192 scf_iter_reset(scf_iter_t *iter)
2193 {
2194         (void) pthread_mutex_lock(&iter->iter_handle->rh_lock);
2195         scf_iter_reset_locked(iter);
2196         (void) pthread_mutex_unlock(&iter->iter_handle->rh_lock);
2197 }
2198 
2199 void
2200 scf_iter_destroy(scf_iter_t *iter)
2201 {
2202         scf_handle_t *handle;
2203 
2204         struct rep_protocol_iter_request request;
2205         struct rep_protocol_response response;
2206 
2207         if (iter == NULL)
2208                 return;
2209 
2210         handle = iter->iter_handle;
2211 
2212         (void) pthread_mutex_lock(&handle->rh_lock);
2213         request.rpr_request = REP_PROTOCOL_ITER_TEARDOWN;
2214         request.rpr_iterid = iter->iter_id;
2215 
2216         (void) make_door_call(handle, &request, sizeof (request),
2217             &response, sizeof (response));
2218 
2219         uu_list_remove(handle->rh_iters, iter);
2220         --handle->rh_extrefs;
2221         handle_unrefed(handle);                 /* drops h->rh_lock */
2222         iter->iter_handle = NULL;
2223 
2224         uu_list_node_fini(iter, &iter->iter_node, iter_pool);
2225         uu_free(iter);
2226 }
2227 
2228 static int
2229 handle_get_local_scope_locked(scf_handle_t *handle, scf_scope_t *out)
2230 {
2231         struct rep_protocol_entity_get request;
2232         struct rep_protocol_name_response response;
2233         ssize_t r;
2234 
2235         assert(MUTEX_HELD(&handle->rh_lock));
2236 
2237         if (handle != out->rd_d.rd_handle)
2238                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2239 
2240         request.rpr_request = REP_PROTOCOL_ENTITY_GET;
2241         request.rpr_entityid = out->rd_d.rd_entity;
2242         request.rpr_object = RP_ENTITY_GET_MOST_LOCAL_SCOPE;
2243 
2244         datael_finish_reset(&out->rd_d);
2245         r = make_door_call(handle, &request, sizeof (request),
2246             &response, sizeof (response));
2247 
2248         if (r < 0)
2249                 DOOR_ERRORS_BLOCK(r);
2250 
2251         if (response.rpr_response != REP_PROTOCOL_SUCCESS)
2252                 return (scf_set_error(proto_error(response.rpr_response)));
2253 
2254         return (SCF_SUCCESS);
2255 }
2256 
2257 int
2258 scf_iter_handle_scopes(scf_iter_t *iter, const scf_handle_t *handle)
2259 {
2260         scf_handle_t *h = iter->iter_handle;
2261         if (h != handle)
2262                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2263 
2264         (void) pthread_mutex_lock(&h->rh_lock);
2265         scf_iter_reset_locked(iter);
2266 
2267         if (!handle_is_bound(h)) {
2268                 (void) pthread_mutex_unlock(&h->rh_lock);
2269                 return (scf_set_error(SCF_ERROR_NOT_BOUND));
2270         }
2271 
2272         if (!handle_has_server_locked(h)) {
2273                 (void) pthread_mutex_unlock(&h->rh_lock);
2274                 return (scf_set_error(SCF_ERROR_CONNECTION_BROKEN));
2275         }
2276 
2277         iter->iter_type = REP_PROTOCOL_ENTITY_SCOPE;
2278         iter->iter_sequence = 1;
2279         (void) pthread_mutex_unlock(&h->rh_lock);
2280         return (0);
2281 }
2282 
2283 int
2284 scf_iter_next_scope(scf_iter_t *iter, scf_scope_t *out)
2285 {
2286         int ret;
2287         scf_handle_t *h = iter->iter_handle;
2288 
2289         if (h != out->rd_d.rd_handle)
2290                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2291 
2292         (void) pthread_mutex_lock(&h->rh_lock);
2293         if (iter->iter_type == REP_PROTOCOL_ENTITY_NONE) {
2294                 (void) pthread_mutex_unlock(&h->rh_lock);
2295                 return (scf_set_error(SCF_ERROR_NOT_SET));
2296         }
2297         if (iter->iter_type != REP_PROTOCOL_ENTITY_SCOPE) {
2298                 (void) pthread_mutex_unlock(&h->rh_lock);
2299                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
2300         }
2301         if (iter->iter_sequence == 1) {
2302                 if ((ret = handle_get_local_scope_locked(h, out)) ==
2303                     SCF_SUCCESS) {
2304                         iter->iter_sequence++;
2305                         ret = 1;
2306                 }
2307         } else {
2308                 datael_reset_locked(&out->rd_d);
2309                 ret = 0;
2310         }
2311         (void) pthread_mutex_unlock(&h->rh_lock);
2312         return (ret);
2313 }
2314 
2315 int
2316 scf_handle_get_scope(scf_handle_t *h, const char *name, scf_scope_t *out)
2317 {
2318         int ret;
2319 
2320         if (h != out->rd_d.rd_handle)
2321                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2322 
2323         (void) pthread_mutex_lock(&h->rh_lock);
2324         if (strcmp(name, SCF_SCOPE_LOCAL) == 0) {
2325                 ret = handle_get_local_scope_locked(h, out);
2326         } else {
2327                 datael_reset_locked(&out->rd_d);
2328                 if (uu_check_name(name, 0) == -1)
2329                         ret = scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2330                 else
2331                         ret = scf_set_error(SCF_ERROR_NOT_FOUND);
2332         }
2333         (void) pthread_mutex_unlock(&h->rh_lock);
2334         return (ret);
2335 }
2336 
2337 static int
2338 datael_setup_iter(scf_iter_t *iter, const scf_datael_t *dp, uint32_t res_type,
2339     boolean_t composed)
2340 {
2341         scf_handle_t *h = dp->rd_handle;
2342 
2343         struct rep_protocol_iter_start request;
2344         struct rep_protocol_response response;
2345 
2346         ssize_t r;
2347 
2348         if (h != iter->iter_handle)
2349                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2350 
2351         (void) pthread_mutex_lock(&h->rh_lock);
2352         scf_iter_reset_locked(iter);
2353         iter->iter_type = res_type;
2354 
2355         request.rpr_request = REP_PROTOCOL_ITER_START;
2356         request.rpr_iterid = iter->iter_id;
2357         request.rpr_entity = dp->rd_entity;
2358         request.rpr_itertype = res_type;
2359         request.rpr_flags = RP_ITER_START_ALL |
2360             (composed ? RP_ITER_START_COMPOSED : 0);
2361         request.rpr_pattern[0] = 0;
2362 
2363         datael_finish_reset(dp);
2364         r = make_door_call(h, &request, sizeof (request),
2365             &response, sizeof (response));
2366 
2367         if (r < 0) {
2368                 (void) pthread_mutex_unlock(&h->rh_lock);
2369                 DOOR_ERRORS_BLOCK(r);
2370         }
2371         if (response.rpr_response != REP_PROTOCOL_SUCCESS) {
2372                 (void) pthread_mutex_unlock(&h->rh_lock);
2373                 return (scf_set_error(proto_error(response.rpr_response)));
2374         }
2375         iter->iter_sequence++;
2376         (void) pthread_mutex_unlock(&h->rh_lock);
2377         return (SCF_SUCCESS);
2378 }
2379 
2380 static int
2381 datael_setup_iter_pgtyped(scf_iter_t *iter, const scf_datael_t *dp,
2382     const char *pgtype, boolean_t composed)
2383 {
2384         scf_handle_t *h = dp->rd_handle;
2385 
2386         struct rep_protocol_iter_start request;
2387         struct rep_protocol_response response;
2388 
2389         ssize_t r;
2390 
2391         if (h != iter->iter_handle)
2392                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2393 
2394         if (pgtype == NULL || strlcpy(request.rpr_pattern, pgtype,
2395             sizeof (request.rpr_pattern)) >= sizeof (request.rpr_pattern)) {
2396                 scf_iter_reset(iter);
2397                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
2398         }
2399 
2400         (void) pthread_mutex_lock(&h->rh_lock);
2401         request.rpr_request = REP_PROTOCOL_ITER_START;
2402         request.rpr_iterid = iter->iter_id;
2403         request.rpr_entity = dp->rd_entity;
2404         request.rpr_itertype = REP_PROTOCOL_ENTITY_PROPERTYGRP;
2405         request.rpr_flags = RP_ITER_START_PGTYPE |
2406             (composed ? RP_ITER_START_COMPOSED : 0);
2407 
2408         datael_finish_reset(dp);
2409         scf_iter_reset_locked(iter);
2410         iter->iter_type = REP_PROTOCOL_ENTITY_PROPERTYGRP;
2411 
2412         r = make_door_call(h, &request, sizeof (request),
2413             &response, sizeof (response));
2414 
2415         if (r < 0) {
2416                 (void) pthread_mutex_unlock(&h->rh_lock);
2417 
2418                 DOOR_ERRORS_BLOCK(r);
2419         }
2420         if (response.rpr_response != REP_PROTOCOL_SUCCESS) {
2421                 (void) pthread_mutex_unlock(&h->rh_lock);
2422                 return (scf_set_error(proto_error(response.rpr_response)));
2423         }
2424         iter->iter_sequence++;
2425         (void) pthread_mutex_unlock(&h->rh_lock);
2426         return (SCF_SUCCESS);
2427 }
2428 
2429 static int
2430 datael_iter_next(scf_iter_t *iter, scf_datael_t *out)
2431 {
2432         scf_handle_t *h = iter->iter_handle;
2433 
2434         struct rep_protocol_iter_read request;
2435         struct rep_protocol_response response;
2436         ssize_t r;
2437 
2438         if (h != out->rd_handle)
2439                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2440 
2441         (void) pthread_mutex_lock(&h->rh_lock);
2442         if (iter->iter_type == REP_PROTOCOL_ENTITY_NONE ||
2443             iter->iter_sequence == 1) {
2444                 (void) pthread_mutex_unlock(&h->rh_lock);
2445                 return (scf_set_error(SCF_ERROR_NOT_SET));
2446         }
2447 
2448         if (out->rd_type != iter->iter_type) {
2449                 (void) pthread_mutex_unlock(&h->rh_lock);
2450                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
2451         }
2452 
2453         request.rpr_request = REP_PROTOCOL_ITER_READ;
2454         request.rpr_iterid = iter->iter_id;
2455         request.rpr_sequence = iter->iter_sequence;
2456         request.rpr_entityid = out->rd_entity;
2457 
2458         datael_finish_reset(out);
2459         r = make_door_call(h, &request, sizeof (request),
2460             &response, sizeof (response));
2461 
2462         if (r < 0) {
2463                 (void) pthread_mutex_unlock(&h->rh_lock);
2464                 DOOR_ERRORS_BLOCK(r);
2465         }
2466 
2467         if (response.rpr_response == REP_PROTOCOL_DONE) {
2468                 (void) pthread_mutex_unlock(&h->rh_lock);
2469                 return (0);
2470         }
2471         if (response.rpr_response != REP_PROTOCOL_SUCCESS) {
2472                 (void) pthread_mutex_unlock(&h->rh_lock);
2473                 return (scf_set_error(proto_error(response.rpr_response)));
2474         }
2475         iter->iter_sequence++;
2476         (void) pthread_mutex_unlock(&h->rh_lock);
2477 
2478         return (1);
2479 }
2480 
2481 int
2482 scf_iter_scope_services(scf_iter_t *iter, const scf_scope_t *s)
2483 {
2484         return (datael_setup_iter(iter, &s->rd_d,
2485             REP_PROTOCOL_ENTITY_SERVICE, 0));
2486 }
2487 
2488 int
2489 scf_iter_next_service(scf_iter_t *iter, scf_service_t *out)
2490 {
2491         return (datael_iter_next(iter, &out->rd_d));
2492 }
2493 
2494 int
2495 scf_iter_service_instances(scf_iter_t *iter, const scf_service_t *svc)
2496 {
2497         return (datael_setup_iter(iter, &svc->rd_d,
2498             REP_PROTOCOL_ENTITY_INSTANCE, 0));
2499 }
2500 
2501 int
2502 scf_iter_next_instance(scf_iter_t *iter, scf_instance_t *out)
2503 {
2504         return (datael_iter_next(iter, &out->rd_d));
2505 }
2506 
2507 int
2508 scf_iter_service_pgs(scf_iter_t *iter, const scf_service_t *svc)
2509 {
2510         return (datael_setup_iter(iter, &svc->rd_d,
2511             REP_PROTOCOL_ENTITY_PROPERTYGRP, 0));
2512 }
2513 
2514 int
2515 scf_iter_service_pgs_typed(scf_iter_t *iter, const scf_service_t *svc,
2516     const char *type)
2517 {
2518         return (datael_setup_iter_pgtyped(iter, &svc->rd_d, type, 0));
2519 }
2520 
2521 int
2522 scf_iter_instance_snapshots(scf_iter_t *iter, const scf_instance_t *inst)
2523 {
2524         return (datael_setup_iter(iter, &inst->rd_d,
2525             REP_PROTOCOL_ENTITY_SNAPSHOT, 0));
2526 }
2527 
2528 int
2529 scf_iter_next_snapshot(scf_iter_t *iter, scf_snapshot_t *out)
2530 {
2531         return (datael_iter_next(iter, &out->rd_d));
2532 }
2533 
2534 int
2535 scf_iter_instance_pgs(scf_iter_t *iter, const scf_instance_t *inst)
2536 {
2537         return (datael_setup_iter(iter, &inst->rd_d,
2538             REP_PROTOCOL_ENTITY_PROPERTYGRP, 0));
2539 }
2540 
2541 int
2542 scf_iter_instance_pgs_typed(scf_iter_t *iter, const scf_instance_t *inst,
2543     const char *type)
2544 {
2545         return (datael_setup_iter_pgtyped(iter, &inst->rd_d, type, 0));
2546 }
2547 
2548 int
2549 scf_iter_instance_pgs_composed(scf_iter_t *iter, const scf_instance_t *inst,
2550     const scf_snapshot_t *snap)
2551 {
2552         if (snap != NULL && inst->rd_d.rd_handle != snap->rd_d.rd_handle)
2553                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2554 
2555         return (datael_setup_iter(iter, snap ? &snap->rd_d : &inst->rd_d,
2556             REP_PROTOCOL_ENTITY_PROPERTYGRP, 1));
2557 }
2558 
2559 int
2560 scf_iter_instance_pgs_typed_composed(scf_iter_t *iter,
2561     const scf_instance_t *inst, const scf_snapshot_t *snap, const char *type)
2562 {
2563         if (snap != NULL && inst->rd_d.rd_handle != snap->rd_d.rd_handle)
2564                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2565 
2566         return (datael_setup_iter_pgtyped(iter,
2567             snap ? &snap->rd_d : &inst->rd_d, type, 1));
2568 }
2569 
2570 int
2571 scf_iter_snaplevel_pgs(scf_iter_t *iter, const scf_snaplevel_t *inst)
2572 {
2573         return (datael_setup_iter(iter, &inst->rd_d,
2574             REP_PROTOCOL_ENTITY_PROPERTYGRP, 0));
2575 }
2576 
2577 int
2578 scf_iter_snaplevel_pgs_typed(scf_iter_t *iter, const scf_snaplevel_t *inst,
2579     const char *type)
2580 {
2581         return (datael_setup_iter_pgtyped(iter, &inst->rd_d, type, 0));
2582 }
2583 
2584 int
2585 scf_iter_next_pg(scf_iter_t *iter, scf_propertygroup_t *out)
2586 {
2587         return (datael_iter_next(iter, &out->rd_d));
2588 }
2589 
2590 int
2591 scf_iter_pg_properties(scf_iter_t *iter, const scf_propertygroup_t *pg)
2592 {
2593         return (datael_setup_iter(iter, &pg->rd_d,
2594             REP_PROTOCOL_ENTITY_PROPERTY, 0));
2595 }
2596 
2597 int
2598 scf_iter_next_property(scf_iter_t *iter, scf_property_t *out)
2599 {
2600         return (datael_iter_next(iter, &out->rd_d));
2601 }
2602 
2603 /*
2604  * Fails with
2605  *   _INVALID_ARGUMENT - handle is NULL
2606  *   _INTERNAL - server response too big
2607  *               entity already set up with different type
2608  *   _NO_RESOURCES
2609  *   _NO_MEMORY
2610  */
2611 scf_scope_t *
2612 scf_scope_create(scf_handle_t *handle)
2613 {
2614         scf_scope_t *ret;
2615 
2616         ret = uu_zalloc(sizeof (*ret));
2617         if (ret != NULL) {
2618                 if (datael_init(&ret->rd_d, handle,
2619                     REP_PROTOCOL_ENTITY_SCOPE) == -1) {
2620                         uu_free(ret);
2621                         return (NULL);
2622                 }
2623         } else {
2624                 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2625         }
2626 
2627         return (ret);
2628 }
2629 
2630 scf_handle_t *
2631 scf_scope_handle(const scf_scope_t *val)
2632 {
2633         return (datael_handle(&val->rd_d));
2634 }
2635 
2636 void
2637 scf_scope_destroy(scf_scope_t *val)
2638 {
2639         if (val == NULL)
2640                 return;
2641 
2642         datael_destroy(&val->rd_d);
2643         uu_free(val);
2644 }
2645 
2646 ssize_t
2647 scf_scope_get_name(const scf_scope_t *rep, char *out, size_t len)
2648 {
2649         return (datael_get_name(&rep->rd_d, out, len, RP_ENTITY_NAME_NAME));
2650 }
2651 
2652 /*ARGSUSED*/
2653 int
2654 scf_scope_get_parent(const scf_scope_t *child, scf_scope_t *parent)
2655 {
2656         char name[1];
2657 
2658         /* fake up the side-effects */
2659         datael_reset(&parent->rd_d);
2660         if (scf_scope_get_name(child, name, sizeof (name)) < 0)
2661                 return (-1);
2662         return (scf_set_error(SCF_ERROR_NOT_FOUND));
2663 }
2664 
2665 /*
2666  * Fails with _INVALID_ARGUMENT (handle is NULL), _HANDLE_DESTROYED, _INTERNAL
2667  * (bad server response or id in use), _NO_RESOURCES, or _NO_MEMORY.
2668  */
2669 scf_service_t *
2670 scf_service_create(scf_handle_t *handle)
2671 {
2672         scf_service_t *ret;
2673         ret = uu_zalloc(sizeof (*ret));
2674         if (ret != NULL) {
2675                 if (datael_init(&ret->rd_d, handle,
2676                     REP_PROTOCOL_ENTITY_SERVICE) == -1) {
2677                         uu_free(ret);
2678                         return (NULL);
2679                 }
2680         } else {
2681                 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2682         }
2683 
2684         return (ret);
2685 }
2686 
2687 
2688 /*
2689  * Fails with
2690  *   _HANDLE_MISMATCH
2691  *   _INVALID_ARGUMENT
2692  *   _NOT_BOUND
2693  *   _CONNECTION_BROKEN
2694  *   _INTERNAL
2695  *   _EXISTS
2696  *   _DELETED
2697  *   _NOT_SET
2698  *   _NO_RESOURCES
2699  *   _PERMISSION_DENIED
2700  *   _BACKEND_ACCESS
2701  *   _BACKEND_READONLY
2702  */
2703 int
2704 scf_scope_add_service(const scf_scope_t *scope, const char *name,
2705     scf_service_t *svc)
2706 {
2707         return (datael_add_child(&scope->rd_d, name,
2708             REP_PROTOCOL_ENTITY_SERVICE, (svc != NULL)? &svc->rd_d : NULL));
2709 }
2710 
2711 /*
2712  * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
2713  * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
2714  * _BACKEND_ACCESS, _NOT_FOUND.
2715  */
2716 int
2717 scf_scope_get_service(const scf_scope_t *s, const char *name,
2718     scf_service_t *svc)
2719 {
2720         return (datael_get_child(&s->rd_d, name, REP_PROTOCOL_ENTITY_SERVICE,
2721             svc ? &svc->rd_d : NULL, 0));
2722 }
2723 
2724 scf_handle_t *
2725 scf_service_handle(const scf_service_t *val)
2726 {
2727         return (datael_handle(&val->rd_d));
2728 }
2729 
2730 int
2731 scf_service_delete(scf_service_t *svc)
2732 {
2733         return (datael_delete(&svc->rd_d));
2734 }
2735 
2736 int
2737 scf_instance_delete(scf_instance_t *inst)
2738 {
2739         return (datael_delete(&inst->rd_d));
2740 }
2741 
2742 int
2743 scf_pg_delete(scf_propertygroup_t *pg)
2744 {
2745         return (datael_delete(&pg->rd_d));
2746 }
2747 
2748 int
2749 _scf_snapshot_delete(scf_snapshot_t *snap)
2750 {
2751         return (datael_delete(&snap->rd_d));
2752 }
2753 
2754 /*
2755  * Fails with
2756  *   _HANDLE_MISMATCH
2757  *   _INVALID_ARGUMENT
2758  *   _NOT_BOUND
2759  *   _CONNECTION_BROKEN
2760  *   _INTERNAL
2761  *   _EXISTS
2762  *   _DELETED
2763  *   _NOT_SET
2764  *   _NO_RESOURCES
2765  *   _PERMISSION_DENIED
2766  *   _BACKEND_ACCESS
2767  *   _BACKEND_READONLY
2768  */
2769 int
2770 scf_service_add_instance(const scf_service_t *svc, const char *name,
2771     scf_instance_t *instance)
2772 {
2773         return (datael_add_child(&svc->rd_d, name,
2774             REP_PROTOCOL_ENTITY_INSTANCE,
2775             (instance != NULL)? &instance->rd_d : NULL));
2776 }
2777 
2778 
2779 /*
2780  * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
2781  * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
2782  * _BACKEND_ACCESS, _NOT_FOUND.
2783  */
2784 int
2785 scf_service_get_instance(const scf_service_t *svc, const char *name,
2786     scf_instance_t *inst)
2787 {
2788         return (datael_get_child(&svc->rd_d, name, REP_PROTOCOL_ENTITY_INSTANCE,
2789             inst ? &inst->rd_d : NULL, 0));
2790 }
2791 
2792 int
2793 scf_service_add_pg(const scf_service_t *svc, const char *name,
2794     const char *type, uint32_t flags, scf_propertygroup_t *pg)
2795 {
2796         return (datael_add_pg(&svc->rd_d, name, type, flags,
2797             (pg != NULL)?&pg->rd_d : NULL));
2798 }
2799 
2800 /*
2801  * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
2802  * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
2803  * _BACKEND_ACCESS, _NOT_FOUND.
2804  */
2805 int
2806 scf_service_get_pg(const scf_service_t *svc, const char *name,
2807     scf_propertygroup_t *pg)
2808 {
2809         return (datael_get_child(&svc->rd_d, name,
2810             REP_PROTOCOL_ENTITY_PROPERTYGRP, pg ? &pg->rd_d : NULL, 0));
2811 }
2812 
2813 int
2814 scf_instance_add_pg(const scf_instance_t *inst, const char *name,
2815     const char *type, uint32_t flags, scf_propertygroup_t *pg)
2816 {
2817         return (datael_add_pg(&inst->rd_d, name, type, flags,
2818             (pg != NULL)?&pg->rd_d : NULL));
2819 }
2820 
2821 /*
2822  * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
2823  * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
2824  * _BACKEND_ACCESS, _NOT_FOUND.
2825  */
2826 int
2827 scf_instance_get_snapshot(const scf_instance_t *inst, const char *name,
2828     scf_snapshot_t *pg)
2829 {
2830         return (datael_get_child(&inst->rd_d, name,
2831             REP_PROTOCOL_ENTITY_SNAPSHOT, pg ? &pg->rd_d : NULL, 0));
2832 }
2833 
2834 /*
2835  * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
2836  * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
2837  * _BACKEND_ACCESS, _NOT_FOUND.
2838  */
2839 int
2840 scf_instance_get_pg(const scf_instance_t *inst, const char *name,
2841     scf_propertygroup_t *pg)
2842 {
2843         return (datael_get_child(&inst->rd_d, name,
2844             REP_PROTOCOL_ENTITY_PROPERTYGRP, pg ? &pg->rd_d : NULL, 0));
2845 }
2846 
2847 /*
2848  * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
2849  * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
2850  * _BACKEND_ACCESS, _NOT_FOUND.
2851  */
2852 int
2853 scf_instance_get_pg_composed(const scf_instance_t *inst,
2854     const scf_snapshot_t *snap, const char *name, scf_propertygroup_t *pg)
2855 {
2856         if (snap != NULL && inst->rd_d.rd_handle != snap->rd_d.rd_handle)
2857                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2858 
2859         return (datael_get_child(snap ? &snap->rd_d : &inst->rd_d, name,
2860             REP_PROTOCOL_ENTITY_PROPERTYGRP, pg ? &pg->rd_d : NULL, 1));
2861 }
2862 
2863 /*
2864  * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
2865  * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
2866  * _BACKEND_ACCESS, _NOT_FOUND.
2867  */
2868 int
2869 scf_pg_get_property(const scf_propertygroup_t *pg, const char *name,
2870     scf_property_t *prop)
2871 {
2872         return (datael_get_child(&pg->rd_d, name, REP_PROTOCOL_ENTITY_PROPERTY,
2873             prop ? &prop->rd_d : NULL, 0));
2874 }
2875 
2876 void
2877 scf_service_destroy(scf_service_t *val)
2878 {
2879         if (val == NULL)
2880                 return;
2881 
2882         datael_destroy(&val->rd_d);
2883         uu_free(val);
2884 }
2885 
2886 ssize_t
2887 scf_service_get_name(const scf_service_t *rep, char *out, size_t len)
2888 {
2889         return (datael_get_name(&rep->rd_d, out, len, RP_ENTITY_NAME_NAME));
2890 }
2891 
2892 /*
2893  * Fails with _INVALID_ARGUMENT (handle is NULL), _HANDLE_DESTROYED, _INTERNAL
2894  * (bad server response or id in use), _NO_RESOURCES, or _NO_MEMORY.
2895  */
2896 scf_instance_t *
2897 scf_instance_create(scf_handle_t *handle)
2898 {
2899         scf_instance_t *ret;
2900 
2901         ret = uu_zalloc(sizeof (*ret));
2902         if (ret != NULL) {
2903                 if (datael_init(&ret->rd_d, handle,
2904                     REP_PROTOCOL_ENTITY_INSTANCE) == -1) {
2905                         uu_free(ret);
2906                         return (NULL);
2907                 }
2908         } else {
2909                 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2910         }
2911 
2912         return (ret);
2913 }
2914 
2915 scf_handle_t *
2916 scf_instance_handle(const scf_instance_t *val)
2917 {
2918         return (datael_handle(&val->rd_d));
2919 }
2920 
2921 void
2922 scf_instance_destroy(scf_instance_t *val)
2923 {
2924         if (val == NULL)
2925                 return;
2926 
2927         datael_destroy(&val->rd_d);
2928         uu_free(val);
2929 }
2930 
2931 ssize_t
2932 scf_instance_get_name(const scf_instance_t *rep, char *out, size_t len)
2933 {
2934         return (datael_get_name(&rep->rd_d, out, len, RP_ENTITY_NAME_NAME));
2935 }
2936 
2937 /*
2938  * Fails with _INVALID_ARGUMENT (handle is NULL), _HANDLE_DESTROYED, _INTERNAL
2939  * (bad server response or id in use), _NO_RESOURCES, or _NO_MEMORY.
2940  */
2941 scf_snapshot_t *
2942 scf_snapshot_create(scf_handle_t *handle)
2943 {
2944         scf_snapshot_t *ret;
2945 
2946         ret = uu_zalloc(sizeof (*ret));
2947         if (ret != NULL) {
2948                 if (datael_init(&ret->rd_d, handle,
2949                     REP_PROTOCOL_ENTITY_SNAPSHOT) == -1) {
2950                         uu_free(ret);
2951                         return (NULL);
2952                 }
2953         } else {
2954                 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
2955         }
2956 
2957         return (ret);
2958 }
2959 
2960 scf_handle_t *
2961 scf_snapshot_handle(const scf_snapshot_t *val)
2962 {
2963         return (datael_handle(&val->rd_d));
2964 }
2965 
2966 void
2967 scf_snapshot_destroy(scf_snapshot_t *val)
2968 {
2969         if (val == NULL)
2970                 return;
2971 
2972         datael_destroy(&val->rd_d);
2973         uu_free(val);
2974 }
2975 
2976 ssize_t
2977 scf_snapshot_get_name(const scf_snapshot_t *rep, char *out, size_t len)
2978 {
2979         return (datael_get_name(&rep->rd_d, out, len, RP_ENTITY_NAME_NAME));
2980 }
2981 
2982 /*
2983  * Fails with _INVALID_ARGUMENT (handle is NULL), _HANDLE_DESTROYED, _INTERNAL
2984  * (bad server response or id in use), _NO_RESOURCES, _NO_MEMORY.
2985  */
2986 scf_snaplevel_t *
2987 scf_snaplevel_create(scf_handle_t *handle)
2988 {
2989         scf_snaplevel_t *ret;
2990 
2991         ret = uu_zalloc(sizeof (*ret));
2992         if (ret != NULL) {
2993                 if (datael_init(&ret->rd_d, handle,
2994                     REP_PROTOCOL_ENTITY_SNAPLEVEL) == -1) {
2995                         uu_free(ret);
2996                         return (NULL);
2997                 }
2998         } else {
2999                 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
3000         }
3001 
3002         return (ret);
3003 }
3004 
3005 scf_handle_t *
3006 scf_snaplevel_handle(const scf_snaplevel_t *val)
3007 {
3008         return (datael_handle(&val->rd_d));
3009 }
3010 
3011 void
3012 scf_snaplevel_destroy(scf_snaplevel_t *val)
3013 {
3014         if (val == NULL)
3015                 return;
3016 
3017         datael_destroy(&val->rd_d);
3018         uu_free(val);
3019 }
3020 
3021 ssize_t
3022 scf_snaplevel_get_scope_name(const scf_snaplevel_t *rep, char *out, size_t len)
3023 {
3024         return (datael_get_name(&rep->rd_d, out, len,
3025             RP_ENTITY_NAME_SNAPLEVEL_SCOPE));
3026 }
3027 
3028 ssize_t
3029 scf_snaplevel_get_service_name(const scf_snaplevel_t *rep, char *out,
3030     size_t len)
3031 {
3032         return (datael_get_name(&rep->rd_d, out, len,
3033             RP_ENTITY_NAME_SNAPLEVEL_SERVICE));
3034 }
3035 
3036 ssize_t
3037 scf_snaplevel_get_instance_name(const scf_snaplevel_t *rep, char *out,
3038     size_t len)
3039 {
3040         return (datael_get_name(&rep->rd_d, out, len,
3041             RP_ENTITY_NAME_SNAPLEVEL_INSTANCE));
3042 }
3043 
3044 /*
3045  * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
3046  * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
3047  * _BACKEND_ACCESS, _NOT_FOUND.
3048  */
3049 int
3050 scf_snaplevel_get_pg(const scf_snaplevel_t *snap, const char *name,
3051     scf_propertygroup_t *pg)
3052 {
3053         return (datael_get_child(&snap->rd_d, name,
3054             REP_PROTOCOL_ENTITY_PROPERTYGRP, pg ? &pg->rd_d : NULL, 0));
3055 }
3056 
3057 static int
3058 snaplevel_next(const scf_datael_t *src, scf_snaplevel_t *dst_arg)
3059 {
3060         scf_handle_t *h = src->rd_handle;
3061         scf_snaplevel_t *dst = dst_arg;
3062         struct rep_protocol_entity_pair request;
3063         struct rep_protocol_response response;
3064         int r;
3065         int dups = 0;
3066 
3067         if (h != dst->rd_d.rd_handle)
3068                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
3069 
3070         if (src == &dst->rd_d) {
3071                 dups = 1;
3072                 dst = HANDLE_HOLD_SNAPLVL(h);
3073         }
3074         (void) pthread_mutex_lock(&h->rh_lock);
3075         request.rpr_request = REP_PROTOCOL_NEXT_SNAPLEVEL;
3076         request.rpr_entity_src = src->rd_entity;
3077         request.rpr_entity_dst = dst->rd_d.rd_entity;
3078 
3079         datael_finish_reset(src);
3080         datael_finish_reset(&dst->rd_d);
3081         r = make_door_call(h, &request, sizeof (request),
3082             &response, sizeof (response));
3083         /*
3084          * if we succeeded, we need to swap dst and dst_arg's identity.  We
3085          * take advantage of the fact that the only in-library knowledge is
3086          * their entity ids.
3087          */
3088         if (dups && r >= 0 &&
3089             (response.rpr_response == REP_PROTOCOL_SUCCESS ||
3090             response.rpr_response == REP_PROTOCOL_DONE)) {
3091                 int entity = dst->rd_d.rd_entity;
3092 
3093                 dst->rd_d.rd_entity = dst_arg->rd_d.rd_entity;
3094                 dst_arg->rd_d.rd_entity = entity;
3095         }
3096         (void) pthread_mutex_unlock(&h->rh_lock);
3097 
3098         if (dups)
3099                 HANDLE_RELE_SNAPLVL(h);
3100 
3101         if (r < 0)
3102                 DOOR_ERRORS_BLOCK(r);
3103 
3104         if (response.rpr_response != REP_PROTOCOL_SUCCESS &&
3105             response.rpr_response != REP_PROTOCOL_DONE) {
3106                 return (scf_set_error(proto_error(response.rpr_response)));
3107         }
3108 
3109         return (response.rpr_response == REP_PROTOCOL_SUCCESS) ?
3110             SCF_SUCCESS : SCF_COMPLETE;
3111 }
3112 
3113 int scf_snapshot_get_base_snaplevel(const scf_snapshot_t *base,
3114     scf_snaplevel_t *out)
3115 {
3116         return (snaplevel_next(&base->rd_d, out));
3117 }
3118 
3119 int scf_snaplevel_get_next_snaplevel(const scf_snaplevel_t *base,
3120     scf_snaplevel_t *out)
3121 {
3122         return (snaplevel_next(&base->rd_d, out));
3123 }
3124 
3125 /*
3126  * Fails with _INVALID_ARGUMENT (handle is NULL), _HANDLE_DESTROYED, _INTERNAL
3127  * (bad server response or id in use), _NO_RESOURCES, or _NO_MEMORY.
3128  */
3129 scf_propertygroup_t *
3130 scf_pg_create(scf_handle_t *handle)
3131 {
3132         scf_propertygroup_t *ret;
3133         ret = uu_zalloc(sizeof (*ret));
3134         if (ret != NULL) {
3135                 if (datael_init(&ret->rd_d, handle,
3136                     REP_PROTOCOL_ENTITY_PROPERTYGRP) == -1) {
3137                         uu_free(ret);
3138                         return (NULL);
3139                 }
3140         } else {
3141                 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
3142         }
3143 
3144         return (ret);
3145 }
3146 
3147 scf_handle_t *
3148 scf_pg_handle(const scf_propertygroup_t *val)
3149 {
3150         return (datael_handle(&val->rd_d));
3151 }
3152 
3153 void
3154 scf_pg_destroy(scf_propertygroup_t *val)
3155 {
3156         if (val == NULL)
3157                 return;
3158 
3159         datael_destroy(&val->rd_d);
3160         uu_free(val);
3161 }
3162 
3163 ssize_t
3164 scf_pg_get_name(const scf_propertygroup_t *pg,  char *out, size_t len)
3165 {
3166         return (datael_get_name(&pg->rd_d, out, len, RP_ENTITY_NAME_NAME));
3167 }
3168 
3169 ssize_t
3170 scf_pg_get_type(const scf_propertygroup_t *pg,  char *out, size_t len)
3171 {
3172         return (datael_get_name(&pg->rd_d, out, len, RP_ENTITY_NAME_PGTYPE));
3173 }
3174 
3175 int
3176 scf_pg_get_flags(const scf_propertygroup_t *pg, uint32_t *out)
3177 {
3178         char buf[REP_PROTOCOL_NAME_LEN];
3179         ssize_t res;
3180 
3181         res = datael_get_name(&pg->rd_d, buf, sizeof (buf),
3182             RP_ENTITY_NAME_PGFLAGS);
3183 
3184         if (res == -1)
3185                 return (-1);
3186 
3187         if (uu_strtouint(buf, out, sizeof (*out), 0, 0, UINT32_MAX) == -1)
3188                 return (scf_set_error(SCF_ERROR_INTERNAL));
3189 
3190         return (0);
3191 }
3192 
3193 static int
3194 datael_update(scf_datael_t *dp)
3195 {
3196         scf_handle_t *h = dp->rd_handle;
3197 
3198         struct rep_protocol_entity_update request;
3199         struct rep_protocol_response response;
3200 
3201         int r;
3202 
3203         (void) pthread_mutex_lock(&h->rh_lock);
3204         request.rpr_request = REP_PROTOCOL_ENTITY_UPDATE;
3205         request.rpr_entityid = dp->rd_entity;
3206 
3207         datael_finish_reset(dp);
3208         request.rpr_changeid = handle_next_changeid(h);
3209 
3210         r = make_door_call(h, &request, sizeof (request),
3211             &response, sizeof (response));
3212         (void) pthread_mutex_unlock(&h->rh_lock);
3213 
3214         if (r < 0)
3215                 DOOR_ERRORS_BLOCK(r);
3216 
3217         /*
3218          * This should never happen but if it does something has
3219          * gone terribly wrong and we should abort.
3220          */
3221         if (response.rpr_response == REP_PROTOCOL_FAIL_BAD_REQUEST)
3222                 abort();
3223 
3224         if (response.rpr_response != REP_PROTOCOL_SUCCESS &&
3225             response.rpr_response != REP_PROTOCOL_DONE) {
3226                 return (scf_set_error(proto_error(response.rpr_response)));
3227         }
3228 
3229         return (response.rpr_response == REP_PROTOCOL_SUCCESS) ?
3230             SCF_SUCCESS : SCF_COMPLETE;
3231 }
3232 
3233 int
3234 scf_pg_update(scf_propertygroup_t *pg)
3235 {
3236         return (datael_update(&pg->rd_d));
3237 }
3238 
3239 int
3240 scf_snapshot_update(scf_snapshot_t *snap)
3241 {
3242         return (datael_update(&snap->rd_d));
3243 }
3244 
3245 int
3246 _scf_pg_wait(scf_propertygroup_t *pg, int timeout)
3247 {
3248         scf_handle_t *h = pg->rd_d.rd_handle;
3249 
3250         struct rep_protocol_propertygrp_request request;
3251         struct rep_protocol_response response;
3252 
3253         struct pollfd pollfd;
3254 
3255         int r;
3256 
3257         (void) pthread_mutex_lock(&h->rh_lock);
3258         request.rpr_request = REP_PROTOCOL_PROPERTYGRP_SETUP_WAIT;
3259         request.rpr_entityid = pg->rd_d.rd_entity;
3260 
3261         datael_finish_reset(&pg->rd_d);
3262         if (!handle_is_bound(h)) {
3263                 (void) pthread_mutex_unlock(&h->rh_lock);
3264                 return (scf_set_error(SCF_ERROR_CONNECTION_BROKEN));
3265         }
3266         r = make_door_call_retfd(h->rh_doorfd, &request, sizeof (request),
3267             &response, sizeof (response), &pollfd.fd);
3268         (void) pthread_mutex_unlock(&h->rh_lock);
3269 
3270         if (r < 0)
3271                 DOOR_ERRORS_BLOCK(r);
3272 
3273         assert((response.rpr_response == REP_PROTOCOL_SUCCESS) ==
3274             (pollfd.fd != -1));
3275 
3276         if (response.rpr_response == REP_PROTOCOL_FAIL_NOT_LATEST)
3277                 return (SCF_SUCCESS);
3278 
3279         if (response.rpr_response != REP_PROTOCOL_SUCCESS)
3280                 return (scf_set_error(proto_error(response.rpr_response)));
3281 
3282         pollfd.events = 0;
3283         pollfd.revents = 0;
3284 
3285         r = poll(&pollfd, 1, timeout * MILLISEC);
3286 
3287         (void) close(pollfd.fd);
3288         return (pollfd.revents ? SCF_SUCCESS : SCF_COMPLETE);
3289 }
3290 
3291 static int
3292 scf_notify_add_pattern(scf_handle_t *h, int type, const char *name)
3293 {
3294         struct rep_protocol_notify_request request;
3295         struct rep_protocol_response response;
3296         int r;
3297 
3298         (void) pthread_mutex_lock(&h->rh_lock);
3299         request.rpr_request = REP_PROTOCOL_CLIENT_ADD_NOTIFY;
3300         request.rpr_type = type;
3301         (void) strlcpy(request.rpr_pattern, name, sizeof (request.rpr_pattern));
3302 
3303         r = make_door_call(h, &request, sizeof (request),
3304             &response, sizeof (response));
3305         (void) pthread_mutex_unlock(&h->rh_lock);
3306 
3307         if (r < 0)
3308                 DOOR_ERRORS_BLOCK(r);
3309 
3310         if (response.rpr_response != REP_PROTOCOL_SUCCESS)
3311                 return (scf_set_error(proto_error(response.rpr_response)));
3312 
3313         return (SCF_SUCCESS);
3314 }
3315 
3316 int
3317 _scf_notify_add_pgname(scf_handle_t *h, const char *name)
3318 {
3319         return (scf_notify_add_pattern(h, REP_PROTOCOL_NOTIFY_PGNAME, name));
3320 }
3321 
3322 int
3323 _scf_notify_add_pgtype(scf_handle_t *h, const char *type)
3324 {
3325         return (scf_notify_add_pattern(h, REP_PROTOCOL_NOTIFY_PGTYPE, type));
3326 }
3327 
3328 int
3329 _scf_notify_wait(scf_propertygroup_t *pg, char *out, size_t sz)
3330 {
3331         struct rep_protocol_wait_request request;
3332         struct rep_protocol_fmri_response response;
3333 
3334         scf_handle_t *h = pg->rd_d.rd_handle;
3335         int dummy;
3336         int fd;
3337         int r;
3338 
3339         (void) pthread_mutex_lock(&h->rh_lock);
3340         datael_finish_reset(&pg->rd_d);
3341         if (!handle_is_bound(h)) {
3342                 (void) pthread_mutex_unlock(&h->rh_lock);
3343                 return (scf_set_error(SCF_ERROR_CONNECTION_BROKEN));
3344         }
3345         fd = h->rh_doorfd;
3346         ++h->rh_fd_users;
3347         assert(h->rh_fd_users > 0);
3348 
3349         request.rpr_request = REP_PROTOCOL_CLIENT_WAIT;
3350         request.rpr_entityid = pg->rd_d.rd_entity;
3351         (void) pthread_mutex_unlock(&h->rh_lock);
3352 
3353         r = make_door_call_retfd(fd, &request, sizeof (request),
3354             &response, sizeof (response), &dummy);
3355 
3356         (void) pthread_mutex_lock(&h->rh_lock);
3357         assert(h->rh_fd_users > 0);
3358         if (--h->rh_fd_users == 0) {
3359                 (void) pthread_cond_broadcast(&h->rh_cv);
3360                 /*
3361                  * check for a delayed close, now that there are no other
3362                  * users.
3363                  */
3364                 if (h->rh_doorfd_old != -1) {
3365                         assert(h->rh_doorfd == -1);
3366                         assert(fd == h->rh_doorfd_old);
3367                         (void) close(h->rh_doorfd_old);
3368                         h->rh_doorfd_old = -1;
3369                 }
3370         }
3371         handle_unrefed(h);                      /* drops h->rh_lock */
3372 
3373         if (r < 0)
3374                 DOOR_ERRORS_BLOCK(r);
3375 
3376         if (response.rpr_response == REP_PROTOCOL_DONE)
3377                 return (scf_set_error(SCF_ERROR_NOT_SET));
3378 
3379         if (response.rpr_response != REP_PROTOCOL_SUCCESS)
3380                 return (scf_set_error(proto_error(response.rpr_response)));
3381 
3382         /* the following will be non-zero for delete notifications */
3383         return (strlcpy(out, response.rpr_fmri, sz));
3384 }
3385 
3386 static int
3387 _scf_snapshot_take(scf_instance_t *inst, const char *name,
3388     scf_snapshot_t *snap, int flags)
3389 {
3390         scf_handle_t *h = inst->rd_d.rd_handle;
3391 
3392         struct rep_protocol_snapshot_take request;
3393         struct rep_protocol_response response;
3394 
3395         int r;
3396 
3397         if (h != snap->rd_d.rd_handle)
3398                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
3399 
3400         if (strlcpy(request.rpr_name, (name != NULL)? name : "",
3401             sizeof (request.rpr_name)) >= sizeof (request.rpr_name))
3402                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
3403 
3404         (void) pthread_mutex_lock(&h->rh_lock);
3405         request.rpr_request = REP_PROTOCOL_SNAPSHOT_TAKE;
3406         request.rpr_entityid_src = inst->rd_d.rd_entity;
3407         request.rpr_entityid_dest = snap->rd_d.rd_entity;
3408         request.rpr_flags = flags;
3409 
3410         datael_finish_reset(&inst->rd_d);
3411         datael_finish_reset(&snap->rd_d);
3412 
3413         r = make_door_call(h, &request, sizeof (request),
3414             &response, sizeof (response));
3415         (void) pthread_mutex_unlock(&h->rh_lock);
3416 
3417         if (r < 0)
3418                 DOOR_ERRORS_BLOCK(r);
3419 
3420         if (response.rpr_response != REP_PROTOCOL_SUCCESS)
3421                 return (scf_set_error(proto_error(response.rpr_response)));
3422 
3423         return (SCF_SUCCESS);
3424 }
3425 
3426 int
3427 _scf_snapshot_take_new_named(scf_instance_t *inst,
3428     const char *svcname, const char *instname, const char *snapname,
3429     scf_snapshot_t *snap)
3430 {
3431         scf_handle_t *h = inst->rd_d.rd_handle;
3432 
3433         struct rep_protocol_snapshot_take_named request;
3434         struct rep_protocol_response response;
3435 
3436         int r;
3437 
3438         if (h != snap->rd_d.rd_handle)
3439                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
3440 
3441         if (strlcpy(request.rpr_svcname, svcname,
3442             sizeof (request.rpr_svcname)) >= sizeof (request.rpr_svcname))
3443                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
3444 
3445         if (strlcpy(request.rpr_instname, instname,
3446             sizeof (request.rpr_instname)) >= sizeof (request.rpr_instname))
3447                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
3448 
3449         if (strlcpy(request.rpr_name, snapname,
3450             sizeof (request.rpr_name)) >= sizeof (request.rpr_name))
3451                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
3452 
3453         (void) pthread_mutex_lock(&h->rh_lock);
3454         request.rpr_request = REP_PROTOCOL_SNAPSHOT_TAKE_NAMED;
3455         request.rpr_entityid_src = inst->rd_d.rd_entity;
3456         request.rpr_entityid_dest = snap->rd_d.rd_entity;
3457 
3458         datael_finish_reset(&inst->rd_d);
3459         datael_finish_reset(&snap->rd_d);
3460 
3461         r = make_door_call(h, &request, sizeof (request),
3462             &response, sizeof (response));
3463         (void) pthread_mutex_unlock(&h->rh_lock);
3464 
3465         if (r < 0)
3466                 DOOR_ERRORS_BLOCK(r);
3467 
3468         if (response.rpr_response != REP_PROTOCOL_SUCCESS) {
3469                 assert(response.rpr_response !=
3470                     REP_PROTOCOL_FAIL_TYPE_MISMATCH);
3471                 return (scf_set_error(proto_error(response.rpr_response)));
3472         }
3473 
3474         return (SCF_SUCCESS);
3475 }
3476 
3477 int
3478 _scf_snapshot_take_new(scf_instance_t *inst, const char *name,
3479     scf_snapshot_t *snap)
3480 {
3481         return (_scf_snapshot_take(inst, name, snap, REP_SNAPSHOT_NEW));
3482 }
3483 
3484 int
3485 _scf_snapshot_take_attach(scf_instance_t *inst, scf_snapshot_t *snap)
3486 {
3487         return (_scf_snapshot_take(inst, NULL, snap, REP_SNAPSHOT_ATTACH));
3488 }
3489 
3490 int
3491 _scf_snapshot_attach(scf_snapshot_t *src, scf_snapshot_t *dest)
3492 {
3493         scf_handle_t *h = dest->rd_d.rd_handle;
3494 
3495         struct rep_protocol_snapshot_attach request;
3496         struct rep_protocol_response response;
3497 
3498         int r;
3499 
3500         if (h != src->rd_d.rd_handle)
3501                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
3502 
3503         (void) pthread_mutex_lock(&h->rh_lock);
3504         request.rpr_request = REP_PROTOCOL_SNAPSHOT_ATTACH;
3505         request.rpr_entityid_src = src->rd_d.rd_entity;
3506         request.rpr_entityid_dest = dest->rd_d.rd_entity;
3507 
3508         datael_finish_reset(&src->rd_d);
3509         datael_finish_reset(&dest->rd_d);
3510 
3511         r = make_door_call(h, &request, sizeof (request),
3512             &response, sizeof (response));
3513         (void) pthread_mutex_unlock(&h->rh_lock);
3514 
3515         if (r < 0)
3516                 DOOR_ERRORS_BLOCK(r);
3517 
3518         if (response.rpr_response != REP_PROTOCOL_SUCCESS)
3519                 return (scf_set_error(proto_error(response.rpr_response)));
3520 
3521         return (SCF_SUCCESS);
3522 }
3523 
3524 /*
3525  * Fails with _INVALID_ARGUMENT (handle is NULL), _HANDLE_DESTROYED, _INTERNAL
3526  * (bad server response or id in use), _NO_RESOURCES, or _NO_MEMORY.
3527  */
3528 scf_property_t *
3529 scf_property_create(scf_handle_t *handle)
3530 {
3531         scf_property_t *ret;
3532         ret = uu_zalloc(sizeof (*ret));
3533         if (ret != NULL) {
3534                 if (datael_init(&ret->rd_d, handle,
3535                     REP_PROTOCOL_ENTITY_PROPERTY) == -1) {
3536                         uu_free(ret);
3537                         return (NULL);
3538                 }
3539         } else {
3540                 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
3541         }
3542 
3543         return (ret);
3544 }
3545 
3546 scf_handle_t *
3547 scf_property_handle(const scf_property_t *val)
3548 {
3549         return (datael_handle(&val->rd_d));
3550 }
3551 
3552 void
3553 scf_property_destroy(scf_property_t *val)
3554 {
3555         if (val == NULL)
3556                 return;
3557 
3558         datael_destroy(&val->rd_d);
3559         uu_free(val);
3560 }
3561 
3562 static int
3563 property_type_locked(const scf_property_t *prop,
3564     rep_protocol_value_type_t *out)
3565 {
3566         scf_handle_t *h = prop->rd_d.rd_handle;
3567 
3568         struct rep_protocol_property_request request;
3569         struct rep_protocol_integer_response response;
3570 
3571         int r;
3572 
3573         assert(MUTEX_HELD(&h->rh_lock));
3574 
3575         request.rpr_request = REP_PROTOCOL_PROPERTY_GET_TYPE;
3576         request.rpr_entityid = prop->rd_d.rd_entity;
3577 
3578         datael_finish_reset(&prop->rd_d);
3579         r = make_door_call(h, &request, sizeof (request),
3580             &response, sizeof (response));
3581 
3582         if (r < 0)
3583                 DOOR_ERRORS_BLOCK(r);
3584 
3585         if (response.rpr_response != REP_PROTOCOL_SUCCESS ||
3586             r < sizeof (response)) {
3587                 return (scf_set_error(proto_error(response.rpr_response)));
3588         }
3589         *out = response.rpr_value;
3590         return (SCF_SUCCESS);
3591 }
3592 
3593 int
3594 scf_property_type(const scf_property_t *prop, scf_type_t *out)
3595 {
3596         scf_handle_t *h = prop->rd_d.rd_handle;
3597         rep_protocol_value_type_t out_raw;
3598         int ret;
3599 
3600         (void) pthread_mutex_lock(&h->rh_lock);
3601         ret = property_type_locked(prop, &out_raw);
3602         (void) pthread_mutex_unlock(&h->rh_lock);
3603 
3604         if (ret == SCF_SUCCESS)
3605                 *out = scf_protocol_type_to_type(out_raw);
3606 
3607         return (ret);
3608 }
3609 
3610 int
3611 scf_property_is_type(const scf_property_t *prop, scf_type_t base_arg)
3612 {
3613         scf_handle_t *h = prop->rd_d.rd_handle;
3614         rep_protocol_value_type_t base = scf_type_to_protocol_type(base_arg);
3615         rep_protocol_value_type_t type;
3616         int ret;
3617 
3618         if (base == REP_PROTOCOL_TYPE_INVALID)
3619                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
3620 
3621         (void) pthread_mutex_lock(&h->rh_lock);
3622         ret = property_type_locked(prop, &type);
3623         (void) pthread_mutex_unlock(&h->rh_lock);
3624 
3625         if (ret == SCF_SUCCESS) {
3626                 if (!scf_is_compatible_protocol_type(base, type))
3627                         return (scf_set_error(SCF_ERROR_TYPE_MISMATCH));
3628         }
3629         return (ret);
3630 }
3631 
3632 int
3633 scf_is_compatible_type(scf_type_t base_arg, scf_type_t type_arg)
3634 {
3635         rep_protocol_value_type_t base = scf_type_to_protocol_type(base_arg);
3636         rep_protocol_value_type_t type = scf_type_to_protocol_type(type_arg);
3637 
3638         if (base == REP_PROTOCOL_TYPE_INVALID ||
3639             type == REP_PROTOCOL_TYPE_INVALID)
3640                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
3641 
3642         if (!scf_is_compatible_protocol_type(base, type))
3643                 return (scf_set_error(SCF_ERROR_TYPE_MISMATCH));
3644 
3645         return (SCF_SUCCESS);
3646 }
3647 
3648 ssize_t
3649 scf_property_get_name(const scf_property_t *prop, char *out, size_t len)
3650 {
3651         return (datael_get_name(&prop->rd_d, out, len, RP_ENTITY_NAME_NAME));
3652 }
3653 
3654 /*
3655  * transaction functions
3656  */
3657 
3658 /*
3659  * Fails with _NO_MEMORY, _INVALID_ARGUMENT (handle is NULL), _HANDLE_DESTROYED,
3660  * _INTERNAL (bad server response or id in use), or _NO_RESOURCES.
3661  */
3662 scf_transaction_t *
3663 scf_transaction_create(scf_handle_t *handle)
3664 {
3665         scf_transaction_t *ret;
3666 
3667         ret = uu_zalloc(sizeof (scf_transaction_t));
3668         if (ret == NULL) {
3669                 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
3670                 return (NULL);
3671         }
3672         if (datael_init(&ret->tran_pg.rd_d, handle,
3673             REP_PROTOCOL_ENTITY_PROPERTYGRP) == -1) {
3674                 uu_free(ret);
3675                 return (NULL);                  /* error already set */
3676         }
3677         ret->tran_state = TRAN_STATE_NEW;
3678         ret->tran_props = uu_list_create(tran_entry_pool, ret, UU_LIST_SORTED);
3679         if (ret->tran_props == NULL) {
3680                 datael_destroy(&ret->tran_pg.rd_d);
3681                 uu_free(ret);
3682                 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
3683                 return (NULL);
3684         }
3685 
3686         return (ret);
3687 }
3688 
3689 scf_handle_t *
3690 scf_transaction_handle(const scf_transaction_t *val)
3691 {
3692         return (handle_get(val->tran_pg.rd_d.rd_handle));
3693 }
3694 
3695 int
3696 scf_transaction_start(scf_transaction_t *tran, scf_propertygroup_t *pg)
3697 {
3698         scf_handle_t *h = tran->tran_pg.rd_d.rd_handle;
3699 
3700         struct rep_protocol_transaction_start request;
3701         struct rep_protocol_response response;
3702         int r;
3703 
3704         if (h != pg->rd_d.rd_handle)
3705                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
3706 
3707         (void) pthread_mutex_lock(&h->rh_lock);
3708         if (tran->tran_state != TRAN_STATE_NEW) {
3709                 (void) pthread_mutex_unlock(&h->rh_lock);
3710                 return (scf_set_error(SCF_ERROR_IN_USE));
3711         }
3712         request.rpr_request = REP_PROTOCOL_PROPERTYGRP_TX_START;
3713         request.rpr_entityid_tx = tran->tran_pg.rd_d.rd_entity;
3714         request.rpr_entityid = pg->rd_d.rd_entity;
3715 
3716         datael_finish_reset(&tran->tran_pg.rd_d);
3717         datael_finish_reset(&pg->rd_d);
3718 
3719         r = make_door_call(h, &request, sizeof (request),
3720             &response, sizeof (response));
3721 
3722         if (r < 0) {
3723                 (void) pthread_mutex_unlock(&h->rh_lock);
3724                 DOOR_ERRORS_BLOCK(r);
3725         }
3726 
3727         /* r < sizeof (response) cannot happen because sizeof (response) == 4 */
3728 
3729         if (response.rpr_response != REP_PROTOCOL_SUCCESS ||
3730             r < sizeof (response)) {
3731                 (void) pthread_mutex_unlock(&h->rh_lock);
3732                 return (scf_set_error(proto_error(response.rpr_response)));
3733         }
3734 
3735         tran->tran_state = TRAN_STATE_SETUP;
3736         tran->tran_invalid = 0;
3737         (void) pthread_mutex_unlock(&h->rh_lock);
3738         return (SCF_SUCCESS);
3739 }
3740 
3741 static void
3742 entry_invalidate(scf_transaction_entry_t *cur, int and_destroy,
3743     int and_reset_value)
3744 {
3745         scf_value_t *v, *next;
3746         scf_transaction_t *tx;
3747         scf_handle_t *h = cur->entry_handle;
3748 
3749         assert(MUTEX_HELD(&h->rh_lock));
3750 
3751         if ((tx = cur->entry_tx) != NULL) {
3752                 tx->tran_invalid = 1;
3753                 uu_list_remove(tx->tran_props, cur);
3754                 cur->entry_tx = NULL;
3755         }
3756 
3757         cur->entry_property = NULL;
3758         cur->entry_state = ENTRY_STATE_INVALID;
3759         cur->entry_action = REP_PROTOCOL_TX_ENTRY_INVALID;
3760         cur->entry_type = REP_PROTOCOL_TYPE_INVALID;
3761 
3762         for (v = cur->entry_head; v != NULL; v = next) {
3763                 next = v->value_next;
3764                 v->value_tx = NULL;
3765                 v->value_next = NULL;
3766                 if (and_destroy || and_reset_value)
3767                         scf_value_reset_locked(v, and_destroy);
3768         }
3769         cur->entry_head = NULL;
3770         cur->entry_tail = NULL;
3771 }
3772 
3773 static void
3774 entry_destroy_locked(scf_transaction_entry_t *entry)
3775 {
3776         scf_handle_t *h = entry->entry_handle;
3777 
3778         assert(MUTEX_HELD(&h->rh_lock));
3779 
3780         entry_invalidate(entry, 0, 0);
3781 
3782         entry->entry_handle = NULL;
3783         assert(h->rh_entries > 0);
3784         --h->rh_entries;
3785         --h->rh_extrefs;
3786         uu_list_node_fini(entry, &entry->entry_link, tran_entry_pool);
3787         uu_free(entry);
3788 }
3789 
3790 /*
3791  * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
3792  * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
3793  * _BACKEND_ACCESS, _IN_USE, _NOT_FOUND, _EXISTS, _TYPE_MISMATCH.
3794  */
3795 static int
3796 transaction_add(scf_transaction_t *tran, scf_transaction_entry_t *entry,
3797     enum rep_protocol_transaction_action action,
3798     const char *prop, rep_protocol_value_type_t type)
3799 {
3800         scf_handle_t *h = tran->tran_pg.rd_d.rd_handle;
3801         scf_transaction_entry_t *old;
3802         scf_property_t *prop_p;
3803         rep_protocol_value_type_t oldtype;
3804         scf_error_t error = SCF_ERROR_NONE;
3805         int ret;
3806         uu_list_index_t idx;
3807 
3808         if (h != entry->entry_handle)
3809                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
3810 
3811         if (action == REP_PROTOCOL_TX_ENTRY_DELETE)
3812                 assert(type == REP_PROTOCOL_TYPE_INVALID);
3813         else if (type == REP_PROTOCOL_TYPE_INVALID)
3814                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
3815 
3816         prop_p = HANDLE_HOLD_PROPERTY(h);
3817 
3818         (void) pthread_mutex_lock(&h->rh_lock);
3819         if (tran->tran_state != TRAN_STATE_SETUP) {
3820                 error = SCF_ERROR_NOT_SET;
3821                 goto error;
3822         }
3823         if (tran->tran_invalid) {
3824                 error = SCF_ERROR_NOT_SET;
3825                 goto error;
3826         }
3827 
3828         if (entry->entry_state != ENTRY_STATE_INVALID)
3829                 entry_invalidate(entry, 0, 0);
3830 
3831         old = uu_list_find(tran->tran_props, &prop, NULL, &idx);
3832         if (old != NULL) {
3833                 error = SCF_ERROR_IN_USE;
3834                 goto error;
3835         }
3836 
3837         ret = datael_get_child_locked(&tran->tran_pg.rd_d, prop,
3838             REP_PROTOCOL_ENTITY_PROPERTY, &prop_p->rd_d);
3839         if (ret == -1 && (error = scf_error()) != SCF_ERROR_NOT_FOUND) {
3840                 goto error;
3841         }
3842 
3843         switch (action) {
3844         case REP_PROTOCOL_TX_ENTRY_DELETE:
3845                 if (ret == -1) {
3846                         error = SCF_ERROR_NOT_FOUND;
3847                         goto error;
3848                 }
3849                 break;
3850         case REP_PROTOCOL_TX_ENTRY_NEW:
3851                 if (ret != -1) {
3852                         error = SCF_ERROR_EXISTS;
3853                         goto error;
3854                 }
3855                 break;
3856 
3857         case REP_PROTOCOL_TX_ENTRY_CLEAR:
3858         case REP_PROTOCOL_TX_ENTRY_REPLACE:
3859                 if (ret == -1) {
3860                         error = SCF_ERROR_NOT_FOUND;
3861                         goto error;
3862                 }
3863                 if (action == REP_PROTOCOL_TX_ENTRY_CLEAR) {
3864                         if (property_type_locked(prop_p, &oldtype) == -1) {
3865                                 error = scf_error();
3866                                 goto error;
3867                         }
3868                         if (oldtype != type) {
3869                                 error = SCF_ERROR_TYPE_MISMATCH;
3870                                 goto error;
3871                         }
3872                 }
3873                 break;
3874         default:
3875                 assert(0);
3876                 abort();
3877         }
3878 
3879         (void) strlcpy(entry->entry_namebuf, prop,
3880             sizeof (entry->entry_namebuf));
3881         entry->entry_property = entry->entry_namebuf;
3882         entry->entry_action = action;
3883         entry->entry_type = type;
3884 
3885         entry->entry_state = ENTRY_STATE_IN_TX_ACTION;
3886         entry->entry_tx = tran;
3887         uu_list_insert(tran->tran_props, entry, idx);
3888 
3889         (void) pthread_mutex_unlock(&h->rh_lock);
3890 
3891         HANDLE_RELE_PROPERTY(h);
3892 
3893         return (SCF_SUCCESS);
3894 
3895 error:
3896         (void) pthread_mutex_unlock(&h->rh_lock);
3897 
3898         HANDLE_RELE_PROPERTY(h);
3899 
3900         return (scf_set_error(error));
3901 }
3902 
3903 /*
3904  * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
3905  * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
3906  * _BACKEND_ACCESS, _IN_USE, _NOT_FOUND, _EXISTS, _TYPE_MISMATCH.
3907  */
3908 int
3909 scf_transaction_property_new(scf_transaction_t *tx,
3910     scf_transaction_entry_t *entry, const char *prop, scf_type_t type)
3911 {
3912         return (transaction_add(tx, entry, REP_PROTOCOL_TX_ENTRY_NEW,
3913             prop, scf_type_to_protocol_type(type)));
3914 }
3915 
3916 /*
3917  * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
3918  * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
3919  * _BACKEND_ACCESS, _IN_USE, _NOT_FOUND, _EXISTS, _TYPE_MISMATCH.
3920  */
3921 int
3922 scf_transaction_property_change(scf_transaction_t *tx,
3923     scf_transaction_entry_t *entry, const char *prop, scf_type_t type)
3924 {
3925         return (transaction_add(tx, entry, REP_PROTOCOL_TX_ENTRY_CLEAR,
3926             prop, scf_type_to_protocol_type(type)));
3927 }
3928 
3929 /*
3930  * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
3931  * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
3932  * _BACKEND_ACCESS, _IN_USE, _NOT_FOUND, _EXISTS, _TYPE_MISMATCH.
3933  */
3934 int
3935 scf_transaction_property_change_type(scf_transaction_t *tx,
3936     scf_transaction_entry_t *entry, const char *prop, scf_type_t type)
3937 {
3938         return (transaction_add(tx, entry, REP_PROTOCOL_TX_ENTRY_REPLACE,
3939             prop, scf_type_to_protocol_type(type)));
3940 }
3941 
3942 /*
3943  * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
3944  * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
3945  * _BACKEND_ACCESS, _IN_USE, _NOT_FOUND, _EXISTS, _TYPE_MISMATCH.
3946  */
3947 int
3948 scf_transaction_property_delete(scf_transaction_t *tx,
3949     scf_transaction_entry_t *entry, const char *prop)
3950 {
3951         return (transaction_add(tx, entry, REP_PROTOCOL_TX_ENTRY_DELETE,
3952             prop, REP_PROTOCOL_TYPE_INVALID));
3953 }
3954 
3955 #define BAD_SIZE (-1UL)
3956 
3957 static size_t
3958 commit_value(caddr_t data, scf_value_t *val, rep_protocol_value_type_t t)
3959 {
3960         size_t len;
3961 
3962         assert(val->value_type == t);
3963 
3964         if (t == REP_PROTOCOL_TYPE_OPAQUE) {
3965                 len = scf_opaque_encode(data, val->value_value,
3966                     val->value_size);
3967         } else {
3968                 if (data != NULL)
3969                         len = strlcpy(data, val->value_value,
3970                             REP_PROTOCOL_VALUE_LEN);
3971                 else
3972                         len = strlen(val->value_value);
3973                 if (len >= REP_PROTOCOL_VALUE_LEN)
3974                         return (BAD_SIZE);
3975         }
3976         return (len + 1);       /* count the '\0' */
3977 }
3978 
3979 static size_t
3980 commit_process(scf_transaction_entry_t *cur,
3981     struct rep_protocol_transaction_cmd *out)
3982 {
3983         scf_value_t *child;
3984         size_t sz = 0;
3985         size_t len;
3986         caddr_t data = (caddr_t)out->rptc_data;
3987         caddr_t val_data;
3988 
3989         if (out != NULL) {
3990                 len = strlcpy(data, cur->entry_property, REP_PROTOCOL_NAME_LEN);
3991 
3992                 out->rptc_action = cur->entry_action;
3993                 out->rptc_type = cur->entry_type;
3994                 out->rptc_name_len = len + 1;
3995         } else {
3996                 len = strlen(cur->entry_property);
3997         }
3998 
3999         if (len >= REP_PROTOCOL_NAME_LEN)
4000                 return (BAD_SIZE);
4001 
4002         len = TX_SIZE(len + 1);
4003 
4004         sz += len;
4005         val_data = data + len;
4006 
4007         for (child = cur->entry_head; child != NULL;
4008             child = child->value_next) {
4009                 assert(cur->entry_action != REP_PROTOCOL_TX_ENTRY_DELETE);
4010                 if (out != NULL) {
4011                         len = commit_value(val_data + sizeof (uint32_t), child,
4012                             cur->entry_type);
4013                         /* LINTED alignment */
4014                         *(uint32_t *)val_data = len;
4015                 } else
4016                         len = commit_value(NULL, child, cur->entry_type);
4017 
4018                 if (len == BAD_SIZE)
4019                         return (BAD_SIZE);
4020 
4021                 len += sizeof (uint32_t);
4022                 len = TX_SIZE(len);
4023 
4024                 sz += len;
4025                 val_data += len;
4026         }
4027 
4028         assert(val_data - data == sz);
4029 
4030         if (out != NULL)
4031                 out->rptc_size = REP_PROTOCOL_TRANSACTION_CMD_SIZE(sz);
4032 
4033         return (REP_PROTOCOL_TRANSACTION_CMD_SIZE(sz));
4034 }
4035 
4036 int
4037 scf_transaction_commit(scf_transaction_t *tran)
4038 {
4039         scf_handle_t *h = tran->tran_pg.rd_d.rd_handle;
4040 
4041         struct rep_protocol_transaction_commit *request;
4042         struct rep_protocol_response response;
4043         uintptr_t cmd;
4044         scf_transaction_entry_t *cur;
4045         size_t total, size;
4046         size_t request_size;
4047         size_t new_total;
4048         int r;
4049 
4050         (void) pthread_mutex_lock(&h->rh_lock);
4051         if (tran->tran_state != TRAN_STATE_SETUP ||
4052             tran->tran_invalid) {
4053                 (void) pthread_mutex_unlock(&h->rh_lock);
4054                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4055         }
4056 
4057         total = 0;
4058         for (cur = uu_list_first(tran->tran_props); cur != NULL;
4059             cur = uu_list_next(tran->tran_props, cur)) {
4060                 size = commit_process(cur, NULL);
4061                 if (size == BAD_SIZE) {
4062                         (void) pthread_mutex_unlock(&h->rh_lock);
4063                         return (scf_set_error(SCF_ERROR_INTERNAL));
4064                 }
4065                 assert(TX_SIZE(size) == size);
4066                 total += size;
4067         }
4068 
4069         request_size = REP_PROTOCOL_TRANSACTION_COMMIT_SIZE(total);
4070         request = alloca(request_size);
4071         (void) memset(request, '\0', request_size);
4072         request->rpr_request = REP_PROTOCOL_PROPERTYGRP_TX_COMMIT;
4073         request->rpr_entityid = tran->tran_pg.rd_d.rd_entity;
4074         request->rpr_size = request_size;
4075         cmd = (uintptr_t)request->rpr_cmd;
4076 
4077         datael_finish_reset(&tran->tran_pg.rd_d);
4078 
4079         new_total = 0;
4080         for (cur = uu_list_first(tran->tran_props); cur != NULL;
4081             cur = uu_list_next(tran->tran_props, cur)) {
4082                 size = commit_process(cur, (void *)cmd);
4083                 if (size == BAD_SIZE) {
4084                         (void) pthread_mutex_unlock(&h->rh_lock);
4085                         return (scf_set_error(SCF_ERROR_INTERNAL));
4086                 }
4087                 cmd += size;
4088                 new_total += size;
4089         }
4090         assert(new_total == total);
4091 
4092         r = make_door_call(h, request, request_size,
4093             &response, sizeof (response));
4094 
4095         if (r < 0) {
4096                 (void) pthread_mutex_unlock(&h->rh_lock);
4097                 DOOR_ERRORS_BLOCK(r);
4098         }
4099 
4100         if (response.rpr_response != REP_PROTOCOL_SUCCESS &&
4101             response.rpr_response != REP_PROTOCOL_FAIL_NOT_LATEST) {
4102                 (void) pthread_mutex_unlock(&h->rh_lock);
4103                 return (scf_set_error(proto_error(response.rpr_response)));
4104         }
4105 
4106         tran->tran_state = TRAN_STATE_COMMITTED;
4107         (void) pthread_mutex_unlock(&h->rh_lock);
4108         return (response.rpr_response == REP_PROTOCOL_SUCCESS);
4109 }
4110 
4111 static void
4112 transaction_reset(scf_transaction_t *tran)
4113 {
4114         assert(MUTEX_HELD(&tran->tran_pg.rd_d.rd_handle->rh_lock));
4115 
4116         tran->tran_state = TRAN_STATE_NEW;
4117         datael_reset_locked(&tran->tran_pg.rd_d);
4118 }
4119 
4120 static void
4121 scf_transaction_reset_impl(scf_transaction_t *tran, int and_destroy,
4122     int and_reset_value)
4123 {
4124         scf_transaction_entry_t *cur;
4125         void *cookie;
4126 
4127         (void) pthread_mutex_lock(&tran->tran_pg.rd_d.rd_handle->rh_lock);
4128         cookie = NULL;
4129         while ((cur = uu_list_teardown(tran->tran_props, &cookie)) != NULL) {
4130                 cur->entry_tx = NULL;
4131 
4132                 assert(cur->entry_state == ENTRY_STATE_IN_TX_ACTION);
4133                 cur->entry_state = ENTRY_STATE_INVALID;
4134 
4135                 entry_invalidate(cur, and_destroy, and_reset_value);
4136                 if (and_destroy)
4137                         entry_destroy_locked(cur);
4138         }
4139         transaction_reset(tran);
4140         handle_unrefed(tran->tran_pg.rd_d.rd_handle);
4141 }
4142 
4143 void
4144 scf_transaction_reset(scf_transaction_t *tran)
4145 {
4146         scf_transaction_reset_impl(tran, 0, 0);
4147 }
4148 
4149 void
4150 scf_transaction_reset_all(scf_transaction_t *tran)
4151 {
4152         scf_transaction_reset_impl(tran, 0, 1);
4153 }
4154 
4155 void
4156 scf_transaction_destroy(scf_transaction_t *val)
4157 {
4158         if (val == NULL)
4159                 return;
4160 
4161         scf_transaction_reset(val);
4162 
4163         datael_destroy(&val->tran_pg.rd_d);
4164 
4165         uu_list_destroy(val->tran_props);
4166         uu_free(val);
4167 }
4168 
4169 void
4170 scf_transaction_destroy_children(scf_transaction_t *tran)
4171 {
4172         if (tran == NULL)
4173                 return;
4174 
4175         scf_transaction_reset_impl(tran, 1, 0);
4176 }
4177 
4178 scf_transaction_entry_t *
4179 scf_entry_create(scf_handle_t *h)
4180 {
4181         scf_transaction_entry_t *ret;
4182 
4183         if (h == NULL) {
4184                 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
4185                 return (NULL);
4186         }
4187 
4188         ret = uu_zalloc(sizeof (scf_transaction_entry_t));
4189         if (ret == NULL) {
4190                 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
4191                 return (NULL);
4192         }
4193         ret->entry_action = REP_PROTOCOL_TX_ENTRY_INVALID;
4194         ret->entry_handle = h;
4195 
4196         (void) pthread_mutex_lock(&h->rh_lock);
4197         if (h->rh_flags & HANDLE_DEAD) {
4198                 (void) pthread_mutex_unlock(&h->rh_lock);
4199                 uu_free(ret);
4200                 (void) scf_set_error(SCF_ERROR_HANDLE_DESTROYED);
4201                 return (NULL);
4202         }
4203         h->rh_entries++;
4204         h->rh_extrefs++;
4205         (void) pthread_mutex_unlock(&h->rh_lock);
4206 
4207         uu_list_node_init(ret, &ret->entry_link, tran_entry_pool);
4208 
4209         return (ret);
4210 }
4211 
4212 scf_handle_t *
4213 scf_entry_handle(const scf_transaction_entry_t *val)
4214 {
4215         return (handle_get(val->entry_handle));
4216 }
4217 
4218 void
4219 scf_entry_reset(scf_transaction_entry_t *entry)
4220 {
4221         scf_handle_t *h = entry->entry_handle;
4222 
4223         (void) pthread_mutex_lock(&h->rh_lock);
4224         entry_invalidate(entry, 0, 0);
4225         (void) pthread_mutex_unlock(&h->rh_lock);
4226 }
4227 
4228 void
4229 scf_entry_destroy_children(scf_transaction_entry_t *entry)
4230 {
4231         scf_handle_t *h = entry->entry_handle;
4232 
4233         (void) pthread_mutex_lock(&h->rh_lock);
4234         entry_invalidate(entry, 1, 0);
4235         handle_unrefed(h);                      /* drops h->rh_lock */
4236 }
4237 
4238 void
4239 scf_entry_destroy(scf_transaction_entry_t *entry)
4240 {
4241         scf_handle_t *h;
4242 
4243         if (entry == NULL)
4244                 return;
4245 
4246         h = entry->entry_handle;
4247 
4248         (void) pthread_mutex_lock(&h->rh_lock);
4249         entry_destroy_locked(entry);
4250         handle_unrefed(h);                      /* drops h->rh_lock */
4251 }
4252 
4253 /*
4254  * Fails with
4255  *   _HANDLE_MISMATCH
4256  *   _NOT_SET - has not been added to a transaction
4257  *   _INTERNAL - entry is corrupt
4258  *   _INVALID_ARGUMENT - entry's transaction is not started or corrupt
4259  *                       entry is set to delete a property
4260  *                       v is reset or corrupt
4261  *   _TYPE_MISMATCH - entry & v's types aren't compatible
4262  *   _IN_USE - v has been added to another entry
4263  */
4264 int
4265 scf_entry_add_value(scf_transaction_entry_t *entry, scf_value_t *v)
4266 {
4267         scf_handle_t *h = entry->entry_handle;
4268 
4269         if (h != v->value_handle)
4270                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
4271 
4272         (void) pthread_mutex_lock(&h->rh_lock);
4273 
4274         if (entry->entry_state == ENTRY_STATE_INVALID) {
4275                 (void) pthread_mutex_unlock(&h->rh_lock);
4276                 return (scf_set_error(SCF_ERROR_NOT_SET));
4277         }
4278 
4279         if (entry->entry_state != ENTRY_STATE_IN_TX_ACTION) {
4280                 (void) pthread_mutex_unlock(&h->rh_lock);
4281                 return (scf_set_error(SCF_ERROR_INTERNAL));
4282         }
4283 
4284         if (entry->entry_tx->tran_state != TRAN_STATE_SETUP) {
4285                 (void) pthread_mutex_unlock(&h->rh_lock);
4286                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4287         }
4288 
4289         if (entry->entry_action == REP_PROTOCOL_TX_ENTRY_DELETE) {
4290                 (void) pthread_mutex_unlock(&h->rh_lock);
4291                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4292         }
4293 
4294         if (v->value_type == REP_PROTOCOL_TYPE_INVALID) {
4295                 (void) pthread_mutex_unlock(&h->rh_lock);
4296                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4297         }
4298 
4299         if (!scf_is_compatible_protocol_type(entry->entry_type,
4300             v->value_type)) {
4301                 (void) pthread_mutex_unlock(&h->rh_lock);
4302                 return (scf_set_error(SCF_ERROR_TYPE_MISMATCH));
4303         }
4304 
4305         if (v->value_tx != NULL) {
4306                 (void) pthread_mutex_unlock(&h->rh_lock);
4307                 return (scf_set_error(SCF_ERROR_IN_USE));
4308         }
4309 
4310         v->value_tx = entry;
4311         v->value_next = NULL;
4312         if (entry->entry_head == NULL) {
4313                 entry->entry_head = v;
4314                 entry->entry_tail = v;
4315         } else {
4316                 entry->entry_tail->value_next = v;
4317                 entry->entry_tail = v;
4318         }
4319 
4320         (void) pthread_mutex_unlock(&h->rh_lock);
4321 
4322         return (SCF_SUCCESS);
4323 }
4324 
4325 /*
4326  * value functions
4327  */
4328 scf_value_t *
4329 scf_value_create(scf_handle_t *h)
4330 {
4331         scf_value_t *ret;
4332 
4333         if (h == NULL) {
4334                 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
4335                 return (NULL);
4336         }
4337 
4338         ret = uu_zalloc(sizeof (*ret));
4339         if (ret != NULL) {
4340                 ret->value_type = REP_PROTOCOL_TYPE_INVALID;
4341                 ret->value_handle = h;
4342                 (void) pthread_mutex_lock(&h->rh_lock);
4343                 if (h->rh_flags & HANDLE_DEAD) {
4344                         (void) pthread_mutex_unlock(&h->rh_lock);
4345                         uu_free(ret);
4346                         (void) scf_set_error(SCF_ERROR_HANDLE_DESTROYED);
4347                         return (NULL);
4348                 }
4349                 h->rh_values++;
4350                 h->rh_extrefs++;
4351                 (void) pthread_mutex_unlock(&h->rh_lock);
4352         } else {
4353                 (void) scf_set_error(SCF_ERROR_NO_MEMORY);
4354         }
4355 
4356         return (ret);
4357 }
4358 
4359 static void
4360 scf_value_reset_locked(scf_value_t *val, int and_destroy)
4361 {
4362         scf_value_t **curp;
4363         scf_transaction_entry_t *te;
4364 
4365         scf_handle_t *h = val->value_handle;
4366         assert(MUTEX_HELD(&h->rh_lock));
4367         if (val->value_tx != NULL) {
4368                 te = val->value_tx;
4369                 te->entry_tx->tran_invalid = 1;
4370 
4371                 val->value_tx = NULL;
4372 
4373                 for (curp = &te->entry_head; *curp != NULL;
4374                     curp = &(*curp)->value_next) {
4375                         if (*curp == val) {
4376                                 *curp = val->value_next;
4377                                 curp = NULL;
4378                                 break;
4379                         }
4380                 }
4381                 assert(curp == NULL);
4382         }
4383         val->value_type = REP_PROTOCOL_TYPE_INVALID;
4384 
4385         if (and_destroy) {
4386                 val->value_handle = NULL;
4387                 assert(h->rh_values > 0);
4388                 --h->rh_values;
4389                 --h->rh_extrefs;
4390                 uu_free(val);
4391         }
4392 }
4393 
4394 void
4395 scf_value_reset(scf_value_t *val)
4396 {
4397         scf_handle_t *h = val->value_handle;
4398 
4399         (void) pthread_mutex_lock(&h->rh_lock);
4400         scf_value_reset_locked(val, 0);
4401         (void) pthread_mutex_unlock(&h->rh_lock);
4402 }
4403 
4404 scf_handle_t *
4405 scf_value_handle(const scf_value_t *val)
4406 {
4407         return (handle_get(val->value_handle));
4408 }
4409 
4410 void
4411 scf_value_destroy(scf_value_t *val)
4412 {
4413         scf_handle_t *h;
4414 
4415         if (val == NULL)
4416                 return;
4417 
4418         h = val->value_handle;
4419 
4420         (void) pthread_mutex_lock(&h->rh_lock);
4421         scf_value_reset_locked(val, 1);
4422         handle_unrefed(h);                      /* drops h->rh_lock */
4423 }
4424 
4425 scf_type_t
4426 scf_value_base_type(const scf_value_t *val)
4427 {
4428         rep_protocol_value_type_t t, cur;
4429         scf_handle_t *h = val->value_handle;
4430 
4431         (void) pthread_mutex_lock(&h->rh_lock);
4432         t = val->value_type;
4433         (void) pthread_mutex_unlock(&h->rh_lock);
4434 
4435         for (;;) {
4436                 cur = scf_proto_underlying_type(t);
4437                 if (cur == t)
4438                         break;
4439                 t = cur;
4440         }
4441 
4442         return (scf_protocol_type_to_type(t));
4443 }
4444 
4445 scf_type_t
4446 scf_value_type(const scf_value_t *val)
4447 {
4448         rep_protocol_value_type_t t;
4449         scf_handle_t *h = val->value_handle;
4450 
4451         (void) pthread_mutex_lock(&h->rh_lock);
4452         t = val->value_type;
4453         (void) pthread_mutex_unlock(&h->rh_lock);
4454 
4455         return (scf_protocol_type_to_type(t));
4456 }
4457 
4458 int
4459 scf_value_is_type(const scf_value_t *val, scf_type_t base_arg)
4460 {
4461         rep_protocol_value_type_t t;
4462         rep_protocol_value_type_t base = scf_type_to_protocol_type(base_arg);
4463         scf_handle_t *h = val->value_handle;
4464 
4465         (void) pthread_mutex_lock(&h->rh_lock);
4466         t = val->value_type;
4467         (void) pthread_mutex_unlock(&h->rh_lock);
4468 
4469         if (t == REP_PROTOCOL_TYPE_INVALID)
4470                 return (scf_set_error(SCF_ERROR_NOT_SET));
4471         if (base == REP_PROTOCOL_TYPE_INVALID)
4472                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4473         if (!scf_is_compatible_protocol_type(base, t))
4474                 return (scf_set_error(SCF_ERROR_TYPE_MISMATCH));
4475 
4476         return (SCF_SUCCESS);
4477 }
4478 
4479 /*
4480  * Fails with
4481  *   _NOT_SET - val is reset
4482  *   _TYPE_MISMATCH - val's type is not compatible with t
4483  */
4484 static int
4485 scf_value_check_type(const scf_value_t *val, rep_protocol_value_type_t t)
4486 {
4487         if (val->value_type == REP_PROTOCOL_TYPE_INVALID) {
4488                 (void) scf_set_error(SCF_ERROR_NOT_SET);
4489                 return (0);
4490         }
4491         if (!scf_is_compatible_protocol_type(t, val->value_type)) {
4492                 (void) scf_set_error(SCF_ERROR_TYPE_MISMATCH);
4493                 return (0);
4494         }
4495         return (1);
4496 }
4497 
4498 /*
4499  * Fails with
4500  *   _NOT_SET - val is reset
4501  *   _TYPE_MISMATCH - val is not _TYPE_BOOLEAN
4502  */
4503 int
4504 scf_value_get_boolean(const scf_value_t *val, uint8_t *out)
4505 {
4506         char c;
4507         scf_handle_t *h = val->value_handle;
4508         uint8_t o;
4509 
4510         (void) pthread_mutex_lock(&h->rh_lock);
4511         if (!scf_value_check_type(val, REP_PROTOCOL_TYPE_BOOLEAN)) {
4512                 (void) pthread_mutex_unlock(&h->rh_lock);
4513                 return (-1);
4514         }
4515 
4516         c = val->value_value[0];
4517         assert((c == '0' || c == '1') && val->value_value[1] == 0);
4518 
4519         o = (c != '0');
4520         (void) pthread_mutex_unlock(&h->rh_lock);
4521         if (out != NULL)
4522                 *out = o;
4523         return (SCF_SUCCESS);
4524 }
4525 
4526 int
4527 scf_value_get_count(const scf_value_t *val, uint64_t *out)
4528 {
4529         scf_handle_t *h = val->value_handle;
4530         uint64_t o;
4531 
4532         (void) pthread_mutex_lock(&h->rh_lock);
4533         if (!scf_value_check_type(val, REP_PROTOCOL_TYPE_COUNT)) {
4534                 (void) pthread_mutex_unlock(&h->rh_lock);
4535                 return (-1);
4536         }
4537 
4538         o = strtoull(val->value_value, NULL, 10);
4539         (void) pthread_mutex_unlock(&h->rh_lock);
4540         if (out != NULL)
4541                 *out = o;
4542         return (SCF_SUCCESS);
4543 }
4544 
4545 int
4546 scf_value_get_integer(const scf_value_t *val, int64_t *out)
4547 {
4548         scf_handle_t *h = val->value_handle;
4549         int64_t o;
4550 
4551         (void) pthread_mutex_lock(&h->rh_lock);
4552         if (!scf_value_check_type(val, REP_PROTOCOL_TYPE_INTEGER)) {
4553                 (void) pthread_mutex_unlock(&h->rh_lock);
4554                 return (-1);
4555         }
4556 
4557         o = strtoll(val->value_value, NULL, 10);
4558         (void) pthread_mutex_unlock(&h->rh_lock);
4559         if (out != NULL)
4560                 *out = o;
4561         return (SCF_SUCCESS);
4562 }
4563 
4564 int
4565 scf_value_get_time(const scf_value_t *val, int64_t *sec_out, int32_t *nsec_out)
4566 {
4567         scf_handle_t *h = val->value_handle;
4568         char *p;
4569         int64_t os;
4570         int32_t ons;
4571 
4572         (void) pthread_mutex_lock(&h->rh_lock);
4573         if (!scf_value_check_type(val, REP_PROTOCOL_TYPE_TIME)) {
4574                 (void) pthread_mutex_unlock(&h->rh_lock);
4575                 return (-1);
4576         }
4577 
4578         os = strtoll(val->value_value, &p, 10);
4579         if (*p == '.')
4580                 ons = strtoul(p + 1, NULL, 10);
4581         else
4582                 ons = 0;
4583         (void) pthread_mutex_unlock(&h->rh_lock);
4584         if (sec_out != NULL)
4585                 *sec_out = os;
4586         if (nsec_out != NULL)
4587                 *nsec_out = ons;
4588 
4589         return (SCF_SUCCESS);
4590 }
4591 
4592 /*
4593  * Fails with
4594  *   _NOT_SET - val is reset
4595  *   _TYPE_MISMATCH - val's type is not compatible with _TYPE_STRING.
4596  */
4597 ssize_t
4598 scf_value_get_astring(const scf_value_t *val, char *out, size_t len)
4599 {
4600         ssize_t ret;
4601         scf_handle_t *h = val->value_handle;
4602 
4603         (void) pthread_mutex_lock(&h->rh_lock);
4604         if (!scf_value_check_type(val, REP_PROTOCOL_TYPE_STRING)) {
4605                 (void) pthread_mutex_unlock(&h->rh_lock);
4606                 return ((ssize_t)-1);
4607         }
4608         ret = (ssize_t)strlcpy(out, val->value_value, len);
4609         (void) pthread_mutex_unlock(&h->rh_lock);
4610         return (ret);
4611 }
4612 
4613 ssize_t
4614 scf_value_get_ustring(const scf_value_t *val, char *out, size_t len)
4615 {
4616         ssize_t ret;
4617         scf_handle_t *h = val->value_handle;
4618 
4619         (void) pthread_mutex_lock(&h->rh_lock);
4620         if (!scf_value_check_type(val, REP_PROTOCOL_SUBTYPE_USTRING)) {
4621                 (void) pthread_mutex_unlock(&h->rh_lock);
4622                 return ((ssize_t)-1);
4623         }
4624         ret = (ssize_t)strlcpy(out, val->value_value, len);
4625         (void) pthread_mutex_unlock(&h->rh_lock);
4626         return (ret);
4627 }
4628 
4629 ssize_t
4630 scf_value_get_opaque(const scf_value_t *v, void *out, size_t len)
4631 {
4632         ssize_t ret;
4633         scf_handle_t *h = v->value_handle;
4634 
4635         (void) pthread_mutex_lock(&h->rh_lock);
4636         if (!scf_value_check_type(v, REP_PROTOCOL_TYPE_OPAQUE)) {
4637                 (void) pthread_mutex_unlock(&h->rh_lock);
4638                 return ((ssize_t)-1);
4639         }
4640         if (len > v->value_size)
4641                 len = v->value_size;
4642         ret = len;
4643 
4644         (void) memcpy(out, v->value_value, len);
4645         (void) pthread_mutex_unlock(&h->rh_lock);
4646         return (ret);
4647 }
4648 
4649 void
4650 scf_value_set_boolean(scf_value_t *v, uint8_t new)
4651 {
4652         scf_handle_t *h = v->value_handle;
4653 
4654         (void) pthread_mutex_lock(&h->rh_lock);
4655         scf_value_reset_locked(v, 0);
4656         v->value_type = REP_PROTOCOL_TYPE_BOOLEAN;
4657         (void) sprintf(v->value_value, "%d", (new != 0));
4658         (void) pthread_mutex_unlock(&h->rh_lock);
4659 }
4660 
4661 void
4662 scf_value_set_count(scf_value_t *v, uint64_t new)
4663 {
4664         scf_handle_t *h = v->value_handle;
4665 
4666         (void) pthread_mutex_lock(&h->rh_lock);
4667         scf_value_reset_locked(v, 0);
4668         v->value_type = REP_PROTOCOL_TYPE_COUNT;
4669         (void) sprintf(v->value_value, "%llu", (unsigned long long)new);
4670         (void) pthread_mutex_unlock(&h->rh_lock);
4671 }
4672 
4673 void
4674 scf_value_set_integer(scf_value_t *v, int64_t new)
4675 {
4676         scf_handle_t *h = v->value_handle;
4677 
4678         (void) pthread_mutex_lock(&h->rh_lock);
4679         scf_value_reset_locked(v, 0);
4680         v->value_type = REP_PROTOCOL_TYPE_INTEGER;
4681         (void) sprintf(v->value_value, "%lld", (long long)new);
4682         (void) pthread_mutex_unlock(&h->rh_lock);
4683 }
4684 
4685 int
4686 scf_value_set_time(scf_value_t *v, int64_t new_sec, int32_t new_nsec)
4687 {
4688         scf_handle_t *h = v->value_handle;
4689 
4690         (void) pthread_mutex_lock(&h->rh_lock);
4691         scf_value_reset_locked(v, 0);
4692         if (new_nsec < 0 || new_nsec >= NANOSEC) {
4693                 (void) pthread_mutex_unlock(&h->rh_lock);
4694                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4695         }
4696         v->value_type = REP_PROTOCOL_TYPE_TIME;
4697         if (new_nsec == 0)
4698                 (void) sprintf(v->value_value, "%lld", (long long)new_sec);
4699         else
4700                 (void) sprintf(v->value_value, "%lld.%09u", (long long)new_sec,
4701                     (unsigned)new_nsec);
4702         (void) pthread_mutex_unlock(&h->rh_lock);
4703         return (0);
4704 }
4705 
4706 int
4707 scf_value_set_astring(scf_value_t *v, const char *new)
4708 {
4709         scf_handle_t *h = v->value_handle;
4710 
4711         (void) pthread_mutex_lock(&h->rh_lock);
4712         scf_value_reset_locked(v, 0);
4713         if (!scf_validate_encoded_value(REP_PROTOCOL_TYPE_STRING, new)) {
4714                 (void) pthread_mutex_unlock(&h->rh_lock);
4715                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4716         }
4717         if (strlcpy(v->value_value, new, sizeof (v->value_value)) >=
4718             sizeof (v->value_value)) {
4719                 (void) pthread_mutex_unlock(&h->rh_lock);
4720                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4721         }
4722         v->value_type = REP_PROTOCOL_TYPE_STRING;
4723         (void) pthread_mutex_unlock(&h->rh_lock);
4724         return (0);
4725 }
4726 
4727 int
4728 scf_value_set_ustring(scf_value_t *v, const char *new)
4729 {
4730         scf_handle_t *h = v->value_handle;
4731 
4732         (void) pthread_mutex_lock(&h->rh_lock);
4733         scf_value_reset_locked(v, 0);
4734         if (!scf_validate_encoded_value(REP_PROTOCOL_SUBTYPE_USTRING, new)) {
4735                 (void) pthread_mutex_unlock(&h->rh_lock);
4736                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4737         }
4738         if (strlcpy(v->value_value, new, sizeof (v->value_value)) >=
4739             sizeof (v->value_value)) {
4740                 (void) pthread_mutex_unlock(&h->rh_lock);
4741                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4742         }
4743         v->value_type = REP_PROTOCOL_SUBTYPE_USTRING;
4744         (void) pthread_mutex_unlock(&h->rh_lock);
4745         return (0);
4746 }
4747 
4748 int
4749 scf_value_set_opaque(scf_value_t *v, const void *new, size_t len)
4750 {
4751         scf_handle_t *h = v->value_handle;
4752 
4753         (void) pthread_mutex_lock(&h->rh_lock);
4754         scf_value_reset_locked(v, 0);
4755         if (len > sizeof (v->value_value)) {
4756                 (void) pthread_mutex_unlock(&h->rh_lock);
4757                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4758         }
4759         (void) memcpy(v->value_value, new, len);
4760         v->value_size = len;
4761         v->value_type = REP_PROTOCOL_TYPE_OPAQUE;
4762         (void) pthread_mutex_unlock(&h->rh_lock);
4763         return (0);
4764 }
4765 
4766 /*
4767  * Fails with
4768  *   _NOT_SET - v_arg is reset
4769  *   _INTERNAL - v_arg is corrupt
4770  *
4771  * If t is not _TYPE_INVALID, fails with
4772  *   _TYPE_MISMATCH - v_arg's type is not compatible with t
4773  */
4774 static ssize_t
4775 scf_value_get_as_string_common(const scf_value_t *v_arg,
4776     rep_protocol_value_type_t t, char *buf, size_t bufsz)
4777 {
4778         scf_handle_t *h = v_arg->value_handle;
4779         scf_value_t v_s;
4780         scf_value_t *v = &v_s;
4781         ssize_t r;
4782         uint8_t b;
4783 
4784         (void) pthread_mutex_lock(&h->rh_lock);
4785         if (t != REP_PROTOCOL_TYPE_INVALID && !scf_value_check_type(v_arg, t)) {
4786                 (void) pthread_mutex_unlock(&h->rh_lock);
4787                 return (-1);
4788         }
4789 
4790         v_s = *v_arg;                   /* copy locally so we can unlock */
4791         h->rh_values++;                      /* keep the handle from going away */
4792         h->rh_extrefs++;
4793         (void) pthread_mutex_unlock(&h->rh_lock);
4794 
4795 
4796         switch (REP_PROTOCOL_BASE_TYPE(v->value_type)) {
4797         case REP_PROTOCOL_TYPE_BOOLEAN:
4798                 r = scf_value_get_boolean(v, &b);
4799                 assert(r == SCF_SUCCESS);
4800 
4801                 r = strlcpy(buf, b ? "true" : "false", bufsz);
4802                 break;
4803 
4804         case REP_PROTOCOL_TYPE_COUNT:
4805         case REP_PROTOCOL_TYPE_INTEGER:
4806         case REP_PROTOCOL_TYPE_TIME:
4807         case REP_PROTOCOL_TYPE_STRING:
4808                 r = strlcpy(buf, v->value_value, bufsz);
4809                 break;
4810 
4811         case REP_PROTOCOL_TYPE_OPAQUE:
4812                 /*
4813                  * Note that we only write out full hex bytes -- if they're
4814                  * short, and bufsz is even, we'll only fill (bufsz - 2) bytes
4815                  * with data.
4816                  */
4817                 if (bufsz > 0)
4818                         (void) scf_opaque_encode(buf, v->value_value,
4819                             MIN(v->value_size, (bufsz - 1)/2));
4820                 r = (v->value_size * 2);
4821                 break;
4822 
4823         case REP_PROTOCOL_TYPE_INVALID:
4824                 r = scf_set_error(SCF_ERROR_NOT_SET);
4825                 break;
4826 
4827         default:
4828                 r = (scf_set_error(SCF_ERROR_INTERNAL));
4829                 break;
4830         }
4831 
4832         (void) pthread_mutex_lock(&h->rh_lock);
4833         h->rh_values--;
4834         h->rh_extrefs--;
4835         handle_unrefed(h);
4836 
4837         return (r);
4838 }
4839 
4840 ssize_t
4841 scf_value_get_as_string(const scf_value_t *v, char *buf, size_t bufsz)
4842 {
4843         return (scf_value_get_as_string_common(v, REP_PROTOCOL_TYPE_INVALID,
4844             buf, bufsz));
4845 }
4846 
4847 ssize_t
4848 scf_value_get_as_string_typed(const scf_value_t *v, scf_type_t type,
4849     char *buf, size_t bufsz)
4850 {
4851         rep_protocol_value_type_t ty = scf_type_to_protocol_type(type);
4852         if (ty == REP_PROTOCOL_TYPE_INVALID)
4853                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4854 
4855         return (scf_value_get_as_string_common(v, ty, buf, bufsz));
4856 }
4857 
4858 int
4859 scf_value_set_from_string(scf_value_t *v, scf_type_t type, const char *str)
4860 {
4861         scf_handle_t *h = v->value_handle;
4862         rep_protocol_value_type_t ty;
4863 
4864         switch (type) {
4865         case SCF_TYPE_BOOLEAN: {
4866                 uint8_t b;
4867 
4868                 if (strcmp(str, "true") == 0 || strcmp(str, "t") == 0 ||
4869                     strcmp(str, "1") == 0)
4870                         b = 1;
4871                 else if (strcmp(str, "false") == 0 ||
4872                     strcmp(str, "f") == 0 || strcmp(str, "0") == 0)
4873                         b = 0;
4874                 else {
4875                         goto bad;
4876                 }
4877 
4878                 scf_value_set_boolean(v, b);
4879                 return (0);
4880         }
4881 
4882         case SCF_TYPE_COUNT: {
4883                 uint64_t c;
4884                 char *endp;
4885 
4886                 errno = 0;
4887                 c = strtoull(str, &endp, 0);
4888 
4889                 if (errno != 0 || endp == str || *endp != '\0')
4890                         goto bad;
4891 
4892                 scf_value_set_count(v, c);
4893                 return (0);
4894         }
4895 
4896         case SCF_TYPE_INTEGER: {
4897                 int64_t i;
4898                 char *endp;
4899 
4900                 errno = 0;
4901                 i = strtoll(str, &endp, 0);
4902 
4903                 if (errno != 0 || endp == str || *endp != '\0')
4904                         goto bad;
4905 
4906                 scf_value_set_integer(v, i);
4907                 return (0);
4908         }
4909 
4910         case SCF_TYPE_TIME: {
4911                 int64_t s;
4912                 uint32_t ns = 0;
4913                 char *endp, *ns_str;
4914                 size_t len;
4915 
4916                 errno = 0;
4917                 s = strtoll(str, &endp, 10);
4918                 if (errno != 0 || endp == str ||
4919                     (*endp != '\0' && *endp != '.'))
4920                         goto bad;
4921 
4922                 if (*endp == '.') {
4923                         ns_str = endp + 1;
4924                         len = strlen(ns_str);
4925                         if (len == 0 || len > 9)
4926                                 goto bad;
4927 
4928                         ns = strtoul(ns_str, &endp, 10);
4929                         if (errno != 0 || endp == ns_str || *endp != '\0')
4930                                 goto bad;
4931 
4932                         while (len++ < 9)
4933                                 ns *= 10;
4934                         assert(ns < NANOSEC);
4935                 }
4936 
4937                 return (scf_value_set_time(v, s, ns));
4938         }
4939 
4940         case SCF_TYPE_ASTRING:
4941         case SCF_TYPE_USTRING:
4942         case SCF_TYPE_OPAQUE:
4943         case SCF_TYPE_URI:
4944         case SCF_TYPE_FMRI:
4945         case SCF_TYPE_HOST:
4946         case SCF_TYPE_HOSTNAME:
4947         case SCF_TYPE_NET_ADDR:
4948         case SCF_TYPE_NET_ADDR_V4:
4949         case SCF_TYPE_NET_ADDR_V6:
4950                 ty = scf_type_to_protocol_type(type);
4951 
4952                 (void) pthread_mutex_lock(&h->rh_lock);
4953                 scf_value_reset_locked(v, 0);
4954                 if (type == SCF_TYPE_OPAQUE) {
4955                         v->value_size = scf_opaque_decode(v->value_value,
4956                             str, sizeof (v->value_value));
4957                         if (!scf_validate_encoded_value(ty, str)) {
4958                                 (void) pthread_mutex_lock(&h->rh_lock);
4959                                 goto bad;
4960                         }
4961                 } else {
4962                         (void) strlcpy(v->value_value, str,
4963                             sizeof (v->value_value));
4964                         if (!scf_validate_encoded_value(ty, v->value_value)) {
4965                                 (void) pthread_mutex_lock(&h->rh_lock);
4966                                 goto bad;
4967                         }
4968                 }
4969                 v->value_type = ty;
4970                 (void) pthread_mutex_unlock(&h->rh_lock);
4971                 return (SCF_SUCCESS);
4972 
4973         case REP_PROTOCOL_TYPE_INVALID:
4974         default:
4975                 scf_value_reset(v);
4976                 return (scf_set_error(SCF_ERROR_TYPE_MISMATCH));
4977         }
4978 bad:
4979         scf_value_reset(v);
4980         return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4981 }
4982 
4983 int
4984 scf_iter_property_values(scf_iter_t *iter, const scf_property_t *prop)
4985 {
4986         return (datael_setup_iter(iter, &prop->rd_d,
4987             REP_PROTOCOL_ENTITY_VALUE, 0));
4988 }
4989 
4990 int
4991 scf_iter_next_value(scf_iter_t *iter, scf_value_t *v)
4992 {
4993         scf_handle_t *h = iter->iter_handle;
4994 
4995         struct rep_protocol_iter_read_value request;
4996         struct rep_protocol_value_response response;
4997 
4998         int r;
4999 
5000         if (h != v->value_handle)
5001                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
5002 
5003         (void) pthread_mutex_lock(&h->rh_lock);
5004 
5005         scf_value_reset_locked(v, 0);
5006 
5007         if (iter->iter_type == REP_PROTOCOL_ENTITY_NONE) {
5008                 (void) pthread_mutex_unlock(&h->rh_lock);
5009                 return (scf_set_error(SCF_ERROR_NOT_SET));
5010         }
5011 
5012         if (iter->iter_type != REP_PROTOCOL_ENTITY_VALUE) {
5013                 (void) pthread_mutex_unlock(&h->rh_lock);
5014                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
5015         }
5016 
5017         request.rpr_request = REP_PROTOCOL_ITER_READ_VALUE;
5018         request.rpr_iterid = iter->iter_id;
5019         request.rpr_sequence = iter->iter_sequence;
5020 
5021         r = make_door_call(h, &request, sizeof (request),
5022             &response, sizeof (response));
5023 
5024         if (r < 0) {
5025                 (void) pthread_mutex_unlock(&h->rh_lock);
5026                 DOOR_ERRORS_BLOCK(r);
5027         }
5028 
5029         if (response.rpr_response == REP_PROTOCOL_DONE) {
5030                 (void) pthread_mutex_unlock(&h->rh_lock);
5031                 return (0);
5032         }
5033         if (response.rpr_response != REP_PROTOCOL_SUCCESS) {
5034                 (void) pthread_mutex_unlock(&h->rh_lock);
5035                 return (scf_set_error(proto_error(response.rpr_response)));
5036         }
5037         iter->iter_sequence++;
5038 
5039         v->value_type = response.rpr_type;
5040 
5041         assert(scf_validate_encoded_value(response.rpr_type,
5042             response.rpr_value));
5043 
5044         if (v->value_type != REP_PROTOCOL_TYPE_OPAQUE) {
5045                 (void) strlcpy(v->value_value, response.rpr_value,
5046                     sizeof (v->value_value));
5047         } else {
5048                 v->value_size = scf_opaque_decode(v->value_value,
5049                     response.rpr_value, sizeof (v->value_value));
5050         }
5051         (void) pthread_mutex_unlock(&h->rh_lock);
5052 
5053         return (1);
5054 }
5055 
5056 int
5057 scf_property_get_value(const scf_property_t *prop, scf_value_t *v)
5058 {
5059         scf_handle_t *h = prop->rd_d.rd_handle;
5060         struct rep_protocol_property_request request;
5061         struct rep_protocol_value_response response;
5062         int r;
5063 
5064         if (h != v->value_handle)
5065                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
5066 
5067         (void) pthread_mutex_lock(&h->rh_lock);
5068 
5069         request.rpr_request = REP_PROTOCOL_PROPERTY_GET_VALUE;
5070         request.rpr_entityid = prop->rd_d.rd_entity;
5071 
5072         scf_value_reset_locked(v, 0);
5073         datael_finish_reset(&prop->rd_d);
5074 
5075         r = make_door_call(h, &request, sizeof (request),
5076             &response, sizeof (response));
5077 
5078         if (r < 0) {
5079                 (void) pthread_mutex_unlock(&h->rh_lock);
5080                 DOOR_ERRORS_BLOCK(r);
5081         }
5082 
5083         if (response.rpr_response != REP_PROTOCOL_SUCCESS &&
5084             response.rpr_response != REP_PROTOCOL_FAIL_TRUNCATED) {
5085                 (void) pthread_mutex_unlock(&h->rh_lock);
5086                 assert(response.rpr_response !=
5087                     REP_PROTOCOL_FAIL_TYPE_MISMATCH);
5088                 return (scf_set_error(proto_error(response.rpr_response)));
5089         }
5090 
5091         v->value_type = response.rpr_type;
5092         if (v->value_type != REP_PROTOCOL_TYPE_OPAQUE) {
5093                 (void) strlcpy(v->value_value, response.rpr_value,
5094                     sizeof (v->value_value));
5095         } else {
5096                 v->value_size = scf_opaque_decode(v->value_value,
5097                     response.rpr_value, sizeof (v->value_value));
5098         }
5099         (void) pthread_mutex_unlock(&h->rh_lock);
5100         return ((response.rpr_response == REP_PROTOCOL_SUCCESS)?
5101             SCF_SUCCESS : scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED));
5102 }
5103 
5104 int
5105 scf_pg_get_parent_service(const scf_propertygroup_t *pg, scf_service_t *svc)
5106 {
5107         return (datael_get_parent(&pg->rd_d, &svc->rd_d));
5108 }
5109 
5110 int
5111 scf_pg_get_parent_instance(const scf_propertygroup_t *pg, scf_instance_t *inst)
5112 {
5113         return (datael_get_parent(&pg->rd_d, &inst->rd_d));
5114 }
5115 
5116 int
5117 scf_pg_get_parent_snaplevel(const scf_propertygroup_t *pg,
5118     scf_snaplevel_t *level)
5119 {
5120         return (datael_get_parent(&pg->rd_d, &level->rd_d));
5121 }
5122 
5123 int
5124 scf_service_get_parent(const scf_service_t *svc, scf_scope_t *s)
5125 {
5126         return (datael_get_parent(&svc->rd_d, &s->rd_d));
5127 }
5128 
5129 int
5130 scf_instance_get_parent(const scf_instance_t *inst, scf_service_t *svc)
5131 {
5132         return (datael_get_parent(&inst->rd_d, &svc->rd_d));
5133 }
5134 
5135 int
5136 scf_snapshot_get_parent(const scf_snapshot_t *inst, scf_instance_t *svc)
5137 {
5138         return (datael_get_parent(&inst->rd_d, &svc->rd_d));
5139 }
5140 
5141 int
5142 scf_snaplevel_get_parent(const scf_snaplevel_t *inst, scf_snapshot_t *svc)
5143 {
5144         return (datael_get_parent(&inst->rd_d, &svc->rd_d));
5145 }
5146 
5147 /*
5148  * FMRI functions
5149  *
5150  * Note: In the scf_parse_svc_fmri(), scf_parse_file_fmri() and
5151  * scf_parse_fmri(), fmri isn't const because that would require
5152  * allocating memory. Also, note that scope, at least, is not necessarily
5153  * in the passed in fmri.
5154  */
5155 
5156 int
5157 scf_parse_svc_fmri(char *fmri, const char **scope, const char **service,
5158     const char **instance, const char **propertygroup, const char **property)
5159 {
5160         char *s, *e, *te, *tpg;
5161         char *my_s = NULL, *my_i = NULL, *my_pg = NULL, *my_p = NULL;
5162 
5163         if (scope != NULL)
5164                 *scope = NULL;
5165         if (service != NULL)
5166                 *service = NULL;
5167         if (instance != NULL)
5168                 *instance = NULL;
5169         if (propertygroup != NULL)
5170                 *propertygroup = NULL;
5171         if (property != NULL)
5172                 *property = NULL;
5173 
5174         s = fmri;
5175         e = strchr(s, '\0');
5176 
5177         if (strncmp(s, SCF_FMRI_SVC_PREFIX,
5178             sizeof (SCF_FMRI_SVC_PREFIX) - 1) == 0)
5179                 s += sizeof (SCF_FMRI_SVC_PREFIX) - 1;
5180 
5181         if (strncmp(s, SCF_FMRI_SCOPE_PREFIX,
5182             sizeof (SCF_FMRI_SCOPE_PREFIX) - 1) == 0) {
5183                 char *my_scope;
5184 
5185                 s += sizeof (SCF_FMRI_SCOPE_PREFIX) - 1;
5186                 te = strstr(s, SCF_FMRI_SERVICE_PREFIX);
5187                 if (te == NULL)
5188                         te = e;
5189 
5190                 *te = 0;
5191                 my_scope = s;
5192 
5193                 s = te;
5194                 if (s < e)
5195                         s += sizeof (SCF_FMRI_SERVICE_PREFIX) - 1;
5196 
5197                 /* If the scope ends with the suffix, remove it. */
5198                 te = strstr(my_scope, SCF_FMRI_SCOPE_SUFFIX);
5199                 if (te != NULL && te[sizeof (SCF_FMRI_SCOPE_SUFFIX) - 1] == 0)
5200                         *te = 0;
5201 
5202                 /* Validate the scope. */
5203                 if (my_scope[0] == '\0')
5204                         my_scope = SCF_FMRI_LOCAL_SCOPE;
5205                 else if (uu_check_name(my_scope, 0) == -1) {
5206                         return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
5207                 }
5208 
5209                 if (scope != NULL)
5210                         *scope = my_scope;
5211         } else {
5212                 if (scope != NULL)
5213                         *scope = SCF_FMRI_LOCAL_SCOPE;
5214         }
5215 
5216         if (s[0] != 0) {
5217                 if (strncmp(s, SCF_FMRI_SERVICE_PREFIX,
5218                     sizeof (SCF_FMRI_SERVICE_PREFIX) - 1) == 0)
5219                         s += sizeof (SCF_FMRI_SERVICE_PREFIX) - 1;
5220 
5221                 /*
5222                  * Can't validate service here because it might not be null
5223                  * terminated.
5224                  */
5225                 my_s = s;
5226         }
5227 
5228         tpg = strstr(s, SCF_FMRI_PROPERTYGRP_PREFIX);
5229         te = strstr(s, SCF_FMRI_INSTANCE_PREFIX);
5230         if (te != NULL && (tpg == NULL || te < tpg)) {
5231                 *te = 0;
5232                 te += sizeof (SCF_FMRI_INSTANCE_PREFIX) - 1;
5233 
5234                 /* Can't validate instance here either. */
5235                 my_i = s = te;
5236 
5237                 te = strstr(s, SCF_FMRI_PROPERTYGRP_PREFIX);
5238         } else {
5239                 te = tpg;
5240         }
5241 
5242         if (te != NULL) {
5243                 *te = 0;
5244                 te += sizeof (SCF_FMRI_PROPERTYGRP_PREFIX) - 1;
5245 
5246                 my_pg = s = te;
5247                 te = strstr(s, SCF_FMRI_PROPERTY_PREFIX);
5248                 if (te != NULL) {
5249                         *te = 0;
5250                         te += sizeof (SCF_FMRI_PROPERTY_PREFIX) - 1;
5251 
5252                         my_p = te;
5253                         s = te;
5254                 }
5255         }
5256 
5257         if (my_s != NULL) {
5258                 if (uu_check_name(my_s, UU_NAME_DOMAIN | UU_NAME_PATH) == -1)
5259                         return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
5260 
5261                 if (service != NULL)
5262                         *service = my_s;
5263         }
5264 
5265         if (my_i != NULL) {
5266                 if (uu_check_name(my_i, UU_NAME_DOMAIN) == -1)
5267                         return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
5268 
5269                 if (instance != NULL)
5270                         *instance = my_i;
5271         }
5272 
5273         if (my_pg != NULL) {
5274                 if (uu_check_name(my_pg, UU_NAME_DOMAIN) == -1)
5275                         return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
5276 
5277                 if (propertygroup != NULL)
5278                         *propertygroup = my_pg;
5279         }
5280 
5281         if (my_p != NULL) {
5282                 if (uu_check_name(my_p, UU_NAME_DOMAIN) == -1)
5283                         return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
5284 
5285                 if (property != NULL)
5286                         *property = my_p;
5287         }
5288 
5289         return (0);
5290 }
5291 
5292 int
5293 scf_parse_file_fmri(char *fmri, const char **scope, const char **path)
5294 {
5295         char *s, *e, *te;
5296 
5297         if (scope != NULL)
5298                 *scope = NULL;
5299 
5300         s = fmri;
5301         e = strchr(s, '\0');
5302 
5303         if (strncmp(s, SCF_FMRI_FILE_PREFIX,
5304             sizeof (SCF_FMRI_FILE_PREFIX) - 1) == 0)
5305                 s += sizeof (SCF_FMRI_FILE_PREFIX) - 1;
5306 
5307         if (strncmp(s, SCF_FMRI_SCOPE_PREFIX,
5308             sizeof (SCF_FMRI_SCOPE_PREFIX) - 1) == 0) {
5309                 char *my_scope;
5310 
5311                 s += sizeof (SCF_FMRI_SCOPE_PREFIX) - 1;
5312                 te = strstr(s, SCF_FMRI_SERVICE_PREFIX);
5313                 if (te == NULL)
5314                         te = e;
5315 
5316                 *te = 0;
5317                 my_scope = s;
5318 
5319                 s = te;
5320 
5321                 /* Validate the scope. */
5322                 if (my_scope[0] != '\0' &&
5323                     strcmp(my_scope, SCF_FMRI_LOCAL_SCOPE) != 0) {
5324                         return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
5325                 }
5326 
5327                 if (scope != NULL)
5328                         *scope = my_scope;
5329         } else {
5330                 /*
5331                  * FMRI paths must be absolute
5332                  */
5333                 if (s[0] != '/')
5334                         return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
5335         }
5336 
5337         s += sizeof (SCF_FMRI_SERVICE_PREFIX) - 1;
5338 
5339         if (s >= e)
5340                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
5341 
5342         /*
5343          * If the user requests it, return the full path of the file.
5344          */
5345         if (path != NULL) {
5346                 assert(s > fmri);
5347                 s[-1] = '/';
5348                 *path = s - 1;
5349         }
5350 
5351         return (0);
5352 }
5353 
5354 int
5355 scf_parse_fmri(char *fmri, int *type, const char **scope, const char **service,
5356     const char **instance, const char **propertygroup, const char **property)
5357 {
5358         if (strncmp(fmri, SCF_FMRI_SVC_PREFIX,
5359             sizeof (SCF_FMRI_SVC_PREFIX) - 1) == 0) {
5360                 if (type)
5361                         *type = SCF_FMRI_TYPE_SVC;
5362                 return (scf_parse_svc_fmri(fmri, scope, service, instance,
5363                     propertygroup, property));
5364         } else if (strncmp(fmri, SCF_FMRI_FILE_PREFIX,
5365             sizeof (SCF_FMRI_FILE_PREFIX) - 1) == 0) {
5366                 if (type)
5367                         *type = SCF_FMRI_TYPE_FILE;
5368                 return (scf_parse_file_fmri(fmri, scope, NULL));
5369         } else {
5370                 /*
5371                  * Parse as a svc if the fmri type is not explicitly
5372                  * specified.
5373                  */
5374                 if (type)
5375                         *type = SCF_FMRI_TYPE_SVC;
5376                 return (scf_parse_svc_fmri(fmri, scope, service, instance,
5377                     propertygroup, property));
5378         }
5379 }
5380 
5381 /*
5382  * Fails with _INVALID_ARGUMENT.  fmri and buf may be equal.
5383  */
5384 ssize_t
5385 scf_canonify_fmri(const char *fmri, char *buf, size_t bufsz)
5386 {
5387         const char *scope, *service, *instance, *pg, *property;
5388         char local[6 * REP_PROTOCOL_NAME_LEN];
5389         int r;
5390         size_t len;
5391 
5392         if (strlcpy(local, fmri, sizeof (local)) >= sizeof (local)) {
5393                 /* Should this be CONSTRAINT_VIOLATED? */
5394                 (void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
5395                 return (-1);
5396         }
5397 
5398 
5399         r = scf_parse_svc_fmri(local, &scope, &service, &instance, &pg,
5400             &property);
5401         if (r != 0)
5402                 return (-1);
5403 
5404         len = strlcpy(buf, "svc:/", bufsz);
5405 
5406         if (scope != NULL && strcmp(scope, SCF_SCOPE_LOCAL) != 0) {
5407                 len += strlcat(buf, "/", bufsz);
5408                 len += strlcat(buf, scope, bufsz);
5409         }
5410 
5411         if (service)
5412                 len += strlcat(buf, service, bufsz);
5413 
5414         if (instance) {
5415                 len += strlcat(buf, ":", bufsz);
5416                 len += strlcat(buf, instance, bufsz);
5417         }
5418 
5419         if (pg) {
5420                 len += strlcat(buf, "/:properties/", bufsz);
5421                 len += strlcat(buf, pg, bufsz);
5422         }
5423 
5424         if (property) {
5425                 len += strlcat(buf, "/", bufsz);
5426                 len += strlcat(buf, property, bufsz);
5427         }
5428 
5429         return (len);
5430 }
5431 
5432 /*
5433  * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _CONSTRAINT_VIOLATED,
5434  * _NOT_FOUND, _NOT_BOUND, _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED,
5435  * _NO_RESOURCES, _BACKEND_ACCESS.
5436  */
5437 int
5438 scf_handle_decode_fmri(scf_handle_t *h, const char *fmri, scf_scope_t *sc,
5439     scf_service_t *svc, scf_instance_t *inst, scf_propertygroup_t *pg,
5440     scf_property_t *prop, int flags)
5441 {
5442         const char *scope, *service, *instance, *propertygroup, *property;
5443         int last;
5444         char local[6 * REP_PROTOCOL_NAME_LEN];
5445         int ret;
5446         const uint32_t holds = RH_HOLD_SCOPE | RH_HOLD_SERVICE |
5447             RH_HOLD_INSTANCE | RH_HOLD_PG | RH_HOLD_PROPERTY;
5448 
5449         /*
5450          * verify that all handles match
5451          */
5452         if ((sc != NULL && h != sc->rd_d.rd_handle) ||
5453             (svc != NULL && h != svc->rd_d.rd_handle) ||
5454             (inst != NULL && h != inst->rd_d.rd_handle) ||
5455             (pg != NULL && h != pg->rd_d.rd_handle) ||
5456             (prop != NULL && h != prop->rd_d.rd_handle))
5457                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
5458 
5459         if (strlcpy(local, fmri, sizeof (local)) >= sizeof (local)) {
5460                 ret = scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
5461                 goto reset_args;
5462         }
5463 
5464         /*
5465          * We can simply return from an error in parsing, because
5466          * scf_parse_fmri sets the error code correctly.
5467          */
5468         if (scf_parse_svc_fmri(local, &scope, &service, &instance,
5469             &propertygroup, &property) == -1) {
5470                 ret = -1;
5471                 goto reset_args;
5472         }
5473 
5474         /*
5475          * the FMRI looks valid at this point -- do constraint checks.
5476          */
5477 
5478         if (instance != NULL && (flags & SCF_DECODE_FMRI_REQUIRE_NO_INSTANCE)) {
5479                 ret = scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
5480                 goto reset_args;
5481         }
5482         if (instance == NULL && (flags & SCF_DECODE_FMRI_REQUIRE_INSTANCE)) {
5483                 ret = scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
5484                 goto reset_args;
5485         }
5486 
5487         if (prop != NULL)
5488                 last = REP_PROTOCOL_ENTITY_PROPERTY;
5489         else if (pg != NULL)
5490                 last = REP_PROTOCOL_ENTITY_PROPERTYGRP;
5491         else if (inst != NULL)
5492                 last = REP_PROTOCOL_ENTITY_INSTANCE;
5493         else if (svc != NULL)
5494                 last = REP_PROTOCOL_ENTITY_SERVICE;
5495         else if (sc != NULL)
5496                 last = REP_PROTOCOL_ENTITY_SCOPE;
5497         else
5498                 last = REP_PROTOCOL_ENTITY_NONE;
5499 
5500         if (flags & SCF_DECODE_FMRI_EXACT) {
5501                 int last_fmri;
5502 
5503                 if (property != NULL)
5504                         last_fmri = REP_PROTOCOL_ENTITY_PROPERTY;
5505                 else if (propertygroup != NULL)
5506                         last_fmri = REP_PROTOCOL_ENTITY_PROPERTYGRP;
5507                 else if (instance != NULL)
5508                         last_fmri = REP_PROTOCOL_ENTITY_INSTANCE;
5509                 else if (service != NULL)
5510                         last_fmri = REP_PROTOCOL_ENTITY_SERVICE;
5511                 else if (scope != NULL)
5512                         last_fmri = REP_PROTOCOL_ENTITY_SCOPE;
5513                 else
5514                         last_fmri = REP_PROTOCOL_ENTITY_NONE;
5515 
5516                 if (last != last_fmri) {
5517                         ret = scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
5518                         goto reset_args;
5519                 }
5520         }
5521 
5522         if ((flags & SCF_DECODE_FMRI_TRUNCATE) &&
5523             last == REP_PROTOCOL_ENTITY_NONE) {
5524                 ret = 0;                                /* nothing to do */
5525                 goto reset_args;
5526         }
5527 
5528         if (!(flags & SCF_DECODE_FMRI_TRUNCATE))
5529                 last = REP_PROTOCOL_ENTITY_NONE;        /* never stop */
5530 
5531         /*
5532          * passed the constraint checks -- try to grab the thing itself.
5533          */
5534 
5535         handle_hold_subhandles(h, holds);
5536         if (sc == NULL)
5537                 sc = h->rh_scope;
5538         else
5539                 datael_reset(&sc->rd_d);
5540 
5541         if (svc == NULL)
5542                 svc = h->rh_service;
5543         else
5544                 datael_reset(&svc->rd_d);
5545 
5546         if (inst == NULL)
5547                 inst = h->rh_instance;
5548         else
5549                 datael_reset(&inst->rd_d);
5550 
5551         if (pg == NULL)
5552                 pg = h->rh_pg;
5553         else
5554                 datael_reset(&pg->rd_d);
5555 
5556         if (prop == NULL)
5557                 prop = h->rh_property;
5558         else
5559                 datael_reset(&prop->rd_d);
5560 
5561         /*
5562          * We only support local scopes, but we check *after* getting
5563          * the local scope, so that any repository-related errors take
5564          * precedence.
5565          */
5566         if (scf_handle_get_scope(h, SCF_SCOPE_LOCAL, sc) == -1) {
5567                 handle_rele_subhandles(h, holds);
5568                 ret = -1;
5569                 goto reset_args;
5570         }
5571 
5572         if (scope != NULL && strcmp(scope, SCF_FMRI_LOCAL_SCOPE) != 0) {
5573                 handle_rele_subhandles(h, holds);
5574                 ret = scf_set_error(SCF_ERROR_NOT_FOUND);
5575                 goto reset_args;
5576         }
5577 
5578 
5579         if (service == NULL || last == REP_PROTOCOL_ENTITY_SCOPE) {
5580                 handle_rele_subhandles(h, holds);
5581                 return (0);
5582         }
5583 
5584         if (scf_scope_get_service(sc, service, svc) == -1) {
5585                 handle_rele_subhandles(h, holds);
5586                 ret = -1;
5587                 assert(scf_error() != SCF_ERROR_NOT_SET);
5588                 if (scf_error() == SCF_ERROR_DELETED)
5589                         (void) scf_set_error(SCF_ERROR_NOT_FOUND);
5590                 goto reset_args;
5591         }
5592 
5593         if (last == REP_PROTOCOL_ENTITY_SERVICE) {
5594                 handle_rele_subhandles(h, holds);
5595                 return (0);
5596         }
5597 
5598         if (instance == NULL) {
5599                 if (propertygroup == NULL ||
5600                     last == REP_PROTOCOL_ENTITY_INSTANCE) {
5601                         handle_rele_subhandles(h, holds);
5602                         return (0);
5603                 }
5604 
5605                 if (scf_service_get_pg(svc, propertygroup, pg) == -1) {
5606                         handle_rele_subhandles(h, holds);
5607                         ret = -1;
5608                         assert(scf_error() != SCF_ERROR_NOT_SET);
5609                         if (scf_error() == SCF_ERROR_DELETED)
5610                                 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
5611                         goto reset_args;
5612                 }
5613         } else {
5614                 if (scf_service_get_instance(svc, instance, inst) == -1) {
5615                         handle_rele_subhandles(h, holds);
5616                         ret = -1;
5617                         assert(scf_error() != SCF_ERROR_NOT_SET);
5618                         if (scf_error() == SCF_ERROR_DELETED)
5619                                 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
5620                         goto reset_args;
5621                 }
5622 
5623                 if (propertygroup == NULL ||
5624                     last == REP_PROTOCOL_ENTITY_INSTANCE) {
5625                         handle_rele_subhandles(h, holds);
5626                         return (0);
5627                 }
5628 
5629                 if (scf_instance_get_pg(inst, propertygroup, pg) == -1) {
5630                         handle_rele_subhandles(h, holds);
5631                         ret = -1;
5632                         assert(scf_error() != SCF_ERROR_NOT_SET);
5633                         if (scf_error() == SCF_ERROR_DELETED)
5634                                 (void) scf_set_error(SCF_ERROR_NOT_FOUND);
5635                         goto reset_args;
5636                 }
5637         }
5638 
5639         if (property == NULL || last == REP_PROTOCOL_ENTITY_PROPERTYGRP) {
5640                 handle_rele_subhandles(h, holds);
5641                 return (0);
5642         }
5643 
5644         if (scf_pg_get_property(pg, property, prop) == -1) {
5645                 handle_rele_subhandles(h, holds);
5646                 ret = -1;
5647                 assert(scf_error() != SCF_ERROR_NOT_SET);
5648                 if (scf_error() == SCF_ERROR_DELETED)
5649                         (void) scf_set_error(SCF_ERROR_NOT_FOUND);
5650                 goto reset_args;
5651         }
5652 
5653         handle_rele_subhandles(h, holds);
5654         return (0);
5655 
5656 reset_args:
5657         if (sc != NULL)
5658                 datael_reset(&sc->rd_d);
5659         if (svc != NULL)
5660                 datael_reset(&svc->rd_d);
5661         if (inst != NULL)
5662                 datael_reset(&inst->rd_d);
5663         if (pg != NULL)
5664                 datael_reset(&pg->rd_d);
5665         if (prop != NULL)
5666                 datael_reset(&prop->rd_d);
5667 
5668         return (ret);
5669 }
5670 
5671 /*
5672  * Fails with _NOT_BOUND, _CONNECTION_BROKEN, _INTERNAL (server response too
5673  * big, bad entity id, request not applicable to entity, name too long for
5674  * buffer), _NOT_SET, or _DELETED.
5675  */
5676 ssize_t
5677 scf_scope_to_fmri(const scf_scope_t *scope, char *out, size_t sz)
5678 {
5679         ssize_t r, len;
5680 
5681         char tmp[REP_PROTOCOL_NAME_LEN];
5682 
5683         r = scf_scope_get_name(scope, tmp, sizeof (tmp));
5684 
5685         if (r <= 0)
5686                 return (r);
5687 
5688         len = strlcpy(out, SCF_FMRI_SVC_PREFIX, sz);
5689         if (strcmp(tmp, SCF_FMRI_LOCAL_SCOPE) != 0) {
5690                 if (len >= sz)
5691                         return (len + r + sizeof (SCF_FMRI_SCOPE_SUFFIX) - 1);
5692 
5693                 len = strlcat(out, tmp, sz);
5694                 if (len >= sz)
5695                         return (len + sizeof (SCF_FMRI_SCOPE_SUFFIX) - 1);
5696                 len = strlcat(out,
5697                     SCF_FMRI_SCOPE_SUFFIX SCF_FMRI_SERVICE_PREFIX, sz);
5698         }
5699 
5700         return (len);
5701 }
5702 
5703 /*
5704  * Fails with _NOT_BOUND, _CONNECTION_BROKEN, _INTERNAL (server response too
5705  * big, bad element id, bad ids, bad types, scope has no parent, request not
5706  * applicable to entity, name too long), _NOT_SET, _DELETED,
5707  */
5708 ssize_t
5709 scf_service_to_fmri(const scf_service_t *svc, char *out, size_t sz)
5710 {
5711         scf_handle_t *h = svc->rd_d.rd_handle;
5712         scf_scope_t *scope = HANDLE_HOLD_SCOPE(h);
5713         ssize_t r, len;
5714 
5715         char tmp[REP_PROTOCOL_NAME_LEN];
5716 
5717         r = datael_get_parent(&svc->rd_d, &scope->rd_d);
5718         if (r != SCF_SUCCESS) {
5719                 HANDLE_RELE_SCOPE(h);
5720 
5721                 assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
5722                 return (-1);
5723         }
5724         if (out != NULL && sz > 0)
5725                 len = scf_scope_to_fmri(scope, out, sz);
5726         else
5727                 len = scf_scope_to_fmri(scope, tmp, 2);
5728 
5729         HANDLE_RELE_SCOPE(h);
5730 
5731         if (len < 0)
5732                 return (-1);
5733 
5734         if (out == NULL || len >= sz)
5735                 len += sizeof (SCF_FMRI_SERVICE_PREFIX) - 1;
5736         else
5737                 len = strlcat(out, SCF_FMRI_SERVICE_PREFIX, sz);
5738 
5739         r = scf_service_get_name(svc, tmp, sizeof (tmp));
5740         if (r < 0)
5741                 return (r);
5742 
5743         if (out == NULL || len >= sz)
5744                 len += r;
5745         else
5746                 len = strlcat(out, tmp, sz);
5747 
5748         return (len);
5749 }
5750 
5751 ssize_t
5752 scf_instance_to_fmri(const scf_instance_t *inst, char *out, size_t sz)
5753 {
5754         scf_handle_t *h = inst->rd_d.rd_handle;
5755         scf_service_t *svc = HANDLE_HOLD_SERVICE(h);
5756         ssize_t r, len;
5757 
5758         char tmp[REP_PROTOCOL_NAME_LEN];
5759 
5760         r = datael_get_parent(&inst->rd_d, &svc->rd_d);
5761         if (r != SCF_SUCCESS) {
5762                 HANDLE_RELE_SERVICE(h);
5763                 return (-1);
5764         }
5765 
5766         len = scf_service_to_fmri(svc, out, sz);
5767 
5768         HANDLE_RELE_SERVICE(h);
5769 
5770         if (len < 0)
5771                 return (len);
5772 
5773         if (len >= sz)
5774                 len += sizeof (SCF_FMRI_INSTANCE_PREFIX) - 1;
5775         else
5776                 len = strlcat(out, SCF_FMRI_INSTANCE_PREFIX, sz);
5777 
5778         r = scf_instance_get_name(inst, tmp, sizeof (tmp));
5779         if (r < 0)
5780                 return (r);
5781 
5782         if (len >= sz)
5783                 len += r;
5784         else
5785                 len = strlcat(out, tmp, sz);
5786 
5787         return (len);
5788 }
5789 
5790 ssize_t
5791 scf_pg_to_fmri(const scf_propertygroup_t *pg, char *out, size_t sz)
5792 {
5793         scf_handle_t *h = pg->rd_d.rd_handle;
5794 
5795         struct rep_protocol_entity_parent_type request;
5796         struct rep_protocol_integer_response response;
5797 
5798         char tmp[REP_PROTOCOL_NAME_LEN];
5799         ssize_t len, r;
5800 
5801         (void) pthread_mutex_lock(&h->rh_lock);
5802         request.rpr_request = REP_PROTOCOL_ENTITY_PARENT_TYPE;
5803         request.rpr_entityid = pg->rd_d.rd_entity;
5804 
5805         datael_finish_reset(&pg->rd_d);
5806         r = make_door_call(h, &request, sizeof (request),
5807             &response, sizeof (response));
5808         (void) pthread_mutex_unlock(&h->rh_lock);
5809 
5810         if (r < 0)
5811                 DOOR_ERRORS_BLOCK(r);
5812 
5813         if (response.rpr_response != REP_PROTOCOL_SUCCESS ||
5814             r < sizeof (response)) {
5815                 return (scf_set_error(proto_error(response.rpr_response)));
5816         }
5817 
5818         switch (response.rpr_value) {
5819         case REP_PROTOCOL_ENTITY_SERVICE: {
5820                 scf_service_t *svc;
5821 
5822                 svc = HANDLE_HOLD_SERVICE(h);
5823 
5824                 r = datael_get_parent(&pg->rd_d, &svc->rd_d);
5825 
5826                 if (r == SCF_SUCCESS)
5827                         len = scf_service_to_fmri(svc, out, sz);
5828 
5829                 HANDLE_RELE_SERVICE(h);
5830                 break;
5831         }
5832 
5833         case REP_PROTOCOL_ENTITY_INSTANCE: {
5834                 scf_instance_t *inst;
5835 
5836                 inst = HANDLE_HOLD_INSTANCE(h);
5837 
5838                 r = datael_get_parent(&pg->rd_d, &inst->rd_d);
5839 
5840                 if (r == SCF_SUCCESS)
5841                         len = scf_instance_to_fmri(inst, out, sz);
5842 
5843                 HANDLE_RELE_INSTANCE(h);
5844                 break;
5845         }
5846 
5847         case REP_PROTOCOL_ENTITY_SNAPLEVEL: {
5848                 scf_instance_t *inst = HANDLE_HOLD_INSTANCE(h);
5849                 scf_snapshot_t *snap = HANDLE_HOLD_SNAPSHOT(h);
5850                 scf_snaplevel_t *level = HANDLE_HOLD_SNAPLVL(h);
5851 
5852                 r = datael_get_parent(&pg->rd_d, &level->rd_d);
5853 
5854                 if (r == SCF_SUCCESS)
5855                         r = datael_get_parent(&level->rd_d, &snap->rd_d);
5856 
5857                 if (r == SCF_SUCCESS)
5858                         r = datael_get_parent(&snap->rd_d, &inst->rd_d);
5859 
5860                 if (r == SCF_SUCCESS)
5861                         len = scf_instance_to_fmri(inst, out, sz);
5862 
5863                 HANDLE_RELE_INSTANCE(h);
5864                 HANDLE_RELE_SNAPSHOT(h);
5865                 HANDLE_RELE_SNAPLVL(h);
5866                 break;
5867         }
5868 
5869         default:
5870                 return (scf_set_error(SCF_ERROR_INTERNAL));
5871         }
5872 
5873         if (r != SCF_SUCCESS)
5874                 return (r);
5875 
5876         if (len >= sz)
5877                 len += sizeof (SCF_FMRI_PROPERTYGRP_PREFIX) - 1;
5878         else
5879                 len = strlcat(out, SCF_FMRI_PROPERTYGRP_PREFIX, sz);
5880 
5881         r = scf_pg_get_name(pg, tmp, sizeof (tmp));
5882 
5883         if (r < 0)
5884                 return (r);
5885 
5886         if (len >= sz)
5887                 len += r;
5888         else
5889                 len = strlcat(out, tmp, sz);
5890 
5891         return (len);
5892 }
5893 
5894 ssize_t
5895 scf_property_to_fmri(const scf_property_t *prop, char *out, size_t sz)
5896 {
5897         scf_handle_t *h = prop->rd_d.rd_handle;
5898         scf_propertygroup_t *pg = HANDLE_HOLD_PG(h);
5899 
5900         char tmp[REP_PROTOCOL_NAME_LEN];
5901         ssize_t len;
5902         int r;
5903 
5904         r = datael_get_parent(&prop->rd_d, &pg->rd_d);
5905         if (r != SCF_SUCCESS) {
5906                 HANDLE_RELE_PG(h);
5907                 return (-1);
5908         }
5909 
5910         len = scf_pg_to_fmri(pg, out, sz);
5911 
5912         HANDLE_RELE_PG(h);
5913 
5914         if (len >= sz)
5915                 len += sizeof (SCF_FMRI_PROPERTY_PREFIX) - 1;
5916         else
5917                 len = strlcat(out, SCF_FMRI_PROPERTY_PREFIX, sz);
5918 
5919         r = scf_property_get_name(prop, tmp, sizeof (tmp));
5920 
5921         if (r < 0)
5922                 return (r);
5923 
5924         if (len >= sz)
5925                 len += r;
5926         else
5927                 len = strlcat(out, tmp, sz);
5928 
5929         return (len);
5930 }
5931 
5932 /*
5933  * Fails with _HANDLE_MISMATCH, _NOT_BOUND, _CONNECTION_BROKEN, _INTERNAL
5934  * (server response too big, bad entity id, request not applicable to entity,
5935  * name too long for buffer, bad element id, iter already exists, element
5936  * cannot have children of type, type is invalid, iter was reset, sequence
5937  * was bad, iter walks values, iter does not walk type entities),
5938  * _NOT_SET, _DELETED, or _CONSTRAINT_VIOLATED,
5939  * _NOT_FOUND (scope has no parent),  _INVALID_ARGUMENT, _NO_RESOURCES,
5940  * _BACKEND_ACCESS.
5941  */
5942 int
5943 scf_pg_get_underlying_pg(const scf_propertygroup_t *pg,
5944     scf_propertygroup_t *out)
5945 {
5946         scf_handle_t *h = pg->rd_d.rd_handle;
5947         scf_service_t *svc;
5948         scf_instance_t *inst;
5949 
5950         char me[REP_PROTOCOL_NAME_LEN];
5951         int r;
5952 
5953         if (h != out->rd_d.rd_handle)
5954                 return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
5955 
5956         r = scf_pg_get_name(pg, me, sizeof (me));
5957 
5958         if (r < 0)
5959                 return (r);
5960 
5961         svc = HANDLE_HOLD_SERVICE(h);
5962         inst = HANDLE_HOLD_INSTANCE(h);
5963 
5964         r = datael_get_parent(&pg->rd_d, &inst->rd_d);
5965 
5966         if (r == SCF_SUCCESS) {
5967                 r = datael_get_parent(&inst->rd_d, &svc->rd_d);
5968                 if (r != SCF_SUCCESS) {
5969                         goto out;
5970                 }
5971                 r = scf_service_get_pg(svc, me, out);
5972         } else {
5973                 r = scf_set_error(SCF_ERROR_NOT_FOUND);
5974         }
5975 
5976 out:
5977         HANDLE_RELE_SERVICE(h);
5978         HANDLE_RELE_INSTANCE(h);
5979         return (r);
5980 }
5981 
5982 #define LEGACY_SCHEME   "lrc:"
5983 #define LEGACY_UNKNOWN  "unknown"
5984 
5985 /*
5986  * Implementation of scf_walk_fmri()
5987  *
5988  * This is a little tricky due to the many-to-many relationship between patterns
5989  * and matches.  We need to be able to satisfy the following requirements:
5990  *
5991  *      1) Detect patterns which match more than one FMRI, and be able to
5992  *         report which FMRIs have been matched.
5993  *      2) Detect patterns which have not matched any FMRIs
5994  *      3) Visit each matching FMRI exactly once across all patterns
5995  *      4) Ignore FMRIs which have only been matched due to multiply-matching
5996  *         patterns.
5997  *
5998  * We maintain an array of scf_pattern_t structures, one for each argument, and
5999  * maintain a linked list of scf_match_t structures for each one.  We first
6000  * qualify each pattern's type:
6001  *
6002  *      PATTERN_INVALID         The argument is invalid (too long).
6003  *
6004  *      PATTERN_EXACT           The pattern is a complete FMRI.  The list of
6005  *                              matches contains only a single entry.
6006  *
6007  *      PATTERN_GLOB            The pattern will be matched against all
6008  *                              FMRIs via fnmatch() in the second phase.
6009  *                              Matches will be added to the pattern's list
6010  *                              as they are found.
6011  *
6012  *      PATTERN_PARTIAL         Everything else.  We will assume that this is
6013  *                              an abbreviated FMRI, and match according to
6014  *                              our abbreviated FMRI rules.  Matches will be
6015  *                              added to the pattern's list as they are found.
6016  *
6017  * The first pass searches for arguments that are complete FMRIs.  These are
6018  * classified as EXACT patterns and do not necessitate searching the entire
6019  * tree.
6020  *
6021  * Once this is done, if we have any GLOB or PARTIAL patterns (or if no
6022  * arguments were given), we iterate over all services and instances in the
6023  * repository, looking for matches.
6024  *
6025  * When a match is found, we add the match to the pattern's list.  We also enter
6026  * the match into a hash table, resulting in something like this:
6027  *
6028  *       scf_pattern_t       scf_match_t
6029  *     +---------------+      +-------+     +-------+
6030  *     | pattern 'foo' |----->| match |---->| match |
6031  *     +---------------+      +-------+     +-------+
6032  *                                |             |
6033  *           scf_match_key_t      |             |
6034  *           +--------------+     |             |
6035  *           | FMRI bar/foo |<----+             |
6036  *           +--------------+                   |
6037  *           | FMRI baz/foo |<------------------+
6038  *           +--------------+
6039  *
6040  * Once we have all of this set up, we do one pass to report patterns matching
6041  * multiple FMRIs (if SCF_WALK_MULTIPLE is not set) and patterns for which no
6042  * match was found.
6043  *
6044  * Finally, we walk through all valid patterns, and for each match, if we
6045  * haven't already seen the match (as recorded in the hash table), then we
6046  * execute the callback.
6047  */
6048 
6049 struct scf_matchkey;
6050 struct scf_match;
6051 
6052 /*
6053  * scf_matchkey_t
6054  */
6055 typedef struct scf_matchkey {
6056         char                    *sk_fmri;       /* Matching FMRI */
6057         char                    *sk_legacy;     /* Legacy name */
6058         int                     sk_seen;        /* If we've been seen */
6059         struct scf_matchkey     *sk_next;       /* Next in hash chain */
6060 } scf_matchkey_t;
6061 
6062 /*
6063  * scf_match_t
6064  */
6065 typedef struct scf_match {
6066         scf_matchkey_t          *sm_key;
6067         struct scf_match        *sm_next;
6068 } scf_match_t;
6069 
6070 #define WALK_HTABLE_SIZE        123
6071 
6072 /*
6073  * scf_get_key()
6074  *
6075  * Given an FMRI and a hash table, returns the scf_matchkey_t corresponding to
6076  * this FMRI.  If the FMRI does not exist, it is added to the hash table.  If a
6077  * new entry cannot be allocated due to lack of memory, NULL is returned.
6078  */
6079 static scf_matchkey_t *
6080 scf_get_key(scf_matchkey_t **htable, const char *fmri, const char *legacy)
6081 {
6082         uint_t h = 0, g;
6083         const char *p, *k;
6084         scf_matchkey_t *key;
6085 
6086         k = strstr(fmri, ":/");
6087         assert(k != NULL);
6088         k += 2;
6089 
6090         /*
6091          * Generic hash function from uts/common/os/modhash.c.
6092          */
6093         for (p = k; *p != '\0'; ++p) {
6094                 h = (h << 4) + *p;
6095                 if ((g = (h & 0xf0000000)) != 0) {
6096                         h ^= (g >> 24);
6097                         h ^= g;
6098                 }
6099         }
6100 
6101         h %= WALK_HTABLE_SIZE;
6102 
6103         /*
6104          * Search for an existing key
6105          */
6106         for (key = htable[h]; key != NULL; key = key->sk_next) {
6107                 if (strcmp(key->sk_fmri, fmri) == 0)
6108                         return (key);
6109         }
6110 
6111         if ((key = calloc(sizeof (scf_matchkey_t), 1)) == NULL)
6112                 return (NULL);
6113 
6114         /*
6115          * Add new key to hash table.
6116          */
6117         if ((key->sk_fmri = strdup(fmri)) == NULL) {
6118                 free(key);
6119                 return (NULL);
6120         }
6121 
6122         if (legacy == NULL) {
6123                 key->sk_legacy = NULL;
6124         } else if ((key->sk_legacy = strdup(legacy)) == NULL) {
6125                 free(key->sk_fmri);
6126                 free(key);
6127                 return (NULL);
6128         }
6129 
6130         key->sk_next = htable[h];
6131         htable[h] = key;
6132 
6133         return (key);
6134 }
6135 
6136 /*
6137  * Given an FMRI, insert it into the pattern's list appropriately.
6138  * svc_explicit indicates whether matching services should take
6139  * precedence over matching instances.
6140  */
6141 static scf_error_t
6142 scf_add_match(scf_matchkey_t **htable, const char *fmri, const char *legacy,
6143     scf_pattern_t *pattern, int svc_explicit)
6144 {
6145         scf_match_t *match;
6146 
6147         /*
6148          * If svc_explicit is set, enforce the constaint that matching
6149          * instances take precedence over matching services. Otherwise,
6150          * matching services take precedence over matching instances.
6151          */
6152         if (svc_explicit) {
6153                 scf_match_t *next, *prev;
6154                 /*
6155                  * If we match an instance, check to see if we must remove
6156                  * any matching services (for SCF_WALK_EXPLICIT).
6157                  */
6158                 for (prev = match = pattern->sp_matches; match != NULL;
6159                     match = next) {
6160                         size_t len = strlen(match->sm_key->sk_fmri);
6161                         next = match->sm_next;
6162                         if (strncmp(match->sm_key->sk_fmri, fmri, len) == 0 &&
6163                             fmri[len] == ':') {
6164                                 if (prev == match)
6165                                         pattern->sp_matches = match->sm_next;
6166                                 else
6167                                         prev->sm_next = match->sm_next;
6168                                 pattern->sp_matchcount--;
6169                                 free(match);
6170                         } else
6171                                 prev = match;
6172                 }
6173         } else {
6174                 /*
6175                  * If we've matched a service don't add any instances (for
6176                  * SCF_WALK_SERVICE).
6177                  */
6178                 for (match = pattern->sp_matches; match != NULL;
6179                     match = match->sm_next) {
6180                         size_t len = strlen(match->sm_key->sk_fmri);
6181                         if (strncmp(match->sm_key->sk_fmri, fmri, len) == 0 &&
6182                             fmri[len] == ':')
6183                                 return (0);
6184                 }
6185         }
6186 
6187         if ((match = malloc(sizeof (scf_match_t))) == NULL)
6188                 return (SCF_ERROR_NO_MEMORY);
6189 
6190         if ((match->sm_key = scf_get_key(htable, fmri, legacy)) == NULL) {
6191                 free(match);
6192                 return (SCF_ERROR_NO_MEMORY);
6193         }
6194 
6195         match->sm_next = pattern->sp_matches;
6196         pattern->sp_matches = match;
6197         pattern->sp_matchcount++;
6198 
6199         return (0);
6200 }
6201 
6202 /*
6203  * Returns 1 if the fmri matches the given pattern, 0 otherwise.
6204  */
6205 int
6206 scf_cmp_pattern(char *fmri, scf_pattern_t *pattern)
6207 {
6208         char *tmp;
6209 
6210         if (pattern->sp_type == PATTERN_GLOB) {
6211                 if (fnmatch(pattern->sp_arg, fmri, 0) == 0)
6212                         return (1);
6213         } else if (pattern->sp_type == PATTERN_PARTIAL &&
6214             (tmp = strstr(fmri, pattern->sp_arg)) != NULL) {
6215                 /*
6216                  * We only allow partial matches anchored on the end of
6217                  * a service or instance, and beginning on an element
6218                  * boundary.
6219                  */
6220                 if (tmp != fmri && tmp[-1] != '/' && tmp[-1] != ':' &&
6221                     tmp[0] != ':')
6222                         return (0);
6223                 tmp += strlen(pattern->sp_arg);
6224                 if (tmp != fmri + strlen(fmri) && tmp[0] != ':' &&
6225                     tmp[-1] != ':')
6226                         return (0);
6227 
6228                 /*
6229                  * If the user has supplied a short pattern that matches
6230                  * 'svc:/' or 'lrc:/', ignore it.
6231                  */
6232                 if (tmp <= fmri + 4)
6233                         return (0);
6234 
6235                 return (1);
6236         }
6237 
6238         return (0);
6239 }
6240 
6241 /*
6242  * Attempts to match the given FMRI against a set of patterns, keeping track of
6243  * the results.
6244  */
6245 static scf_error_t
6246 scf_pattern_match(scf_matchkey_t **htable, char *fmri, const char *legacy,
6247     int npattern, scf_pattern_t *pattern, int svc_explicit)
6248 {
6249         int i;
6250         int ret = 0;
6251 
6252         for (i = 0; i < npattern; i++) {
6253                 if (scf_cmp_pattern(fmri, &pattern[i]) &&
6254                     (ret = scf_add_match(htable, fmri,
6255                     legacy, &pattern[i], svc_explicit)) != 0)
6256                         return (ret);
6257         }
6258 
6259         return (0);
6260 }
6261 
6262 /*
6263  * Fails with _INVALID_ARGUMENT, _HANDLE_DESTROYED, _INTERNAL (bad server
6264  * response or id in use), _NO_MEMORY, _HANDLE_MISMATCH, _CONSTRAINT_VIOLATED,
6265  * _NOT_FOUND, _NOT_BOUND, _CONNECTION_BROKEN, _NOT_SET, _DELETED,
6266  * _NO_RESOURCES, _BACKEND_ACCESS, _TYPE_MISMATCH.
6267  */
6268 scf_error_t
6269 scf_walk_fmri(scf_handle_t *h, int argc, char **argv, int flags,
6270     scf_walk_callback callback, void *data, int *err,
6271     void (*errfunc)(const char *, ...))
6272 {
6273         scf_pattern_t *pattern = NULL;
6274         int i;
6275         char *fmri = NULL;
6276         ssize_t max_fmri_length;
6277         scf_service_t *svc = NULL;
6278         scf_instance_t *inst = NULL;
6279         scf_iter_t *iter = NULL, *sciter = NULL, *siter = NULL;
6280         scf_scope_t *scope = NULL;
6281         scf_propertygroup_t *pg = NULL;
6282         scf_property_t *prop = NULL;
6283         scf_value_t *value = NULL;
6284         int ret = 0;
6285         scf_matchkey_t **htable = NULL;
6286         int pattern_search = 0;
6287         ssize_t max_name_length;
6288         char *pgname = NULL;
6289         scf_walkinfo_t info;
6290         boolean_t partial_fmri = B_FALSE;
6291         boolean_t wildcard_fmri = B_FALSE;
6292 
6293 #ifndef NDEBUG
6294         if (flags & SCF_WALK_EXPLICIT)
6295                 assert(flags & SCF_WALK_SERVICE);
6296         if (flags & SCF_WALK_NOINSTANCE)
6297                 assert(flags & SCF_WALK_SERVICE);
6298         if (flags & SCF_WALK_PROPERTY)
6299                 assert(!(flags & SCF_WALK_LEGACY));
6300 #endif
6301 
6302         /*
6303          * Setup initial variables
6304          */
6305         max_fmri_length = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
6306         assert(max_fmri_length != -1);
6307         max_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
6308         assert(max_name_length != -1);
6309 
6310         if ((fmri = malloc(max_fmri_length + 1)) == NULL ||
6311             (pgname = malloc(max_name_length + 1)) == NULL) {
6312                 ret = SCF_ERROR_NO_MEMORY;
6313                 goto error;
6314         }
6315 
6316         if (argc == 0) {
6317                 pattern = NULL;
6318         } else if ((pattern = calloc(argc, sizeof (scf_pattern_t)))
6319             == NULL) {
6320                 ret = SCF_ERROR_NO_MEMORY;
6321                 goto error;
6322         }
6323 
6324         if ((htable = calloc(WALK_HTABLE_SIZE, sizeof (void *))) == NULL) {
6325                 ret = SCF_ERROR_NO_MEMORY;
6326                 goto error;
6327         }
6328 
6329         if ((inst = scf_instance_create(h)) == NULL ||
6330             (svc = scf_service_create(h)) == NULL ||
6331             (iter = scf_iter_create(h)) == NULL ||
6332             (sciter = scf_iter_create(h)) == NULL ||
6333             (siter = scf_iter_create(h)) == NULL ||
6334             (scope = scf_scope_create(h)) == NULL ||
6335             (pg = scf_pg_create(h)) == NULL ||
6336             (prop = scf_property_create(h)) == NULL ||
6337             (value = scf_value_create(h)) == NULL) {
6338                 ret = scf_error();
6339                 goto error;
6340         }
6341 
6342         /*
6343          * For each fmri given, we first check to see if it's a full service,
6344          * instance, property group, or property FMRI.  This avoids having to do
6345          * the (rather expensive) walk of all instances.  Any element which does
6346          * not match a full fmri is identified as a globbed pattern or a partial
6347          * fmri and stored in a private array when walking instances.
6348          */
6349         for (i = 0; i < argc; i++) {
6350                 const char *scope_name, *svc_name, *inst_name, *pg_name;
6351                 const char *prop_name;
6352 
6353                 if (strlen(argv[i]) > max_fmri_length) {
6354                         errfunc(scf_get_msg(SCF_MSG_ARGTOOLONG), argv[i]);
6355                         if (err != NULL)
6356                                 *err = UU_EXIT_FATAL;
6357                         continue;
6358                 }
6359 
6360                 (void) strcpy(fmri, argv[i]);
6361                 if (scf_parse_svc_fmri(fmri, &scope_name, &svc_name, &inst_name,
6362                     &pg_name, &prop_name) != SCF_SUCCESS)
6363                         goto badfmri;
6364 
6365                 /*
6366                  * If the user has specified SCF_WALK_PROPERTY, allow property
6367                  * groups and properties.
6368                  */
6369                 if (pg_name != NULL || prop_name != NULL) {
6370                         if (!(flags & SCF_WALK_PROPERTY))
6371                                 goto badfmri;
6372 
6373                         if (scf_handle_decode_fmri(h, argv[i], NULL, NULL,
6374                             NULL, pg, prop, 0) != 0)
6375                                 goto badfmri;
6376 
6377                         if (scf_pg_get_name(pg, NULL, 0) < 0 &&
6378                             scf_property_get_name(prop, NULL, 0) < 0)
6379                                 goto badfmri;
6380 
6381                         if (scf_canonify_fmri(argv[i], fmri, max_fmri_length)
6382                             <= 0) {
6383                                 /*
6384                                  * scf_parse_fmri() should have caught this.
6385                                  */
6386                                 abort();
6387                         }
6388 
6389                         if ((ret = scf_add_match(htable, fmri, NULL,
6390                             &pattern[i], flags & SCF_WALK_EXPLICIT)) != 0)
6391                                 goto error;
6392 
6393                         if ((pattern[i].sp_arg = strdup(argv[i])) == NULL) {
6394                                 ret = SCF_ERROR_NO_MEMORY;
6395                                 goto error;
6396                         }
6397                         pattern[i].sp_type = PATTERN_EXACT;
6398                 }
6399 
6400                 /*
6401                  * We need at least a service name
6402                  */
6403                 if (scope_name == NULL || svc_name == NULL)
6404                         goto badfmri;
6405 
6406                 /*
6407                  * If we have a fully qualified instance, add it to our list of
6408                  * fmris to watch.
6409                  */
6410                 if (inst_name != NULL) {
6411                         if (flags & SCF_WALK_NOINSTANCE)
6412                                 goto badfmri;
6413 
6414                         if (scf_handle_decode_fmri(h, argv[i], NULL, NULL,
6415                             inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) != 0)
6416                                 goto badfmri;
6417 
6418                         if (scf_canonify_fmri(argv[i], fmri, max_fmri_length)
6419                             <= 0)
6420                                 goto badfmri;
6421 
6422                         if ((ret = scf_add_match(htable, fmri, NULL,
6423                             &pattern[i], flags & SCF_WALK_EXPLICIT)) != 0)
6424                                 goto error;
6425 
6426                         if ((pattern[i].sp_arg = strdup(argv[i])) == NULL) {
6427                                 ret = SCF_ERROR_NO_MEMORY;
6428                                 goto error;
6429                         }
6430                         pattern[i].sp_type = PATTERN_EXACT;
6431 
6432                         continue;
6433                 }
6434 
6435                 if (scf_handle_decode_fmri(h, argv[i], NULL, svc,
6436                     NULL, NULL, NULL, SCF_DECODE_FMRI_EXACT) !=
6437                     SCF_SUCCESS)
6438                         goto badfmri;
6439 
6440                 /*
6441                  * If the user allows for bare services, then simply
6442                  * pass this service on.
6443                  */
6444                 if (flags & SCF_WALK_SERVICE) {
6445                         if (scf_service_to_fmri(svc, fmri,
6446                             max_fmri_length + 1) <= 0) {
6447                                 ret = scf_error();
6448                                 goto error;
6449                         }
6450 
6451                         if ((ret = scf_add_match(htable, fmri, NULL,
6452                             &pattern[i], flags & SCF_WALK_EXPLICIT)) != 0)
6453                                 goto error;
6454 
6455                         if ((pattern[i].sp_arg = strdup(argv[i]))
6456                             == NULL) {
6457                                 ret = SCF_ERROR_NO_MEMORY;
6458                                 goto error;
6459                         }
6460                         pattern[i].sp_type = PATTERN_EXACT;
6461                         continue;
6462                 }
6463 
6464                 if (flags & SCF_WALK_NOINSTANCE)
6465                         goto badfmri;
6466 
6467                 /*
6468                  * Otherwise, iterate over all instances in the service.
6469                  */
6470                 if (scf_iter_service_instances(iter, svc) !=
6471                     SCF_SUCCESS) {
6472                         ret = scf_error();
6473                         goto error;
6474                 }
6475 
6476                 for (;;) {
6477                         ret = scf_iter_next_instance(iter, inst);
6478                         if (ret == 0)
6479                                 break;
6480                         if (ret != 1) {
6481                                 ret = scf_error();
6482                                 goto error;
6483                         }
6484 
6485                         if (scf_instance_to_fmri(inst, fmri,
6486                             max_fmri_length + 1) == -1)
6487                                 goto badfmri;
6488 
6489                         if ((ret = scf_add_match(htable, fmri, NULL,
6490                             &pattern[i], flags & SCF_WALK_EXPLICIT)) != 0)
6491                                 goto error;
6492                 }
6493 
6494                 if ((pattern[i].sp_arg = strdup(argv[i])) == NULL) {
6495                         ret = SCF_ERROR_NO_MEMORY;
6496                         goto error;
6497                 }
6498                 pattern[i].sp_type = PATTERN_EXACT;
6499                 partial_fmri = B_TRUE;  /* we just iterated all instances */
6500 
6501                 continue;
6502 
6503 badfmri:
6504 
6505                 /*
6506                  * If we got here because of a fatal error, bail out
6507                  * immediately.
6508                  */
6509                 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN) {
6510                         ret = scf_error();
6511                         goto error;
6512                 }
6513 
6514                 /*
6515                  * At this point we failed to interpret the argument as a
6516                  * complete fmri, so mark it as a partial or globbed FMRI for
6517                  * later processing.
6518                  */
6519                 if (strpbrk(argv[i], "*?[") != NULL) {
6520                         /*
6521                          * Prepend svc:/ to patterns which don't begin with * or
6522                          * svc: or lrc:.
6523                          */
6524                         wildcard_fmri = B_TRUE;
6525                         pattern[i].sp_type = PATTERN_GLOB;
6526                         if (argv[i][0] == '*' ||
6527                             (strlen(argv[i]) >= 4 && argv[i][3] == ':'))
6528                                 pattern[i].sp_arg = strdup(argv[i]);
6529                         else {
6530                                 pattern[i].sp_arg = malloc(strlen(argv[i]) + 6);
6531                                 if (pattern[i].sp_arg != NULL)
6532                                         (void) snprintf(pattern[i].sp_arg,
6533                                             strlen(argv[i]) + 6, "svc:/%s",
6534                                             argv[i]);
6535                         }
6536                 } else {
6537                         partial_fmri = B_TRUE;
6538                         pattern[i].sp_type = PATTERN_PARTIAL;
6539                         pattern[i].sp_arg = strdup(argv[i]);
6540                 }
6541                 pattern_search = 1;
6542                 if (pattern[i].sp_arg == NULL) {
6543                         ret = SCF_ERROR_NO_MEMORY;
6544                         goto error;
6545                 }
6546         }
6547 
6548         if (pattern_search || argc == 0) {
6549                 /*
6550                  * We have a set of patterns to search for.  Iterate over all
6551                  * instances and legacy services searching for matches.
6552                  */
6553                 if (scf_handle_get_local_scope(h, scope) != 0) {
6554                         ret = scf_error();
6555                         goto error;
6556                 }
6557 
6558                 if (scf_iter_scope_services(sciter, scope) != 0) {
6559                         ret = scf_error();
6560                         goto error;
6561                 }
6562 
6563                 for (;;) {
6564                         ret = scf_iter_next_service(sciter, svc);
6565                         if (ret == 0)
6566                                 break;
6567                         if (ret != 1) {
6568                                 ret = scf_error();
6569                                 goto error;
6570                         }
6571 
6572                         if (flags & SCF_WALK_SERVICE) {
6573                                 /*
6574                                  * If the user is requesting bare services, try
6575                                  * to match the service first.
6576                                  */
6577                                 if (scf_service_to_fmri(svc, fmri,
6578                                     max_fmri_length + 1) < 0) {
6579                                         ret = scf_error();
6580                                         goto error;
6581                                 }
6582 
6583                                 if (argc == 0) {
6584                                         info.fmri = fmri;
6585                                         info.scope = scope;
6586                                         info.svc = svc;
6587                                         info.inst = NULL;
6588                                         info.pg = NULL;
6589                                         info.prop = NULL;
6590                                         if ((ret = callback(data, &info)) != 0)
6591                                                 goto error;
6592                                         continue;
6593                                 } else if ((ret = scf_pattern_match(htable,
6594                                     fmri, NULL, argc, pattern,
6595                                     flags & SCF_WALK_EXPLICIT)) != 0) {
6596                                         goto error;
6597                                 }
6598                         }
6599 
6600                         if (flags & SCF_WALK_NOINSTANCE)
6601                                 continue;
6602 
6603                         /*
6604                          * Iterate over all instances in the service.
6605                          */
6606                         if (scf_iter_service_instances(siter, svc) != 0) {
6607                                 if (scf_error() != SCF_ERROR_DELETED) {
6608                                         ret = scf_error();
6609                                         goto error;
6610                                 }
6611                                 continue;
6612                         }
6613 
6614                         for (;;) {
6615                                 ret = scf_iter_next_instance(siter, inst);
6616                                 if (ret == 0)
6617                                         break;
6618                                 if (ret != 1) {
6619                                         if (scf_error() != SCF_ERROR_DELETED) {
6620                                                 ret = scf_error();
6621                                                 goto error;
6622                                         }
6623                                         break;
6624                                 }
6625 
6626                                 if (scf_instance_to_fmri(inst, fmri,
6627                                     max_fmri_length + 1) < 0) {
6628                                         ret = scf_error();
6629                                         goto error;
6630                                 }
6631 
6632                                 /*
6633                                  * Without arguments, execute the callback
6634                                  * immediately.
6635                                  */
6636                                 if (argc == 0) {
6637                                         info.fmri = fmri;
6638                                         info.scope = scope;
6639                                         info.svc = svc;
6640                                         info.inst = inst;
6641                                         info.pg = NULL;
6642                                         info.prop = NULL;
6643                                         if ((ret = callback(data, &info)) != 0)
6644                                                 goto error;
6645                                 } else if ((ret = scf_pattern_match(htable,
6646                                     fmri, NULL, argc, pattern,
6647                                     flags & SCF_WALK_EXPLICIT)) != 0) {
6648                                         goto error;
6649                                 }
6650                         }
6651                 }
6652 
6653                 /*
6654                  * Search legacy services
6655                  */
6656                 if ((flags & SCF_WALK_LEGACY)) {
6657                         if (scf_scope_get_service(scope, SCF_LEGACY_SERVICE,
6658                             svc) != 0) {
6659                                 if (scf_error() != SCF_ERROR_NOT_FOUND) {
6660                                         ret = scf_error();
6661                                         goto error;
6662                                 }
6663 
6664                                 goto nolegacy;
6665                         }
6666 
6667                         if (scf_iter_service_pgs_typed(iter, svc,
6668                             SCF_GROUP_FRAMEWORK) != SCF_SUCCESS) {
6669                                 ret = scf_error();
6670                                 goto error;
6671                         }
6672 
6673                         (void) strcpy(fmri, LEGACY_SCHEME);
6674 
6675                         for (;;) {
6676                                 ret = scf_iter_next_pg(iter, pg);
6677                                 if (ret == -1) {
6678                                         ret = scf_error();
6679                                         goto error;
6680                                 }
6681                                 if (ret == 0)
6682                                         break;
6683 
6684                                 if (scf_pg_get_property(pg,
6685                                     SCF_LEGACY_PROPERTY_NAME, prop) == -1) {
6686                                         ret = scf_error();
6687                                         if (ret == SCF_ERROR_DELETED ||
6688                                             ret == SCF_ERROR_NOT_FOUND) {
6689                                                 ret = 0;
6690                                                 continue;
6691                                         }
6692                                         goto error;
6693                                 }
6694 
6695                                 if (scf_property_is_type(prop, SCF_TYPE_ASTRING)
6696                                     != SCF_SUCCESS) {
6697                                         if (scf_error() == SCF_ERROR_DELETED)
6698                                                 continue;
6699                                         ret = scf_error();
6700                                         goto error;
6701                                 }
6702 
6703                                 if (scf_property_get_value(prop, value) !=
6704                                     SCF_SUCCESS)
6705                                         continue;
6706 
6707                                 if (scf_value_get_astring(value,
6708                                     fmri + sizeof (LEGACY_SCHEME) - 1,
6709                                     max_fmri_length + 2 -
6710                                     sizeof (LEGACY_SCHEME)) <= 0)
6711                                         continue;
6712 
6713                                 if (scf_pg_get_name(pg, pgname,
6714                                     max_name_length + 1) <= 0) {
6715                                         if (scf_error() == SCF_ERROR_DELETED)
6716                                                 continue;
6717                                         ret = scf_error();
6718                                         goto error;
6719                                 }
6720 
6721                                 if (argc == 0) {
6722                                         info.fmri = fmri;
6723                                         info.scope = scope;
6724                                         info.svc = NULL;
6725                                         info.inst = NULL;
6726                                         info.pg = pg;
6727                                         info.prop = NULL;
6728                                         if ((ret = callback(data, &info)) != 0)
6729                                                 goto error;
6730                                 } else if ((ret = scf_pattern_match(htable,
6731                                     fmri, pgname, argc, pattern,
6732                                     flags & SCF_WALK_EXPLICIT)) != 0)
6733                                         goto error;
6734                         }
6735 
6736                 }
6737         }
6738 nolegacy:
6739         ret = 0;
6740 
6741         if (argc == 0)
6742                 goto error;
6743 
6744         /*
6745          * Check all patterns, and see if we have that any that didn't match
6746          * or any that matched multiple instances.  For svcprop, add up the
6747          * total number of matching keys.
6748          */
6749         info.count = 0;
6750         for (i = 0; i < argc; i++) {
6751                 scf_match_t *match;
6752 
6753                 if (pattern[i].sp_type == PATTERN_INVALID)
6754                         continue;
6755                 if (pattern[i].sp_matchcount == 0) {
6756                         scf_msg_t msgid;
6757                         /*
6758                          * Provide a useful error message based on the argument
6759                          * and the type of entity requested.
6760                          */
6761                         if (!(flags & SCF_WALK_LEGACY) &&
6762                             strncmp(pattern[i].sp_arg, "lrc:/", 5) == 0)
6763                                 msgid = SCF_MSG_PATTERN_LEGACY;
6764                         else if (flags & SCF_WALK_PROPERTY)
6765                                 msgid = SCF_MSG_PATTERN_NOENTITY;
6766                         else if (flags & SCF_WALK_NOINSTANCE)
6767                                 msgid = SCF_MSG_PATTERN_NOSERVICE;
6768                         else if (flags & SCF_WALK_SERVICE)
6769                                 msgid = SCF_MSG_PATTERN_NOINSTSVC;
6770                         else
6771                                 msgid = SCF_MSG_PATTERN_NOINSTANCE;
6772 
6773                         errfunc(scf_get_msg(msgid), pattern[i].sp_arg);
6774                         if (err)
6775                                 *err = UU_EXIT_FATAL;
6776                 } else if (!(flags & SCF_WALK_MULTIPLE) &&
6777                     pattern[i].sp_matchcount > 1) {
6778                         size_t len, off;
6779                         char *msg;
6780 
6781                         /*
6782                          * Construct a message with all possible FMRIs before
6783                          * passing off to error handling function.
6784                          *
6785                          * Note that strlen(scf_get_msg(...)) includes the
6786                          * length of '%s', which accounts for the terminating
6787                          * null byte.
6788                          */
6789                         len = strlen(scf_get_msg(SCF_MSG_PATTERN_MULTIMATCH)) +
6790                             strlen(pattern[i].sp_arg);
6791                         for (match = pattern[i].sp_matches; match != NULL;
6792                             match = match->sm_next) {
6793                                 len += strlen(match->sm_key->sk_fmri) + 2;
6794                         }
6795                         if ((msg = malloc(len)) == NULL) {
6796                                 ret = SCF_ERROR_NO_MEMORY;
6797                                 goto error;
6798                         }
6799 
6800                         /* LINTED - format argument */
6801                         (void) snprintf(msg, len,
6802                             scf_get_msg(SCF_MSG_PATTERN_MULTIMATCH),
6803                             pattern[i].sp_arg);
6804                         off = strlen(msg);
6805                         for (match = pattern[i].sp_matches; match != NULL;
6806                             match = match->sm_next) {
6807                                 off += snprintf(msg + off, len - off, "\t%s\n",
6808                                     match->sm_key->sk_fmri);
6809                         }
6810 
6811                         errfunc(msg);
6812                         if (err != NULL)
6813                                 *err = UU_EXIT_FATAL;
6814 
6815                         free(msg);
6816                 } else {
6817                         for (match = pattern[i].sp_matches; match != NULL;
6818                             match = match->sm_next) {
6819                                 if (!match->sm_key->sk_seen)
6820                                         info.count++;
6821                                 match->sm_key->sk_seen = 1;
6822                         }
6823                 }
6824         }
6825 
6826         if (flags & SCF_WALK_UNIPARTIAL && info.count > 1) {
6827                 /*
6828                  * If the SCF_WALK_UNIPARTIAL flag was passed in and we have
6829                  * more than one fmri, then this is an error if we matched
6830                  * because of a partial fmri parameter, unless we also matched
6831                  * more than one fmri because of wildcards in the parameters.
6832                  * That is, the presence of wildcards indicates that it is ok
6833                  * to match more than one fmri in this case.
6834                  * For example, a parameter of 'foo' that matches more than
6835                  * one fmri is an error, but parameters of 'foo *bar*' that
6836                  * matches more than one is fine.
6837                  */
6838                 if (partial_fmri && !wildcard_fmri) {
6839                         errfunc(scf_get_msg(SCF_MSG_PATTERN_MULTIPARTIAL));
6840                         if (err != NULL)
6841                                 *err = UU_EXIT_FATAL;
6842                         goto error;
6843                 }
6844         }
6845 
6846         /*
6847          * Clear 'sk_seen' for all keys.
6848          */
6849         for (i = 0; i < WALK_HTABLE_SIZE; i++) {
6850                 scf_matchkey_t *key;
6851                 for (key = htable[i]; key != NULL; key = key->sk_next)
6852                         key->sk_seen = 0;
6853         }
6854 
6855         /*
6856          * Iterate over all the FMRIs in our hash table and execute the
6857          * callback.
6858          */
6859         for (i = 0; i < argc; i++) {
6860                 scf_match_t *match;
6861                 scf_matchkey_t *key;
6862 
6863                 /*
6864                  * Ignore patterns which didn't match anything or matched too
6865                  * many FMRIs.
6866                  */
6867                 if (pattern[i].sp_matchcount == 0 ||
6868                     (!(flags & SCF_WALK_MULTIPLE) &&
6869                     pattern[i].sp_matchcount > 1))
6870                         continue;
6871 
6872                 for (match = pattern[i].sp_matches; match != NULL;
6873                     match = match->sm_next) {
6874 
6875                         key = match->sm_key;
6876                         if (key->sk_seen)
6877                                 continue;
6878 
6879                         key->sk_seen = 1;
6880 
6881                         if (key->sk_legacy != NULL) {
6882                                 if (scf_scope_get_service(scope,
6883                                     "smf/legacy_run", svc) != 0) {
6884                                         ret = scf_error();
6885                                         goto error;
6886                                 }
6887 
6888                                 if (scf_service_get_pg(svc, key->sk_legacy,
6889                                     pg) != 0)
6890                                         continue;
6891 
6892                                 info.fmri = key->sk_fmri;
6893                                 info.scope = scope;
6894                                 info.svc = NULL;
6895                                 info.inst = NULL;
6896                                 info.pg = pg;
6897                                 info.prop = NULL;
6898                                 if ((ret = callback(data, &info)) != 0)
6899                                         goto error;
6900                         } else {
6901                                 if (scf_handle_decode_fmri(h, key->sk_fmri,
6902                                     scope, svc, inst, pg, prop, 0) !=
6903                                     SCF_SUCCESS)
6904                                         continue;
6905 
6906                                 info.fmri = key->sk_fmri;
6907                                 info.scope = scope;
6908                                 info.svc = svc;
6909                                 if (scf_instance_get_name(inst, NULL, 0) < 0) {
6910                                         if (scf_error() ==
6911                                             SCF_ERROR_CONNECTION_BROKEN) {
6912                                                 ret = scf_error();
6913                                                 goto error;
6914                                         }
6915                                         info.inst = NULL;
6916                                 } else {
6917                                         info.inst = inst;
6918                                 }
6919                                 if (scf_pg_get_name(pg, NULL, 0) < 0) {
6920                                         if (scf_error() ==
6921                                             SCF_ERROR_CONNECTION_BROKEN) {
6922                                                 ret = scf_error();
6923                                                 goto error;
6924                                         }
6925                                         info.pg = NULL;
6926                                 } else {
6927                                         info.pg = pg;
6928                                 }
6929                                 if (scf_property_get_name(prop, NULL, 0) < 0) {
6930                                         if (scf_error() ==
6931                                             SCF_ERROR_CONNECTION_BROKEN) {
6932                                                 ret = scf_error();
6933                                                 goto error;
6934                                         }
6935                                         info.prop = NULL;
6936                                 } else {
6937                                         info.prop = prop;
6938                                 }
6939 
6940                                 if ((ret = callback(data, &info)) != 0)
6941                                         goto error;
6942                         }
6943                 }
6944         }
6945 
6946 error:
6947         if (htable) {
6948                 scf_matchkey_t *key, *next;
6949 
6950                 for (i = 0; i < WALK_HTABLE_SIZE; i++) {
6951 
6952                         for (key = htable[i]; key != NULL;
6953                             key = next) {
6954 
6955                                 next = key->sk_next;
6956 
6957                                 if (key->sk_fmri != NULL)
6958                                         free(key->sk_fmri);
6959                                 if (key->sk_legacy != NULL)
6960                                         free(key->sk_legacy);
6961                                 free(key);
6962                         }
6963                 }
6964                 free(htable);
6965         }
6966         if (pattern != NULL) {
6967                 for (i = 0; i < argc; i++) {
6968                         scf_match_t *match, *next;
6969 
6970                         if (pattern[i].sp_arg != NULL)
6971                                 free(pattern[i].sp_arg);
6972 
6973                         for (match = pattern[i].sp_matches; match != NULL;
6974                             match = next) {
6975 
6976                                 next = match->sm_next;
6977 
6978                                 free(match);
6979                         }
6980                 }
6981                 free(pattern);
6982         }
6983 
6984         free(fmri);
6985         free(pgname);
6986 
6987         scf_value_destroy(value);
6988         scf_property_destroy(prop);
6989         scf_pg_destroy(pg);
6990         scf_scope_destroy(scope);
6991         scf_iter_destroy(siter);
6992         scf_iter_destroy(sciter);
6993         scf_iter_destroy(iter);
6994         scf_instance_destroy(inst);
6995         scf_service_destroy(svc);
6996 
6997         return (ret);
6998 }
6999 
7000 /*
7001  * scf_encode32() is an implementation of Base32 encoding as described in
7002  * section 6 of RFC 4648 - "The Base16, Base32, and Base64 Data
7003  * Encodings". See http://www.ietf.org/rfc/rfc4648.txt?number=4648.  The
7004  * input stream is divided into groups of 5 characters (40 bits).  Each
7005  * group is encoded into 8 output characters where each output character
7006  * represents 5 bits of input.
7007  *
7008  * If the input is not an even multiple of 5 characters, the output will be
7009  * padded so that the output is an even multiple of 8 characters.  The
7010  * standard specifies that the pad character is '='.  Unfortunately, '=' is
7011  * not a legal character in SMF property names.  Thus, the caller can
7012  * specify an alternate pad character with the pad argument.  If pad is 0,
7013  * scf_encode32() will use '='.  Note that use of anything other than '='
7014  * produces output that is not in conformance with RFC 4648.  It is
7015  * suitable, however, for internal use of SMF software.  When the encoded
7016  * data is used as part of an SMF property name, SCF_ENCODE32_PAD should be
7017  * used as the pad character.
7018  *
7019  * Arguments:
7020  *      input -         Address of the buffer to be encoded.
7021  *      inlen -         Number of characters at input.
7022  *      output -        Address of the buffer to receive the encoded data.
7023  *      outmax -        Size of the buffer at output.
7024  *      outlen -        If it is not NULL, outlen receives the number of
7025  *                      bytes placed in output.
7026  *      pad -           Alternate padding character.
7027  *
7028  * Returns:
7029  *      0       Buffer was successfully encoded.
7030  *      -1      Indicates output buffer too small, or pad is one of the
7031  *              standard encoding characters.
7032  */
7033 int
7034 scf_encode32(const char *input, size_t inlen, char *output, size_t outmax,
7035     size_t *outlen, char pad)
7036 {
7037         uint_t group_size = 5;
7038         uint_t i;
7039         const unsigned char *in = (const unsigned char *)input;
7040         size_t olen;
7041         uchar_t *out = (uchar_t *)output;
7042         uint_t oval;
7043         uint_t pad_count;
7044 
7045         /* Verify that there is enough room for the output. */
7046         olen = ((inlen + (group_size - 1)) / group_size) * 8;
7047         if (outlen)
7048                 *outlen = olen;
7049         if (olen > outmax)
7050                 return (-1);
7051 
7052         /* If caller did not provide pad character, use the default. */
7053         if (pad == 0) {
7054                 pad = '=';
7055         } else {
7056                 /*
7057                  * Make sure that caller's pad is not one of the encoding
7058                  * characters.
7059                  */
7060                 for (i = 0; i < sizeof (base32) - 1; i++) {
7061                         if (pad == base32[i])
7062                                 return (-1);
7063                 }
7064         }
7065 
7066         /* Process full groups capturing 5 bits per output character. */
7067         for (; inlen >= group_size; in += group_size, inlen -= group_size) {
7068                 /*
7069                  * The comments in this section number the bits in an
7070                  * 8 bit byte 0 to 7.  The high order bit is bit 7 and
7071                  * the low order bit is bit 0.
7072                  */
7073 
7074                 /* top 5 bits (7-3) from in[0] */
7075                 *out++ = base32[in[0] >> 3];
7076                 /* bits 2-0 from in[0] and top 2 (7-6) from in[1] */
7077                 *out++ = base32[((in[0] << 2) & 0x1c) | (in[1] >> 6)];
7078                 /* 5 bits (5-1) from in[1] */
7079                 *out++ = base32[(in[1] >> 1) & 0x1f];
7080                 /* low bit (0) from in[1] and top 4 (7-4) from in[2] */
7081                 *out++ = base32[((in[1] << 4) & 0x10) | ((in[2] >> 4) & 0xf)];
7082                 /* low 4 (3-0) from in[2] and top bit (7) from in[3] */
7083                 *out++ = base32[((in[2] << 1) & 0x1e) | (in[3] >> 7)];
7084                 /* 5 bits (6-2) from in[3] */
7085                 *out++ = base32[(in[3] >> 2) & 0x1f];
7086                 /* low 2 (1-0) from in[3] and top 3 (7-5) from in[4] */
7087                 *out++ = base32[((in[3] << 3) & 0x18) | (in[4] >> 5)];
7088                 /* low 5 (4-0) from in[4] */
7089                 *out++ = base32[in[4] & 0x1f];
7090         }
7091 
7092         /* Take care of final input bytes. */
7093         pad_count = 0;
7094         if (inlen) {
7095                 /* top 5 bits (7-3) from in[0] */
7096                 *out++ = base32[in[0] >> 3];
7097                 /*
7098                  * low 3 (2-0) from in[0] and top 2 (7-6) from in[1] if
7099                  * available.
7100                  */
7101                 oval = (in[0] << 2) & 0x1c;
7102                 if (inlen == 1) {
7103                         *out++ = base32[oval];
7104                         pad_count = 6;
7105                         goto padout;
7106                 }
7107                 oval |= in[1] >> 6;
7108                 *out++ = base32[oval];
7109                 /* 5 bits (5-1) from in[1] */
7110                 *out++ = base32[(in[1] >> 1) & 0x1f];
7111                 /*
7112                  * low bit (0) from in[1] and top 4 (7-4) from in[2] if
7113                  * available.
7114                  */
7115                 oval = (in[1] << 4) & 0x10;
7116                 if (inlen == 2) {
7117                         *out++ = base32[oval];
7118                         pad_count = 4;
7119                         goto padout;
7120                 }
7121                 oval |= in[2] >> 4;
7122                 *out++ = base32[oval];
7123                 /*
7124                  * low 4 (3-0) from in[2] and top 1 (7) from in[3] if
7125                  * available.
7126                  */
7127                 oval = (in[2] << 1) & 0x1e;
7128                 if (inlen == 3) {
7129                         *out++ = base32[oval];
7130                         pad_count = 3;
7131                         goto padout;
7132                 }
7133                 oval |= in[3] >> 7;
7134                 *out++ = base32[oval];
7135                 /* 5 bits (6-2) from in[3] */
7136                 *out++ = base32[(in[3] >> 2) & 0x1f];
7137                 /* low 2 bits (1-0) from in[3] */
7138                 *out++ = base32[(in[3] << 3) & 0x18];
7139                 pad_count = 1;
7140         }
7141 padout:
7142         /*
7143          * Pad the output so that it is a multiple of 8 bytes.
7144          */
7145         for (; pad_count > 0; pad_count--) {
7146                 *out++ = pad;
7147         }
7148 
7149         /*
7150          * Null terminate the output if there is enough room.
7151          */
7152         if (olen < outmax)
7153                 *out = 0;
7154 
7155         return (0);
7156 }
7157 
7158 /*
7159  * scf_decode32() is an implementation of Base32 decoding as described in
7160  * section 6 of RFC 4648 - "The Base16, Base32, and Base64 Data
7161  * Encodings". See http://www.ietf.org/rfc/rfc4648.txt?number=4648.  The
7162  * input stream is divided into groups of 8 encoded characters.  Each
7163  * encoded character represents 5 bits of data.  Thus, the 8 encoded
7164  * characters are used to produce 40 bits or 5 bytes of unencoded data in
7165  * outbuf.
7166  *
7167  * If the encoder did not have enough data to generate a mulitple of 8
7168  * characters of encoded data, it used a pad character to get to the 8
7169  * character boundry. The standard specifies that the pad character is '='.
7170  * Unfortunately, '=' is not a legal character in SMF property names.
7171  * Thus, the caller can specify an alternate pad character with the pad
7172  * argument.  If pad is 0, scf_decode32() will use '='.  Note that use of
7173  * anything other than '=' is not in conformance with RFC 4648.  It is
7174  * suitable, however, for internal use of SMF software.  When the encoded
7175  * data is used in SMF property names, SCF_ENCODE32_PAD should be used as
7176  * the pad character.
7177  *
7178  * Arguments:
7179  *      in -            Buffer of encoded characters.
7180  *      inlen -         Number of characters at in.
7181  *      outbuf -        Buffer to receive the decoded bytes.  It can be the
7182  *                      same buffer as in.
7183  *      outmax -        Size of the buffer at outbuf.
7184  *      outlen -        If it is not NULL, outlen receives the number of
7185  *                      bytes placed in output.
7186  *      pad -           Alternate padding character.
7187  *
7188  * Returns:
7189  *      0       Buffer was successfully decoded.
7190  *      -1      Indicates an invalid input character, output buffer too
7191  *              small, or pad is one of the standard encoding characters.
7192  */
7193 int
7194 scf_decode32(const char *in, size_t inlen, char *outbuf, size_t outmax,
7195     size_t *outlen, char pad)
7196 {
7197         char *bufend = outbuf + outmax;
7198         char c;
7199         uint_t count;
7200         uint32_t g[DECODE32_GS];
7201         size_t i;
7202         uint_t j;
7203         char *out = outbuf;
7204         boolean_t pad_seen = B_FALSE;
7205 
7206         /* If caller did not provide pad character, use the default. */
7207         if (pad == 0) {
7208                 pad = '=';
7209         } else {
7210                 /*
7211                  * Make sure that caller's pad is not one of the encoding
7212                  * characters.
7213                  */
7214                 for (i = 0; i < sizeof (base32) - 1; i++) {
7215                         if (pad == base32[i])
7216                                 return (-1);
7217                 }
7218         }
7219 
7220         i = 0;
7221         while ((i < inlen) && (out < bufend)) {
7222                 /* Get a group of input characters. */
7223                 for (j = 0, count = 0;
7224                     (j < DECODE32_GS) && (i < inlen);
7225                     i++) {
7226                         c = in[i];
7227                         /*
7228                          * RFC 4648 allows for the encoded data to be split
7229                          * into multiple lines, so skip carriage returns
7230                          * and new lines.
7231                          */
7232                         if ((c == '\r') || (c == '\n'))
7233                                 continue;
7234                         if ((pad_seen == B_TRUE) && (c != pad)) {
7235                                 /* Group not completed by pads */
7236                                 return (-1);
7237                         }
7238                         if ((c < 0) || (c >= sizeof (index32))) {
7239                                 /* Illegal character. */
7240                                 return (-1);
7241                         }
7242                         if (c == pad) {
7243                                 pad_seen = B_TRUE;
7244                                 continue;
7245                         }
7246                         if ((g[j++] = index32[c]) == 0xff) {
7247                                 /* Illegal character */
7248                                 return (-1);
7249                         }
7250                         count++;
7251                 }
7252 
7253                 /* Pack the group into five 8 bit bytes. */
7254                 if ((count >= 2) && (out < bufend)) {
7255                         /*
7256                          * Output byte 0:
7257                          *      5 bits (7-3) from g[0]
7258                          *      3 bits (2-0) from g[1] (4-2)
7259                          */
7260                         *out++ = (g[0] << 3) | ((g[1] >> 2) & 0x7);
7261                 }
7262                 if ((count >= 4) && (out < bufend)) {
7263                         /*
7264                          * Output byte 1:
7265                          *      2 bits (7-6) from g[1] (1-0)
7266                          *      5 bits (5-1) from g[2] (4-0)
7267                          *      1 bit (0) from g[3] (4)
7268                          */
7269                         *out++ = (g[1] << 6) | (g[2] << 1) | \
7270                             ((g[3] >> 4) & 0x1);
7271                 }
7272                 if ((count >= 5) && (out < bufend)) {
7273                         /*
7274                          * Output byte 2:
7275                          *      4 bits (7-4) from g[3] (3-0)
7276                          *      4 bits (3-0) from g[4] (4-1)
7277                          */
7278                         *out++ = (g[3] << 4) | ((g[4] >> 1) & 0xf);
7279                 }
7280                 if ((count >= 7) && (out < bufend)) {
7281                         /*
7282                          * Output byte 3:
7283                          *      1 bit (7) from g[4] (0)
7284                          *      5 bits (6-2) from g[5] (4-0)
7285                          *      2 bits (0-1) from g[6] (4-3)
7286                          */
7287                         *out++ = (g[4] << 7) | (g[5] << 2) |
7288                             ((g[6] >> 3) & 0x3);
7289                 }
7290                 if ((count == 8) && (out < bufend)) {
7291                         /*
7292                          * Output byte 4;
7293                          *      3 bits (7-5) from g[6] (2-0)
7294                          *      5 bits (4-0) from g[7] (4-0)
7295                          */
7296                         *out++ = (g[6] << 5) | g[7];
7297                 }
7298         }
7299         if (i < inlen) {
7300                 /* Did not process all input characters. */
7301                 return (-1);
7302         }
7303         if (outlen)
7304                 *outlen = out - outbuf;
7305         /* Null terminate the output if there is room. */
7306         if (out < bufend)
7307                 *out = 0;
7308         return (0);
7309 }
7310 
7311 
7312 /*
7313  * _scf_request_backup:  a simple wrapper routine
7314  */
7315 int
7316 _scf_request_backup(scf_handle_t *h, const char *name)
7317 {
7318         struct rep_protocol_backup_request request;
7319         struct rep_protocol_response response;
7320 
7321         int r;
7322 
7323         if (strlcpy(request.rpr_name, name, sizeof (request.rpr_name)) >=
7324             sizeof (request.rpr_name))
7325                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
7326 
7327         (void) pthread_mutex_lock(&h->rh_lock);
7328         request.rpr_request = REP_PROTOCOL_BACKUP;
7329         request.rpr_changeid = handle_next_changeid(h);
7330 
7331         r = make_door_call(h, &request, sizeof (request),
7332             &response, sizeof (response));
7333         (void) pthread_mutex_unlock(&h->rh_lock);
7334 
7335         if (r < 0) {
7336                 DOOR_ERRORS_BLOCK(r);
7337         }
7338 
7339         if (response.rpr_response != REP_PROTOCOL_SUCCESS)
7340                 return (scf_set_error(proto_error(response.rpr_response)));
7341         return (SCF_SUCCESS);
7342 }
7343 
7344 /*
7345  * Request svc.configd daemon to switch repository database.
7346  *
7347  * Can fail:
7348  *
7349  *      _NOT_BOUND              handle is not bound
7350  *      _CONNECTION_BROKEN      server is not reachable
7351  *      _INTERNAL               file operation error
7352  *                              the server response is too big
7353  *      _PERMISSION_DENIED      not enough privileges to do request
7354  *      _BACKEND_READONLY       backend is not writable
7355  *      _BACKEND_ACCESS         backend access fails
7356  *      _NO_RESOURCES           svc.configd is out of memory
7357  */
7358 int
7359 _scf_repository_switch(scf_handle_t *h, int scf_sw)
7360 {
7361         struct rep_protocol_switch_request request;
7362         struct rep_protocol_response response;
7363         int     r;
7364 
7365         /*
7366          * Setup request protocol and make door call
7367          * Hold rh_lock lock before handle_next_changeid call
7368          */
7369         (void) pthread_mutex_lock(&h->rh_lock);
7370 
7371         request.rpr_flag = scf_sw;
7372         request.rpr_request = REP_PROTOCOL_SWITCH;
7373         request.rpr_changeid = handle_next_changeid(h);
7374 
7375         r = make_door_call(h, &request, sizeof (request),
7376             &response, sizeof (response));
7377 
7378         (void) pthread_mutex_unlock(&h->rh_lock);
7379 
7380         if (r < 0) {
7381                 DOOR_ERRORS_BLOCK(r);
7382         }
7383 
7384         /*
7385          * Pass protocol error up
7386          */
7387         if (response.rpr_response != REP_PROTOCOL_SUCCESS)
7388                 return (scf_set_error(proto_error(response.rpr_response)));
7389 
7390         return (SCF_SUCCESS);
7391 }
7392 
7393 int
7394 _scf_pg_is_read_protected(const scf_propertygroup_t *pg, boolean_t *out)
7395 {
7396         char buf[REP_PROTOCOL_NAME_LEN];
7397         ssize_t res;
7398 
7399         res = datael_get_name(&pg->rd_d, buf, sizeof (buf),
7400             RP_ENTITY_NAME_PGREADPROT);
7401 
7402         if (res == -1)
7403                 return (-1);
7404 
7405         if (uu_strtouint(buf, out, sizeof (*out), 0, 0, 1) == -1)
7406                 return (scf_set_error(SCF_ERROR_INTERNAL));
7407         return (SCF_SUCCESS);
7408 }
7409 
7410 /*
7411  * _scf_set_annotation: a wrapper to set the annotation fields for SMF
7412  * security auditing.
7413  *
7414  * Fails with following in scf_error_key thread specific data:
7415  *      _INVALID_ARGUMENT - operation or file too large
7416  *      _NOT_BOUND
7417  *      _CONNECTION_BROKEN
7418  *      _INTERNAL
7419  *      _NO_RESOURCES
7420  */
7421 int
7422 _scf_set_annotation(scf_handle_t *h, const char *operation, const char *file)
7423 {
7424         struct rep_protocol_annotation request;
7425         struct rep_protocol_response response;
7426         size_t copied;
7427         int r;
7428 
7429         if (h == NULL) {
7430                 /* We can't do anything if the handle is destroyed. */
7431                 return (scf_set_error(SCF_ERROR_HANDLE_DESTROYED));
7432         }
7433 
7434         request.rpr_request = REP_PROTOCOL_SET_AUDIT_ANNOTATION;
7435         copied = strlcpy(request.rpr_operation,
7436             (operation == NULL) ? "" : operation,
7437             sizeof (request.rpr_operation));
7438         if (copied >= sizeof (request.rpr_operation))
7439                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
7440 
7441         copied = strlcpy(request.rpr_file,
7442             (file == NULL) ? "" : file,
7443             sizeof (request.rpr_file));
7444         if (copied >= sizeof (request.rpr_file))
7445                 return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
7446 
7447         (void) pthread_mutex_lock(&h->rh_lock);
7448         r = make_door_call(h, &request, sizeof (request),
7449             &response, sizeof (response));
7450         (void) pthread_mutex_unlock(&h->rh_lock);
7451 
7452         if (r < 0) {
7453                 DOOR_ERRORS_BLOCK(r);
7454         }
7455 
7456         if (response.rpr_response != REP_PROTOCOL_SUCCESS)
7457                 return (scf_set_error(proto_error(response.rpr_response)));
7458         return (0);
7459 }