/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This file contains routines to manipulate lists of repository values that
* are used to store process ids and the internal state. There are routines
* inspect the lists. It also contains routines that deal with the
* repository side of contract ids.
*/
#include <errno.h>
#include <stdlib.h>
#include <libintl.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <libscf_priv.h>
#include "inetd_impl.h"
/*
* Number of consecutive repository bind retries performed by bind_to_rep()
* before failing.
*/
/* Name of property group where inetd's state for a service is stored. */
/* uu_list repval list pool */
/*
* Repository object pointers that get set-up in repval_init() and closed down
* in repval_fini(). They're used in _retrieve_rep_vals(), _store_rep_vals(),
* add_remove_contract_norebind(), and adopt_repository_contracts(). They're
* global so they can be initialized once on inetd startup, and re-used
* there-after in the referenced functions.
*/
/*
* Pathname storage for paths generated from the fmri.
* Used when updating the ctid and (start) pid files for an inetd service.
*/
/*
* Try and make the given handle bind be bound to the repository. If
* it's already bound, or we succeed a new bind return 0; else return
* -1 on failure, with the SCF error set to one of the following:
* SCF_ERROR_NO_SERVER
* SCF_ERROR_NO_RESOURCES
*/
int
{
if ((scf_handle_bind(hdl) == 0) ||
(scf_error() == SCF_ERROR_IN_USE))
return (0);
}
return (-1);
}
int
repval_init(void)
{
/*
* Create the repval list pool.
*/
if (rep_val_pool == NULL) {
uu_strerror(uu_error()));
return (-1);
}
/*
* Create and bind a repository handle, and create all repository
* objects that we'll use later that are associated with it. On any
* errors we simply return -1 and let repval_fini() clean-up after
* us.
*/
error_msg("%s: %s",
gettext("Failed to create repository handle"),
scf_strerror(scf_error()));
goto cleanup;
goto cleanup;
error_msg("%s: %s",
gettext("Failed to create repository object"),
scf_strerror(scf_error()));
goto cleanup;
}
return (0);
repval_fini();
return (-1);
}
void
repval_fini(void)
{
if (rep_handle != NULL) {
/*
* We unbind from the repository before we free the repository
* objects for efficiency reasons.
*/
(void) scf_handle_unbind(rep_handle);
rep_handle = NULL;
}
if (rep_val_pool != NULL) {
rep_val_pool = NULL;
}
}
create_rep_val_list(void)
{
return (ret);
}
void
{
}
}
{
break;
}
return (rv);
}
int
{
return (-1);
return (0);
}
void
{
}
}
void
{
}
}
{
}
int
{
return (-1);
} else {
}
return (0);
}
/*
* Partner to add_tr_entry_values. This function frees the scf_values created
* in add_tr_entry_values() in the list 'vals'.
*/
static void
{
}
}
}
/*
* This function creates and associates with transaction entry 'entry' an
* scf value for each value in 'vals'. The pointers to the scf values
* are stored in the list for later cleanup by remove_tr_entry_values.
* Returns 0 on success, else -1 on error with scf_error() set to:
* SCF_ERROR_NO_MEMORY if memory allocation failed.
* SCF_ERROR_CONNECTION_BROKEN if the connection to the repository was broken.
*/
static int
{
return (-1);
}
return (-1);
}
}
return (0);
}
/*
* Stores the values contained in the list 'vals' into the property 'prop_name'
* of the instance with fmri 'inst_fmri', within the instance's instance
* state property group.
*
* Returns 0 on success, else one of the following on failure:
* SCF_ERROR_NO_MEMORY if memory allocation failed.
* SCF_ERROR_NO_RESOURCES if the server doesn't have required resources.
* SCF_ERROR_VERSION_MISMATCH if program compiled against a newer libscf
* than on system.
* SCF_ERROR_PERMISSION_DENIED if insufficient privileges to modify pg.
* SCF_ERROR_BACKEND_ACCESS if the repository back-end refused the pg modify.
* SCF_ERROR_CONNECTION_BROKEN if the connection to the repository was broken.
*/
static scf_error_t
{
int cret;
int ret;
return (scf_error());
/*
* Fetch the instance state pg, and if it doesn't exist try and
* create it.
*/
if (scf_error() != SCF_ERROR_NOT_FOUND)
return (scf_error());
return (scf_error());
}
/*
* Perform a transaction to write the values to the requested property.
* If someone got there before us, loop and retry.
*/
do {
return (scf_error());
prop_name, SCF_TYPE_INTEGER) < 0) &&
prop_name, SCF_TYPE_INTEGER) < 0)) {
goto cleanup;
}
goto cleanup;
}
goto cleanup;
} else if (cret == 0) {
if (scf_pg_update(pg) < 0) {
goto cleanup;
}
}
} while (cret == 0);
ret = 0;
return (ret);
}
/*
* Retrieves the repository values of property 'prop_name', of the instance
* with fmri 'fmri', from within the instance's instance state property
* group and adds them to the value list 'list'.
*
* Returns 0 on success, else one of the following values on error:
* SCF_ERROR_NOT_FOUND if the property doesn't exist.
* SCF_ERROR_NO_MEMORY if memory allocation failed.
* SCF_ERROR_CONNECTION_BROKEN if the connection to the repository was broken.
* SCF_ERROR_TYPE_MISMATCH if the property was of an unexpected type.
*
*/
static scf_error_t
{
return (scf_error());
return (SCF_ERROR_NO_MEMORY);
}
}
if (scf_error() != SCF_ERROR_NONE) {
return (scf_error());
}
return (0);
}
/*
* Writes the repository values in the vals list to
* a file that is generated based on the passed in fmri and name.
* Returns 0 on success,
* ENAMETOOLONG if unable to generate filename from fmri (including
* the inability to create the directory for the generated filename) and
* ENOENT on all other failures.
*/
static int
{
int tfd;
int ret = 0;
genfmri_temp_filename) != 0) {
/* Failure either from fmri too long or mkdir failure */
return (ENAMETOOLONG);
}
return (ENOENT);
}
goto unlink_out;
}
goto unlink_out;
}
goto unlink_out;
}
}
goto unlink_out;
}
goto unlink_out;
}
return (0);
if (unlink(genfmri_temp_filename) != 0) {
"%s failed. Please remove manually."),
}
return (ret);
}
/*
* an error other than a broken repository connection or
* the number of retries reaches REP_OP_RETRIES.
* problems with libscf
* Returns 0 on success, else the error value from either _store_rep_vals or
* _retrieve_rep_vals (based on whether 'store' was set or not), or one of the
* following:
* SCF_ERROR_NO_RESOURCES if the server doesn't have adequate resources
* SCF_ERROR_NO_MEMORY if a memory allocation failure
* SCF_ERROR_NO_SERVER if the server isn't running.
* SCF_ERROR_CONSTRAINT_VIOLATED if an error in dealing with the speedy files
*/
static scf_error_t
{
int fscanf_ret;
/* inetd specific action for START_PIDS property */
/*
* Storage performance of START_PIDS is important,
* so each instance has its own file and all start_pids
* in the list are written to a temp file and then
* moved (renamed).
*/
if (store) {
/* Write all values in list to file */
return (SCF_ERROR_CONSTRAINT_VIOLATED);
}
} else {
/* no temp name needed */
NULL) != 0)
return (SCF_ERROR_CONSTRAINT_VIOLATED);
/* It's ok if no file, there are just no pids */
goto retry_fopen;
}
return (0);
}
/* fscanf may not set errno, so clear it first */
errno = 0;
/* If tval isn't a valid pid, then fail. */
return (SCF_ERROR_CONSTRAINT_VIOLATED);
}
return (SCF_ERROR_NO_MEMORY);
}
errno = 0;
}
/* EOF is ok when no errno */
return (SCF_ERROR_CONSTRAINT_VIOLATED);
}
/* for close failure just log a message */
}
}
} else {
break;
}
break;
(void) scf_handle_unbind(rep_handle);
}
}
out:
return (ret);
}
{
}
{
}
/*
* Then the cached list is written to a file named "ctid" in a directory
* based on the fmri. Cached list is written to a file due to scalability
* problems in libscf. The file "ctid" is used when inetd is restarted
* so that inetd can adopt the contracts that it had previously.
* Returns:
* 0 on success
* ENAMETOOLONG if unable to generate filename from fmri (including
* the inability to create the directory for the generated filename)
* ENOENT - failure accessing file
* ENOMEM - memory allocation failure
*/
int
{
int ret = 0;
int repval_ret = 0;
/*
* Storage performance of contract ids is important,
* so each instance has its own file. An add of a
* ctid will be appended to the ctid file.
* The removal of a ctid will result in the remaining
* ctids in the list being written to a temp file and then
* moved (renamed).
*/
if (add) {
NULL) != 0) {
/* Failure either from fmri too long or mkdir failure */
return (ENAMETOOLONG);
}
goto retry_fopen;
}
goto out;
}
/* Always store ctids as long long */
goto out;
}
goto out;
}
goto out;
}
} else {
/* Write all values in list to file */
inst->start_ctids)) != 0) {
ret = repval_ret;
goto out;
}
}
out:
return (ret);
}
/*
* If sig !=0, iterate over all contracts in the cached list of contract
* ids kept in the instance. Send each contract the specified signal.
* If sig == 0, read in the contract ids that were last associated
* with this instance (reload the cache) and call adopt_contract()
* to take ownership.
*
* Returns 0 on success;
* ENAMETOOLONG if unable to generate filename from fmri (including
* the inability to create the directory for the generated filename) and
* ENXIO if a failure accessing the file
* ENOMEM if there was a memory allocation failure
* ENOENT if the instance, its restarter property group, or its
* contract property don't exist
* EIO if invalid data read from the file
*/
int
{
int ret = 0;
int fscanf_ret;
if (sig != 0) {
/*
* Send a signal to all in the contract; ESRCH just
* means they all exited before we could kill them
*/
"contract members of instance %s: %s"),
}
}
return (0);
}
/*
* sig == 0 case.
* Attempt to adopt the contract for each ctid.
*/
NULL) != 0) {
/* Failure either from fmri too long or mkdir failure */
return (ENAMETOOLONG);
}
/* It's ok if no file, there are no ctids to adopt */
goto retry_fopen;
}
return (0);
}
/*
* Read ctids from file into 2 lists:
* - temporary list to be traversed (uup)
* - cached list that can be modified if adoption of
* contract fails (inst->start_ctids).
* Always treat ctids as long longs.
*/
uup = create_rep_val_list();
/* fscanf may not set errno, so clear it first */
errno = 0;
/* If tval isn't a valid ctid, then fail. */
if (tval == 0) {
goto out;
}
goto out;
}
errno = 0;
}
/* EOF is not a failure when no errno */
goto out;
}
goto out;
}
/* Try to adopt the contract */
/*
* Adoption failed. No reason to think it'll
* work later, so remove the id from our list
* in the instance.
*/
}
}
out:
if (uup) {
}
if (ret != 0)
return (ret);
}