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