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  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Routines used by inetd to read inetd's configuration from the repository,
31  * to validate it and setup inetd's data structures appropriately based on
32  * in.
33  */
34 
35 #include <stdlib.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <netdb.h>
40 #include <netinet/in.h>
41 #include <libintl.h>
42 #include <nss_dbdefs.h>
43 #include <signal.h>
44 #include <wait.h>
45 #include "inetd_impl.h"
46 
47 
48 /* method timeout used if one isn't explicitly specified */
49 #define	DEFAULT_METHOD_TIMEOUT	10
50 
51 
52 /* supported method properties and their attributes */
53 static inetd_prop_t method_props[] = {
54 {PR_EXEC_NAME, "", INET_TYPE_STRING, B_FALSE, IVE_UNSET, NULL, B_FALSE},
55 {PR_ARG0_NAME, "", INET_TYPE_STRING, B_TRUE, IVE_UNSET, NULL, B_FALSE},
56 {SCF_PROPERTY_TIMEOUT, "", INET_TYPE_COUNT, B_TRUE, IVE_UNSET, NULL, B_FALSE},
57 {NULL},
58 };
59 
60 /* enumeration of method properties; used to index into method_props[] */
61 typedef enum {
62 	MP_EXEC,
63 	MP_ARG0,
64 	MP_TIMEOUT
65 } method_prop_t;
66 
67 
68 /* handle used for repository access in read_prop() */
69 static scf_handle_t	*rep_handle = NULL;
70 
71 /* pool used to create proto_info_t lists (generic proto info structure) */
72 static uu_list_pool_t	*proto_info_pool = NULL;
73 
74 static void destroy_method_props(inetd_prop_t *);
75 static int proto_info_compare(const void *, const void *, void *);
76 
77 int
78 config_init(void)
79 {
80 	if ((rep_handle = scf_handle_create(SCF_VERSION)) == NULL) {
81 		error_msg("%s: %s",
82 		    gettext("Failed to create repository handle"),
83 		    scf_strerror(scf_error()));
84 		return (-1);
85 	} else if (make_handle_bound(rep_handle) == -1) {
86 		/* let config_fini clean-up */
87 		return (-1);
88 	}
89 
90 	if ((proto_info_pool = uu_list_pool_create("proto_info_pool",
91 	    sizeof (proto_info_t), offsetof(proto_info_t, link),
92 	    proto_info_compare, UU_LIST_POOL_DEBUG)) == NULL) {
93 		error_msg(gettext("Failed to create uu list pool: %s"),
94 		    uu_strerror(uu_error()));
95 		return (-1);
96 	}
97 
98 	return (0);
99 }
100 
101 void
102 config_fini(void)
103 {
104 	if (rep_handle == NULL)
105 		return;
106 
107 	if (proto_info_pool != NULL) {
108 		uu_list_pool_destroy(proto_info_pool);
109 		proto_info_pool = NULL;
110 	}
111 
112 	(void) scf_handle_unbind(rep_handle);
113 	scf_handle_destroy(rep_handle);
114 	rep_handle = NULL;
115 }
116 
117 static void
118 destroy_method_info(method_info_t *mi)
119 {
120 	if (mi == NULL)
121 		return;
122 
123 	if (mi->wordexp_arg0_backup != NULL) {
124 		/*
125 		 * Return the wordexp structure back to its original
126 		 * state so it can be consumed by wordfree.
127 		 */
128 		free(mi->exec_args_we.we_wordv[0]);
129 		mi->exec_args_we.we_wordv[0] =
130 		    (char *)mi->wordexp_arg0_backup;
131 	}
132 
133 	free(mi->exec_path);
134 
135 	wordfree(&mi->exec_args_we);
136 
137 	free(mi);
138 }
139 
140 /*
141  * Transforms the properties read from the repository for a method into a
142  * method_info_t and returns a pointer to it. If expansion of the exec
143  * property fails, due to an invalid string or memory allocation failure,
144  * NULL is returned and exec_invalid is set appropriately to indicate whether
145  * it was a memory allocation failure or an invalid exec string.
146  */
147 static method_info_t *
148 create_method_info(const inetd_prop_t *mprops, boolean_t *exec_invalid)
149 {
150 	method_info_t	*ret;
151 	int		i;
152 
153 	debug_msg("Entering create_method_info");
154 
155 	if ((ret = calloc(1, sizeof (method_info_t))) == NULL)
156 		goto alloc_fail;
157 
158 	/* Expand the exec string. */
159 	if ((i = wordexp(get_prop_value_string(mprops, PR_EXEC_NAME),
160 	    &ret->exec_args_we, WRDE_NOCMD|WRDE_UNDEF)) != 0) {
161 		if (i == WRDE_NOSPACE)
162 			goto alloc_fail;
163 
164 		*exec_invalid = B_TRUE;
165 		free(ret);
166 		return (NULL);
167 	}
168 
169 	if ((ret->exec_path = strdup(ret->exec_args_we.we_wordv[0])) == NULL)
170 		goto alloc_fail;
171 
172 	if (mprops[MP_ARG0].ip_error == IVE_VALID) {	/* arg0 is set */
173 		/*
174 		 * Keep a copy of arg0 of the wordexp structure so that
175 		 * wordfree() gets passed what wordexp() originally returned,
176 		 * as documented as required in the man page.
177 		 */
178 		ret->wordexp_arg0_backup = ret->exec_args_we.we_wordv[0];
179 		if ((ret->exec_args_we.we_wordv[0] =
180 		    strdup(get_prop_value_string(mprops, PR_ARG0_NAME)))
181 		    == NULL)
182 			goto alloc_fail;
183 	}
184 
185 	if (mprops[MP_TIMEOUT].ip_error == IVE_VALID) {
186 		ret->timeout = get_prop_value_count(mprops,
187 		    SCF_PROPERTY_TIMEOUT);
188 	} else {
189 		ret->timeout = DEFAULT_METHOD_TIMEOUT;
190 	}
191 
192 	/* exec_invalid not set on success */
193 
194 	return (ret);
195 
196 alloc_fail:
197 	error_msg(strerror(errno));
198 	destroy_method_info(ret);
199 	*exec_invalid = B_FALSE;
200 	return (NULL);
201 }
202 
203 /*
204  * Returns B_TRUE if the contents of the 2 method_info_t structures are
205  * equivalent, else B_FALSE.
206  */
207 boolean_t
208 method_info_equal(const method_info_t *mi, const method_info_t *mi2)
209 {
210 	int		i;
211 
212 	debug_msg("Entering method_info_equal");
213 
214 	if ((mi == NULL) && (mi2 == NULL)) {
215 		return (B_TRUE);
216 	} else if (((mi == NULL) || (mi2 == NULL)) ||
217 	    (mi->exec_args_we.we_wordc != mi2->exec_args_we.we_wordc) ||
218 	    (strcmp(mi->exec_path, mi2->exec_path) != 0)) {
219 		return (B_FALSE);
220 	}
221 
222 	for (i = 0; i < mi->exec_args_we.we_wordc; i++) {
223 		if (strcmp(mi->exec_args_we.we_wordv[i],
224 		    mi2->exec_args_we.we_wordv[i]) != 0) {
225 			return (B_FALSE);
226 		}
227 	}
228 
229 	return (B_TRUE);
230 }
231 
232 /*
233  * Checks if the contents of the 2 socket_info_t structures are equivalent.
234  * If 'isrpc' is false, the address components of the two structures are
235  * compared for equality as part of this. If the two structures are
236  * equivalent B_TRUE is returned, else B_FALSE.
237  */
238 boolean_t
239 socket_info_equal(const socket_info_t *si, const socket_info_t *si2,
240     boolean_t isrpc)
241 {
242 	return ((isrpc || (memcmp(&si->local_addr, &si2->local_addr,
243 	    sizeof (si->local_addr)) == 0)) &&
244 	    (si->type == si2->type));
245 
246 }
247 
248 /*
249  * proto_info_t comparison function. Returns 0 on match, else -1, as required
250  * by uu_list_find().
251  */
252 static int
253 proto_info_compare(const void *lv, const void *rv, void *istlx)
254 {
255 	proto_info_t	*pi = (proto_info_t *)lv;
256 	proto_info_t	*pi2 = (proto_info_t *)rv;
257 
258 	/* check their RPC configuration matches */
259 	if (pi->ri != NULL) {
260 		if ((pi2->ri == NULL) || !rpc_info_equal(pi->ri, pi2->ri))
261 			return (-1);
262 	} else if (pi2->ri != NULL) {
263 		return (-1);
264 	}
265 
266 	if (pi->v6only != pi2->v6only)
267 		return (-1);
268 
269 	if (*(boolean_t *)istlx) {
270 		if (tlx_info_equal((tlx_info_t *)lv, (tlx_info_t *)rv,
271 		    pi->ri != NULL))
272 			return (0);
273 	} else {
274 		if (socket_info_equal((socket_info_t *)lv,
275 		    (socket_info_t *)rv, pi->ri != NULL))
276 			return (0);
277 	}
278 	return (-1);
279 }
280 
281 /*
282  * Returns B_TRUE if the bind configuration of the two instance_cfg_t
283  * structures are equivalent, else B_FALSE.
284  */
285 boolean_t
286 bind_config_equal(const basic_cfg_t *c1, const basic_cfg_t *c2)
287 {
288 	proto_info_t	*pi;
289 
290 	debug_msg("Entering bind_config_equal");
291 
292 	if ((c1->iswait != c2->iswait) ||
293 	    (c1->istlx != c2->istlx))
294 		return (B_FALSE);
295 
296 	if (uu_list_numnodes(c1->proto_list) !=
297 	    uu_list_numnodes(c2->proto_list))
298 		return (B_FALSE);
299 	/*
300 	 * For each element in the first configuration's socket/tlx list,
301 	 * check there's a matching one in the other list.
302 	 */
303 	for (pi = uu_list_first(c1->proto_list); pi != NULL;
304 	    pi = uu_list_next(c1->proto_list, pi)) {
305 		uu_list_index_t idx;
306 
307 		if (uu_list_find(c2->proto_list, pi, (void *)&c1->istlx,
308 		    &idx) == NULL)
309 			return (B_FALSE);
310 	}
311 
312 	return (B_TRUE);
313 }
314 
315 /*
316  * Write the default values contained in 'bprops', read by
317  * read_instance_props(), into 'cfg'.
318  * Returns -1 if memory allocation fails, else 0.
319  */
320 static int
321 populate_defaults(inetd_prop_t *bprops, basic_cfg_t *cfg)
322 {
323 	debug_msg("Entering populate_defaults");
324 
325 	cfg->do_tcp_wrappers = get_prop_value_boolean(bprops,
326 	    PR_DO_TCP_WRAPPERS_NAME);
327 	cfg->do_tcp_trace = get_prop_value_boolean(bprops,
328 	    PR_DO_TCP_TRACE_NAME);
329 	cfg->inherit_env = get_prop_value_boolean(bprops, PR_INHERIT_ENV_NAME);
330 	cfg->wait_fail_cnt = get_prop_value_int(bprops,
331 	    PR_MAX_FAIL_RATE_CNT_NAME);
332 	cfg->wait_fail_interval =  get_prop_value_int(bprops,
333 	    PR_MAX_FAIL_RATE_INTVL_NAME);
334 	cfg->max_copies = get_prop_value_int(bprops, PR_MAX_COPIES_NAME);
335 	cfg->conn_rate_offline = get_prop_value_int(bprops,
336 	    PR_CON_RATE_OFFLINE_NAME);
337 	cfg->conn_rate_max = get_prop_value_int(bprops, PR_CON_RATE_MAX_NAME);
338 	cfg->bind_fail_interval = get_prop_value_int(bprops,
339 	    PR_BIND_FAIL_INTVL_NAME);
340 	cfg->bind_fail_max = get_prop_value_int(bprops, PR_BIND_FAIL_MAX_NAME);
341 	if ((cfg->bind_addr =
342 	    strdup(get_prop_value_string(bprops, PR_BIND_ADDR_NAME))) == NULL) {
343 		error_msg(strerror(errno));
344 		return (-1);
345 	}
346 	return (0);
347 }
348 
349 void
350 destroy_method_infos(method_info_t **mis)
351 {
352 	int i;
353 
354 	for (i = 0; i < NUM_METHODS; i++) {
355 		destroy_method_info(mis[i]);
356 		mis[i] = NULL;
357 	}
358 }
359 
360 /*
361  * For each method, if it was specifed convert its entry in 'mprops',
362  * into an entry in 'mis'. Returns -1 if memory allocation fails or one of the
363  * exec strings was invalid, else 0.
364  */
365 static int
366 create_method_infos(const char *fmri, inetd_prop_t **mprops,
367     method_info_t **mis)
368 {
369 	int i;
370 
371 	debug_msg("Entering create_method_infos, inst: %s", fmri);
372 
373 	for (i = 0; i < NUM_METHODS; i++) {
374 		/*
375 		 * Only create a method info structure if the method properties
376 		 * contain an exec string, which we take to mean the method
377 		 * is specified.
378 		 */
379 		if (mprops[i][MP_EXEC].ip_error == IVE_VALID) {
380 			boolean_t exec_invalid;
381 
382 			if ((mis[i] = create_method_info(mprops[i],
383 			    &exec_invalid)) == NULL) {
384 				if (exec_invalid) {
385 					error_msg(gettext("Property %s for "
386 					    "method %s of instance %s is "
387 					    "invalid"), PR_EXEC_NAME,
388 					    methods[i].name, fmri);
389 				}
390 				return (-1);
391 			}
392 		}
393 	}
394 	return (0);
395 }
396 
397 /*
398  * Try and read each of the method properties for the method 'method' of
399  * instance 'inst', and return a table containing all method properties. If an
400  * error occurs, NULL is returned, with 'err' set to indicate the cause.
401  * Otherwise, a pointer to an inetd_prop_t table is returned containing all
402  * the method properties, and each of the properties is flagged according to
403  * whether it was present or not, and if it was present its value is set in
404  * the property's entry in the table.
405  */
406 static inetd_prop_t *
407 read_method_props(const char *inst, instance_method_t method, scf_error_t *err)
408 {
409 	inetd_prop_t	*ret;
410 	int		i;
411 
412 	debug_msg("Entering read_method_props");
413 
414 	if ((ret = calloc(1, sizeof (method_props))) == NULL) {
415 		*err = SCF_ERROR_NO_MEMORY;
416 		return (NULL);
417 	}
418 
419 	(void) memcpy(ret, method_props, sizeof (method_props));
420 	for (i = 0; ret[i].ip_name != NULL; i++) {
421 		*err = read_prop(rep_handle, &ret[i], i, inst,
422 		    methods[method].name);
423 		if ((*err != 0) && (*err != SCF_ERROR_NOT_FOUND)) {
424 			destroy_method_props(ret);
425 			return (NULL);
426 		}
427 	}
428 
429 	return (ret);
430 }
431 
432 static void
433 destroy_method_props(inetd_prop_t *mprop)
434 {
435 	int i;
436 
437 	if (mprop == NULL)
438 		return;
439 
440 	for (i = 0; mprop[i].ip_name != NULL; i++) {
441 		if (mprop[i].ip_type == INET_TYPE_STRING &&
442 		    mprop[i].ip_error == IVE_VALID)
443 			free(mprop[i].ip_value.iv_string);
444 	}
445 
446 	free(mprop);
447 }
448 
449 /*
450  * Destroy the basic and method properties returned by read_inst_props().
451  */
452 static void
453 destroy_inst_props(inetd_prop_t *bprops, inetd_prop_t **mprops)
454 {
455 	int	i;
456 
457 	free_instance_props(bprops);
458 	for (i = 0; i < NUM_METHODS; i++)
459 		destroy_method_props(mprops[i]);
460 }
461 
462 /*
463  * Read all the basic and method properties for instance 'inst', as inetd_prop_t
464  * tables, into the spaces referenced by 'bprops' and 'mprops' respectively.
465  * Each of the properties in the tables are flagged to indicate if the
466  * property was present or not, and if it was the value is stored within it.
467  * If an error occurs at any time -1 is returned and 'err' is set to
468  * indicate the reason, else 0 is returned.
469  */
470 static int
471 read_inst_props(const char *fmri, inetd_prop_t **bprops,
472     inetd_prop_t **mprops, scf_error_t *err)
473 {
474 	size_t		nprops;
475 	int		i;
476 
477 	debug_msg("Entering read_inst_props");
478 
479 	if ((*bprops = read_instance_props(rep_handle, (char *)fmri, &nprops,
480 	    err)) == NULL)
481 		return (-1);
482 
483 	for (i = 0; i < NUM_METHODS; i++) {
484 		if ((mprops[i] =
485 		    read_method_props(fmri, (instance_method_t)i, err)) ==
486 		    NULL) {
487 			for (i--; i >= 0; i--)
488 				destroy_method_props(mprops[i]);
489 			free_instance_props(*bprops);
490 			return (-1);
491 		}
492 	}
493 
494 	return (0);
495 }
496 
497 /*
498  * Returns B_TRUE if all required properties were read from the repository
499  * (whether taken from the defaults or directly from the instance), they
500  * all had valid values, all the required methods were present, and they
501  * each had the required properties with valid values. Else, returns B_FALSE.
502  * If the function returns B_TRUE, the storage referenced by 'cfg' is set
503  * to point at an allocated instance_cfg_t initialized based on the basic
504  * properties (not method or defaults).
505  */
506 static boolean_t
507 valid_inst_props(const char *fmri, inetd_prop_t *bprops, inetd_prop_t **mprops,
508     basic_cfg_t **cfg)
509 {
510 	boolean_t	valid;
511 	size_t		num_bprops;
512 	int		i;
513 
514 	debug_msg("Entering valid_inst_props: inst: %s, bprops: %x, mprops: %x",
515 	    fmri, bprops, *mprops);
516 
517 	valid = valid_props(bprops, fmri, cfg, proto_info_pool, conn_ind_pool);
518 
519 	/*
520 	 * Double check we've got all necessary properties (valid_props()
521 	 * doesn't enforce the presence of defaults), and output error messages
522 	 * for each invalid/ missing property.
523 	 */
524 	(void) get_prop_table(&num_bprops);
525 	for (i = 0; bprops[i].ip_name != NULL; i++) {
526 		switch (bprops[i].ip_error) {
527 		case IVE_UNSET:
528 			if (!bprops[i].ip_default)
529 				continue;
530 			if ((i == PT_ARG0_INDEX) || (i == PT_EXEC_INDEX))
531 				continue;
532 			/* FALLTHROUGH */
533 		case IVE_INVALID:
534 			error_msg(gettext("Property '%s' of instance "
535 			    "%s is missing, inconsistent or invalid"),
536 			    bprops[i].ip_name, fmri);
537 			valid = B_FALSE;
538 		}
539 	}
540 
541 	for (i = 0; i < NUM_METHODS; i++) {
542 		int	j;
543 
544 		/* check if any properties are set */
545 		for (j = 0; mprops[i][j].ip_name != NULL; j++) {
546 			if (mprops[i][j].ip_error != IVE_UNSET)
547 				break;
548 		}
549 
550 		if (mprops[i][j].ip_name == NULL) {
551 			/* an unspecified method */
552 			if ((instance_method_t)i == IM_START) {
553 				error_msg(gettext(
554 				    "Unspecified %s method for instance %s"),
555 				    START_METHOD_NAME, fmri);
556 				valid = B_FALSE;
557 			}
558 		} else if (mprops[i][MP_EXEC].ip_error == IVE_UNSET) {
559 			error_msg(gettext("Missing %s property from method %s "
560 			    "of instance %s"), PR_EXEC_NAME,
561 			    methods[(instance_method_t)i].name, fmri);
562 			valid = B_FALSE;
563 		}
564 	}
565 
566 	if (!valid)
567 		destroy_basic_cfg(*cfg);
568 
569 	return (valid);
570 }
571 
572 void
573 destroy_instance_cfg(instance_cfg_t *cfg)
574 {
575 	if (cfg != NULL) {
576 		destroy_basic_cfg(cfg->basic);
577 		destroy_method_infos(cfg->methods);
578 		free(cfg);
579 	}
580 }
581 
582 /*
583  * Returns an allocated instance_cfg_t representation of an instance's
584  * configuration read from the repository. If the configuration is invalid, a
585  * repository error occurred, or a memory allocation occurred returns NULL,
586  * else returns a pointer to the allocated instance_cfg_t.
587  */
588 instance_cfg_t *
589 read_instance_cfg(const char *fmri)
590 {
591 	uint_t		retries;
592 	inetd_prop_t	*bprops;
593 	inetd_prop_t	*mprops[NUM_METHODS];
594 	instance_cfg_t	*ret = NULL;
595 	scf_error_t	err;
596 
597 	debug_msg("Entering read_instance_cfg");
598 
599 	if ((ret = calloc(1, sizeof (instance_cfg_t))) == NULL)
600 		return (NULL);
601 
602 	for (retries = 0; retries <= REP_OP_RETRIES; retries++) {
603 		if (make_handle_bound(rep_handle) == -1) {
604 			err = scf_error();
605 			goto read_error;
606 		}
607 
608 		if (read_inst_props(fmri, &bprops, mprops, &err) == 0)
609 			break;
610 		if (err != SCF_ERROR_CONNECTION_BROKEN)
611 			goto read_error;
612 		(void) scf_handle_unbind(rep_handle);
613 	}
614 	if (retries > REP_OP_RETRIES)
615 		goto read_error;
616 
617 	/*
618 	 * Switch off validation of the start method's exec string, since
619 	 * during boot the filesystem it resides on may not have been
620 	 * mounted yet, which would result in a false validation failure.
621 	 * We'll catch any real errors when the start method is first run
622 	 * in passes_basic_exec_checks().
623 	 */
624 	bprops[PT_EXEC_INDEX].ip_error = IVE_UNSET;
625 
626 	if ((!valid_inst_props(fmri, bprops, mprops, &ret->basic)) ||
627 	    (populate_defaults(bprops, ret->basic) != 0) ||
628 	    (create_method_infos(fmri, mprops, ret->methods) != 0)) {
629 		destroy_instance_cfg(ret);
630 		ret = NULL;
631 	}
632 
633 	destroy_inst_props(bprops, mprops);
634 	return (ret);
635 
636 read_error:
637 	error_msg(gettext(
638 	    "Failed to read the configuration of instance %s: %s"), fmri,
639 	    scf_strerror(err));
640 	free(ret);
641 	return (NULL);
642 }
643 
644 /*
645  * Returns a pointer to an allocated method context for the specified method
646  * of the specified instance if it could retrieve it. Else, if there were
647  * errors retrieving it, NULL is returned and the pointer referenced by
648  * 'errstr' is set to point at an appropriate error string.
649  */
650 struct method_context *
651 read_method_context(const char *inst_fmri, const char *method, const char *path,
652     const char **errstr)
653 {
654 	scf_instance_t			*scf_inst = NULL;
655 	struct method_context		*ret;
656 	uint_t				retries;
657 	const char			*tmpstr;
658 
659 	debug_msg("Entering read_method_context: inst: %s, method: %s, "
660 	    "path: %s", inst_fmri, method, path);
661 
662 	for (retries = 0; retries <= REP_OP_RETRIES; retries++) {
663 		if (make_handle_bound(rep_handle) == -1)
664 			goto inst_failure;
665 
666 		if (((scf_inst = scf_instance_create(rep_handle)) != NULL) &&
667 		    (scf_handle_decode_fmri(rep_handle, inst_fmri, NULL, NULL,
668 		    scf_inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) == 0))
669 			break;
670 		if (scf_error() != SCF_ERROR_CONNECTION_BROKEN) {
671 			scf_instance_destroy(scf_inst);
672 			goto inst_failure;
673 		}
674 
675 		(void) scf_instance_destroy(scf_inst);
676 		scf_inst = NULL;
677 
678 		(void) scf_handle_unbind(rep_handle);
679 	}
680 	if (retries > REP_OP_RETRIES)
681 		goto inst_failure;
682 
683 	if ((tmpstr = restarter_get_method_context(
684 	    RESTARTER_METHOD_CONTEXT_VERSION, scf_inst, NULL, method, path,
685 	    &ret)) != NULL) {
686 		ret = NULL;
687 		*errstr = tmpstr;
688 	}
689 
690 	scf_instance_destroy(scf_inst);
691 	return (ret);
692 
693 inst_failure:
694 	/*
695 	 * We can rely on this string not becoming invalid
696 	 * since we don't call bind_textdomain_codeset() or
697 	 * setlocale(3C) after initialization.
698 	 */
699 	*errstr = gettext("failed to get instance from repository");
700 	return (NULL);
701 }
702 
703 /*
704  * Reads the value of the enabled property from the named property group
705  * of the given instance.
706  * If an error occurs, the SCF error code is returned. The possible errors are:
707  * - SCF_ERROR_INVALID_ARGUMENT: The enabled property is not a boolean.
708  * - SCF_ERROR_NONE: No value exists for the enabled property.
709  * - SCF_ERROR_CONNECTION_BROKEN: Repository connection broken.
710  * - SCF_ERROR_NOT_FOUND: The property wasn't found.
711  * - SCF_ERROR_NO_MEMORY: allocation failure.
712  * Else 0 is returned and 'enabled' set appropriately.
713  */
714 static scf_error_t
715 read_enable_prop(const char *fmri, boolean_t *enabled, const char *pg)
716 {
717 	scf_simple_prop_t	*sp;
718 	uint8_t			*u8p;
719 
720 	if ((sp = scf_simple_prop_get(rep_handle, fmri, pg,
721 	    SCF_PROPERTY_ENABLED)) == NULL)
722 		return (scf_error());
723 
724 	if ((u8p = scf_simple_prop_next_boolean(sp)) == NULL) {
725 		scf_simple_prop_free(sp);
726 		return (scf_error());
727 	}
728 
729 	*enabled = (*u8p != 0);
730 	scf_simple_prop_free(sp);
731 	return (0);
732 }
733 
734 /*
735  * Reads the enabled value for the given instance FMRI. The read value
736  * is based on a merge of the 'standard' enabled property, and the temporary
737  * override one; the merge involves using the latter properties value if
738  * present, else resporting to the formers. If an error occurs -1 is returned,
739  * else 0 is returned and 'enabled' set approriately.
740  */
741 int
742 read_enable_merged(const char *fmri, boolean_t *enabled)
743 {
744 	uint_t		retries;
745 
746 	debug_msg("Entering read_enabled_prop: inst: %s", fmri);
747 
748 	for (retries = 0; retries <= REP_OP_RETRIES; retries++) {
749 		if (make_handle_bound(rep_handle) == -1)
750 			goto gen_fail;
751 
752 		switch (read_enable_prop(fmri, enabled, SCF_PG_GENERAL_OVR)) {
753 		case 0:
754 			debug_msg("read %d from override", *enabled);
755 			return (0);
756 		case SCF_ERROR_CONNECTION_BROKEN:
757 			break;
758 		case SCF_ERROR_NOT_FOUND:
759 		case SCF_ERROR_NONE:
760 		case SCF_ERROR_INVALID_ARGUMENT:
761 			switch (read_enable_prop(fmri, enabled,
762 			    SCF_PG_GENERAL)) {
763 			case 0:
764 				debug_msg("read %d from non_override",
765 				    *enabled);
766 				return (0);
767 			case SCF_ERROR_CONNECTION_BROKEN:
768 				break;
769 			case SCF_ERROR_NOT_FOUND:
770 			case SCF_ERROR_NONE:
771 			case SCF_ERROR_INVALID_ARGUMENT:
772 				error_msg(gettext("Missing %s property/value "
773 				    "for instance %s"), SCF_PROPERTY_ENABLED,
774 				    fmri);
775 				return (-1);
776 			default:
777 				goto gen_fail;
778 			}
779 			break;
780 		default:
781 			goto gen_fail;
782 		}
783 
784 		(void) scf_handle_unbind(rep_handle);
785 		continue;
786 	}
787 
788 gen_fail:
789 	error_msg(gettext("Failed to read the %s property of instance %s: %s"),
790 	    SCF_PROPERTY_ENABLED, fmri, scf_strerror(scf_error()));
791 	return (-1);
792 }
793