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