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