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