17c478bdstevel@tonic-gate/*
27c478bdstevel@tonic-gate * CDDL HEADER START
37c478bdstevel@tonic-gate *
47c478bdstevel@tonic-gate * The contents of this file are subject to the terms of the
5103b2b1gww * Common Development and Distribution License (the "License").
6103b2b1gww * You may not use this file except in compliance with the License.
77c478bdstevel@tonic-gate *
87c478bdstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bdstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bdstevel@tonic-gate * See the License for the specific language governing permissions
117c478bdstevel@tonic-gate * and limitations under the License.
127c478bdstevel@tonic-gate *
137c478bdstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bdstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bdstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bdstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bdstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bdstevel@tonic-gate *
197c478bdstevel@tonic-gate * CDDL HEADER END
207c478bdstevel@tonic-gate */
21a574db8raf
227c478bdstevel@tonic-gate/*
23b56bf88Antonello Cruz * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
246c7c876Jerry Jelinek * Copyright 2013, Joyent, Inc. All rights reserved.
254f5c6faAndrew Stormont * Copyright 2016 RackTop Systems.
2648bbca8Daniel Hoffman * Copyright (c) 2016 by Delphix. All rights reserved.
27ad3ad82Andy Fiddaman * Copyright 2017 OmniOS Community Edition (OmniOSce) Association.
287c478bdstevel@tonic-gate */
297c478bdstevel@tonic-gate
307c478bdstevel@tonic-gate/*
317c478bdstevel@tonic-gate * This is the main implementation file for the low-level repository
327c478bdstevel@tonic-gate * interface.
337c478bdstevel@tonic-gate */
347c478bdstevel@tonic-gate
357c478bdstevel@tonic-gate#include "lowlevel_impl.h"
367c478bdstevel@tonic-gate
377c478bdstevel@tonic-gate#include "repcache_protocol.h"
387c478bdstevel@tonic-gate#include "scf_type.h"
397c478bdstevel@tonic-gate
407c478bdstevel@tonic-gate#include <assert.h>
417c478bdstevel@tonic-gate#include <alloca.h>
427c478bdstevel@tonic-gate#include <door.h>
437c478bdstevel@tonic-gate#include <errno.h>
447c478bdstevel@tonic-gate#include <fcntl.h>
457c478bdstevel@tonic-gate#include <fnmatch.h>
467c478bdstevel@tonic-gate#include <libuutil.h>
477c478bdstevel@tonic-gate#include <poll.h>
487c478bdstevel@tonic-gate#include <pthread.h>
4953f3aeaRoger A. Faulkner#include <synch.h>
507c478bdstevel@tonic-gate#include <stddef.h>
517c478bdstevel@tonic-gate#include <stdio.h>
527c478bdstevel@tonic-gate#include <stdlib.h>
537c478bdstevel@tonic-gate#include <string.h>
547c478bdstevel@tonic-gate#include <sys/mman.h>
557c478bdstevel@tonic-gate#include <sys/sysmacros.h>
56048b027Bryan Cantrill#include <libzonecfg.h>
577c478bdstevel@tonic-gate#include <unistd.h>
58048b027Bryan Cantrill#include <dlfcn.h>
597c478bdstevel@tonic-gate
607c478bdstevel@tonic-gate#define	ENV_SCF_DEBUG		"LIBSCF_DEBUG"
617c478bdstevel@tonic-gate#define	ENV_SCF_DOORPATH	"LIBSCF_DOORPATH"
627c478bdstevel@tonic-gate
637c478bdstevel@tonic-gatestatic uint32_t default_debug = 0;
647c478bdstevel@tonic-gatestatic const char *default_door_path = REPOSITORY_DOOR_NAME;
657c478bdstevel@tonic-gate
667c478bdstevel@tonic-gate#define	CALL_FAILED		-1
677c478bdstevel@tonic-gate#define	RESULT_TOO_BIG		-2
687c478bdstevel@tonic-gate#define	NOT_BOUND		-3
697c478bdstevel@tonic-gate
707c478bdstevel@tonic-gatestatic pthread_mutex_t	lowlevel_init_lock;
717c478bdstevel@tonic-gatestatic int32_t		lowlevel_inited;
727c478bdstevel@tonic-gate
737c478bdstevel@tonic-gatestatic uu_list_pool_t	*tran_entry_pool;
747c478bdstevel@tonic-gatestatic uu_list_pool_t	*datael_pool;
757c478bdstevel@tonic-gatestatic uu_list_pool_t	*iter_pool;
767c478bdstevel@tonic-gate
777c478bdstevel@tonic-gate/*
781f6eb02Liane Praza * base32[] index32[] are used in base32 encoding and decoding.
791f6eb02Liane Praza */
801f6eb02Liane Prazastatic char base32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
811f6eb02Liane Prazastatic char index32[128] = {
821f6eb02Liane Praza	-1, -1, -1, -1, -1, -1, -1, -1,	/* 0-7 */
831f6eb02Liane Praza	-1, -1, -1, -1, -1, -1, -1, -1,	/* 8-15 */
841f6eb02Liane Praza	-1, -1, -1, -1, -1, -1, -1, -1,	/* 16-23 */
851f6eb02Liane Praza	-1, -1, -1, -1, -1, -1, -1, -1,	/* 24-31 */
861f6eb02Liane Praza	-1, -1, -1, -1, -1, -1, -1, -1,	/* 32-39 */
871f6eb02Liane Praza	-1, -1, -1, -1, -1, -1, -1, -1,	/* 40-47 */
881f6eb02Liane Praza	-1, -1, 26, 27, 28, 29, 30, 31,	/* 48-55 */
891f6eb02Liane Praza	-1, -1, -1, -1, -1, -1, -1, -1,	/* 56-63 */
901f6eb02Liane Praza	-1, 0, 1, 2, 3, 4, 5, 6,	/* 64-71 */
911f6eb02Liane Praza	7, 8, 9, 10, 11, 12, 13, 14,	/* 72-79 */
921f6eb02Liane Praza	15, 16, 17, 18, 19, 20, 21, 22,	/* 80-87 */
931f6eb02Liane Praza	23, 24, 25, -1, -1, -1, -1, -1,	/* 88-95 */
941f6eb02Liane Praza	-1, -1, -1, -1, -1, -1, -1, -1,	/* 96-103 */
951f6eb02Liane Praza	-1, -1, -1, -1, -1, -1, -1, -1,	/* 104-111 */
961f6eb02Liane Praza	-1, -1, -1, -1, -1, -1, -1, -1,	/* 112-119 */
971f6eb02Liane Praza	-1, -1, -1, -1, -1, -1, -1, -1	/* 120-127 */
981f6eb02Liane Praza};
991f6eb02Liane Praza
1001f6eb02Liane Praza#define	DECODE32_GS	(8)	/* scf_decode32 group size */
1011f6eb02Liane Praza
1027c478bdstevel@tonic-gate#ifdef lint
1037c478bdstevel@tonic-gate#define	assert_nolint(x) (void)0
1047c478bdstevel@tonic-gate#else
1057c478bdstevel@tonic-gate#define	assert_nolint(x) assert(x)
1067c478bdstevel@tonic-gate#endif
1077c478bdstevel@tonic-gate
1087c478bdstevel@tonic-gatestatic void scf_iter_reset_locked(scf_iter_t *iter);
1097c478bdstevel@tonic-gatestatic void scf_value_reset_locked(scf_value_t *val, int and_destroy);
1107c478bdstevel@tonic-gate
1117c478bdstevel@tonic-gate#define	TYPE_VALUE	(-100)
1127c478bdstevel@tonic-gate
1137c478bdstevel@tonic-gate/*
1147c478bdstevel@tonic-gate * Hold and release subhandles.  We only allow one thread access to the
11548bbca8Daniel Hoffman * subhandles at a time, and it can use any subset, grabbing and releasing
1167c478bdstevel@tonic-gate * them in any order.  The only restrictions are that you cannot hold an
1177c478bdstevel@tonic-gate * already-held subhandle, and all subhandles must be released before
1187c478bdstevel@tonic-gate * returning to the original caller.
1197c478bdstevel@tonic-gate */
1207c478bdstevel@tonic-gatestatic void
1217c478bdstevel@tonic-gatehandle_hold_subhandles(scf_handle_t *h, int mask)
1227c478bdstevel@tonic-gate{
1237c478bdstevel@tonic-gate	assert(mask != 0 && (mask & ~RH_HOLD_ALL) == 0);
1247c478bdstevel@tonic-gate
1257c478bdstevel@tonic-gate	(void) pthread_mutex_lock(&h->rh_lock);
126a574db8raf	while (h->rh_hold_flags != 0 && h->rh_holder != pthread_self()) {
127a574db8raf		int cancel_state;
128a574db8raf
129a574db8raf		(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
130a574db8raf		    &cancel_state);
131a574db8raf		(void) pthread_cond_wait(&h->rh_cv, &h->rh_lock);
132a574db8raf		(void) pthread_setcancelstate(cancel_state, NULL);
133a574db8raf	}
1347c478bdstevel@tonic-gate	if (h->rh_hold_flags == 0)
1357c478bdstevel@tonic-gate		h->rh_holder = pthread_self();
1367c478bdstevel@tonic-gate	assert(!(h->rh_hold_flags & mask));
1377c478bdstevel@tonic-gate	h->rh_hold_flags |= mask;
1387c478bdstevel@tonic-gate	(void) pthread_mutex_unlock(&h->rh_lock);
1397c478bdstevel@tonic-gate}
1407c478bdstevel@tonic-gate
1417c478bdstevel@tonic-gatestatic void
1427c478bdstevel@tonic-gatehandle_rele_subhandles(scf_handle_t *h, int mask)
1437c478bdstevel@tonic-gate{
1447c478bdstevel@tonic-gate	assert(mask != 0 && (mask & ~RH_HOLD_ALL) == 0);
1457c478bdstevel@tonic-gate
1467c478bdstevel@tonic-gate	(void) pthread_mutex_lock(&h->rh_lock);
1477c478bdstevel@tonic-gate	assert(h->rh_holder == pthread_self());
1487c478bdstevel@tonic-gate	assert((h->rh_hold_flags & mask));
1497c478bdstevel@tonic-gate
1507c478bdstevel@tonic-gate	h->rh_hold_flags &= ~mask;
1517c478bdstevel@tonic-gate	if (h->rh_hold_flags == 0)
1527c478bdstevel@tonic-gate		(void) pthread_cond_signal(&h->rh_cv);
1537c478bdstevel@tonic-gate	(void) pthread_mutex_unlock(&h->rh_lock);
1547c478bdstevel@tonic-gate}
1557c478bdstevel@tonic-gate
1567c478bdstevel@tonic-gate#define	HOLD_HANDLE(h, flag, field) \
1577c478bdstevel@tonic-gate	(handle_hold_subhandles((h), (flag)), (h)->field)
1587c478bdstevel@tonic-gate
1597c478bdstevel@tonic-gate#define	RELE_HANDLE(h, flag) \
1607c478bdstevel@tonic-gate	(handle_rele_subhandles((h), (flag)))
1617c478bdstevel@tonic-gate
1627c478bdstevel@tonic-gate/*
1637c478bdstevel@tonic-gate * convenience macros, for functions that only need a one or two handles at
1647c478bdstevel@tonic-gate * any given time
1657c478bdstevel@tonic-gate */
1667c478bdstevel@tonic-gate#define	HANDLE_HOLD_ITER(h)	HOLD_HANDLE((h), RH_HOLD_ITER, rh_iter)
1677c478bdstevel@tonic-gate#define	HANDLE_HOLD_SCOPE(h)	HOLD_HANDLE((h), RH_HOLD_SCOPE, rh_scope)
1687c478bdstevel@tonic-gate#define	HANDLE_HOLD_SERVICE(h)	HOLD_HANDLE((h), RH_HOLD_SERVICE, rh_service)
1697c478bdstevel@tonic-gate#define	HANDLE_HOLD_INSTANCE(h)	HOLD_HANDLE((h), RH_HOLD_INSTANCE, rh_instance)
1707c478bdstevel@tonic-gate#define	HANDLE_HOLD_SNAPSHOT(h)	HOLD_HANDLE((h), RH_HOLD_SNAPSHOT, rh_snapshot)
1717c478bdstevel@tonic-gate#define	HANDLE_HOLD_SNAPLVL(h)	HOLD_HANDLE((h), RH_HOLD_SNAPLVL, rh_snaplvl)
1727c478bdstevel@tonic-gate#define	HANDLE_HOLD_PG(h)	HOLD_HANDLE((h), RH_HOLD_PG, rh_pg)
1737c478bdstevel@tonic-gate#define	HANDLE_HOLD_PROPERTY(h)	HOLD_HANDLE((h), RH_HOLD_PROPERTY, rh_property)
1747c478bdstevel@tonic-gate#define	HANDLE_HOLD_VALUE(h)	HOLD_HANDLE((h), RH_HOLD_VALUE, rh_value)
1757c478bdstevel@tonic-gate
1767c478bdstevel@tonic-gate#define	HANDLE_RELE_ITER(h)	RELE_HANDLE((h), RH_HOLD_ITER)
1777c478bdstevel@tonic-gate#define	HANDLE_RELE_SCOPE(h)	RELE_HANDLE((h), RH_HOLD_SCOPE)
1787c478bdstevel@tonic-gate#define	HANDLE_RELE_SERVICE(h)	RELE_HANDLE((h), RH_HOLD_SERVICE)
1797c478bdstevel@tonic-gate#define	HANDLE_RELE_INSTANCE(h)	RELE_HANDLE((h), RH_HOLD_INSTANCE)
1807c478bdstevel@tonic-gate#define	HANDLE_RELE_SNAPSHOT(h)	RELE_HANDLE((h), RH_HOLD_SNAPSHOT)
1817c478bdstevel@tonic-gate#define	HANDLE_RELE_SNAPLVL(h)	RELE_HANDLE((h), RH_HOLD_SNAPLVL)
1827c478bdstevel@tonic-gate#define	HANDLE_RELE_PG(h)	RELE_HANDLE((h), RH_HOLD_PG)
1837c478bdstevel@tonic-gate#define	HANDLE_RELE_PROPERTY(h)	RELE_HANDLE((h), RH_HOLD_PROPERTY)
1847c478bdstevel@tonic-gate#define	HANDLE_RELE_VALUE(h)	RELE_HANDLE((h), RH_HOLD_VALUE)
1857c478bdstevel@tonic-gate
1867c478bdstevel@tonic-gate/*ARGSUSED*/
1877c478bdstevel@tonic-gatestatic int
1887c478bdstevel@tonic-gatetransaction_entry_compare(const void *l_arg, const void *r_arg, void *private)
1897c478bdstevel@tonic-gate{
1907c478bdstevel@tonic-gate	const char *l_prop =
1917c478bdstevel@tonic-gate	    ((scf_transaction_entry_t *)l_arg)->entry_property;
1927c478bdstevel@tonic-gate	const char *r_prop =
1937c478bdstevel@tonic-gate	    ((scf_transaction_entry_t *)r_arg)->entry_property;
1947c478bdstevel@tonic-gate
1957c478bdstevel@tonic-gate	int ret;
1967c478bdstevel@tonic-gate
1977c478bdstevel@tonic-gate	ret = strcmp(l_prop, r_prop);
1987c478bdstevel@tonic-gate	if (ret > 0)
1997c478bdstevel@tonic-gate		return (1);
2008918dffjwadams	if (ret < 0)
2018918dffjwadams		return (-1);
2028918dffjwadams	return (0);
2038918dffjwadams}
2048918dffjwadams
2058918dffjwadamsstatic int
2068918dffjwadamsdatael_compare(const void *l_arg, const void *r_arg, void *private)
2078918dffjwadams{
2088918dffjwadams	uint32_t l_id = ((scf_datael_t *)l_arg)->rd_entity;
2098918dffjwadams	uint32_t r_id = (r_arg != NULL) ? ((scf_datael_t *)r_arg)->rd_entity :
2108918dffjwadams	    *(uint32_t *)private;
2118918dffjwadams
2128918dffjwadams	if (l_id > r_id)
2138918dffjwadams		return (1);
2148918dffjwadams	if (l_id < r_id)
2158918dffjwadams		return (-1);
2168918dffjwadams	return (0);
2178918dffjwadams}
2188918dffjwadams
2198918dffjwadamsstatic int
2208918dffjwadamsiter_compare(const void *l_arg, const void *r_arg, void *private)
2218918dffjwadams{
2228918dffjwadams	uint32_t l_id = ((scf_iter_t *)l_arg)->iter_id;
2238918dffjwadams	uint32_t r_id = (r_arg != NULL) ? ((scf_iter_t *)r_arg)->iter_id :
2248918dffjwadams	    *(uint32_t *)private;
2258918dffjwadams
2268918dffjwadams	if (l_id > r_id)
2278918dffjwadams		return (1);
2288918dffjwadams	if (l_id < r_id)
2297c478bdstevel@tonic-gate		return (-1);
2307c478bdstevel@tonic-gate	return (0);
2317c478bdstevel@tonic-gate}
2327c478bdstevel@tonic-gate
2337c478bdstevel@tonic-gatestatic int
2347c478bdstevel@tonic-gatelowlevel_init(void)
2357c478bdstevel@tonic-gate{
2367c478bdstevel@tonic-gate	const char *debug;
2377c478bdstevel@tonic-gate	const char *door_path;
2387c478bdstevel@tonic-gate
2397c478bdstevel@tonic-gate	(void) pthread_mutex_lock(&lowlevel_init_lock);
2407c478bdstevel@tonic-gate	if (lowlevel_inited == 0) {
2417c478bdstevel@tonic-gate		if (!issetugid() &&
2427c478bdstevel@tonic-gate		    (debug = getenv(ENV_SCF_DEBUG)) != NULL && debug[0] != 0 &&
2437c478bdstevel@tonic-gate		    uu_strtoint(debug, &default_debug, sizeof (default_debug),
2447c478bdstevel@tonic-gate		    0, 0, 0) == -1) {
2457c478bdstevel@tonic-gate			(void) fprintf(stderr, "LIBSCF: $%s (%s): %s",
2467c478bdstevel@tonic-gate			    ENV_SCF_DEBUG, debug,
2477c478bdstevel@tonic-gate			    uu_strerror(uu_error()));
2487c478bdstevel@tonic-gate		}
2497c478bdstevel@tonic-gate
2507c478bdstevel@tonic-gate		if (!issetugid() &&
2517c478bdstevel@tonic-gate		    (door_path = getenv(ENV_SCF_DOORPATH)) != NULL &&
2527c478bdstevel@tonic-gate		    door_path[0] != 0) {
2537c478bdstevel@tonic-gate			default_door_path = strdup(door_path);
2547c478bdstevel@tonic-gate			if (default_door_path == NULL)
2557c478bdstevel@tonic-gate				default_door_path = door_path;
2567c478bdstevel@tonic-gate		}
2577c478bdstevel@tonic-gate
2587c478bdstevel@tonic-gate		datael_pool = uu_list_pool_create("SUNW,libscf_datael",
2597c478bdstevel@tonic-gate		    sizeof (scf_datael_t), offsetof(scf_datael_t, rd_node),
2608918dffjwadams		    datael_compare, UU_LIST_POOL_DEBUG);
2617c478bdstevel@tonic-gate
2627c478bdstevel@tonic-gate		iter_pool = uu_list_pool_create("SUNW,libscf_iter",
2637c478bdstevel@tonic-gate		    sizeof (scf_iter_t), offsetof(scf_iter_t, iter_node),
2648918dffjwadams		    iter_compare, UU_LIST_POOL_DEBUG);
2657c478bdstevel@tonic-gate
2667c478bdstevel@tonic-gate		assert_nolint(offsetof(scf_transaction_entry_t,
2677c478bdstevel@tonic-gate		    entry_property) == 0);
2687c478bdstevel@tonic-gate		tran_entry_pool = uu_list_pool_create(
2697c478bdstevel@tonic-gate		    "SUNW,libscf_transaction_entity",
2707c478bdstevel@tonic-gate		    sizeof (scf_transaction_entry_t),
2717c478bdstevel@tonic-gate		    offsetof(scf_transaction_entry_t, entry_link),
2727c478bdstevel@tonic-gate		    transaction_entry_compare, UU_LIST_POOL_DEBUG);
2737c478bdstevel@tonic-gate
2747c478bdstevel@tonic-gate		if (datael_pool == NULL || iter_pool == NULL ||
2757c478bdstevel@tonic-gate		    tran_entry_pool == NULL) {
2767c478bdstevel@tonic-gate			lowlevel_inited = -1;
2777c478bdstevel@tonic-gate			goto end;
2787c478bdstevel@tonic-gate		}
2797c478bdstevel@tonic-gate
2807c478bdstevel@tonic-gate		if (!scf_setup_error()) {
2817c478bdstevel@tonic-gate			lowlevel_inited = -1;
2827c478bdstevel@tonic-gate			goto end;
2837c478bdstevel@tonic-gate		}
2847c478bdstevel@tonic-gate		lowlevel_inited = 1;
2857c478bdstevel@tonic-gate	}
2867c478bdstevel@tonic-gateend:
2877c478bdstevel@tonic-gate	(void) pthread_mutex_unlock(&lowlevel_init_lock);
2887c478bdstevel@tonic-gate	if (lowlevel_inited > 0)
2897c478bdstevel@tonic-gate		return (1);
2907c478bdstevel@tonic-gate	return (0);
2917c478bdstevel@tonic-gate}
2927c478bdstevel@tonic-gate
2937c478bdstevel@tonic-gatestatic const struct {
2947c478bdstevel@tonic-gate	scf_type_t ti_type;
2957c478bdstevel@tonic-gate	rep_protocol_value_type_t ti_proto_type;
2967c478bdstevel@tonic-gate	const char *ti_name;
2977c478bdstevel@tonic-gate} scf_type_info[] = {
2984f5c6faAndrew Stormont	{SCF_TYPE_BOOLEAN,	REP_PROTOCOL_TYPE_BOOLEAN,
2994f5c6faAndrew Stormont	    SCF_TYPE_STRING_BOOLEAN},
3004f5c6faAndrew Stormont	{SCF_TYPE_COUNT,	REP_PROTOCOL_TYPE_COUNT,
3014f5c6faAndrew Stormont	    SCF_TYPE_STRING_COUNT},
3024f5c6faAndrew Stormont	{SCF_TYPE_INTEGER,	REP_PROTOCOL_TYPE_INTEGER,
3034f5c6faAndrew Stormont	    SCF_TYPE_STRING_INTEGER},
3044f5c6faAndrew Stormont	{SCF_TYPE_TIME,		REP_PROTOCOL_TYPE_TIME,
3054f5c6faAndrew Stormont	    SCF_TYPE_STRING_TIME},
3064f5c6faAndrew Stormont	{SCF_TYPE_ASTRING,	REP_PROTOCOL_TYPE_STRING,
3074f5c6faAndrew Stormont	    SCF_TYPE_STRING_ASTRING},
3084f5c6faAndrew Stormont	{SCF_TYPE_OPAQUE,	REP_PROTOCOL_TYPE_OPAQUE,
3094f5c6faAndrew Stormont	    SCF_TYPE_STRING_OPAQUE},
3104f5c6faAndrew Stormont	{SCF_TYPE_USTRING,	REP_PROTOCOL_SUBTYPE_USTRING,
3114f5c6faAndrew Stormont	    SCF_TYPE_STRING_USTRING},
3124f5c6faAndrew Stormont	{SCF_TYPE_URI,		REP_PROTOCOL_SUBTYPE_URI,
3134f5c6faAndrew Stormont	    SCF_TYPE_STRING_URI},
3144f5c6faAndrew Stormont	{SCF_TYPE_FMRI,		REP_PROTOCOL_SUBTYPE_FMRI,
3154f5c6faAndrew Stormont	    SCF_TYPE_STRING_FMRI},
3164f5c6faAndrew Stormont	{SCF_TYPE_HOST,		REP_PROTOCOL_SUBTYPE_HOST,
3174f5c6faAndrew Stormont	    SCF_TYPE_STRING_HOST},
3184f5c6faAndrew Stormont	{SCF_TYPE_HOSTNAME,	REP_PROTOCOL_SUBTYPE_HOSTNAME,
3194f5c6faAndrew Stormont	    SCF_TYPE_STRING_HOSTNAME},
3204f5c6faAndrew Stormont	{SCF_TYPE_NET_ADDR,	REP_PROTOCOL_SUBTYPE_NETADDR,
3214f5c6faAndrew Stormont	    SCF_TYPE_STRING_NET_ADDR},
3227c478bdstevel@tonic-gate	{SCF_TYPE_NET_ADDR_V4,	REP_PROTOCOL_SUBTYPE_NETADDR_V4,
3234f5c6faAndrew Stormont	    SCF_TYPE_STRING_NET_ADDR_V4},
3247c478bdstevel@tonic-gate	{SCF_TYPE_NET_ADDR_V6,	REP_PROTOCOL_SUBTYPE_NETADDR_V6,
3254f5c6faAndrew Stormont	    SCF_TYPE_STRING_NET_ADDR_V6}
3267c478bdstevel@tonic-gate};
3277c478bdstevel@tonic-gate
3287c478bdstevel@tonic-gate#define	SCF_TYPE_INFO_COUNT (sizeof (scf_type_info) / sizeof (*scf_type_info))
3297c478bdstevel@tonic-gatestatic rep_protocol_value_type_t
3307c478bdstevel@tonic-gatescf_type_to_protocol_type(scf_type_t t)
3317c478bdstevel@tonic-gate{
3327c478bdstevel@tonic-gate	int i;
3337c478bdstevel@tonic-gate
3347c478bdstevel@tonic-gate	for (i = 0; i < SCF_TYPE_INFO_COUNT; i++)
3357c478bdstevel@tonic-gate		if (scf_type_info[i].ti_type == t)
3367c478bdstevel@tonic-gate			return (scf_type_info[i].ti_proto_type);
3377c478bdstevel@tonic-gate
3387c478bdstevel@tonic-gate	return (REP_PROTOCOL_TYPE_INVALID);
3397c478bdstevel@tonic-gate}
3407c478bdstevel@tonic-gate
3417c478bdstevel@tonic-gatestatic scf_type_t
3427c478bdstevel@tonic-gatescf_protocol_type_to_type(rep_protocol_value_type_t t)
3437c478bdstevel@tonic-gate{
3447c478bdstevel@tonic-gate	int i;
3457c478bdstevel@tonic-gate
3467c478bdstevel@tonic-gate	for (i = 0; i < SCF_TYPE_INFO_COUNT; i++)
3477c478bdstevel@tonic-gate		if (scf_type_info[i].ti_proto_type == t)
3487c478bdstevel@tonic-gate			return (scf_type_info[i].ti_type);
3497c478bdstevel@tonic-gate
3507c478bdstevel@tonic-gate	return (SCF_TYPE_INVALID);
3517c478bdstevel@tonic-gate}
3527c478bdstevel@tonic-gate
3537c478bdstevel@tonic-gateconst char *
3547c478bdstevel@tonic-gatescf_type_to_string(scf_type_t ty)
3557c478bdstevel@tonic-gate{
3567c478bdstevel@tonic-gate	int i;
3577c478bdstevel@tonic-gate
3587c478bdstevel@tonic-gate	for (i = 0; i < SCF_TYPE_INFO_COUNT; i++)
3597c478bdstevel@tonic-gate		if (scf_type_info[i].ti_type == ty)
3607c478bdstevel@tonic-gate			return (scf_type_info[i].ti_name);
3617c478bdstevel@tonic-gate
3627c478bdstevel@tonic-gate	return ("unknown");
3637c478bdstevel@tonic-gate}
3647c478bdstevel@tonic-gate
3657c478bdstevel@tonic-gatescf_type_t
3667c478bdstevel@tonic-gatescf_string_to_type(const char *name)
3677c478bdstevel@tonic-gate{
3687c478bdstevel@tonic-gate	int i;
3697c478bdstevel@tonic-gate
3707c478bdstevel@tonic-gate	for (i = 0; i < sizeof (scf_type_info) / sizeof (*scf_type_info); i++)
3717c478bdstevel@tonic-gate		if (strcmp(scf_type_info[i].ti_name, name) == 0)
3727c478bdstevel@tonic-gate			return (scf_type_info[i].ti_type);
3737c478bdstevel@tonic-gate
3747c478bdstevel@tonic-gate	return (SCF_TYPE_INVALID);
3757c478bdstevel@tonic-gate}
3767c478bdstevel@tonic-gate
3777c478bdstevel@tonic-gateint
3787c478bdstevel@tonic-gatescf_type_base_type(scf_type_t type, scf_type_t *out)
3797c478bdstevel@tonic-gate{
3807c478bdstevel@tonic-gate	rep_protocol_value_type_t t = scf_type_to_protocol_type(type);
3817c478bdstevel@tonic-gate	if (t == REP_PROTOCOL_TYPE_INVALID)
3827c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
3837c478bdstevel@tonic-gate
3847c478bdstevel@tonic-gate	*out = scf_protocol_type_to_type(scf_proto_underlying_type(t));
3857c478bdstevel@tonic-gate	return (SCF_SUCCESS);
3867c478bdstevel@tonic-gate}
3877c478bdstevel@tonic-gate
3887c478bdstevel@tonic-gate/*
3897c478bdstevel@tonic-gate * Convert a protocol error code into an SCF_ERROR_* code.
3907c478bdstevel@tonic-gate */
3917c478bdstevel@tonic-gatestatic scf_error_t
3927c478bdstevel@tonic-gateproto_error(rep_protocol_responseid_t e)
3937c478bdstevel@tonic-gate{
3947c478bdstevel@tonic-gate	switch (e) {
3957c478bdstevel@tonic-gate	case REP_PROTOCOL_FAIL_MISORDERED:
3967c478bdstevel@tonic-gate	case REP_PROTOCOL_FAIL_UNKNOWN_ID:
3977c478bdstevel@tonic-gate	case REP_PROTOCOL_FAIL_INVALID_TYPE:
3987c478bdstevel@tonic-gate	case REP_PROTOCOL_FAIL_TRUNCATED:
3997c478bdstevel@tonic-gate	case REP_PROTOCOL_FAIL_TYPE_MISMATCH:
4007c478bdstevel@tonic-gate	case REP_PROTOCOL_FAIL_NOT_APPLICABLE:
4017c478bdstevel@tonic-gate	case REP_PROTOCOL_FAIL_UNKNOWN:
4027c478bdstevel@tonic-gate		return (SCF_ERROR_INTERNAL);
4037c478bdstevel@tonic-gate
4047c478bdstevel@tonic-gate	case REP_PROTOCOL_FAIL_BAD_TX:
4057c478bdstevel@tonic-gate		return (SCF_ERROR_INVALID_ARGUMENT);
4067c478bdstevel@tonic-gate	case REP_PROTOCOL_FAIL_BAD_REQUEST:
4077c478bdstevel@tonic-gate		return (SCF_ERROR_INVALID_ARGUMENT);
4087c478bdstevel@tonic-gate	case REP_PROTOCOL_FAIL_NO_RESOURCES:
4097c478bdstevel@tonic-gate		return (SCF_ERROR_NO_RESOURCES);
4107c478bdstevel@tonic-gate	case REP_PROTOCOL_FAIL_NOT_FOUND:
4117c478bdstevel@tonic-gate		return (SCF_ERROR_NOT_FOUND);
4127c478bdstevel@tonic-gate	case REP_PROTOCOL_FAIL_DELETED:
4137c478bdstevel@tonic-gate		return (SCF_ERROR_DELETED);
4147c478bdstevel@tonic-gate	case REP_PROTOCOL_FAIL_NOT_SET:
4157c478bdstevel@tonic-gate		return (SCF_ERROR_NOT_SET);
4167c478bdstevel@tonic-gate	case REP_PROTOCOL_FAIL_EXISTS:
4177c478bdstevel@tonic-gate		return (SCF_ERROR_EXISTS);
4187c478bdstevel@tonic-gate	case REP_PROTOCOL_FAIL_DUPLICATE_ID:
4197c478bdstevel@tonic-gate		return (SCF_ERROR_EXISTS);
4207c478bdstevel@tonic-gate	case REP_PROTOCOL_FAIL_PERMISSION_DENIED:
4217c478bdstevel@tonic-gate		return (SCF_ERROR_PERMISSION_DENIED);
4227c478bdstevel@tonic-gate	case REP_PROTOCOL_FAIL_BACKEND_ACCESS:
4237c478bdstevel@tonic-gate		return (SCF_ERROR_BACKEND_ACCESS);
4247c478bdstevel@tonic-gate	case REP_PROTOCOL_FAIL_BACKEND_READONLY:
4257c478bdstevel@tonic-gate		return (SCF_ERROR_BACKEND_READONLY);
4267c478bdstevel@tonic-gate
4277c478bdstevel@tonic-gate	case REP_PROTOCOL_SUCCESS:
4287c478bdstevel@tonic-gate	case REP_PROTOCOL_DONE:
4297c478bdstevel@tonic-gate	case REP_PROTOCOL_FAIL_NOT_LATEST:	/* TX code should handle this */
4307c478bdstevel@tonic-gate	default:
4317c478bdstevel@tonic-gate#ifndef NDEBUG
4327c478bdstevel@tonic-gate		uu_warn("%s:%d: Bad error code %d passed to proto_error().\n",
4337c478bdstevel@tonic-gate		    __FILE__, __LINE__, e);
4347c478bdstevel@tonic-gate#endif
4357c478bdstevel@tonic-gate		abort();
4367c478bdstevel@tonic-gate		/*NOTREACHED*/
4377c478bdstevel@tonic-gate	}
4387c478bdstevel@tonic-gate}
4397c478bdstevel@tonic-gate
4407c478bdstevel@tonic-gatessize_t
4417c478bdstevel@tonic-gatescf_limit(uint32_t limit)
4427c478bdstevel@tonic-gate{
4437c478bdstevel@tonic-gate	switch (limit) {
4447c478bdstevel@tonic-gate	case SCF_LIMIT_MAX_NAME_LENGTH:
4457c478bdstevel@tonic-gate	case SCF_LIMIT_MAX_PG_TYPE_LENGTH:
4467c478bdstevel@tonic-gate		return (REP_PROTOCOL_NAME_LEN - 1);
4477c478bdstevel@tonic-gate	case SCF_LIMIT_MAX_VALUE_LENGTH:
4487c478bdstevel@tonic-gate		return (REP_PROTOCOL_VALUE_LEN - 1);
4497c478bdstevel@tonic-gate	case SCF_LIMIT_MAX_FMRI_LENGTH:
4507c478bdstevel@tonic-gate		return (SCF_FMRI_PREFIX_MAX_LEN +
4517c478bdstevel@tonic-gate		    sizeof (SCF_FMRI_SCOPE_PREFIX) - 1 +
4527c478bdstevel@tonic-gate		    sizeof (SCF_FMRI_SCOPE_SUFFIX) - 1 +
4537c478bdstevel@tonic-gate		    sizeof (SCF_FMRI_SERVICE_PREFIX) - 1 +
4547c478bdstevel@tonic-gate		    sizeof (SCF_FMRI_INSTANCE_PREFIX) - 1 +
4557c478bdstevel@tonic-gate		    sizeof (SCF_FMRI_PROPERTYGRP_PREFIX) - 1 +
4567c478bdstevel@tonic-gate		    sizeof (SCF_FMRI_PROPERTY_PREFIX) - 1 +
4577c478bdstevel@tonic-gate		    5 * (REP_PROTOCOL_NAME_LEN - 1));
4587c478bdstevel@tonic-gate	default:
4597c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
4607c478bdstevel@tonic-gate	}
4617c478bdstevel@tonic-gate}
4627c478bdstevel@tonic-gate
4637c478bdstevel@tonic-gatestatic size_t
4647c478bdstevel@tonic-gatescf_opaque_decode(char *out_arg, const char *in, size_t max_out)
4657c478bdstevel@tonic-gate{
4667c478bdstevel@tonic-gate	char a, b;
4677c478bdstevel@tonic-gate	char *out = out_arg;
4687c478bdstevel@tonic-gate
4697c478bdstevel@tonic-gate	while (max_out > 0 && (a = in[0]) != 0 && (b = in[1]) != 0) {
4707c478bdstevel@tonic-gate		in += 2;
4717c478bdstevel@tonic-gate
4727c478bdstevel@tonic-gate		if (a >= '0' && a <= '9')
4737c478bdstevel@tonic-gate			a -= '0';
4747c478bdstevel@tonic-gate		else if (a >= 'a' && a <= 'f')
4757c478bdstevel@tonic-gate			a = a - 'a' + 10;
4767c478bdstevel@tonic-gate		else if (a >= 'A' && a <= 'F')
4777c478bdstevel@tonic-gate			a = a - 'A' + 10;
4787c478bdstevel@tonic-gate		else
4797c478bdstevel@tonic-gate			break;
4807c478bdstevel@tonic-gate
4817c478bdstevel@tonic-gate		if (b >= '0' && b <= '9')
4827c478bdstevel@tonic-gate			b -= '0';
4837c478bdstevel@tonic-gate		else if (b >= 'a' && b <= 'f')
4847c478bdstevel@tonic-gate			b = b - 'a' + 10;
4857c478bdstevel@tonic-gate		else if (b >= 'A' && b <= 'F')
4867c478bdstevel@tonic-gate			b = b - 'A' + 10;
4877c478bdstevel@tonic-gate		else
4887c478bdstevel@tonic-gate			break;
4897c478bdstevel@tonic-gate
4907c478bdstevel@tonic-gate		*out++ = (a << 4) | b;
4917c478bdstevel@tonic-gate		max_out--;
4927c478bdstevel@tonic-gate	}
4937c478bdstevel@tonic-gate
4947c478bdstevel@tonic-gate	return (out - out_arg);
4957c478bdstevel@tonic-gate}
4967c478bdstevel@tonic-gate
4977c478bdstevel@tonic-gatestatic size_t
4987c478bdstevel@tonic-gatescf_opaque_encode(char *out_arg, const char *in_arg, size_t in_sz)
4997c478bdstevel@tonic-gate{
5007c478bdstevel@tonic-gate	uint8_t *in = (uint8_t *)in_arg;
5017c478bdstevel@tonic-gate	uint8_t *end = in + in_sz;
5027c478bdstevel@tonic-gate	char *out = out_arg;
5037c478bdstevel@tonic-gate
5047c478bdstevel@tonic-gate	if (out == NULL)
5057c478bdstevel@tonic-gate		return (2 * in_sz);
5067c478bdstevel@tonic-gate
5077c478bdstevel@tonic-gate	while (in < end) {
5087c478bdstevel@tonic-gate		uint8_t c = *in++;
5097c478bdstevel@tonic-gate
5107c478bdstevel@tonic-gate		uint8_t a = (c & 0xf0) >> 4;
5117c478bdstevel@tonic-gate		uint8_t b = (c & 0x0f);
5127c478bdstevel@tonic-gate
5137c478bdstevel@tonic-gate		if (a <= 9)
5147c478bdstevel@tonic-gate			*out++ = a + '0';
5157c478bdstevel@tonic-gate		else
5167c478bdstevel@tonic-gate			*out++ = a + 'a' - 10;
5177c478bdstevel@tonic-gate
5187c478bdstevel@tonic-gate		if (b <= 9)
5197c478bdstevel@tonic-gate			*out++ = b + '0';
5207c478bdstevel@tonic-gate		else
5217c478bdstevel@tonic-gate			*out++ = b + 'a' - 10;
5227c478bdstevel@tonic-gate	}
5237c478bdstevel@tonic-gate
5247c478bdstevel@tonic-gate	*out = 0;
5257c478bdstevel@tonic-gate
5267c478bdstevel@tonic-gate	return (out - out_arg);
5277c478bdstevel@tonic-gate}
5287c478bdstevel@tonic-gate
5297c478bdstevel@tonic-gatestatic void
5307c478bdstevel@tonic-gatehandle_do_close(scf_handle_t *h)
5317c478bdstevel@tonic-gate{
5327c478bdstevel@tonic-gate	assert(MUTEX_HELD(&h->rh_lock));
5337c478bdstevel@tonic-gate	assert(h->rh_doorfd != -1);
5347c478bdstevel@tonic-gate
5357c478bdstevel@tonic-gate	/*
5367c478bdstevel@tonic-gate	 * if there are any active FD users, we just move the FD over
5377c478bdstevel@tonic-gate	 * to rh_doorfd_old -- they'll close it when they finish.
5387c478bdstevel@tonic-gate	 */
5397c478bdstevel@tonic-gate	if (h->rh_fd_users > 0) {
5407c478bdstevel@tonic-gate		h->rh_doorfd_old = h->rh_doorfd;
5417c478bdstevel@tonic-gate		h->rh_doorfd = -1;
5427c478bdstevel@tonic-gate	} else {
5437c478bdstevel@tonic-gate		assert(h->rh_doorfd_old == -1);
5447c478bdstevel@tonic-gate		(void) close(h->rh_doorfd);
5457c478bdstevel@tonic-gate		h->rh_doorfd = -1;
5467c478bdstevel@tonic-gate	}
5477c478bdstevel@tonic-gate}
5487c478bdstevel@tonic-gate
5497c478bdstevel@tonic-gate/*
5507c478bdstevel@tonic-gate * Check if a handle is currently bound.  fork()ing implicitly unbinds
5517c478bdstevel@tonic-gate * the handle in the child.
5527c478bdstevel@tonic-gate */
5537c478bdstevel@tonic-gatestatic int
5547c478bdstevel@tonic-gatehandle_is_bound(scf_handle_t *h)
5557c478bdstevel@tonic-gate{
5567c478bdstevel@tonic-gate	assert(MUTEX_HELD(&h->rh_lock));
5577c478bdstevel@tonic-gate
5587c478bdstevel@tonic-gate	if (h->rh_doorfd == -1)
5597c478bdstevel@tonic-gate		return (0);
5607c478bdstevel@tonic-gate
5617c478bdstevel@tonic-gate	if (getpid() == h->rh_doorpid)
5627c478bdstevel@tonic-gate		return (1);
5637c478bdstevel@tonic-gate
5647c478bdstevel@tonic-gate	/* forked since our last bind -- initiate handle close */
5657c478bdstevel@tonic-gate	handle_do_close(h);
5667c478bdstevel@tonic-gate	return (0);
5677c478bdstevel@tonic-gate}
5687c478bdstevel@tonic-gate
5697c478bdstevel@tonic-gatestatic int
5708918dffjwadamshandle_has_server_locked(scf_handle_t *h)
5717c478bdstevel@tonic-gate{
5727c478bdstevel@tonic-gate	door_info_t i;
5738918dffjwadams	assert(MUTEX_HELD(&h->rh_lock));
5748918dffjwadams
5758918dffjwadams	return (handle_is_bound(h) && door_info(h->rh_doorfd, &i) != -1 &&
5768918dffjwadams	    i.di_target != -1);
5778918dffjwadams}
5788918dffjwadams
5798918dffjwadamsstatic int
5808918dffjwadamshandle_has_server(scf_handle_t *h)
5818918dffjwadams{
5827c478bdstevel@tonic-gate	int ret;
5837c478bdstevel@tonic-gate
5847c478bdstevel@tonic-gate	(void) pthread_mutex_lock(&h->rh_lock);
5858918dffjwadams	ret = handle_has_server_locked(h);
5867c478bdstevel@tonic-gate	(void) pthread_mutex_unlock(&h->rh_lock);
5877c478bdstevel@tonic-gate
5887c478bdstevel@tonic-gate	return (ret);
5897c478bdstevel@tonic-gate}
5907c478bdstevel@tonic-gate
5917c478bdstevel@tonic-gate/*
5927c478bdstevel@tonic-gate * This makes a door request on the client door associated with handle h.
5937c478bdstevel@tonic-gate * It will automatically retry calls which fail on EINTR.  If h is not bound,
5947c478bdstevel@tonic-gate * returns NOT_BOUND.  If the door call fails or the server response is too
5957c478bdstevel@tonic-gate * small, returns CALL_FAILED.  If the server response is too big, truncates the
5967c478bdstevel@tonic-gate * response and returns RESULT_TOO_BIG.  Otherwise, the size of the result is
5977c478bdstevel@tonic-gate * returned.
5987c478bdstevel@tonic-gate */
5997c478bdstevel@tonic-gatestatic ssize_t
6007c478bdstevel@tonic-gatemake_door_call(scf_handle_t *h, const void *req, size_t req_sz,
6017c478bdstevel@tonic-gate    void *res, size_t res_sz)
6027c478bdstevel@tonic-gate{
6037c478bdstevel@tonic-gate	door_arg_t arg;
6047c478bdstevel@tonic-gate	int r;
6057c478bdstevel@tonic-gate
6067c478bdstevel@tonic-gate	assert(MUTEX_HELD(&h->rh_lock));
6077c478bdstevel@tonic-gate
6087c478bdstevel@tonic-gate	if (!handle_is_bound(h)) {
6097c478bdstevel@tonic-gate		return (NOT_BOUND);
6107c478bdstevel@tonic-gate	}
6117c478bdstevel@tonic-gate
6127c478bdstevel@tonic-gate	arg.data_ptr = (void *)req;
6137c478bdstevel@tonic-gate	arg.data_size = req_sz;
6147c478bdstevel@tonic-gate	arg.desc_ptr = NULL;
6157c478bdstevel@tonic-gate	arg.desc_num = 0;
6167c478bdstevel@tonic-gate	arg.rbuf = res;
6177c478bdstevel@tonic-gate	arg.rsize = res_sz;
6187c478bdstevel@tonic-gate
6197c478bdstevel@tonic-gate	while ((r = door_call(h->rh_doorfd, &arg)) < 0) {
6207c478bdstevel@tonic-gate		if (errno != EINTR)
6217c478bdstevel@tonic-gate			break;
6227c478bdstevel@tonic-gate	}
6237c478bdstevel@tonic-gate
6247c478bdstevel@tonic-gate	if (r < 0) {
6257c478bdstevel@tonic-gate		return (CALL_FAILED);
6267c478bdstevel@tonic-gate	}
6277c478bdstevel@tonic-gate
6287c478bdstevel@tonic-gate	if (arg.desc_num > 0) {
6297c478bdstevel@tonic-gate		while (arg.desc_num > 0) {
6307c478bdstevel@tonic-gate			if (arg.desc_ptr->d_attributes & DOOR_DESCRIPTOR) {
6317c478bdstevel@tonic-gate				int cfd = arg.desc_ptr->d_data.d_desc.d_id;
6327c478bdstevel@tonic-gate				(void) close(cfd);
6337c478bdstevel@tonic-gate			}
6347c478bdstevel@tonic-gate			arg.desc_ptr++;
6357c478bdstevel@tonic-gate			arg.desc_num--;
6367c478bdstevel@tonic-gate		}
6377c478bdstevel@tonic-gate	}
6387c478bdstevel@tonic-gate	if (arg.data_ptr != res && arg.data_size > 0)
6397c478bdstevel@tonic-gate		(void) memmove(res, arg.data_ptr, MIN(arg.data_size, res_sz));
6407c478bdstevel@tonic-gate
6417c478bdstevel@tonic-gate	if (arg.rbuf != res)
6427c478bdstevel@tonic-gate		(void) munmap(arg.rbuf, arg.rsize);
6437c478bdstevel@tonic-gate
6447c478bdstevel@tonic-gate	if (arg.data_size > res_sz)
6457c478bdstevel@tonic-gate		return (RESULT_TOO_BIG);
6467c478bdstevel@tonic-gate
6477c478bdstevel@tonic-gate	if (arg.data_size < sizeof (uint32_t))
6487c478bdstevel@tonic-gate		return (CALL_FAILED);
6497c478bdstevel@tonic-gate
6507c478bdstevel@tonic-gate	return (arg.data_size);
6517c478bdstevel@tonic-gate}
6527c478bdstevel@tonic-gate
6537c478bdstevel@tonic-gate/*
6547c478bdstevel@tonic-gate * Should only be used when r < 0.
6557c478bdstevel@tonic-gate */
6567c478bdstevel@tonic-gate#define	DOOR_ERRORS_BLOCK(r)	{					\
6577c478bdstevel@tonic-gate	switch (r) {							\
6587c478bdstevel@tonic-gate	case NOT_BOUND:							\
6597c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_NOT_BOUND));		\
6607c478bdstevel@tonic-gate									\
6617c478bdstevel@tonic-gate	case CALL_FAILED:						\
6627c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_CONNECTION_BROKEN));	\
6637c478bdstevel@tonic-gate									\
6647c478bdstevel@tonic-gate	case RESULT_TOO_BIG:						\
6657c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_INTERNAL));		\
6667c478bdstevel@tonic-gate									\
6677c478bdstevel@tonic-gate	default:							\
6687c478bdstevel@tonic-gate		assert(r == NOT_BOUND || r == CALL_FAILED ||		\
6697c478bdstevel@tonic-gate		    r == RESULT_TOO_BIG);				\
6707c478bdstevel@tonic-gate		abort();						\
6717c478bdstevel@tonic-gate	}								\
6727c478bdstevel@tonic-gate}
6737c478bdstevel@tonic-gate
6747c478bdstevel@tonic-gate/*
6757c478bdstevel@tonic-gate * Like make_door_call(), but takes an fd instead of a handle, and expects
6767c478bdstevel@tonic-gate * a single file descriptor, returned via res_fd.
6777c478bdstevel@tonic-gate *
6787c478bdstevel@tonic-gate * If no file descriptor is returned, *res_fd == -1.
6797c478bdstevel@tonic-gate */
6807c478bdstevel@tonic-gatestatic int
6817c478bdstevel@tonic-gatemake_door_call_retfd(int fd, const void *req, size_t req_sz, void *res,
6827c478bdstevel@tonic-gate    size_t res_sz, int *res_fd)
6837c478bdstevel@tonic-gate{
6847c478bdstevel@tonic-gate	door_arg_t arg;
6857c478bdstevel@tonic-gate	int r;
6867c478bdstevel@tonic-gate	char rbuf[256];
6877c478bdstevel@tonic-gate
6887c478bdstevel@tonic-gate	*res_fd = -1;
6897c478bdstevel@tonic-gate
6907c478bdstevel@tonic-gate	if (fd == -1)
6917c478bdstevel@tonic-gate		return (NOT_BOUND);
6927c478bdstevel@tonic-gate
6937c478bdstevel@tonic-gate	arg.data_ptr = (void *)req;
6947c478bdstevel@tonic-gate	arg.data_size = req_sz;
6957c478bdstevel@tonic-gate	arg.desc_ptr = NULL;
6967c478bdstevel@tonic-gate	arg.desc_num = 0;
6977c478bdstevel@tonic-gate	arg.rbuf = rbuf;
6987c478bdstevel@tonic-gate	arg.rsize = sizeof (rbuf);
6997c478bdstevel@tonic-gate
7007c478bdstevel@tonic-gate	while ((r = door_call(fd, &arg)) < 0) {
7017c478bdstevel@tonic-gate		if (errno != EINTR)
7027c478bdstevel@tonic-gate			break;
7037c478bdstevel@tonic-gate	}
7047c478bdstevel@tonic-gate
7057c478bdstevel@tonic-gate	if (r < 0)
7067c478bdstevel@tonic-gate		return (CALL_FAILED);
7077c478bdstevel@tonic-gate
7087c478bdstevel@tonic-gate	if (arg.desc_num > 1) {
7097c478bdstevel@tonic-gate		while (arg.desc_num > 0) {
7107c478bdstevel@tonic-gate			if (arg.desc_ptr->d_attributes & DOOR_DESCRIPTOR) {
7117c478bdstevel@tonic-gate				int cfd =
7127c478bdstevel@tonic-gate				    arg.desc_ptr->d_data.d_desc.d_descriptor;
7137c478bdstevel@tonic-gate				(void) close(cfd);
7147c478bdstevel@tonic-gate			}
7157c478bdstevel@tonic-gate			arg.desc_ptr++;
7167c478bdstevel@tonic-gate			arg.desc_num--;
7177c478bdstevel@tonic-gate		}
7187c478bdstevel@tonic-gate	}
7197c478bdstevel@tonic-gate	if (arg.desc_num == 1 && arg.desc_ptr->d_attributes & DOOR_DESCRIPTOR)
7207c478bdstevel@tonic-gate		*res_fd = arg.desc_ptr->d_data.d_desc.d_descriptor;
7217c478bdstevel@tonic-gate
7227c478bdstevel@tonic-gate	if (arg.data_size > 0)
7237c478bdstevel@tonic-gate		(void) memmove(res, arg.data_ptr, MIN(arg.data_size, res_sz));
7247c478bdstevel@tonic-gate
7257c478bdstevel@tonic-gate	if (arg.rbuf != rbuf)
7267c478bdstevel@tonic-gate		(void) munmap(arg.rbuf, arg.rsize);
7277c478bdstevel@tonic-gate
7287c478bdstevel@tonic-gate	if (arg.data_size > res_sz)
7297c478bdstevel@tonic-gate		return (RESULT_TOO_BIG);
7307c478bdstevel@tonic-gate
7317c478bdstevel@tonic-gate	if (arg.data_size < sizeof (uint32_t))
7327c478bdstevel@tonic-gate		return (CALL_FAILED);
7337c478bdstevel@tonic-gate
7347c478bdstevel@tonic-gate	return (arg.data_size);
7357c478bdstevel@tonic-gate}
7367c478bdstevel@tonic-gate
7377c478bdstevel@tonic-gate/*
7387c478bdstevel@tonic-gate * Fails with
7397c478bdstevel@tonic-gate *   _VERSION_MISMATCH
7407c478bdstevel@tonic-gate *   _NO_MEMORY
7417c478bdstevel@tonic-gate */
7427c478bdstevel@tonic-gatescf_handle_t *
7437c478bdstevel@tonic-gatescf_handle_create(scf_version_t v)
7447c478bdstevel@tonic-gate{
7457c478bdstevel@tonic-gate	scf_handle_t *ret;
7467c478bdstevel@tonic-gate	int failed;
7477c478bdstevel@tonic-gate
7487c478bdstevel@tonic-gate	/*
7497c478bdstevel@tonic-gate	 * This will need to be revisited when we bump SCF_VERSION
7507c478bdstevel@tonic-gate	 */
7517c478bdstevel@tonic-gate	if (v != SCF_VERSION) {
7527c478bdstevel@tonic-gate		(void) scf_set_error(SCF_ERROR_VERSION_MISMATCH);
7537c478bdstevel@tonic-gate		return (NULL);
7547c478bdstevel@tonic-gate	}
7557c478bdstevel@tonic-gate
7567c478bdstevel@tonic-gate	if (!lowlevel_init()) {
7577c478bdstevel@tonic-gate		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
7587c478bdstevel@tonic-gate		return (NULL);
7597c478bdstevel@tonic-gate	}
7607c478bdstevel@tonic-gate
7617c478bdstevel@tonic-gate	ret = uu_zalloc(sizeof (*ret));
7627c478bdstevel@tonic-gate	if (ret == NULL) {
7637c478bdstevel@tonic-gate		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
7647c478bdstevel@tonic-gate		return (NULL);
7657c478bdstevel@tonic-gate	}
7667c478bdstevel@tonic-gate
7677c478bdstevel@tonic-gate	ret->rh_dataels = uu_list_create(datael_pool, ret, 0);
7687c478bdstevel@tonic-gate	ret->rh_iters = uu_list_create(iter_pool, ret, 0);
7697c478bdstevel@tonic-gate	if (ret->rh_dataels == NULL || ret->rh_iters == NULL) {
7707c478bdstevel@tonic-gate		if (ret->rh_dataels != NULL)
7717c478bdstevel@tonic-gate			uu_list_destroy(ret->rh_dataels);
7727c478bdstevel@tonic-gate		if (ret->rh_iters != NULL)
7737c478bdstevel@tonic-gate			uu_list_destroy(ret->rh_iters);
7747c478bdstevel@tonic-gate		uu_free(ret);
7757c478bdstevel@tonic-gate		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
7767c478bdstevel@tonic-gate		return (NULL);
7777c478bdstevel@tonic-gate	}
7787c478bdstevel@tonic-gate
7797c478bdstevel@tonic-gate	ret->rh_doorfd = -1;
7807c478bdstevel@tonic-gate	ret->rh_doorfd_old = -1;
7817c478bdstevel@tonic-gate	(void) pthread_mutex_init(&ret->rh_lock, NULL);
7827c478bdstevel@tonic-gate
7837c478bdstevel@tonic-gate	handle_hold_subhandles(ret, RH_HOLD_ALL);
7847c478bdstevel@tonic-gate
7857c478bdstevel@tonic-gate	failed = ((ret->rh_iter = scf_iter_create(ret)) == NULL ||
7867c478bdstevel@tonic-gate	    (ret->rh_scope = scf_scope_create(ret)) == NULL ||
7877c478bdstevel@tonic-gate	    (ret->rh_service = scf_service_create(ret)) == NULL ||
7887c478bdstevel@tonic-gate	    (ret->rh_instance = scf_instance_create(ret)) == NULL ||
7897c478bdstevel@tonic-gate	    (ret->rh_snapshot = scf_snapshot_create(ret)) == NULL ||
7907c478bdstevel@tonic-gate	    (ret->rh_snaplvl = scf_snaplevel_create(ret)) == NULL ||
7917c478bdstevel@tonic-gate	    (ret->rh_pg = scf_pg_create(ret)) == NULL ||
7927c478bdstevel@tonic-gate	    (ret->rh_property = scf_property_create(ret)) == NULL ||
7937c478bdstevel@tonic-gate	    (ret->rh_value = scf_value_create(ret)) == NULL);
7947c478bdstevel@tonic-gate
7957c478bdstevel@tonic-gate	/*
7967c478bdstevel@tonic-gate	 * these subhandles count as internal references, not external ones.
7977c478bdstevel@tonic-gate	 */
7987c478bdstevel@tonic-gate	ret->rh_intrefs = ret->rh_extrefs;
7997c478bdstevel@tonic-gate	ret->rh_extrefs = 0;
8007c478bdstevel@tonic-gate	handle_rele_subhandles(ret, RH_HOLD_ALL);
8017c478bdstevel@tonic-gate
8027c478bdstevel@tonic-gate	if (failed) {
8037c478bdstevel@tonic-gate		scf_handle_destroy(ret);
8047c478bdstevel@tonic-gate		(void) scf_set_error(SCF_ERROR_NO_MEMORY);
8057c478bdstevel@tonic-gate		return (NULL);
8067c478bdstevel@tonic-gate	}
8077c478bdstevel@tonic-gate
8087c478bdstevel@tonic-gate	scf_value_set_count(ret->rh_value, default_debug);
8097c478bdstevel@tonic-gate	(void) scf_handle_decorate(ret, "debug", ret->rh_value);
8107c478bdstevel@tonic-gate
8117c478bdstevel@tonic-gate	return (ret);
8127c478bdstevel@tonic-gate}
8137c478bdstevel@tonic-gate
814f6e214cGavin Maltby/*
815f6e214cGavin Maltby * Fails with
816f6e214cGavin Maltby *   _NO_MEMORY
817f6e214cGavin Maltby *   _NO_SERVER - server door could not be open()ed
818f6e214cGavin Maltby *		  door call failed
819f6e214cGavin Maltby *		  door_info() failed
820f6e214cGavin Maltby *   _VERSION_MISMATCH - server returned bad file descriptor
821f6e214cGavin Maltby *			 server claimed bad request
822f6e214cGavin Maltby *			 server reported version mismatch
823f6e214cGavin Maltby *			 server refused with unknown reason
824f6e214cGavin Maltby *   _INVALID_ARGUMENT
825f6e214cGavin Maltby *   _NO_RESOURCES - server is out of memory
826f6e214cGavin Maltby *   _PERMISSION_DENIED
827f6e214cGavin Maltby *   _INTERNAL - could not set up entities or iters
828f6e214cGavin Maltby *		 server response too big
829f6e214cGavin Maltby */
830f6e214cGavin Maltbyscf_handle_t *
831f6e214cGavin Maltby_scf_handle_create_and_bind(scf_version_t ver)
832f6e214cGavin Maltby{
833f6e214cGavin Maltby	scf_handle_t *h;
834f6e214cGavin Maltby
835f6e214cGavin Maltby	h = scf_handle_create(ver);
836f6e214cGavin Maltby	if (h == NULL)
837f6e214cGavin Maltby		return (NULL);
838f6e214cGavin Maltby
839f6e214cGavin Maltby	if (scf_handle_bind(h) == -1) {
840f6e214cGavin Maltby		scf_handle_destroy(h);
841f6e214cGavin Maltby		return (NULL);
842f6e214cGavin Maltby	}
843f6e214cGavin Maltby	return (h);
844f6e214cGavin Maltby}
845f6e214cGavin Maltby
8467c478bdstevel@tonic-gateint
8477c478bdstevel@tonic-gatescf_handle_decorate(scf_handle_t *handle, const char *name, scf_value_t *v)
8487c478bdstevel@tonic-gate{
8497c478bdstevel@tonic-gate	if (v != SCF_DECORATE_CLEAR && handle != v->value_handle)
8507c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
8517c478bdstevel@tonic-gate
8527c478bdstevel@tonic-gate	(void) pthread_mutex_lock(&handle->rh_lock);
8537c478bdstevel@tonic-gate	if (handle_is_bound(handle)) {
8547c478bdstevel@tonic-gate		(void) pthread_mutex_unlock(&handle->rh_lock);
8557c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_IN_USE));
8567c478bdstevel@tonic-gate	}
8577c478bdstevel@tonic-gate	(void) pthread_mutex_unlock(&handle->rh_lock);
8587c478bdstevel@tonic-gate
8597c478bdstevel@tonic-gate	if (strcmp(name, "debug") == 0) {
8607c478bdstevel@tonic-gate		if (v == SCF_DECORATE_CLEAR) {
8617c478bdstevel@tonic-gate			(void) pthread_mutex_lock(&handle->rh_lock);
8627c478bdstevel@tonic-gate			handle->rh_debug = 0;
8637c478bdstevel@tonic-gate			(void) pthread_mutex_unlock(&handle->rh_lock);
8647c478bdstevel@tonic-gate		} else {
8657c478bdstevel@tonic-gate			uint64_t val;
8667c478bdstevel@tonic-gate			if (scf_value_get_count(v, &val) < 0)
8677c478bdstevel@tonic-gate				return (-1);		/* error already set */
8687c478bdstevel@tonic-gate
8697c478bdstevel@tonic-gate			(void) pthread_mutex_lock(&handle->rh_lock);
8707c478bdstevel@tonic-gate			handle->rh_debug = (uid_t)val;
8717c478bdstevel@tonic-gate			(void) pthread_mutex_unlock(&handle->rh_lock);
8727c478bdstevel@tonic-gate		}
8737c478bdstevel@tonic-gate		return (0);
8747c478bdstevel@tonic-gate	}
8757c478bdstevel@tonic-gate	if (strcmp(name, "door_path") == 0) {
8767c478bdstevel@tonic-gate		char name[sizeof (handle->rh_doorpath)];
8777c478bdstevel@tonic-gate
8787c478bdstevel@tonic-gate		if (v == SCF_DECORATE_CLEAR) {
8797c478bdstevel@tonic-gate			(void) pthread_mutex_lock(&handle->rh_lock);
8807c478bdstevel@tonic-gate			handle->rh_doorpath[0] = 0;
8817c478bdstevel@tonic-gate			(void) pthread_mutex_unlock(&handle->rh_lock);
8827c478bdstevel@tonic-gate		} else {
8837c478bdstevel@tonic-gate			ssize_t len;
8847c478bdstevel@tonic-gate
8857c478bdstevel@tonic-gate			if ((len = scf_value_get_astring(v, name,
8867c478bdstevel@tonic-gate			    sizeof (name))) < 0) {
8877c478bdstevel@tonic-gate				return (-1);		/* error already set */
8887c478bdstevel@tonic-gate			}
8897c478bdstevel@tonic-gate			if (len == 0 || len >= sizeof (name)) {
8907c478bdstevel@tonic-gate				return (scf_set_error(
8917c478bdstevel@tonic-gate				    SCF_ERROR_INVALID_ARGUMENT));
8927c478bdstevel@tonic-gate			}
8937c478bdstevel@tonic-gate			(void) pthread_mutex_lock(&handle->rh_lock);
8947c478bdstevel@tonic-gate			(void) strlcpy(handle->rh_doorpath, name,
8957c478bdstevel@tonic-gate			    sizeof (handle->rh_doorpath));
8967c478bdstevel@tonic-gate			(void) pthread_mutex_unlock(&handle->rh_lock);
8977c478bdstevel@tonic-gate		}
8987c478bdstevel@tonic-gate		return (0);
8997c478bdstevel@tonic-gate	}
900048b027Bryan Cantrill
901048b027Bryan Cantrill	if (strcmp(name, "zone") == 0) {
902048b027Bryan Cantrill		char zone[MAXPATHLEN], root[MAXPATHLEN], door[MAXPATHLEN];
903048b027Bryan Cantrill		static int (*zone_get_rootpath)(char *, char *, size_t);
904048b027Bryan Cantrill		ssize_t len;
905048b027Bryan Cantrill
906048b027Bryan Cantrill		/*
907048b027Bryan Cantrill		 * In order to be able to set the zone on a handle, we want
908048b027Bryan Cantrill		 * to determine the zone's path, which requires us to call into
909048b027Bryan Cantrill		 * libzonecfg -- but libzonecfg.so links against libscf.so so
910048b027Bryan Cantrill		 * we must not explicitly link to it.  To circumvent the
911048b027Bryan Cantrill		 * circular dependency, we will pull it in here via dlopen().
912048b027Bryan Cantrill		 */
913048b027Bryan Cantrill		if (zone_get_rootpath == NULL) {
914048b027Bryan Cantrill			void *dl = dlopen("libzonecfg.so.1", RTLD_LAZY), *sym;
915048b027Bryan Cantrill
916048b027Bryan Cantrill			if (dl == NULL)
917048b027Bryan Cantrill				return (scf_set_error(SCF_ERROR_NOT_FOUND));
918048b027Bryan Cantrill
919048b027Bryan Cantrill			if ((sym = dlsym(dl, "zone_get_rootpath")) == NULL) {
920048b027Bryan Cantrill				(void) dlclose(dl);
921048b027Bryan Cantrill				return (scf_set_error(SCF_ERROR_INTERNAL));
922048b027Bryan Cantrill			}
923048b027Bryan Cantrill
924048b027Bryan Cantrill			zone_get_rootpath = (int(*)(char *, char *, size_t))sym;
925048b027Bryan Cantrill		}
926048b027Bryan Cantrill
927048b027Bryan Cantrill		if (v == SCF_DECORATE_CLEAR) {
928048b027Bryan Cantrill			(void) pthread_mutex_lock(&handle->rh_lock);
929048b027Bryan Cantrill			handle->rh_doorpath[0] = 0;
930048b027Bryan Cantrill			(void) pthread_mutex_unlock(&handle->rh_lock);
931048b027Bryan Cantrill
932048b027Bryan Cantrill			return (0);
933048b027Bryan Cantrill		}
934048b027Bryan Cantrill
935048b027Bryan Cantrill		if ((len = scf_value_get_astring(v, zone, sizeof (zone))) < 0)
936048b027Bryan Cantrill			return (-1);
937048b027Bryan Cantrill
938048b027Bryan Cantrill		if (len == 0 || len >= sizeof (zone))
939048b027Bryan Cantrill			return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
940048b027Bryan Cantrill
941048b027Bryan Cantrill		if (zone_get_rootpath(zone, root, sizeof (root)) != Z_OK) {
942048b027Bryan Cantrill			if (strcmp(zone, GLOBAL_ZONENAME) == 0) {
943048b027Bryan Cantrill				root[0] = '\0';
944048b027Bryan Cantrill			} else {
945048b027Bryan Cantrill				return (scf_set_error(SCF_ERROR_NOT_FOUND));
946048b027Bryan Cantrill			}
947048b027Bryan Cantrill		}
948048b027Bryan Cantrill
949048b027Bryan Cantrill		if (snprintf(door, sizeof (door), "%s/%s", root,
950048b027Bryan Cantrill		    default_door_path) >= sizeof (door))
951048b027Bryan Cantrill			return (scf_set_error(SCF_ERROR_INTERNAL));
952048b027Bryan Cantrill
953048b027Bryan Cantrill		(void) pthread_mutex_lock(&handle->rh_lock);
954048b027Bryan Cantrill		(void) strlcpy(handle->rh_doorpath, door,
955048b027Bryan Cantrill		    sizeof (handle->rh_doorpath));
956048b027Bryan Cantrill		(void) pthread_mutex_unlock(&handle->rh_lock);
957048b027Bryan Cantrill
958048b027Bryan Cantrill		return (0);
959048b027Bryan Cantrill	}
960048b027Bryan Cantrill
9617c478bdstevel@tonic-gate	return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
9627c478bdstevel@tonic-gate}
9637c478bdstevel@tonic-gate
9647c478bdstevel@tonic-gate/*
9657c478bdstevel@tonic-gate * fails with INVALID_ARGUMENT and HANDLE_MISMATCH.
9667c478bdstevel@tonic-gate */
9677c478bdstevel@tonic-gateint
9687c478bdstevel@tonic-gate_scf_handle_decorations(scf_handle_t *handle, scf_decoration_func *f,
9697c478bdstevel@tonic-gate    scf_value_t *v, void *data)
9707c478bdstevel@tonic-gate{
9717c478bdstevel@tonic-gate	scf_decoration_info_t i;
9727c478bdstevel@tonic-gate	char name[sizeof (handle->rh_doorpath)];
9737c478bdstevel@tonic-gate	uint64_t debug;
9747c478bdstevel@tonic-gate
9757c478bdstevel@tonic-gate	if (f == NULL || v == NULL)
9767c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
9777c478bdstevel@tonic-gate
9787c478bdstevel@tonic-gate	if (v->value_handle != handle)
9797c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_HANDLE_MISMATCH));
9807c478bdstevel@tonic-gate
9817c478bdstevel@tonic-gate	i.sdi_name = (const char *)"debug";
9827c478bdstevel@tonic-gate	i.sdi_type = SCF_TYPE_COUNT;
9837c478bdstevel@tonic-gate	(void) pthread_mutex_lock(&handle->rh_lock);
9847c478bdstevel@tonic-gate	debug = handle->rh_debug;
9857c478bdstevel@tonic-gate	(void) pthread_mutex_unlock(&handle->rh_lock);
9867c478bdstevel@tonic-gate	if (debug != 0) {
9877c478bdstevel@tonic-gate		scf_value_set_count(v, debug);
9887c478bdstevel@tonic-gate		i.sdi_value = v;
9897c478bdstevel@tonic-gate	} else {
9907c478bdstevel@tonic-gate		i.sdi_value = SCF_DECORATE_CLEAR;
9917c478bdstevel@tonic-gate	}
9927c478bdstevel@tonic-gate
9937c478bdstevel@tonic-gate	if ((*f)(&i, data) == 0)
9947c478bdstevel@tonic-gate		return (0);
9957c478bdstevel@tonic-gate
9967c478bdstevel@tonic-gate	i.sdi_name = (const char *)"door_path";
9977c478bdstevel@tonic-gate	i.sdi_type = SCF_TYPE_ASTRING;
9987c478bdstevel@tonic-gate	(void) pthread_mutex_lock(&handle->rh_lock);
9997c478bdstevel@tonic-gate	(void) strlcpy(name, handle->rh_doorpath, sizeof (name));
10007c478bdstevel@tonic-gate	(void) pthread_mutex_unlock(&handle->rh_lock);
10017c478bdstevel@tonic-gate	if (name[0] != 0) {
10027c478bdstevel@tonic-gate		(void) scf_value_set_astring(v, name);
10037c478bdstevel@tonic-gate		i.sdi_value = v;
10047c478bdstevel@tonic-gate	} else {
10057c478bdstevel@tonic-gate		i.sdi_value = SCF_DECORATE_CLEAR;
10067c478bdstevel@tonic-gate	}
10077c478bdstevel@tonic-gate
10087c478bdstevel@tonic-gate	if ((*f)(&i, data) == 0)
10097c478bdstevel@tonic-gate		return (0);
10107c478bdstevel@tonic-gate
10117c478bdstevel@tonic-gate	return (1);
10127c478bdstevel@tonic-gate}
10137c478bdstevel@tonic-gate
10147c478bdstevel@tonic-gate/*
10157c478bdstevel@tonic-gate * Fails if handle is not bound.
10167c478bdstevel@tonic-gate */
10177c478bdstevel@tonic-gatestatic int
10187c478bdstevel@tonic-gatehandle_unbind_unlocked(scf_handle_t *handle)
10197c478bdstevel@tonic-gate{
10207c478bdstevel@tonic-gate	rep_protocol_request_t request;
10217c478bdstevel@tonic-gate	rep_protocol_response_t response;
10227c478bdstevel@tonic-gate
10237c478bdstevel@tonic-gate	if (!handle_is_bound(handle))
10247c478bdstevel@tonic-gate		return (-1);
10257c478bdstevel@tonic-gate
10267c478bdstevel@tonic-gate	request.rpr_request = REP_PROTOCOL_CLOSE;
10277c478bdstevel@tonic-gate
10287c478bdstevel@tonic-gate	(void) make_door_call(handle, &request, sizeof (request),
10297c478bdstevel@tonic-gate	    &response, sizeof (response));
10307c478bdstevel@tonic-gate
10317c478bdstevel@tonic-gate	handle_do_close(handle);
10327c478bdstevel@tonic-gate
10337c478bdstevel@tonic-gate	return (SCF_SUCCESS);
10347c478bdstevel@tonic-gate}
10357c478bdstevel@tonic-gate
10367c478bdstevel@tonic-gate/*
10377c478bdstevel@tonic-gate * Fails with
10387c478bdstevel@tonic-gate *   _HANDLE_DESTROYED - dp's handle has been destroyed
10397c478bdstevel@tonic-gate *   _INTERNAL - server response too big
10407c478bdstevel@tonic-gate *		 entity already set up with different type
10417c478bdstevel@tonic-gate *   _NO_RESOURCES - server out of memory
10427c478bdstevel@tonic-gate */
10437c478bdstevel@tonic-gatestatic int
10447c478bdstevel@tonic-gatedatael_attach(scf_datael_t *dp)
10457c478bdstevel@tonic-gate{
10467c478bdstevel@tonic-gate	scf_handle_t *h = dp->rd_handle;
10477c478bdstevel@tonic-gate
10487c478bdstevel@tonic-gate	struct rep_protocol_entity_setup request;
10497c478bdstevel@tonic-gate	rep_protocol_response_t response;
10507c478bdstevel@tonic-gate	ssize_t r;
10517c478bdstevel@tonic-gate
10527c478bdstevel@tonic-gate	assert(MUTEX_HELD(&h->rh_lock));
10537c478bdstevel@tonic-gate
10547c478bdstevel@tonic-gate	dp->rd_reset = 0;		/* setup implicitly resets */
10557c478bdstevel@tonic-gate
10567c478bdstevel@tonic-gate	if (h->rh_flags & HANDLE_DEAD)
10577c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_HANDLE_DESTROYED));
10587c478bdstevel@tonic-gate
10597c478bdstevel@tonic-gate	if (!handle_is_bound(h))
10607c478bdstevel@tonic-gate		return (SCF_SUCCESS);		/* nothing to do */
10617c478bdstevel@tonic-gate
10627c478bdstevel@tonic-gate	request.rpr_request = REP_PROTOCOL_ENTITY_SETUP;
10637c478bdstevel@tonic-gate	request.rpr_entityid = dp->rd_entity;
10647c478bdstevel@tonic-gate	request.rpr_entitytype = dp->rd_type;
10657c478bdstevel@tonic-gate
10667c478bdstevel@tonic-gate	r = make_door_call(h, &request, sizeof (request),
10677c478bdstevel@tonic-gate	    &response, sizeof (response));
10687c478bdstevel@tonic-gate
10697c478bdstevel@tonic-gate	if (r == NOT_BOUND || r == CALL_FAILED)
10707c478bdstevel@tonic-gate		return (SCF_SUCCESS);
10717c478bdstevel@tonic-gate	if (r == RESULT_TOO_BIG)
10727c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_INTERNAL));
10737c478bdstevel@tonic-gate
10747c478bdstevel@tonic-gate	if (response.rpr_response != REP_PROTOCOL_SUCCESS)
10757c478bdstevel@tonic-gate		return (scf_set_error(proto_error(response.rpr_response)));
10767c478bdstevel@tonic-gate
10777c478bdstevel@tonic-gate	return (SCF_SUCCESS);
10787c478bdstevel@tonic-gate}
10797c478bdstevel@tonic-gate
10807c478bdstevel@tonic-gate/*
10817c478bdstevel@tonic-gate * Fails with
10827c478bdstevel@tonic-gate *   _HANDLE_DESTROYED - iter's handle has been destroyed
10837c478bdstevel@tonic-gate *   _INTERNAL - server response too big
10847c478bdstevel@tonic-gate *		 iter already existed
10857c478bdstevel@tonic-gate *   _NO_RESOURCES
10867c478bdstevel@tonic-gate */
10877c478bdstevel@tonic-gatestatic int
10887c478bdstevel@tonic-gateiter_attach(scf_iter_t *iter)
10897c478bdstevel@tonic-gate{
10907c478bdstevel@tonic-gate	scf_handle_t *h = iter->iter_handle;
10917c478bdstevel@tonic-gate	struct rep_protocol_iter_request request;
10927c478bdstevel@tonic-gate	struct rep_protocol_response response;
10937c478bdstevel@tonic-gate	int r;
10947c478bdstevel@tonic-gate
10957c478bdstevel@tonic-gate	assert(MUTEX_HELD(&h->rh_lock));
10967c478bdstevel@tonic-gate
10977c478bdstevel@tonic-gate	if (h->rh_flags & HANDLE_DEAD)
10987c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_HANDLE_DESTROYED));
10997c478bdstevel@tonic-gate
11007c478bdstevel@tonic-gate	if (!handle_is_bound(h))
11017c478bdstevel@tonic-gate		return (SCF_SUCCESS);		/* nothing to do */
11027c478bdstevel@tonic-gate
11037c478bdstevel@tonic-gate	request.rpr_request = REP_PROTOCOL_ITER_SETUP;
11047c478bdstevel@tonic-gate	request.rpr_iterid = iter->iter_id;
11057c478bdstevel@tonic-gate
11067c478bdstevel@tonic-gate	r = make_door_call(h, &request, sizeof (request),
11077c478bdstevel@tonic-gate	    &response, sizeof (response));
11087c478bdstevel@tonic-gate
11097c478bdstevel@tonic-gate	if (r == NOT_BOUND || r == CALL_FAILED)
11107c478bdstevel@tonic-gate		return (SCF_SUCCESS);
11117c478bdstevel@tonic-gate	if (r == RESULT_TOO_BIG)
11127c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_INTERNAL));
11137c478bdstevel@tonic-gate
11147c478bdstevel@tonic-gate	if (response.rpr_response != REP_PROTOCOL_SUCCESS)
11157c478bdstevel@tonic-gate		return (scf_set_error(proto_error(response.rpr_response)));
11167c478bdstevel@tonic-gate
11177c478bdstevel@tonic-gate	return (SCF_SUCCESS);
11187c478bdstevel@tonic-gate}
11197c478bdstevel@tonic-gate
11207c478bdstevel@tonic-gate/*
11217c478bdstevel@tonic-gate * Fails with
11227c478bdstevel@tonic-gate *   _IN_USE - handle already bound
11237c478bdstevel@tonic-gate *   _NO_SERVER - server door could not be open()ed
11247c478bdstevel@tonic-gate *		  door call failed
11257c478bdstevel@tonic-gate *		  door_info() failed
11267c478bdstevel@tonic-gate *   _VERSION_MISMATCH - server returned bad file descriptor
11277c478bdstevel@tonic-gate *			 server claimed bad request
11287c478bdstevel@tonic-gate *			 server reported version mismatch
11297c478bdstevel@tonic-gate *			 server refused with unknown reason
11307c478bdstevel@tonic-gate *   _INVALID_ARGUMENT
11317c478bdstevel@tonic-gate *   _NO_RESOURCES - server is out of memory
11327c478bdstevel@tonic-gate *   _PERMISSION_DENIED
11337c478bdstevel@tonic-gate *   _INTERNAL - could not set up entities or iters
11347c478bdstevel@tonic-gate *		 server response too big
11357c478bdstevel@tonic-gate *
11367c478bdstevel@tonic-gate * perhaps this should try multiple times.
11377c478bdstevel@tonic-gate */
11387c478bdstevel@tonic-gateint
11397c478bdstevel@tonic-gatescf_handle_bind(scf_handle_t *handle)
11407c478bdstevel@tonic-gate{
11417c478bdstevel@tonic-gate	scf_datael_t *el;
11427c478bdstevel@tonic-gate	scf_iter_t *iter;
11437c478bdstevel@tonic-gate
11447c478bdstevel@tonic-gate	pid_t pid;
11457c478bdstevel@tonic-gate	int fd;
11467c478bdstevel@tonic-gate	int res;
11477c478bdstevel@tonic-gate	door_info_t info;
11487c478bdstevel@tonic-gate	repository_door_request_t request;
11497c478bdstevel@tonic-gate	repository_door_response_t response;
11507c478bdstevel@tonic-gate	const char *door_name = default_door_path;
11517c478bdstevel@tonic-gate
11527c478bdstevel@tonic-gate	(void) pthread_mutex_lock(&handle->rh_lock);
11537c478bdstevel@tonic-gate	if (handle_is_bound(handle)) {
11547c478bdstevel@tonic-gate		(void) pthread_mutex_unlock(&handle->rh_lock);
11557c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_IN_USE));
11567c478bdstevel@tonic-gate	}
11577c478bdstevel@tonic-gate
11587c478bdstevel@tonic-gate	/* wait until any active fd users have cleared out */
1159a574db8raf	while (handle->rh_fd_users > 0) {
1160a574db8raf		int cancel_state;
1161a574db8raf
1162a574db8raf		(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
1163a574db8raf		    &cancel_state);
1164a574db8raf		(void) pthread_cond_wait(&handle->rh_cv, &handle->rh_lock);
1165a574db8raf		(void) pthread_setcancelstate(cancel_state, NULL);
1166a574db8raf	}
11677c478bdstevel@tonic-gate
11687c478bdstevel@tonic-gate	/* check again, since we had to drop the lock */
11697c478bdstevel@tonic-gate	if (handle_is_bound(handle)) {
11707c478bdstevel@tonic-gate		(void) pthread_mutex_unlock(&handle->rh_lock);
11717c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_IN_USE));
11727c478bdstevel@tonic-gate	}
11737c478bdstevel@tonic-gate
11747c478bdstevel@tonic-gate	assert(handle->rh_doorfd == -1 && handle->rh_doorfd_old == -1);
11757c478bdstevel@tonic-gate
11767c478bdstevel@tonic-gate	if (handle->rh_doorpath[0] != 0)
11777c478bdstevel@tonic-gate		door_name = handle->rh_doorpath;
11787c478bdstevel@tonic-gate
11797c478bdstevel@tonic-gate	fd = open(door_name, O_RDONLY, 0);
11807c478bdstevel@tonic-gate	if (fd == -1) {
11817c478bdstevel@tonic-gate		(void) pthread_mutex_unlock(&handle->rh_lock);
11827c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_NO_SERVER));
11837c478bdstevel@tonic-gate	}
11847c478bdstevel@tonic-gate
11857c478bdstevel@tonic-gate	request.rdr_version = REPOSITORY_DOOR_VERSION;
11867c478bdstevel@tonic-gate	request.rdr_request = REPOSITORY_DOOR_REQUEST_CONNECT;
11877c478bdstevel@tonic-gate	request.rdr_flags = handle->rh_flags;
11887c478bdstevel@tonic-gate	request.rdr_debug = handle->rh_debug;
11897c478bdstevel@tonic-gate
11907c478bdstevel@tonic-gate	pid = getpid();
11917c478bdstevel@tonic-gate
11927c478bdstevel@tonic-gate	res = make_door_call_retfd(fd, &request, sizeof (request),
11937c478bdstevel@tonic-gate	    &response, sizeof (response), &handle->rh_doorfd);
11947c478bdstevel@tonic-gate
11957c478bdstevel@tonic-gate	(void) close(fd);
11967c478bdstevel@tonic-gate
11977c478bdstevel@tonic-gate	if (res < 0) {
11987c478bdstevel@tonic-gate		(void) pthread_mutex_unlock(&handle->rh_lock);
11997c478bdstevel@tonic-gate
12007c478bdstevel@tonic-gate		assert(res != NOT_BOUND);
12017c478bdstevel@tonic-gate		if (res == CALL_FAILED)
12027c478bdstevel@tonic-gate			return (scf_set_error(SCF_ERROR_NO_SERVER));
12037c478bdstevel@tonic-gate		assert(res == RESULT_TOO_BIG);
12047c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_INTERNAL));
12057c478bdstevel@tonic-gate	}
12067c478bdstevel@tonic-gate
12077c478bdstevel@tonic-gate	if (handle->rh_doorfd < 0) {
12087c478bdstevel@tonic-gate		(void) pthread_mutex_unlock(&handle->rh_lock);
12097c478bdstevel@tonic-gate
12107c478bdstevel@tonic-gate		switch (response.rdr_status) {
12117c478bdstevel@tonic-gate		case REPOSITORY_DOOR_SUCCESS:
12127c478bdstevel@tonic-gate			return (scf_set_error(SCF_ERROR_VERSION_MISMATCH));
12137c478bdstevel@tonic-gate
12147c478bdstevel@tonic-gate		case REPOSITORY_DOOR_FAIL_BAD_REQUEST:
12157c478bdstevel@tonic-gate			return (scf_set_error(SCF_ERROR_VERSION_MISMATCH));
12167c478bdstevel@tonic-gate
12177c478bdstevel@tonic-gate		case REPOSITORY_DOOR_FAIL_VERSION_MISMATCH:
12187c478bdstevel@tonic-gate			return (scf_set_error(SCF_ERROR_VERSION_MISMATCH));
12197c478bdstevel@tonic-gate
12207c478bdstevel@tonic-gate		case REPOSITORY_DOOR_FAIL_BAD_FLAG:
12217c478bdstevel@tonic-gate			return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
12227c478bdstevel@tonic-gate
12237c478bdstevel@tonic-gate		case REPOSITORY_DOOR_FAIL_NO_RESOURCES:
12247c478bdstevel@tonic-gate			return (scf_set_error(SCF_ERROR_NO_RESOURCES));
12257c478bdstevel@tonic-gate
12267c478bdstevel@tonic-gate		case REPOSITORY_DOOR_FAIL_PERMISSION_DENIED:
12277c478bdstevel@tonic-gate			return (scf_set_error(SCF_ERROR_PERMISSION_DENIED));
12287c478bdstevel@tonic-gate
12297c478bdstevel@tonic-gate		default:
12307c478bdstevel@tonic-gate			return (scf_set_error(SCF_ERROR_VERSION_MISMATCH));
12317c478bdstevel@tonic-gate		}
12327c478bdstevel@tonic-gate	}
12337c478bdstevel@tonic-gate
12347c478bdstevel@tonic-gate	(void) fcntl(handle->rh_doorfd, F_SETFD, FD_CLOEXEC);
12357c478bdstevel@tonic-gate
12367c478bdstevel@tonic-gate	if (door_info(handle->rh_doorfd, &info) < 0) {
12377c478bdstevel@tonic-gate		(void) close(handle->rh_doorfd);
12387c478bdstevel@tonic-gate		handle->rh_doorfd = -1;
12397c478bdstevel@tonic-gate
12407c478bdstevel@tonic-gate		(void) pthread_mutex_unlock(&handle->rh_lock);
12417c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_NO_SERVER));
12427c478bdstevel@tonic-gate	}
12437c478bdstevel@tonic-gate
12447c478bdstevel@tonic-gate	handle->rh_doorpid = pid;
12457c478bdstevel@tonic-gate	handle->rh_doorid = info.di_uniquifier;
12467c478bdstevel@tonic-gate
12477c478bdstevel@tonic-gate	/*
12487c478bdstevel@tonic-gate	 * Now, re-attach everything
12497c478bdstevel@tonic-gate	 */
12507c478bdstevel@tonic-gate	for (el = uu_list_first(handle->rh_dataels); el != NULL;
12517c478bdstevel@tonic-gate	    el = uu_list_next(handle->rh_dataels, el)) {
12527c478bdstevel@tonic-gate		if (datael_attach(el) == -1) {
12537c478bdstevel@tonic-gate			assert(scf_error() != SCF_ERROR_HANDLE_DESTROYED);
12547c478bdstevel@tonic-gate			(void) handle_unbind_unlocked(handle);
12557c478bdstevel@tonic-gate			(void) pthread_mutex_unlock(&handle->rh_lock);
12567c478bdstevel@tonic-gate			return (-1);
12577c478bdstevel@tonic-gate		}
12587c478bdstevel@tonic-gate	}
12597c478bdstevel@tonic-gate
12607c478bdstevel@tonic-gate	for (iter = uu_list_first(handle->rh_iters); iter != NULL;
12617c478bdstevel@tonic-gate	    iter = uu_list_next(handle->rh_iters, iter)) {
12627c478bdstevel@tonic-gate		if (iter_attach(iter) == -1) {
12637c478bdstevel@tonic-gate			assert(scf_error() != SCF_ERROR_HANDLE_DESTROYED);
12647c478bdstevel@tonic-gate			(void) handle_unbind_unlocked(handle);
12657c478bdstevel@tonic-gate			(void) pthread_mutex_unlock(&handle->rh_lock);
12667c478bdstevel@tonic-gate			return (-1);
12677c478bdstevel@tonic-gate		}
12687c478bdstevel@tonic-gate	}
12697c478bdstevel@tonic-gate	(void) pthread_mutex_unlock(&handle->rh_lock);
12707c478bdstevel@tonic-gate	return (SCF_SUCCESS);
12717c478bdstevel@tonic-gate}
12727c478bdstevel@tonic-gate
12737c478bdstevel@tonic-gateint
12747c478bdstevel@tonic-gatescf_handle_unbind(scf_handle_t *handle)
12757c478bdstevel@tonic-gate{
12767c478bdstevel@tonic-gate	int ret;
12777c478bdstevel@tonic-gate	(void) pthread_mutex_lock(&handle->rh_lock);
12787c478bdstevel@tonic-gate	ret = handle_unbind_unlocked(handle);
12797c478bdstevel@tonic-gate	(void) pthread_mutex_unlock(&handle->rh_lock);
12807c478bdstevel@tonic-gate	return (ret == SCF_SUCCESS ? ret : scf_set_error(SCF_ERROR_NOT_BOUND));
12817c478bdstevel@tonic-gate}
12827c478bdstevel@tonic-gate
12837c478bdstevel@tonic-gatestatic scf_handle_t *
12847c478bdstevel@tonic-gatehandle_get(scf_handle_t *h)
12857c478bdstevel@tonic-gate{
12867c478bdstevel@tonic-gate	(void) pthread_mutex_lock(&h->rh_lock);
12877c478bdstevel@tonic-gate	if (h->rh_flags & HANDLE_DEAD) {
12887c478bdstevel@tonic-gate		(void) pthread_mutex_unlock(&h->rh_lock);
12897c478bdstevel@tonic-gate		(void) scf_set_error(SCF_ERROR_HANDLE_DESTROYED);
12907c478bdstevel@tonic-gate		return (NULL);
12917c478bdstevel@tonic-gate	}
12927c478bdstevel@tonic-gate	(void) pthread_mutex_unlock(&h->rh_lock);
12937c478bdstevel@tonic-gate	return (h);
12947c478bdstevel@tonic-gate}
12957c478bdstevel@tonic-gate
12967c478bdstevel@tonic-gate/*
12977c478bdstevel@tonic-gate * Called when an object is removed from the handle.  On the last remove,
12987c478bdstevel@tonic-gate * cleans up and frees the handle.
12997c478bdstevel@tonic-gate */
13007c478bdstevel@tonic-gatestatic void
13017c478bdstevel@tonic-gatehandle_unrefed(scf_handle_t *handle)
13027c478bdstevel@tonic-gate{
13037c478bdstevel@tonic-gate	scf_iter_t *iter;
13047c478bdstevel@tonic-gate	scf_value_t *v;
13057c478bdstevel@tonic-gate	scf_scope_t *sc;
13067c478bdstevel@tonic-gate	scf_service_t *svc;
13077c478bdstevel@tonic-gate	scf_instance_t *inst;
13087c478bdstevel@tonic-gate	scf_snapshot_t *snap;
13097c478bdstevel@tonic-gate	scf_snaplevel_t *snaplvl;
13107c478bdstevel@tonic-gate	scf_propertygroup_t *pg;
13117c478bdstevel@tonic-gate	scf_property_t *prop;
13127c478bdstevel@tonic-gate
13137c478bdstevel@tonic-gate	assert(MUTEX_HELD(&handle->rh_lock));
13147c478bdstevel@tonic-gate
13157c478bdstevel@tonic-gate	/*
13167c478bdstevel@tonic-gate	 * Don't do anything if the handle has not yet been destroyed, there
13177c478bdstevel@tonic-gate	 * are still external references, or we're already doing unrefed
13187c478bdstevel@tonic-gate	 * handling.
13197c478bdstevel@tonic-gate	 */
13207c478bdstevel@tonic-gate	if (!(handle->rh_flags & HANDLE_DEAD) ||
13217c478bdstevel@tonic-gate	    handle->rh_extrefs > 0 ||
13227c478bdstevel@tonic-gate	    handle->rh_fd_users > 0 ||
13237c478bdstevel@tonic-gate	    (handle->rh_flags & HANDLE_UNREFED)) {
13247c478bdstevel@tonic-gate		(void) pthread_mutex_unlock(&handle->rh_lock);
13257c478bdstevel@tonic-gate		return;
13267c478bdstevel@tonic-gate	}
13277c478bdstevel@tonic-gate
13287c478bdstevel@tonic-gate	handle->rh_flags |= HANDLE_UNREFED;
13297c478bdstevel@tonic-gate
13307c478bdstevel@tonic-gate	/*
13317c478bdstevel@tonic-gate	 * Now that we know that there are no external references, and the
13327c478bdstevel@tonic-gate	 * HANDLE_DEAD flag keeps new ones from appearing, we can clean up
13337c478bdstevel@tonic-gate	 * our subhandles and destroy the handle completely.
13347c478bdstevel@tonic-gate	 */
13357c478bdstevel@tonic-gate	assert(handle->rh_intrefs >= 0);
13367c478bdstevel@tonic-gate	handle->rh_extrefs = handle->rh_intrefs;
13377c478bdstevel@tonic-gate	handle->rh_intrefs = 0;
13387c478bdstevel@tonic-gate	(void) pthread_mutex_unlock(&handle->rh_lock);
13397c478bdstevel@tonic-gate
13407c478bdstevel@tonic-gate	handle_hold_subhandles(handle, RH_HOLD_ALL);
13417c478bdstevel@tonic-gate
13427c478bdstevel@tonic-gate	iter = handle->rh_iter;
13437c478bdstevel@tonic-gate	sc = handle->rh_scope;
13447c478bdstevel@tonic-gate	svc = handle->rh_service;
13457c478bdstevel@tonic-gate	inst = handle->rh_instance;
13467c478bdstevel@tonic-gate	snap = handle->rh_snapshot;
13477c478bdstevel@tonic-gate	snaplvl = handle->rh_snaplvl;
13487c478bdstevel@tonic-gate	pg = handle->rh_pg;
13497c478bdstevel@tonic-gate	prop = handle->rh_property;
13507c478bdstevel@tonic-gate	v = handle->rh_value;
13517c478bdstevel@tonic-gate
13527c478bdstevel@tonic-gate	handle->rh_iter = NULL;
13537c478bdstevel@tonic-gate	handle->rh_scope = NULL;
13547c478bdstevel@tonic-gate	handle->rh_service = NULL;
13557c478bdstevel@tonic-gate	handle->rh_instance = NULL;
13567c478bdstevel@tonic-gate	handle->rh_snapshot = NULL;
13577c478bdstevel@tonic-gate	handle->rh_snaplvl = NULL;
13587c478bdstevel@tonic-gate	handle->rh_pg = NULL;
13597c478bdstevel@tonic-gate	handle->rh_property = NULL;
13607c478bdstevel@tonic-gate	handle->rh_value = NULL;
13617c478bdstevel@tonic-gate
13627c478bdstevel@tonic-gate	if (iter != NULL)
13637c478bdstevel@tonic-gate		scf_iter_destroy(iter);
13647c478bdstevel@tonic-gate	if (sc != NULL)
13657c478bdstevel@tonic-gate		scf_scope_destroy(sc);
13667c478bdstevel@tonic-gate	if (svc != NULL)
13677c478bdstevel@tonic-gate		scf_service_destroy(svc);
13687c478bdstevel@tonic-gate	if (inst != NULL)
13697c478bdstevel@tonic-gate		scf_instance_destroy(inst);
13707c478bdstevel@tonic-gate	if (snap != NULL)
13717c478bdstevel@tonic-gate		scf_snapshot_destroy(snap);
13727c478bdstevel@tonic-gate	if (snaplvl != NULL)
13737c478bdstevel@tonic-gate		scf_snaplevel_destroy(snaplvl);
13747c478bdstevel@tonic-gate	if (pg != NULL)
13757c478bdstevel@tonic-gate		scf_pg_destroy(pg);
13767c478bdstevel@tonic-gate	if (prop != NULL)
13777c478bdstevel@tonic-gate		scf_property_destroy(prop);
13787c478bdstevel@tonic-gate	if (v != NULL)
13797c478bdstevel@tonic-gate		scf_value_destroy(v);
13807c478bdstevel@tonic-gate
13817c478bdstevel@tonic-gate	(void) pthread_mutex_lock(&handle->rh_lock);
13827c478bdstevel@tonic-gate
13837c478bdstevel@tonic-gate	/* there should be no outstanding children at this point */
13847c478bdstevel@tonic-gate	assert(handle->rh_extrefs == 0);
13857c478bdstevel@tonic-gate	assert(handle->rh_intrefs == 0);
13867c478bdstevel@tonic-gate	assert(handle->rh_values == 0);
13877c478bdstevel@tonic-gate	assert(handle->rh_entries == 0);
13887c478bdstevel@tonic-gate	assert(uu_list_numnodes(handle->rh_dataels) == 0);
13897c478bdstevel@tonic-gate	assert(uu_list_numnodes(handle->rh_iters) == 0);
13907c478bdstevel@tonic-gate
13917c478bdstevel@tonic-gate	uu_list_destroy(handle->rh_dataels);
13927c478bdstevel@tonic-gate	uu_list_destroy(handle->rh_iters);
13937c478bdstevel@tonic-gate	handle->rh_dataels = NULL;
13947c478bdstevel@tonic-gate	handle->rh_iters = NULL;
13957c478bdstevel@tonic-gate	(void) pthread_mutex_unlock(&handle->rh_lock);
13967c478bdstevel@tonic-gate
13977c478bdstevel@tonic-gate	(void) pthread_mutex_destroy(&handle->rh_lock);
13987c478bdstevel@tonic-gate
13997c478bdstevel@tonic-gate	uu_free(handle);
14007c478bdstevel@tonic-gate}
14017c478bdstevel@tonic-gate
14027c478bdstevel@tonic-gatevoid
14037c478bdstevel@tonic-gatescf_handle_destroy(scf_handle_t *handle)
14047c478bdstevel@tonic-gate{
14057c478bdstevel@tonic-gate	if (handle == NULL)
14067c478bdstevel@tonic-gate		return;
14077c478bdstevel@tonic-gate
14087c478bdstevel@tonic-gate	(void) pthread_mutex_lock(&handle->rh_lock);
14097c478bdstevel@tonic-gate	if (handle->rh_flags & HANDLE_DEAD) {
14107c478bdstevel@tonic-gate		/*
14117c478bdstevel@tonic-gate		 * This is an error (you are not allowed to reference the
14127c478bdstevel@tonic-gate		 * handle after it is destroyed), but we can't report it.
14137c478bdstevel@tonic-gate		 */
14147c478bdstevel@tonic-gate		(void) pthread_mutex_unlock(&handle->rh_lock);
14157c478bdstevel@tonic-gate		return;
14167c478bdstevel@tonic-gate	}
14177c478bdstevel@tonic-gate	handle->rh_flags |= HANDLE_DEAD;
14187c478bdstevel@tonic-gate	(void) handle_unbind_unlocked(handle);
14197c478bdstevel@tonic-gate	handle_unrefed(handle);
14207c478bdstevel@tonic-gate}
14217c478bdstevel@tonic-gate
14227c478bdstevel@tonic-gatessize_t
14237c478bdstevel@tonic-gatescf_myname(scf_handle_t *h, char *out, size_t len)
14247c478bdstevel@tonic-gate{
14257c478bdstevel@tonic-gate	char *cp;
14267c478bdstevel@tonic-gate
14277c478bdstevel@tonic-gate	if (!handle_has_server(h))
14287c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_CONNECTION_BROKEN));
14297c478bdstevel@tonic-gate
14307c478bdstevel@tonic-gate	cp = getenv("SMF_FMRI");
14317c478bdstevel@tonic-gate	if (cp == NULL)
14327c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_NOT_SET));
14337c478bdstevel@tonic-gate
14347c478bdstevel@tonic-gate	return (strlcpy(out, cp, len));
14357c478bdstevel@tonic-gate}
14367c478bdstevel@tonic-gate
14377c478bdstevel@tonic-gatestatic uint32_t
14388918dffjwadamshandle_alloc_entityid(scf_handle_t *h)
14397c478bdstevel@tonic-gate{
14408918dffjwadams	uint32_t nextid;
14418918dffjwadams
14428918dffjwadams	assert(MUTEX_HELD(&h->rh_lock));
14438918dffjwadams
14448918dffjwadams	if (uu_list_numnodes(h->rh_dataels) == UINT32_MAX)
14458918dffjwadams		return (0);		/* no ids available */
14468918dffjwadams
14478918dffjwadams	/*
14488918dffjwadams	 * The following loop assumes that there are not a huge number of
14498918dffjwadams	 * outstanding entities when we've wrapped.  If that ends up not
14508918dffjwadams	 * being the case, the O(N^2) nature of this search will hurt a lot,
14518918dffjwadams	 * and the data structure should be switched to an AVL tree.
14528918dffjwadams	 */
14538918dffjwadams	nextid = h->rh_nextentity + 1;
14548918dffjwadams	for (;;) {
14558918dffjwadams		scf_datael_t *cur;
14568918dffjwadams
14578918dffjwadams		if (nextid == 0) {
14588918dffjwadams			nextid++;
14598918dffjwadams			h->rh_flags |= HANDLE_WRAPPED_ENTITY;
14608918dffjwadams		}
14618918dffjwadams		if (!(h->rh_flags & HANDLE_WRAPPED_ENTITY))
14628918dffjwadams			break;
14638918dffjwadams
14648918dffjwadams		cur = uu_list_find(h->rh_dataels, NULL, &nextid, NULL);
14658918dffjwadams		if (cur == NULL)
14668918dffjwadams			break;		/* not in use */
14678918dffjwadams
14688918dffjwadams		if (nextid == h->rh_nextentity)
14698918dffjwadams			return (0);	/* wrapped around; no ids available */
14708918dffjwadams		nextid++;
14718918dffjwadams	}
14728918dffjwadams
14738918dffjwadams	h->rh_nextentity = nextid;
14748918dffjwadams	return (nextid);
14757c478bdstevel@tonic-gate}
14767c478bdstevel@tonic-gate
14777c478bdstevel@tonic-gatestatic uint32_t
14788918dffjwadamshandle_alloc_iterid(scf_handle_t *h)
14797c478bdstevel@tonic-gate{
14808918dffjwadams	uint32_t nextid;
14818918dffjwadams
14828918dffjwadams	assert(MUTEX_HELD(&h->rh_lock));
14838918dffjwadams
14848918dffjwadams	if (uu_list_numnodes(h->rh_iters) == UINT32_MAX)
14858918dffjwadams		return (0);		/* no ids available */
14868918dffjwadams
14878918dffjwadams	/* see the comment in handle_alloc_entityid */
14888918dffjwadams	nextid = h->rh_nextiter + 1;
14898918dffjwadams	for (;;) {
14908918dffjwadams		scf_iter_t *cur;
14918918dffjwadams
14928918dffjwadams		if (nextid == 0) {
14938918dffjwadams			nextid++;
14948918dffjwadams			h->rh_flags |= HANDLE_WRAPPED_ITER;
14958918dffjwadams		}
14968918dffjwadams		if (!(h->rh_flags & HANDLE_WRAPPED_ITER))
14978918dffjwadams			break;			/* not yet wrapped */
14988918dffjwadams
14998918dffjwadams		cur = uu_list_find(h->rh_iters, NULL, &nextid, NULL);
15008918dffjwadams		if (cur == NULL)
15018918dffjwadams			break;		/* not in use */
15028918dffjwadams
15038918dffjwadams		if (nextid == h->rh_nextiter)
15048918dffjwadams			return (0);	/* wrapped around; no ids available */
15058918dffjwadams		nextid++;
15068918dffjwadams	}
15078918dffjwadams
15088918dffjwadams	h->rh_nextiter = nextid;
15098918dffjwadams	return (nextid);
15107c478bdstevel@tonic-gate}
15117c478bdstevel@tonic-gate
15127c478bdstevel@tonic-gatestatic uint32_t
15138918dffjwadamshandle_next_changeid(scf_handle_t *handle)
15147c478bdstevel@tonic-gate{
15158918dffjwadams	uint32_t nextid;
15168918dffjwadams
15177c478bdstevel@tonic-gate	assert(MUTEX_HELD(&handle->rh_lock));
15188918dffjwadams
15198918dffjwadams	nextid = ++handle->rh_nextchangeid;
15208918dffjwadams	if (nextid == 0)
15218918dffjwadams		nextid = ++handle->rh_nextchangeid;
15228918dffjwadams	return (nextid);
15237c478bdstevel@tonic-gate}
15247c478bdstevel@tonic-gate
15257c478bdstevel@tonic-gate/*
15267c478bdstevel@tonic-gate * Fails with
15277c478bdstevel@tonic-gate *   _INVALID_ARGUMENT - h is NULL
15287c478bdstevel@tonic-gate *   _HANDLE_DESTROYED
15297c478bdstevel@tonic-gate *   _INTERNAL - server response too big
15307c478bdstevel@tonic-gate *		 entity already set up with different type
15317c478bdstevel@tonic-gate *   _NO_RESOURCES
15327c478bdstevel@tonic-gate */
15337c478bdstevel@tonic-gatestatic int
15347c478bdstevel@tonic-gatedatael_init(scf_datael_t *dp, scf_handle_t *h, uint32_t type)
15357c478bdstevel@tonic-gate{
15367c478bdstevel@tonic-gate	int ret;
15377c478bdstevel@tonic-gate
15387c478bdstevel@tonic-gate	if (h == NULL)
15397c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
15407c478bdstevel@tonic-gate
15417c478bdstevel@tonic-gate	uu_list_node_init(dp, &dp->rd_node, datael_pool);
15427c478bdstevel@tonic-gate
15437c478bdstevel@tonic-gate	dp->rd_handle = h;
15447c478bdstevel@tonic-gate	dp->rd_type = type;
15457c478bdstevel@tonic-gate	dp->rd_reset = 0;
15467c478bdstevel@tonic-gate
15477c478bdstevel@tonic-gate	(void) pthread_mutex_lock(&h->rh_lock);
15487c478bdstevel@tonic-gate	if (h->rh_flags & HANDLE_DEAD) {
15497c478bdstevel@tonic-gate		/*
15507c478bdstevel@tonic-gate		 * we're in undefined territory (the user cannot use a handle
15517c478bdstevel@tonic-gate		 * directly after it has been destroyed), but we don't want
15527c478bdstevel@tonic-gate		 * to allow any new references to happen, so we fail here.
15537c478bdstevel@tonic-gate		 */
15547c478bdstevel@tonic-gate		(void) pthread_mutex_unlock(&h->rh_lock);
15557c478bdstevel@tonic-gate		return (scf_set_error(SCF_ERROR_HANDLE_DESTROYED));
15567c478bdstevel@tonic-gate	}
15577c478bdstevel@tonic-gate	dp->rd_entity = handle_alloc_entityid(h);
15588918dffjwadams	if (dp->rd_entity == 0) {
15598918dffjwadams		(void) pthread_mutex_unlock(&h->rh_lock);
15608918dffjwadams		uu_list_node_fini(dp, &dp->rd_node, datael_pool);
15618918dffjwadams		return (scf_set_error(SCF_ERROR_NO_MEMORY));
1562