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