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