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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
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
37 #include <netinet/in.h>
39 #include <nss_dbdefs.h>
42 #include "inetd_impl.h"
45 /* method timeout used if one isn't explicitly specified */
46 #define DEFAULT_METHOD_TIMEOUT 10
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
},
57 /* enumeration of method properties; used to index into method_props[] */
65 /* handle used for repository access in read_prop() */
66 static scf_handle_t
*rep_handle
= NULL
;
68 /* pool used to create proto_info_t lists (generic proto info structure) */
69 static uu_list_pool_t
*proto_info_pool
= NULL
;
71 static void destroy_method_props(inetd_prop_t
*);
72 static int proto_info_compare(const void *, const void *, void *);
77 if ((rep_handle
= scf_handle_create(SCF_VERSION
)) == NULL
) {
79 gettext("Failed to create repository handle"),
80 scf_strerror(scf_error()));
82 } else if (make_handle_bound(rep_handle
) == -1) {
83 /* let config_fini clean-up */
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()));
101 if (rep_handle
== NULL
)
104 if (proto_info_pool
!= NULL
) {
105 uu_list_pool_destroy(proto_info_pool
);
106 proto_info_pool
= NULL
;
109 (void) scf_handle_unbind(rep_handle
);
110 scf_handle_destroy(rep_handle
);
115 destroy_method_info(method_info_t
*mi
)
120 if (mi
->wordexp_arg0_backup
!= NULL
) {
122 * Return the wordexp structure back to its original
123 * state so it can be consumed by wordfree.
125 free(mi
->exec_args_we
.we_wordv
[0]);
126 mi
->exec_args_we
.we_wordv
[0] =
127 (char *)mi
->wordexp_arg0_backup
;
132 wordfree(&mi
->exec_args_we
);
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.
144 static method_info_t
*
145 create_method_info(const inetd_prop_t
*mprops
, boolean_t
*exec_invalid
)
150 if ((ret
= calloc(1, sizeof (method_info_t
))) == NULL
)
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
)
159 *exec_invalid
= B_TRUE
;
164 if ((ret
->exec_path
= strdup(ret
->exec_args_we
.we_wordv
[0])) == NULL
)
167 if (mprops
[MP_ARG0
].ip_error
== IVE_VALID
) { /* arg0 is set */
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.
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
)))
180 if (mprops
[MP_TIMEOUT
].ip_error
== IVE_VALID
) {
181 ret
->timeout
= get_prop_value_count(mprops
,
182 SCF_PROPERTY_TIMEOUT
);
184 ret
->timeout
= DEFAULT_METHOD_TIMEOUT
;
187 /* exec_invalid not set on success */
192 error_msg(strerror(errno
));
193 destroy_method_info(ret
);
194 *exec_invalid
= B_FALSE
;
199 * Returns B_TRUE if the contents of the 2 method_info_t structures are
200 * equivalent, else B_FALSE.
203 method_info_equal(const method_info_t
*mi
, const method_info_t
*mi2
)
207 if ((mi
== NULL
) && (mi2
== NULL
)) {
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)) {
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) {
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.
232 socket_info_equal(const socket_info_t
*si
, const socket_info_t
*si2
,
235 return ((isrpc
|| (memcmp(&si
->local_addr
, &si2
->local_addr
,
236 sizeof (si
->local_addr
)) == 0)) &&
237 (si
->type
== si2
->type
));
242 * proto_info_t comparison function. Returns 0 on match, else -1, as required
246 proto_info_compare(const void *lv
, const void *rv
, void *istlx
)
248 proto_info_t
*pi
= (proto_info_t
*)lv
;
249 proto_info_t
*pi2
= (proto_info_t
*)rv
;
251 /* check their RPC configuration matches */
252 if (pi
->ri
!= NULL
) {
253 if ((pi2
->ri
== NULL
) || !rpc_info_equal(pi
->ri
, pi2
->ri
))
255 } else if (pi2
->ri
!= NULL
) {
259 if (pi
->v6only
!= pi2
->v6only
)
262 if (*(boolean_t
*)istlx
) {
263 if (tlx_info_equal((tlx_info_t
*)lv
, (tlx_info_t
*)rv
,
267 if (socket_info_equal((socket_info_t
*)lv
,
268 (socket_info_t
*)rv
, pi
->ri
!= NULL
))
275 * Returns B_TRUE if the bind configuration of the two instance_cfg_t
276 * structures are equivalent, else B_FALSE.
279 bind_config_equal(const basic_cfg_t
*c1
, const basic_cfg_t
*c2
)
283 if ((c1
->iswait
!= c2
->iswait
) ||
284 (c1
->istlx
!= c2
->istlx
))
287 if (uu_list_numnodes(c1
->proto_list
) !=
288 uu_list_numnodes(c2
->proto_list
))
291 * For each element in the first configuration's socket/tlx list,
292 * check there's a matching one in the other list.
294 for (pi
= uu_list_first(c1
->proto_list
); pi
!= NULL
;
295 pi
= uu_list_next(c1
->proto_list
, pi
)) {
298 if (uu_list_find(c2
->proto_list
, pi
, (void *)&c1
->istlx
,
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.
312 populate_defaults(inetd_prop_t
*bprops
, basic_cfg_t
*cfg
)
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
->do_tcp_keepalive
= get_prop_value_boolean(bprops
,
319 PR_DO_TCP_KEEPALIVE_NAME
);
320 cfg
->inherit_env
= get_prop_value_boolean(bprops
, PR_INHERIT_ENV_NAME
);
321 cfg
->wait_fail_cnt
= get_prop_value_int(bprops
,
322 PR_MAX_FAIL_RATE_CNT_NAME
);
323 cfg
->wait_fail_interval
= get_prop_value_int(bprops
,
324 PR_MAX_FAIL_RATE_INTVL_NAME
);
325 cfg
->max_copies
= get_prop_value_int(bprops
, PR_MAX_COPIES_NAME
);
326 cfg
->conn_rate_offline
= get_prop_value_int(bprops
,
327 PR_CON_RATE_OFFLINE_NAME
);
328 cfg
->conn_rate_max
= get_prop_value_int(bprops
, PR_CON_RATE_MAX_NAME
);
329 cfg
->bind_fail_interval
= get_prop_value_int(bprops
,
330 PR_BIND_FAIL_INTVL_NAME
);
331 cfg
->bind_fail_max
= get_prop_value_int(bprops
, PR_BIND_FAIL_MAX_NAME
);
332 cfg
->conn_backlog
= get_prop_value_int(bprops
,
333 PR_CONNECTION_BACKLOG_NAME
);
334 if ((cfg
->bind_addr
=
335 strdup(get_prop_value_string(bprops
, PR_BIND_ADDR_NAME
))) == NULL
) {
336 error_msg(strerror(errno
));
343 destroy_method_infos(method_info_t
**mis
)
347 for (i
= 0; i
< NUM_METHODS
; i
++) {
348 destroy_method_info(mis
[i
]);
354 * For each method, if it was specifed convert its entry in 'mprops',
355 * into an entry in 'mis'. Returns -1 if memory allocation fails or one of the
356 * exec strings was invalid, else 0.
359 create_method_infos(const char *fmri
, inetd_prop_t
**mprops
,
364 for (i
= 0; i
< NUM_METHODS
; i
++) {
366 * Only create a method info structure if the method properties
367 * contain an exec string, which we take to mean the method
370 if (mprops
[i
][MP_EXEC
].ip_error
== IVE_VALID
) {
371 boolean_t exec_invalid
;
373 if ((mis
[i
] = create_method_info(mprops
[i
],
374 &exec_invalid
)) == NULL
) {
376 error_msg(gettext("Property %s for "
377 "method %s of instance %s is "
378 "invalid"), PR_EXEC_NAME
,
379 methods
[i
].name
, fmri
);
389 * Try and read each of the method properties for the method 'method' of
390 * instance 'inst', and return a table containing all method properties. If an
391 * error occurs, NULL is returned, with 'err' set to indicate the cause.
392 * Otherwise, a pointer to an inetd_prop_t table is returned containing all
393 * the method properties, and each of the properties is flagged according to
394 * whether it was present or not, and if it was present its value is set in
395 * the property's entry in the table.
397 static inetd_prop_t
*
398 read_method_props(const char *inst
, instance_method_t method
, scf_error_t
*err
)
403 if ((ret
= calloc(1, sizeof (method_props
))) == NULL
) {
404 *err
= SCF_ERROR_NO_MEMORY
;
408 (void) memcpy(ret
, method_props
, sizeof (method_props
));
409 for (i
= 0; ret
[i
].ip_name
!= NULL
; i
++) {
410 *err
= read_prop(rep_handle
, &ret
[i
], i
, inst
,
411 methods
[method
].name
);
412 if ((*err
!= 0) && (*err
!= SCF_ERROR_NOT_FOUND
)) {
413 destroy_method_props(ret
);
422 destroy_method_props(inetd_prop_t
*mprop
)
429 for (i
= 0; mprop
[i
].ip_name
!= NULL
; i
++) {
430 if (mprop
[i
].ip_type
== INET_TYPE_STRING
&&
431 mprop
[i
].ip_error
== IVE_VALID
)
432 free(mprop
[i
].ip_value
.iv_string
);
439 * Destroy the basic and method properties returned by read_inst_props().
442 destroy_inst_props(inetd_prop_t
*bprops
, inetd_prop_t
**mprops
)
446 free_instance_props(bprops
);
447 for (i
= 0; i
< NUM_METHODS
; i
++)
448 destroy_method_props(mprops
[i
]);
452 * Read all the basic and method properties for instance 'inst', as inetd_prop_t
453 * tables, into the spaces referenced by 'bprops' and 'mprops' respectively.
454 * Each of the properties in the tables are flagged to indicate if the
455 * property was present or not, and if it was the value is stored within it.
456 * If an error occurs at any time -1 is returned and 'err' is set to
457 * indicate the reason, else 0 is returned.
460 read_inst_props(const char *fmri
, inetd_prop_t
**bprops
,
461 inetd_prop_t
**mprops
, scf_error_t
*err
)
466 if ((*bprops
= read_instance_props(rep_handle
, (char *)fmri
, &nprops
,
470 for (i
= 0; i
< NUM_METHODS
; i
++) {
472 read_method_props(fmri
, (instance_method_t
)i
, err
)) ==
474 for (i
--; i
>= 0; i
--)
475 destroy_method_props(mprops
[i
]);
476 free_instance_props(*bprops
);
485 * Returns B_TRUE if all required properties were read from the repository
486 * (whether taken from the defaults or directly from the instance), they
487 * all had valid values, all the required methods were present, and they
488 * each had the required properties with valid values. Else, returns B_FALSE.
489 * If the function returns B_TRUE, the storage referenced by 'cfg' is set
490 * to point at an allocated instance_cfg_t initialized based on the basic
491 * properties (not method or defaults).
494 valid_inst_props(const char *fmri
, inetd_prop_t
*bprops
, inetd_prop_t
**mprops
,
501 valid
= valid_props(bprops
, fmri
, cfg
, proto_info_pool
, conn_ind_pool
);
504 * Double check we've got all necessary properties (valid_props()
505 * doesn't enforce the presence of defaults), and output error messages
506 * for each invalid/ missing property.
508 (void) get_prop_table(&num_bprops
);
509 for (i
= 0; bprops
[i
].ip_name
!= NULL
; i
++) {
510 switch (bprops
[i
].ip_error
) {
512 if (!bprops
[i
].ip_default
)
514 if ((i
== PT_ARG0_INDEX
) || (i
== PT_EXEC_INDEX
))
518 error_msg(gettext("Property '%s' of instance "
519 "%s is missing, inconsistent or invalid"),
520 bprops
[i
].ip_name
, fmri
);
525 for (i
= 0; i
< NUM_METHODS
; i
++) {
528 /* check if any properties are set */
529 for (j
= 0; mprops
[i
][j
].ip_name
!= NULL
; j
++) {
530 if (mprops
[i
][j
].ip_error
!= IVE_UNSET
)
534 if (mprops
[i
][j
].ip_name
== NULL
) {
535 /* an unspecified method */
536 if ((instance_method_t
)i
== IM_START
) {
538 "Unspecified %s method for instance %s"),
539 START_METHOD_NAME
, fmri
);
542 } else if (mprops
[i
][MP_EXEC
].ip_error
== IVE_UNSET
) {
543 error_msg(gettext("Missing %s property from method %s "
544 "of instance %s"), PR_EXEC_NAME
,
545 methods
[(instance_method_t
)i
].name
, fmri
);
551 destroy_basic_cfg(*cfg
);
559 destroy_instance_cfg(instance_cfg_t
*cfg
)
562 destroy_basic_cfg(cfg
->basic
);
563 destroy_method_infos(cfg
->methods
);
569 * Returns an allocated instance_cfg_t representation of an instance's
570 * configuration read from the repository. If the configuration is invalid, a
571 * repository error occurred, or a memory allocation occurred returns NULL,
572 * else returns a pointer to the allocated instance_cfg_t.
575 read_instance_cfg(const char *fmri
)
578 inetd_prop_t
*bprops
;
579 inetd_prop_t
*mprops
[NUM_METHODS
];
580 instance_cfg_t
*ret
= NULL
;
583 if ((ret
= calloc(1, sizeof (instance_cfg_t
))) == NULL
)
586 for (retries
= 0; retries
<= REP_OP_RETRIES
; retries
++) {
587 if (make_handle_bound(rep_handle
) == -1) {
592 if (read_inst_props(fmri
, &bprops
, mprops
, &err
) == 0)
594 if (err
!= SCF_ERROR_CONNECTION_BROKEN
)
596 (void) scf_handle_unbind(rep_handle
);
598 if (retries
> REP_OP_RETRIES
)
602 * Switch off validation of the start method's exec string, since
603 * during boot the filesystem it resides on may not have been
604 * mounted yet, which would result in a false validation failure.
605 * We'll catch any real errors when the start method is first run
606 * in passes_basic_exec_checks().
608 bprops
[PT_EXEC_INDEX
].ip_error
= IVE_UNSET
;
610 if ((!valid_inst_props(fmri
, bprops
, mprops
, &ret
->basic
)) ||
611 (populate_defaults(bprops
, ret
->basic
) != 0) ||
612 (create_method_infos(fmri
, mprops
, ret
->methods
) != 0)) {
613 destroy_instance_cfg(ret
);
617 destroy_inst_props(bprops
, mprops
);
622 "Failed to read the configuration of instance %s: %s"), fmri
,
629 * Returns a pointer to an allocated method context for the specified method
630 * of the specified instance if it could retrieve it. Else, if there were
631 * errors retrieving it, NULL is returned and the pointer referenced by
632 * 'errstr' is set to point at an appropriate error string.
634 struct method_context
*
635 read_method_context(const char *inst_fmri
, const char *method
, const char *path
)
637 scf_instance_t
*scf_inst
= NULL
;
638 struct method_context
*ret
;
643 fail
= gettext("Failed to retrieve method context for the %s method of "
645 for (retries
= 0; retries
<= REP_OP_RETRIES
; retries
++) {
646 if (make_handle_bound(rep_handle
) == -1)
649 if (((scf_inst
= scf_instance_create(rep_handle
)) != NULL
) &&
650 (scf_handle_decode_fmri(rep_handle
, inst_fmri
, NULL
, NULL
,
651 scf_inst
, NULL
, NULL
, SCF_DECODE_FMRI_EXACT
) == 0))
653 if (scf_error() != SCF_ERROR_CONNECTION_BROKEN
) {
654 scf_instance_destroy(scf_inst
);
658 (void) scf_instance_destroy(scf_inst
);
661 (void) scf_handle_unbind(rep_handle
);
663 if (retries
> REP_OP_RETRIES
)
666 if ((tmperr
= restarter_get_method_context(
667 RESTARTER_METHOD_CONTEXT_VERSION
, scf_inst
, NULL
, method
, path
,
670 error_msg(fail
, method
, inst_fmri
, tmperr
->msg
);
671 restarter_mc_error_destroy(tmperr
);
674 scf_instance_destroy(scf_inst
);
679 * We can rely on this string not becoming invalid
680 * since we don't call bind_textdomain_codeset() or
681 * setlocale(3C) after initialization.
683 error_msg(fail
, method
, inst_fmri
,
684 gettext("failed to get instance from repository"));
689 * Reads the value of the enabled property from the named property group
690 * of the given instance.
691 * If an error occurs, the SCF error code is returned. The possible errors are:
692 * - SCF_ERROR_INVALID_ARGUMENT: The enabled property is not a boolean.
693 * - SCF_ERROR_NONE: No value exists for the enabled property.
694 * - SCF_ERROR_CONNECTION_BROKEN: Repository connection broken.
695 * - SCF_ERROR_NOT_FOUND: The property wasn't found.
696 * - SCF_ERROR_NO_MEMORY: allocation failure.
697 * Else 0 is returned and 'enabled' set appropriately.
700 read_enable_prop(const char *fmri
, boolean_t
*enabled
, const char *pg
)
702 scf_simple_prop_t
*sp
;
705 if ((sp
= scf_simple_prop_get(rep_handle
, fmri
, pg
,
706 SCF_PROPERTY_ENABLED
)) == NULL
)
707 return (scf_error());
709 if ((u8p
= scf_simple_prop_next_boolean(sp
)) == NULL
) {
710 scf_simple_prop_free(sp
);
711 return (scf_error());
714 *enabled
= (*u8p
!= 0);
715 scf_simple_prop_free(sp
);
720 * Reads the enabled value for the given instance FMRI. The read value
721 * is based on a merge of the 'standard' enabled property, and the temporary
722 * override one; the merge involves using the latter properties value if
723 * present, else resporting to the formers. If an error occurs -1 is returned,
724 * else 0 is returned and 'enabled' set approriately.
727 read_enable_merged(const char *fmri
, boolean_t
*enabled
)
731 for (retries
= 0; retries
<= REP_OP_RETRIES
; retries
++) {
732 if (make_handle_bound(rep_handle
) == -1)
735 switch (read_enable_prop(fmri
, enabled
, SCF_PG_GENERAL_OVR
)) {
737 debug_msg("read %d from override", *enabled
);
739 case SCF_ERROR_CONNECTION_BROKEN
:
741 case SCF_ERROR_NOT_FOUND
:
743 case SCF_ERROR_INVALID_ARGUMENT
:
744 switch (read_enable_prop(fmri
, enabled
,
747 debug_msg("read %d from non_override",
750 case SCF_ERROR_CONNECTION_BROKEN
:
752 case SCF_ERROR_NOT_FOUND
:
754 case SCF_ERROR_INVALID_ARGUMENT
:
755 error_msg(gettext("Missing %s property/value "
756 "for instance %s"), SCF_PROPERTY_ENABLED
,
767 (void) scf_handle_unbind(rep_handle
);
772 error_msg(gettext("Failed to read the %s property of instance %s: %s"),
773 SCF_PROPERTY_ENABLED
, fmri
, scf_strerror(scf_error()));
778 * Refresh the value of debug property under the property group "config"
779 * for network/inetd service.
782 refresh_debug_flag(void)
784 scf_simple_prop_t
*sprop
;
787 if ((sprop
= scf_simple_prop_get(rep_handle
, INETD_INSTANCE_FMRI
,
788 PG_NAME_APPLICATION_CONFIG
, PR_NAME_DEBUG_FLAG
)) == NULL
) {
789 error_msg(gettext("Unable to read %s property from %s property "
790 "group. scf_simple_prop_get() failed: %s"),
791 PR_NAME_DEBUG_FLAG
, PG_NAME_APPLICATION_CONFIG
,
792 scf_strerror(scf_error()));
794 } else if ((tmp_bool
= scf_simple_prop_next_boolean(sprop
)) == NULL
) {
795 error_msg(gettext("Unable to read %s property for %s service. "
796 "scf_simple_prop_next_boolean() failed: %s"),
797 PR_NAME_DEBUG_FLAG
, INETD_INSTANCE_FMRI
,
798 scf_strerror(scf_error()));
800 debug_enabled
= ((*tmp_bool
== 0) ? B_FALSE
: B_TRUE
);
803 scf_simple_prop_free(sprop
);