Mercurial > illumos > illumos-gate
changeset 3175:5903f61aa150
6335025 telnet or rsh processing 5-15 times slower in S10 (6-330 times slower on T2000)
author | skamm |
---|---|
date | Mon, 27 Nov 2006 12:42:57 -0800 |
parents | 95c3e05e2071 |
children | a38e922f22b0 |
files | usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.c usr/src/cmd/cmd-inet/usr.lib/inetd/inetd_impl.h usr/src/cmd/cmd-inet/usr.lib/inetd/repval.c usr/src/cmd/svc/svcs/svcs.c usr/src/lib/libscf/Makefile.com usr/src/lib/libscf/common/mapfile-vers usr/src/lib/libscf/common/midlevel.c usr/src/lib/libscf/inc/libscf_priv.h |
diffstat | 8 files changed, 809 insertions(+), 375 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.c Mon Nov 27 11:55:12 2006 -0800 +++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.c Mon Nov 27 12:42:57 2006 -0800 @@ -1223,7 +1223,7 @@ debug_msg("Entering add_method_ids"); if (cid != -1) - (void) add_remove_contract(ins->fmri, B_TRUE, cid); + (void) add_remove_contract(ins, B_TRUE, cid); if (mthd == IM_START) { if (add_rep_val(ins->start_pids, (int64_t)pid) == 0) { @@ -1249,7 +1249,7 @@ debug_msg("Entering remove_method_ids"); if (cid != -1) - (void) add_remove_contract(inst->fmri, B_FALSE, cid); + (void) add_remove_contract(inst, B_FALSE, cid); if (mthd == IM_START) { remove_rep_val(inst->start_pids, (int64_t)pid); @@ -1282,7 +1282,8 @@ ret->bind_fail_count = 0; if (((ret->non_start_pid = create_rep_val_list()) == NULL) || - ((ret->start_pids = create_rep_val_list()) == NULL)) + ((ret->start_pids = create_rep_val_list()) == NULL) || + ((ret->start_ctids = create_rep_val_list()) == NULL)) goto alloc_fail; ret->cur_istate = IIS_NONE; @@ -1329,6 +1330,7 @@ destroy_rep_val_list(inst->start_pids); destroy_rep_val_list(inst->non_start_pid); + destroy_rep_val_list(inst->start_ctids); free(inst->fmri); @@ -2053,7 +2055,7 @@ goto fail; } - if (((err = iterate_repository_contracts(instance->fmri, 0)) + if (((err = iterate_repository_contracts(instance, 0)) != 0) && (err != ENOENT)) { error_msg(gettext( "Failed to adopt contracts of instance %s: %s"), @@ -2944,7 +2946,7 @@ if ((sig = restarter_is_kill_method(mi->exec_path)) >= 0) { /* Carry out contract assassination */ - ret = iterate_repository_contracts(instance->fmri, sig); + ret = iterate_repository_contracts(instance, sig); /* ENOENT means we didn't find any contracts */ if (ret != 0 && ret != ENOENT) { error_msg(gettext("Failed to send signal %d "
--- a/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd_impl.h Mon Nov 27 11:55:12 2006 -0800 +++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd_impl.h Mon Nov 27 12:42:57 2006 -0800 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * 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 * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -178,6 +177,9 @@ uu_list_t *non_start_pid; uu_list_t *start_pids; + /* ctids of currently running start methods */ + uu_list_t *start_ctids; + /* remote address, used for TCP tracing */ struct sockaddr_storage remote_addr; @@ -319,8 +321,8 @@ extern void remove_rep_val(uu_list_t *, int64_t); extern void empty_rep_val_list(uu_list_t *); extern int make_handle_bound(scf_handle_t *); -extern int add_remove_contract(const char *, boolean_t, ctid_t); -extern int iterate_repository_contracts(const char *, int); +extern int add_remove_contract(instance_t *, boolean_t, ctid_t); +extern int iterate_repository_contracts(instance_t *, int); /* * contracts.c
--- a/usr/src/cmd/cmd-inet/usr.lib/inetd/repval.c Mon Nov 27 11:55:12 2006 -0800 +++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/repval.c Mon Nov 27 12:42:57 2006 -0800 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * 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 * or http://www.opensolaris.org/os/licensing. @@ -21,7 +20,7 @@ */ /* * - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -41,9 +40,12 @@ #include <unistd.h> #include <string.h> #include <signal.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <libscf_priv.h> #include "inetd_impl.h" - /* * Number of consecutive repository bind retries performed by bind_to_rep() * before failing. @@ -71,6 +73,13 @@ static scf_property_t *prop = NULL; /* + * Pathname storage for paths generated from the fmri. + * Used when updating the ctid and (start) pid files for an inetd service. + */ +static char genfmri_filename[MAXPATHLEN] = ""; +static char genfmri_temp_filename[MAXPATHLEN] = ""; + +/* * 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: @@ -468,39 +477,172 @@ } /* - * A routine that loops trying to read/write repository values until - * either success, an error other that a broken repository connection or + * 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 +repvals_to_file(const char *fmri, const char *name, uu_list_t *vals) +{ + int tfd; + FILE *tfp; /* temp fp */ + rep_val_t *spval; /* Contains a start_pid or ctid */ + int ret = 0; + + debug_msg("Entering repvals_to_file, fmri:%s, name=%s\n", + fmri, name); + + if (gen_filenms_from_fmri(fmri, name, genfmri_filename, + genfmri_temp_filename) != 0) { + /* Failure either from fmri too long or mkdir failure */ + return (ENAMETOOLONG); + } + + if ((tfd = mkstemp(genfmri_temp_filename)) == -1) { + return (ENOENT); + } + + if (fchmod(tfd, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) { + (void) close(tfd); + ret = ENOENT; + goto unlink_out; + } + + if ((tfp = fdopen(tfd, "w")) == NULL) { + (void) close(tfd); + ret = ENOENT; + goto unlink_out; + } + + for (spval = uu_list_first(vals); spval != NULL; + spval = uu_list_next(vals, spval)) { + if (fprintf(tfp, "%lld\n", spval->val) <= 0) { + (void) fclose(tfp); + ret = ENOENT; + goto unlink_out; + } + } + if (fclose(tfp) != 0) { + ret = ENOENT; + goto unlink_out; + } + if (rename(genfmri_temp_filename, genfmri_filename) != 0) { + ret = ENOENT; + goto unlink_out; + } + return (0); + +unlink_out: + if (unlink(genfmri_temp_filename) != 0) { + warn_msg(gettext("Removal of temp file " + "%s failed. Please remove manually."), + genfmri_temp_filename); + } + return (ret); +} + +/* + * A routine that loops trying to read/write values until either success, + * an error other than a broken repository connection or * the number of retries reaches REP_OP_RETRIES. + * This action is used to read/write the values: + * reads/writes to a file for the START_PIDS property due to scalability + * problems with libscf + * reads/writes to the repository for all other properties. * 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 if a rebind failed: - * SCF_ERROR_NO_RESOURCES if the server doesn't have adequate resources. + * _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 store_retrieve_rep_vals(uu_list_t *vals, const char *fmri, const char *prop, boolean_t store) { - scf_error_t ret; + scf_error_t ret = 0; uint_t retries; + FILE *tfp; /* temp fp */ + int64_t tval; /* temp val holder */ + int fscanf_ret; + int fopen_retry_cnt = 2; debug_msg("Entering store_retrieve_rep_vals, store: %d", store); - - for (retries = 0; retries <= REP_OP_RETRIES; retries++) { - if (make_handle_bound(rep_handle) == -1) { - ret = scf_error(); - break; - } + /* inetd specific action for START_PIDS property */ + if (strcmp(prop, PR_NAME_START_PIDS) == 0) { + /* + * 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 */ + if (repvals_to_file(fmri, "pid", vals)) { + return (SCF_ERROR_CONSTRAINT_VIOLATED); + } + } else { + /* no temp name needed */ + if (gen_filenms_from_fmri(fmri, "pid", genfmri_filename, + NULL) != 0) + return (SCF_ERROR_CONSTRAINT_VIOLATED); - if ((ret = (store ? _store_rep_vals(vals, fmri, prop) : - _retrieve_rep_vals(vals, fmri, prop))) != - SCF_ERROR_CONNECTION_BROKEN) - break; +retry_fopen: + /* It's ok if no file, there are just no pids */ + if ((tfp = fopen(genfmri_filename, "r")) == NULL) { + if ((errno == EINTR) && (fopen_retry_cnt > 0)) { + fopen_retry_cnt--; + goto retry_fopen; + } + return (0); + } + /* fscanf may not set errno, so clear it first */ + errno = 0; + while ((fscanf_ret = fscanf(tfp, "%lld", &tval)) == 1) { + /* If tval isn't a valid pid, then fail. */ + if ((tval > MAXPID) || (tval <= 0)) { + empty_rep_val_list(vals); + return (SCF_ERROR_CONSTRAINT_VIOLATED); + } + if (add_rep_val(vals, tval) == -1) { + empty_rep_val_list(vals); + return (SCF_ERROR_NO_MEMORY); + } + errno = 0; + } + /* EOF is ok when no errno */ + if ((fscanf_ret != EOF) || (errno != 0)) { + empty_rep_val_list(vals); + return (SCF_ERROR_CONSTRAINT_VIOLATED); + } + if (fclose(tfp) != 0) { + /* for close failure just log a message */ + warn_msg(gettext("Close of file %s failed."), + genfmri_filename); + } + } + } else { + for (retries = 0; retries <= REP_OP_RETRIES; retries++) { + if (make_handle_bound(rep_handle) == -1) { + ret = scf_error(); + break; + } - (void) scf_handle_unbind(rep_handle); + if ((ret = (store ? _store_rep_vals(vals, fmri, prop) : + _retrieve_rep_vals(vals, fmri, prop))) != + SCF_ERROR_CONNECTION_BROKEN) + break; + + (void) scf_handle_unbind(rep_handle); + } } +out: return (ret); } @@ -517,304 +659,207 @@ } /* - * Fails with ECONNABORTED, ENOENT, EACCES, EROFS, ENOMEM, or EPERM. - */ -static int -add_remove_contract_norebind(const char *fmri, boolean_t add, ctid_t ctid) -{ - int err; - - if (scf_handle_decode_fmri(rep_handle, fmri, NULL, NULL, inst, NULL, - NULL, SCF_DECODE_FMRI_EXACT) != 0) { - switch (scf_error()) { - case SCF_ERROR_CONNECTION_BROKEN: - return (ECONNABORTED); - - case SCF_ERROR_NOT_FOUND: - return (ENOENT); - - case SCF_ERROR_CONSTRAINT_VIOLATED: - case SCF_ERROR_INVALID_ARGUMENT: - case SCF_ERROR_HANDLE_MISMATCH: - default: - assert(0); - abort(); - } - } - -redo: - if (add) - err = restarter_store_contract(inst, ctid, - RESTARTER_CONTRACT_PRIMARY); - else - err = restarter_remove_contract(inst, ctid, - RESTARTER_CONTRACT_PRIMARY); - switch (err) { - case 0: - case ENOMEM: - case ECONNABORTED: - return (err); - - case ECANCELED: - return (ENOENT); - - case EPERM: - assert(0); - return (err); - - case EACCES: - error_msg(add ? gettext("Failed to write contract id %ld for " - "instance %s to repository: backend access denied.") : - gettext("Failed to remove contract id %ld for instance %s " - "from repository: backend access denied."), ctid, fmri); - return (err); - - case EROFS: - error_msg(add ? gettext("Failed to write contract id %ld for " - "instance %s to repository: backend is read-only.") : - gettext("Failed to remove contract id %ld for instance %s " - "from repository: backend is read-only."), ctid, fmri); - return (err); - - case EINVAL: - case EBADF: - default: - assert(0); - abort(); - /* NOTREACHED */ - } -} - -/* - * Tries to add/remove (dependent on the value of 'add') the specified - * contract id to the specified instance until either success, an error - * other that connection broken occurs, or the number of bind retries reaches - * REP_OP_RETRIES. - * Returns 0 on success else fails with one of ENOENT, EACCES, EROFS, EPERM, - * ECONNABORTED or ENOMEM. - */ -int -add_remove_contract(const char *fmri, boolean_t add, ctid_t ctid) -{ - uint_t retries; - int err; - - for (retries = 0; retries <= REP_OP_RETRIES; retries++) { - if (make_handle_bound(rep_handle) == -1) { - err = ECONNABORTED; - break; - } - - if ((err = add_remove_contract_norebind(fmri, add, ctid)) != - ECONNABORTED) - break; - - (void) scf_handle_unbind(rep_handle); - } - - return (err); -} - -/* - * Iterate over all contracts associated with the instance specified by - * fmri; if sig !=0, we send each contract the specified signal, otherwise - * we call adopt_contract() to take ownership. This really ought to be - * reworked to use a callback mechanism if more functionality is added. - * - * Returns 0 on success or ENOENT if the instance, its restarter property - * group, or its contract property don't exist, or EINVAL if the property - * is not of the correct type, ENOMEM if there was a memory allocation - * failure, EPERM if there were permission problems accessing the repository, - * or ECONNABORTED if the connection with the repository was broken. + * Adds/removes a contract id to/from the cached list kept in the instance. + * 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 -iterate_repository_contracts(const char *fmri, int sig) +add_remove_contract(instance_t *inst, boolean_t add, ctid_t ctid) { - scf_iter_t *iter; - scf_value_t *val = NULL; - uint64_t c; - int err; + FILE *tfp; /* temp fp */ int ret = 0; - uint_t retries = 0; - - debug_msg("Entering iterate_repository_contracts"); - - if (make_handle_bound(rep_handle) == -1) - return (ECONNABORTED); - - if (((iter = scf_iter_create(rep_handle)) == NULL) || - ((val = scf_value_create(rep_handle)) == NULL)) { - ret = ENOMEM; - goto out; - } + int repval_ret = 0; + int fopen_retry_cnt = 2; -rep_retry: - if (scf_handle_decode_fmri(rep_handle, fmri, NULL, NULL, inst, NULL, - NULL, SCF_DECODE_FMRI_EXACT) != 0) { - switch (scf_error()) { - case SCF_ERROR_CONNECTION_BROKEN: -rebind: - (void) scf_handle_unbind(rep_handle); - - if (retries++ == REP_OP_RETRIES) { - ret = ECONNABORTED; - goto out; - } - - if (make_handle_bound(rep_handle) == -1) { - ret = ECONNABORTED; - goto out; - } - - goto rep_retry; + debug_msg("Entering add_remove_contract, add: %d\n", add); - case SCF_ERROR_NOT_FOUND: - ret = ENOENT; - goto out; - - case SCF_ERROR_CONSTRAINT_VIOLATED: - case SCF_ERROR_INVALID_ARGUMENT: - case SCF_ERROR_HANDLE_MISMATCH: - default: - assert(0); - abort(); + /* + * 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) { + if (gen_filenms_from_fmri(inst->fmri, "ctid", genfmri_filename, + NULL) != 0) { + /* Failure either from fmri too long or mkdir failure */ + return (ENAMETOOLONG); } - } - - if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, pg) != 0) { - switch (scf_error()) { - case SCF_ERROR_CONNECTION_BROKEN: - goto rebind; - - case SCF_ERROR_NOT_SET: - ret = 0; - goto out; - case SCF_ERROR_NOT_FOUND: - ret = ENOENT; - goto out; - - case SCF_ERROR_INVALID_ARGUMENT: - case SCF_ERROR_HANDLE_MISMATCH: - default: - assert(0); - abort(); - } - } - - if (scf_pg_get_property(pg, SCF_PROPERTY_CONTRACT, prop) != 0) { - switch (scf_error()) { - case SCF_ERROR_CONNECTION_BROKEN: - goto rebind; - - case SCF_ERROR_NOT_SET: - ret = 0; - goto out; - - case SCF_ERROR_NOT_FOUND: +retry_fopen: + if ((tfp = fopen(genfmri_filename, "a")) == NULL) { + if ((errno == EINTR) && (fopen_retry_cnt > 0)) { + fopen_retry_cnt--; + goto retry_fopen; + } ret = ENOENT; goto out; + } - case SCF_ERROR_INVALID_ARGUMENT: - case SCF_ERROR_HANDLE_MISMATCH: - default: - assert(0); - abort(); - } - } - - if (scf_property_is_type(prop, SCF_TYPE_COUNT) != 0) { - switch (scf_error()) { - case SCF_ERROR_CONNECTION_BROKEN: - goto rebind; - - case SCF_ERROR_NOT_SET: + /* Always store ctids as long long */ + if (fprintf(tfp, "%llu\n", (uint64_t)ctid) <= 0) { + (void) fclose(tfp); ret = ENOENT; goto out; - - case SCF_ERROR_TYPE_MISMATCH: - ret = EINVAL; - goto out; + } - default: - assert(0); - abort(); - } - } - - if (scf_iter_property_values(iter, prop) != 0) { - switch (scf_error()) { - case SCF_ERROR_CONNECTION_BROKEN: - goto rebind; - - case SCF_ERROR_NOT_SET: + if (fclose(tfp) != 0) { ret = ENOENT; goto out; - - case SCF_ERROR_HANDLE_MISMATCH: - default: - assert(0); - abort(); - } - } - - for (;;) { - err = scf_iter_next_value(iter, val); - if (err == 0) { - break; - } else if (err != 1) { - assert(scf_error() == SCF_ERROR_CONNECTION_BROKEN); - goto rebind; } - err = scf_value_get_count(val, &c); - assert(err == 0); + if (add_rep_val(inst->start_ctids, ctid) != 0) { + ret = ENOMEM; + goto out; + } + } else { + remove_rep_val(inst->start_ctids, ctid); - if (sig == 0) { - /* Try to adopt the contract */ - if (adopt_contract((ctid_t)c, fmri) != 0) { - /* - * Adoption failed. No reason to think it'll - * work later, so remove the id from our list - * in the repository. - * - * Beware: add_remove_contract_norebind() uses - * the global scf_ handles. Fortunately we're - * done with them. We need to be cognizant of - * repository disconnection, though. - */ - switch (add_remove_contract_norebind(fmri, - B_FALSE, (ctid_t)c)) { - case 0: - case ENOENT: - case EACCES: - case EROFS: - break; - - case ECONNABORTED: - goto rebind; - - default: - assert(0); - abort(); - } - } - } else { - /* - * Send a signal to all in the contract; ESRCH just - * means they all exited before we could kill them - */ - if (sigsend(P_CTID, (ctid_t)c, sig) == -1 && - errno != ESRCH) { - warn_msg(gettext("Unable to signal all contract" - "members of instance %s: %s"), fmri, - strerror(errno)); - } + /* Write all values in list to file */ + if ((repval_ret = repvals_to_file(inst->fmri, "ctid", + inst->start_ctids)) != 0) { + ret = repval_ret; + goto out; } } out: - scf_value_destroy(val); - scf_iter_destroy(iter); 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 +iterate_repository_contracts(instance_t *inst, int sig) +{ + int ret = 0; + FILE *fp; + rep_val_t *spval = NULL; /* Contains a start_pid */ + uint64_t tval; /* temp val holder */ + uu_list_t *uup = NULL; + int fscanf_ret; + int fopen_retry_cnt = 2; + + debug_msg("Entering iterate_repository_contracts, sig: %d", sig); + + if (sig != 0) { + /* + * Send a signal to all in the contract; ESRCH just + * means they all exited before we could kill them + */ + for (spval = uu_list_first(inst->start_ctids); spval != NULL; + spval = uu_list_next(inst->start_ctids, spval)) { + if (sigsend(P_CTID, (ctid_t)spval->val, sig) == -1 && + errno != ESRCH) { + warn_msg(gettext("Unable to signal all " + "contract members of instance %s: %s"), + inst->fmri, strerror(errno)); + } + } + return (0); + } + + /* + * sig == 0 case. + * Attempt to adopt the contract for each ctid. + */ + if (gen_filenms_from_fmri(inst->fmri, "ctid", genfmri_filename, + NULL) != 0) { + /* Failure either from fmri too long or mkdir failure */ + return (ENAMETOOLONG); + } + +retry_fopen: + /* It's ok if no file, there are no ctids to adopt */ + if ((fp = fopen(genfmri_filename, "r")) == NULL) { + if ((errno == EINTR) && (fopen_retry_cnt > 0)) { + fopen_retry_cnt--; + 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; + while ((fscanf_ret = fscanf(fp, "%llu", &tval)) == 1) { + /* If tval isn't a valid ctid, then fail. */ + if (tval == 0) { + (void) fclose(fp); + ret = EIO; + goto out; + } + if ((add_rep_val(uup, tval) == -1) || + (add_rep_val(inst->start_ctids, tval) == -1)) { + (void) fclose(fp); + ret = ENOMEM; + goto out; + } + errno = 0; + } + /* EOF is not a failure when no errno */ + if ((fscanf_ret != EOF) || (errno != 0)) { + ret = EIO; + goto out; + } + + if (fclose(fp) != 0) { + ret = ENXIO; + goto out; + } + + for (spval = uu_list_first(uup); spval != NULL; + spval = uu_list_next(uup, spval)) { + /* Try to adopt the contract */ + if (adopt_contract((ctid_t)spval->val, + inst->fmri) != 0) { + /* + * Adoption failed. No reason to think it'll + * work later, so remove the id from our list + * in the instance. + */ + remove_rep_val(inst->start_ctids, spval->val); + } + } +out: + if (uup) { + empty_rep_val_list(uup); + destroy_rep_val_list(uup); + } + + if (ret != 0) + empty_rep_val_list(inst->start_ctids); + + return (ret); +}
--- a/usr/src/cmd/svc/svcs/svcs.c Mon Nov 27 11:55:12 2006 -0800 +++ b/usr/src/cmd/svc/svcs/svcs.c Mon Nov 27 12:42:57 2006 -0800 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * 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 * or http://www.opensolaris.org/os/licensing. @@ -19,7 +18,7 @@ * * CDDL HEADER END * - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -133,6 +132,12 @@ static char *common_name_buf; /* Sized for maximal length value. */ char *locale; /* Current locale. */ +/* + * Pathname storage for path generated from the fmri. + * Used for reading the ctid and (start) pid files for an inetd service. + */ +static char genfmri_filename[MAXPATHLEN] = ""; + /* Options */ static int *opt_columns = NULL; /* Indices into columns to display. */ static int opt_cnum = 0; @@ -533,16 +538,58 @@ * Generic functions */ +/* + * Return an array of pids associated with the given contract id. + * Returned pids are added to the end of the pidsp array. + */ +static void +ctid_to_pids(uint64_t c, pid_t **pidsp, uint_t *np) +{ + ct_stathdl_t ctst; + uint_t m; + int fd; + int r, err; + pid_t *pids; + + fd = contract_open(c, NULL, "status", O_RDONLY); + if (fd < 0) + return; + + err = ct_status_read(fd, CTD_ALL, &ctst); + if (err != 0) { + uu_warn(gettext("Could not read status of contract " + "%ld: %s.\n"), c, strerror(err)); + (void) close(fd); + return; + } + + (void) close(fd); + + r = ct_pr_status_get_members(ctst, &pids, &m); + assert(r == 0); + + if (m == 0) { + ct_status_free(ctst); + return; + } + + *pidsp = realloc(*pidsp, (*np + m) * sizeof (*pidsp)); + if (*pidsp == NULL) + uu_die(gettext("Out of memory")); + + bcopy(pids, *pidsp + *np, m * sizeof (*pids)); + *np += m; + + ct_status_free(ctst); +} + static int propvals_to_pids(scf_propertygroup_t *pg, const char *pname, pid_t **pidsp, uint_t *np, scf_property_t *prop, scf_value_t *val, scf_iter_t *iter) { scf_type_t ty; - int r, fd, err; uint64_t c; - ct_stathdl_t ctst; - pid_t *pids; - uint_t m; + int r; if (scf_pg_get_property(pg, pname, prop) != 0) { if (scf_error() != SCF_ERROR_NOT_FOUND) @@ -570,46 +617,267 @@ if (scf_value_get_count(val, &c) != 0) scfdie(); - fd = contract_open(c, NULL, "status", O_RDONLY); - if (fd < 0) - continue; - - err = ct_status_read(fd, CTD_ALL, &ctst); - if (err != 0) { - uu_warn(gettext("Could not read status of contract " - "%ld: %s.\n"), c, strerror(err)); - (void) close(fd); - continue; - } - - (void) close(fd); - - r = ct_pr_status_get_members(ctst, &pids, &m); - assert(r == 0); - - if (m == 0) { - ct_status_free(ctst); - continue; - } - - *pidsp = realloc(*pidsp, (*np + m) * sizeof (*pidsp)); - if (*pidsp == NULL) - uu_die(gettext("Out of memory")); - - bcopy(pids, *pidsp + *np, m * sizeof (*pids)); - *np += m; - - ct_status_free(ctst); + ctid_to_pids(c, pidsp, np); } return (0); } +/* + * Check if instance has general/restarter property that matches + * given string. Restarter string must be in canonified form. + * Returns 0 for success; -1 otherwise. + */ static int -instance_processes(scf_instance_t *inst, pid_t **pids, uint_t *np) +check_for_restarter(scf_instance_t *inst, const char *restarter) +{ + char *fmri_buf; + char *fmri_buf_canonified = NULL; + int ret = -1; + + if (inst == NULL) + return (-1); + + /* Get restarter */ + fmri_buf = safe_malloc(max_scf_fmri_length + 1); + if (inst_get_single_val(inst, SCF_PG_GENERAL, + SCF_PROPERTY_RESTARTER, SCF_TYPE_ASTRING, fmri_buf, + max_scf_fmri_length + 1, 0, 0, 1) != 0) + goto out; + + fmri_buf_canonified = safe_malloc(max_scf_fmri_length + 1); + if (scf_canonify_fmri(fmri_buf, fmri_buf_canonified, + (max_scf_fmri_length + 1)) < 0) + goto out; + + if (strcmp(fmri_buf, restarter) == 0) + ret = 0; + +out: + free(fmri_buf); + if (fmri_buf_canonified) + free(fmri_buf_canonified); + return (ret); +} + +/* + * Common code that is used by ctids_by_restarter and pids_by_restarter. + * Checks for a common restarter and if one is available, it generates + * the appropriate filename using wip->fmri and stores that in the + * global genfmri_filename. + * + * Restarters currently supported are: svc:/network/inetd:default + * If a restarter specific action is available, then restarter_spec + * is set to 1. If a restarter specific action is not available, then + * restarter_spec is set to 0 and a -1 is returned. + * + * Returns: + * 0 if success: restarter specific action found and filename generated + * -1 if restarter specific action not found, + * if restarter specific action found but an error was encountered + * during the generation of the wip->fmri based filename + */ +static int +common_by_restarter(scf_instance_t *inst, const char *fmri, + int *restarter_specp) +{ + int ret = -1; + int r; + + /* Check for inetd specific restarter */ + if (check_for_restarter(inst, "svc:/network/inetd:default") != 0) { + *restarter_specp = 0; + return (ret); + } + + *restarter_specp = 1; + + /* Get the ctid filename associated with this instance */ + r = gen_filenms_from_fmri(fmri, "ctid", genfmri_filename, NULL); + + switch (r) { + case 0: + break; + + case -1: + /* + * Unable to get filename from fmri. Print warning + * and return failure with no ctids. + */ + uu_warn(gettext("Unable to read contract ids for %s -- " + "FMRI is too long\n"), fmri); + return (ret); + + case -2: + /* + * The directory didn't exist, so no contracts. + * Return failure with no ctids. + */ + return (ret); + + default: + uu_warn(gettext("%s:%d: gen_filenms_from_fmri() failed with " + "unknown error %d\n"), __FILE__, __LINE__, r); + abort(); + } + + return (0); + +} + +/* + * Get or print a contract id using a restarter specific action. + * + * If the print_flag is not set, this routine gets the single contract + * id associated with this instance. + * If the print flag is set, then print each contract id found. + * + * Returns: + * 0 if success: restarter specific action found and used with no error + * -1 if restarter specific action not found + * -1 if restarter specific action found, but there was a failure + * -1 if print flag is not set and no contract id is found or multiple + * contract ids were found + * E2BIG if print flag is not set, MULTI_OK bit in flag is set and multiple + * contract ids were found + */ +static int +ctids_by_restarter(scf_walkinfo_t *wip, uint64_t *cp, int print_flag, + uint_t flags, int *restarter_specp, void (*callback_header)(), + void (*callback_ctid)(uint64_t)) +{ + FILE *fp; + int ret = -1; + int fscanf_ret; + uint64_t cp2; + int rest_ret; + + /* Check if callbacks are needed and were passed in */ + if (print_flag) { + if ((callback_header == NULL) || (callback_ctid == NULL)) + return (ret); + } + + /* Check for restarter specific action and generation of filename */ + rest_ret = common_by_restarter(wip->inst, wip->fmri, restarter_specp); + if (rest_ret != 0) + return (rest_ret); + + /* + * If fopen fails, then ctid file hasn't been created yet. + * If print_flag is set, this is ok; otherwise fail. + */ + if ((fp = fopen(genfmri_filename, "r")) == NULL) { + if (print_flag) + return (0); + goto out; + } + + if (print_flag) { + /* + * Print all contract ids that are found. + * First callback to print ctid header. + */ + callback_header(); + + /* fscanf may not set errno, so be sure to clear it first */ + errno = 0; + while ((fscanf_ret = fscanf(fp, "%llu", cp)) == 1) { + /* Callback to print contract id */ + callback_ctid(*cp); + errno = 0; + } + /* EOF is not a failure when no errno. */ + if ((fscanf_ret != EOF) || (errno != 0)) { + uu_die(gettext("Unable to read ctid file for %s"), + wip->fmri); + } + (void) putchar('\n'); + ret = 0; + } else { + /* Must find 1 ctid or fail */ + if (fscanf(fp, "%llu", cp) == 1) { + /* If 2nd ctid found - fail */ + if (fscanf(fp, "%llu", &cp2) == 1) { + if (flags & MULTI_OK) + ret = E2BIG; + } else { + /* Success - found only 1 ctid */ + ret = 0; + } + } + } + (void) fclose(fp); + +out: + return (ret); +} + +/* + * Get the process ids associated with an instance using a restarter + * specific action. + * + * Returns: + * 0 if success: restarter specific action found and used with no error + * -1 restarter specific action not found or if failure + */ +static int +pids_by_restarter(scf_instance_t *inst, const char *fmri, + pid_t **pids, uint_t *np, int *restarter_specp) +{ + uint64_t c; + FILE *fp; + int fscanf_ret; + int rest_ret; + + /* Check for restarter specific action and generation of filename */ + rest_ret = common_by_restarter(inst, fmri, restarter_specp); + if (rest_ret != 0) + return (rest_ret); + + /* + * If fopen fails with ENOENT then the ctid file hasn't been + * created yet so return success. + * For all other errors - fail with uu_die. + */ + if ((fp = fopen(genfmri_filename, "r")) == NULL) { + if (errno == ENOENT) + return (0); + uu_die(gettext("Unable to open ctid file for %s"), fmri); + } + + /* fscanf may not set errno, so be sure to clear it first */ + errno = 0; + while ((fscanf_ret = fscanf(fp, "%llu", &c)) == 1) { + if (c == 0) { + (void) fclose(fp); + uu_die(gettext("ctid file for %s has corrupt data"), + fmri); + } + ctid_to_pids(c, pids, np); + errno = 0; + } + /* EOF is not a failure when no errno. */ + if ((fscanf_ret != EOF) || (errno != 0)) { + uu_die(gettext("Unable to read ctid file for %s"), fmri); + } + + (void) fclose(fp); + return (0); +} + +static int +instance_processes(scf_instance_t *inst, const char *fmri, + pid_t **pids, uint_t *np) { scf_iter_t *iter; int ret; + int restarter_spec; + + /* Use the restarter specific get pids routine, if available. */ + ret = pids_by_restarter(inst, fmri, pids, np, &restarter_spec); + if (restarter_spec == 1) + return (ret); if ((iter = scf_iter_create(h)) == NULL) scfdie(); @@ -709,13 +977,24 @@ uint64_t c; size_t newsize = (*buf ? strlen(*buf) : 0) + CTID_COLUMN_WIDTH + 2; char *newbuf = safe_malloc(newsize); - - if (wip->pg != NULL) + int restarter_spec; + + /* + * Use the restarter specific get pids routine, if available. + * Only check for non-legacy services (wip->pg == 0). + */ + if (wip->pg != NULL) { r = pg_get_single_val(wip->pg, scf_property_contract, SCF_TYPE_COUNT, &c, 0, EMPTY_OK | MULTI_OK); - else - r = get_restarter_count_prop(wip->inst, scf_property_contract, - &c, EMPTY_OK | MULTI_OK); + } else { + r = ctids_by_restarter(wip, &c, 0, MULTI_OK, &restarter_spec, + NULL, NULL); + if (restarter_spec == 0) { + /* No restarter specific routine */ + r = get_restarter_count_prop(wip->inst, + scf_property_contract, &c, EMPTY_OK | MULTI_OK); + } + } if (r == 0) (void) snprintf(newbuf, newsize, "%s%*lu ", @@ -738,13 +1017,24 @@ { int r; uint64_t c; - - if (wip->pg != NULL) + int restarter_spec; + + /* + * Use the restarter specific get pids routine, if available. + * Only check for non-legacy services (wip->pg == 0). + */ + if (wip->pg != NULL) { r = pg_get_single_val(wip->pg, scf_property_contract, SCF_TYPE_COUNT, &c, 0, EMPTY_OK); - else - r = get_restarter_count_prop(wip->inst, scf_property_contract, - &c, EMPTY_OK); + } else { + r = ctids_by_restarter(wip, &c, 0, MULTI_OK, &restarter_spec, + NULL, NULL); + if (restarter_spec == 0) { + /* No restarter specific routine */ + r = get_restarter_count_prop(wip->inst, + scf_property_contract, &c, EMPTY_OK); + } + } if (r == 0) { /* @@ -1628,19 +1918,39 @@ */ #define DETAILED_WIDTH (11 + 2) +/* + * Callback routine to print header for contract id. + * Called by ctids_by_restarter and print_detailed. + */ static void -detailed_list_processes(scf_instance_t *inst) +print_ctid_header() +{ + (void) printf("%-*s", DETAILED_WIDTH, "contract_id"); +} + +/* + * Callback routine to print a contract id. + * Called by ctids_by_restarter and print_detailed. + */ +static void +print_ctid_detailed(uint64_t c) +{ + (void) printf("%lu ", (ctid_t)c); +} + +static void +detailed_list_processes(scf_walkinfo_t *wip) { uint64_t c; pid_t *pids; uint_t i, n; psinfo_t psi; - if (get_restarter_count_prop(inst, scf_property_contract, &c, + if (get_restarter_count_prop(wip->inst, scf_property_contract, &c, EMPTY_OK) != 0) return; - if (instance_processes(inst, &pids, &n) != 0) + if (instance_processes(wip->inst, wip->fmri, &pids, &n) != 0) return; qsort(pids, n, sizeof (*pids), pidcmp); @@ -1887,6 +2197,8 @@ struct timeval tv; time_t stime; struct tm *tmp; + int restarter_spec; + int restarter_ret; const char * const fmt = "%-*s%s\n"; @@ -2003,6 +2315,19 @@ free(buf); + /* + * Use the restarter specific routine to print the ctids, if available. + * If restarter specific action is available and it fails, then die. + */ + restarter_ret = ctids_by_restarter(wip, &c, 1, 0, + &restarter_spec, print_ctid_header, print_ctid_detailed); + if (restarter_spec == 1) { + if (restarter_ret != 0) + uu_die(gettext("Unable to get restarter for %s"), + wip->fmri); + goto restarter_common; + } + if (rpg) { scf_iter_t *iter; @@ -2012,8 +2337,9 @@ if (scf_pg_get_property(rpg, scf_property_contract, g_prop) == 0) { if (scf_property_is_type(g_prop, SCF_TYPE_COUNT) == 0) { - (void) printf("%-*s", DETAILED_WIDTH, - "contract_id"); + + /* Callback to print ctid header */ + print_ctid_header(); if (scf_iter_property_values(iter, g_prop) != 0) scfdie(); @@ -2027,7 +2353,9 @@ if (scf_value_get_count(g_val, &c) != 0) scfdie(); - (void) printf("%lu ", (ctid_t)c); + + /* Callback to print contract id. */ + print_ctid_detailed(c); } (void) putchar('\n'); @@ -2046,6 +2374,7 @@ scfdie(); } +restarter_common: scf_pg_destroy(rpg); /* Dependencies. */ @@ -2067,7 +2396,7 @@ scf_iter_destroy(pg_iter); if (opt_processes) - detailed_list_processes(wip->inst); + detailed_list_processes(wip); return (0); } @@ -2077,15 +2406,16 @@ * return the augmented string. */ static char * -add_processes(char *line, scf_instance_t *inst, scf_propertygroup_t *lpg) +add_processes(scf_walkinfo_t *wip, char *line, scf_propertygroup_t *lpg) { pid_t *pids = NULL; uint_t i, n = 0; if (lpg == NULL) { - if (instance_processes(inst, &pids, &n) != 0) + if (instance_processes(wip->inst, wip->fmri, &pids, &n) != 0) return (line); } else { + /* Legacy services */ scf_iter_t *iter; if ((iter = scf_iter_create(h)) == NULL) @@ -2211,7 +2541,7 @@ /* If we're supposed to list the processes, too, do that now. */ if (opt_processes) - lp->str = add_processes(lp->str, wip->inst, wip->pg); + lp->str = add_processes(wip, lp->str, wip->pg); /* Create the sort key. */ cp = lp->key = safe_malloc(sortkey_sz);
--- a/usr/src/lib/libscf/Makefile.com Mon Nov 27 11:55:12 2006 -0800 +++ b/usr/src/lib/libscf/Makefile.com Mon Nov 27 12:42:57 2006 -0800 @@ -43,7 +43,7 @@ $(NATIVE_BUILD)VERS = $(NATIVE_BUILD)LIBS = $(DYNLIB) -LDLIBS += -luutil -lc +LDLIBS += -luutil -lc -lgen SRCDIR = ../common $(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) @@ -61,7 +61,8 @@ MY_NATIVE_CPPFLAGS =\ -DNATIVE_BUILD $(DTEXTDOM) \ -I../inc -I$(COMDIR) -I$(LIBUUTIL)/common -MY_NATIVE_LDLIBS = -L$(LIBUUTIL)/native -R$(LIBUUTIL)/native -luutil -ldoor -lc +MY_NATIVE_LDLIBS = -L$(LIBUUTIL)/native -R$(LIBUUTIL)/native -luutil -ldoor -lc \ + -lgen .KEEP_STATE:
--- a/usr/src/lib/libscf/common/mapfile-vers Mon Nov 27 11:55:12 2006 -0800 +++ b/usr/src/lib/libscf/common/mapfile-vers Mon Nov 27 12:42:57 2006 -0800 @@ -225,6 +225,7 @@ scf_type_to_string; scf_walk_fmri; _smf_refresh_instance_i; + gen_filenms_from_fmri; local: *; };
--- a/usr/src/lib/libscf/common/midlevel.c Mon Nov 27 11:55:12 2006 -0800 +++ b/usr/src/lib/libscf/common/midlevel.c Mon Nov 27 12:42:57 2006 -0800 @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * 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 * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -33,9 +32,11 @@ #include <strings.h> #include <string.h> #include <stdlib.h> +#include <sys/param.h> +#include <errno.h> +#include <libgen.h> #include "midlevel_impl.h" - #ifndef NDEBUG #define bad_error(func, err) { \ uu_warn("%s:%d: %s failed with unexpected error %d. Aborting.\n", \ @@ -46,6 +47,9 @@ #define bad_error(func, err) abort() #endif +/* Path to speedy files area must end with a slash */ +#define SMF_SPEEDY_FILES_PATH "/etc/svc/volatile/" + /* * Internal private function that creates and binds a handle. */ @@ -2085,3 +2089,50 @@ *length = ret->pv_opaque.o_size; return (ret->pv_opaque.o_value); } + +/* + * Generate a filename based on the fmri and the given name and return + * it in the buffer of MAXPATHLEN provided by the caller. + * If temp_filename is non-zero, also generate a temporary, unique filename + * and return it in the temp buffer of MAXPATHLEN provided by the caller. + * The path to the generated pathname is also created. + * Given fmri should begin with a scheme such as "svc:". + * Returns + * 0 on success + * -1 if filename would exceed MAXPATHLEN or + * -2 if unable to create directory to filename path + */ +int +gen_filenms_from_fmri(const char *fmri, const char *name, char *filename, + char *temp_filename) +{ + int len; + + len = strlen(SMF_SPEEDY_FILES_PATH); + len += strlen(fmri); + len += 2; /* for slash and null */ + len += strlen(name); + len += 6; /* For X's needed for mkstemp */ + + if (len > MAXPATHLEN) + return (-1); + + /* Construct directory name first - speedy path ends in slash */ + (void) strcpy(filename, SMF_SPEEDY_FILES_PATH); + (void) strcat(filename, fmri); + if (mkdirp(filename, 0755) == -1) { + /* errno is set */ + if (errno != EEXIST) + return (-2); + } + + (void) strcat(filename, "/"); + (void) strcat(filename, name); + + if (temp_filename) { + (void) strcpy(temp_filename, filename); + (void) strcat(temp_filename, "XXXXXX"); + } + + return (0); +}
--- a/usr/src/lib/libscf/inc/libscf_priv.h Mon Nov 27 11:55:12 2006 -0800 +++ b/usr/src/lib/libscf/inc/libscf_priv.h Mon Nov 27 12:42:57 2006 -0800 @@ -293,6 +293,8 @@ int scf_cmp_pattern(char *, scf_pattern_t *); +int gen_filenms_from_fmri(const char *, const char *, char *, char *); + #ifdef __cplusplus } #endif