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