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