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