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