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