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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  *
24  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 /*
31  * This file contains routines to manipulate lists of repository values that
32  * are used to store process ids and the internal state. There are routines
33  * to read/write the lists from/to the repository and routines to modify or
34  * inspect the lists. It also contains routines that deal with the
35  * repository side of contract ids.
36  */
37 
38 #include <errno.h>
39 #include <stdlib.h>
40 #include <libintl.h>
41 #include <unistd.h>
42 #include <string.h>
43 #include <signal.h>
44 #include "inetd_impl.h"
45 
46 
47 /*
48  * Number of consecutive repository bind retries performed by bind_to_rep()
49  * before failing.
50  */
51 #define	BIND_TO_REP_RETRIES	10
52 
53 /* Name of property group where inetd's state for a service is stored. */
54 #define	PG_NAME_INSTANCE_STATE (const char *) "inetd_state"
55 
56 /* uu_list repval list pool */
57 static uu_list_pool_t *rep_val_pool = NULL;
58 
59 /*
60  * Repository object pointers that get set-up in repval_init() and closed down
61  * in repval_fini(). They're used in _retrieve_rep_vals(), _store_rep_vals(),
62  * add_remove_contract_norebind(), and adopt_repository_contracts().  They're
63  * global so they can be initialized once on inetd startup, and re-used
64  * there-after in the referenced functions.
65  */
66 static scf_handle_t		*rep_handle = NULL;
67 static scf_propertygroup_t	*pg = NULL;
68 static scf_instance_t		*inst = NULL;
69 static scf_transaction_t	*trans = NULL;
70 static scf_transaction_entry_t	*entry = NULL;
71 static scf_property_t		*prop = NULL;
72 
73 /*
74  * Try and make the given handle bind be bound to the repository. If
75  * it's already bound, or we succeed a new bind return 0; else return
76  * -1 on failure, with the SCF error set to one of the following:
77  * SCF_ERROR_NO_SERVER
78  * SCF_ERROR_NO_RESOURCES
79  */
80 int
81 make_handle_bound(scf_handle_t *hdl)
82 {
83 	uint_t retries;
84 
85 	for (retries = 0; retries <= BIND_TO_REP_RETRIES; retries++) {
86 		if ((scf_handle_bind(hdl) == 0) ||
87 		    (scf_error() == SCF_ERROR_IN_USE))
88 			return (0);
89 
90 		assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT);
91 	}
92 
93 	return (-1);
94 }
95 
96 int
97 repval_init(void)
98 {
99 	debug_msg("Entering repval_init");
100 
101 	/*
102 	 * Create the repval list pool.
103 	 */
104 	rep_val_pool = uu_list_pool_create("rep_val_pool", sizeof (rep_val_t),
105 	    offsetof(rep_val_t, link), NULL, UU_LIST_POOL_DEBUG);
106 	if (rep_val_pool == NULL) {
107 		error_msg("%s: %s", gettext("Failed to create rep_val pool"),
108 		    uu_strerror(uu_error()));
109 		return (-1);
110 	}
111 
112 	/*
113 	 * Create and bind a repository handle, and create all repository
114 	 * objects that we'll use later that are associated with it. On any
115 	 * errors we simply return -1 and let repval_fini() clean-up after
116 	 * us.
117 	 */
118 	if ((rep_handle = scf_handle_create(SCF_VERSION)) == NULL) {
119 		error_msg("%s: %s",
120 		    gettext("Failed to create repository handle"),
121 		    scf_strerror(scf_error()));
122 		goto cleanup;
123 	} else if (make_handle_bound(rep_handle) == -1) {
124 		goto cleanup;
125 	} else if (((pg = scf_pg_create(rep_handle)) == NULL) ||
126 	    ((inst = scf_instance_create(rep_handle)) == NULL) ||
127 	    ((trans = scf_transaction_create(rep_handle)) == NULL) ||
128 	    ((entry = scf_entry_create(rep_handle)) == NULL) ||
129 	    ((prop = scf_property_create(rep_handle)) == NULL)) {
130 		error_msg("%s: %s",
131 		    gettext("Failed to create repository object"),
132 		    scf_strerror(scf_error()));
133 		goto cleanup;
134 	}
135 
136 	return (0);
137 cleanup:
138 	repval_fini();
139 	return (-1);
140 }
141 
142 void
143 repval_fini(void)
144 {
145 	debug_msg("Entering repval_fini");
146 
147 	if (rep_handle != NULL) {
148 		/*
149 		 * We unbind from the repository before we free the repository
150 		 * objects for efficiency reasons.
151 		 */
152 		(void) scf_handle_unbind(rep_handle);
153 
154 		scf_pg_destroy(pg);
155 		pg = NULL;
156 		scf_instance_destroy(inst);
157 		inst = NULL;
158 		scf_transaction_destroy(trans);
159 		trans = NULL;
160 		scf_entry_destroy(entry);
161 		entry = NULL;
162 		scf_property_destroy(prop);
163 		prop = NULL;
164 
165 		scf_handle_destroy(rep_handle);
166 		rep_handle = NULL;
167 	}
168 
169 	if (rep_val_pool != NULL) {
170 		uu_list_pool_destroy(rep_val_pool);
171 		rep_val_pool = NULL;
172 	}
173 }
174 
175 uu_list_t *
176 create_rep_val_list(void)
177 {
178 	uu_list_t	*ret;
179 
180 	debug_msg("Entering create_rep_val_list");
181 
182 	if ((ret = uu_list_create(rep_val_pool, NULL, 0)) == NULL)
183 		assert(uu_error() == UU_ERROR_NO_MEMORY);
184 
185 	return (ret);
186 }
187 
188 void
189 destroy_rep_val_list(uu_list_t *list)
190 {
191 	debug_msg("Entering destroy_rep_val_list");
192 
193 	if (list != NULL) {
194 		empty_rep_val_list(list);
195 		uu_list_destroy(list);
196 	}
197 }
198 
199 rep_val_t *
200 find_rep_val(uu_list_t *list, int64_t val)
201 {
202 	rep_val_t *rv;
203 
204 	debug_msg("Entering find_rep_val: val: %lld", val);
205 
206 	for (rv = uu_list_first(list); rv != NULL;
207 	    rv = uu_list_next(list, rv)) {
208 		if (rv->val == val)
209 			break;
210 	}
211 	return (rv);
212 }
213 
214 int
215 add_rep_val(uu_list_t *list, int64_t val)
216 {
217 	rep_val_t *rv;
218 
219 	debug_msg("Entering add_rep_val: val: %lld", val);
220 
221 	if ((rv = malloc(sizeof (rep_val_t))) == NULL)
222 		return (-1);
223 
224 	uu_list_node_init(rv, &rv->link, rep_val_pool);
225 	rv->val = val;
226 	rv->scf_val = NULL;
227 	(void) uu_list_insert_after(list, NULL, rv);
228 
229 	return (0);
230 }
231 
232 void
233 remove_rep_val(uu_list_t *list, int64_t val)
234 {
235 	rep_val_t *rv;
236 
237 	debug_msg("Entering remove_rep_val: val: %lld", val);
238 
239 	if ((rv = find_rep_val(list, val)) != NULL) {
240 		uu_list_remove(list, rv);
241 		assert(rv->scf_val == NULL);
242 		free(rv);
243 	}
244 }
245 
246 void
247 empty_rep_val_list(uu_list_t *list)
248 {
249 	void		*cookie = NULL;
250 	rep_val_t	*rv;
251 
252 	debug_msg("Entering empty_rep_val_list");
253 
254 	while ((rv = uu_list_teardown(list, &cookie)) != NULL) {
255 		if (rv->scf_val != NULL)
256 			scf_value_destroy(rv->scf_val);
257 		free(rv);
258 	}
259 }
260 
261 int64_t
262 get_single_rep_val(uu_list_t *list)
263 {
264 	rep_val_t *rv = uu_list_first(list);
265 
266 	debug_msg("Entering get_single_rep_val");
267 
268 	assert(rv != NULL);
269 	return (rv->val);
270 }
271 
272 int
273 set_single_rep_val(uu_list_t *list, int64_t val)
274 {
275 	rep_val_t *rv = uu_list_first(list);
276 
277 	debug_msg("Entering set_single_rep_val");
278 
279 	if (rv == NULL) {
280 		if (add_rep_val(list, val) == -1)
281 			return (-1);
282 	} else {
283 		rv->val = val;
284 	}
285 
286 	return (0);
287 }
288 
289 /*
290  * Partner to add_tr_entry_values. This function frees the scf_values created
291  * in add_tr_entry_values() in the list 'vals'.
292  */
293 static void
294 remove_tr_entry_values(uu_list_t *vals)
295 {
296 	rep_val_t	*rval;
297 
298 	debug_msg("Entering remove_tr_entry_values");
299 
300 	for (rval = uu_list_first(vals); rval != NULL;
301 	    rval = uu_list_next(vals, rval)) {
302 		if (rval->scf_val != NULL) {
303 			scf_value_destroy(rval->scf_val);
304 			rval->scf_val = NULL;
305 		}
306 	}
307 }
308 
309 /*
310  * This function creates and associates with transaction entry 'entry' an
311  * scf value for each value in 'vals'. The pointers to the scf values
312  * are stored in the list for later cleanup by remove_tr_entry_values.
313  * Returns 0 on success, else -1 on error with scf_error() set to:
314  * SCF_ERROR_NO_MEMORY if memory allocation failed.
315  * SCF_ERROR_CONNECTION_BROKEN if the connection to the repository was broken.
316  */
317 static int
318 add_tr_entry_values(scf_handle_t *hdl, scf_transaction_entry_t *entry,
319     uu_list_t *vals)
320 {
321 	rep_val_t *rval;
322 
323 	debug_msg("Entering add_tr_entry_values");
324 
325 	for (rval = uu_list_first(vals); rval != NULL;
326 	    rval = uu_list_next(vals, rval)) {
327 
328 		assert(rval->scf_val == NULL);
329 		if ((rval->scf_val = scf_value_create(hdl)) == NULL) {
330 			remove_tr_entry_values(vals);
331 			return (-1);
332 		}
333 
334 		scf_value_set_integer(rval->scf_val, rval->val);
335 
336 		if (scf_entry_add_value(entry, rval->scf_val) < 0) {
337 			remove_tr_entry_values(vals);
338 			return (-1);
339 		}
340 	}
341 
342 	return (0);
343 }
344 
345 /*
346  * Stores the values contained in the list 'vals' into the property 'prop_name'
347  * of the instance with fmri 'inst_fmri', within the instance's instance
348  * state property group.
349  *
350  * Returns 0 on success, else one of the following on failure:
351  * SCF_ERROR_NO_MEMORY if memory allocation failed.
352  * SCF_ERROR_NO_RESOURCES if the server doesn't have required resources.
353  * SCF_ERROR_VERSION_MISMATCH if program compiled against a newer libscf
354  * than on system.
355  * SCF_ERROR_PERMISSION_DENIED if insufficient privileges to modify pg.
356  * SCF_ERROR_BACKEND_ACCESS if the repository back-end refused the pg modify.
357  * SCF_ERROR_CONNECTION_BROKEN if the connection to the repository was broken.
358  */
359 static scf_error_t
360 _store_rep_vals(uu_list_t *vals, const char *inst_fmri, const char *prop_name)
361 {
362 	int			cret;
363 	int			ret;
364 
365 	debug_msg("Entering _store_rep_vals: fmri: %s, prop: %s", inst_fmri,
366 	    prop_name);
367 
368 	if (scf_handle_decode_fmri(rep_handle, inst_fmri, NULL, NULL, inst,
369 	    NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1)
370 		return (scf_error());
371 
372 	/*
373 	 * Fetch the instance state pg, and if it doesn't exist try and
374 	 * create it.
375 	 */
376 	if (scf_instance_get_pg(inst, PG_NAME_INSTANCE_STATE, pg) < 0) {
377 		if (scf_error() != SCF_ERROR_NOT_FOUND)
378 			return (scf_error());
379 		if (scf_instance_add_pg(inst, PG_NAME_INSTANCE_STATE,
380 		    SCF_GROUP_FRAMEWORK, SCF_PG_FLAG_NONPERSISTENT, pg) < 0)
381 			return (scf_error());
382 	}
383 
384 	/*
385 	 * Perform a transaction to write the values to the requested property.
386 	 * If someone got there before us, loop and retry.
387 	 */
388 	do {
389 		if (scf_transaction_start(trans, pg) < 0)
390 			return (scf_error());
391 
392 		if ((scf_transaction_property_new(trans, entry,
393 		    prop_name, SCF_TYPE_INTEGER) < 0) &&
394 		    (scf_transaction_property_change_type(trans, entry,
395 		    prop_name, SCF_TYPE_INTEGER) < 0)) {
396 			ret = scf_error();
397 			goto cleanup;
398 		}
399 
400 		if (add_tr_entry_values(rep_handle, entry, vals) < 0) {
401 			ret = scf_error();
402 			goto cleanup;
403 		}
404 
405 		if ((cret = scf_transaction_commit(trans)) < 0) {
406 			ret = scf_error();
407 			goto cleanup;
408 		} else if (cret == 0) {
409 			scf_transaction_reset(trans);
410 			scf_entry_reset(entry);
411 			remove_tr_entry_values(vals);
412 			if (scf_pg_update(pg) < 0) {
413 				ret = scf_error();
414 				goto cleanup;
415 			}
416 		}
417 	} while (cret == 0);
418 
419 	ret = 0;
420 cleanup:
421 	scf_transaction_reset(trans);
422 	scf_entry_reset(entry);
423 	remove_tr_entry_values(vals);
424 	return (ret);
425 }
426 
427 /*
428  * Retrieves the repository values of property 'prop_name', of the instance
429  * with fmri 'fmri', from within the instance's instance state property
430  * group and adds them to the value list 'list'.
431  *
432  * Returns 0 on success, else one of the following values on error:
433  * SCF_ERROR_NOT_FOUND if the property doesn't exist.
434  * SCF_ERROR_NO_MEMORY if memory allocation failed.
435  * SCF_ERROR_CONNECTION_BROKEN if the connection to the repository was broken.
436  * SCF_ERROR_TYPE_MISMATCH if the property was of an unexpected type.
437  *
438  */
439 static scf_error_t
440 _retrieve_rep_vals(uu_list_t *list, const char *fmri, const char *prop_name)
441 {
442 	scf_simple_prop_t	*sp;
443 	int64_t			*ip;
444 
445 	debug_msg("Entering _retrieve_rep_vals: fmri: %s, prop: %s", fmri,
446 	    prop_name);
447 
448 	if ((sp = scf_simple_prop_get(rep_handle, fmri, PG_NAME_INSTANCE_STATE,
449 	    prop_name)) == NULL)
450 		return (scf_error());
451 
452 	while ((ip = scf_simple_prop_next_integer(sp)) != NULL) {
453 		if (add_rep_val(list, *ip) == -1) {
454 			empty_rep_val_list(list);
455 			scf_simple_prop_free(sp);
456 			return (SCF_ERROR_NO_MEMORY);
457 		}
458 	}
459 	if (scf_error() != SCF_ERROR_NONE) {
460 		assert(scf_error() == SCF_ERROR_TYPE_MISMATCH);
461 		empty_rep_val_list(list);
462 		scf_simple_prop_free(sp);
463 		return (scf_error());
464 	}
465 
466 	scf_simple_prop_free(sp);
467 	return (0);
468 }
469 
470 /*
471  * A routine that loops trying to read/write repository values until
472  * either success, an error other that a broken repository connection or
473  * the number of retries reaches REP_OP_RETRIES.
474  * Returns 0 on success, else the error value from either _store_rep_vals or
475  * retrieve_rep_vals (based on whether 'store' was set or not), or one of the
476  * following if a rebind failed:
477  * SCF_ERROR_NO_RESOURCES if the server doesn't have adequate resources.
478  * SCF_ERROR_NO_SERVER if the server isn't running.
479  */
480 static scf_error_t
481 store_retrieve_rep_vals(uu_list_t *vals, const char *fmri,
482     const char *prop, boolean_t store)
483 {
484 	scf_error_t	ret;
485 	uint_t		retries;
486 
487 	debug_msg("Entering store_retrieve_rep_vals, store: %d", store);
488 
489 
490 	for (retries = 0; retries <= REP_OP_RETRIES; retries++) {
491 		if (make_handle_bound(rep_handle) == -1) {
492 			ret = scf_error();
493 			break;
494 		}
495 
496 		if ((ret = (store ? _store_rep_vals(vals, fmri, prop) :
497 		    _retrieve_rep_vals(vals, fmri, prop))) !=
498 		    SCF_ERROR_CONNECTION_BROKEN)
499 			break;
500 
501 		(void) scf_handle_unbind(rep_handle);
502 	}
503 
504 	return (ret);
505 }
506 
507 scf_error_t
508 store_rep_vals(uu_list_t *vals, const char *fmri, const char *prop)
509 {
510 	return (store_retrieve_rep_vals(vals, fmri, prop, B_TRUE));
511 }
512 
513 scf_error_t
514 retrieve_rep_vals(uu_list_t *vals, const char *fmri, const char *prop)
515 {
516 	return (store_retrieve_rep_vals(vals, fmri, prop, B_FALSE));
517 }
518 
519 /*
520  * Fails with ECONNABORTED, ENOENT, EACCES, EROFS, ENOMEM, or EPERM.
521  */
522 static int
523 add_remove_contract_norebind(const char *fmri, boolean_t add, ctid_t ctid)
524 {
525 	int err;
526 
527 	if (scf_handle_decode_fmri(rep_handle, fmri, NULL, NULL, inst, NULL,
528 	    NULL, SCF_DECODE_FMRI_EXACT) != 0) {
529 		switch (scf_error()) {
530 		case SCF_ERROR_CONNECTION_BROKEN:
531 			return (ECONNABORTED);
532 
533 		case SCF_ERROR_NOT_FOUND:
534 			return (ENOENT);
535 
536 		case SCF_ERROR_CONSTRAINT_VIOLATED:
537 		case SCF_ERROR_INVALID_ARGUMENT:
538 		case SCF_ERROR_HANDLE_MISMATCH:
539 		default:
540 			assert(0);
541 			abort();
542 		}
543 	}
544 
545 redo:
546 	if (add)
547 		err = restarter_store_contract(inst, ctid,
548 		    RESTARTER_CONTRACT_PRIMARY);
549 	else
550 		err = restarter_remove_contract(inst, ctid,
551 		    RESTARTER_CONTRACT_PRIMARY);
552 	switch (err) {
553 	case 0:
554 	case ENOMEM:
555 	case ECONNABORTED:
556 		return (err);
557 
558 	case ECANCELED:
559 		return (ENOENT);
560 
561 	case EPERM:
562 		assert(0);
563 		return (err);
564 
565 	case EACCES:
566 		error_msg(add ? gettext("Failed to write contract id %ld for "
567 		    "instance %s to repository: backend access denied.") :
568 		    gettext("Failed to remove contract id %ld for instance %s "
569 		    "from repository: backend access denied."), ctid, fmri);
570 		return (err);
571 
572 	case EROFS:
573 		error_msg(add ? gettext("Failed to write contract id %ld for "
574 		    "instance %s to repository: backend is read-only.") :
575 		    gettext("Failed to remove contract id %ld for instance %s "
576 		    "from repository: backend is read-only."), ctid, fmri);
577 		return (err);
578 
579 	case EINVAL:
580 	case EBADF:
581 	default:
582 		assert(0);
583 		abort();
584 		/* NOTREACHED */
585 	}
586 }
587 
588 /*
589  * Tries to add/remove (dependent on the value of 'add') the specified
590  * contract id to the specified instance until either success, an error
591  * other that connection broken occurs, or the number of bind retries reaches
592  * REP_OP_RETRIES.
593  * Returns 0 on success else fails with one of ENOENT, EACCES, EROFS, EPERM,
594  * ECONNABORTED or ENOMEM.
595  */
596 int
597 add_remove_contract(const char *fmri, boolean_t add, ctid_t ctid)
598 {
599 	uint_t	retries;
600 	int	err;
601 
602 	for (retries = 0; retries <= REP_OP_RETRIES; retries++) {
603 		if (make_handle_bound(rep_handle) == -1) {
604 			err = ECONNABORTED;
605 			break;
606 		}
607 
608 		if ((err = add_remove_contract_norebind(fmri, add, ctid)) !=
609 		    ECONNABORTED)
610 			break;
611 
612 		(void) scf_handle_unbind(rep_handle);
613 	}
614 
615 	return (err);
616 }
617 
618 /*
619  * Iterate over all contracts associated with the instance specified by
620  * fmri; if sig !=0, we send each contract the specified signal, otherwise
621  * we call adopt_contract() to take ownership.  This really ought to be
622  * reworked to use a callback mechanism if more functionality is added.
623  *
624  * Returns 0 on success or ENOENT if the instance, its restarter property
625  * group, or its contract property don't exist, or EINVAL if the property
626  * is not of the correct type, ENOMEM if there was a memory allocation
627  * failure, EPERM if there were permission problems accessing the repository,
628  * or ECONNABORTED if the connection with the repository was broken.
629  */
630 int
631 iterate_repository_contracts(const char *fmri, int sig)
632 {
633 	scf_iter_t	*iter;
634 	scf_value_t	*val = NULL;
635 	uint64_t	c;
636 	int		err;
637 	int		ret = 0;
638 	uint_t		retries = 0;
639 
640 	debug_msg("Entering iterate_repository_contracts");
641 
642 	if (make_handle_bound(rep_handle) == -1)
643 		return (ECONNABORTED);
644 
645 	if (((iter = scf_iter_create(rep_handle)) == NULL) ||
646 	    ((val = scf_value_create(rep_handle)) == NULL)) {
647 		ret = ENOMEM;
648 		goto out;
649 	}
650 
651 rep_retry:
652 	if (scf_handle_decode_fmri(rep_handle, fmri, NULL, NULL, inst, NULL,
653 	    NULL, SCF_DECODE_FMRI_EXACT) != 0) {
654 		switch (scf_error()) {
655 		case SCF_ERROR_CONNECTION_BROKEN:
656 rebind:
657 			(void) scf_handle_unbind(rep_handle);
658 
659 			if (retries++ == REP_OP_RETRIES) {
660 				ret = ECONNABORTED;
661 				goto out;
662 			}
663 
664 			if (make_handle_bound(rep_handle) == -1) {
665 				ret = ECONNABORTED;
666 				goto out;
667 			}
668 
669 			goto rep_retry;
670 
671 		case SCF_ERROR_NOT_FOUND:
672 			ret = ENOENT;
673 			goto out;
674 
675 		case SCF_ERROR_CONSTRAINT_VIOLATED:
676 		case SCF_ERROR_INVALID_ARGUMENT:
677 		case SCF_ERROR_HANDLE_MISMATCH:
678 		default:
679 			assert(0);
680 			abort();
681 		}
682 	}
683 
684 	if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, pg) != 0) {
685 		switch (scf_error()) {
686 		case SCF_ERROR_CONNECTION_BROKEN:
687 			goto rebind;
688 
689 		case SCF_ERROR_NOT_SET:
690 			ret = 0;
691 			goto out;
692 
693 		case SCF_ERROR_NOT_FOUND:
694 			ret = ENOENT;
695 			goto out;
696 
697 		case SCF_ERROR_INVALID_ARGUMENT:
698 		case SCF_ERROR_HANDLE_MISMATCH:
699 		default:
700 			assert(0);
701 			abort();
702 		}
703 	}
704 
705 	if (scf_pg_get_property(pg, SCF_PROPERTY_CONTRACT, prop) != 0) {
706 		switch (scf_error()) {
707 		case SCF_ERROR_CONNECTION_BROKEN:
708 			goto rebind;
709 
710 		case SCF_ERROR_NOT_SET:
711 			ret = 0;
712 			goto out;
713 
714 		case SCF_ERROR_NOT_FOUND:
715 			ret = ENOENT;
716 			goto out;
717 
718 		case SCF_ERROR_INVALID_ARGUMENT:
719 		case SCF_ERROR_HANDLE_MISMATCH:
720 		default:
721 			assert(0);
722 			abort();
723 		}
724 	}
725 
726 	if (scf_property_is_type(prop, SCF_TYPE_COUNT) != 0) {
727 		switch (scf_error()) {
728 		case SCF_ERROR_CONNECTION_BROKEN:
729 			goto rebind;
730 
731 		case SCF_ERROR_NOT_SET:
732 			ret = ENOENT;
733 			goto out;
734 
735 		case SCF_ERROR_TYPE_MISMATCH:
736 			ret = EINVAL;
737 			goto out;
738 
739 		default:
740 			assert(0);
741 			abort();
742 		}
743 	}
744 
745 	if (scf_iter_property_values(iter, prop) != 0) {
746 		switch (scf_error()) {
747 		case SCF_ERROR_CONNECTION_BROKEN:
748 			goto rebind;
749 
750 		case SCF_ERROR_NOT_SET:
751 			ret = ENOENT;
752 			goto out;
753 
754 		case SCF_ERROR_HANDLE_MISMATCH:
755 		default:
756 			assert(0);
757 			abort();
758 		}
759 	}
760 
761 	for (;;) {
762 		err = scf_iter_next_value(iter, val);
763 		if (err == 0) {
764 			break;
765 		} else if (err != 1) {
766 			assert(scf_error() == SCF_ERROR_CONNECTION_BROKEN);
767 			goto rebind;
768 		}
769 
770 		err = scf_value_get_count(val, &c);
771 		assert(err == 0);
772 
773 		if (sig == 0) {
774 			/* Try to adopt the contract */
775 			if (adopt_contract((ctid_t)c, fmri) != 0) {
776 				/*
777 				 * Adoption failed.  No reason to think it'll
778 				 * work later, so remove the id from our list
779 				 * in the repository.
780 				 *
781 				 * Beware: add_remove_contract_norebind() uses
782 				 * the global scf_ handles.  Fortunately we're
783 				 * done with them.  We need to be cognizant of
784 				 * repository disconnection, though.
785 				 */
786 				switch (add_remove_contract_norebind(fmri,
787 				    B_FALSE, (ctid_t)c)) {
788 				case 0:
789 				case ENOENT:
790 				case EACCES:
791 				case EROFS:
792 					break;
793 
794 				case ECONNABORTED:
795 					goto rebind;
796 
797 				default:
798 					assert(0);
799 					abort();
800 				}
801 			}
802 		} else {
803 			/*
804 			 * Send a signal to all in the contract; ESRCH just
805 			 * means they all exited before we could kill them
806 			 */
807 			if (sigsend(P_CTID, (ctid_t)c, sig) == -1 &&
808 			    errno != ESRCH) {
809 				warn_msg(gettext("Unable to signal all contract"
810 				    "members of instance %s: %s"), fmri,
811 				    strerror(errno));
812 			}
813 		}
814 	}
815 
816 out:
817 	scf_value_destroy(val);
818 	scf_iter_destroy(iter);
819 	return (ret);
820 }
821