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