lowlevel.c revision 5405:f7a026c6d133
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28/*
29 * This is the main implementation file for the low-level repository
30 * interface.
31 */
32
33#include "lowlevel_impl.h"
34
35#include "repcache_protocol.h"
36#include "scf_type.h"
37
38#include <assert.h>
39#include <alloca.h>
40#include <door.h>
41#include <errno.h>
42#include <fcntl.h>
43#include <fnmatch.h>
44#include <libuutil.h>
45#include <poll.h>
46#include <pthread.h>
47#include <stddef.h>
48#include <stdio.h>
49#include <stdlib.h>
50#include <string.h>
51#include <sys/mman.h>
52#include <sys/sysmacros.h>
53#include <unistd.h>
54
55#define	ENV_SCF_DEBUG		"LIBSCF_DEBUG"
56#define	ENV_SCF_DOORPATH	"LIBSCF_DOORPATH"
57
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, _NOT_FOUND.
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
1735/*
1736 * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT (out does not have type type,
1737 * name is invalid), _NOT_BOUND, _CONNECTION_BROKEN, _INTERNAL (server response
1738 * too big, bad id, iter already exists, element cannot have children of type,
1739 * type is invalid, iter was reset, sequence was bad, iter walks values, iter
1740 * does not walk type entities), _NOT_SET, _DELETED, _NO_RESOURCES,
1741 * _BACKEND_ACCESS, _NOT_FOUND.
1742 */
1743static int
1744datael_get_child(const scf_datael_t *dp, const char *name, uint32_t type,
1745    scf_datael_t *out, boolean_t composed)
1746{
1747	scf_handle_t *h = dp->rd_handle;
1748	uint32_t held = 0;
1749	int ret;
1750
1751	scf_iter_t *iter = NULL;
1752
1753	if (composed)
1754		iter = HANDLE_HOLD_ITER(h);
1755
1756	if (out == NULL) {
1757		switch (type) {
1758		case REP_PROTOCOL_ENTITY_SERVICE:
1759			out = &HANDLE_HOLD_SERVICE(h)->rd_d;
1760			held = RH_HOLD_SERVICE;
1761			break;
1762
1763		case REP_PROTOCOL_ENTITY_INSTANCE:
1764			out = &HANDLE_HOLD_INSTANCE(h)->rd_d;
1765			held = RH_HOLD_INSTANCE;
1766			break;
1767
1768		case REP_PROTOCOL_ENTITY_SNAPSHOT:
1769			out = &HANDLE_HOLD_SNAPSHOT(h)->rd_d;
1770			held = RH_HOLD_SNAPSHOT;
1771			break;
1772
1773		case REP_PROTOCOL_ENTITY_SNAPLEVEL:
1774			out = &HANDLE_HOLD_SNAPLVL(h)->rd_d;
1775			held = RH_HOLD_SNAPLVL;
1776			break;
1777
1778		case REP_PROTOCOL_ENTITY_PROPERTYGRP:
1779			out = &HANDLE_HOLD_PG(h)->rd_d;
1780			held = RH_HOLD_PG;
1781			break;
1782
1783		case REP_PROTOCOL_ENTITY_PROPERTY:
1784			out = &HANDLE_HOLD_PROPERTY(h)->rd_d;
1785			held = RH_HOLD_PROPERTY;
1786			break;
1787
1788		default:
1789			assert(0);
1790			abort();
1791		}
1792	}
1793
1794	(void) pthread_mutex_lock(&h->rh_lock);
1795	if (composed)
1796		ret = datael_get_child_composed_locked(dp, name, type, out,
1797		    iter);
1798	else
1799		ret = datael_get_child_locked(dp, name, type, out);
1800	(void) pthread_mutex_unlock(&h->rh_lock);
1801
1802	if (composed)
1803		HANDLE_RELE_ITER(h);
1804
1805	if (held)
1806		handle_rele_subhandles(h, held);
1807
1808	return (ret);
1809}
1810
1811/*
1812 * Fails with
1813 *   _HANDLE_MISMATCH
1814 *   _INVALID_ARGUMENT - name is too long
1815 *			 invalid changeid
1816 *			 name is invalid
1817 *			 cannot create children for dp's type of node
1818 *   _NOT_BOUND - handle is not bound
1819 *   _CONNECTION_BROKEN - server is not reachable
1820 *   _INTERNAL - server response too big
1821 *		 dp or cp has unknown id
1822 *		 type is _PROPERTYGRP
1823 *		 type is invalid
1824 *		 dp cannot have children of type type
1825 *		 database is corrupt
1826 *   _EXISTS - dp & cp have the same id
1827 *   _EXISTS - child already exists
1828 *   _DELETED - dp has been deleted
1829 *   _NOT_SET - dp is reset
1830 *   _NO_RESOURCES
1831 *   _PERMISSION_DENIED
1832 *   _BACKEND_ACCESS
1833 *   _BACKEND_READONLY
1834 */
1835static int
1836datael_add_child(const scf_datael_t *dp, const char *name, uint32_t type,
1837    scf_datael_t *cp)
1838{
1839	scf_handle_t *h = dp->rd_handle;
1840
1841	struct rep_protocol_entity_create_child request;
1842	struct rep_protocol_response response;
1843	ssize_t r;
1844	uint32_t held = 0;
1845
1846	if (cp == NULL) {
1847		switch (type) {
1848		case REP_PROTOCOL_ENTITY_SCOPE:
1849			cp = &HANDLE_HOLD_SCOPE(h)->rd_d;
1850			held = RH_HOLD_SCOPE;
1851			break;
1852		case REP_PROTOCOL_ENTITY_SERVICE:
1853			cp = &HANDLE_HOLD_SERVICE(h)->rd_d;
1854			held = RH_HOLD_SERVICE;
1855			break;
1856		case REP_PROTOCOL_ENTITY_INSTANCE:
1857			cp = &HANDLE_HOLD_INSTANCE(h)->rd_d;
1858			held = RH_HOLD_INSTANCE;
1859			break;
1860		case REP_PROTOCOL_ENTITY_SNAPSHOT:
1861		default:
1862			assert(0);
1863			abort();
1864		}
1865		assert(h == cp->rd_handle);
1866
1867	} else if (h != cp->rd_handle) {
1868		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
1869	}
1870
1871	if (strlcpy(request.rpr_name, name, sizeof (request.rpr_name)) >=
1872	    sizeof (request.rpr_name)) {
1873		r = scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
1874		goto err;
1875	}
1876
1877	(void) pthread_mutex_lock(&h->rh_lock);
1878	request.rpr_request = REP_PROTOCOL_ENTITY_CREATE_CHILD;
1879	request.rpr_entityid = dp->rd_entity;
1880	request.rpr_childtype = type;
1881	request.rpr_childid = cp->rd_entity;
1882
1883	datael_finish_reset(dp);
1884	request.rpr_changeid = handle_next_changeid(h);
1885	r = make_door_call(h, &request, sizeof (request),
1886	    &response, sizeof (response));
1887	(void) pthread_mutex_unlock(&h->rh_lock);
1888
1889	if (held)
1890		handle_rele_subhandles(h, held);
1891
1892	if (r < 0)
1893		DOOR_ERRORS_BLOCK(r);
1894
1895	if (response.rpr_response != REP_PROTOCOL_SUCCESS)
1896		return (scf_set_error(proto_error(response.rpr_response)));
1897
1898	return (SCF_SUCCESS);
1899
1900err:
1901	if (held)
1902		handle_rele_subhandles(h, held);
1903	return (r);
1904}
1905
1906static int
1907datael_add_pg(const scf_datael_t *dp, const char *name, const char *type,
1908    uint32_t flags, scf_datael_t *cp)
1909{
1910	scf_handle_t *h = dp->rd_handle;
1911
1912	struct rep_protocol_entity_create_pg request;
1913	struct rep_protocol_response response;
1914	ssize_t r;
1915
1916	int holding_els = 0;
1917
1918	if (cp == NULL) {
1919		holding_els = 1;
1920		cp = &HANDLE_HOLD_PG(h)->rd_d;
1921		assert(h == cp->rd_handle);
1922
1923	} else if (h != cp->rd_handle) {
1924		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
1925	}
1926
1927	request.rpr_request = REP_PROTOCOL_ENTITY_CREATE_PG;
1928
1929	if (name == NULL || strlcpy(request.rpr_name, name,
1930	    sizeof (request.rpr_name)) > sizeof (request.rpr_name)) {
1931		r = scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
1932		goto err;
1933	}
1934
1935	if (type == NULL || strlcpy(request.rpr_type, type,
1936	    sizeof (request.rpr_type)) > sizeof (request.rpr_type)) {
1937		r = scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
1938		goto err;
1939	}
1940
1941	(void) pthread_mutex_lock(&h->rh_lock);
1942	request.rpr_entityid = dp->rd_entity;
1943	request.rpr_childid = cp->rd_entity;
1944	request.rpr_flags = flags;
1945
1946	datael_finish_reset(dp);
1947	datael_finish_reset(cp);
1948	request.rpr_changeid = handle_next_changeid(h);
1949	r = make_door_call(h, &request, sizeof (request),
1950	    &response, sizeof (response));
1951	(void) pthread_mutex_unlock(&h->rh_lock);
1952
1953	if (holding_els)
1954		HANDLE_RELE_PG(h);
1955
1956	if (r < 0)
1957		DOOR_ERRORS_BLOCK(r);
1958
1959	if (response.rpr_response != REP_PROTOCOL_SUCCESS)
1960		return (scf_set_error(proto_error(response.rpr_response)));
1961
1962	return (SCF_SUCCESS);
1963
1964err:
1965	if (holding_els)
1966		HANDLE_RELE_PG(h);
1967	return (r);
1968}
1969
1970static int
1971datael_delete(const scf_datael_t *dp)
1972{
1973	scf_handle_t *h = dp->rd_handle;
1974
1975	struct rep_protocol_entity_delete request;
1976	struct rep_protocol_response response;
1977	ssize_t r;
1978
1979	(void) pthread_mutex_lock(&h->rh_lock);
1980	request.rpr_request = REP_PROTOCOL_ENTITY_DELETE;
1981	request.rpr_entityid = dp->rd_entity;
1982
1983	datael_finish_reset(dp);
1984	request.rpr_changeid = handle_next_changeid(h);
1985	r = make_door_call(h, &request, sizeof (request),
1986	    &response, sizeof (response));
1987	(void) pthread_mutex_unlock(&h->rh_lock);
1988
1989	if (r < 0)
1990		DOOR_ERRORS_BLOCK(r);
1991
1992	if (response.rpr_response != REP_PROTOCOL_SUCCESS)
1993		return (scf_set_error(proto_error(response.rpr_response)));
1994
1995	return (SCF_SUCCESS);
1996}
1997
1998/*
1999 * Fails with
2000 *   _INVALID_ARGUMENT - h is NULL
2001 *   _NO_MEMORY
2002 *   _HANDLE_DESTROYED - h has been destroyed
2003 *   _INTERNAL - server response too big
2004 *		 iter already exists
2005 *   _NO_RESOURCES
2006 */
2007scf_iter_t *
2008scf_iter_create(scf_handle_t *h)
2009{
2010	scf_iter_t *iter;
2011
2012	if (h == NULL) {
2013		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2014		return (NULL);
2015	}
2016
2017	iter = uu_zalloc(sizeof (*iter));
2018	if (iter == NULL) {
2019		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
2020		return (NULL);
2021	}
2022
2023	uu_list_node_init(iter, &iter->iter_node, iter_pool);
2024	iter->iter_handle = h;
2025	iter->iter_sequence = 1;
2026	iter->iter_type = REP_PROTOCOL_ENTITY_NONE;
2027
2028	(void) pthread_mutex_lock(&h->rh_lock);
2029	iter->iter_id = handle_alloc_iterid(h);
2030	if (iter->iter_id == 0) {
2031		(void) pthread_mutex_unlock(&h->rh_lock);
2032		uu_list_node_fini(iter, &iter->iter_node, iter_pool);
2033		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
2034		return (NULL);
2035	}
2036	if (iter_attach(iter) == -1) {
2037		uu_list_node_fini(iter, &iter->iter_node, iter_pool);
2038		(void) pthread_mutex_unlock(&h->rh_lock);
2039		uu_free(iter);
2040		return (NULL);
2041	}
2042	(void) uu_list_insert_before(h->rh_iters, NULL, iter);
2043	h->rh_extrefs++;
2044	(void) pthread_mutex_unlock(&h->rh_lock);
2045	return (iter);
2046}
2047
2048scf_handle_t *
2049scf_iter_handle(const scf_iter_t *iter)
2050{
2051	return (handle_get(iter->iter_handle));
2052}
2053
2054static void
2055scf_iter_reset_locked(scf_iter_t *iter)
2056{
2057	struct rep_protocol_iter_request request;
2058	struct rep_protocol_response response;
2059
2060	request.rpr_request = REP_PROTOCOL_ITER_RESET;
2061	request.rpr_iterid = iter->iter_id;
2062
2063	assert(MUTEX_HELD(&iter->iter_handle->rh_lock));
2064
2065	(void) make_door_call(iter->iter_handle,
2066	    &request, sizeof (request), &response, sizeof (response));
2067
2068	iter->iter_type = REP_PROTOCOL_ENTITY_NONE;
2069	iter->iter_sequence = 1;
2070}
2071
2072void
2073scf_iter_reset(scf_iter_t *iter)
2074{
2075	(void) pthread_mutex_lock(&iter->iter_handle->rh_lock);
2076	scf_iter_reset_locked(iter);
2077	(void) pthread_mutex_unlock(&iter->iter_handle->rh_lock);
2078}
2079
2080void
2081scf_iter_destroy(scf_iter_t *iter)
2082{
2083	scf_handle_t *handle;
2084
2085	struct rep_protocol_iter_request request;
2086	struct rep_protocol_response response;
2087
2088	if (iter == NULL)
2089		return;
2090
2091	handle = iter->iter_handle;
2092
2093	(void) pthread_mutex_lock(&handle->rh_lock);
2094	request.rpr_request = REP_PROTOCOL_ITER_TEARDOWN;
2095	request.rpr_iterid = iter->iter_id;
2096
2097	(void) make_door_call(handle, &request, sizeof (request),
2098	    &response, sizeof (response));
2099
2100	uu_list_remove(handle->rh_iters, iter);
2101	--handle->rh_extrefs;
2102	handle_unrefed(handle);			/* drops h->rh_lock */
2103	iter->iter_handle = NULL;
2104
2105	uu_list_node_fini(iter, &iter->iter_node, iter_pool);
2106	uu_free(iter);
2107}
2108
2109static int
2110handle_get_local_scope_locked(scf_handle_t *handle, scf_scope_t *out)
2111{
2112	struct rep_protocol_entity_get request;
2113	struct rep_protocol_name_response response;
2114	ssize_t r;
2115
2116	assert(MUTEX_HELD(&handle->rh_lock));
2117
2118	if (handle != out->rd_d.rd_handle)
2119		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2120
2121	request.rpr_request = REP_PROTOCOL_ENTITY_GET;
2122	request.rpr_entityid = out->rd_d.rd_entity;
2123	request.rpr_object = RP_ENTITY_GET_MOST_LOCAL_SCOPE;
2124
2125	datael_finish_reset(&out->rd_d);
2126	r = make_door_call(handle, &request, sizeof (request),
2127	    &response, sizeof (response));
2128
2129	if (r < 0)
2130		DOOR_ERRORS_BLOCK(r);
2131
2132	if (response.rpr_response != REP_PROTOCOL_SUCCESS)
2133		return (scf_set_error(proto_error(response.rpr_response)));
2134
2135	return (SCF_SUCCESS);
2136}
2137
2138int
2139scf_iter_handle_scopes(scf_iter_t *iter, const scf_handle_t *handle)
2140{
2141	scf_handle_t *h = iter->iter_handle;
2142	if (h != handle)
2143		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2144
2145	(void) pthread_mutex_lock(&h->rh_lock);
2146	scf_iter_reset_locked(iter);
2147
2148	if (!handle_is_bound(h)) {
2149		(void) pthread_mutex_unlock(&h->rh_lock);
2150		return (scf_set_error(SCF_ERROR_NOT_BOUND));
2151	}
2152
2153	if (!handle_has_server_locked(h)) {
2154		(void) pthread_mutex_unlock(&h->rh_lock);
2155		return (scf_set_error(SCF_ERROR_CONNECTION_BROKEN));
2156	}
2157
2158	iter->iter_type = REP_PROTOCOL_ENTITY_SCOPE;
2159	iter->iter_sequence = 1;
2160	(void) pthread_mutex_unlock(&h->rh_lock);
2161	return (0);
2162}
2163
2164int
2165scf_iter_next_scope(scf_iter_t *iter, scf_scope_t *out)
2166{
2167	int ret;
2168	scf_handle_t *h = iter->iter_handle;
2169
2170	if (h != out->rd_d.rd_handle)
2171		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2172
2173	(void) pthread_mutex_lock(&h->rh_lock);
2174	if (iter->iter_type == REP_PROTOCOL_ENTITY_NONE) {
2175		(void) pthread_mutex_unlock(&h->rh_lock);
2176		return (scf_set_error(SCF_ERROR_NOT_SET));
2177	}
2178	if (iter->iter_type != REP_PROTOCOL_ENTITY_SCOPE) {
2179		(void) pthread_mutex_unlock(&h->rh_lock);
2180		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
2181	}
2182	if (iter->iter_sequence == 1) {
2183		if ((ret = handle_get_local_scope_locked(h, out)) ==
2184		    SCF_SUCCESS) {
2185			iter->iter_sequence++;
2186			ret = 1;
2187		}
2188	} else {
2189		datael_reset_locked(&out->rd_d);
2190		ret = 0;
2191	}
2192	(void) pthread_mutex_unlock(&h->rh_lock);
2193	return (ret);
2194}
2195
2196int
2197scf_handle_get_scope(scf_handle_t *h, const char *name, scf_scope_t *out)
2198{
2199	int ret;
2200
2201	if (h != out->rd_d.rd_handle)
2202		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2203
2204	(void) pthread_mutex_lock(&h->rh_lock);
2205	if (strcmp(name, SCF_SCOPE_LOCAL) == 0) {
2206		ret = handle_get_local_scope_locked(h, out);
2207	} else {
2208		datael_reset_locked(&out->rd_d);
2209		if (uu_check_name(name, 0) == -1)
2210			ret = scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
2211		else
2212			ret = scf_set_error(SCF_ERROR_NOT_FOUND);
2213	}
2214	(void) pthread_mutex_unlock(&h->rh_lock);
2215	return (ret);
2216}
2217
2218static int
2219datael_setup_iter(scf_iter_t *iter, const scf_datael_t *dp, uint32_t res_type,
2220    boolean_t composed)
2221{
2222	scf_handle_t *h = dp->rd_handle;
2223
2224	struct rep_protocol_iter_start request;
2225	struct rep_protocol_response response;
2226
2227	ssize_t r;
2228
2229	if (h != iter->iter_handle)
2230		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2231
2232	(void) pthread_mutex_lock(&h->rh_lock);
2233	scf_iter_reset_locked(iter);
2234	iter->iter_type = res_type;
2235
2236	request.rpr_request = REP_PROTOCOL_ITER_START;
2237	request.rpr_iterid = iter->iter_id;
2238	request.rpr_entity = dp->rd_entity;
2239	request.rpr_itertype = res_type;
2240	request.rpr_flags = RP_ITER_START_ALL |
2241	    (composed ? RP_ITER_START_COMPOSED : 0);
2242	request.rpr_pattern[0] = 0;
2243
2244	datael_finish_reset(dp);
2245	r = make_door_call(h, &request, sizeof (request),
2246	    &response, sizeof (response));
2247
2248	if (r < 0) {
2249		(void) pthread_mutex_unlock(&h->rh_lock);
2250		DOOR_ERRORS_BLOCK(r);
2251	}
2252	if (response.rpr_response != REP_PROTOCOL_SUCCESS) {
2253		(void) pthread_mutex_unlock(&h->rh_lock);
2254		return (scf_set_error(proto_error(response.rpr_response)));
2255	}
2256	iter->iter_sequence++;
2257	(void) pthread_mutex_unlock(&h->rh_lock);
2258	return (SCF_SUCCESS);
2259}
2260
2261static int
2262datael_setup_iter_pgtyped(scf_iter_t *iter, const scf_datael_t *dp,
2263    const char *pgtype, boolean_t composed)
2264{
2265	scf_handle_t *h = dp->rd_handle;
2266
2267	struct rep_protocol_iter_start request;
2268	struct rep_protocol_response response;
2269
2270	ssize_t r;
2271
2272	if (h != iter->iter_handle)
2273		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2274
2275	if (pgtype == NULL || strlcpy(request.rpr_pattern, pgtype,
2276	    sizeof (request.rpr_pattern)) >= sizeof (request.rpr_pattern)) {
2277		scf_iter_reset(iter);
2278		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
2279	}
2280
2281	(void) pthread_mutex_lock(&h->rh_lock);
2282	request.rpr_request = REP_PROTOCOL_ITER_START;
2283	request.rpr_iterid = iter->iter_id;
2284	request.rpr_entity = dp->rd_entity;
2285	request.rpr_itertype = REP_PROTOCOL_ENTITY_PROPERTYGRP;
2286	request.rpr_flags = RP_ITER_START_PGTYPE |
2287	    (composed ? RP_ITER_START_COMPOSED : 0);
2288
2289	datael_finish_reset(dp);
2290	scf_iter_reset_locked(iter);
2291	iter->iter_type = REP_PROTOCOL_ENTITY_PROPERTYGRP;
2292
2293	r = make_door_call(h, &request, sizeof (request),
2294	    &response, sizeof (response));
2295
2296	if (r < 0) {
2297		(void) pthread_mutex_unlock(&h->rh_lock);
2298
2299		DOOR_ERRORS_BLOCK(r);
2300	}
2301	if (response.rpr_response != REP_PROTOCOL_SUCCESS) {
2302		(void) pthread_mutex_unlock(&h->rh_lock);
2303		return (scf_set_error(proto_error(response.rpr_response)));
2304	}
2305	iter->iter_sequence++;
2306	(void) pthread_mutex_unlock(&h->rh_lock);
2307	return (SCF_SUCCESS);
2308}
2309
2310static int
2311datael_iter_next(scf_iter_t *iter, scf_datael_t *out)
2312{
2313	scf_handle_t *h = iter->iter_handle;
2314
2315	struct rep_protocol_iter_read request;
2316	struct rep_protocol_response response;
2317	ssize_t r;
2318
2319	if (h != out->rd_handle)
2320		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2321
2322	(void) pthread_mutex_lock(&h->rh_lock);
2323	if (iter->iter_type == REP_PROTOCOL_ENTITY_NONE ||
2324	    iter->iter_sequence == 1) {
2325		(void) pthread_mutex_unlock(&h->rh_lock);
2326		return (scf_set_error(SCF_ERROR_NOT_SET));
2327	}
2328
2329	if (out->rd_type != iter->iter_type) {
2330		(void) pthread_mutex_unlock(&h->rh_lock);
2331		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
2332	}
2333
2334	request.rpr_request = REP_PROTOCOL_ITER_READ;
2335	request.rpr_iterid = iter->iter_id;
2336	request.rpr_sequence = iter->iter_sequence;
2337	request.rpr_entityid = out->rd_entity;
2338
2339	datael_finish_reset(out);
2340	r = make_door_call(h, &request, sizeof (request),
2341	    &response, sizeof (response));
2342
2343	if (r < 0) {
2344		(void) pthread_mutex_unlock(&h->rh_lock);
2345		DOOR_ERRORS_BLOCK(r);
2346	}
2347
2348	if (response.rpr_response == REP_PROTOCOL_DONE) {
2349		(void) pthread_mutex_unlock(&h->rh_lock);
2350		return (0);
2351	}
2352	if (response.rpr_response != REP_PROTOCOL_SUCCESS) {
2353		(void) pthread_mutex_unlock(&h->rh_lock);
2354		return (scf_set_error(proto_error(response.rpr_response)));
2355	}
2356	iter->iter_sequence++;
2357	(void) pthread_mutex_unlock(&h->rh_lock);
2358
2359	return (1);
2360}
2361
2362int
2363scf_iter_scope_services(scf_iter_t *iter, const scf_scope_t *s)
2364{
2365	return (datael_setup_iter(iter, &s->rd_d,
2366	    REP_PROTOCOL_ENTITY_SERVICE, 0));
2367}
2368
2369int
2370scf_iter_next_service(scf_iter_t *iter, scf_service_t *out)
2371{
2372	return (datael_iter_next(iter, &out->rd_d));
2373}
2374
2375int
2376scf_iter_service_instances(scf_iter_t *iter, const scf_service_t *svc)
2377{
2378	return (datael_setup_iter(iter, &svc->rd_d,
2379	    REP_PROTOCOL_ENTITY_INSTANCE, 0));
2380}
2381
2382int
2383scf_iter_next_instance(scf_iter_t *iter, scf_instance_t *out)
2384{
2385	return (datael_iter_next(iter, &out->rd_d));
2386}
2387
2388int
2389scf_iter_service_pgs(scf_iter_t *iter, const scf_service_t *svc)
2390{
2391	return (datael_setup_iter(iter, &svc->rd_d,
2392	    REP_PROTOCOL_ENTITY_PROPERTYGRP, 0));
2393}
2394
2395int
2396scf_iter_service_pgs_typed(scf_iter_t *iter, const scf_service_t *svc,
2397    const char *type)
2398{
2399	return (datael_setup_iter_pgtyped(iter, &svc->rd_d, type, 0));
2400}
2401
2402int
2403scf_iter_instance_snapshots(scf_iter_t *iter, const scf_instance_t *inst)
2404{
2405	return (datael_setup_iter(iter, &inst->rd_d,
2406	    REP_PROTOCOL_ENTITY_SNAPSHOT, 0));
2407}
2408
2409int
2410scf_iter_next_snapshot(scf_iter_t *iter, scf_snapshot_t *out)
2411{
2412	return (datael_iter_next(iter, &out->rd_d));
2413}
2414
2415int
2416scf_iter_instance_pgs(scf_iter_t *iter, const scf_instance_t *inst)
2417{
2418	return (datael_setup_iter(iter, &inst->rd_d,
2419	    REP_PROTOCOL_ENTITY_PROPERTYGRP, 0));
2420}
2421
2422int
2423scf_iter_instance_pgs_typed(scf_iter_t *iter, const scf_instance_t *inst,
2424    const char *type)
2425{
2426	return (datael_setup_iter_pgtyped(iter, &inst->rd_d, type, 0));
2427}
2428
2429int
2430scf_iter_instance_pgs_composed(scf_iter_t *iter, const scf_instance_t *inst,
2431    const scf_snapshot_t *snap)
2432{
2433	if (snap != NULL && inst->rd_d.rd_handle != snap->rd_d.rd_handle)
2434		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2435
2436	return (datael_setup_iter(iter, snap ? &snap->rd_d : &inst->rd_d,
2437	    REP_PROTOCOL_ENTITY_PROPERTYGRP, 1));
2438}
2439
2440int
2441scf_iter_instance_pgs_typed_composed(scf_iter_t *iter,
2442    const scf_instance_t *inst, const scf_snapshot_t *snap, const char *type)
2443{
2444	if (snap != NULL && inst->rd_d.rd_handle != snap->rd_d.rd_handle)
2445		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2446
2447	return (datael_setup_iter_pgtyped(iter,
2448	    snap ? &snap->rd_d : &inst->rd_d, type, 1));
2449}
2450
2451int
2452scf_iter_snaplevel_pgs(scf_iter_t *iter, const scf_snaplevel_t *inst)
2453{
2454	return (datael_setup_iter(iter, &inst->rd_d,
2455	    REP_PROTOCOL_ENTITY_PROPERTYGRP, 0));
2456}
2457
2458int
2459scf_iter_snaplevel_pgs_typed(scf_iter_t *iter, const scf_snaplevel_t *inst,
2460    const char *type)
2461{
2462	return (datael_setup_iter_pgtyped(iter, &inst->rd_d, type, 0));
2463}
2464
2465int
2466scf_iter_next_pg(scf_iter_t *iter, scf_propertygroup_t *out)
2467{
2468	return (datael_iter_next(iter, &out->rd_d));
2469}
2470
2471int
2472scf_iter_pg_properties(scf_iter_t *iter, const scf_propertygroup_t *pg)
2473{
2474	return (datael_setup_iter(iter, &pg->rd_d,
2475	    REP_PROTOCOL_ENTITY_PROPERTY, 0));
2476}
2477
2478int
2479scf_iter_next_property(scf_iter_t *iter, scf_property_t *out)
2480{
2481	return (datael_iter_next(iter, &out->rd_d));
2482}
2483
2484/*
2485 * Fails with
2486 *   _INVALID_ARGUMENT - handle is NULL
2487 *   _INTERNAL - server response too big
2488 *		 entity already set up with different type
2489 *   _NO_RESOURCES
2490 *   _NO_MEMORY
2491 */
2492scf_scope_t *
2493scf_scope_create(scf_handle_t *handle)
2494{
2495	scf_scope_t *ret;
2496
2497	ret = uu_zalloc(sizeof (*ret));
2498	if (ret != NULL) {
2499		if (datael_init(&ret->rd_d, handle,
2500		    REP_PROTOCOL_ENTITY_SCOPE) == -1) {
2501			uu_free(ret);
2502			return (NULL);
2503		}
2504	} else {
2505		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
2506	}
2507
2508	return (ret);
2509}
2510
2511scf_handle_t *
2512scf_scope_handle(const scf_scope_t *val)
2513{
2514	return (datael_handle(&val->rd_d));
2515}
2516
2517void
2518scf_scope_destroy(scf_scope_t *val)
2519{
2520	if (val == NULL)
2521		return;
2522
2523	datael_destroy(&val->rd_d);
2524	uu_free(val);
2525}
2526
2527ssize_t
2528scf_scope_get_name(const scf_scope_t *rep, char *out, size_t len)
2529{
2530	return (datael_get_name(&rep->rd_d, out, len, RP_ENTITY_NAME_NAME));
2531}
2532
2533/*ARGSUSED*/
2534int
2535scf_scope_get_parent(const scf_scope_t *child, scf_scope_t *parent)
2536{
2537	char name[1];
2538
2539	/* fake up the side-effects */
2540	datael_reset(&parent->rd_d);
2541	if (scf_scope_get_name(child, name, sizeof (name)) < 0)
2542		return (-1);
2543	return (scf_set_error(SCF_ERROR_NOT_FOUND));
2544}
2545
2546/*
2547 * Fails with _INVALID_ARGUMENT (handle is NULL), _HANDLE_DESTROYED, _INTERNAL
2548 * (bad server response or id in use), _NO_RESOURCES, or _NO_MEMORY.
2549 */
2550scf_service_t *
2551scf_service_create(scf_handle_t *handle)
2552{
2553	scf_service_t *ret;
2554	ret = uu_zalloc(sizeof (*ret));
2555	if (ret != NULL) {
2556		if (datael_init(&ret->rd_d, handle,
2557		    REP_PROTOCOL_ENTITY_SERVICE) == -1) {
2558			uu_free(ret);
2559			return (NULL);
2560		}
2561	} else {
2562		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
2563	}
2564
2565	return (ret);
2566}
2567
2568
2569/*
2570 * Fails with
2571 *   _HANDLE_MISMATCH
2572 *   _INVALID_ARGUMENT
2573 *   _NOT_BOUND
2574 *   _CONNECTION_BROKEN
2575 *   _INTERNAL
2576 *   _EXISTS
2577 *   _DELETED
2578 *   _NOT_SET
2579 *   _NO_RESOURCES
2580 *   _PERMISSION_DENIED
2581 *   _BACKEND_ACCESS
2582 *   _BACKEND_READONLY
2583 */
2584int
2585scf_scope_add_service(const scf_scope_t *scope, const char *name,
2586    scf_service_t *svc)
2587{
2588	return (datael_add_child(&scope->rd_d, name,
2589	    REP_PROTOCOL_ENTITY_SERVICE, (svc != NULL)? &svc->rd_d : NULL));
2590}
2591
2592/*
2593 * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
2594 * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
2595 * _BACKEND_ACCESS, _NOT_FOUND.
2596 */
2597int
2598scf_scope_get_service(const scf_scope_t *s, const char *name,
2599    scf_service_t *svc)
2600{
2601	return (datael_get_child(&s->rd_d, name, REP_PROTOCOL_ENTITY_SERVICE,
2602	    svc ? &svc->rd_d : NULL, 0));
2603}
2604
2605scf_handle_t *
2606scf_service_handle(const scf_service_t *val)
2607{
2608	return (datael_handle(&val->rd_d));
2609}
2610
2611int
2612scf_service_delete(scf_service_t *svc)
2613{
2614	return (datael_delete(&svc->rd_d));
2615}
2616
2617int
2618scf_instance_delete(scf_instance_t *inst)
2619{
2620	return (datael_delete(&inst->rd_d));
2621}
2622
2623int
2624scf_pg_delete(scf_propertygroup_t *pg)
2625{
2626	return (datael_delete(&pg->rd_d));
2627}
2628
2629int
2630_scf_snapshot_delete(scf_snapshot_t *snap)
2631{
2632	return (datael_delete(&snap->rd_d));
2633}
2634
2635/*
2636 * Fails with
2637 *   _HANDLE_MISMATCH
2638 *   _INVALID_ARGUMENT
2639 *   _NOT_BOUND
2640 *   _CONNECTION_BROKEN
2641 *   _INTERNAL
2642 *   _EXISTS
2643 *   _DELETED
2644 *   _NOT_SET
2645 *   _NO_RESOURCES
2646 *   _PERMISSION_DENIED
2647 *   _BACKEND_ACCESS
2648 *   _BACKEND_READONLY
2649 */
2650int
2651scf_service_add_instance(const scf_service_t *svc, const char *name,
2652    scf_instance_t *instance)
2653{
2654	return (datael_add_child(&svc->rd_d, name,
2655	    REP_PROTOCOL_ENTITY_INSTANCE,
2656	    (instance != NULL)? &instance->rd_d : NULL));
2657}
2658
2659
2660/*
2661 * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
2662 * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
2663 * _BACKEND_ACCESS, _NOT_FOUND.
2664 */
2665int
2666scf_service_get_instance(const scf_service_t *svc, const char *name,
2667    scf_instance_t *inst)
2668{
2669	return (datael_get_child(&svc->rd_d, name, REP_PROTOCOL_ENTITY_INSTANCE,
2670	    inst ? &inst->rd_d : NULL, 0));
2671}
2672
2673int
2674scf_service_add_pg(const scf_service_t *svc, const char *name,
2675    const char *type, uint32_t flags, scf_propertygroup_t *pg)
2676{
2677	return (datael_add_pg(&svc->rd_d, name, type, flags,
2678	    (pg != NULL)?&pg->rd_d : NULL));
2679}
2680
2681/*
2682 * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
2683 * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
2684 * _BACKEND_ACCESS, _NOT_FOUND.
2685 */
2686int
2687scf_service_get_pg(const scf_service_t *svc, const char *name,
2688    scf_propertygroup_t *pg)
2689{
2690	return (datael_get_child(&svc->rd_d, name,
2691	    REP_PROTOCOL_ENTITY_PROPERTYGRP, pg ? &pg->rd_d : NULL, 0));
2692}
2693
2694int
2695scf_instance_add_pg(const scf_instance_t *inst, const char *name,
2696    const char *type, uint32_t flags, scf_propertygroup_t *pg)
2697{
2698	return (datael_add_pg(&inst->rd_d, name, type, flags,
2699	    (pg != NULL)?&pg->rd_d : NULL));
2700}
2701
2702/*
2703 * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
2704 * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
2705 * _BACKEND_ACCESS, _NOT_FOUND.
2706 */
2707int
2708scf_instance_get_snapshot(const scf_instance_t *inst, const char *name,
2709    scf_snapshot_t *pg)
2710{
2711	return (datael_get_child(&inst->rd_d, name,
2712	    REP_PROTOCOL_ENTITY_SNAPSHOT, pg ? &pg->rd_d : NULL, 0));
2713}
2714
2715/*
2716 * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
2717 * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
2718 * _BACKEND_ACCESS, _NOT_FOUND.
2719 */
2720int
2721scf_instance_get_pg(const scf_instance_t *inst, const char *name,
2722    scf_propertygroup_t *pg)
2723{
2724	return (datael_get_child(&inst->rd_d, name,
2725	    REP_PROTOCOL_ENTITY_PROPERTYGRP, pg ? &pg->rd_d : NULL, 0));
2726}
2727
2728/*
2729 * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
2730 * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
2731 * _BACKEND_ACCESS, _NOT_FOUND.
2732 */
2733int
2734scf_instance_get_pg_composed(const scf_instance_t *inst,
2735    const scf_snapshot_t *snap, const char *name, scf_propertygroup_t *pg)
2736{
2737	if (snap != NULL && inst->rd_d.rd_handle != snap->rd_d.rd_handle)
2738		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2739
2740	return (datael_get_child(snap ? &snap->rd_d : &inst->rd_d, name,
2741	    REP_PROTOCOL_ENTITY_PROPERTYGRP, pg ? &pg->rd_d : NULL, 1));
2742}
2743
2744/*
2745 * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
2746 * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
2747 * _BACKEND_ACCESS, _NOT_FOUND.
2748 */
2749int
2750scf_pg_get_property(const scf_propertygroup_t *pg, const char *name,
2751    scf_property_t *prop)
2752{
2753	return (datael_get_child(&pg->rd_d, name, REP_PROTOCOL_ENTITY_PROPERTY,
2754	    prop ? &prop->rd_d : NULL, 0));
2755}
2756
2757void
2758scf_service_destroy(scf_service_t *val)
2759{
2760	if (val == NULL)
2761		return;
2762
2763	datael_destroy(&val->rd_d);
2764	uu_free(val);
2765}
2766
2767ssize_t
2768scf_service_get_name(const scf_service_t *rep, char *out, size_t len)
2769{
2770	return (datael_get_name(&rep->rd_d, out, len, RP_ENTITY_NAME_NAME));
2771}
2772
2773/*
2774 * Fails with _INVALID_ARGUMENT (handle is NULL), _HANDLE_DESTROYED, _INTERNAL
2775 * (bad server response or id in use), _NO_RESOURCES, or _NO_MEMORY.
2776 */
2777scf_instance_t *
2778scf_instance_create(scf_handle_t *handle)
2779{
2780	scf_instance_t *ret;
2781
2782	ret = uu_zalloc(sizeof (*ret));
2783	if (ret != NULL) {
2784		if (datael_init(&ret->rd_d, handle,
2785		    REP_PROTOCOL_ENTITY_INSTANCE) == -1) {
2786			uu_free(ret);
2787			return (NULL);
2788		}
2789	} else {
2790		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
2791	}
2792
2793	return (ret);
2794}
2795
2796scf_handle_t *
2797scf_instance_handle(const scf_instance_t *val)
2798{
2799	return (datael_handle(&val->rd_d));
2800}
2801
2802void
2803scf_instance_destroy(scf_instance_t *val)
2804{
2805	if (val == NULL)
2806		return;
2807
2808	datael_destroy(&val->rd_d);
2809	uu_free(val);
2810}
2811
2812ssize_t
2813scf_instance_get_name(const scf_instance_t *rep, char *out, size_t len)
2814{
2815	return (datael_get_name(&rep->rd_d, out, len, RP_ENTITY_NAME_NAME));
2816}
2817
2818/*
2819 * Fails with _INVALID_ARGUMENT (handle is NULL), _HANDLE_DESTROYED, _INTERNAL
2820 * (bad server response or id in use), _NO_RESOURCES, or _NO_MEMORY.
2821 */
2822scf_snapshot_t *
2823scf_snapshot_create(scf_handle_t *handle)
2824{
2825	scf_snapshot_t *ret;
2826
2827	ret = uu_zalloc(sizeof (*ret));
2828	if (ret != NULL) {
2829		if (datael_init(&ret->rd_d, handle,
2830		    REP_PROTOCOL_ENTITY_SNAPSHOT) == -1) {
2831			uu_free(ret);
2832			return (NULL);
2833		}
2834	} else {
2835		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
2836	}
2837
2838	return (ret);
2839}
2840
2841scf_handle_t *
2842scf_snapshot_handle(const scf_snapshot_t *val)
2843{
2844	return (datael_handle(&val->rd_d));
2845}
2846
2847void
2848scf_snapshot_destroy(scf_snapshot_t *val)
2849{
2850	if (val == NULL)
2851		return;
2852
2853	datael_destroy(&val->rd_d);
2854	uu_free(val);
2855}
2856
2857ssize_t
2858scf_snapshot_get_name(const scf_snapshot_t *rep, char *out, size_t len)
2859{
2860	return (datael_get_name(&rep->rd_d, out, len, RP_ENTITY_NAME_NAME));
2861}
2862
2863/*
2864 * Fails with _INVALID_ARGUMENT (handle is NULL), _HANDLE_DESTROYED, _INTERNAL
2865 * (bad server response or id in use), _NO_RESOURCES, _NO_MEMORY.
2866 */
2867scf_snaplevel_t *
2868scf_snaplevel_create(scf_handle_t *handle)
2869{
2870	scf_snaplevel_t *ret;
2871
2872	ret = uu_zalloc(sizeof (*ret));
2873	if (ret != NULL) {
2874		if (datael_init(&ret->rd_d, handle,
2875		    REP_PROTOCOL_ENTITY_SNAPLEVEL) == -1) {
2876			uu_free(ret);
2877			return (NULL);
2878		}
2879	} else {
2880		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
2881	}
2882
2883	return (ret);
2884}
2885
2886scf_handle_t *
2887scf_snaplevel_handle(const scf_snaplevel_t *val)
2888{
2889	return (datael_handle(&val->rd_d));
2890}
2891
2892void
2893scf_snaplevel_destroy(scf_snaplevel_t *val)
2894{
2895	if (val == NULL)
2896		return;
2897
2898	datael_destroy(&val->rd_d);
2899	uu_free(val);
2900}
2901
2902ssize_t
2903scf_snaplevel_get_scope_name(const scf_snaplevel_t *rep, char *out, size_t len)
2904{
2905	return (datael_get_name(&rep->rd_d, out, len,
2906	    RP_ENTITY_NAME_SNAPLEVEL_SCOPE));
2907}
2908
2909ssize_t
2910scf_snaplevel_get_service_name(const scf_snaplevel_t *rep, char *out,
2911    size_t len)
2912{
2913	return (datael_get_name(&rep->rd_d, out, len,
2914	    RP_ENTITY_NAME_SNAPLEVEL_SERVICE));
2915}
2916
2917ssize_t
2918scf_snaplevel_get_instance_name(const scf_snaplevel_t *rep, char *out,
2919    size_t len)
2920{
2921	return (datael_get_name(&rep->rd_d, out, len,
2922	    RP_ENTITY_NAME_SNAPLEVEL_INSTANCE));
2923}
2924
2925/*
2926 * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
2927 * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
2928 * _BACKEND_ACCESS, _NOT_FOUND.
2929 */
2930int
2931scf_snaplevel_get_pg(const scf_snaplevel_t *snap, const char *name,
2932    scf_propertygroup_t *pg)
2933{
2934	return (datael_get_child(&snap->rd_d, name,
2935	    REP_PROTOCOL_ENTITY_PROPERTYGRP, pg ? &pg->rd_d : NULL, 0));
2936}
2937
2938static int
2939snaplevel_next(const scf_datael_t *src, scf_snaplevel_t *dst_arg)
2940{
2941	scf_handle_t *h = src->rd_handle;
2942	scf_snaplevel_t *dst = dst_arg;
2943	struct rep_protocol_entity_pair request;
2944	struct rep_protocol_response response;
2945	int r;
2946	int dups = 0;
2947
2948	if (h != dst->rd_d.rd_handle)
2949		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
2950
2951	if (src == &dst->rd_d) {
2952		dups = 1;
2953		dst = HANDLE_HOLD_SNAPLVL(h);
2954	}
2955	(void) pthread_mutex_lock(&h->rh_lock);
2956	request.rpr_request = REP_PROTOCOL_NEXT_SNAPLEVEL;
2957	request.rpr_entity_src = src->rd_entity;
2958	request.rpr_entity_dst = dst->rd_d.rd_entity;
2959
2960	datael_finish_reset(src);
2961	datael_finish_reset(&dst->rd_d);
2962	r = make_door_call(h, &request, sizeof (request),
2963	    &response, sizeof (response));
2964	/*
2965	 * if we succeeded, we need to swap dst and dst_arg's identity.  We
2966	 * take advantage of the fact that the only in-library knowledge is
2967	 * their entity ids.
2968	 */
2969	if (dups && r >= 0 &&
2970	    (response.rpr_response == REP_PROTOCOL_SUCCESS ||
2971	    response.rpr_response == REP_PROTOCOL_DONE)) {
2972		int entity = dst->rd_d.rd_entity;
2973
2974		dst->rd_d.rd_entity = dst_arg->rd_d.rd_entity;
2975		dst_arg->rd_d.rd_entity = entity;
2976	}
2977	(void) pthread_mutex_unlock(&h->rh_lock);
2978
2979	if (dups)
2980		HANDLE_RELE_SNAPLVL(h);
2981
2982	if (r < 0)
2983		DOOR_ERRORS_BLOCK(r);
2984
2985	if (response.rpr_response != REP_PROTOCOL_SUCCESS &&
2986	    response.rpr_response != REP_PROTOCOL_DONE) {
2987		return (scf_set_error(proto_error(response.rpr_response)));
2988	}
2989
2990	return (response.rpr_response == REP_PROTOCOL_SUCCESS) ?
2991	    SCF_SUCCESS : SCF_COMPLETE;
2992}
2993
2994int scf_snapshot_get_base_snaplevel(const scf_snapshot_t *base,
2995    scf_snaplevel_t *out)
2996{
2997	return (snaplevel_next(&base->rd_d, out));
2998}
2999
3000int scf_snaplevel_get_next_snaplevel(const scf_snaplevel_t *base,
3001    scf_snaplevel_t *out)
3002{
3003	return (snaplevel_next(&base->rd_d, out));
3004}
3005
3006/*
3007 * Fails with _INVALID_ARGUMENT (handle is NULL), _HANDLE_DESTROYED, _INTERNAL
3008 * (bad server response or id in use), _NO_RESOURCES, or _NO_MEMORY.
3009 */
3010scf_propertygroup_t *
3011scf_pg_create(scf_handle_t *handle)
3012{
3013	scf_propertygroup_t *ret;
3014	ret = uu_zalloc(sizeof (*ret));
3015	if (ret != NULL) {
3016		if (datael_init(&ret->rd_d, handle,
3017		    REP_PROTOCOL_ENTITY_PROPERTYGRP) == -1) {
3018			uu_free(ret);
3019			return (NULL);
3020		}
3021	} else {
3022		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
3023	}
3024
3025	return (ret);
3026}
3027
3028scf_handle_t *
3029scf_pg_handle(const scf_propertygroup_t *val)
3030{
3031	return (datael_handle(&val->rd_d));
3032}
3033
3034void
3035scf_pg_destroy(scf_propertygroup_t *val)
3036{
3037	if (val == NULL)
3038		return;
3039
3040	datael_destroy(&val->rd_d);
3041	uu_free(val);
3042}
3043
3044ssize_t
3045scf_pg_get_name(const scf_propertygroup_t *pg,  char *out, size_t len)
3046{
3047	return (datael_get_name(&pg->rd_d, out, len, RP_ENTITY_NAME_NAME));
3048}
3049
3050ssize_t
3051scf_pg_get_type(const scf_propertygroup_t *pg,  char *out, size_t len)
3052{
3053	return (datael_get_name(&pg->rd_d, out, len, RP_ENTITY_NAME_PGTYPE));
3054}
3055
3056int
3057scf_pg_get_flags(const scf_propertygroup_t *pg, uint32_t *out)
3058{
3059	char buf[REP_PROTOCOL_NAME_LEN];
3060	ssize_t res;
3061
3062	res = datael_get_name(&pg->rd_d, buf, sizeof (buf),
3063	    RP_ENTITY_NAME_PGFLAGS);
3064
3065	if (res == -1)
3066		return (-1);
3067
3068	if (uu_strtouint(buf, out, sizeof (*out), 0, 0, UINT32_MAX) == -1)
3069		return (scf_set_error(SCF_ERROR_INTERNAL));
3070
3071	return (0);
3072}
3073
3074static int
3075datael_update(scf_datael_t *dp)
3076{
3077	scf_handle_t *h = dp->rd_handle;
3078
3079	struct rep_protocol_entity_update request;
3080	struct rep_protocol_response response;
3081
3082	int r;
3083
3084	(void) pthread_mutex_lock(&h->rh_lock);
3085	request.rpr_request = REP_PROTOCOL_ENTITY_UPDATE;
3086	request.rpr_entityid = dp->rd_entity;
3087
3088	datael_finish_reset(dp);
3089	request.rpr_changeid = handle_next_changeid(h);
3090
3091	r = make_door_call(h, &request, sizeof (request),
3092	    &response, sizeof (response));
3093	(void) pthread_mutex_unlock(&h->rh_lock);
3094
3095	if (r < 0)
3096		DOOR_ERRORS_BLOCK(r);
3097
3098	/*
3099	 * This should never happen but if it does something has
3100	 * gone terribly wrong and we should abort.
3101	 */
3102	if (response.rpr_response == REP_PROTOCOL_FAIL_BAD_REQUEST)
3103		abort();
3104
3105	if (response.rpr_response != REP_PROTOCOL_SUCCESS &&
3106	    response.rpr_response != REP_PROTOCOL_DONE) {
3107		return (scf_set_error(proto_error(response.rpr_response)));
3108	}
3109
3110	return (response.rpr_response == REP_PROTOCOL_SUCCESS) ?
3111	    SCF_SUCCESS : SCF_COMPLETE;
3112}
3113
3114int
3115scf_pg_update(scf_propertygroup_t *pg)
3116{
3117	return (datael_update(&pg->rd_d));
3118}
3119
3120int
3121scf_snapshot_update(scf_snapshot_t *snap)
3122{
3123	return (datael_update(&snap->rd_d));
3124}
3125
3126int
3127_scf_pg_wait(scf_propertygroup_t *pg, int timeout)
3128{
3129	scf_handle_t *h = pg->rd_d.rd_handle;
3130
3131	struct rep_protocol_propertygrp_request request;
3132	struct rep_protocol_response response;
3133
3134	struct pollfd pollfd;
3135
3136	int r;
3137
3138	(void) pthread_mutex_lock(&h->rh_lock);
3139	request.rpr_request = REP_PROTOCOL_PROPERTYGRP_SETUP_WAIT;
3140	request.rpr_entityid = pg->rd_d.rd_entity;
3141
3142	datael_finish_reset(&pg->rd_d);
3143	if (!handle_is_bound(h)) {
3144		(void) pthread_mutex_unlock(&h->rh_lock);
3145		return (scf_set_error(SCF_ERROR_CONNECTION_BROKEN));
3146	}
3147	r = make_door_call_retfd(h->rh_doorfd, &request, sizeof (request),
3148	    &response, sizeof (response), &pollfd.fd);
3149	(void) pthread_mutex_unlock(&h->rh_lock);
3150
3151	if (r < 0)
3152		DOOR_ERRORS_BLOCK(r);
3153
3154	assert((response.rpr_response == REP_PROTOCOL_SUCCESS) ==
3155	    (pollfd.fd != -1));
3156
3157	if (response.rpr_response == REP_PROTOCOL_FAIL_NOT_LATEST)
3158		return (SCF_SUCCESS);
3159
3160	if (response.rpr_response != REP_PROTOCOL_SUCCESS)
3161		return (scf_set_error(proto_error(response.rpr_response)));
3162
3163	pollfd.events = 0;
3164	pollfd.revents = 0;
3165
3166	r = poll(&pollfd, 1, timeout * MILLISEC);
3167
3168	(void) close(pollfd.fd);
3169	return (pollfd.revents ? SCF_SUCCESS : SCF_COMPLETE);
3170}
3171
3172static int
3173scf_notify_add_pattern(scf_handle_t *h, int type, const char *name)
3174{
3175	struct rep_protocol_notify_request request;
3176	struct rep_protocol_response response;
3177	int r;
3178
3179	(void) pthread_mutex_lock(&h->rh_lock);
3180	request.rpr_request = REP_PROTOCOL_CLIENT_ADD_NOTIFY;
3181	request.rpr_type = type;
3182	(void) strlcpy(request.rpr_pattern, name, sizeof (request.rpr_pattern));
3183
3184	r = make_door_call(h, &request, sizeof (request),
3185	    &response, sizeof (response));
3186	(void) pthread_mutex_unlock(&h->rh_lock);
3187
3188	if (r < 0)
3189		DOOR_ERRORS_BLOCK(r);
3190
3191	if (response.rpr_response != REP_PROTOCOL_SUCCESS)
3192		return (scf_set_error(proto_error(response.rpr_response)));
3193
3194	return (SCF_SUCCESS);
3195}
3196
3197int
3198_scf_notify_add_pgname(scf_handle_t *h, const char *name)
3199{
3200	return (scf_notify_add_pattern(h, REP_PROTOCOL_NOTIFY_PGNAME, name));
3201}
3202
3203int
3204_scf_notify_add_pgtype(scf_handle_t *h, const char *type)
3205{
3206	return (scf_notify_add_pattern(h, REP_PROTOCOL_NOTIFY_PGTYPE, type));
3207}
3208
3209int
3210_scf_notify_wait(scf_propertygroup_t *pg, char *out, size_t sz)
3211{
3212	struct rep_protocol_wait_request request;
3213	struct rep_protocol_fmri_response response;
3214
3215	scf_handle_t *h = pg->rd_d.rd_handle;
3216	int dummy;
3217	int fd;
3218	int r;
3219
3220	(void) pthread_mutex_lock(&h->rh_lock);
3221	datael_finish_reset(&pg->rd_d);
3222	if (!handle_is_bound(h)) {
3223		(void) pthread_mutex_unlock(&h->rh_lock);
3224		return (scf_set_error(SCF_ERROR_CONNECTION_BROKEN));
3225	}
3226	fd = h->rh_doorfd;
3227	++h->rh_fd_users;
3228	assert(h->rh_fd_users > 0);
3229
3230	request.rpr_request = REP_PROTOCOL_CLIENT_WAIT;
3231	request.rpr_entityid = pg->rd_d.rd_entity;
3232	(void) pthread_mutex_unlock(&h->rh_lock);
3233
3234	r = make_door_call_retfd(fd, &request, sizeof (request),
3235	    &response, sizeof (response), &dummy);
3236
3237	(void) pthread_mutex_lock(&h->rh_lock);
3238	assert(h->rh_fd_users > 0);
3239	if (--h->rh_fd_users == 0) {
3240		(void) pthread_cond_broadcast(&h->rh_cv);
3241		/*
3242		 * check for a delayed close, now that there are no other
3243		 * users.
3244		 */
3245		if (h->rh_doorfd_old != -1) {
3246			assert(h->rh_doorfd == -1);
3247			assert(fd == h->rh_doorfd_old);
3248			(void) close(h->rh_doorfd_old);
3249			h->rh_doorfd_old = -1;
3250		}
3251	}
3252	handle_unrefed(h);			/* drops h->rh_lock */
3253
3254	if (r < 0)
3255		DOOR_ERRORS_BLOCK(r);
3256
3257	if (response.rpr_response == REP_PROTOCOL_DONE)
3258		return (scf_set_error(SCF_ERROR_NOT_SET));
3259
3260	if (response.rpr_response != REP_PROTOCOL_SUCCESS)
3261		return (scf_set_error(proto_error(response.rpr_response)));
3262
3263	/* the following will be non-zero for delete notifications */
3264	return (strlcpy(out, response.rpr_fmri, sz));
3265}
3266
3267static int
3268_scf_snapshot_take(scf_instance_t *inst, const char *name,
3269    scf_snapshot_t *snap, int flags)
3270{
3271	scf_handle_t *h = inst->rd_d.rd_handle;
3272
3273	struct rep_protocol_snapshot_take request;
3274	struct rep_protocol_response response;
3275
3276	int r;
3277
3278	if (h != snap->rd_d.rd_handle)
3279		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
3280
3281	if (strlcpy(request.rpr_name, (name != NULL)? name : "",
3282	    sizeof (request.rpr_name)) >= sizeof (request.rpr_name))
3283		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
3284
3285	(void) pthread_mutex_lock(&h->rh_lock);
3286	request.rpr_request = REP_PROTOCOL_SNAPSHOT_TAKE;
3287	request.rpr_entityid_src = inst->rd_d.rd_entity;
3288	request.rpr_entityid_dest = snap->rd_d.rd_entity;
3289	request.rpr_flags = flags;
3290
3291	datael_finish_reset(&inst->rd_d);
3292	datael_finish_reset(&snap->rd_d);
3293
3294	r = make_door_call(h, &request, sizeof (request),
3295	    &response, sizeof (response));
3296	(void) pthread_mutex_unlock(&h->rh_lock);
3297
3298	if (r < 0)
3299		DOOR_ERRORS_BLOCK(r);
3300
3301	if (response.rpr_response != REP_PROTOCOL_SUCCESS)
3302		return (scf_set_error(proto_error(response.rpr_response)));
3303
3304	return (SCF_SUCCESS);
3305}
3306
3307int
3308_scf_snapshot_take_new_named(scf_instance_t *inst,
3309    const char *svcname, const char *instname, const char *snapname,
3310    scf_snapshot_t *snap)
3311{
3312	scf_handle_t *h = inst->rd_d.rd_handle;
3313
3314	struct rep_protocol_snapshot_take_named request;
3315	struct rep_protocol_response response;
3316
3317	int r;
3318
3319	if (h != snap->rd_d.rd_handle)
3320		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
3321
3322	if (strlcpy(request.rpr_svcname, svcname,
3323	    sizeof (request.rpr_svcname)) >= sizeof (request.rpr_svcname))
3324		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
3325
3326	if (strlcpy(request.rpr_instname, instname,
3327	    sizeof (request.rpr_instname)) >= sizeof (request.rpr_instname))
3328		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
3329
3330	if (strlcpy(request.rpr_name, snapname,
3331	    sizeof (request.rpr_name)) >= sizeof (request.rpr_name))
3332		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
3333
3334	(void) pthread_mutex_lock(&h->rh_lock);
3335	request.rpr_request = REP_PROTOCOL_SNAPSHOT_TAKE_NAMED;
3336	request.rpr_entityid_src = inst->rd_d.rd_entity;
3337	request.rpr_entityid_dest = snap->rd_d.rd_entity;
3338
3339	datael_finish_reset(&inst->rd_d);
3340	datael_finish_reset(&snap->rd_d);
3341
3342	r = make_door_call(h, &request, sizeof (request),
3343	    &response, sizeof (response));
3344	(void) pthread_mutex_unlock(&h->rh_lock);
3345
3346	if (r < 0)
3347		DOOR_ERRORS_BLOCK(r);
3348
3349	if (response.rpr_response != REP_PROTOCOL_SUCCESS) {
3350		assert(response.rpr_response !=
3351		    REP_PROTOCOL_FAIL_TYPE_MISMATCH);
3352		return (scf_set_error(proto_error(response.rpr_response)));
3353	}
3354
3355	return (SCF_SUCCESS);
3356}
3357
3358int
3359_scf_snapshot_take_new(scf_instance_t *inst, const char *name,
3360    scf_snapshot_t *snap)
3361{
3362	return (_scf_snapshot_take(inst, name, snap, REP_SNAPSHOT_NEW));
3363}
3364
3365int
3366_scf_snapshot_take_attach(scf_instance_t *inst, scf_snapshot_t *snap)
3367{
3368	return (_scf_snapshot_take(inst, NULL, snap, REP_SNAPSHOT_ATTACH));
3369}
3370
3371int
3372_scf_snapshot_attach(scf_snapshot_t *src, scf_snapshot_t *dest)
3373{
3374	scf_handle_t *h = dest->rd_d.rd_handle;
3375
3376	struct rep_protocol_snapshot_attach request;
3377	struct rep_protocol_response response;
3378
3379	int r;
3380
3381	if (h != src->rd_d.rd_handle)
3382		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
3383
3384	(void) pthread_mutex_lock(&h->rh_lock);
3385	request.rpr_request = REP_PROTOCOL_SNAPSHOT_ATTACH;
3386	request.rpr_entityid_src = src->rd_d.rd_entity;
3387	request.rpr_entityid_dest = dest->rd_d.rd_entity;
3388
3389	datael_finish_reset(&src->rd_d);
3390	datael_finish_reset(&dest->rd_d);
3391
3392	r = make_door_call(h, &request, sizeof (request),
3393	    &response, sizeof (response));
3394	(void) pthread_mutex_unlock(&h->rh_lock);
3395
3396	if (r < 0)
3397		DOOR_ERRORS_BLOCK(r);
3398
3399	if (response.rpr_response != REP_PROTOCOL_SUCCESS)
3400		return (scf_set_error(proto_error(response.rpr_response)));
3401
3402	return (SCF_SUCCESS);
3403}
3404
3405/*
3406 * Fails with _INVALID_ARGUMENT (handle is NULL), _HANDLE_DESTROYED, _INTERNAL
3407 * (bad server response or id in use), _NO_RESOURCES, or _NO_MEMORY.
3408 */
3409scf_property_t *
3410scf_property_create(scf_handle_t *handle)
3411{
3412	scf_property_t *ret;
3413	ret = uu_zalloc(sizeof (*ret));
3414	if (ret != NULL) {
3415		if (datael_init(&ret->rd_d, handle,
3416		    REP_PROTOCOL_ENTITY_PROPERTY) == -1) {
3417			uu_free(ret);
3418			return (NULL);
3419		}
3420	} else {
3421		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
3422	}
3423
3424	return (ret);
3425}
3426
3427scf_handle_t *
3428scf_property_handle(const scf_property_t *val)
3429{
3430	return (datael_handle(&val->rd_d));
3431}
3432
3433void
3434scf_property_destroy(scf_property_t *val)
3435{
3436	if (val == NULL)
3437		return;
3438
3439	datael_destroy(&val->rd_d);
3440	uu_free(val);
3441}
3442
3443static int
3444property_type_locked(const scf_property_t *prop,
3445    rep_protocol_value_type_t *out)
3446{
3447	scf_handle_t *h = prop->rd_d.rd_handle;
3448
3449	struct rep_protocol_property_request request;
3450	struct rep_protocol_integer_response response;
3451
3452	int r;
3453
3454	assert(MUTEX_HELD(&h->rh_lock));
3455
3456	request.rpr_request = REP_PROTOCOL_PROPERTY_GET_TYPE;
3457	request.rpr_entityid = prop->rd_d.rd_entity;
3458
3459	datael_finish_reset(&prop->rd_d);
3460	r = make_door_call(h, &request, sizeof (request),
3461	    &response, sizeof (response));
3462
3463	if (r < 0)
3464		DOOR_ERRORS_BLOCK(r);
3465
3466	if (response.rpr_response != REP_PROTOCOL_SUCCESS ||
3467	    r < sizeof (response)) {
3468		return (scf_set_error(proto_error(response.rpr_response)));
3469	}
3470	*out = response.rpr_value;
3471	return (SCF_SUCCESS);
3472}
3473
3474int
3475scf_property_type(const scf_property_t *prop, scf_type_t *out)
3476{
3477	scf_handle_t *h = prop->rd_d.rd_handle;
3478	rep_protocol_value_type_t out_raw;
3479	int ret;
3480
3481	(void) pthread_mutex_lock(&h->rh_lock);
3482	ret = property_type_locked(prop, &out_raw);
3483	(void) pthread_mutex_unlock(&h->rh_lock);
3484
3485	if (ret == SCF_SUCCESS)
3486		*out = scf_protocol_type_to_type(out_raw);
3487
3488	return (ret);
3489}
3490
3491int
3492scf_property_is_type(const scf_property_t *prop, scf_type_t base_arg)
3493{
3494	scf_handle_t *h = prop->rd_d.rd_handle;
3495	rep_protocol_value_type_t base = scf_type_to_protocol_type(base_arg);
3496	rep_protocol_value_type_t type;
3497	int ret;
3498
3499	if (base == REP_PROTOCOL_TYPE_INVALID)
3500		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
3501
3502	(void) pthread_mutex_lock(&h->rh_lock);
3503	ret = property_type_locked(prop, &type);
3504	(void) pthread_mutex_unlock(&h->rh_lock);
3505
3506	if (ret == SCF_SUCCESS) {
3507		if (!scf_is_compatible_type(base, type))
3508			return (scf_set_error(SCF_ERROR_TYPE_MISMATCH));
3509	}
3510	return (ret);
3511}
3512
3513ssize_t
3514scf_property_get_name(const scf_property_t *prop, char *out, size_t len)
3515{
3516	return (datael_get_name(&prop->rd_d, out, len, RP_ENTITY_NAME_NAME));
3517}
3518
3519/*
3520 * transaction functions
3521 */
3522
3523/*
3524 * Fails with _NO_MEMORY, _INVALID_ARGUMENT (handle is NULL), _HANDLE_DESTROYED,
3525 * _INTERNAL (bad server response or id in use), or _NO_RESOURCES.
3526 */
3527scf_transaction_t *
3528scf_transaction_create(scf_handle_t *handle)
3529{
3530	scf_transaction_t *ret;
3531
3532	ret = uu_zalloc(sizeof (scf_transaction_t));
3533	if (ret == NULL) {
3534		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
3535		return (NULL);
3536	}
3537	if (datael_init(&ret->tran_pg.rd_d, handle,
3538	    REP_PROTOCOL_ENTITY_PROPERTYGRP) == -1) {
3539		uu_free(ret);
3540		return (NULL);			/* error already set */
3541	}
3542	ret->tran_state = TRAN_STATE_NEW;
3543	ret->tran_props = uu_list_create(tran_entry_pool, ret, UU_LIST_SORTED);
3544	if (ret->tran_props == NULL) {
3545		datael_destroy(&ret->tran_pg.rd_d);
3546		uu_free(ret);
3547		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
3548		return (NULL);
3549	}
3550
3551	return (ret);
3552}
3553
3554scf_handle_t *
3555scf_transaction_handle(const scf_transaction_t *val)
3556{
3557	return (handle_get(val->tran_pg.rd_d.rd_handle));
3558}
3559
3560int
3561scf_transaction_start(scf_transaction_t *tran, scf_propertygroup_t *pg)
3562{
3563	scf_handle_t *h = tran->tran_pg.rd_d.rd_handle;
3564
3565	struct rep_protocol_transaction_start request;
3566	struct rep_protocol_response response;
3567	int r;
3568
3569	if (h != pg->rd_d.rd_handle)
3570		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
3571
3572	(void) pthread_mutex_lock(&h->rh_lock);
3573	if (tran->tran_state != TRAN_STATE_NEW) {
3574		(void) pthread_mutex_unlock(&h->rh_lock);
3575		return (scf_set_error(SCF_ERROR_IN_USE));
3576	}
3577	request.rpr_request = REP_PROTOCOL_PROPERTYGRP_TX_START;
3578	request.rpr_entityid_tx = tran->tran_pg.rd_d.rd_entity;
3579	request.rpr_entityid = pg->rd_d.rd_entity;
3580
3581	datael_finish_reset(&tran->tran_pg.rd_d);
3582	datael_finish_reset(&pg->rd_d);
3583
3584	r = make_door_call(h, &request, sizeof (request),
3585	    &response, sizeof (response));
3586
3587	if (r < 0) {
3588		(void) pthread_mutex_unlock(&h->rh_lock);
3589		DOOR_ERRORS_BLOCK(r);
3590	}
3591
3592	/* r < sizeof (response) cannot happen because sizeof (response) == 4 */
3593
3594	if (response.rpr_response != REP_PROTOCOL_SUCCESS ||
3595	    r < sizeof (response)) {
3596		(void) pthread_mutex_unlock(&h->rh_lock);
3597		return (scf_set_error(proto_error(response.rpr_response)));
3598	}
3599
3600	tran->tran_state = TRAN_STATE_SETUP;
3601	tran->tran_invalid = 0;
3602	(void) pthread_mutex_unlock(&h->rh_lock);
3603	return (SCF_SUCCESS);
3604}
3605
3606static void
3607entry_invalidate(scf_transaction_entry_t *cur, int and_destroy,
3608    int and_reset_value)
3609{
3610	scf_value_t *v, *next;
3611	scf_transaction_t *tx;
3612	scf_handle_t *h = cur->entry_handle;
3613
3614	assert(MUTEX_HELD(&h->rh_lock));
3615
3616	if ((tx = cur->entry_tx) != NULL) {
3617		tx->tran_invalid = 1;
3618		uu_list_remove(tx->tran_props, cur);
3619		cur->entry_tx = NULL;
3620	}
3621
3622	cur->entry_property = NULL;
3623	cur->entry_state = ENTRY_STATE_INVALID;
3624	cur->entry_action = REP_PROTOCOL_TX_ENTRY_INVALID;
3625	cur->entry_type = REP_PROTOCOL_TYPE_INVALID;
3626
3627	for (v = cur->entry_head; v != NULL; v = next) {
3628		next = v->value_next;
3629		v->value_tx = NULL;
3630		v->value_next = NULL;
3631		if (and_destroy || and_reset_value)
3632			scf_value_reset_locked(v, and_destroy);
3633	}
3634	cur->entry_head = NULL;
3635}
3636
3637static void
3638entry_destroy_locked(scf_transaction_entry_t *entry)
3639{
3640	scf_handle_t *h = entry->entry_handle;
3641
3642	assert(MUTEX_HELD(&h->rh_lock));
3643
3644	entry_invalidate(entry, 0, 0);
3645
3646	entry->entry_handle = NULL;
3647	assert(h->rh_entries > 0);
3648	--h->rh_entries;
3649	--h->rh_extrefs;
3650	uu_list_node_fini(entry, &entry->entry_link, tran_entry_pool);
3651	uu_free(entry);
3652}
3653
3654/*
3655 * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
3656 * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
3657 * _BACKEND_ACCESS, _IN_USE, _NOT_FOUND, _EXISTS, _TYPE_MISMATCH.
3658 */
3659static int
3660transaction_add(scf_transaction_t *tran, scf_transaction_entry_t *entry,
3661    enum rep_protocol_transaction_action action,
3662    const char *prop, rep_protocol_value_type_t type)
3663{
3664	scf_handle_t *h = tran->tran_pg.rd_d.rd_handle;
3665	scf_transaction_entry_t *old;
3666	scf_property_t *prop_p;
3667	rep_protocol_value_type_t oldtype;
3668	scf_error_t error = SCF_ERROR_NONE;
3669	int ret;
3670	uu_list_index_t idx;
3671
3672	if (h != entry->entry_handle)
3673		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
3674
3675	if (action == REP_PROTOCOL_TX_ENTRY_DELETE)
3676		assert(type == REP_PROTOCOL_TYPE_INVALID);
3677	else if (type == REP_PROTOCOL_TYPE_INVALID)
3678		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
3679
3680	prop_p = HANDLE_HOLD_PROPERTY(h);
3681
3682	(void) pthread_mutex_lock(&h->rh_lock);
3683	if (tran->tran_state != TRAN_STATE_SETUP) {
3684		error = SCF_ERROR_NOT_SET;
3685		goto error;
3686	}
3687	if (tran->tran_invalid) {
3688		error = SCF_ERROR_NOT_SET;
3689		goto error;
3690	}
3691
3692	if (entry->entry_state != ENTRY_STATE_INVALID)
3693		entry_invalidate(entry, 0, 0);
3694
3695	old = uu_list_find(tran->tran_props, &prop, NULL, &idx);
3696	if (old != NULL) {
3697		error = SCF_ERROR_IN_USE;
3698		goto error;
3699	}
3700
3701	ret = datael_get_child_locked(&tran->tran_pg.rd_d, prop,
3702	    REP_PROTOCOL_ENTITY_PROPERTY, &prop_p->rd_d);
3703	if (ret == -1 && (error = scf_error()) != SCF_ERROR_NOT_FOUND) {
3704		goto error;
3705	}
3706
3707	switch (action) {
3708	case REP_PROTOCOL_TX_ENTRY_DELETE:
3709		if (ret == -1) {
3710			error = SCF_ERROR_NOT_FOUND;
3711			goto error;
3712		}
3713		break;
3714	case REP_PROTOCOL_TX_ENTRY_NEW:
3715		if (ret != -1) {
3716			error = SCF_ERROR_EXISTS;
3717			goto error;
3718		}
3719		break;
3720
3721	case REP_PROTOCOL_TX_ENTRY_CLEAR:
3722	case REP_PROTOCOL_TX_ENTRY_REPLACE:
3723		if (ret == -1) {
3724			error = SCF_ERROR_NOT_FOUND;
3725			goto error;
3726		}
3727		if (action == REP_PROTOCOL_TX_ENTRY_CLEAR) {
3728			if (property_type_locked(prop_p, &oldtype) == -1) {
3729				error = scf_error();
3730				goto error;
3731			}
3732			if (oldtype != type) {
3733				error = SCF_ERROR_TYPE_MISMATCH;
3734				goto error;
3735			}
3736		}
3737		break;
3738	default:
3739		assert(0);
3740		abort();
3741	}
3742
3743	(void) strlcpy(entry->entry_namebuf, prop,
3744	    sizeof (entry->entry_namebuf));
3745	entry->entry_property = entry->entry_namebuf;
3746	entry->entry_action = action;
3747	entry->entry_type = type;
3748
3749	entry->entry_state = ENTRY_STATE_IN_TX_ACTION;
3750	entry->entry_tx = tran;
3751	uu_list_insert(tran->tran_props, entry, idx);
3752
3753	(void) pthread_mutex_unlock(&h->rh_lock);
3754
3755	HANDLE_RELE_PROPERTY(h);
3756
3757	return (SCF_SUCCESS);
3758
3759error:
3760	(void) pthread_mutex_unlock(&h->rh_lock);
3761
3762	HANDLE_RELE_PROPERTY(h);
3763
3764	return (scf_set_error(error));
3765}
3766
3767/*
3768 * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
3769 * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
3770 * _BACKEND_ACCESS, _IN_USE, _NOT_FOUND, _EXISTS, _TYPE_MISMATCH.
3771 */
3772int
3773scf_transaction_property_new(scf_transaction_t *tx,
3774    scf_transaction_entry_t *entry, const char *prop, scf_type_t type)
3775{
3776	return (transaction_add(tx, entry, REP_PROTOCOL_TX_ENTRY_NEW,
3777	    prop, scf_type_to_protocol_type(type)));
3778}
3779
3780/*
3781 * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
3782 * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
3783 * _BACKEND_ACCESS, _IN_USE, _NOT_FOUND, _EXISTS, _TYPE_MISMATCH.
3784 */
3785int
3786scf_transaction_property_change(scf_transaction_t *tx,
3787    scf_transaction_entry_t *entry, const char *prop, scf_type_t type)
3788{
3789	return (transaction_add(tx, entry, REP_PROTOCOL_TX_ENTRY_CLEAR,
3790	    prop, scf_type_to_protocol_type(type)));
3791}
3792
3793/*
3794 * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
3795 * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
3796 * _BACKEND_ACCESS, _IN_USE, _NOT_FOUND, _EXISTS, _TYPE_MISMATCH.
3797 */
3798int
3799scf_transaction_property_change_type(scf_transaction_t *tx,
3800    scf_transaction_entry_t *entry, const char *prop, scf_type_t type)
3801{
3802	return (transaction_add(tx, entry, REP_PROTOCOL_TX_ENTRY_REPLACE,
3803	    prop, scf_type_to_protocol_type(type)));
3804}
3805
3806/*
3807 * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _NOT_BOUND,
3808 * _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED, _NO_RESOURCES,
3809 * _BACKEND_ACCESS, _IN_USE, _NOT_FOUND, _EXISTS, _TYPE_MISMATCH.
3810 */
3811int
3812scf_transaction_property_delete(scf_transaction_t *tx,
3813    scf_transaction_entry_t *entry, const char *prop)
3814{
3815	return (transaction_add(tx, entry, REP_PROTOCOL_TX_ENTRY_DELETE,
3816	    prop, REP_PROTOCOL_TYPE_INVALID));
3817}
3818
3819#define	BAD_SIZE (-1UL)
3820
3821static size_t
3822commit_value(caddr_t data, scf_value_t *val, rep_protocol_value_type_t t)
3823{
3824	size_t len;
3825
3826	assert(val->value_type == t);
3827
3828	if (t == REP_PROTOCOL_TYPE_OPAQUE) {
3829		len = scf_opaque_encode(data, val->value_value,
3830		    val->value_size);
3831	} else {
3832		if (data != NULL)
3833			len = strlcpy(data, val->value_value,
3834			    REP_PROTOCOL_VALUE_LEN);
3835		else
3836			len = strlen(val->value_value);
3837		if (len >= REP_PROTOCOL_VALUE_LEN)
3838			return (BAD_SIZE);
3839	}
3840	return (len + 1);	/* count the '\0' */
3841}
3842
3843static size_t
3844commit_process(scf_transaction_entry_t *cur,
3845    struct rep_protocol_transaction_cmd *out)
3846{
3847	scf_value_t *child;
3848	size_t sz = 0;
3849	size_t len;
3850	caddr_t data = (caddr_t)out->rptc_data;
3851	caddr_t val_data;
3852
3853	if (out != NULL) {
3854		len = strlcpy(data, cur->entry_property, REP_PROTOCOL_NAME_LEN);
3855
3856		out->rptc_action = cur->entry_action;
3857		out->rptc_type = cur->entry_type;
3858		out->rptc_name_len = len + 1;
3859	} else {
3860		len = strlen(cur->entry_property);
3861	}
3862
3863	if (len >= REP_PROTOCOL_NAME_LEN)
3864		return (BAD_SIZE);
3865
3866	len = TX_SIZE(len + 1);
3867
3868	sz += len;
3869	val_data = data + len;
3870
3871	for (child = cur->entry_head; child != NULL;
3872	    child = child->value_next) {
3873		assert(cur->entry_action != REP_PROTOCOL_TX_ENTRY_DELETE);
3874		if (out != NULL) {
3875			len = commit_value(val_data + sizeof (uint32_t), child,
3876			    cur->entry_type);
3877			/* LINTED alignment */
3878			*(uint32_t *)val_data = len;
3879		} else
3880			len = commit_value(NULL, child, cur->entry_type);
3881
3882		if (len == BAD_SIZE)
3883			return (BAD_SIZE);
3884
3885		len += sizeof (uint32_t);
3886		len = TX_SIZE(len);
3887
3888		sz += len;
3889		val_data += len;
3890	}
3891
3892	assert(val_data - data == sz);
3893
3894	if (out != NULL)
3895		out->rptc_size = REP_PROTOCOL_TRANSACTION_CMD_SIZE(sz);
3896
3897	return (REP_PROTOCOL_TRANSACTION_CMD_SIZE(sz));
3898}
3899
3900int
3901scf_transaction_commit(scf_transaction_t *tran)
3902{
3903	scf_handle_t *h = tran->tran_pg.rd_d.rd_handle;
3904
3905	struct rep_protocol_transaction_commit *request;
3906	struct rep_protocol_response response;
3907	uintptr_t cmd;
3908	scf_transaction_entry_t *cur;
3909	size_t total, size;
3910	size_t request_size;
3911	size_t new_total;
3912	int r;
3913
3914	(void) pthread_mutex_lock(&h->rh_lock);
3915	if (tran->tran_state != TRAN_STATE_SETUP ||
3916	    tran->tran_invalid) {
3917		(void) pthread_mutex_unlock(&h->rh_lock);
3918		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
3919	}
3920
3921	total = 0;
3922	for (cur = uu_list_first(tran->tran_props); cur != NULL;
3923	    cur = uu_list_next(tran->tran_props, cur)) {
3924		size = commit_process(cur, NULL);
3925		if (size == BAD_SIZE) {
3926			(void) pthread_mutex_unlock(&h->rh_lock);
3927			return (scf_set_error(SCF_ERROR_INTERNAL));
3928		}
3929		assert(TX_SIZE(size) == size);
3930		total += size;
3931	}
3932
3933	request_size = REP_PROTOCOL_TRANSACTION_COMMIT_SIZE(total);
3934	request = alloca(request_size);
3935	(void) memset(request, '\0', request_size);
3936	request->rpr_request = REP_PROTOCOL_PROPERTYGRP_TX_COMMIT;
3937	request->rpr_entityid = tran->tran_pg.rd_d.rd_entity;
3938	request->rpr_size = request_size;
3939	cmd = (uintptr_t)request->rpr_cmd;
3940
3941	datael_finish_reset(&tran->tran_pg.rd_d);
3942
3943	new_total = 0;
3944	for (cur = uu_list_first(tran->tran_props); cur != NULL;
3945	    cur = uu_list_next(tran->tran_props, cur)) {
3946		size = commit_process(cur, (void *)cmd);
3947		if (size == BAD_SIZE) {
3948			(void) pthread_mutex_unlock(&h->rh_lock);
3949			return (scf_set_error(SCF_ERROR_INTERNAL));
3950		}
3951		cmd += size;
3952		new_total += size;
3953	}
3954	assert(new_total == total);
3955
3956	r = make_door_call(h, request, request_size,
3957	    &response, sizeof (response));
3958
3959	if (r < 0) {
3960		(void) pthread_mutex_unlock(&h->rh_lock);
3961		DOOR_ERRORS_BLOCK(r);
3962	}
3963
3964	if (response.rpr_response != REP_PROTOCOL_SUCCESS &&
3965	    response.rpr_response != REP_PROTOCOL_FAIL_NOT_LATEST) {
3966		(void) pthread_mutex_unlock(&h->rh_lock);
3967		return (scf_set_error(proto_error(response.rpr_response)));
3968	}
3969
3970	tran->tran_state = TRAN_STATE_COMMITTED;
3971	(void) pthread_mutex_unlock(&h->rh_lock);
3972	return (response.rpr_response == REP_PROTOCOL_SUCCESS);
3973}
3974
3975static void
3976transaction_reset(scf_transaction_t *tran)
3977{
3978	assert(MUTEX_HELD(&tran->tran_pg.rd_d.rd_handle->rh_lock));
3979
3980	tran->tran_state = TRAN_STATE_NEW;
3981	datael_reset_locked(&tran->tran_pg.rd_d);
3982}
3983
3984static void
3985scf_transaction_reset_impl(scf_transaction_t *tran, int and_destroy,
3986    int and_reset_value)
3987{
3988	scf_transaction_entry_t *cur;
3989	void *cookie;
3990
3991	(void) pthread_mutex_lock(&tran->tran_pg.rd_d.rd_handle->rh_lock);
3992	cookie = NULL;
3993	while ((cur = uu_list_teardown(tran->tran_props, &cookie)) != NULL) {
3994		cur->entry_tx = NULL;
3995
3996		assert(cur->entry_state == ENTRY_STATE_IN_TX_ACTION);
3997		cur->entry_state = ENTRY_STATE_INVALID;
3998
3999		entry_invalidate(cur, and_destroy, and_reset_value);
4000		if (and_destroy)
4001			entry_destroy_locked(cur);
4002	}
4003	transaction_reset(tran);
4004	handle_unrefed(tran->tran_pg.rd_d.rd_handle);
4005}
4006
4007void
4008scf_transaction_reset(scf_transaction_t *tran)
4009{
4010	scf_transaction_reset_impl(tran, 0, 0);
4011}
4012
4013void
4014scf_transaction_reset_all(scf_transaction_t *tran)
4015{
4016	scf_transaction_reset_impl(tran, 0, 1);
4017}
4018
4019void
4020scf_transaction_destroy(scf_transaction_t *val)
4021{
4022	if (val == NULL)
4023		return;
4024
4025	scf_transaction_reset(val);
4026
4027	datael_destroy(&val->tran_pg.rd_d);
4028
4029	uu_list_destroy(val->tran_props);
4030	uu_free(val);
4031}
4032
4033void
4034scf_transaction_destroy_children(scf_transaction_t *tran)
4035{
4036	scf_transaction_reset_impl(tran, 1, 0);
4037}
4038
4039scf_transaction_entry_t *
4040scf_entry_create(scf_handle_t *h)
4041{
4042	scf_transaction_entry_t *ret;
4043
4044	if (h == NULL) {
4045		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
4046		return (NULL);
4047	}
4048
4049	ret = uu_zalloc(sizeof (scf_transaction_entry_t));
4050	if (ret == NULL) {
4051		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
4052		return (NULL);
4053	}
4054	ret->entry_action = REP_PROTOCOL_TX_ENTRY_INVALID;
4055	ret->entry_handle = h;
4056
4057	(void) pthread_mutex_lock(&h->rh_lock);
4058	if (h->rh_flags & HANDLE_DEAD) {
4059		(void) pthread_mutex_unlock(&h->rh_lock);
4060		uu_free(ret);
4061		(void) scf_set_error(SCF_ERROR_HANDLE_DESTROYED);
4062		return (NULL);
4063	}
4064	h->rh_entries++;
4065	h->rh_extrefs++;
4066	(void) pthread_mutex_unlock(&h->rh_lock);
4067
4068	uu_list_node_init(ret, &ret->entry_link, tran_entry_pool);
4069
4070	return (ret);
4071}
4072
4073scf_handle_t *
4074scf_entry_handle(const scf_transaction_entry_t *val)
4075{
4076	return (handle_get(val->entry_handle));
4077}
4078
4079void
4080scf_entry_reset(scf_transaction_entry_t *entry)
4081{
4082	scf_handle_t *h = entry->entry_handle;
4083
4084	(void) pthread_mutex_lock(&h->rh_lock);
4085	entry_invalidate(entry, 0, 0);
4086	(void) pthread_mutex_unlock(&h->rh_lock);
4087}
4088
4089void
4090scf_entry_destroy_children(scf_transaction_entry_t *entry)
4091{
4092	scf_handle_t *h = entry->entry_handle;
4093
4094	(void) pthread_mutex_lock(&h->rh_lock);
4095	entry_invalidate(entry, 1, 0);
4096	handle_unrefed(h);			/* drops h->rh_lock */
4097}
4098
4099void
4100scf_entry_destroy(scf_transaction_entry_t *entry)
4101{
4102	scf_handle_t *h;
4103
4104	if (entry == NULL)
4105		return;
4106
4107	h = entry->entry_handle;
4108
4109	(void) pthread_mutex_lock(&h->rh_lock);
4110	entry_destroy_locked(entry);
4111	handle_unrefed(h);			/* drops h->rh_lock */
4112}
4113
4114/*
4115 * Fails with
4116 *   _HANDLE_MISMATCH
4117 *   _NOT_SET - has not been added to a transaction
4118 *   _INTERNAL - entry is corrupt
4119 *   _INVALID_ARGUMENT - entry's transaction is not started or corrupt
4120 *			 entry is set to delete a property
4121 *			 v is reset or corrupt
4122 *   _TYPE_MISMATCH - entry & v's types aren't compatible
4123 *   _IN_USE - v has been added to another entry
4124 */
4125int
4126scf_entry_add_value(scf_transaction_entry_t *entry, scf_value_t *v)
4127{
4128	scf_handle_t *h = entry->entry_handle;
4129
4130	if (h != v->value_handle)
4131		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
4132
4133	(void) pthread_mutex_lock(&h->rh_lock);
4134
4135	if (entry->entry_state == ENTRY_STATE_INVALID) {
4136		(void) pthread_mutex_unlock(&h->rh_lock);
4137		return (scf_set_error(SCF_ERROR_NOT_SET));
4138	}
4139
4140	if (entry->entry_state != ENTRY_STATE_IN_TX_ACTION) {
4141		(void) pthread_mutex_unlock(&h->rh_lock);
4142		return (scf_set_error(SCF_ERROR_INTERNAL));
4143	}
4144
4145	if (entry->entry_tx->tran_state != TRAN_STATE_SETUP) {
4146		(void) pthread_mutex_unlock(&h->rh_lock);
4147		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4148	}
4149
4150	if (entry->entry_action == REP_PROTOCOL_TX_ENTRY_DELETE) {
4151		(void) pthread_mutex_unlock(&h->rh_lock);
4152		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4153	}
4154
4155	if (v->value_type == REP_PROTOCOL_TYPE_INVALID) {
4156		(void) pthread_mutex_unlock(&h->rh_lock);
4157		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4158	}
4159
4160	if (!scf_is_compatible_type(entry->entry_type, v->value_type)) {
4161		(void) pthread_mutex_unlock(&h->rh_lock);
4162		return (scf_set_error(SCF_ERROR_TYPE_MISMATCH));
4163	}
4164
4165	if (v->value_tx != NULL) {
4166		(void) pthread_mutex_unlock(&h->rh_lock);
4167		return (scf_set_error(SCF_ERROR_IN_USE));
4168	}
4169
4170	v->value_tx = entry;
4171	v->value_next = entry->entry_head;
4172	entry->entry_head = v;
4173	(void) pthread_mutex_unlock(&h->rh_lock);
4174
4175	return (SCF_SUCCESS);
4176}
4177
4178/*
4179 * value functions
4180 */
4181scf_value_t *
4182scf_value_create(scf_handle_t *h)
4183{
4184	scf_value_t *ret;
4185
4186	if (h == NULL) {
4187		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
4188		return (NULL);
4189	}
4190
4191	ret = uu_zalloc(sizeof (*ret));
4192	if (ret != NULL) {
4193		ret->value_type = REP_PROTOCOL_TYPE_INVALID;
4194		ret->value_handle = h;
4195		(void) pthread_mutex_lock(&h->rh_lock);
4196		if (h->rh_flags & HANDLE_DEAD) {
4197			(void) pthread_mutex_unlock(&h->rh_lock);
4198			uu_free(ret);
4199			(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
4200			return (NULL);
4201		}
4202		h->rh_values++;
4203		h->rh_extrefs++;
4204		(void) pthread_mutex_unlock(&h->rh_lock);
4205	} else {
4206		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
4207	}
4208
4209	return (ret);
4210}
4211
4212static void
4213scf_value_reset_locked(scf_value_t *val, int and_destroy)
4214{
4215	scf_value_t **curp;
4216	scf_transaction_entry_t *te;
4217
4218	scf_handle_t *h = val->value_handle;
4219	assert(MUTEX_HELD(&h->rh_lock));
4220	if (val->value_tx != NULL) {
4221		te = val->value_tx;
4222		te->entry_tx->tran_invalid = 1;
4223
4224		val->value_tx = NULL;
4225
4226		for (curp = &te->entry_head; *curp != NULL;
4227		    curp = &(*curp)->value_next) {
4228			if (*curp == val) {
4229				*curp = val->value_next;
4230				curp = NULL;
4231				break;
4232			}
4233		}
4234		assert(curp == NULL);
4235	}
4236	val->value_type = REP_PROTOCOL_TYPE_INVALID;
4237
4238	if (and_destroy) {
4239		val->value_handle = NULL;
4240		assert(h->rh_values > 0);
4241		--h->rh_values;
4242		--h->rh_extrefs;
4243		uu_free(val);
4244	}
4245}
4246
4247void
4248scf_value_reset(scf_value_t *val)
4249{
4250	scf_handle_t *h = val->value_handle;
4251
4252	(void) pthread_mutex_lock(&h->rh_lock);
4253	scf_value_reset_locked(val, 0);
4254	(void) pthread_mutex_unlock(&h->rh_lock);
4255}
4256
4257scf_handle_t *
4258scf_value_handle(const scf_value_t *val)
4259{
4260	return (handle_get(val->value_handle));
4261}
4262
4263void
4264scf_value_destroy(scf_value_t *val)
4265{
4266	scf_handle_t *h;
4267
4268	if (val == NULL)
4269		return;
4270
4271	h = val->value_handle;
4272
4273	(void) pthread_mutex_lock(&h->rh_lock);
4274	scf_value_reset_locked(val, 1);
4275	handle_unrefed(h);			/* drops h->rh_lock */
4276}
4277
4278scf_type_t
4279scf_value_base_type(const scf_value_t *val)
4280{
4281	rep_protocol_value_type_t t, cur;
4282	scf_handle_t *h = val->value_handle;
4283
4284	(void) pthread_mutex_lock(&h->rh_lock);
4285	t = val->value_type;
4286	(void) pthread_mutex_unlock(&h->rh_lock);
4287
4288	for (;;) {
4289		cur = scf_proto_underlying_type(t);
4290		if (cur == t)
4291			break;
4292		t = cur;
4293	}
4294
4295	return (scf_protocol_type_to_type(t));
4296}
4297
4298scf_type_t
4299scf_value_type(const scf_value_t *val)
4300{
4301	rep_protocol_value_type_t t;
4302	scf_handle_t *h = val->value_handle;
4303
4304	(void) pthread_mutex_lock(&h->rh_lock);
4305	t = val->value_type;
4306	(void) pthread_mutex_unlock(&h->rh_lock);
4307
4308	return (scf_protocol_type_to_type(t));
4309}
4310
4311int
4312scf_value_is_type(const scf_value_t *val, scf_type_t base_arg)
4313{
4314	rep_protocol_value_type_t t;
4315	rep_protocol_value_type_t base = scf_type_to_protocol_type(base_arg);
4316	scf_handle_t *h = val->value_handle;
4317
4318	(void) pthread_mutex_lock(&h->rh_lock);
4319	t = val->value_type;
4320	(void) pthread_mutex_unlock(&h->rh_lock);
4321
4322	if (t == REP_PROTOCOL_TYPE_INVALID)
4323		return (scf_set_error(SCF_ERROR_NOT_SET));
4324	if (base == REP_PROTOCOL_TYPE_INVALID)
4325		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4326	if (!scf_is_compatible_type(base, t))
4327		return (scf_set_error(SCF_ERROR_TYPE_MISMATCH));
4328
4329	return (SCF_SUCCESS);
4330}
4331
4332/*
4333 * Fails with
4334 *   _NOT_SET - val is reset
4335 *   _TYPE_MISMATCH - val's type is not compatible with t
4336 */
4337static int
4338scf_value_check_type(const scf_value_t *val, rep_protocol_value_type_t t)
4339{
4340	if (val->value_type == REP_PROTOCOL_TYPE_INVALID) {
4341		(void) scf_set_error(SCF_ERROR_NOT_SET);
4342		return (0);
4343	}
4344	if (!scf_is_compatible_type(t, val->value_type)) {
4345		(void) scf_set_error(SCF_ERROR_TYPE_MISMATCH);
4346		return (0);
4347	}
4348	return (1);
4349}
4350
4351/*
4352 * Fails with
4353 *   _NOT_SET - val is reset
4354 *   _TYPE_MISMATCH - val is not _TYPE_BOOLEAN
4355 */
4356int
4357scf_value_get_boolean(const scf_value_t *val, uint8_t *out)
4358{
4359	char c;
4360	scf_handle_t *h = val->value_handle;
4361	uint8_t o;
4362
4363	(void) pthread_mutex_lock(&h->rh_lock);
4364	if (!scf_value_check_type(val, REP_PROTOCOL_TYPE_BOOLEAN)) {
4365		(void) pthread_mutex_unlock(&h->rh_lock);
4366		return (-1);
4367	}
4368
4369	c = val->value_value[0];
4370	assert((c == '0' || c == '1') && val->value_value[1] == 0);
4371
4372	o = (c != '0');
4373	(void) pthread_mutex_unlock(&h->rh_lock);
4374	if (out != NULL)
4375		*out = o;
4376	return (SCF_SUCCESS);
4377}
4378
4379int
4380scf_value_get_count(const scf_value_t *val, uint64_t *out)
4381{
4382	scf_handle_t *h = val->value_handle;
4383	uint64_t o;
4384
4385	(void) pthread_mutex_lock(&h->rh_lock);
4386	if (!scf_value_check_type(val, REP_PROTOCOL_TYPE_COUNT)) {
4387		(void) pthread_mutex_unlock(&h->rh_lock);
4388		return (-1);
4389	}
4390
4391	o = strtoull(val->value_value, NULL, 10);
4392	(void) pthread_mutex_unlock(&h->rh_lock);
4393	if (out != NULL)
4394		*out = o;
4395	return (SCF_SUCCESS);
4396}
4397
4398int
4399scf_value_get_integer(const scf_value_t *val, int64_t *out)
4400{
4401	scf_handle_t *h = val->value_handle;
4402	int64_t o;
4403
4404	(void) pthread_mutex_lock(&h->rh_lock);
4405	if (!scf_value_check_type(val, REP_PROTOCOL_TYPE_INTEGER)) {
4406		(void) pthread_mutex_unlock(&h->rh_lock);
4407		return (-1);
4408	}
4409
4410	o = strtoll(val->value_value, NULL, 10);
4411	(void) pthread_mutex_unlock(&h->rh_lock);
4412	if (out != NULL)
4413		*out = o;
4414	return (SCF_SUCCESS);
4415}
4416
4417int
4418scf_value_get_time(const scf_value_t *val, int64_t *sec_out, int32_t *nsec_out)
4419{
4420	scf_handle_t *h = val->value_handle;
4421	char *p;
4422	int64_t os;
4423	int32_t ons;
4424
4425	(void) pthread_mutex_lock(&h->rh_lock);
4426	if (!scf_value_check_type(val, REP_PROTOCOL_TYPE_TIME)) {
4427		(void) pthread_mutex_unlock(&h->rh_lock);
4428		return (-1);
4429	}
4430
4431	os = strtoll(val->value_value, &p, 10);
4432	if (*p == '.')
4433		ons = strtoul(p + 1, NULL, 10);
4434	else
4435		ons = 0;
4436	(void) pthread_mutex_unlock(&h->rh_lock);
4437	if (sec_out != NULL)
4438		*sec_out = os;
4439	if (nsec_out != NULL)
4440		*nsec_out = ons;
4441
4442	return (SCF_SUCCESS);
4443}
4444
4445/*
4446 * Fails with
4447 *   _NOT_SET - val is reset
4448 *   _TYPE_MISMATCH - val's type is not compatible with _TYPE_STRING.
4449 */
4450ssize_t
4451scf_value_get_astring(const scf_value_t *val, char *out, size_t len)
4452{
4453	ssize_t ret;
4454	scf_handle_t *h = val->value_handle;
4455
4456	(void) pthread_mutex_lock(&h->rh_lock);
4457	if (!scf_value_check_type(val, REP_PROTOCOL_TYPE_STRING)) {
4458		(void) pthread_mutex_unlock(&h->rh_lock);
4459		return ((ssize_t)-1);
4460	}
4461	ret = (ssize_t)strlcpy(out, val->value_value, len);
4462	(void) pthread_mutex_unlock(&h->rh_lock);
4463	return (ret);
4464}
4465
4466ssize_t
4467scf_value_get_ustring(const scf_value_t *val, char *out, size_t len)
4468{
4469	ssize_t ret;
4470	scf_handle_t *h = val->value_handle;
4471
4472	(void) pthread_mutex_lock(&h->rh_lock);
4473	if (!scf_value_check_type(val, REP_PROTOCOL_SUBTYPE_USTRING)) {
4474		(void) pthread_mutex_unlock(&h->rh_lock);
4475		return ((ssize_t)-1);
4476	}
4477	ret = (ssize_t)strlcpy(out, val->value_value, len);
4478	(void) pthread_mutex_unlock(&h->rh_lock);
4479	return (ret);
4480}
4481
4482ssize_t
4483scf_value_get_opaque(const scf_value_t *v, void *out, size_t len)
4484{
4485	ssize_t ret;
4486	scf_handle_t *h = v->value_handle;
4487
4488	(void) pthread_mutex_lock(&h->rh_lock);
4489	if (!scf_value_check_type(v, REP_PROTOCOL_TYPE_OPAQUE)) {
4490		(void) pthread_mutex_unlock(&h->rh_lock);
4491		return ((ssize_t)-1);
4492	}
4493	if (len > v->value_size)
4494		len = v->value_size;
4495	ret = len;
4496
4497	(void) memcpy(out, v->value_value, len);
4498	(void) pthread_mutex_unlock(&h->rh_lock);
4499	return (ret);
4500}
4501
4502void
4503scf_value_set_boolean(scf_value_t *v, uint8_t new)
4504{
4505	scf_handle_t *h = v->value_handle;
4506
4507	(void) pthread_mutex_lock(&h->rh_lock);
4508	scf_value_reset_locked(v, 0);
4509	v->value_type = REP_PROTOCOL_TYPE_BOOLEAN;
4510	(void) sprintf(v->value_value, "%d", (new != 0));
4511	(void) pthread_mutex_unlock(&h->rh_lock);
4512}
4513
4514void
4515scf_value_set_count(scf_value_t *v, uint64_t new)
4516{
4517	scf_handle_t *h = v->value_handle;
4518
4519	(void) pthread_mutex_lock(&h->rh_lock);
4520	scf_value_reset_locked(v, 0);
4521	v->value_type = REP_PROTOCOL_TYPE_COUNT;
4522	(void) sprintf(v->value_value, "%llu", (unsigned long long)new);
4523	(void) pthread_mutex_unlock(&h->rh_lock);
4524}
4525
4526void
4527scf_value_set_integer(scf_value_t *v, int64_t new)
4528{
4529	scf_handle_t *h = v->value_handle;
4530
4531	(void) pthread_mutex_lock(&h->rh_lock);
4532	scf_value_reset_locked(v, 0);
4533	v->value_type = REP_PROTOCOL_TYPE_INTEGER;
4534	(void) sprintf(v->value_value, "%lld", (long long)new);
4535	(void) pthread_mutex_unlock(&h->rh_lock);
4536}
4537
4538int
4539scf_value_set_time(scf_value_t *v, int64_t new_sec, int32_t new_nsec)
4540{
4541	scf_handle_t *h = v->value_handle;
4542
4543	(void) pthread_mutex_lock(&h->rh_lock);
4544	scf_value_reset_locked(v, 0);
4545	if (new_nsec < 0 || new_nsec >= NANOSEC) {
4546		(void) pthread_mutex_unlock(&h->rh_lock);
4547		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4548	}
4549	v->value_type = REP_PROTOCOL_TYPE_TIME;
4550	if (new_nsec == 0)
4551		(void) sprintf(v->value_value, "%lld", (long long)new_sec);
4552	else
4553		(void) sprintf(v->value_value, "%lld.%09u", (long long)new_sec,
4554		    (unsigned)new_nsec);
4555	(void) pthread_mutex_unlock(&h->rh_lock);
4556	return (0);
4557}
4558
4559int
4560scf_value_set_astring(scf_value_t *v, const char *new)
4561{
4562	scf_handle_t *h = v->value_handle;
4563
4564	(void) pthread_mutex_lock(&h->rh_lock);
4565	scf_value_reset_locked(v, 0);
4566	if (!scf_validate_encoded_value(REP_PROTOCOL_TYPE_STRING, new)) {
4567		(void) pthread_mutex_unlock(&h->rh_lock);
4568		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4569	}
4570	if (strlcpy(v->value_value, new, sizeof (v->value_value)) >=
4571	    sizeof (v->value_value)) {
4572		(void) pthread_mutex_unlock(&h->rh_lock);
4573		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4574	}
4575	v->value_type = REP_PROTOCOL_TYPE_STRING;
4576	(void) pthread_mutex_unlock(&h->rh_lock);
4577	return (0);
4578}
4579
4580int
4581scf_value_set_ustring(scf_value_t *v, const char *new)
4582{
4583	scf_handle_t *h = v->value_handle;
4584
4585	(void) pthread_mutex_lock(&h->rh_lock);
4586	scf_value_reset_locked(v, 0);
4587	if (!scf_validate_encoded_value(REP_PROTOCOL_SUBTYPE_USTRING, new)) {
4588		(void) pthread_mutex_unlock(&h->rh_lock);
4589		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4590	}
4591	if (strlcpy(v->value_value, new, sizeof (v->value_value)) >=
4592	    sizeof (v->value_value)) {
4593		(void) pthread_mutex_unlock(&h->rh_lock);
4594		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4595	}
4596	v->value_type = REP_PROTOCOL_SUBTYPE_USTRING;
4597	(void) pthread_mutex_unlock(&h->rh_lock);
4598	return (0);
4599}
4600
4601int
4602scf_value_set_opaque(scf_value_t *v, const void *new, size_t len)
4603{
4604	scf_handle_t *h = v->value_handle;
4605
4606	(void) pthread_mutex_lock(&h->rh_lock);
4607	scf_value_reset_locked(v, 0);
4608	if (len > sizeof (v->value_value)) {
4609		(void) pthread_mutex_unlock(&h->rh_lock);
4610		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4611	}
4612	(void) memcpy(v->value_value, new, len);
4613	v->value_size = len;
4614	v->value_type = REP_PROTOCOL_TYPE_OPAQUE;
4615	(void) pthread_mutex_unlock(&h->rh_lock);
4616	return (0);
4617}
4618
4619/*
4620 * Fails with
4621 *   _NOT_SET - v_arg is reset
4622 *   _INTERNAL - v_arg is corrupt
4623 *
4624 * If t is not _TYPE_INVALID, fails with
4625 *   _TYPE_MISMATCH - v_arg's type is not compatible with t
4626 */
4627static ssize_t
4628scf_value_get_as_string_common(const scf_value_t *v_arg,
4629    rep_protocol_value_type_t t, char *buf, size_t bufsz)
4630{
4631	scf_handle_t *h = v_arg->value_handle;
4632	scf_value_t v_s;
4633	scf_value_t *v = &v_s;
4634	ssize_t r;
4635	uint8_t b;
4636
4637	(void) pthread_mutex_lock(&h->rh_lock);
4638	if (t != REP_PROTOCOL_TYPE_INVALID && !scf_value_check_type(v_arg, t)) {
4639		(void) pthread_mutex_unlock(&h->rh_lock);
4640		return (-1);
4641	}
4642
4643	v_s = *v_arg;			/* copy locally so we can unlock */
4644	h->rh_values++;			/* keep the handle from going away */
4645	h->rh_extrefs++;
4646	(void) pthread_mutex_unlock(&h->rh_lock);
4647
4648
4649	switch (REP_PROTOCOL_BASE_TYPE(v->value_type)) {
4650	case REP_PROTOCOL_TYPE_BOOLEAN:
4651		r = scf_value_get_boolean(v, &b);
4652		assert(r == SCF_SUCCESS);
4653
4654		r = strlcpy(buf, b ? "true" : "false", bufsz);
4655		break;
4656
4657	case REP_PROTOCOL_TYPE_COUNT:
4658	case REP_PROTOCOL_TYPE_INTEGER:
4659	case REP_PROTOCOL_TYPE_TIME:
4660	case REP_PROTOCOL_TYPE_STRING:
4661		r = strlcpy(buf, v->value_value, bufsz);
4662		break;
4663
4664	case REP_PROTOCOL_TYPE_OPAQUE:
4665		/*
4666		 * Note that we only write out full hex bytes -- if they're
4667		 * short, and bufsz is even, we'll only fill (bufsz - 2) bytes
4668		 * with data.
4669		 */
4670		if (bufsz > 0)
4671			(void) scf_opaque_encode(buf, v->value_value,
4672			    MIN(v->value_size, (bufsz - 1)/2));
4673		r = (v->value_size * 2);
4674		break;
4675
4676	case REP_PROTOCOL_TYPE_INVALID:
4677		r = scf_set_error(SCF_ERROR_NOT_SET);
4678		break;
4679
4680	default:
4681		r = (scf_set_error(SCF_ERROR_INTERNAL));
4682		break;
4683	}
4684
4685	(void) pthread_mutex_lock(&h->rh_lock);
4686	h->rh_values--;
4687	h->rh_extrefs--;
4688	handle_unrefed(h);
4689
4690	return (r);
4691}
4692
4693ssize_t
4694scf_value_get_as_string(const scf_value_t *v, char *buf, size_t bufsz)
4695{
4696	return (scf_value_get_as_string_common(v, REP_PROTOCOL_TYPE_INVALID,
4697	    buf, bufsz));
4698}
4699
4700ssize_t
4701scf_value_get_as_string_typed(const scf_value_t *v, scf_type_t type,
4702    char *buf, size_t bufsz)
4703{
4704	rep_protocol_value_type_t ty = scf_type_to_protocol_type(type);
4705	if (ty == REP_PROTOCOL_TYPE_INVALID)
4706		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4707
4708	return (scf_value_get_as_string_common(v, ty, buf, bufsz));
4709}
4710
4711int
4712scf_value_set_from_string(scf_value_t *v, scf_type_t type, const char *str)
4713{
4714	scf_handle_t *h = v->value_handle;
4715	rep_protocol_value_type_t ty;
4716
4717	switch (type) {
4718	case SCF_TYPE_BOOLEAN: {
4719		uint8_t b;
4720
4721		if (strcmp(str, "true") == 0 || strcmp(str, "t") == 0 ||
4722		    strcmp(str, "1") == 0)
4723			b = 1;
4724		else if (strcmp(str, "false") == 0 ||
4725		    strcmp(str, "f") == 0 || strcmp(str, "0") == 0)
4726			b = 0;
4727		else {
4728			goto bad;
4729		}
4730
4731		scf_value_set_boolean(v, b);
4732		return (0);
4733	}
4734
4735	case SCF_TYPE_COUNT: {
4736		uint64_t c;
4737		char *endp;
4738
4739		errno = 0;
4740		c = strtoull(str, &endp, 0);
4741
4742		if (errno != 0 || endp == str || *endp != '\0')
4743			goto bad;
4744
4745		scf_value_set_count(v, c);
4746		return (0);
4747	}
4748
4749	case SCF_TYPE_INTEGER: {
4750		int64_t i;
4751		char *endp;
4752
4753		errno = 0;
4754		i = strtoll(str, &endp, 0);
4755
4756		if (errno != 0 || endp == str || *endp != '\0')
4757			goto bad;
4758
4759		scf_value_set_integer(v, i);
4760		return (0);
4761	}
4762
4763	case SCF_TYPE_TIME: {
4764		int64_t s;
4765		uint32_t ns = 0;
4766		char *endp, *ns_str;
4767		size_t len;
4768
4769		errno = 0;
4770		s = strtoll(str, &endp, 10);
4771		if (errno != 0 || endp == str ||
4772		    (*endp != '\0' && *endp != '.'))
4773			goto bad;
4774
4775		if (*endp == '.') {
4776			ns_str = endp + 1;
4777			len = strlen(ns_str);
4778			if (len == 0 || len > 9)
4779				goto bad;
4780
4781			ns = strtoul(ns_str, &endp, 10);
4782			if (errno != 0 || endp == ns_str || *endp != '\0')
4783				goto bad;
4784
4785			while (len++ < 9)
4786				ns *= 10;
4787			assert(ns < NANOSEC);
4788		}
4789
4790		return (scf_value_set_time(v, s, ns));
4791	}
4792
4793	case SCF_TYPE_ASTRING:
4794	case SCF_TYPE_USTRING:
4795	case SCF_TYPE_OPAQUE:
4796	case SCF_TYPE_URI:
4797	case SCF_TYPE_FMRI:
4798	case SCF_TYPE_HOST:
4799	case SCF_TYPE_HOSTNAME:
4800	case SCF_TYPE_NET_ADDR_V4:
4801	case SCF_TYPE_NET_ADDR_V6:
4802		ty = scf_type_to_protocol_type(type);
4803
4804		(void) pthread_mutex_lock(&h->rh_lock);
4805		scf_value_reset_locked(v, 0);
4806		if (type == SCF_TYPE_OPAQUE) {
4807			v->value_size = scf_opaque_decode(v->value_value,
4808			    str, sizeof (v->value_value));
4809			if (!scf_validate_encoded_value(ty, str)) {
4810				(void) pthread_mutex_lock(&h->rh_lock);
4811				goto bad;
4812			}
4813		} else {
4814			(void) strlcpy(v->value_value, str,
4815			    sizeof (v->value_value));
4816			if (!scf_validate_encoded_value(ty, v->value_value)) {
4817				(void) pthread_mutex_lock(&h->rh_lock);
4818				goto bad;
4819			}
4820		}
4821		v->value_type = ty;
4822		(void) pthread_mutex_unlock(&h->rh_lock);
4823		return (SCF_SUCCESS);
4824
4825	case REP_PROTOCOL_TYPE_INVALID:
4826	default:
4827		scf_value_reset(v);
4828		return (scf_set_error(SCF_ERROR_TYPE_MISMATCH));
4829	}
4830bad:
4831	scf_value_reset(v);
4832	return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4833}
4834
4835int
4836scf_iter_property_values(scf_iter_t *iter, const scf_property_t *prop)
4837{
4838	return (datael_setup_iter(iter, &prop->rd_d,
4839	    REP_PROTOCOL_ENTITY_VALUE, 0));
4840}
4841
4842int
4843scf_iter_next_value(scf_iter_t *iter, scf_value_t *v)
4844{
4845	scf_handle_t *h = iter->iter_handle;
4846
4847	struct rep_protocol_iter_read_value request;
4848	struct rep_protocol_value_response response;
4849
4850	int r;
4851
4852	if (h != v->value_handle)
4853		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
4854
4855	(void) pthread_mutex_lock(&h->rh_lock);
4856
4857	scf_value_reset_locked(v, 0);
4858
4859	if (iter->iter_type == REP_PROTOCOL_ENTITY_NONE) {
4860		(void) pthread_mutex_unlock(&h->rh_lock);
4861		return (scf_set_error(SCF_ERROR_NOT_SET));
4862	}
4863
4864	if (iter->iter_type != REP_PROTOCOL_ENTITY_VALUE) {
4865		(void) pthread_mutex_unlock(&h->rh_lock);
4866		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4867	}
4868
4869	request.rpr_request = REP_PROTOCOL_ITER_READ_VALUE;
4870	request.rpr_iterid = iter->iter_id;
4871	request.rpr_sequence = iter->iter_sequence;
4872
4873	r = make_door_call(h, &request, sizeof (request),
4874	    &response, sizeof (response));
4875
4876	if (r < 0) {
4877		(void) pthread_mutex_unlock(&h->rh_lock);
4878		DOOR_ERRORS_BLOCK(r);
4879	}
4880
4881	if (response.rpr_response == REP_PROTOCOL_DONE) {
4882		(void) pthread_mutex_unlock(&h->rh_lock);
4883		return (0);
4884	}
4885	if (response.rpr_response != REP_PROTOCOL_SUCCESS) {
4886		(void) pthread_mutex_unlock(&h->rh_lock);
4887		return (scf_set_error(proto_error(response.rpr_response)));
4888	}
4889	iter->iter_sequence++;
4890
4891	v->value_type = response.rpr_type;
4892
4893	assert(scf_validate_encoded_value(response.rpr_type,
4894	    response.rpr_value));
4895
4896	if (v->value_type != REP_PROTOCOL_TYPE_OPAQUE) {
4897		(void) strlcpy(v->value_value, response.rpr_value,
4898		    sizeof (v->value_value));
4899	} else {
4900		v->value_size = scf_opaque_decode(v->value_value,
4901		    response.rpr_value, sizeof (v->value_value));
4902	}
4903	(void) pthread_mutex_unlock(&h->rh_lock);
4904
4905	return (1);
4906}
4907
4908int
4909scf_property_get_value(const scf_property_t *prop, scf_value_t *v)
4910{
4911	scf_handle_t *h = prop->rd_d.rd_handle;
4912	struct rep_protocol_property_request request;
4913	struct rep_protocol_value_response response;
4914	int r;
4915
4916	if (h != v->value_handle)
4917		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
4918
4919	(void) pthread_mutex_lock(&h->rh_lock);
4920
4921	request.rpr_request = REP_PROTOCOL_PROPERTY_GET_VALUE;
4922	request.rpr_entityid = prop->rd_d.rd_entity;
4923
4924	scf_value_reset_locked(v, 0);
4925	datael_finish_reset(&prop->rd_d);
4926
4927	r = make_door_call(h, &request, sizeof (request),
4928	    &response, sizeof (response));
4929
4930	if (r < 0) {
4931		(void) pthread_mutex_unlock(&h->rh_lock);
4932		DOOR_ERRORS_BLOCK(r);
4933	}
4934
4935	if (response.rpr_response != REP_PROTOCOL_SUCCESS &&
4936	    response.rpr_response != REP_PROTOCOL_FAIL_TRUNCATED) {
4937		(void) pthread_mutex_unlock(&h->rh_lock);
4938		assert(response.rpr_response !=
4939		    REP_PROTOCOL_FAIL_TYPE_MISMATCH);
4940		return (scf_set_error(proto_error(response.rpr_response)));
4941	}
4942
4943	v->value_type = response.rpr_type;
4944	if (v->value_type != REP_PROTOCOL_TYPE_OPAQUE) {
4945		(void) strlcpy(v->value_value, response.rpr_value,
4946		    sizeof (v->value_value));
4947	} else {
4948		v->value_size = scf_opaque_decode(v->value_value,
4949		    response.rpr_value, sizeof (v->value_value));
4950	}
4951	(void) pthread_mutex_unlock(&h->rh_lock);
4952	return ((response.rpr_response == REP_PROTOCOL_SUCCESS)?
4953	    SCF_SUCCESS : scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED));
4954}
4955
4956int
4957scf_pg_get_parent_service(const scf_propertygroup_t *pg, scf_service_t *svc)
4958{
4959	return (datael_get_parent(&pg->rd_d, &svc->rd_d));
4960}
4961
4962int
4963scf_pg_get_parent_instance(const scf_propertygroup_t *pg, scf_instance_t *inst)
4964{
4965	return (datael_get_parent(&pg->rd_d, &inst->rd_d));
4966}
4967
4968int
4969scf_pg_get_parent_snaplevel(const scf_propertygroup_t *pg,
4970    scf_snaplevel_t *level)
4971{
4972	return (datael_get_parent(&pg->rd_d, &level->rd_d));
4973}
4974
4975int
4976scf_service_get_parent(const scf_service_t *svc, scf_scope_t *s)
4977{
4978	return (datael_get_parent(&svc->rd_d, &s->rd_d));
4979}
4980
4981int
4982scf_instance_get_parent(const scf_instance_t *inst, scf_service_t *svc)
4983{
4984	return (datael_get_parent(&inst->rd_d, &svc->rd_d));
4985}
4986
4987int
4988scf_snapshot_get_parent(const scf_snapshot_t *inst, scf_instance_t *svc)
4989{
4990	return (datael_get_parent(&inst->rd_d, &svc->rd_d));
4991}
4992
4993int
4994scf_snaplevel_get_parent(const scf_snaplevel_t *inst, scf_snapshot_t *svc)
4995{
4996	return (datael_get_parent(&inst->rd_d, &svc->rd_d));
4997}
4998
4999/*
5000 * FMRI functions
5001 *
5002 * Note: In the scf_parse_svc_fmri(), scf_parse_file_fmri() and
5003 * scf_parse_fmri(), fmri isn't const because that would require
5004 * allocating memory. Also, note that scope, at least, is not necessarily
5005 * in the passed in fmri.
5006 */
5007
5008int
5009scf_parse_svc_fmri(char *fmri, const char **scope, const char **service,
5010    const char **instance, const char **propertygroup, const char **property)
5011{
5012	char *s, *e, *te, *tpg;
5013	char *my_s = NULL, *my_i = NULL, *my_pg = NULL, *my_p = NULL;
5014
5015	if (scope != NULL)
5016		*scope = NULL;
5017	if (service != NULL)
5018		*service = NULL;
5019	if (instance != NULL)
5020		*instance = NULL;
5021	if (propertygroup != NULL)
5022		*propertygroup = NULL;
5023	if (property != NULL)
5024		*property = NULL;
5025
5026	s = fmri;
5027	e = strchr(s, '\0');
5028
5029	if (strncmp(s, SCF_FMRI_SVC_PREFIX,
5030	    sizeof (SCF_FMRI_SVC_PREFIX) - 1) == 0)
5031		s += sizeof (SCF_FMRI_SVC_PREFIX) - 1;
5032
5033	if (strncmp(s, SCF_FMRI_SCOPE_PREFIX,
5034	    sizeof (SCF_FMRI_SCOPE_PREFIX) - 1) == 0) {
5035		char *my_scope;
5036
5037		s += sizeof (SCF_FMRI_SCOPE_PREFIX) - 1;
5038		te = strstr(s, SCF_FMRI_SERVICE_PREFIX);
5039		if (te == NULL)
5040			te = e;
5041
5042		*te = 0;
5043		my_scope = s;
5044
5045		s = te;
5046		if (s < e)
5047			s += sizeof (SCF_FMRI_SERVICE_PREFIX) - 1;
5048
5049		/* If the scope ends with the suffix, remove it. */
5050		te = strstr(my_scope, SCF_FMRI_SCOPE_SUFFIX);
5051		if (te != NULL && te[sizeof (SCF_FMRI_SCOPE_SUFFIX) - 1] == 0)
5052			*te = 0;
5053
5054		/* Validate the scope. */
5055		if (my_scope[0] == '\0')
5056			my_scope = SCF_FMRI_LOCAL_SCOPE;
5057		else if (uu_check_name(my_scope, 0) == -1) {
5058			return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
5059		}
5060
5061		if (scope != NULL)
5062			*scope = my_scope;
5063	} else {
5064		if (scope != NULL)
5065			*scope = SCF_FMRI_LOCAL_SCOPE;
5066	}
5067
5068	if (s[0] != 0) {
5069		if (strncmp(s, SCF_FMRI_SERVICE_PREFIX,
5070		    sizeof (SCF_FMRI_SERVICE_PREFIX) - 1) == 0)
5071			s += sizeof (SCF_FMRI_SERVICE_PREFIX) - 1;
5072
5073		/*
5074		 * Can't validate service here because it might not be null
5075		 * terminated.
5076		 */
5077		my_s = s;
5078	}
5079
5080	tpg = strstr(s, SCF_FMRI_PROPERTYGRP_PREFIX);
5081	te = strstr(s, SCF_FMRI_INSTANCE_PREFIX);
5082	if (te != NULL && (tpg == NULL || te < tpg)) {
5083		*te = 0;
5084		te += sizeof (SCF_FMRI_INSTANCE_PREFIX) - 1;
5085
5086		/* Can't validate instance here either. */
5087		my_i = s = te;
5088
5089		te = strstr(s, SCF_FMRI_PROPERTYGRP_PREFIX);
5090	} else {
5091		te = tpg;
5092	}
5093
5094	if (te != NULL) {
5095		*te = 0;
5096		te += sizeof (SCF_FMRI_PROPERTYGRP_PREFIX) - 1;
5097
5098		my_pg = s = te;
5099		te = strstr(s, SCF_FMRI_PROPERTY_PREFIX);
5100		if (te != NULL) {
5101			*te = 0;
5102			te += sizeof (SCF_FMRI_PROPERTY_PREFIX) - 1;
5103
5104			my_p = te;
5105			s = te;
5106		}
5107	}
5108
5109	if (my_s != NULL) {
5110		if (uu_check_name(my_s, UU_NAME_DOMAIN | UU_NAME_PATH) == -1)
5111			return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
5112
5113		if (service != NULL)
5114			*service = my_s;
5115	}
5116
5117	if (my_i != NULL) {
5118		if (uu_check_name(my_i, UU_NAME_DOMAIN) == -1)
5119			return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
5120
5121		if (instance != NULL)
5122			*instance = my_i;
5123	}
5124
5125	if (my_pg != NULL) {
5126		if (uu_check_name(my_pg, UU_NAME_DOMAIN) == -1)
5127			return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
5128
5129		if (propertygroup != NULL)
5130			*propertygroup = my_pg;
5131	}
5132
5133	if (my_p != NULL) {
5134		if (uu_check_name(my_p, UU_NAME_DOMAIN) == -1)
5135			return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
5136
5137		if (property != NULL)
5138			*property = my_p;
5139	}
5140
5141	return (0);
5142}
5143
5144int
5145scf_parse_file_fmri(char *fmri, const char **scope, const char **path)
5146{
5147	char *s, *e, *te;
5148
5149	if (scope != NULL)
5150		*scope = NULL;
5151
5152	s = fmri;
5153	e = strchr(s, '\0');
5154
5155	if (strncmp(s, SCF_FMRI_FILE_PREFIX,
5156	    sizeof (SCF_FMRI_FILE_PREFIX) - 1) == 0)
5157		s += sizeof (SCF_FMRI_FILE_PREFIX) - 1;
5158
5159	if (strncmp(s, SCF_FMRI_SCOPE_PREFIX,
5160	    sizeof (SCF_FMRI_SCOPE_PREFIX) - 1) == 0) {
5161		char *my_scope;
5162
5163		s += sizeof (SCF_FMRI_SCOPE_PREFIX) - 1;
5164		te = strstr(s, SCF_FMRI_SERVICE_PREFIX);
5165		if (te == NULL)
5166			te = e;
5167
5168		*te = 0;
5169		my_scope = s;
5170
5171		s = te;
5172
5173		/* Validate the scope. */
5174		if (my_scope[0] != '\0' &&
5175		    strcmp(my_scope, SCF_FMRI_LOCAL_SCOPE) != 0) {
5176			return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
5177		}
5178
5179		if (scope != NULL)
5180			*scope = my_scope;
5181	} else {
5182		/*
5183		 * FMRI paths must be absolute
5184		 */
5185		if (s[0] != '/')
5186			return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
5187	}
5188
5189	s += sizeof (SCF_FMRI_SERVICE_PREFIX) - 1;
5190
5191	if (s >= e)
5192		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
5193
5194	/*
5195	 * If the user requests it, return the full path of the file.
5196	 */
5197	if (path != NULL) {
5198		assert(s > fmri);
5199		s[-1] = '/';
5200		*path = s - 1;
5201	}
5202
5203	return (0);
5204}
5205
5206int
5207scf_parse_fmri(char *fmri, int *type, const char **scope, const char **service,
5208    const char **instance, const char **propertygroup, const char **property)
5209{
5210	if (strncmp(fmri, SCF_FMRI_SVC_PREFIX,
5211	    sizeof (SCF_FMRI_SVC_PREFIX) - 1) == 0) {
5212		if (type)
5213			*type = SCF_FMRI_TYPE_SVC;
5214		return (scf_parse_svc_fmri(fmri, scope, service, instance,
5215		    propertygroup, property));
5216	} else if (strncmp(fmri, SCF_FMRI_FILE_PREFIX,
5217	    sizeof (SCF_FMRI_FILE_PREFIX) - 1) == 0) {
5218		if (type)
5219			*type = SCF_FMRI_TYPE_FILE;
5220		return (scf_parse_file_fmri(fmri, scope, NULL));
5221	} else {
5222		/*
5223		 * Parse as a svc if the fmri type is not explicitly
5224		 * specified.
5225		 */
5226		if (type)
5227			*type = SCF_FMRI_TYPE_SVC;
5228		return (scf_parse_svc_fmri(fmri, scope, service, instance,
5229		    propertygroup, property));
5230	}
5231}
5232
5233/*
5234 * Fails with _INVALID_ARGUMENT.  fmri and buf may be equal.
5235 */
5236ssize_t
5237scf_canonify_fmri(const char *fmri, char *buf, size_t bufsz)
5238{
5239	const char *scope, *service, *instance, *pg, *property;
5240	char local[6 * REP_PROTOCOL_NAME_LEN];
5241	int r;
5242	size_t len;
5243
5244	if (strlcpy(local, fmri, sizeof (local)) >= sizeof (local)) {
5245		/* Should this be CONSTRAINT_VIOLATED? */
5246		(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
5247		return (-1);
5248	}
5249
5250
5251	r = scf_parse_svc_fmri(local, &scope, &service, &instance, &pg,
5252	    &property);
5253	if (r != 0)
5254		return (-1);
5255
5256	len = strlcpy(buf, "svc:/", bufsz);
5257
5258	if (scope != NULL && strcmp(scope, SCF_SCOPE_LOCAL) != 0) {
5259		len += strlcat(buf, "/", bufsz);
5260		len += strlcat(buf, scope, bufsz);
5261	}
5262
5263	if (service)
5264		len += strlcat(buf, service, bufsz);
5265
5266	if (instance) {
5267		len += strlcat(buf, ":", bufsz);
5268		len += strlcat(buf, instance, bufsz);
5269	}
5270
5271	if (pg) {
5272		len += strlcat(buf, "/:properties/", bufsz);
5273		len += strlcat(buf, pg, bufsz);
5274	}
5275
5276	if (property) {
5277		len += strlcat(buf, "/", bufsz);
5278		len += strlcat(buf, property, bufsz);
5279	}
5280
5281	return (len);
5282}
5283
5284/*
5285 * Fails with _HANDLE_MISMATCH, _INVALID_ARGUMENT, _CONSTRAINT_VIOLATED,
5286 * _NOT_FOUND, _NOT_BOUND, _CONNECTION_BROKEN, _INTERNAL, _NOT_SET, _DELETED,
5287 * _NO_RESOURCES, _BACKEND_ACCESS.
5288 */
5289int
5290scf_handle_decode_fmri(scf_handle_t *h, const char *fmri, scf_scope_t *sc,
5291    scf_service_t *svc, scf_instance_t *inst, scf_propertygroup_t *pg,
5292    scf_property_t *prop, int flags)
5293{
5294	const char *scope, *service, *instance, *propertygroup, *property;
5295	int last;
5296	char local[6 * REP_PROTOCOL_NAME_LEN];
5297	int ret;
5298	const uint32_t holds = RH_HOLD_SCOPE | RH_HOLD_SERVICE |
5299	    RH_HOLD_INSTANCE | RH_HOLD_PG | RH_HOLD_PROPERTY;
5300
5301	/*
5302	 * verify that all handles match
5303	 */
5304	if ((sc != NULL && h != sc->rd_d.rd_handle) ||
5305	    (svc != NULL && h != svc->rd_d.rd_handle) ||
5306	    (inst != NULL && h != inst->rd_d.rd_handle) ||
5307	    (pg != NULL && h != pg->rd_d.rd_handle) ||
5308	    (prop != NULL && h != prop->rd_d.rd_handle))
5309		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
5310
5311	if (strlcpy(local, fmri, sizeof (local)) >= sizeof (local)) {
5312		ret = scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
5313		goto reset_args;
5314	}
5315
5316	/*
5317	 * We can simply return from an error in parsing, because
5318	 * scf_parse_fmri sets the error code correctly.
5319	 */
5320	if (scf_parse_svc_fmri(local, &scope, &service, &instance,
5321	    &propertygroup, &property) == -1) {
5322		ret = -1;
5323		goto reset_args;
5324	}
5325
5326	/*
5327	 * the FMRI looks valid at this point -- do constraint checks.
5328	 */
5329
5330	if (instance != NULL && (flags & SCF_DECODE_FMRI_REQUIRE_NO_INSTANCE)) {
5331		ret = scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
5332		goto reset_args;
5333	}
5334	if (instance == NULL && (flags & SCF_DECODE_FMRI_REQUIRE_INSTANCE)) {
5335		ret = scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
5336		goto reset_args;
5337	}
5338
5339	if (prop != NULL)
5340		last = REP_PROTOCOL_ENTITY_PROPERTY;
5341	else if (pg != NULL)
5342		last = REP_PROTOCOL_ENTITY_PROPERTYGRP;
5343	else if (inst != NULL)
5344		last = REP_PROTOCOL_ENTITY_INSTANCE;
5345	else if (svc != NULL)
5346		last = REP_PROTOCOL_ENTITY_SERVICE;
5347	else if (sc != NULL)
5348		last = REP_PROTOCOL_ENTITY_SCOPE;
5349	else
5350		last = REP_PROTOCOL_ENTITY_NONE;
5351
5352	if (flags & SCF_DECODE_FMRI_EXACT) {
5353		int last_fmri;
5354
5355		if (property != NULL)
5356			last_fmri = REP_PROTOCOL_ENTITY_PROPERTY;
5357		else if (propertygroup != NULL)
5358			last_fmri = REP_PROTOCOL_ENTITY_PROPERTYGRP;
5359		else if (instance != NULL)
5360			last_fmri = REP_PROTOCOL_ENTITY_INSTANCE;
5361		else if (service != NULL)
5362			last_fmri = REP_PROTOCOL_ENTITY_SERVICE;
5363		else if (scope != NULL)
5364			last_fmri = REP_PROTOCOL_ENTITY_SCOPE;
5365		else
5366			last_fmri = REP_PROTOCOL_ENTITY_NONE;
5367
5368		if (last != last_fmri) {
5369			ret = scf_set_error(SCF_ERROR_CONSTRAINT_VIOLATED);
5370			goto reset_args;
5371		}
5372	}
5373
5374	if ((flags & SCF_DECODE_FMRI_TRUNCATE) &&
5375	    last == REP_PROTOCOL_ENTITY_NONE) {
5376		ret = 0;				/* nothing to do */
5377		goto reset_args;
5378	}
5379
5380	if (!(flags & SCF_DECODE_FMRI_TRUNCATE))
5381		last = REP_PROTOCOL_ENTITY_NONE;	/* never stop */
5382
5383	/*
5384	 * passed the constraint checks -- try to grab the thing itself.
5385	 */
5386
5387	handle_hold_subhandles(h, holds);
5388	if (sc == NULL)
5389		sc = h->rh_scope;
5390	else
5391		datael_reset(&sc->rd_d);
5392
5393	if (svc == NULL)
5394		svc = h->rh_service;
5395	else
5396		datael_reset(&svc->rd_d);
5397
5398	if (inst == NULL)
5399		inst = h->rh_instance;
5400	else
5401		datael_reset(&inst->rd_d);
5402
5403	if (pg == NULL)
5404		pg = h->rh_pg;
5405	else
5406		datael_reset(&pg->rd_d);
5407
5408	if (prop == NULL)
5409		prop = h->rh_property;
5410	else
5411		datael_reset(&prop->rd_d);
5412
5413	/*
5414	 * We only support local scopes, but we check *after* getting
5415	 * the local scope, so that any repository-related errors take
5416	 * precedence.
5417	 */
5418	if (scf_handle_get_scope(h, SCF_SCOPE_LOCAL, sc) == -1) {
5419		handle_rele_subhandles(h, holds);
5420		ret = -1;
5421		goto reset_args;
5422	}
5423
5424	if (scope != NULL && strcmp(scope, SCF_FMRI_LOCAL_SCOPE) != 0) {
5425		handle_rele_subhandles(h, holds);
5426		ret = scf_set_error(SCF_ERROR_NOT_FOUND);
5427		goto reset_args;
5428	}
5429
5430
5431	if (service == NULL || last == REP_PROTOCOL_ENTITY_SCOPE) {
5432		handle_rele_subhandles(h, holds);
5433		return (0);
5434	}
5435
5436	if (scf_scope_get_service(sc, service, svc) == -1) {
5437		handle_rele_subhandles(h, holds);
5438		ret = -1;
5439		assert(scf_error() != SCF_ERROR_NOT_SET);
5440		if (scf_error() == SCF_ERROR_DELETED)
5441			(void) scf_set_error(SCF_ERROR_NOT_FOUND);
5442		goto reset_args;
5443	}
5444
5445	if (last == REP_PROTOCOL_ENTITY_SERVICE) {
5446		handle_rele_subhandles(h, holds);
5447		return (0);
5448	}
5449
5450	if (instance == NULL) {
5451		if (propertygroup == NULL ||
5452		    last == REP_PROTOCOL_ENTITY_INSTANCE) {
5453			handle_rele_subhandles(h, holds);
5454			return (0);
5455		}
5456
5457		if (scf_service_get_pg(svc, propertygroup, pg) == -1) {
5458			handle_rele_subhandles(h, holds);
5459			ret = -1;
5460			assert(scf_error() != SCF_ERROR_NOT_SET);
5461			if (scf_error() == SCF_ERROR_DELETED)
5462				(void) scf_set_error(SCF_ERROR_NOT_FOUND);
5463			goto reset_args;
5464		}
5465	} else {
5466		if (scf_service_get_instance(svc, instance, inst) == -1) {
5467			handle_rele_subhandles(h, holds);
5468			ret = -1;
5469			assert(scf_error() != SCF_ERROR_NOT_SET);
5470			if (scf_error() == SCF_ERROR_DELETED)
5471				(void) scf_set_error(SCF_ERROR_NOT_FOUND);
5472			goto reset_args;
5473		}
5474
5475		if (propertygroup == NULL ||
5476		    last == REP_PROTOCOL_ENTITY_INSTANCE) {
5477			handle_rele_subhandles(h, holds);
5478			return (0);
5479		}
5480
5481		if (scf_instance_get_pg(inst, propertygroup, pg) == -1) {
5482			handle_rele_subhandles(h, holds);
5483			ret = -1;
5484			assert(scf_error() != SCF_ERROR_NOT_SET);
5485			if (scf_error() == SCF_ERROR_DELETED)
5486				(void) scf_set_error(SCF_ERROR_NOT_FOUND);
5487			goto reset_args;
5488		}
5489	}
5490
5491	if (property == NULL || last == REP_PROTOCOL_ENTITY_PROPERTYGRP) {
5492		handle_rele_subhandles(h, holds);
5493		return (0);
5494	}
5495
5496	if (scf_pg_get_property(pg, property, prop) == -1) {
5497		handle_rele_subhandles(h, holds);
5498		ret = -1;
5499		assert(scf_error() != SCF_ERROR_NOT_SET);
5500		if (scf_error() == SCF_ERROR_DELETED)
5501			(void) scf_set_error(SCF_ERROR_NOT_FOUND);
5502		goto reset_args;
5503	}
5504
5505	handle_rele_subhandles(h, holds);
5506	return (0);
5507
5508reset_args:
5509	if (sc != NULL)
5510		datael_reset(&sc->rd_d);
5511	if (svc != NULL)
5512		datael_reset(&svc->rd_d);
5513	if (inst != NULL)
5514		datael_reset(&inst->rd_d);
5515	if (pg != NULL)
5516		datael_reset(&pg->rd_d);
5517	if (prop != NULL)
5518		datael_reset(&prop->rd_d);
5519
5520	return (ret);
5521}
5522
5523/*
5524 * Fails with _NOT_BOUND, _CONNECTION_BROKEN, _INTERNAL (server response too
5525 * big, bad entity id, request not applicable to entity, name too long for
5526 * buffer), _NOT_SET, or _DELETED.
5527 */
5528ssize_t
5529scf_scope_to_fmri(const scf_scope_t *scope, char *out, size_t sz)
5530{
5531	ssize_t r, len;
5532
5533	char tmp[REP_PROTOCOL_NAME_LEN];
5534
5535	r = scf_scope_get_name(scope, tmp, sizeof (tmp));
5536
5537	if (r <= 0)
5538		return (r);
5539
5540	len = strlcpy(out, SCF_FMRI_SVC_PREFIX, sz);
5541	if (strcmp(tmp, SCF_FMRI_LOCAL_SCOPE) != 0) {
5542		if (len >= sz)
5543			return (len + r + sizeof (SCF_FMRI_SCOPE_SUFFIX) - 1);
5544
5545		len = strlcat(out, tmp, sz);
5546		if (len >= sz)
5547			return (len + sizeof (SCF_FMRI_SCOPE_SUFFIX) - 1);
5548		len = strlcat(out,
5549		    SCF_FMRI_SCOPE_SUFFIX SCF_FMRI_SERVICE_PREFIX, sz);
5550	}
5551
5552	return (len);
5553}
5554
5555/*
5556 * Fails with _NOT_BOUND, _CONNECTION_BROKEN, _INTERNAL (server response too
5557 * big, bad element id, bad ids, bad types, scope has no parent, request not
5558 * applicable to entity, name too long), _NOT_SET, _DELETED,
5559 */
5560ssize_t
5561scf_service_to_fmri(const scf_service_t *svc, char *out, size_t sz)
5562{
5563	scf_handle_t *h = svc->rd_d.rd_handle;
5564	scf_scope_t *scope = HANDLE_HOLD_SCOPE(h);
5565	ssize_t r, len;
5566
5567	char tmp[REP_PROTOCOL_NAME_LEN];
5568
5569	r = datael_get_parent(&svc->rd_d, &scope->rd_d);
5570	if (r != SCF_SUCCESS) {
5571		HANDLE_RELE_SCOPE(h);
5572
5573		assert(scf_error() != SCF_ERROR_HANDLE_MISMATCH);
5574		return (-1);
5575	}
5576	if (out != NULL && sz > 0)
5577		len = scf_scope_to_fmri(scope, out, sz);
5578	else
5579		len = scf_scope_to_fmri(scope, tmp, 2);
5580
5581	HANDLE_RELE_SCOPE(h);
5582
5583	if (len < 0)
5584		return (-1);
5585
5586	if (out == NULL || len >= sz)
5587		len += sizeof (SCF_FMRI_SERVICE_PREFIX) - 1;
5588	else
5589		len = strlcat(out, SCF_FMRI_SERVICE_PREFIX, sz);
5590
5591	r = scf_service_get_name(svc, tmp, sizeof (tmp));
5592	if (r < 0)
5593		return (r);
5594
5595	if (out == NULL || len >= sz)
5596		len += r;
5597	else
5598		len = strlcat(out, tmp, sz);
5599
5600	return (len);
5601}
5602
5603ssize_t
5604scf_instance_to_fmri(const scf_instance_t *inst, char *out, size_t sz)
5605{
5606	scf_handle_t *h = inst->rd_d.rd_handle;
5607	scf_service_t *svc = HANDLE_HOLD_SERVICE(h);
5608	ssize_t r, len;
5609
5610	char tmp[REP_PROTOCOL_NAME_LEN];
5611
5612	r = datael_get_parent(&inst->rd_d, &svc->rd_d);
5613	if (r != SCF_SUCCESS) {
5614		HANDLE_RELE_SERVICE(h);
5615		return (-1);
5616	}
5617
5618	len = scf_service_to_fmri(svc, out, sz);
5619
5620	HANDLE_RELE_SERVICE(h);
5621
5622	if (len < 0)
5623		return (len);
5624
5625	if (len >= sz)
5626		len += sizeof (SCF_FMRI_INSTANCE_PREFIX) - 1;
5627	else
5628		len = strlcat(out, SCF_FMRI_INSTANCE_PREFIX, sz);
5629
5630	r = scf_instance_get_name(inst, tmp, sizeof (tmp));
5631	if (r < 0)
5632		return (r);
5633
5634	if (len >= sz)
5635		len += r;
5636	else
5637		len = strlcat(out, tmp, sz);
5638
5639	return (len);
5640}
5641
5642ssize_t
5643scf_pg_to_fmri(const scf_propertygroup_t *pg, char *out, size_t sz)
5644{
5645	scf_handle_t *h = pg->rd_d.rd_handle;
5646
5647	struct rep_protocol_entity_parent_type request;
5648	struct rep_protocol_integer_response response;
5649
5650	char tmp[REP_PROTOCOL_NAME_LEN];
5651	ssize_t len, r;
5652
5653	(void) pthread_mutex_lock(&h->rh_lock);
5654	request.rpr_request = REP_PROTOCOL_ENTITY_PARENT_TYPE;
5655	request.rpr_entityid = pg->rd_d.rd_entity;
5656
5657	datael_finish_reset(&pg->rd_d);
5658	r = make_door_call(h, &request, sizeof (request),
5659	    &response, sizeof (response));
5660	(void) pthread_mutex_unlock(&h->rh_lock);
5661
5662	if (r < 0)
5663		DOOR_ERRORS_BLOCK(r);
5664
5665	if (response.rpr_response != REP_PROTOCOL_SUCCESS ||
5666	    r < sizeof (response)) {
5667		return (scf_set_error(proto_error(response.rpr_response)));
5668	}
5669
5670	switch (response.rpr_value) {
5671	case REP_PROTOCOL_ENTITY_SERVICE: {
5672		scf_service_t *svc;
5673
5674		svc = HANDLE_HOLD_SERVICE(h);
5675
5676		r = datael_get_parent(&pg->rd_d, &svc->rd_d);
5677
5678		if (r == SCF_SUCCESS)
5679			len = scf_service_to_fmri(svc, out, sz);
5680
5681		HANDLE_RELE_SERVICE(h);
5682		break;
5683	}
5684
5685	case REP_PROTOCOL_ENTITY_INSTANCE: {
5686		scf_instance_t *inst;
5687
5688		inst = HANDLE_HOLD_INSTANCE(h);
5689
5690		r = datael_get_parent(&pg->rd_d, &inst->rd_d);
5691
5692		if (r == SCF_SUCCESS)
5693			len = scf_instance_to_fmri(inst, out, sz);
5694
5695		HANDLE_RELE_INSTANCE(h);
5696		break;
5697	}
5698
5699	case REP_PROTOCOL_ENTITY_SNAPLEVEL: {
5700		scf_instance_t *inst = HANDLE_HOLD_INSTANCE(h);
5701		scf_snapshot_t *snap = HANDLE_HOLD_SNAPSHOT(h);
5702		scf_snaplevel_t *level = HANDLE_HOLD_SNAPLVL(h);
5703
5704		r = datael_get_parent(&pg->rd_d, &level->rd_d);
5705
5706		if (r == SCF_SUCCESS)
5707			r = datael_get_parent(&level->rd_d, &snap->rd_d);
5708
5709		if (r == SCF_SUCCESS)
5710			r = datael_get_parent(&snap->rd_d, &inst->rd_d);
5711
5712		if (r == SCF_SUCCESS)
5713			len = scf_instance_to_fmri(inst, out, sz);
5714
5715		HANDLE_RELE_INSTANCE(h);
5716		HANDLE_RELE_SNAPSHOT(h);
5717		HANDLE_RELE_SNAPLVL(h);
5718		break;
5719	}
5720
5721	default:
5722		return (scf_set_error(SCF_ERROR_INTERNAL));
5723	}
5724
5725	if (r != SCF_SUCCESS)
5726		return (r);
5727
5728	if (len >= sz)
5729		len += sizeof (SCF_FMRI_PROPERTYGRP_PREFIX) - 1;
5730	else
5731		len = strlcat(out, SCF_FMRI_PROPERTYGRP_PREFIX, sz);
5732
5733	r = scf_pg_get_name(pg, tmp, sizeof (tmp));
5734
5735	if (r < 0)
5736		return (r);
5737
5738	if (len >= sz)
5739		len += r;
5740	else
5741		len = strlcat(out, tmp, sz);
5742
5743	return (len);
5744}
5745
5746ssize_t
5747scf_property_to_fmri(const scf_property_t *prop, char *out, size_t sz)
5748{
5749	scf_handle_t *h = prop->rd_d.rd_handle;
5750	scf_propertygroup_t *pg = HANDLE_HOLD_PG(h);
5751
5752	char tmp[REP_PROTOCOL_NAME_LEN];
5753	ssize_t len;
5754	int r;
5755
5756	r = datael_get_parent(&prop->rd_d, &pg->rd_d);
5757	if (r != SCF_SUCCESS) {
5758		HANDLE_RELE_PG(h);
5759		return (-1);
5760	}
5761
5762	len = scf_pg_to_fmri(pg, out, sz);
5763
5764	HANDLE_RELE_PG(h);
5765
5766	if (len >= sz)
5767		len += sizeof (SCF_FMRI_PROPERTY_PREFIX) - 1;
5768	else
5769		len = strlcat(out, SCF_FMRI_PROPERTY_PREFIX, sz);
5770
5771	r = scf_property_get_name(prop, tmp, sizeof (tmp));
5772
5773	if (r < 0)
5774		return (r);
5775
5776	if (len >= sz)
5777		len += r;
5778	else
5779		len = strlcat(out, tmp, sz);
5780
5781	return (len);
5782}
5783
5784/*
5785 * Fails with _HANDLE_MISMATCH, _NOT_BOUND, _CONNECTION_BROKEN, _INTERNAL
5786 * (server response too big, bad entity id, request not applicable to entity,
5787 * name too long for buffer, bad element id, iter already exists, element
5788 * cannot have children of type, type is invalid, iter was reset, sequence
5789 * was bad, iter walks values, iter does not walk type entities),
5790 * _NOT_SET, _DELETED, or _CONSTRAINT_VIOLATED,
5791 * _NOT_FOUND (scope has no parent),  _INVALID_ARGUMENT, _NO_RESOURCES,
5792 * _BACKEND_ACCESS.
5793 */
5794int
5795scf_pg_get_underlying_pg(const scf_propertygroup_t *pg,
5796    scf_propertygroup_t *out)
5797{
5798	scf_handle_t *h = pg->rd_d.rd_handle;
5799	scf_service_t *svc;
5800	scf_instance_t *inst;
5801
5802	char me[REP_PROTOCOL_NAME_LEN];
5803	int r;
5804
5805	if (h != out->rd_d.rd_handle)
5806		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
5807
5808	r = scf_pg_get_name(pg, me, sizeof (me));
5809
5810	if (r < 0)
5811		return (r);
5812
5813	svc = HANDLE_HOLD_SERVICE(h);
5814	inst = HANDLE_HOLD_INSTANCE(h);
5815
5816	r = datael_get_parent(&pg->rd_d, &inst->rd_d);
5817
5818	if (r == SCF_SUCCESS) {
5819		r = datael_get_parent(&inst->rd_d, &svc->rd_d);
5820		if (r != SCF_SUCCESS) {
5821			goto out;
5822		}
5823		r = scf_service_get_pg(svc, me, out);
5824	} else {
5825		r = scf_set_error(SCF_ERROR_NOT_FOUND);
5826	}
5827
5828out:
5829	HANDLE_RELE_SERVICE(h);
5830	HANDLE_RELE_INSTANCE(h);
5831	return (r);
5832}
5833
5834#define	LEGACY_SCHEME	"lrc:"
5835#define	LEGACY_UNKNOWN	"unknown"
5836
5837/*
5838 * Implementation of scf_walk_fmri()
5839 *
5840 * This is a little tricky due to the many-to-many relationship between patterns
5841 * and matches.  We need to be able to satisfy the following requirements:
5842 *
5843 * 	1) Detect patterns which match more than one FMRI, and be able to
5844 *         report which FMRIs have been matched.
5845 * 	2) Detect patterns which have not matched any FMRIs
5846 * 	3) Visit each matching FMRI exactly once across all patterns
5847 * 	4) Ignore FMRIs which have only been matched due to multiply-matching
5848 *         patterns.
5849 *
5850 * We maintain an array of scf_pattern_t structures, one for each argument, and
5851 * maintain a linked list of scf_match_t structures for each one.  We first
5852 * qualify each pattern's type:
5853 *
5854 *	PATTERN_INVALID		The argument is invalid (too long).
5855 *
5856 *	PATTERN_EXACT		The pattern is a complete FMRI.  The list of
5857 *				matches contains only a single entry.
5858 *
5859 * 	PATTERN_GLOB		The pattern will be matched against all
5860 * 				FMRIs via fnmatch() in the second phase.
5861 * 				Matches will be added to the pattern's list
5862 * 				as they are found.
5863 *
5864 * 	PATTERN_PARTIAL		Everything else.  We will assume that this is
5865 * 				an abbreviated FMRI, and match according to
5866 * 				our abbreviated FMRI rules.  Matches will be
5867 * 				added to the pattern's list as they are found.
5868 *
5869 * The first pass searches for arguments that are complete FMRIs.  These are
5870 * classified as EXACT patterns and do not necessitate searching the entire
5871 * tree.
5872 *
5873 * Once this is done, if we have any GLOB or PARTIAL patterns (or if no
5874 * arguments were given), we iterate over all services and instances in the
5875 * repository, looking for matches.
5876 *
5877 * When a match is found, we add the match to the pattern's list.  We also enter
5878 * the match into a hash table, resulting in something like this:
5879 *
5880 *       scf_pattern_t       scf_match_t
5881 *     +---------------+      +-------+     +-------+
5882 *     | pattern 'foo' |----->| match |---->| match |
5883 *     +---------------+      +-------+     +-------+
5884 *                                |             |
5885 *           scf_match_key_t      |             |
5886 *           +--------------+     |             |
5887 *           | FMRI bar/foo |<----+             |
5888 *           +--------------+                   |
5889 *           | FMRI baz/foo |<------------------+
5890 *           +--------------+
5891 *
5892 * Once we have all of this set up, we do one pass to report patterns matching
5893 * multiple FMRIs (if SCF_WALK_MULTIPLE is not set) and patterns for which no
5894 * match was found.
5895 *
5896 * Finally, we walk through all valid patterns, and for each match, if we
5897 * haven't already seen the match (as recorded in the hash table), then we
5898 * execute the callback.
5899 */
5900
5901struct scf_matchkey;
5902struct scf_match;
5903
5904/*
5905 * scf_matchkey_t
5906 */
5907typedef struct scf_matchkey {
5908	char			*sk_fmri;	/* Matching FMRI */
5909	char			*sk_legacy;	/* Legacy name */
5910	int			sk_seen;	/* If we've been seen */
5911	struct scf_matchkey	*sk_next;	/* Next in hash chain */
5912} scf_matchkey_t;
5913
5914/*
5915 * scf_match_t
5916 */
5917typedef struct scf_match {
5918	scf_matchkey_t		*sm_key;
5919	struct scf_match	*sm_next;
5920} scf_match_t;
5921
5922#define	WALK_HTABLE_SIZE	123
5923
5924/*
5925 * scf_get_key()
5926 *
5927 * Given an FMRI and a hash table, returns the scf_matchkey_t corresponding to
5928 * this FMRI.  If the FMRI does not exist, it is added to the hash table.  If a
5929 * new entry cannot be allocated due to lack of memory, NULL is returned.
5930 */
5931static scf_matchkey_t *
5932scf_get_key(scf_matchkey_t **htable, const char *fmri, const char *legacy)
5933{
5934	uint_t h = 0, g;
5935	const char *p, *k;
5936	scf_matchkey_t *key;
5937
5938	k = strstr(fmri, ":/");
5939	assert(k != NULL);
5940	k += 2;
5941
5942	/*
5943	 * Generic hash function from uts/common/os/modhash.c.
5944	 */
5945	for (p = k; *p != '\0'; ++p) {
5946		h = (h << 4) + *p;
5947		if ((g = (h & 0xf0000000)) != 0) {
5948			h ^= (g >> 24);
5949			h ^= g;
5950		}
5951	}
5952
5953	h %= WALK_HTABLE_SIZE;
5954
5955	/*
5956	 * Search for an existing key
5957	 */
5958	for (key = htable[h]; key != NULL; key = key->sk_next) {
5959		if (strcmp(key->sk_fmri, fmri) == 0)
5960			return (key);
5961	}
5962
5963	if ((key = calloc(sizeof (scf_matchkey_t), 1)) == NULL)
5964		return (NULL);
5965
5966	/*
5967	 * Add new key to hash table.
5968	 */
5969	if ((key->sk_fmri = strdup(fmri)) == NULL) {
5970		free(key);
5971		return (NULL);
5972	}
5973
5974	if (legacy == NULL) {
5975		key->sk_legacy = NULL;
5976	} else if ((key->sk_legacy = strdup(legacy)) == NULL) {
5977		free(key->sk_fmri);
5978		free(key);
5979		return (NULL);
5980	}
5981
5982	key->sk_next = htable[h];
5983	htable[h] = key;
5984
5985	return (key);
5986}
5987
5988/*
5989 * Given an FMRI, insert it into the pattern's list appropriately.
5990 * svc_explicit indicates whether matching services should take
5991 * precedence over matching instances.
5992 */
5993static scf_error_t
5994scf_add_match(scf_matchkey_t **htable, const char *fmri, const char *legacy,
5995    scf_pattern_t *pattern, int svc_explicit)
5996{
5997	scf_match_t *match;
5998
5999	/*
6000	 * If svc_explicit is set, enforce the constaint that matching
6001	 * instances take precedence over matching services. Otherwise,
6002	 * matching services take precedence over matching instances.
6003	 */
6004	if (svc_explicit) {
6005		scf_match_t *next, *prev;
6006		/*
6007		 * If we match an instance, check to see if we must remove
6008		 * any matching services (for SCF_WALK_EXPLICIT).
6009		 */
6010		for (prev = match = pattern->sp_matches; match != NULL;
6011		    match = next) {
6012			size_t len = strlen(match->sm_key->sk_fmri);
6013			next = match->sm_next;
6014			if (strncmp(match->sm_key->sk_fmri, fmri, len) == 0 &&
6015			    fmri[len] == ':') {
6016				if (prev == match)
6017					pattern->sp_matches = match->sm_next;
6018				else
6019					prev->sm_next = match->sm_next;
6020				pattern->sp_matchcount--;
6021				free(match);
6022			} else
6023				prev = match;
6024		}
6025	} else {
6026		/*
6027		 * If we've matched a service don't add any instances (for
6028		 * SCF_WALK_SERVICE).
6029		 */
6030		for (match = pattern->sp_matches; match != NULL;
6031		    match = match->sm_next) {
6032			size_t len = strlen(match->sm_key->sk_fmri);
6033			if (strncmp(match->sm_key->sk_fmri, fmri, len) == 0 &&
6034			    fmri[len] == ':')
6035				return (0);
6036		}
6037	}
6038
6039	if ((match = malloc(sizeof (scf_match_t))) == NULL)
6040		return (SCF_ERROR_NO_MEMORY);
6041
6042	if ((match->sm_key = scf_get_key(htable, fmri, legacy)) == NULL) {
6043		free(match);
6044		return (SCF_ERROR_NO_MEMORY);
6045	}
6046
6047	match->sm_next = pattern->sp_matches;
6048	pattern->sp_matches = match;
6049	pattern->sp_matchcount++;
6050
6051	return (0);
6052}
6053
6054/*
6055 * Returns 1 if the fmri matches the given pattern, 0 otherwise.
6056 */
6057int
6058scf_cmp_pattern(char *fmri, scf_pattern_t *pattern)
6059{
6060	char *tmp;
6061
6062	if (pattern->sp_type == PATTERN_GLOB) {
6063		if (fnmatch(pattern->sp_arg, fmri, 0) == 0)
6064			return (1);
6065	} else if (pattern->sp_type == PATTERN_PARTIAL &&
6066	    (tmp = strstr(fmri, pattern->sp_arg)) != NULL) {
6067		/*
6068		 * We only allow partial matches anchored on the end of
6069		 * a service or instance, and beginning on an element
6070		 * boundary.
6071		 */
6072		if (tmp != fmri && tmp[-1] != '/' && tmp[-1] != ':' &&
6073		    tmp[0] != ':')
6074			return (0);
6075		tmp += strlen(pattern->sp_arg);
6076		if (tmp != fmri + strlen(fmri) && tmp[0] != ':' &&
6077		    tmp[-1] != ':')
6078			return (0);
6079
6080		/*
6081		 * If the user has supplied a short pattern that matches
6082		 * 'svc:/' or 'lrc:/', ignore it.
6083		 */
6084		if (tmp <= fmri + 4)
6085			return (0);
6086
6087		return (1);
6088	}
6089
6090	return (0);
6091}
6092
6093/*
6094 * Attempts to match the given FMRI against a set of patterns, keeping track of
6095 * the results.
6096 */
6097static scf_error_t
6098scf_pattern_match(scf_matchkey_t **htable, char *fmri, const char *legacy,
6099    int npattern, scf_pattern_t *pattern, int svc_explicit)
6100{
6101	int i;
6102	int ret = 0;
6103
6104	for (i = 0; i < npattern; i++) {
6105		if (scf_cmp_pattern(fmri, &pattern[i]) &&
6106		    (ret = scf_add_match(htable, fmri,
6107		    legacy, &pattern[i], svc_explicit)) != 0)
6108			return (ret);
6109	}
6110
6111	return (0);
6112}
6113
6114/*
6115 * Fails with _INVALID_ARGUMENT, _HANDLE_DESTROYED, _INTERNAL (bad server
6116 * response or id in use), _NO_MEMORY, _HANDLE_MISMATCH, _CONSTRAINT_VIOLATED,
6117 * _NOT_FOUND, _NOT_BOUND, _CONNECTION_BROKEN, _NOT_SET, _DELETED,
6118 * _NO_RESOURCES, _BACKEND_ACCESS, _TYPE_MISMATCH.
6119 */
6120scf_error_t
6121scf_walk_fmri(scf_handle_t *h, int argc, char **argv, int flags,
6122    scf_walk_callback callback, void *data, int *err,
6123    void (*errfunc)(const char *, ...))
6124{
6125	scf_pattern_t *pattern = NULL;
6126	int i;
6127	char *fmri = NULL;
6128	ssize_t max_fmri_length;
6129	scf_service_t *svc = NULL;
6130	scf_instance_t *inst = NULL;
6131	scf_iter_t *iter = NULL, *sciter = NULL, *siter = NULL;
6132	scf_scope_t *scope = NULL;
6133	scf_propertygroup_t *pg = NULL;
6134	scf_property_t *prop = NULL;
6135	scf_value_t *value = NULL;
6136	int ret = 0;
6137	scf_matchkey_t **htable = NULL;
6138	int pattern_search = 0;
6139	ssize_t max_name_length;
6140	char *pgname = NULL;
6141	scf_walkinfo_t info;
6142
6143#ifndef NDEBUG
6144	if (flags & SCF_WALK_EXPLICIT)
6145		assert(flags & SCF_WALK_SERVICE);
6146	if (flags & SCF_WALK_NOINSTANCE)
6147		assert(flags & SCF_WALK_SERVICE);
6148	if (flags & SCF_WALK_PROPERTY)
6149		assert(!(flags & SCF_WALK_LEGACY));
6150#endif
6151
6152	/*
6153	 * Setup initial variables
6154	 */
6155	if ((max_fmri_length = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH)) == -1 ||
6156	    (max_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH)) == -1)
6157		return (SCF_ERROR_INTERNAL);
6158
6159	if ((fmri = malloc(max_fmri_length + 1)) == NULL ||
6160	    (pgname = malloc(max_name_length + 1)) == NULL) {
6161		ret = SCF_ERROR_NO_MEMORY;
6162		goto error;
6163	}
6164
6165	if (argc == 0) {
6166		pattern = NULL;
6167	} else if ((pattern = calloc(argc, sizeof (scf_pattern_t)))
6168	    == NULL) {
6169		ret = SCF_ERROR_NO_MEMORY;
6170		goto error;
6171	}
6172
6173	if ((htable = calloc(WALK_HTABLE_SIZE, sizeof (void *))) == NULL) {
6174		ret = SCF_ERROR_NO_MEMORY;
6175		goto error;
6176	}
6177
6178	if ((inst = scf_instance_create(h)) == NULL ||
6179	    (svc = scf_service_create(h)) == NULL ||
6180	    (iter = scf_iter_create(h)) == NULL ||
6181	    (sciter = scf_iter_create(h)) == NULL ||
6182	    (siter = scf_iter_create(h)) == NULL ||
6183	    (scope = scf_scope_create(h)) == NULL ||
6184	    (pg = scf_pg_create(h)) == NULL ||
6185	    (prop = scf_property_create(h)) == NULL ||
6186	    (value = scf_value_create(h)) == NULL) {
6187		ret = scf_error();
6188		goto error;
6189	}
6190
6191	/*
6192	 * For each fmri given, we first check to see if it's a full service,
6193	 * instance, property group, or property FMRI.  This avoids having to do
6194	 * the (rather expensive) walk of all instances.  Any element which does
6195	 * not match a full fmri is identified as a globbed pattern or a partial
6196	 * fmri and stored in a private array when walking instances.
6197	 */
6198	for (i = 0; i < argc; i++) {
6199		const char *scope_name, *svc_name, *inst_name, *pg_name;
6200		const char *prop_name;
6201
6202		if (strlen(argv[i]) > max_fmri_length) {
6203			errfunc(scf_get_msg(SCF_MSG_ARGTOOLONG), argv[i]);
6204			if (err != NULL)
6205				*err = UU_EXIT_FATAL;
6206			continue;
6207		}
6208
6209		(void) strcpy(fmri, argv[i]);
6210		if (scf_parse_svc_fmri(fmri, &scope_name, &svc_name, &inst_name,
6211		    &pg_name, &prop_name) != SCF_SUCCESS)
6212			goto badfmri;
6213
6214		/*
6215		 * If the user has specified SCF_WALK_PROPERTY, allow property
6216		 * groups and properties.
6217		 */
6218		if (pg_name != NULL || prop_name != NULL) {
6219			if (!(flags & SCF_WALK_PROPERTY))
6220				goto badfmri;
6221
6222			if (scf_handle_decode_fmri(h, argv[i], NULL, NULL,
6223			    NULL, pg, prop, 0) != 0)
6224				goto badfmri;
6225
6226			if (scf_pg_get_name(pg, NULL, 0) < 0 &&
6227			    scf_property_get_name(prop, NULL, 0) < 0)
6228				goto badfmri;
6229
6230			if (scf_canonify_fmri(argv[i], fmri, max_fmri_length)
6231			    <= 0) {
6232				/*
6233				 * scf_parse_fmri() should have caught this.
6234				 */
6235				abort();
6236			}
6237
6238			if ((ret = scf_add_match(htable, fmri, NULL,
6239			    &pattern[i], flags & SCF_WALK_EXPLICIT)) != 0)
6240				goto error;
6241
6242			if ((pattern[i].sp_arg = strdup(argv[i])) == NULL) {
6243				ret = SCF_ERROR_NO_MEMORY;
6244				goto error;
6245			}
6246			pattern[i].sp_type = PATTERN_EXACT;
6247		}
6248
6249		/*
6250		 * We need at least a service name
6251		 */
6252		if (scope_name == NULL || svc_name == NULL)
6253			goto badfmri;
6254
6255		/*
6256		 * If we have a fully qualified instance, add it to our list of
6257		 * fmris to watch.
6258		 */
6259		if (inst_name != NULL) {
6260			if (flags & SCF_WALK_NOINSTANCE)
6261				goto badfmri;
6262
6263			if (scf_handle_decode_fmri(h, argv[i], NULL, NULL,
6264			    inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) != 0)
6265				goto badfmri;
6266
6267			if (scf_canonify_fmri(argv[i], fmri, max_fmri_length)
6268			    <= 0)
6269				goto badfmri;
6270
6271			if ((ret = scf_add_match(htable, fmri, NULL,
6272			    &pattern[i], flags & SCF_WALK_EXPLICIT)) != 0)
6273				goto error;
6274
6275			if ((pattern[i].sp_arg = strdup(argv[i])) == NULL) {
6276				ret = SCF_ERROR_NO_MEMORY;
6277				goto error;
6278			}
6279			pattern[i].sp_type = PATTERN_EXACT;
6280
6281			continue;
6282		}
6283
6284		if (scf_handle_decode_fmri(h, argv[i], NULL, svc,
6285		    NULL, NULL, NULL, SCF_DECODE_FMRI_EXACT) !=
6286		    SCF_SUCCESS)
6287			goto badfmri;
6288
6289		/*
6290		 * If the user allows for bare services, then simply
6291		 * pass this service on.
6292		 */
6293		if (flags & SCF_WALK_SERVICE) {
6294			if (scf_service_to_fmri(svc, fmri,
6295			    max_fmri_length + 1) <= 0) {
6296				ret = scf_error();
6297				goto error;
6298			}
6299
6300			if ((ret = scf_add_match(htable, fmri, NULL,
6301			    &pattern[i], flags & SCF_WALK_EXPLICIT)) != 0)
6302				goto error;
6303
6304			if ((pattern[i].sp_arg = strdup(argv[i]))
6305			    == NULL) {
6306				ret = SCF_ERROR_NO_MEMORY;
6307				goto error;
6308			}
6309			pattern[i].sp_type = PATTERN_EXACT;
6310			continue;
6311		}
6312
6313		if (flags & SCF_WALK_NOINSTANCE)
6314			goto badfmri;
6315
6316		/*
6317		 * Otherwise, iterate over all instances in the service.
6318		 */
6319		if (scf_iter_service_instances(iter, svc) !=
6320		    SCF_SUCCESS) {
6321			ret = scf_error();
6322			goto error;
6323		}
6324
6325		for (;;) {
6326			ret = scf_iter_next_instance(iter, inst);
6327			if (ret == 0)
6328				break;
6329			if (ret != 1) {
6330				ret = scf_error();
6331				goto error;
6332			}
6333
6334			if (scf_instance_to_fmri(inst, fmri,
6335			    max_fmri_length + 1) == -1)
6336				goto badfmri;
6337
6338			if ((ret = scf_add_match(htable, fmri, NULL,
6339			    &pattern[i], flags & SCF_WALK_EXPLICIT)) != 0)
6340				goto error;
6341		}
6342
6343		if ((pattern[i].sp_arg = strdup(argv[i])) == NULL) {
6344			ret = SCF_ERROR_NO_MEMORY;
6345			goto error;
6346		}
6347		pattern[i].sp_type = PATTERN_EXACT;
6348
6349		continue;
6350
6351badfmri:
6352
6353		/*
6354		 * If we got here because of a fatal error, bail out
6355		 * immediately.
6356		 */
6357		if (scf_error() == SCF_ERROR_CONNECTION_BROKEN) {
6358			ret = scf_error();
6359			goto error;
6360		}
6361
6362		/*
6363		 * At this point we failed to interpret the argument as a
6364		 * complete fmri, so mark it as a partial or globbed FMRI for
6365		 * later processing.
6366		 */
6367		if (strpbrk(argv[i], "*?[") != NULL) {
6368			/*
6369			 * Prepend svc:/ to patterns which don't begin with * or
6370			 * svc: or lrc:.
6371			 */
6372			pattern[i].sp_type = PATTERN_GLOB;
6373			if (argv[i][0] == '*' ||
6374			    (strlen(argv[i]) >= 4 && argv[i][3] == ':'))
6375				pattern[i].sp_arg = strdup(argv[i]);
6376			else {
6377				pattern[i].sp_arg = malloc(strlen(argv[i]) + 6);
6378				if (pattern[i].sp_arg != NULL)
6379					(void) snprintf(pattern[i].sp_arg,
6380					    strlen(argv[i]) + 6, "svc:/%s",
6381					    argv[i]);
6382			}
6383		} else {
6384			pattern[i].sp_type = PATTERN_PARTIAL;
6385			pattern[i].sp_arg = strdup(argv[i]);
6386		}
6387		pattern_search = 1;
6388		if (pattern[i].sp_arg == NULL) {
6389			ret = SCF_ERROR_NO_MEMORY;
6390			goto error;
6391		}
6392	}
6393
6394	if (pattern_search || argc == 0) {
6395		/*
6396		 * We have a set of patterns to search for.  Iterate over all
6397		 * instances and legacy services searching for matches.
6398		 */
6399		if (scf_handle_get_local_scope(h, scope) != 0) {
6400			ret = scf_error();
6401			goto error;
6402		}
6403
6404		if (scf_iter_scope_services(sciter, scope) != 0) {
6405			ret = scf_error();
6406			goto error;
6407		}
6408
6409		for (;;) {
6410			ret = scf_iter_next_service(sciter, svc);
6411			if (ret == 0)
6412				break;
6413			if (ret != 1) {
6414				ret = scf_error();
6415				goto error;
6416			}
6417
6418			if (flags & SCF_WALK_SERVICE) {
6419				/*
6420				 * If the user is requesting bare services, try
6421				 * to match the service first.
6422				 */
6423				if (scf_service_to_fmri(svc, fmri,
6424				    max_fmri_length + 1) < 0) {
6425					ret = scf_error();
6426					goto error;
6427				}
6428
6429				if (argc == 0) {
6430					info.fmri = fmri;
6431					info.scope = scope;
6432					info.svc = svc;
6433					info.inst = NULL;
6434					info.pg = NULL;
6435					info.prop = NULL;
6436					if ((ret = callback(data, &info)) != 0)
6437						goto error;
6438					continue;
6439				} else if ((ret = scf_pattern_match(htable,
6440				    fmri, NULL, argc, pattern,
6441				    flags & SCF_WALK_EXPLICIT)) != 0) {
6442					goto error;
6443				}
6444			}
6445
6446			if (flags & SCF_WALK_NOINSTANCE)
6447				continue;
6448
6449			/*
6450			 * Iterate over all instances in the service.
6451			 */
6452			if (scf_iter_service_instances(siter, svc) != 0) {
6453				if (scf_error() != SCF_ERROR_DELETED) {
6454					ret = scf_error();
6455					goto error;
6456				}
6457				continue;
6458			}
6459
6460			for (;;) {
6461				ret = scf_iter_next_instance(siter, inst);
6462				if (ret == 0)
6463					break;
6464				if (ret != 1) {
6465					if (scf_error() != SCF_ERROR_DELETED) {
6466						ret = scf_error();
6467						goto error;
6468					}
6469					break;
6470				}
6471
6472				if (scf_instance_to_fmri(inst, fmri,
6473				    max_fmri_length + 1) < 0) {
6474					ret = scf_error();
6475					goto error;
6476				}
6477
6478				/*
6479				 * Without arguments, execute the callback
6480				 * immediately.
6481				 */
6482				if (argc == 0) {
6483					info.fmri = fmri;
6484					info.scope = scope;
6485					info.svc = svc;
6486					info.inst = inst;
6487					info.pg = NULL;
6488					info.prop = NULL;
6489					if ((ret = callback(data, &info)) != 0)
6490						goto error;
6491				} else if ((ret = scf_pattern_match(htable,
6492				    fmri, NULL, argc, pattern,
6493				    flags & SCF_WALK_EXPLICIT)) != 0) {
6494					goto error;
6495				}
6496			}
6497		}
6498
6499		/*
6500		 * Search legacy services
6501		 */
6502		if ((flags & SCF_WALK_LEGACY)) {
6503			if (scf_scope_get_service(scope, SCF_LEGACY_SERVICE,
6504			    svc) != 0) {
6505				if (scf_error() != SCF_ERROR_NOT_FOUND) {
6506					ret = scf_error();
6507					goto error;
6508				}
6509
6510				goto nolegacy;
6511			}
6512
6513			if (scf_iter_service_pgs_typed(iter, svc,
6514			    SCF_GROUP_FRAMEWORK) != SCF_SUCCESS) {
6515				ret = scf_error();
6516				goto error;
6517			}
6518
6519			(void) strcpy(fmri, LEGACY_SCHEME);
6520
6521			for (;;) {
6522				ret = scf_iter_next_pg(iter, pg);
6523				if (ret == -1) {
6524					ret = scf_error();
6525					goto error;
6526				}
6527				if (ret == 0)
6528					break;
6529
6530				if (scf_pg_get_property(pg,
6531				    SCF_LEGACY_PROPERTY_NAME, prop) == -1) {
6532					ret = scf_error();
6533					if (ret == SCF_ERROR_DELETED ||
6534					    ret == SCF_ERROR_NOT_FOUND) {
6535						ret = 0;
6536						continue;
6537					}
6538					goto error;
6539				}
6540
6541				if (scf_property_is_type(prop, SCF_TYPE_ASTRING)
6542				    != SCF_SUCCESS) {
6543					if (scf_error() == SCF_ERROR_DELETED)
6544						continue;
6545					ret = scf_error();
6546					goto error;
6547				}
6548
6549				if (scf_property_get_value(prop, value) !=
6550				    SCF_SUCCESS)
6551					continue;
6552
6553				if (scf_value_get_astring(value,
6554				    fmri + sizeof (LEGACY_SCHEME) - 1,
6555				    max_fmri_length + 2 -
6556				    sizeof (LEGACY_SCHEME)) <= 0)
6557					continue;
6558
6559				if (scf_pg_get_name(pg, pgname,
6560				    max_name_length + 1) <= 0) {
6561					if (scf_error() == SCF_ERROR_DELETED)
6562						continue;
6563					ret = scf_error();
6564					goto error;
6565				}
6566
6567				if (argc == 0) {
6568					info.fmri = fmri;
6569					info.scope = scope;
6570					info.svc = NULL;
6571					info.inst = NULL;
6572					info.pg = pg;
6573					info.prop = NULL;
6574					if ((ret = callback(data, &info)) != 0)
6575						goto error;
6576				} else if ((ret = scf_pattern_match(htable,
6577				    fmri, pgname, argc, pattern,
6578				    flags & SCF_WALK_EXPLICIT)) != 0)
6579					goto error;
6580			}
6581
6582		}
6583	}
6584nolegacy:
6585	ret = 0;
6586
6587	if (argc == 0)
6588		goto error;
6589
6590	/*
6591	 * Check all patterns, and see if we have that any that didn't match
6592	 * or any that matched multiple instances.  For svcprop, add up the
6593	 * total number of matching keys.
6594	 */
6595	info.count = 0;
6596	for (i = 0; i < argc; i++) {
6597		scf_match_t *match;
6598
6599		if (pattern[i].sp_type == PATTERN_INVALID)
6600			continue;
6601		if (pattern[i].sp_matchcount == 0) {
6602			scf_msg_t msgid;
6603			/*
6604			 * Provide a useful error message based on the argument
6605			 * and the type of entity requested.
6606			 */
6607			if (!(flags & SCF_WALK_LEGACY) &&
6608			    strncmp(pattern[i].sp_arg, "lrc:/", 5) == 0)
6609				msgid = SCF_MSG_PATTERN_LEGACY;
6610			else if (flags & SCF_WALK_PROPERTY)
6611				msgid = SCF_MSG_PATTERN_NOENTITY;
6612			else if (flags & SCF_WALK_NOINSTANCE)
6613				msgid = SCF_MSG_PATTERN_NOSERVICE;
6614			else if (flags & SCF_WALK_SERVICE)
6615				msgid = SCF_MSG_PATTERN_NOINSTSVC;
6616			else
6617				msgid = SCF_MSG_PATTERN_NOINSTANCE;
6618
6619			errfunc(scf_get_msg(msgid), pattern[i].sp_arg);
6620			if (err)
6621				*err = UU_EXIT_FATAL;
6622		} else if (!(flags & SCF_WALK_MULTIPLE) &&
6623		    pattern[i].sp_matchcount > 1) {
6624			size_t len, off;
6625			char *msg;
6626
6627			/*
6628			 * Construct a message with all possible FMRIs before
6629			 * passing off to error handling function.
6630			 *
6631			 * Note that strlen(scf_get_msg(...)) includes the
6632			 * length of '%s', which accounts for the terminating
6633			 * null byte.
6634			 */
6635			len = strlen(scf_get_msg(SCF_MSG_PATTERN_MULTIMATCH)) +
6636			    strlen(pattern[i].sp_arg);
6637			for (match = pattern[i].sp_matches; match != NULL;
6638			    match = match->sm_next) {
6639				len += strlen(match->sm_key->sk_fmri) + 2;
6640			}
6641			if ((msg = malloc(len)) == NULL) {
6642				ret = SCF_ERROR_NO_MEMORY;
6643				goto error;
6644			}
6645
6646			/* LINTED - format argument */
6647			(void) snprintf(msg, len,
6648			    scf_get_msg(SCF_MSG_PATTERN_MULTIMATCH),
6649			    pattern[i].sp_arg);
6650			off = strlen(msg);
6651			for (match = pattern[i].sp_matches; match != NULL;
6652			    match = match->sm_next) {
6653				off += snprintf(msg + off, len - off, "\t%s\n",
6654				    match->sm_key->sk_fmri);
6655			}
6656
6657			errfunc(msg);
6658			if (err != NULL)
6659				*err = UU_EXIT_FATAL;
6660
6661			free(msg);
6662		} else {
6663			for (match = pattern[i].sp_matches; match != NULL;
6664			    match = match->sm_next) {
6665				if (!match->sm_key->sk_seen)
6666					info.count++;
6667				match->sm_key->sk_seen = 1;
6668			}
6669		}
6670	}
6671
6672	/*
6673	 * Clear 'sk_seen' for all keys.
6674	 */
6675	for (i = 0; i < WALK_HTABLE_SIZE; i++) {
6676		scf_matchkey_t *key;
6677		for (key = htable[i]; key != NULL; key = key->sk_next)
6678			key->sk_seen = 0;
6679	}
6680
6681	/*
6682	 * Iterate over all the FMRIs in our hash table and execute the
6683	 * callback.
6684	 */
6685	for (i = 0; i < argc; i++) {
6686		scf_match_t *match;
6687		scf_matchkey_t *key;
6688
6689		/*
6690		 * Ignore patterns which didn't match anything or matched too
6691		 * many FMRIs.
6692		 */
6693		if (pattern[i].sp_matchcount == 0 ||
6694		    (!(flags & SCF_WALK_MULTIPLE) &&
6695		    pattern[i].sp_matchcount > 1))
6696			continue;
6697
6698		for (match = pattern[i].sp_matches; match != NULL;
6699		    match = match->sm_next) {
6700
6701			key = match->sm_key;
6702			if (key->sk_seen)
6703				continue;
6704
6705			key->sk_seen = 1;
6706
6707			if (key->sk_legacy != NULL) {
6708				if (scf_scope_get_service(scope,
6709				    "smf/legacy_run", svc) != 0) {
6710					ret = scf_error();
6711					goto error;
6712				}
6713
6714				if (scf_service_get_pg(svc, key->sk_legacy,
6715				    pg) != 0)
6716					continue;
6717
6718				info.fmri = key->sk_fmri;
6719				info.scope = scope;
6720				info.svc = NULL;
6721				info.inst = NULL;
6722				info.pg = pg;
6723				info.prop = NULL;
6724				if ((ret = callback(data, &info)) != 0)
6725					goto error;
6726			} else {
6727				if (scf_handle_decode_fmri(h, key->sk_fmri,
6728				    scope, svc, inst, pg, prop, 0) !=
6729				    SCF_SUCCESS)
6730					continue;
6731
6732				info.fmri = key->sk_fmri;
6733				info.scope = scope;
6734				info.svc = svc;
6735				if (scf_instance_get_name(inst, NULL, 0) < 0) {
6736					if (scf_error() ==
6737					    SCF_ERROR_CONNECTION_BROKEN) {
6738						ret = scf_error();
6739						goto error;
6740					}
6741					info.inst = NULL;
6742				} else {
6743					info.inst = inst;
6744				}
6745				if (scf_pg_get_name(pg, NULL, 0) < 0) {
6746					if (scf_error() ==
6747					    SCF_ERROR_CONNECTION_BROKEN) {
6748						ret = scf_error();
6749						goto error;
6750					}
6751					info.pg = NULL;
6752				} else {
6753					info.pg = pg;
6754				}
6755				if (scf_property_get_name(prop, NULL, 0) < 0) {
6756					if (scf_error() ==
6757					    SCF_ERROR_CONNECTION_BROKEN) {
6758						ret = scf_error();
6759						goto error;
6760					}
6761					info.prop = NULL;
6762				} else {
6763					info.prop = prop;
6764				}
6765
6766				if ((ret = callback(data, &info)) != 0)
6767					goto error;
6768			}
6769		}
6770	}
6771
6772error:
6773	if (htable) {
6774		scf_matchkey_t *key, *next;
6775
6776		for (i = 0; i < WALK_HTABLE_SIZE; i++) {
6777
6778			for (key = htable[i]; key != NULL;
6779			    key = next) {
6780
6781				next = key->sk_next;
6782
6783				if (key->sk_fmri != NULL)
6784					free(key->sk_fmri);
6785				if (key->sk_legacy != NULL)
6786					free(key->sk_legacy);
6787				free(key);
6788			}
6789		}
6790		free(htable);
6791	}
6792	if (pattern != NULL) {
6793		for (i = 0; i < argc; i++) {
6794			scf_match_t *match, *next;
6795
6796			if (pattern[i].sp_arg != NULL)
6797				free(pattern[i].sp_arg);
6798
6799			for (match = pattern[i].sp_matches; match != NULL;
6800			    match = next) {
6801
6802				next = match->sm_next;
6803
6804				free(match);
6805			}
6806		}
6807		free(pattern);
6808	}
6809
6810	free(fmri);
6811	free(pgname);
6812
6813	scf_value_destroy(value);
6814	scf_property_destroy(prop);
6815	scf_pg_destroy(pg);
6816	scf_scope_destroy(scope);
6817	scf_iter_destroy(siter);
6818	scf_iter_destroy(sciter);
6819	scf_iter_destroy(iter);
6820	scf_instance_destroy(inst);
6821	scf_service_destroy(svc);
6822
6823	return (ret);
6824}
6825
6826/*
6827 * _scf_request_backup:  a simple wrapper routine
6828 */
6829int
6830_scf_request_backup(scf_handle_t *h, const char *name)
6831{
6832	struct rep_protocol_backup_request request;
6833	struct rep_protocol_response response;
6834
6835	int r;
6836
6837	if (strlcpy(request.rpr_name, name, sizeof (request.rpr_name)) >=
6838	    sizeof (request.rpr_name))
6839		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
6840
6841	(void) pthread_mutex_lock(&h->rh_lock);
6842	request.rpr_request = REP_PROTOCOL_BACKUP;
6843	request.rpr_changeid = handle_next_changeid(h);
6844
6845	r = make_door_call(h, &request, sizeof (request),
6846	    &response, sizeof (response));
6847	(void) pthread_mutex_unlock(&h->rh_lock);
6848
6849	if (r < 0) {
6850		DOOR_ERRORS_BLOCK(r);
6851	}
6852
6853	if (response.rpr_response != REP_PROTOCOL_SUCCESS)
6854		return (scf_set_error(proto_error(response.rpr_response)));
6855	return (SCF_SUCCESS);
6856}
6857
6858int
6859_scf_pg_is_read_protected(const scf_propertygroup_t *pg, boolean_t *out)
6860{
6861	char buf[REP_PROTOCOL_NAME_LEN];
6862	ssize_t res;
6863
6864	res = datael_get_name(&pg->rd_d, buf, sizeof (buf),
6865	    RP_ENTITY_NAME_PGREADPROT);
6866
6867	if (res == -1)
6868		return (-1);
6869
6870	if (uu_strtouint(buf, out, sizeof (*out), 0, 0, 1) == -1)
6871		return (scf_set_error(SCF_ERROR_INTERNAL));
6872	return (SCF_SUCCESS);
6873}
6874
6875/*
6876 * _scf_set_annotation: a wrapper to set the annotation fields for SMF
6877 * security auditing.
6878 *
6879 * Fails with following in scf_error_key thread specific data:
6880 *	_INVALID_ARGUMENT - operation or file too large
6881 *	_NOT_BOUND
6882 *	_CONNECTION_BROKEN
6883 *	_INTERNAL
6884 *	_NO_RESOURCES
6885 */
6886int
6887_scf_set_annotation(scf_handle_t *h, const char *operation, const char *file)
6888{
6889	struct rep_protocol_annotation request;
6890	struct rep_protocol_response response;
6891	size_t copied;
6892	int r;
6893
6894	request.rpr_request = REP_PROTOCOL_SET_AUDIT_ANNOTATION;
6895	copied = strlcpy(request.rpr_operation,
6896	    (operation == NULL) ? "" : operation,
6897	    sizeof (request.rpr_operation));
6898	if (copied >= sizeof (request.rpr_operation))
6899		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
6900
6901	copied = strlcpy(request.rpr_file,
6902	    (file == NULL) ? "" : file,
6903	    sizeof (request.rpr_file));
6904	if (copied >= sizeof (request.rpr_operation))
6905		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
6906
6907	(void) pthread_mutex_lock(&h->rh_lock);
6908	r = make_door_call(h, &request, sizeof (request),
6909	    &response, sizeof (response));
6910	(void) pthread_mutex_unlock(&h->rh_lock);
6911
6912	if (r < 0) {
6913		DOOR_ERRORS_BLOCK(r);
6914	}
6915
6916	if (response.rpr_response != REP_PROTOCOL_SUCCESS)
6917		return (scf_set_error(proto_error(response.rpr_response)));
6918	return (0);
6919}
6920