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