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