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]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * This file contains routines to manipulate lists of repository values that
31 * are used to store process ids and the internal state. There are routines
32 * to read/write the lists from/to the repository and routines to modify or
33 * inspect the lists. It also contains routines that deal with the
34 * repository side of contract ids.
43 #include <sys/param.h>
44 #include <sys/types.h>
46 #include <libscf_priv.h>
47 #include "inetd_impl.h"
50 * Number of consecutive repository bind retries performed by bind_to_rep()
53 #define BIND_TO_REP_RETRIES 10
55 /* Name of property group where inetd's state for a service is stored. */
56 #define PG_NAME_INSTANCE_STATE (const char *) "inetd_state"
58 /* uu_list repval list pool */
59 static uu_list_pool_t
*rep_val_pool
= NULL
;
62 * Repository object pointers that get set-up in repval_init() and closed down
63 * in repval_fini(). They're used in _retrieve_rep_vals(), _store_rep_vals(),
64 * add_remove_contract_norebind(), and adopt_repository_contracts(). They're
65 * global so they can be initialized once on inetd startup, and re-used
66 * there-after in the referenced functions.
68 static scf_handle_t
*rep_handle
= NULL
;
69 static scf_propertygroup_t
*pg
= NULL
;
70 static scf_instance_t
*inst
= NULL
;
71 static scf_transaction_t
*trans
= NULL
;
72 static scf_transaction_entry_t
*entry
= NULL
;
73 static scf_property_t
*prop
= NULL
;
76 * Pathname storage for paths generated from the fmri.
77 * Used when updating the ctid and (start) pid files for an inetd service.
79 static char genfmri_filename
[MAXPATHLEN
] = "";
80 static char genfmri_temp_filename
[MAXPATHLEN
] = "";
83 * Try and make the given handle bind be bound to the repository. If
84 * it's already bound, or we succeed a new bind return 0; else return
85 * -1 on failure, with the SCF error set to one of the following:
87 * SCF_ERROR_NO_RESOURCES
90 make_handle_bound(scf_handle_t
*hdl
)
94 for (retries
= 0; retries
<= BIND_TO_REP_RETRIES
; retries
++) {
95 if ((scf_handle_bind(hdl
) == 0) ||
96 (scf_error() == SCF_ERROR_IN_USE
))
99 assert(scf_error() != SCF_ERROR_INVALID_ARGUMENT
);
109 * Create the repval list pool.
111 rep_val_pool
= uu_list_pool_create("rep_val_pool", sizeof (rep_val_t
),
112 offsetof(rep_val_t
, link
), NULL
, UU_LIST_POOL_DEBUG
);
113 if (rep_val_pool
== NULL
) {
114 error_msg("%s: %s", gettext("Failed to create rep_val pool"),
115 uu_strerror(uu_error()));
120 * Create and bind a repository handle, and create all repository
121 * objects that we'll use later that are associated with it. On any
122 * errors we simply return -1 and let repval_fini() clean-up after
125 if ((rep_handle
= scf_handle_create(SCF_VERSION
)) == NULL
) {
127 gettext("Failed to create repository handle"),
128 scf_strerror(scf_error()));
130 } else if (make_handle_bound(rep_handle
) == -1) {
132 } else if (((pg
= scf_pg_create(rep_handle
)) == NULL
) ||
133 ((inst
= scf_instance_create(rep_handle
)) == NULL
) ||
134 ((trans
= scf_transaction_create(rep_handle
)) == NULL
) ||
135 ((entry
= scf_entry_create(rep_handle
)) == NULL
) ||
136 ((prop
= scf_property_create(rep_handle
)) == NULL
)) {
138 gettext("Failed to create repository object"),
139 scf_strerror(scf_error()));
152 if (rep_handle
!= NULL
) {
154 * We unbind from the repository before we free the repository
155 * objects for efficiency reasons.
157 (void) scf_handle_unbind(rep_handle
);
161 scf_instance_destroy(inst
);
163 scf_transaction_destroy(trans
);
165 scf_entry_destroy(entry
);
167 scf_property_destroy(prop
);
170 scf_handle_destroy(rep_handle
);
174 if (rep_val_pool
!= NULL
) {
175 uu_list_pool_destroy(rep_val_pool
);
181 create_rep_val_list(void)
185 if ((ret
= uu_list_create(rep_val_pool
, NULL
, 0)) == NULL
)
186 assert(uu_error() == UU_ERROR_NO_MEMORY
);
192 destroy_rep_val_list(uu_list_t
*list
)
195 empty_rep_val_list(list
);
196 uu_list_destroy(list
);
201 find_rep_val(uu_list_t
*list
, int64_t val
)
205 for (rv
= uu_list_first(list
); rv
!= NULL
;
206 rv
= uu_list_next(list
, rv
)) {
214 add_rep_val(uu_list_t
*list
, int64_t val
)
218 if ((rv
= malloc(sizeof (rep_val_t
))) == NULL
)
221 uu_list_node_init(rv
, &rv
->link
, rep_val_pool
);
224 (void) uu_list_insert_after(list
, NULL
, rv
);
230 remove_rep_val(uu_list_t
*list
, int64_t val
)
234 if ((rv
= find_rep_val(list
, val
)) != NULL
) {
235 uu_list_remove(list
, rv
);
236 assert(rv
->scf_val
== NULL
);
242 empty_rep_val_list(uu_list_t
*list
)
247 while ((rv
= uu_list_teardown(list
, &cookie
)) != NULL
) {
248 if (rv
->scf_val
!= NULL
)
249 scf_value_destroy(rv
->scf_val
);
255 get_single_rep_val(uu_list_t
*list
)
257 rep_val_t
*rv
= uu_list_first(list
);
264 set_single_rep_val(uu_list_t
*list
, int64_t val
)
266 rep_val_t
*rv
= uu_list_first(list
);
269 if (add_rep_val(list
, val
) == -1)
279 * Partner to add_tr_entry_values. This function frees the scf_values created
280 * in add_tr_entry_values() in the list 'vals'.
283 remove_tr_entry_values(uu_list_t
*vals
)
287 for (rval
= uu_list_first(vals
); rval
!= NULL
;
288 rval
= uu_list_next(vals
, rval
)) {
289 if (rval
->scf_val
!= NULL
) {
290 scf_value_destroy(rval
->scf_val
);
291 rval
->scf_val
= NULL
;
297 * This function creates and associates with transaction entry 'entry' an
298 * scf value for each value in 'vals'. The pointers to the scf values
299 * are stored in the list for later cleanup by remove_tr_entry_values.
300 * Returns 0 on success, else -1 on error with scf_error() set to:
301 * SCF_ERROR_NO_MEMORY if memory allocation failed.
302 * SCF_ERROR_CONNECTION_BROKEN if the connection to the repository was broken.
305 add_tr_entry_values(scf_handle_t
*hdl
, scf_transaction_entry_t
*entry
,
310 for (rval
= uu_list_first(vals
); rval
!= NULL
;
311 rval
= uu_list_next(vals
, rval
)) {
313 assert(rval
->scf_val
== NULL
);
314 if ((rval
->scf_val
= scf_value_create(hdl
)) == NULL
) {
315 remove_tr_entry_values(vals
);
319 scf_value_set_integer(rval
->scf_val
, rval
->val
);
321 if (scf_entry_add_value(entry
, rval
->scf_val
) < 0) {
322 remove_tr_entry_values(vals
);
331 * Stores the values contained in the list 'vals' into the property 'prop_name'
332 * of the instance with fmri 'inst_fmri', within the instance's instance
333 * state property group.
335 * Returns 0 on success, else one of the following on failure:
336 * SCF_ERROR_NO_MEMORY if memory allocation failed.
337 * SCF_ERROR_NO_RESOURCES if the server doesn't have required resources.
338 * SCF_ERROR_VERSION_MISMATCH if program compiled against a newer libscf
340 * SCF_ERROR_PERMISSION_DENIED if insufficient privileges to modify pg.
341 * SCF_ERROR_BACKEND_ACCESS if the repository back-end refused the pg modify.
342 * SCF_ERROR_CONNECTION_BROKEN if the connection to the repository was broken.
345 _store_rep_vals(uu_list_t
*vals
, const char *inst_fmri
, const char *prop_name
)
350 if (scf_handle_decode_fmri(rep_handle
, inst_fmri
, NULL
, NULL
, inst
,
351 NULL
, NULL
, SCF_DECODE_FMRI_EXACT
) == -1)
352 return (scf_error());
355 * Fetch the instance state pg, and if it doesn't exist try and
358 if (scf_instance_get_pg(inst
, PG_NAME_INSTANCE_STATE
, pg
) < 0) {
359 if (scf_error() != SCF_ERROR_NOT_FOUND
)
360 return (scf_error());
361 if (scf_instance_add_pg(inst
, PG_NAME_INSTANCE_STATE
,
362 SCF_GROUP_FRAMEWORK
, SCF_PG_FLAG_NONPERSISTENT
, pg
) < 0)
363 return (scf_error());
367 * Perform a transaction to write the values to the requested property.
368 * If someone got there before us, loop and retry.
371 if (scf_transaction_start(trans
, pg
) < 0)
372 return (scf_error());
374 if ((scf_transaction_property_new(trans
, entry
,
375 prop_name
, SCF_TYPE_INTEGER
) < 0) &&
376 (scf_transaction_property_change_type(trans
, entry
,
377 prop_name
, SCF_TYPE_INTEGER
) < 0)) {
382 if (add_tr_entry_values(rep_handle
, entry
, vals
) < 0) {
387 if ((cret
= scf_transaction_commit(trans
)) < 0) {
390 } else if (cret
== 0) {
391 scf_transaction_reset(trans
);
392 scf_entry_reset(entry
);
393 remove_tr_entry_values(vals
);
394 if (scf_pg_update(pg
) < 0) {
403 scf_transaction_reset(trans
);
404 scf_entry_reset(entry
);
405 remove_tr_entry_values(vals
);
410 * Retrieves the repository values of property 'prop_name', of the instance
411 * with fmri 'fmri', from within the instance's instance state property
412 * group and adds them to the value list 'list'.
414 * Returns 0 on success, else one of the following values on error:
415 * SCF_ERROR_NOT_FOUND if the property doesn't exist.
416 * SCF_ERROR_NO_MEMORY if memory allocation failed.
417 * SCF_ERROR_CONNECTION_BROKEN if the connection to the repository was broken.
418 * SCF_ERROR_TYPE_MISMATCH if the property was of an unexpected type.
422 _retrieve_rep_vals(uu_list_t
*list
, const char *fmri
, const char *prop_name
)
424 scf_simple_prop_t
*sp
;
427 if ((sp
= scf_simple_prop_get(rep_handle
, fmri
, PG_NAME_INSTANCE_STATE
,
429 return (scf_error());
431 while ((ip
= scf_simple_prop_next_integer(sp
)) != NULL
) {
432 if (add_rep_val(list
, *ip
) == -1) {
433 empty_rep_val_list(list
);
434 scf_simple_prop_free(sp
);
435 return (SCF_ERROR_NO_MEMORY
);
438 if (scf_error() != SCF_ERROR_NONE
) {
439 assert(scf_error() == SCF_ERROR_TYPE_MISMATCH
);
440 empty_rep_val_list(list
);
441 scf_simple_prop_free(sp
);
442 return (scf_error());
445 scf_simple_prop_free(sp
);
450 * Writes the repository values in the vals list to
451 * a file that is generated based on the passed in fmri and name.
452 * Returns 0 on success,
453 * ENAMETOOLONG if unable to generate filename from fmri (including
454 * the inability to create the directory for the generated filename) and
455 * ENOENT on all other failures.
458 repvals_to_file(const char *fmri
, const char *name
, uu_list_t
*vals
)
461 FILE *tfp
; /* temp fp */
462 rep_val_t
*spval
; /* Contains a start_pid or ctid */
465 if (gen_filenms_from_fmri(fmri
, name
, genfmri_filename
,
466 genfmri_temp_filename
) != 0) {
467 /* Failure either from fmri too long or mkdir failure */
468 return (ENAMETOOLONG
);
471 if ((tfd
= mkstemp(genfmri_temp_filename
)) == -1) {
475 if (fchmod(tfd
, (S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
)) == -1) {
481 if ((tfp
= fdopen(tfd
, "w")) == NULL
) {
487 for (spval
= uu_list_first(vals
); spval
!= NULL
;
488 spval
= uu_list_next(vals
, spval
)) {
489 if (fprintf(tfp
, "%lld\n", spval
->val
) <= 0) {
495 if (fclose(tfp
) != 0) {
499 if (rename(genfmri_temp_filename
, genfmri_filename
) != 0) {
506 if (unlink(genfmri_temp_filename
) != 0) {
507 warn_msg(gettext("Removal of temp file "
508 "%s failed. Please remove manually."),
509 genfmri_temp_filename
);
515 * A routine that loops trying to read/write values until either success,
516 * an error other than a broken repository connection or
517 * the number of retries reaches REP_OP_RETRIES.
518 * This action is used to read/write the values:
519 * reads/writes to a file for the START_PIDS property due to scalability
520 * problems with libscf
521 * reads/writes to the repository for all other properties.
522 * Returns 0 on success, else the error value from either _store_rep_vals or
523 * _retrieve_rep_vals (based on whether 'store' was set or not), or one of the
525 * SCF_ERROR_NO_RESOURCES if the server doesn't have adequate resources
526 * SCF_ERROR_NO_MEMORY if a memory allocation failure
527 * SCF_ERROR_NO_SERVER if the server isn't running.
528 * SCF_ERROR_CONSTRAINT_VIOLATED if an error in dealing with the speedy files
531 store_retrieve_rep_vals(uu_list_t
*vals
, const char *fmri
,
532 const char *prop
, boolean_t store
)
536 FILE *tfp
; /* temp fp */
537 int64_t tval
; /* temp val holder */
539 int fopen_retry_cnt
= 2;
541 /* inetd specific action for START_PIDS property */
542 if (strcmp(prop
, PR_NAME_START_PIDS
) == 0) {
544 * Storage performance of START_PIDS is important,
545 * so each instance has its own file and all start_pids
546 * in the list are written to a temp file and then
550 /* Write all values in list to file */
551 if (repvals_to_file(fmri
, "pid", vals
)) {
552 return (SCF_ERROR_CONSTRAINT_VIOLATED
);
555 /* no temp name needed */
556 if (gen_filenms_from_fmri(fmri
, "pid", genfmri_filename
,
558 return (SCF_ERROR_CONSTRAINT_VIOLATED
);
561 /* It's ok if no file, there are just no pids */
562 if ((tfp
= fopen(genfmri_filename
, "r")) == NULL
) {
563 if ((errno
== EINTR
) && (fopen_retry_cnt
> 0)) {
569 /* fscanf may not set errno, so clear it first */
571 while ((fscanf_ret
= fscanf(tfp
, "%lld", &tval
)) == 1) {
572 /* If tval isn't a valid pid, then fail. */
573 if ((tval
> MAXPID
) || (tval
<= 0)) {
574 empty_rep_val_list(vals
);
575 return (SCF_ERROR_CONSTRAINT_VIOLATED
);
577 if (add_rep_val(vals
, tval
) == -1) {
578 empty_rep_val_list(vals
);
579 return (SCF_ERROR_NO_MEMORY
);
583 /* EOF is ok when no errno */
584 if ((fscanf_ret
!= EOF
) || (errno
!= 0)) {
585 empty_rep_val_list(vals
);
586 return (SCF_ERROR_CONSTRAINT_VIOLATED
);
588 if (fclose(tfp
) != 0) {
589 /* for close failure just log a message */
590 warn_msg(gettext("Close of file %s failed."),
595 for (retries
= 0; retries
<= REP_OP_RETRIES
; retries
++) {
596 if (make_handle_bound(rep_handle
) == -1) {
601 if ((ret
= (store
? _store_rep_vals(vals
, fmri
, prop
) :
602 _retrieve_rep_vals(vals
, fmri
, prop
))) !=
603 SCF_ERROR_CONNECTION_BROKEN
)
606 (void) scf_handle_unbind(rep_handle
);
615 store_rep_vals(uu_list_t
*vals
, const char *fmri
, const char *prop
)
617 return (store_retrieve_rep_vals(vals
, fmri
, prop
, B_TRUE
));
621 retrieve_rep_vals(uu_list_t
*vals
, const char *fmri
, const char *prop
)
623 return (store_retrieve_rep_vals(vals
, fmri
, prop
, B_FALSE
));
627 * Adds/removes a contract id to/from the cached list kept in the instance.
628 * Then the cached list is written to a file named "ctid" in a directory
629 * based on the fmri. Cached list is written to a file due to scalability
630 * problems in libscf. The file "ctid" is used when inetd is restarted
631 * so that inetd can adopt the contracts that it had previously.
634 * ENAMETOOLONG if unable to generate filename from fmri (including
635 * the inability to create the directory for the generated filename)
636 * ENOENT - failure accessing file
637 * ENOMEM - memory allocation failure
640 add_remove_contract(instance_t
*inst
, boolean_t add
, ctid_t ctid
)
642 FILE *tfp
; /* temp fp */
645 int fopen_retry_cnt
= 2;
648 * Storage performance of contract ids is important,
649 * so each instance has its own file. An add of a
650 * ctid will be appended to the ctid file.
651 * The removal of a ctid will result in the remaining
652 * ctids in the list being written to a temp file and then
656 if (gen_filenms_from_fmri(inst
->fmri
, "ctid", genfmri_filename
,
658 /* Failure either from fmri too long or mkdir failure */
659 return (ENAMETOOLONG
);
663 if ((tfp
= fopen(genfmri_filename
, "a")) == NULL
) {
664 if ((errno
== EINTR
) && (fopen_retry_cnt
> 0)) {
672 /* Always store ctids as long long */
673 if (fprintf(tfp
, "%llu\n", (uint64_t)ctid
) <= 0) {
679 if (fclose(tfp
) != 0) {
684 if (add_rep_val(inst
->start_ctids
, ctid
) != 0) {
689 remove_rep_val(inst
->start_ctids
, ctid
);
691 /* Write all values in list to file */
692 if ((repval_ret
= repvals_to_file(inst
->fmri
, "ctid",
693 inst
->start_ctids
)) != 0) {
704 * If sig !=0, iterate over all contracts in the cached list of contract
705 * ids kept in the instance. Send each contract the specified signal.
706 * If sig == 0, read in the contract ids that were last associated
707 * with this instance (reload the cache) and call adopt_contract()
710 * Returns 0 on success;
711 * ENAMETOOLONG if unable to generate filename from fmri (including
712 * the inability to create the directory for the generated filename) and
713 * ENXIO if a failure accessing the file
714 * ENOMEM if there was a memory allocation failure
715 * ENOENT if the instance, its restarter property group, or its
716 * contract property don't exist
717 * EIO if invalid data read from the file
720 iterate_repository_contracts(instance_t
*inst
, int sig
)
724 rep_val_t
*spval
= NULL
; /* Contains a start_pid */
725 uint64_t tval
; /* temp val holder */
726 uu_list_t
*uup
= NULL
;
728 int fopen_retry_cnt
= 2;
732 * Send a signal to all in the contract; ESRCH just
733 * means they all exited before we could kill them
735 for (spval
= uu_list_first(inst
->start_ctids
); spval
!= NULL
;
736 spval
= uu_list_next(inst
->start_ctids
, spval
)) {
737 if (sigsend(P_CTID
, (ctid_t
)spval
->val
, sig
) == -1 &&
739 warn_msg(gettext("Unable to signal all "
740 "contract members of instance %s: %s"),
741 inst
->fmri
, strerror(errno
));
749 * Attempt to adopt the contract for each ctid.
751 if (gen_filenms_from_fmri(inst
->fmri
, "ctid", genfmri_filename
,
753 /* Failure either from fmri too long or mkdir failure */
754 return (ENAMETOOLONG
);
758 /* It's ok if no file, there are no ctids to adopt */
759 if ((fp
= fopen(genfmri_filename
, "r")) == NULL
) {
760 if ((errno
== EINTR
) && (fopen_retry_cnt
> 0)) {
768 * Read ctids from file into 2 lists:
769 * - temporary list to be traversed (uup)
770 * - cached list that can be modified if adoption of
771 * contract fails (inst->start_ctids).
772 * Always treat ctids as long longs.
774 uup
= create_rep_val_list();
775 /* fscanf may not set errno, so clear it first */
777 while ((fscanf_ret
= fscanf(fp
, "%llu", &tval
)) == 1) {
778 /* If tval isn't a valid ctid, then fail. */
784 if ((add_rep_val(uup
, tval
) == -1) ||
785 (add_rep_val(inst
->start_ctids
, tval
) == -1)) {
792 /* EOF is not a failure when no errno */
793 if ((fscanf_ret
!= EOF
) || (errno
!= 0)) {
798 if (fclose(fp
) != 0) {
803 for (spval
= uu_list_first(uup
); spval
!= NULL
;
804 spval
= uu_list_next(uup
, spval
)) {
805 /* Try to adopt the contract */
806 if (adopt_contract((ctid_t
)spval
->val
,
809 * Adoption failed. No reason to think it'll
810 * work later, so remove the id from our list
813 remove_rep_val(inst
->start_ctids
, spval
->val
);
818 empty_rep_val_list(uup
);
819 destroy_rep_val_list(uup
);
823 empty_rep_val_list(inst
->start_ctids
);